Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(theme-classic): new navbar item linking to a sidebar #6139

Merged
merged 26 commits into from
Jan 6, 2022
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
54f1468
<feat>(content-docs): added GlobalSidebar, GlobalSidebarLink, toGloba…
lmpham1 Dec 19, 2021
1f71b22
<feat>(theme-classic): added DocSidebarNavbarItem as the new NavbarIt…
lmpham1 Dec 19, 2021
99f52a3
<feat>(theme-classic, content-doc): normalized doc id into URL in plu…
lmpham1 Dec 20, 2021
e75ab58
<feat>(content-docs): fixed a bug in getFirstDocIdOfSidebar function …
lmpham1 Dec 21, 2021
7a6813b
Merge branch 'main' into navbar-sidebar-first-doc
Josh-Cena Dec 21, 2021
4c9015b
Various refactors
Josh-Cena Dec 21, 2021
1b30b5a
id => sidebarId
Josh-Cena Dec 21, 2021
e41a956
Link should always be defined
Josh-Cena Dec 21, 2021
8d79727
Merge branch 'main' into navbar-sidebar-first-doc
Josh-Cena Dec 21, 2021
3e3c280
Fallback to sidebar ID
Josh-Cena Dec 21, 2021
939d0a3
Added unit tests for getFirstDocOfSidebar
lmpham1 Dec 21, 2021
5941f7b
added documentation for the new DocSidebarNavbarItem type
lmpham1 Dec 22, 2021
8b926c6
Update theme-configuration.md
Josh-Cena Dec 22, 2021
00bc1ac
Merge branch 'main' into navbar-sidebar-first-doc
slorber Dec 28, 2021
95bf5ea
Update packages/docusaurus-theme-classic/src/theme/NavbarItem/DocSide…
lmpham1 Dec 30, 2021
04e70b4
changed getSidebarInVersion function in the DocSidebarNavbarItem modu…
lmpham1 Dec 30, 2021
1c7c848
refactor findFirstLink and toGlobalSidebars functions in doc plugin, …
lmpham1 Dec 30, 2021
3192dcb
make findFirstLink receive a LoadedVersion object to find a doc's per…
lmpham1 Dec 30, 2021
2ae7dfe
Merge branch 'main' into navbar-sidebar-first-doc
Josh-Cena Jan 2, 2022
95643c8
Revert "make findFirstLink receive a LoadedVersion object to find a d…
Josh-Cena Jan 2, 2022
ae7be3a
fix tests
Josh-Cena Jan 2, 2022
794ac8c
Lift type
Josh-Cena Jan 2, 2022
04c196a
Update packages/docusaurus-plugin-content-docs/src/globalData.ts
Josh-Cena Jan 5, 2022
ec7dd46
fixes
Josh-Cena Jan 6, 2022
a2ad091
Fix docs
Josh-Cena Jan 6, 2022
bcd01a9
Update utils.ts
Josh-Cena Jan 6, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,11 @@ Object {
"mainDocId": "hello",
"name": "current",
"path": "/docs",
"sidebars": Object {
"docs": Object {
"link": "/docs/foo/bar",
},
},
},
],
},
Expand Down Expand Up @@ -1007,6 +1012,11 @@ Object {
"mainDocId": "hello",
"name": "current",
"path": "/docs",
"sidebars": Object {
"docs": Object {
"link": "/docs/foo/bar",
},
},
},
],
},
Expand Down Expand Up @@ -2359,6 +2369,11 @@ Object {
"mainDocId": "team",
"name": "current",
"path": "/community/next",
"sidebars": Object {
"community": Object {
"link": "/community/next/team",
},
},
},
Object {
"docs": Array [
Expand All @@ -2373,6 +2388,11 @@ Object {
"mainDocId": "team",
"name": "1.0.0",
"path": "/community",
"sidebars": Object {
"version-1.0.0/community": Object {
"link": "/community/team",
},
},
},
],
},
Expand Down Expand Up @@ -3407,6 +3427,11 @@ Object {
"mainDocId": "hello",
"name": "current",
"path": "/docs/next",
"sidebars": Object {
"docs": Object {
"link": "/docs/next/foo/barSlug",
},
},
},
Object {
"docs": Array [
Expand All @@ -3426,6 +3451,11 @@ Object {
"mainDocId": "hello",
"name": "1.0.1",
"path": "/docs",
"sidebars": Object {
"VersionedSideBarNameDoesNotMatter/docs": Object {
"link": "/docs/foo/bar",
},
},
},
Object {
"docs": Array [
Expand All @@ -3450,6 +3480,11 @@ Object {
"mainDocId": "hello",
"name": "1.0.0",
"path": "/docs/1.0.0",
"sidebars": Object {
"version-1.0.0/docs": Object {
"link": "/docs/1.0.0/foo/barSlug",
},
},
},
Object {
"docs": Array [
Expand Down Expand Up @@ -3499,6 +3534,11 @@ Object {
"mainDocId": "rootAbsoluteSlug",
"name": "withSlugs",
"path": "/docs/withSlugs",
"sidebars": Object {
"version-1.0.1/docs": Object {
"link": "/docs/withSlugs/rootAbsoluteSlug",
},
},
},
],
},
Expand Down
29 changes: 29 additions & 0 deletions packages/docusaurus-plugin-content-docs/src/globalData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@
* LICENSE file in the root directory of this source tree.
*/

import {mapValues} from 'lodash';
import {normalizeUrl} from '@docusaurus/utils';
import type {Sidebars} from './sidebars/types';
import {createSidebarsUtils} from './sidebars/utils';
import type {
DocMetadata,
GlobalDoc,
LoadedVersion,
GlobalVersion,
GlobalSidebar,
} from './types';

export function toGlobalDataDoc(doc: DocMetadata): GlobalDoc {
Expand All @@ -20,6 +25,29 @@ export function toGlobalDataDoc(doc: DocMetadata): GlobalDoc {
};
}

export function toGlobalSidebars(
sidebars: Sidebars,
version: LoadedVersion,
): Record<string, GlobalSidebar> {
const {getFirstDocOfSidebar} = createSidebarsUtils(sidebars);
return mapValues(sidebars, (sidebar, sidebarId) => {
const firstDoc = getFirstDocOfSidebar(sidebarId);
if (!firstDoc) {
throw new Error(
`The sidebar "${sidebarId}" doesn't seem to contain any documentation`,
);
slorber marked this conversation as resolved.
Show resolved Hide resolved
}
return {
link: firstDoc.isAutoGeneratedIndex
? normalizeUrl([version.versionPath, firstDoc.slug])
: version.docs.find(
(doc) =>
doc.id === firstDoc.id || doc.unversionedId === firstDoc.id,
)!.permalink,
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved
};
});
}

export function toGlobalDataVersion(version: LoadedVersion): GlobalVersion {
return {
name: version.versionName,
Expand All @@ -28,5 +56,6 @@ export function toGlobalDataVersion(version: LoadedVersion): GlobalVersion {
path: version.versionPath,
mainDocId: version.mainDocId,
docs: version.docs.map(toGlobalDataDoc),
sidebars: toGlobalSidebars(version.sidebars, version),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,33 @@ describe('createSidebarsUtils', () => {
},
];

const sidebars: Sidebars = {sidebar1, sidebar2, sidebar3};
const sidebar4: Sidebar = [
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'S4 Category',
link: {
type: 'generated-index',
slug: '/s4-category-slug',
permalink: '/s4-category-permalink',
},
items: [
{type: 'doc', id: 'doc8'},
{type: 'doc', id: 'doc9'},
],
},
];

const sidebars: Sidebars = {sidebar1, sidebar2, sidebar3, sidebar4};

const {
getFirstDocIdOfFirstSidebar,
getSidebarNameByDocId,
getDocNavigation,
getCategoryGeneratedIndexNavigation,
getCategoryGeneratedIndexList,
getFirstDocOfSidebar,
} = createSidebarsUtils(sidebars);

test('getSidebarNameByDocId', async () => {
Expand Down Expand Up @@ -224,8 +243,28 @@ describe('createSidebarsUtils', () => {
type: 'category',
label: 'S3 SubSubCategory',
},
{
type: 'category',
label: 'S4 Category',
},
]);
});

test('getFirstDocOfSidebar', async () => {
expect(getFirstDocOfSidebar('sidebar1')).toMatchObject({
id: 'doc1',
});
expect(getFirstDocOfSidebar('sidebar2')).toMatchObject({
id: 'doc3',
});
expect(getFirstDocOfSidebar('sidebar3')).toMatchObject({
id: 'doc5',
});
expect(getFirstDocOfSidebar('sidebar4')).toMatchObject({
isAutoGeneratedIndex: true,
slug: '/s4-category-slug',
});
});
});

describe('collectSidebarDocItems', () => {
Expand Down
50 changes: 50 additions & 0 deletions packages/docusaurus-plugin-content-docs/src/sidebars/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,16 @@ export type SidebarsUtils = {
getCategoryGeneratedIndexNavigation: (
categoryGeneratedIndexPermalink: string,
) => SidebarNavigation;
getFirstDocOfSidebar: (sidebarId: string) =>
| {
isAutoGeneratedIndex: false;
id: string;
}
| {
isAutoGeneratedIndex: true;
slug: string;
}
| undefined;

checkSidebarsDocIds: (validDocIds: string[], sidebarFilePath: string) => void;
};
Expand Down Expand Up @@ -266,6 +276,45 @@ Available document ids are:
}
}

function getFirstDocOfSidebar(sidebar: Sidebar):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about refactor to following signature?

function findFirstLink(sidebar: Sidebar): {label: string, path: string} | undefined

Copy link
Contributor Author

@lmpham1 lmpham1 Dec 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So to make this function return the doc's path, I passed into it a LoadedVersion object to look up the doc's permalink:

function findFirstLink(sidebar: Sidebar, version: LoadedVersion): { label: string; path: string } | undefined

I just refactored the function in my recent commit. Let me know if you think this is a good approach, or if you think it's a bit overkill to pass LoadedVersion into this function.

Copy link
Collaborator

@Josh-Cena Josh-Cena Jan 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's overkill. Passing in such a big entity makes this function very hard to reason about and optimize in the future. I prefer the initial implementation: the return type is a bit complicated, but it's lightweight and easy to be piped into other processing.

I reverted that refactor commit. Sorry for this but I do have some opinions on what sidebarUtils should do. It should strictly be a pure function operating on the sidebar and nothing else. Hooking into the version information is always overkill and should be done where it's actually needed.

| {
isAutoGeneratedIndex: false;
id: string;
}
| {
isAutoGeneratedIndex: true;
slug: string;
}
| undefined {
// eslint-disable-next-line no-restricted-syntax
for (const item of sidebar) {
if (item.type === 'doc') {
return {
isAutoGeneratedIndex: false,
id: item.id,
};
} else if (item.type === 'category') {
if (item.link?.type === 'doc') {
return {
isAutoGeneratedIndex: false,
id: item.link.id,
};
} else if (item.link?.type === 'generated-index') {
return {
isAutoGeneratedIndex: true,
slug: item.link.slug,
};
} else {
const doc = getFirstDocOfSidebar(item.items);
if (doc) {
return doc;
}
}
}
}
return undefined;
}

return {
sidebars,
getFirstDocIdOfFirstSidebar,
Expand All @@ -274,6 +323,7 @@ Available document ids are:
getCategoryGeneratedIndexList,
getCategoryGeneratedIndexNavigation,
checkSidebarsDocIds,
getFirstDocOfSidebar: (id) => getFirstDocOfSidebar(sidebars[id]),
};
}

Expand Down
6 changes: 6 additions & 0 deletions packages/docusaurus-plugin-content-docs/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,12 @@ export type GlobalVersion = {
path: string;
mainDocId: string; // home doc (if docs homepage configured), or first doc
docs: GlobalDoc[];
sidebars?: Record<string, GlobalSidebar>;
};

export type GlobalSidebar = {
link: string;
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved
// ... we may add other things here later
};

export type GlobalPluginData = {
Expand Down
16 changes: 15 additions & 1 deletion packages/docusaurus-theme-classic/src/theme-classic.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,10 +488,23 @@ declare module '@theme/NavbarItem/DocNavbarItem' {
export default DocsSidebarNavbarItem;
}

declare module '@theme/NavbarItem/DocSidebarNavbarItem' {
import type {Props as DefaultNavbarItemProps} from '@theme/NavbarItem/DefaultNavbarItem';

export interface Props extends DefaultNavbarItemProps {
readonly sidebarId: string;
readonly docsPluginId?: string;
}

const DocSidebarNavbarItem: (props: Props) => JSX.Element;
export default DocSidebarNavbarItem;
}

declare module '@theme/NavbarItem' {
import type {ComponentProps} from 'react';
import type {Props as DefaultNavbarItemProps} from '@theme/NavbarItem/DefaultNavbarItem';
import type {Props as DocNavbarItemProps} from '@theme/NavbarItem/DocNavbarItem';
import type {Props as DocSidebarNavbarItemProps} from '@theme/NavbarItem/DocSidebarNavbarItem';
import type {Props as DocsVersionNavbarItemProps} from '@theme/NavbarItem/DocsVersionNavbarItem';
import type {Props as DropdownNavbarItemProps} from '@theme/NavbarItem/DropdownNavbarItem';
import type {Props as DocsVersionDropdownNavbarItemProps} from '@theme/NavbarItem/DocsVersionDropdownNavbarItem';
Expand All @@ -501,7 +514,8 @@ declare module '@theme/NavbarItem' {
export type LinkLikeNavbarItemProps =
| ({readonly type?: 'default'} & DefaultNavbarItemProps)
| ({readonly type: 'doc'} & DocNavbarItemProps)
| ({readonly type: 'docsVersion'} & DocsVersionNavbarItemProps);
| ({readonly type: 'docsVersion'} & DocsVersionNavbarItemProps)
| ({readonly type: 'docSidebar'} & DocSidebarNavbarItemProps);

export type Props = ComponentProps<'a'> & {
readonly position?: 'left' | 'right';
Expand Down
Loading