mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-13 00:57:53 +02:00
feat(theme-classic): new navbar item linking to a sidebar (#6139)
Co-authored-by: Sébastien Lorber <slorber@users.noreply.github.com> Co-authored-by: Joshua Chen <sidachen2003@gmail.com> Co-authored-by: sebastienlorber <lorber.sebastien@gmail.com>
This commit is contained in:
parent
3cb99124de
commit
eade41a702
12 changed files with 396 additions and 13 deletions
|
@ -323,6 +323,14 @@ Object {
|
|||
"mainDocId": "hello",
|
||||
"name": "current",
|
||||
"path": "/docs",
|
||||
"sidebars": Object {
|
||||
"docs": Object {
|
||||
"link": Object {
|
||||
"label": "foo/bar",
|
||||
"path": "/docs/foo/bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -1007,6 +1015,14 @@ Object {
|
|||
"mainDocId": "hello",
|
||||
"name": "current",
|
||||
"path": "/docs",
|
||||
"sidebars": Object {
|
||||
"docs": Object {
|
||||
"link": Object {
|
||||
"label": "foo/bar",
|
||||
"path": "/docs/foo/bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -2359,6 +2375,14 @@ Object {
|
|||
"mainDocId": "team",
|
||||
"name": "current",
|
||||
"path": "/community/next",
|
||||
"sidebars": Object {
|
||||
"community": Object {
|
||||
"link": Object {
|
||||
"label": "team",
|
||||
"path": "/community/next/team",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"docs": Array [
|
||||
|
@ -2373,6 +2397,14 @@ Object {
|
|||
"mainDocId": "team",
|
||||
"name": "1.0.0",
|
||||
"path": "/community",
|
||||
"sidebars": Object {
|
||||
"version-1.0.0/community": Object {
|
||||
"link": Object {
|
||||
"label": "version-1.0.0/team",
|
||||
"path": "/community/team",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -3407,6 +3439,14 @@ Object {
|
|||
"mainDocId": "hello",
|
||||
"name": "current",
|
||||
"path": "/docs/next",
|
||||
"sidebars": Object {
|
||||
"docs": Object {
|
||||
"link": Object {
|
||||
"label": "foo/bar",
|
||||
"path": "/docs/next/foo/barSlug",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"docs": Array [
|
||||
|
@ -3426,6 +3466,14 @@ Object {
|
|||
"mainDocId": "hello",
|
||||
"name": "1.0.1",
|
||||
"path": "/docs",
|
||||
"sidebars": Object {
|
||||
"VersionedSideBarNameDoesNotMatter/docs": Object {
|
||||
"link": Object {
|
||||
"label": "foo/bar",
|
||||
"path": "/docs/foo/bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"docs": Array [
|
||||
|
@ -3450,6 +3498,14 @@ Object {
|
|||
"mainDocId": "hello",
|
||||
"name": "1.0.0",
|
||||
"path": "/docs/1.0.0",
|
||||
"sidebars": Object {
|
||||
"version-1.0.0/docs": Object {
|
||||
"link": Object {
|
||||
"label": "version-1.0.0/foo/bar",
|
||||
"path": "/docs/1.0.0/foo/barSlug",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"docs": Array [
|
||||
|
@ -3499,6 +3555,14 @@ Object {
|
|||
"mainDocId": "rootAbsoluteSlug",
|
||||
"name": "withSlugs",
|
||||
"path": "/docs/withSlugs",
|
||||
"sidebars": Object {
|
||||
"version-1.0.1/docs": Object {
|
||||
"link": Object {
|
||||
"label": "version-withSlugs/rootAbsoluteSlug",
|
||||
"path": "/docs/withSlugs/rootAbsoluteSlug",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -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 {
|
||||
|
@ -20,6 +25,31 @@ export function toGlobalDataDoc(doc: DocMetadata): GlobalDoc {
|
|||
};
|
||||
}
|
||||
|
||||
export function toGlobalSidebars(
|
||||
sidebars: Sidebars,
|
||||
version: LoadedVersion,
|
||||
): Record<string, GlobalSidebar> {
|
||||
const {getFirstLink} = createSidebarsUtils(sidebars);
|
||||
return mapValues(sidebars, (sidebar, sidebarId) => {
|
||||
const firstLink = getFirstLink(sidebarId);
|
||||
if (!firstLink) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
link: {
|
||||
path:
|
||||
firstLink.type === 'generated-index'
|
||||
? normalizeUrl([version.versionPath, firstLink.slug])
|
||||
: version.docs.find(
|
||||
(doc) =>
|
||||
doc.id === firstLink.id || doc.unversionedId === firstLink.id,
|
||||
)!.permalink,
|
||||
label: firstLink.label,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function toGlobalDataVersion(version: LoadedVersion): GlobalVersion {
|
||||
return {
|
||||
name: version.versionName,
|
||||
|
@ -28,5 +58,6 @@ export function toGlobalDataVersion(version: LoadedVersion): GlobalVersion {
|
|||
path: version.versionPath,
|
||||
mainDocId: version.mainDocId,
|
||||
docs: version.docs.map(toGlobalDataDoc),
|
||||
sidebars: toGlobalSidebars(version.sidebars, version),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -11,9 +11,10 @@ declare module '@docusaurus/plugin-content-docs' {
|
|||
export type VersionBanner = import('./types').VersionBanner;
|
||||
type GlobalDataVersion = import('./types').GlobalVersion;
|
||||
type GlobalDataDoc = import('./types').GlobalDoc;
|
||||
type GlobalDataSidebar = import('./types').GlobalSidebar;
|
||||
type VersionTag = import('./types').VersionTag;
|
||||
|
||||
export type {GlobalDataVersion, GlobalDataDoc};
|
||||
export type {GlobalDataVersion, GlobalDataDoc, GlobalDataSidebar};
|
||||
|
||||
export type PropNavigationLink = {
|
||||
readonly title: string;
|
||||
|
|
|
@ -46,7 +46,7 @@ describe('createSidebarsUtils', () => {
|
|||
collapsible: true,
|
||||
label: 'S2 Category',
|
||||
items: [
|
||||
{type: 'doc', id: 'doc3'},
|
||||
{type: 'doc', id: 'doc3', label: 'Doc 3'},
|
||||
{type: 'doc', id: 'doc4'},
|
||||
],
|
||||
},
|
||||
|
@ -95,7 +95,25 @@ 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,
|
||||
|
@ -103,6 +121,7 @@ describe('createSidebarsUtils', () => {
|
|||
getDocNavigation,
|
||||
getCategoryGeneratedIndexNavigation,
|
||||
getCategoryGeneratedIndexList,
|
||||
getFirstLink,
|
||||
} = createSidebarsUtils(sidebars);
|
||||
|
||||
test('getSidebarNameByDocId', async () => {
|
||||
|
@ -121,7 +140,7 @@ describe('createSidebarsUtils', () => {
|
|||
});
|
||||
|
||||
test('getDocNavigation', async () => {
|
||||
expect(getDocNavigation('doc1')).toEqual({
|
||||
expect(getDocNavigation('doc1', 'doc1')).toEqual({
|
||||
sidebarName: 'sidebar1',
|
||||
previous: undefined,
|
||||
next: {
|
||||
|
@ -129,7 +148,7 @@ describe('createSidebarsUtils', () => {
|
|||
id: 'doc2',
|
||||
},
|
||||
} as SidebarNavigation);
|
||||
expect(getDocNavigation('doc2')).toEqual({
|
||||
expect(getDocNavigation('doc2', 'doc2')).toEqual({
|
||||
sidebarName: 'sidebar1',
|
||||
previous: {
|
||||
type: 'doc',
|
||||
|
@ -138,7 +157,7 @@ describe('createSidebarsUtils', () => {
|
|||
next: undefined,
|
||||
} as SidebarNavigation);
|
||||
|
||||
expect(getDocNavigation('doc3')).toEqual({
|
||||
expect(getDocNavigation('doc3', 'doc3')).toEqual({
|
||||
sidebarName: 'sidebar2',
|
||||
previous: undefined,
|
||||
next: {
|
||||
|
@ -146,16 +165,17 @@ describe('createSidebarsUtils', () => {
|
|||
id: 'doc4',
|
||||
},
|
||||
} as SidebarNavigation);
|
||||
expect(getDocNavigation('doc4')).toEqual({
|
||||
expect(getDocNavigation('doc4', 'doc4')).toEqual({
|
||||
sidebarName: 'sidebar2',
|
||||
previous: {
|
||||
type: 'doc',
|
||||
id: 'doc3',
|
||||
label: 'Doc 3',
|
||||
},
|
||||
next: undefined,
|
||||
} as SidebarNavigation);
|
||||
|
||||
expect(getDocNavigation('doc5')).toMatchObject({
|
||||
expect(getDocNavigation('doc5', 'doc5')).toMatchObject({
|
||||
sidebarName: 'sidebar3',
|
||||
previous: undefined,
|
||||
next: {
|
||||
|
@ -163,7 +183,7 @@ describe('createSidebarsUtils', () => {
|
|||
label: 'S3 SubCategory',
|
||||
},
|
||||
} as SidebarNavigation);
|
||||
expect(getDocNavigation('doc6')).toMatchObject({
|
||||
expect(getDocNavigation('doc6', 'doc6')).toMatchObject({
|
||||
sidebarName: 'sidebar3',
|
||||
previous: {
|
||||
type: 'category',
|
||||
|
@ -174,7 +194,7 @@ describe('createSidebarsUtils', () => {
|
|||
id: 'doc7',
|
||||
},
|
||||
} as SidebarNavigation);
|
||||
expect(getDocNavigation('doc7')).toMatchObject({
|
||||
expect(getDocNavigation('doc7', 'doc7')).toMatchObject({
|
||||
sidebarName: 'sidebar3',
|
||||
previous: {
|
||||
type: 'doc',
|
||||
|
@ -224,8 +244,35 @@ describe('createSidebarsUtils', () => {
|
|||
type: 'category',
|
||||
label: 'S3 SubSubCategory',
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: 'S4 Category',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('getFirstLink', () => {
|
||||
expect(getFirstLink('sidebar1')).toEqual({
|
||||
id: 'doc1',
|
||||
type: 'doc',
|
||||
label: 'doc1',
|
||||
});
|
||||
expect(getFirstLink('sidebar2')).toEqual({
|
||||
id: 'doc3',
|
||||
type: 'doc',
|
||||
label: 'Doc 3',
|
||||
});
|
||||
expect(getFirstLink('sidebar3')).toEqual({
|
||||
id: 'doc5',
|
||||
type: 'doc',
|
||||
label: 'S3 Category',
|
||||
});
|
||||
expect(getFirstLink('sidebar4')).toEqual({
|
||||
type: 'generated-index',
|
||||
slug: '/s4-category-slug',
|
||||
label: 'S4 Category',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('collectSidebarDocItems', () => {
|
||||
|
|
|
@ -136,6 +136,18 @@ export type SidebarsUtils = {
|
|||
getCategoryGeneratedIndexNavigation: (
|
||||
categoryGeneratedIndexPermalink: string,
|
||||
) => SidebarNavigation;
|
||||
getFirstLink: (sidebarId: string) =>
|
||||
| {
|
||||
type: 'doc';
|
||||
id: string;
|
||||
label: string;
|
||||
}
|
||||
| {
|
||||
type: 'generated-index';
|
||||
slug: string;
|
||||
label: string;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
checkSidebarsDocIds: (validDocIds: string[], sidebarFilePath: string) => void;
|
||||
};
|
||||
|
@ -264,6 +276,50 @@ Available document ids are:
|
|||
}
|
||||
}
|
||||
|
||||
function getFirstLink(sidebar: Sidebar):
|
||||
| {
|
||||
type: 'doc';
|
||||
id: string;
|
||||
label: string;
|
||||
}
|
||||
| {
|
||||
type: 'generated-index';
|
||||
slug: string;
|
||||
label: string;
|
||||
}
|
||||
| undefined {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const item of sidebar) {
|
||||
if (item.type === 'doc') {
|
||||
return {
|
||||
type: 'doc',
|
||||
id: item.id,
|
||||
label: item.label ?? item.id,
|
||||
};
|
||||
} else if (item.type === 'category') {
|
||||
if (item.link?.type === 'doc') {
|
||||
return {
|
||||
type: 'doc',
|
||||
id: item.link.id,
|
||||
label: item.label,
|
||||
};
|
||||
} else if (item.link?.type === 'generated-index') {
|
||||
return {
|
||||
type: 'generated-index',
|
||||
slug: item.link.slug,
|
||||
label: item.label,
|
||||
};
|
||||
} else {
|
||||
const firstSubItem = getFirstLink(item.items);
|
||||
if (firstSubItem) {
|
||||
return firstSubItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
sidebars,
|
||||
getFirstDocIdOfFirstSidebar,
|
||||
|
@ -272,6 +328,7 @@ Available document ids are:
|
|||
getCategoryGeneratedIndexList,
|
||||
getCategoryGeneratedIndexNavigation,
|
||||
checkSidebarsDocIds,
|
||||
getFirstLink: (id) => getFirstLink(sidebars[id]),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -216,6 +216,17 @@ export type GlobalVersion = {
|
|||
path: string;
|
||||
mainDocId: string; // home doc (if docs homepage configured), or first doc
|
||||
docs: GlobalDoc[];
|
||||
sidebars?: Record<string, GlobalSidebar>;
|
||||
};
|
||||
|
||||
export type GlobalSidebarLink = {
|
||||
label: string;
|
||||
path: string;
|
||||
};
|
||||
|
||||
export type GlobalSidebar = {
|
||||
link?: GlobalSidebarLink;
|
||||
// ... we may add other things here later
|
||||
};
|
||||
|
||||
export type GlobalPluginData = {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue