Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
* style and digest toc options.url. TODO. update version selector to take in props instead of reading db

* take in options from toc node and filter with available versions

* minor fix. check for null associatedProducts

* update unit test

* update unit test for toc context

* add fetch for merged toc metadata on build time

* check for associated products existence

* check for null remotemetadata

* address comments

* use a drawer for options.versions toc node

* update comment

* revert testing change
  • Loading branch information
seungpark authored Feb 7, 2023
1 parent 3bccef0 commit 63d08be
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 58 deletions.
21 changes: 21 additions & 0 deletions gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,26 @@ exports.createPages = async ({ actions }) => {
throw err;
}

// get remote metadata for updated ToC in Atlas
let metadata = siteMetadata;
try {
const filter = {
project: manifestMetadata.project,
branch: manifestMetadata.branch,
};
if (isAssociatedProduct || manifestMetadata?.associated_products.length) {
filter['is_merged_toc'] = true;
}
const findOptions = {
sort: { build_id: -1 },
};
metadata = await db.stitchInterface.getMetadata(filter, findOptions);
} catch (e) {
console.error('Error while fetching metadata from Atlas, falling back to manifest metadata');
console.error(e);
metadata = siteMetadata;
}

return new Promise((resolve, reject) => {
PAGES.forEach((page) => {
const pageNodes = RESOLVED_REF_DOC_MAPPING[page]?.ast;
Expand All @@ -214,6 +234,7 @@ exports.createPages = async ({ actions }) => {
slug,
repoBranches,
associatedReposInfo,
remoteMetadata: metadata,
isAssociatedProduct,
template: pageNodes?.options?.template,
page: pageNodes,
Expand Down
3 changes: 2 additions & 1 deletion src/components/RootProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const RootProvider = ({
repoBranches,
associatedReposInfo,
isAssociatedProduct,
remoteMetadata,
}) => (
<TabProvider selectors={selectors}>
<ContentsProvider headingNodes={headingNodes}>
Expand All @@ -26,7 +27,7 @@ const RootProvider = ({
associatedReposInfo={associatedReposInfo}
isAssociatedProduct={isAssociatedProduct}
>
<TocContextProvider>
<TocContextProvider remoteMetadata={remoteMetadata}>
<NavigationProvider>
<SidenavContextProvider>{children}</SidenavContextProvider>
</NavigationProvider>
Expand Down
55 changes: 36 additions & 19 deletions src/components/Sidenav/TOCNode.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useContext, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { css } from '@emotion/react';
import { cx, css as LeafyCSS } from '@leafygreen-ui/emotion';
Expand All @@ -8,6 +8,7 @@ import Box from '@leafygreen-ui/box';
import Icon from '@leafygreen-ui/icon';
import { theme } from '../../theme/docsTheme';
import Link from '../Link';
import { VersionContext } from '../../context/version-context';
import { formatText } from '../../utils/format-text';
import { isActiveTocNode } from '../../utils/is-active-toc-node';
import { isSelectedTocNode } from '../../utils/is-selected-toc-node';
Expand Down Expand Up @@ -38,13 +39,26 @@ const wrapperStyle = LeafyCSS`
flex: 1 1 auto;
}
`;

const overwriteLinkStyle = LeafyCSS`
span {
display: flex;
}
`;
/**
*
* @param {hasVersions} boolean
* @returns Wrapper for Version Selector, or returns children
*/
const Wrapper = ({ children, hasVersions }) => {
return hasVersions ? <Box className={cx(wrapperStyle)}>{children}</Box> : <>{children}</>;
const Wrapper = ({ children, hasVersions, project, versions }) => {
return hasVersions ? (
<Box className={cx(wrapperStyle)}>
{children}
{hasVersions && <VersionSelector versionedProject={project} tocVersionNames={versions} />}
</Box>
) : (
<>{children}</>
);
};

/**
Expand All @@ -53,12 +67,13 @@ const Wrapper = ({ children, hasVersions }) => {
*/
const TOCNode = ({ activeSection, handleClick, level = BASE_NODE_LEVEL, node }) => {
const { title, slug, url, children, options = {} } = node;
const target = slug || url;
const { activeVersions } = useContext(VersionContext);
const target = options.urls?.[activeVersions[options.project]] || slug || url;
const hasChildren = !!children?.length;
const hasVersions = !!(options?.versions?.length > 1); // in the event there is only one version, do we show version selector?
const hasVersions = !!(options?.versions?.length > 1);
const isActive = isActiveTocNode(activeSection, slug, children);
const isSelected = isSelectedTocNode(activeSection, slug);
const isDrawer = !!(options && options.drawer);
const isDrawer = !!(options && (options.drawer || options.versions)); // TODO: convert versions option to drawer in backend
const isTocIcon = !!(options.tocicon === 'sync');
const [isOpen, setIsOpen] = useState(isActive);

Expand Down Expand Up @@ -94,36 +109,38 @@ const TOCNode = ({ activeSection, handleClick, level = BASE_NODE_LEVEL, node })

if (isDrawer && hasChildren) {
return (
<SideNavItem
className={cx(sideNavItemTOCStyling({ level }))}
onClick={() => {
setIsOpen(!isOpen);
}}
>
<Icon className={cx(caretStyle)} glyph={iconType} fill={palette.gray.base} onClick={onCaretClick} />
{isTocIcon && <SyncCloud />}
{formattedTitle}
</SideNavItem>
<Wrapper hasVersions={hasVersions} project={options.project} versions={options.versions}>
<SideNavItem
className={cx(sideNavItemTOCStyling({ level }))}
onClick={() => {
setIsOpen(!isOpen);
}}
>
<Icon className={cx(caretStyle)} glyph={iconType} fill={palette.gray.base} onClick={onCaretClick} />
{isTocIcon && <SyncCloud />}
{formattedTitle}
</SideNavItem>
</Wrapper>
);
}
return (
<Wrapper hasVersions={hasVersions}>
<Wrapper hasVersions={hasVersions} project={options.project} versions={options.versions}>
<SideNavItem
as={Link}
to={target}
active={isSelected}
className={cx(sideNavItemTOCStyling({ level }))}
className={cx(sideNavItemTOCStyling({ level }), overwriteLinkStyle)}
onClick={(e) => {
setIsOpen(!isOpen);
}}
hideExternalIcon={true}
>
{hasChildren && (
<Icon className={cx(caretStyle)} glyph={iconType} fill={palette.gray.base} onClick={onCaretClick} />
)}
{isTocIcon && <SyncCloud />}
{formattedTitle}
</SideNavItem>
{hasVersions && <VersionSelector versionedProject={options.project} />}
</Wrapper>
);
};
Expand Down
2 changes: 1 addition & 1 deletion src/components/Sidenav/Toctree.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const Toctree = ({ handleClick, slug, toctree: { children } }) => {
return (
<>
{children.map((c) => (
<TOCNode key={c.slug || c.url} activeSection={slug} handleClick={handleClick} node={c} />
<TOCNode key={c.slug || c.url || c.options?.urls} activeSection={slug} handleClick={handleClick} node={c} />
))}
</>
);
Expand Down
16 changes: 6 additions & 10 deletions src/components/Sidenav/VersionSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ const buildChoice = (branch) => {
};
};

const buildChoices = (branches) => {
return !branches ? [] : branches.map(buildChoice);
const buildChoices = (branches, tocVersionNames) => {
return !branches ? [] : branches.filter((branch) => tocVersionNames.includes(branch.gitBranchName)).map(buildChoice);
};

const StyledSelect = styled(Select)`
Expand All @@ -39,17 +39,13 @@ const StyledSelect = styled(Select)`
}
`;

const VersionSelector = ({ versionedProject = '' }) => {
// verify if this version selector is for current product
// determines if we should use reach router or not
// ie. on atlas-cli v1.3 , switch to v1.0 -> should update link (what if link is 404)
const VersionSelector = ({ versionedProject = '', tocVersionNames = [] }) => {
const { activeVersions, availableVersions, onVersionSelect } = useContext(VersionContext);

const [options, setOptions] = useState(buildChoices(availableVersions[versionedProject]));
const [options, setOptions] = useState(buildChoices(availableVersions[versionedProject], tocVersionNames));

useEffect(() => {
setOptions(buildChoices(availableVersions[versionedProject]));
}, [availableVersions, versionedProject]);
setOptions(buildChoices(availableVersions[versionedProject], tocVersionNames));
}, [availableVersions, tocVersionNames, versionedProject]);

const onChange = useCallback(
({ value }) => {
Expand Down
36 changes: 21 additions & 15 deletions src/context/toc-context.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { createContext, useContext, useEffect, useState } from 'react';
import { useCallback } from 'react';
import { METADATA_COLLECTION } from '../build-constants';
import { useSiteMetadata } from '../hooks/use-site-metadata';
import { fetchDocument } from '../utils/realm';
import { fetchDocuments } from '../utils/realm';
import useSnootyMetadata from '../utils/use-snooty-metadata';
import { VersionContext } from './version-context';

Expand All @@ -12,30 +12,36 @@ const TocContext = createContext({

// ToC context that provides ToC content in form of *above*
// filters all available ToC by currently selected version via VersionContext
const TocContextProvider = ({ children }) => {
const { activeVersions, setActiveVersions } = useContext(VersionContext);
const { toctree } = useSnootyMetadata();
const { database, project } = useSiteMetadata();
const TocContextProvider = ({ children, remoteMetadata }) => {
const { activeVersions, setActiveVersions, showVersionDropdown } = useContext(VersionContext);
const { toctree, associated_products: associatedProducts } = useSnootyMetadata();
const { database, project, parserBranch } = useSiteMetadata();
const [remoteToc, setRemoteToc] = useState();
const [activeToc, setActiveToc] = useState(toctree);
const [activeToc, setActiveToc] = useState(remoteMetadata?.toctree || toctree);

const getTocMetadata = useCallback(async () => {
try {
// TODO: update metadata to have 'project' field. don't have to construct page_id
// NOTE: see snooty_dev.metadata for documents with 'project' field defined. input testing metadata
let filter = {
const filter = {
project: `${project}`,
branch: parserBranch,
};
let db = database;
const metadata = await fetchDocument(db, METADATA_COLLECTION, filter);
return metadata.toctree;
const findOptions = {
sort: { build_id: -1 },
};

if (associatedProducts?.length || showVersionDropdown) {
filter['is_merged_toc'] = true;
}
const db = database;
const metadata = await fetchDocuments(db, METADATA_COLLECTION, filter, undefined, findOptions);
return metadata[0]?.toctree ?? toctree;
} catch (e) {
// fallback to toctree from build time
console.error(e);
return toctree;
return remoteMetadata?.toctree || toctree;
}
// below dependents are server constants
}, [database, project, toctree]);
}, [project, parserBranch, associatedProducts, showVersionDropdown, database, toctree, remoteMetadata]);

const getFilteredToc = useCallback(() => {
// filter remoteToc by activeVersions and return a copy
Expand Down Expand Up @@ -64,7 +70,7 @@ const TocContextProvider = ({ children }) => {
}
}

if (clonedNode.children.length) {
if (clonedNode.children?.length) {
clonedToc.children.push(clonedNode);
}
}
Expand Down
10 changes: 8 additions & 2 deletions src/init/DocumentDatabase.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,14 @@ class StitchInterface {
return this.stitchClient.callFunction('fetchDocuments', [DB, collection, buildFilter]);
}

async getMetadata(buildFilter) {
return this.stitchClient.callFunction('fetchDocument', [DB, METADATA_COLLECTION, buildFilter]);
async getMetadata(buildFilter, findOptions) {
return this.stitchClient.callFunction('fetchDocument', [
DB,
METADATA_COLLECTION,
buildFilter,
undefined,
findOptions,
]);
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/layouts/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const GlobalGrid = styled('div')`

const DefaultLayout = ({
children,
pageContext: { page, slug, repoBranches, template, associatedReposInfo, isAssociatedProduct },
pageContext: { page, slug, repoBranches, template, associatedReposInfo, isAssociatedProduct, remoteMetadata },
}) => {
const { sidenav } = getTemplate(template);
const { chapters, guides, publishedBranches, slugToTitle, title, toctree, eol } = useSnootyMetadata();
Expand All @@ -95,6 +95,7 @@ const DefaultLayout = ({
headingNodes={page?.options?.headings}
selectors={page?.options?.selectors}
isAssociatedProduct={isAssociatedProduct}
remoteMetadata={remoteMetadata}
>
<GlobalGrid>
<Header sidenav={sidenav} eol={eol} />
Expand Down
8 changes: 4 additions & 4 deletions src/utils/realm.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ export const fetchOASFile = async (apiName, database) => {
return fetchData('fetchOASFile', apiName, database);
};

export const fetchDocument = async (database, collectionName, query) => {
return fetchData('fetchDocument', database, collectionName, query);
export const fetchDocument = async (database, collectionName, query, projections) => {
return fetchData('fetchDocument', database, collectionName, query, projections);
};

export const fetchDocuments = async (database, collectionName, query) => {
return fetchData('fetchDocuments', database, collectionName, query);
export const fetchDocuments = async (database, collectionName, query, projections, options) => {
return fetchData('fetchDocuments', database, collectionName, query, projections, options);
};
19 changes: 14 additions & 5 deletions tests/context/toc-context.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { TocContext, TocContextProvider } from '../../src/context/toc-context';
// <------------------ START test data mocks ------------------>
const siteMetadataMock = jest.spyOn(siteMetadata, 'useSiteMetadata');
const snootyMetadataMock = jest.spyOn(snootyMetadata, 'default');
const realmMock = jest.spyOn(realm, 'fetchDocument');
const realmMock = jest.spyOn(realm, 'fetchDocuments');
const project = 'cloud-docs';

let sampleTocTree, responseTree, mockedResponse;
Expand All @@ -33,10 +33,12 @@ const setResponse = () => {
},
{
title: [{ type: 'text', position: { start: { line: 1 } }, value: 'sample page3' }],
slug: 'sample-page3',
options: {
versions: ['v1.0'],
project: 'atlas-cli',
urls: {
'v1.0': 'sample-page3-v1.0',
},
},
children: [
{
Expand All @@ -56,6 +58,11 @@ const setResponse = () => {
responseTree.children[responseTree.children.length - 1].options = {
versions: ['v1.0', 'v1.2', 'v0.8'],
project: 'atlas-cli',
urls: {
'v1.0': 'sample-page3-v1.0',
'v1.2': 'sample-page3-v1.2',
'v0.8': 'sample-page3-v0.8',
},
};
responseTree.children[responseTree.children.length - 1].children.push({
title: [{ type: 'text', position: { start: { line: 1 } }, value: 'sample child v1.2' }],
Expand All @@ -73,9 +80,11 @@ const setResponse = () => {
},
});

mockedResponse = {
toctree: responseTree,
};
mockedResponse = [
{
toctree: responseTree,
},
];
};

const setMocks = () => {
Expand Down

0 comments on commit 63d08be

Please sign in to comment.