diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/__snapshots__/normalization.test.ts.snap b/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/__snapshots__/normalization.test.ts.snap new file mode 100644 index 0000000000..91efc153bb --- /dev/null +++ b/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/__snapshots__/normalization.test.ts.snap @@ -0,0 +1,96 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`normalization normalizes shorthands 1`] = ` +Object { + "sidebar": Array [ + Object { + "items": Array [ + Object { + "id": "doc1", + "type": "doc", + }, + Object { + "id": "doc2", + "type": "doc", + }, + ], + "label": "Category", + "type": "category", + }, + Object { + "items": Array [ + Object { + "items": Array [ + Object { + "id": "doc3", + "type": "doc", + }, + Object { + "id": "doc4", + "type": "doc", + }, + ], + "label": "Subcategory 1", + "type": "category", + }, + Object { + "items": Array [ + Object { + "id": "doc5", + "type": "doc", + }, + Object { + "id": "doc6", + "type": "doc", + }, + ], + "label": "Subcategory 2", + "type": "category", + }, + ], + "label": "Category 2", + "type": "category", + }, + ], +} +`; + +exports[`normalization normalizes shorthands 2`] = ` +Object { + "sidebar": Array [ + Object { + "href": "https://google.com", + "label": "Google", + "type": "link", + }, + Object { + "items": Array [ + Object { + "id": "doc1", + "type": "doc", + }, + Object { + "id": "doc2", + "type": "doc", + }, + ], + "label": "Category 1", + "type": "category", + }, + Object { + "items": Array [ + Object { + "id": "doc3", + "type": "doc", + }, + Object { + "id": "doc4", + "type": "doc", + }, + ], + "label": "Category 2", + "type": "category", + }, + ], +} +`; diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/index.test.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/index.test.ts index 96e18a9e86..8b259d8fcd 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/index.test.ts @@ -58,7 +58,7 @@ describe('loadSidebars', () => { await expect(() => loadSidebars(sidebarPath, params), ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Invalid category {\\"type\\":\\"category\\",\\"label\\":\\"Category Label\\",\\"items\\":\\"doc1\\"}: items must be an array"`, + `"Invalid category {\\"type\\":\\"category\\",\\"label\\":\\"Category Label\\",\\"items\\":\\"doc1\\"}: items must be an array of sidebar items or a category shorthand"`, ); }); diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/normalization.test.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/normalization.test.ts new file mode 100644 index 0000000000..23234b60de --- /dev/null +++ b/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/normalization.test.ts @@ -0,0 +1,40 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {normalizeSidebars} from '../normalization'; + +describe('normalization', () => { + test('normalizes shorthands', () => { + expect( + normalizeSidebars({ + sidebar: { + Category: ['doc1', 'doc2'], + 'Category 2': { + 'Subcategory 1': ['doc3', 'doc4'], + 'Subcategory 2': ['doc5', 'doc6'], + }, + }, + }), + ).toMatchSnapshot(); + + expect( + normalizeSidebars({ + sidebar: [ + { + type: 'link', + label: 'Google', + href: 'https://google.com', + }, + { + 'Category 1': ['doc1', 'doc2'], + 'Category 2': ['doc3', 'doc4'], + }, + ], + }), + ).toMatchSnapshot(); + }); +}); diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/normalization.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/normalization.ts index 6fa04a8102..60c7b80405 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars/normalization.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars/normalization.ts @@ -50,14 +50,20 @@ export function normalizeItem( ); } if (item.type === 'category') { - if (typeof item.items !== 'undefined' && !Array.isArray(item.items)) { + if (typeof item.items !== 'undefined' && typeof item.items !== 'object') { throw new Error( - `Invalid category ${JSON.stringify(item)}: items must be an array`, + `Invalid category ${JSON.stringify( + item, + )}: items must be an array of sidebar items or a category shorthand`, ); } const normalizedCategory: NormalizedSidebarItemCategory = { ...item, - items: item.items.flatMap((subItem) => normalizeItem(subItem)), + items: Array.isArray(item.items) + ? item.items.flatMap((subItem) => normalizeItem(subItem)) + : normalizeCategoriesShorthand(item.items).flatMap((subItem) => + normalizeItem(subItem), + ), }; return [normalizedCategory]; } diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/types.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/types.ts index 7a3d4fb5a5..620344c4ed 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars/types.ts @@ -83,13 +83,13 @@ export type SidebarItemCategoryLink = // The user-given configuration in sidebars.js, before normalization export type SidebarItemCategoryConfig = Expand< Optional & { - items: SidebarItemConfig[]; + items: SidebarCategoriesShorthand | SidebarItemConfig[]; link?: SidebarItemCategoryLinkConfig; } >; export type SidebarCategoriesShorthand = { - [sidebarCategory: string]: SidebarItemConfig[]; + [sidebarCategory: string]: SidebarCategoriesShorthand | SidebarItemConfig[]; }; export type SidebarItemConfig = diff --git a/website/docs/guides/docs/sidebar/items.md b/website/docs/guides/docs/sidebar/items.md index 248f684f9c..456c6e603b 100644 --- a/website/docs/guides/docs/sidebar/items.md +++ b/website/docs/guides/docs/sidebar/items.md @@ -3,6 +3,11 @@ toc_max_heading_level: 4 slug: /sidebar/items --- +```mdx-code-block +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +``` + # Sidebar items We have introduced three types of item types in the example in the previous section: `doc`, `category`, and `link`, whose usages are fairly intuitive. We will formally introduce their APIs. There's also a fourth type: `autogenerated`, which we will explain in detail later. @@ -101,6 +106,55 @@ module.exports = { }; ``` +## HTML: render custom markup {#sidebar-item-html} + +Use the `html` type to render custom HTML within the item's `
  • ` tag. + +This can be useful for inserting custom items such as dividers, section titles, ads, and images. + +```ts +type SidebarItemHtml = { + type: 'html'; + value: string; + defaultStyle?: boolean; // Use default menu item styles + className?: string; +}; +``` + +Example: + +```js title="sidebars.js" +module.exports = { + myHtmlSidebar: [ + // highlight-start + { + type: 'html', + value: 'Sponsor', // The HTML to be rendered + defaultStyle: true, // Use the default menu item styling + }, + // highlight-end + ], +}; +``` + +:::tip + +The menu item is already wrapped in an `
  • ` tag, so if your custom item is simple, such as a title, just supply a string as the value and use the `className` property to style it: + +```js title="sidebars.js" +module.exports = { + myHtmlSidebar: [ + { + type: 'html', + value: 'Core concepts', + className: 'sidebar-title', + }, + ], +}; +``` + +::: + ## Category: create a hierarchy {#sidebar-item-category} Use the `category` type to create a hierarchy of sidebar items. @@ -161,55 +215,6 @@ module.exports = { ::: -## HTML: render custom markup {#sidebar-item-html} - -Use the `html` type to render custom HTML within the item's `
  • ` tag. - -This can be useful for inserting custom items such as dividers, section titles, ads, and images. - -```ts -type SidebarItemHtml = { - type: 'html'; - value: string; - defaultStyle?: boolean; // Use default menu item styles - className?: string; -}; -``` - -Example: - -```js title="sidebars.js" -module.exports = { - myHtmlSidebar: [ - // highlight-start - { - type: 'html', - value: 'Sponsor', // The HTML to be rendered - defaultStyle: true, // Use the default menu item styling - }, - // highlight-end - ], -}; -``` - -:::tip - -The menu item is already wrapped in an `
  • ` tag, so if your custom item is simple, such as a title, just supply a string as the value and use the `className` property to style it: - -```js title="sidebars.js" -module.exports = { - myHtmlSidebar: [ - { - type: 'html', - value: 'Core concepts', - className: 'sidebar-title', - }, - ], -}; -``` - -::: - ### Category links {#category-link} With category links, clicking on a category can navigate you to another page. @@ -390,20 +395,38 @@ You can express typical sidebar items without much customization more concisely An item with type `doc` can be simply a string representing its ID: -```js -// ================= -// This item: -// ================= -{ - type: 'doc', - id: 'myDoc', + + + +```js title="sidebars.js" +module.exports = { + sidebar: [ + // highlight-start + { + type: 'doc', + id: 'myDoc', + }, + // highlight-end + ], }; -// ================= -// Is equivalent to: -// ================= -'myDoc'; ``` + + + +```js title="sidebars.js" +module.exports = { + sidebar: [ + // highlight-start + 'myDoc', + // highlight-end + ], +}; +``` + + + + So it's possible to simplify the example above to: ```js title="sidebars.js" @@ -440,23 +463,41 @@ module.exports = { A category item can be represented by an object whose key is its label, and the value is an array of subitems. -```js -// =================== -// This item: -// =================== -{ - type: 'category', - label: 'Getting started', - items: ['doc1', 'doc2'], -}; -// =================== -// Is equivalent to: -// =================== -{ - 'Getting started': ['doc1', 'doc2'], + + + +```js title="sidebars.js" +module.exports = { + sidebar: [ + // highlight-start + { + type: 'category', + label: 'Getting started', + items: ['doc1', 'doc2'], + }, + // highlight-end + ], }; ``` + + + +```js title="sidebars.js" +module.exports = { + sidebar: [ + // highlight-start + { + 'Getting started': ['doc1', 'doc2'], + }, + // highlight-end + ], +}; +``` + + + + This permits us to simplify that example to: ```js title="sidebars.js" @@ -500,3 +541,42 @@ module.exports = { ``` Note how the two consecutive category shorthands are compressed into one object with two entries. This syntax generates a **sidebar slice**: you shouldn't see that object as one bulk item—this object is unwrapped, with each entry becoming a separate item, and they spliced together with the rest of the items (in this case, the "Learn more" link) to form the final sidebar level. Sidebar slices are also important when discussing [autogenerated sidebars](autogenerated.md). + +Wherever you have an array of items that is reduced to one category shorthand, you can omit that enclosing array as well. + + + + +```js title="sidebars.js" +module.exports = { + sidebar: [ + { + 'Getting started': ['doc1'], + Docusaurus: [ + { + 'Basic guides': ['doc2', 'doc3'], + 'Advanced guides': ['doc4', 'doc5'], + }, + ], + }, + ], +}; +``` + + + + +```js title="sidebars.js" +module.exports = { + sidebar: { + 'Getting started': ['doc1'], + Docusaurus: { + 'Basic guides': ['doc2', 'doc3'], + 'Advanced guides': ['doc4', 'doc5'], + }, + }, +}; +``` + + +