From 3e08c727160e204633723cb8550f95b7f86774f3 Mon Sep 17 00:00:00 2001 From: Jody Heavener Date: Sun, 30 Jan 2022 15:09:12 -0400 Subject: [PATCH 01/12] feat: add breadcrumbs hook, component to classic theme --- .../src/plugin-content-docs.d.ts | 4 ++ .../src/__tests__/validateThemeConfig.test.ts | 10 +++ .../src/theme/DocBreadcrumbs/index.tsx | 69 +++++++++++++++++++ .../theme/DocBreadcrumbs/styles.module.css | 26 +++++++ .../DocCategoryGeneratedIndexPage/index.tsx | 2 + .../src/theme/DocItem/index.tsx | 2 + .../src/validateThemeConfig.ts | 5 ++ packages/docusaurus-theme-common/src/index.ts | 1 + .../src/utils/ThemeClassNames.ts | 1 + .../src/utils/docsUtils.tsx | 34 +++++++++ .../src/utils/useThemeConfig.ts | 3 + .../docs/api/themes/theme-configuration.md | 25 +++++++ 12 files changed, 182 insertions(+) create mode 100644 packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx create mode 100644 packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/styles.module.css diff --git a/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts b/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts index c63dc522442e..29f721f5eea3 100644 --- a/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts +++ b/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts @@ -237,6 +237,10 @@ declare module '@theme/DocTagDocListPage' { export default function DocTagDocListPage(props: Props): JSX.Element; } +declare module '@theme/DocBreadcrumbs' { + export default function DocBreadcrumbs(): JSX.Element; +} + declare module '@theme/DocPage' { import type {PropVersionMetadata} from '@docusaurus/plugin-content-docs'; import type {DocumentRoute} from '@theme/DocItem'; diff --git a/packages/docusaurus-theme-classic/src/__tests__/validateThemeConfig.test.ts b/packages/docusaurus-theme-classic/src/__tests__/validateThemeConfig.test.ts index 67a2ec87a285..d8a7de49b923 100644 --- a/packages/docusaurus-theme-classic/src/__tests__/validateThemeConfig.test.ts +++ b/packages/docusaurus-theme-classic/src/__tests__/validateThemeConfig.test.ts @@ -701,4 +701,14 @@ describe('themeConfig tableOfContents', () => { `"\\"tableOfContents.minHeadingLevel\\" must be less than or equal to ref:maxHeadingLevel"`, ); }); + + test('should accept nested breadcrumb config', () => { + const breadcrumbConfig = { + breadcrumbs: 'nested', + }; + expect(testValidateThemeConfig(breadcrumbConfig)).toEqual({ + ...DEFAULT_CONFIG, + ...breadcrumbConfig, + }); + }); }); diff --git a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx new file mode 100644 index 000000000000..0f835bca6d09 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx @@ -0,0 +1,69 @@ +/** + * 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 React from 'react'; +import { + ThemeClassNames, + useDocsBreadcrumbs, + useThemeConfig, +} from '@docusaurus/theme-common'; +import styles from './styles.module.css'; +import clsx from 'clsx'; +import {useLocation} from '@docusaurus/router'; + +export default function DocBreadcrumbs(): JSX.Element | null { + const {pathname} = useLocation(); + const breadcrumbs = useDocsBreadcrumbs(); + const {breadcrumbs: enabled} = useThemeConfig(); + + function isExact( + items: { + href?: string; + }[], + ) { + return ( + items.length === 1 && + items[0].href?.replace(/\/$/, '') === pathname.replace(/\/$/, '') + ); + } + + if ( + !breadcrumbs.length || + enabled === false || + (enabled === 'nested' && isExact(breadcrumbs)) + ) { + return null; + } + + return ( + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/styles.module.css b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/styles.module.css new file mode 100644 index 000000000000..e02c4741dc78 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/styles.module.css @@ -0,0 +1,26 @@ +/** + * 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. + */ + +.breadcrumbsContainer { + margin-bottom: 1rem; +} + +.breadcrumbItem { + --ifm-breadcrumb-size-multiplier: 0.75; + margin-bottom: 0.5rem; + background: var(--ifm-color-gray-100); +} + +html[data-theme='dark'] .breadcrumbItem { + background-color: var(--ifm-color-gray-900); +} + +@media (min-width: 997px) { + .breadcrumbItem { + --ifm-breadcrumb-size-multiplier: 0.8; + } +} diff --git a/packages/docusaurus-theme-classic/src/theme/DocCategoryGeneratedIndexPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocCategoryGeneratedIndexPage/index.tsx index 1214a86aab3f..0d6f3e5ffe7d 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocCategoryGeneratedIndexPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocCategoryGeneratedIndexPage/index.tsx @@ -13,6 +13,7 @@ import DocPaginator from '@theme/DocPaginator'; import Seo from '@theme/Seo'; import DocVersionBanner from '@theme/DocVersionBanner'; import DocVersionBadge from '@theme/DocVersionBadge'; +import DocBreadcrumbs from '@theme/DocBreadcrumbs'; import Heading from '@theme/Heading'; import useBaseUrl from '@docusaurus/useBaseUrl'; @@ -33,6 +34,7 @@ export default function DocCategoryGeneratedIndexPage({ />
+
diff --git a/packages/docusaurus-theme-classic/src/theme/DocItem/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocItem/index.tsx index d595bb186f6d..69befc86231a 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocItem/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocItem/index.tsx @@ -18,6 +18,7 @@ import TOCCollapsible from '@theme/TOCCollapsible'; import Heading from '@theme/Heading'; import styles from './styles.module.css'; import {ThemeClassNames, useWindowSize} from '@docusaurus/theme-common'; +import DocBreadcrumbs from '@theme/DocBreadcrumbs'; export default function DocItem(props: Props): JSX.Element { const {content: DocContent} = props; @@ -58,6 +59,7 @@ export default function DocItem(props: Props): JSX.Element {
+ {canRenderTOC && ( diff --git a/packages/docusaurus-theme-classic/src/validateThemeConfig.ts b/packages/docusaurus-theme-classic/src/validateThemeConfig.ts index a3c9a3bbc368..753d8956d820 100644 --- a/packages/docusaurus-theme-classic/src/validateThemeConfig.ts +++ b/packages/docusaurus-theme-classic/src/validateThemeConfig.ts @@ -46,6 +46,7 @@ const DEFAULT_CONFIG = { minHeadingLevel: 2, maxHeadingLevel: 3, }, + breadcrumbs: 'nested', }; const NavbarItemPosition = Joi.string().equal('left', 'right').default('left'); @@ -384,6 +385,10 @@ const ThemeConfigSchema = Joi.object({ .max(6) .default(DEFAULT_CONFIG.tableOfContents.maxHeadingLevel), }).default(DEFAULT_CONFIG.tableOfContents), + breadcrumbs: Joi.alternatives( + Joi.bool(), + Joi.string().valid('nested'), + ).default(DEFAULT_CONFIG.breadcrumbs), }); export {DEFAULT_CONFIG, ThemeConfigSchema}; diff --git a/packages/docusaurus-theme-common/src/index.ts b/packages/docusaurus-theme-common/src/index.ts index 980738e13747..df4b609b550f 100644 --- a/packages/docusaurus-theme-common/src/index.ts +++ b/packages/docusaurus-theme-common/src/index.ts @@ -49,6 +49,7 @@ export { findFirstCategoryLink, useCurrentSidebarCategory, isActiveSidebarItem, + useDocsBreadcrumbs, } from './utils/docsUtils'; export {isSamePath} from './utils/pathUtils'; diff --git a/packages/docusaurus-theme-common/src/utils/ThemeClassNames.ts b/packages/docusaurus-theme-common/src/utils/ThemeClassNames.ts index fea5ac7d4e29..e5b49926357f 100644 --- a/packages/docusaurus-theme-common/src/utils/ThemeClassNames.ts +++ b/packages/docusaurus-theme-common/src/utils/ThemeClassNames.ts @@ -43,6 +43,7 @@ export const ThemeClassNames = { docs: { docVersionBanner: 'theme-doc-version-banner', docVersionBadge: 'theme-doc-version-badge', + docBreadcrumbs: 'theme-doc-breadcrumbs', docMarkdown: 'theme-doc-markdown', docTocMobile: 'theme-doc-toc-mobile', docTocDesktop: 'theme-doc-toc-desktop', diff --git a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx index 8be209b921e3..630b212dab91 100644 --- a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx +++ b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx @@ -17,6 +17,11 @@ import type { import {isSamePath} from './pathUtils'; import {useLocation} from '@docusaurus/router'; +type BreadcrumbItem = { + label: string; + href?: string; +}; + // TODO not ideal, see also "useDocs" export const isDocsPluginEnabled: boolean = !!useAllDocsData; @@ -181,3 +186,32 @@ export function isActiveSidebarItem( return false; } + +export function useDocsBreadcrumbs(): BreadcrumbItem[] { + const sidebar = useDocsSidebar(); + const {pathname} = useLocation(); + const breadcrumbs: BreadcrumbItem[] = []; + + function extract(items: PropSidebar) { + // eslint-disable-next-line no-restricted-syntax + for (const item of items) { + if (item.type === 'category' && extract(item.items)) { + breadcrumbs.push({label: item.label, href: item.href}); + return true; + } else if ( + item.href?.replace(/\/$/, '') === pathname.replace(/\/$/, '') + ) { + breadcrumbs.push({label: item.label, href: item.href}); + return true; + } + } + + return false; + } + + if (sidebar) { + extract(sidebar); + } + + return breadcrumbs.reverse(); +} diff --git a/packages/docusaurus-theme-common/src/utils/useThemeConfig.ts b/packages/docusaurus-theme-common/src/utils/useThemeConfig.ts index 26975d2563ce..4a336f203263 100644 --- a/packages/docusaurus-theme-common/src/utils/useThemeConfig.ts +++ b/packages/docusaurus-theme-common/src/utils/useThemeConfig.ts @@ -105,6 +105,8 @@ export type TableOfContents = { maxHeadingLevel: number; }; +export type Breadcrumbs = boolean | 'nested'; + // Theme config after validation/normalization export type ThemeConfig = { docs: { @@ -127,6 +129,7 @@ export type ThemeConfig = { metadata: Array>; sidebarCollapsible: boolean; tableOfContents: TableOfContents; + breadcrumbs: Breadcrumbs; }; // User-provided theme config, unnormalized diff --git a/website/docs/api/themes/theme-configuration.md b/website/docs/api/themes/theme-configuration.md index e79cf89614cf..aa3a1e0a22ec 100644 --- a/website/docs/api/themes/theme-configuration.md +++ b/website/docs/api/themes/theme-configuration.md @@ -159,6 +159,31 @@ module.exports = { }; ``` +### Breadcrumbs {#breadcrumbs} + +Breadcrumbs can be rendered on each document page, based on sidebar items. + +Accepted fields: + + + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| `breadcrumbs` | boolean \| 'nested' | `nested` | Use boolean value to strictly enable or disable all breadcrumbs, or use `'nested'` when you only want breadcrumbs visible in nested pages. | + + + +Example configuration: + +```js title="docusaurus.config.js" +module.exports = { + themeConfig: { + // highlight-next-line + breadcrumbs: 'nested', + }, +}; +``` + ## Navbar {#navbar} Accepted fields: From 590ee53120471f01a953bf75263b153a34264ff0 Mon Sep 17 00:00:00 2001 From: Jody Heavener Date: Thu, 3 Feb 2022 20:24:54 -0400 Subject: [PATCH 02/12] use PropSidebar instead of custom BreadcrumbItem type, use isSamePath --- .../src/theme/DocBreadcrumbs/index.tsx | 17 +++++++++-------- .../src/utils/docsUtils.tsx | 17 +++++------------ 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx index 0f835bca6d09..1f45741cc029 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { + isSamePath, ThemeClassNames, useDocsBreadcrumbs, useThemeConfig, @@ -14,20 +15,19 @@ import { import styles from './styles.module.css'; import clsx from 'clsx'; import {useLocation} from '@docusaurus/router'; +import type {PropSidebar} from '@docusaurus/plugin-content-docs'; export default function DocBreadcrumbs(): JSX.Element | null { const {pathname} = useLocation(); const breadcrumbs = useDocsBreadcrumbs(); const {breadcrumbs: enabled} = useThemeConfig(); - function isExact( - items: { - href?: string; - }[], - ) { + function isExact(items: PropSidebar) { + const singleItem = items[0]; return ( items.length === 1 && - items[0].href?.replace(/\/$/, '') === pathname.replace(/\/$/, '') + singleItem.type === 'link' && + isSamePath(singleItem.href, pathname) ); } @@ -49,13 +49,14 @@ export default function DocBreadcrumbs(): JSX.Element | null {
    {breadcrumbs.map((breadcrumb, idx) => (
  • - {breadcrumb.href ? ( + {breadcrumb.type === 'link' && ( {breadcrumb.label} - ) : ( + )} + {breadcrumb.type === 'category' && ( {breadcrumb.label} diff --git a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx index 630b212dab91..fc442c9e9f90 100644 --- a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx +++ b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx @@ -17,11 +17,6 @@ import type { import {isSamePath} from './pathUtils'; import {useLocation} from '@docusaurus/router'; -type BreadcrumbItem = { - label: string; - href?: string; -}; - // TODO not ideal, see also "useDocs" export const isDocsPluginEnabled: boolean = !!useAllDocsData; @@ -187,21 +182,19 @@ export function isActiveSidebarItem( return false; } -export function useDocsBreadcrumbs(): BreadcrumbItem[] { +export function useDocsBreadcrumbs(): PropSidebar { const sidebar = useDocsSidebar(); const {pathname} = useLocation(); - const breadcrumbs: BreadcrumbItem[] = []; + const breadcrumbs: PropSidebar = []; function extract(items: PropSidebar) { // eslint-disable-next-line no-restricted-syntax for (const item of items) { if (item.type === 'category' && extract(item.items)) { - breadcrumbs.push({label: item.label, href: item.href}); + breadcrumbs.push(item); return true; - } else if ( - item.href?.replace(/\/$/, '') === pathname.replace(/\/$/, '') - ) { - breadcrumbs.push({label: item.label, href: item.href}); + } else if (item.type === 'link' && isSamePath(item.href, pathname)) { + breadcrumbs.push(item); return true; } } From b1a76d5fe8500fc675c08f755ce20dee0e2d434f Mon Sep 17 00:00:00 2001 From: Jody Heavener Date: Fri, 4 Feb 2022 15:13:58 -0400 Subject: [PATCH 03/12] rename useDocsBreadcrumbs to useSidebarBreadcrumbs --- .../src/theme/DocBreadcrumbs/index.tsx | 4 ++-- packages/docusaurus-theme-common/src/index.ts | 2 +- packages/docusaurus-theme-common/src/utils/docsUtils.tsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx index 1f45741cc029..c707a0a1bb38 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { isSamePath, ThemeClassNames, - useDocsBreadcrumbs, + useSidebarBreadcrumbs, useThemeConfig, } from '@docusaurus/theme-common'; import styles from './styles.module.css'; @@ -19,7 +19,7 @@ import type {PropSidebar} from '@docusaurus/plugin-content-docs'; export default function DocBreadcrumbs(): JSX.Element | null { const {pathname} = useLocation(); - const breadcrumbs = useDocsBreadcrumbs(); + const breadcrumbs = useSidebarBreadcrumbs(); const {breadcrumbs: enabled} = useThemeConfig(); function isExact(items: PropSidebar) { diff --git a/packages/docusaurus-theme-common/src/index.ts b/packages/docusaurus-theme-common/src/index.ts index df4b609b550f..ebcb240c3611 100644 --- a/packages/docusaurus-theme-common/src/index.ts +++ b/packages/docusaurus-theme-common/src/index.ts @@ -49,7 +49,7 @@ export { findFirstCategoryLink, useCurrentSidebarCategory, isActiveSidebarItem, - useDocsBreadcrumbs, + useSidebarBreadcrumbs, } from './utils/docsUtils'; export {isSamePath} from './utils/pathUtils'; diff --git a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx index fc442c9e9f90..64e319a075e9 100644 --- a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx +++ b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx @@ -182,7 +182,7 @@ export function isActiveSidebarItem( return false; } -export function useDocsBreadcrumbs(): PropSidebar { +export function useSidebarBreadcrumbs(): PropSidebar { const sidebar = useDocsSidebar(); const {pathname} = useLocation(); const breadcrumbs: PropSidebar = []; From bc5287de205ba926b58cc655617c0d90d22129f7 Mon Sep 17 00:00:00 2001 From: Jody Heavener Date: Fri, 11 Feb 2022 10:51:47 -0400 Subject: [PATCH 04/12] Refactor breadcrumbs to use plugin-based config instead of theme, house logic in hook --- .../__snapshots__/index.test.ts.snap | 4 +++ .../src/__tests__/options.test.ts | 1 + .../src/index.ts | 2 ++ .../src/options.ts | 5 ++++ .../src/plugin-content-docs.d.ts | 2 ++ .../src/__tests__/validateThemeConfig.test.ts | 10 ------- .../src/theme/DocBreadcrumbs/index.tsx | 26 ++---------------- .../src/validateThemeConfig.ts | 5 ---- .../src/utils/docsUtils.tsx | 27 ++++++++++++++++--- .../src/utils/useThemeConfig.ts | 3 --- 10 files changed, 40 insertions(+), 45 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap index bba2e20e8e5a..88fb0433601c 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap @@ -242,6 +242,7 @@ exports[`simple website content 5`] = ` Object { "pluginName": Object { "pluginId": Object { + "breadcrumbs": "nested", "path": "/docs", "versions": Array [ Object { @@ -955,6 +956,7 @@ exports[`simple website content: global data 1`] = ` Object { "pluginName": Object { "pluginId": Object { + "breadcrumbs": "nested", "path": "/docs", "versions": Array [ Object { @@ -2411,6 +2413,7 @@ exports[`versioned website (community) content: global data 1`] = ` Object { "pluginName": Object { "pluginId": Object { + "breadcrumbs": "nested", "path": "/community", "versions": Array [ Object { @@ -3450,6 +3453,7 @@ exports[`versioned website content: global data 1`] = ` Object { "pluginName": Object { "pluginId": Object { + "breadcrumbs": "nested", "path": "/docs", "versions": Array [ Object { diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts index a4d94d4c36a5..5ef88f9e26ba 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts @@ -56,6 +56,7 @@ describe('normalizeDocsPluginOptions', () => { rehypePlugins: [markdownPluginsFunctionStub], beforeDefaultRehypePlugins: [], beforeDefaultRemarkPlugins: [], + breadcrumbs: 'nested', showLastUpdateTime: true, showLastUpdateAuthor: true, admonitions: {}, diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index df71049e602b..9aeac134bdd9 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -217,6 +217,7 @@ export default async function pluginContentDocs( docLayoutComponent, docItemComponent, docCategoryGeneratedIndexComponent, + breadcrumbs, } = options; const {addRoute, createData, setGlobalData} = actions; @@ -295,6 +296,7 @@ export default async function pluginContentDocs( setGlobalData({ path: normalizeUrl([baseUrl, options.routeBasePath]), versions: loadedVersions.map(toGlobalDataVersion), + breadcrumbs, }); }, diff --git a/packages/docusaurus-plugin-content-docs/src/options.ts b/packages/docusaurus-plugin-content-docs/src/options.ts index 7c79e5ac02d5..520652578c2b 100644 --- a/packages/docusaurus-plugin-content-docs/src/options.ts +++ b/packages/docusaurus-plugin-content-docs/src/options.ts @@ -55,6 +55,7 @@ export const DEFAULT_OPTIONS: Omit = { editLocalizedFiles: false, sidebarCollapsible: true, sidebarCollapsed: true, + breadcrumbs: 'nested', }; const VersionOptionsSchema = Joi.object({ @@ -139,6 +140,10 @@ export const OptionsSchema = Joi.object({ disableVersioning: Joi.bool().default(DEFAULT_OPTIONS.disableVersioning), lastVersion: Joi.string().optional(), versions: VersionsOptionsSchema, + breadcrumbs: Joi.alternatives( + Joi.bool(), + Joi.string().valid('nested'), + ).default(DEFAULT_OPTIONS.breadcrumbs), }); export function validateOptions({ diff --git a/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts b/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts index 29f721f5eea3..e140b05c95d3 100644 --- a/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts +++ b/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts @@ -38,6 +38,7 @@ declare module '@docusaurus/plugin-content-docs' { showLastUpdateTime?: boolean; showLastUpdateAuthor?: boolean; numberPrefixParser: NumberPrefixParser; + breadcrumbs: boolean | 'nested'; }; export type PathOptions = { @@ -298,6 +299,7 @@ declare module '@docusaurus/plugin-content-docs/client' { export type GlobalPluginData = { path: string; versions: GlobalVersion[]; + breadcrumbs: boolean | 'nested'; }; export type DocVersionSuggestions = { // suggest the latest version diff --git a/packages/docusaurus-theme-classic/src/__tests__/validateThemeConfig.test.ts b/packages/docusaurus-theme-classic/src/__tests__/validateThemeConfig.test.ts index d8a7de49b923..67a2ec87a285 100644 --- a/packages/docusaurus-theme-classic/src/__tests__/validateThemeConfig.test.ts +++ b/packages/docusaurus-theme-classic/src/__tests__/validateThemeConfig.test.ts @@ -701,14 +701,4 @@ describe('themeConfig tableOfContents', () => { `"\\"tableOfContents.minHeadingLevel\\" must be less than or equal to ref:maxHeadingLevel"`, ); }); - - test('should accept nested breadcrumb config', () => { - const breadcrumbConfig = { - breadcrumbs: 'nested', - }; - expect(testValidateThemeConfig(breadcrumbConfig)).toEqual({ - ...DEFAULT_CONFIG, - ...breadcrumbConfig, - }); - }); }); diff --git a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx index c707a0a1bb38..bcd994419fb8 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx @@ -6,36 +6,14 @@ */ import React from 'react'; -import { - isSamePath, - ThemeClassNames, - useSidebarBreadcrumbs, - useThemeConfig, -} from '@docusaurus/theme-common'; +import {ThemeClassNames, useSidebarBreadcrumbs} from '@docusaurus/theme-common'; import styles from './styles.module.css'; import clsx from 'clsx'; -import {useLocation} from '@docusaurus/router'; -import type {PropSidebar} from '@docusaurus/plugin-content-docs'; export default function DocBreadcrumbs(): JSX.Element | null { - const {pathname} = useLocation(); const breadcrumbs = useSidebarBreadcrumbs(); - const {breadcrumbs: enabled} = useThemeConfig(); - function isExact(items: PropSidebar) { - const singleItem = items[0]; - return ( - items.length === 1 && - singleItem.type === 'link' && - isSamePath(singleItem.href, pathname) - ); - } - - if ( - !breadcrumbs.length || - enabled === false || - (enabled === 'nested' && isExact(breadcrumbs)) - ) { + if (!breadcrumbs) { return null; } diff --git a/packages/docusaurus-theme-classic/src/validateThemeConfig.ts b/packages/docusaurus-theme-classic/src/validateThemeConfig.ts index 753d8956d820..a3c9a3bbc368 100644 --- a/packages/docusaurus-theme-classic/src/validateThemeConfig.ts +++ b/packages/docusaurus-theme-classic/src/validateThemeConfig.ts @@ -46,7 +46,6 @@ const DEFAULT_CONFIG = { minHeadingLevel: 2, maxHeadingLevel: 3, }, - breadcrumbs: 'nested', }; const NavbarItemPosition = Joi.string().equal('left', 'right').default('left'); @@ -385,10 +384,6 @@ const ThemeConfigSchema = Joi.object({ .max(6) .default(DEFAULT_CONFIG.tableOfContents.maxHeadingLevel), }).default(DEFAULT_CONFIG.tableOfContents), - breadcrumbs: Joi.alternatives( - Joi.bool(), - Joi.string().valid('nested'), - ).default(DEFAULT_CONFIG.breadcrumbs), }); export {DEFAULT_CONFIG, ThemeConfigSchema}; diff --git a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx index 64e319a075e9..8932fb29a32e 100644 --- a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx +++ b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx @@ -6,7 +6,10 @@ */ import React, {createContext, type ReactNode, useContext} from 'react'; -import {useAllDocsData} from '@docusaurus/plugin-content-docs/client'; +import { + useActivePlugin, + useAllDocsData, +} from '@docusaurus/plugin-content-docs/client'; import type { PropSidebar, PropSidebarItem, @@ -182,13 +185,13 @@ export function isActiveSidebarItem( return false; } -export function useSidebarBreadcrumbs(): PropSidebar { +export function useSidebarBreadcrumbs(): PropSidebar | null { const sidebar = useDocsSidebar(); const {pathname} = useLocation(); const breadcrumbs: PropSidebar = []; + const enabled = useActivePlugin()?.pluginData?.breadcrumbs; function extract(items: PropSidebar) { - // eslint-disable-next-line no-restricted-syntax for (const item of items) { if (item.type === 'category' && extract(item.items)) { breadcrumbs.push(item); @@ -202,9 +205,27 @@ export function useSidebarBreadcrumbs(): PropSidebar { return false; } + // Check if the only breadcrumb item + // is a link to the current page + function onlyPageLink() { + return ( + breadcrumbs.length === 1 && + breadcrumbs[0].type === 'link' && + isSamePath(breadcrumbs[0].href, pathname) + ); + } + if (sidebar) { extract(sidebar); } + if ( + !breadcrumbs.length || + enabled === false || + (enabled === 'nested' && onlyPageLink()) + ) { + return null; + } + return breadcrumbs.reverse(); } diff --git a/packages/docusaurus-theme-common/src/utils/useThemeConfig.ts b/packages/docusaurus-theme-common/src/utils/useThemeConfig.ts index 4a336f203263..26975d2563ce 100644 --- a/packages/docusaurus-theme-common/src/utils/useThemeConfig.ts +++ b/packages/docusaurus-theme-common/src/utils/useThemeConfig.ts @@ -105,8 +105,6 @@ export type TableOfContents = { maxHeadingLevel: number; }; -export type Breadcrumbs = boolean | 'nested'; - // Theme config after validation/normalization export type ThemeConfig = { docs: { @@ -129,7 +127,6 @@ export type ThemeConfig = { metadata: Array>; sidebarCollapsible: boolean; tableOfContents: TableOfContents; - breadcrumbs: Breadcrumbs; }; // User-provided theme config, unnormalized From 0e3ff8a1052cbc1bd8a0e05179d07bf0c93abd3b Mon Sep 17 00:00:00 2001 From: sebastienlorber Date: Wed, 16 Feb 2022 16:55:25 +0100 Subject: [PATCH 05/12] Ability to link to a category --- .../src/plugin-content-docs.d.ts | 2 + .../src/sidebars/types.ts | 4 ++ .../src/theme/DocBreadcrumbs/index.tsx | 48 ++++++++++++------- .../src/utils/docsUtils.tsx | 5 +- 4 files changed, 41 insertions(+), 18 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts b/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts index e140b05c95d3..d205affe712c 100644 --- a/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts +++ b/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts @@ -127,6 +127,8 @@ declare module '@docusaurus/plugin-content-docs' { export type PropSidebarItemCategory = import('./sidebars/types').PropSidebarItemCategory; export type PropSidebarItem = import('./sidebars/types').PropSidebarItem; + export type PropSidebarBreadcrumbsItem = + import('./sidebars/types').PropSidebarBreadcrumbsItem; export type PropSidebar = import('./sidebars/types').PropSidebar; export type PropSidebars = import('./sidebars/types').PropSidebars; diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/types.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/types.ts index 620344c4ed48..914a854b6389 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars/types.ts @@ -195,6 +195,10 @@ export type PropSidebars = { [sidebarId: string]: PropSidebar; }; +export type PropSidebarBreadcrumbsItem = + | PropSidebarItemLink + | PropSidebarItemCategory; + export type PropVersionDoc = { id: string; title: string; diff --git a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx index bcd994419fb8..ae75fe6c764a 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx @@ -9,6 +9,36 @@ import React from 'react'; import {ThemeClassNames, useSidebarBreadcrumbs} from '@docusaurus/theme-common'; import styles from './styles.module.css'; import clsx from 'clsx'; +import type {PropSidebarBreadcrumbsItem} from '@docusaurus/plugin-content-docs'; +import Link from '@docusaurus/Link'; + +function DocBreadcrumbsItemContent({ + item, +}: { + item: PropSidebarBreadcrumbsItem; +}): JSX.Element { + const className = clsx('breadcrumbs__link', styles.breadcrumbItem); + + return item.href ? ( + + {item.label} + + ) : ( + {item.label} + ); +} + +function DocBreadcrumbsItem({ + item, +}: { + item: PropSidebarBreadcrumbsItem; +}): JSX.Element { + return ( +
  • + +
  • + ); +} export default function DocBreadcrumbs(): JSX.Element | null { const breadcrumbs = useSidebarBreadcrumbs(); @@ -25,22 +55,8 @@ export default function DocBreadcrumbs(): JSX.Element | null { )} aria-label="breadcrumbs">
      - {breadcrumbs.map((breadcrumb, idx) => ( -
    • - {breadcrumb.type === 'link' && ( - - {breadcrumb.label} - - )} - {breadcrumb.type === 'category' && ( - - {breadcrumb.label} - - )} -
    • + {breadcrumbs.map((item, idx) => ( + ))}
    diff --git a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx index 8932fb29a32e..2685e6928ba5 100644 --- a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx +++ b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx @@ -16,6 +16,7 @@ import type { PropSidebarItemCategory, PropVersionDoc, PropVersionMetadata, + PropSidebarBreadcrumbsItem, } from '@docusaurus/plugin-content-docs'; import {isSamePath} from './pathUtils'; import {useLocation} from '@docusaurus/router'; @@ -185,10 +186,10 @@ export function isActiveSidebarItem( return false; } -export function useSidebarBreadcrumbs(): PropSidebar | null { +export function useSidebarBreadcrumbs(): PropSidebarBreadcrumbsItem[] | null { const sidebar = useDocsSidebar(); const {pathname} = useLocation(); - const breadcrumbs: PropSidebar = []; + const breadcrumbs: PropSidebarBreadcrumbsItem[] = []; const enabled = useActivePlugin()?.pluginData?.breadcrumbs; function extract(items: PropSidebar) { From a4858e794d67be7364ab8c6812ba91d9055d852c Mon Sep 17 00:00:00 2001 From: sebastienlorber Date: Wed, 16 Feb 2022 16:58:42 +0100 Subject: [PATCH 06/12] reduce margin --- .../src/theme/DocBreadcrumbs/styles.module.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/styles.module.css b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/styles.module.css index e02c4741dc78..2952f465a9f2 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/styles.module.css @@ -6,7 +6,7 @@ */ .breadcrumbsContainer { - margin-bottom: 1rem; + margin-bottom: 0.5rem; } .breadcrumbItem { From d8f8d3bfba5a6a3701212bdf1c7bfb8eb736151c Mon Sep 17 00:00:00 2001 From: sebastienlorber Date: Wed, 16 Feb 2022 17:02:35 +0100 Subject: [PATCH 07/12] add active class to breadcrumb --- .../src/theme/DocBreadcrumbs/index.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx index ae75fe6c764a..f651f16771e7 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx @@ -30,11 +30,16 @@ function DocBreadcrumbsItemContent({ function DocBreadcrumbsItem({ item, + active, }: { item: PropSidebarBreadcrumbsItem; + active?: boolean; }): JSX.Element { return ( -
  • +
  • ); @@ -56,7 +61,11 @@ export default function DocBreadcrumbs(): JSX.Element | null { aria-label="breadcrumbs">
      {breadcrumbs.map((item, idx) => ( - + ))}
    From 006c780deb7495e053f3a434877c5d86b37b8739 Mon Sep 17 00:00:00 2001 From: sebastienlorber Date: Wed, 16 Feb 2022 17:27:43 +0100 Subject: [PATCH 08/12] css changes --- .../src/theme/DocBreadcrumbs/styles.module.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/styles.module.css b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/styles.module.css index 2952f465a9f2..15c9e62fb20d 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/styles.module.css @@ -6,12 +6,12 @@ */ .breadcrumbsContainer { - margin-bottom: 0.5rem; + margin-bottom: 0.4rem; } .breadcrumbItem { - --ifm-breadcrumb-size-multiplier: 0.75; - margin-bottom: 0.5rem; + --ifm-breadcrumb-size-multiplier: 0.7 !important; + margin-bottom: 0.4rem; background: var(--ifm-color-gray-100); } From 1af22104b888697798bf8e14503e9343deb55e8e Mon Sep 17 00:00:00 2001 From: sebastienlorber Date: Wed, 16 Feb 2022 18:00:02 +0100 Subject: [PATCH 09/12] fix tree traversal edge case for category generated-index + add tests --- .../src/utils/__tests__/docsUtils.test.tsx | 130 ++++++++++++++++++ .../src/utils/docsUtils.tsx | 48 ++++--- 2 files changed, 156 insertions(+), 22 deletions(-) diff --git a/packages/docusaurus-theme-common/src/utils/__tests__/docsUtils.test.tsx b/packages/docusaurus-theme-common/src/utils/__tests__/docsUtils.test.tsx index 06fd3686d201..e0700f8d8638 100644 --- a/packages/docusaurus-theme-common/src/utils/__tests__/docsUtils.test.tsx +++ b/packages/docusaurus-theme-common/src/utils/__tests__/docsUtils.test.tsx @@ -16,11 +16,13 @@ import { useDocsSidebar, DocsSidebarProvider, findSidebarCategory, + getBreadcrumbs, } from '../docsUtils'; import type { PropSidebar, PropSidebarItem, PropSidebarItemCategory, + PropSidebarItemLink, PropVersionMetadata, } from '@docusaurus/plugin-content-docs'; @@ -39,6 +41,15 @@ function testCategory( }; } +function testLink(data?: Partial): PropSidebarItemLink { + return { + type: 'link', + href: '/testLinkHref', + label: 'Link label', + ...data, + }; +} + function testVersion(data?: Partial): PropVersionMetadata { return { version: 'versionName', @@ -330,4 +341,123 @@ describe('docsUtils', () => { expect(isActiveSidebarItem(item, '/sub-sub-link-path/')).toEqual(true); }); }); + + describe('getBreadcrumbs', () => { + test('should return empty for empty sidebar', () => { + expect( + getBreadcrumbs({ + sidebar: [], + pathname: '/doesNotExist', + }), + ).toEqual([]); + }); + + test('should return empty for sidebar but unknown pathname', () => { + const sidebar = [testCategory(), testLink()]; + expect( + getBreadcrumbs({ + sidebar, + pathname: '/doesNotExist', + }), + ).toEqual([]); + }); + + test('should return first level category', () => { + const pathname = '/somePathName'; + const sidebar = [testCategory({href: pathname}), testLink()]; + + expect( + getBreadcrumbs({ + sidebar, + pathname, + }), + ).toEqual([sidebar[0]]); + }); + + test('should return first level link', () => { + const pathname = '/somePathName'; + const sidebar = [testCategory(), testLink({href: pathname})]; + + expect( + getBreadcrumbs({ + sidebar, + pathname, + }), + ).toEqual([sidebar[1]]); + }); + + test('should return nested category', () => { + const pathname = '/somePathName'; + + const categoryLevel3 = testCategory({ + href: pathname, + }); + + const categoryLevel2 = testCategory({ + items: [ + testCategory(), + categoryLevel3, + testLink({href: pathname}), + testLink(), + ], + }); + + const categoryLevel1 = testCategory({ + items: [testLink(), categoryLevel2], + }); + + const sidebar = [ + testLink(), + testCategory(), + categoryLevel1, + testLink(), + testCategory(), + ]; + + expect( + getBreadcrumbs({ + sidebar, + pathname, + }), + ).toEqual([categoryLevel1, categoryLevel2, categoryLevel3]); + }); + }); + + test('should return nested link', () => { + const pathname = '/somePathName'; + + const link = testLink({href: pathname}); + + const categoryLevel3 = testCategory({ + items: [testLink(), link, testLink()], + }); + + const categoryLevel2 = testCategory({ + items: [ + testCategory(), + categoryLevel3, + testLink({href: pathname}), + testLink(), + ], + }); + + const categoryLevel1 = testCategory({ + items: [testLink(), categoryLevel2], + }); + + const sidebar = [ + testLink(), + testCategory(), + categoryLevel1, + testLink(), + testCategory(), + ]; + + expect( + getBreadcrumbs({ + sidebar, + pathname, + }), + ).toEqual([categoryLevel1, categoryLevel2, categoryLevel3, link]); + }); }); diff --git a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx index 2685e6928ba5..dcf8f0d9fe4a 100644 --- a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx +++ b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx @@ -186,15 +186,21 @@ export function isActiveSidebarItem( return false; } -export function useSidebarBreadcrumbs(): PropSidebarBreadcrumbsItem[] | null { - const sidebar = useDocsSidebar(); - const {pathname} = useLocation(); +export function getBreadcrumbs({ + sidebar, + pathname, +}: { + sidebar: PropSidebar; + pathname: string; +}): PropSidebarBreadcrumbsItem[] { const breadcrumbs: PropSidebarBreadcrumbsItem[] = []; - const enabled = useActivePlugin()?.pluginData?.breadcrumbs; function extract(items: PropSidebar) { for (const item of items) { - if (item.type === 'category' && extract(item.items)) { + if ( + item.type === 'category' && + (isSamePath(item.href, pathname) || extract(item.items)) + ) { breadcrumbs.push(item); return true; } else if (item.type === 'link' && isSamePath(item.href, pathname)) { @@ -206,27 +212,25 @@ export function useSidebarBreadcrumbs(): PropSidebarBreadcrumbsItem[] | null { return false; } - // Check if the only breadcrumb item - // is a link to the current page - function onlyPageLink() { - return ( - breadcrumbs.length === 1 && - breadcrumbs[0].type === 'link' && - isSamePath(breadcrumbs[0].href, pathname) - ); - } + extract(sidebar); + + return breadcrumbs.reverse(); +} - if (sidebar) { - extract(sidebar); +export function useSidebarBreadcrumbs(): PropSidebarBreadcrumbsItem[] | null { + const sidebar = useDocsSidebar(); + const {pathname} = useLocation(); + const breadcrumbsOption = useActivePlugin()?.pluginData?.breadcrumbs; + + if (!sidebar || breadcrumbsOption === false) { + return null; } - if ( - !breadcrumbs.length || - enabled === false || - (enabled === 'nested' && onlyPageLink()) - ) { + const breadcrumbs = getBreadcrumbs({sidebar, pathname}); + + if (breadcrumbsOption === 'nested' && breadcrumbs.length < 2) { return null; } - return breadcrumbs.reverse(); + return breadcrumbs; } From 8a769742edc8577058363e4e2533dd0b3a65a363 Mon Sep 17 00:00:00 2001 From: sebastienlorber Date: Wed, 16 Feb 2022 18:11:43 +0100 Subject: [PATCH 10/12] add home breadcrumb item --- .../src/theme/DocBreadcrumbs/index.tsx | 53 ++++++++++++------- .../theme/DocBreadcrumbs/styles.module.css | 6 +-- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx index f651f16771e7..8eecbe54170a 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx @@ -5,34 +5,37 @@ * LICENSE file in the root directory of this source tree. */ -import React from 'react'; +import React, {type ReactNode} from 'react'; import {ThemeClassNames, useSidebarBreadcrumbs} from '@docusaurus/theme-common'; import styles from './styles.module.css'; import clsx from 'clsx'; -import type {PropSidebarBreadcrumbsItem} from '@docusaurus/plugin-content-docs'; import Link from '@docusaurus/Link'; +import useBaseUrl from '@docusaurus/useBaseUrl'; -function DocBreadcrumbsItemContent({ - item, +// TODO move to design system folder +function BreadcrumbsItemLink({ + children, + href, }: { - item: PropSidebarBreadcrumbsItem; + children: ReactNode; + href?: string; }): JSX.Element { - const className = clsx('breadcrumbs__link', styles.breadcrumbItem); - - return item.href ? ( - - {item.label} + const className = clsx('breadcrumbs__link', styles.breadcrumbsItemLink); + return href ? ( + + {children} ) : ( - {item.label} + {children} ); } -function DocBreadcrumbsItem({ - item, +// TODO move to design system folder +function BreadcrumbsItem({ + children, active, }: { - item: PropSidebarBreadcrumbsItem; + children: ReactNode; active?: boolean; }): JSX.Element { return ( @@ -40,11 +43,20 @@ function DocBreadcrumbsItem({ className={clsx('breadcrumbs__item', { 'breadcrumbs__item--active': active, })}> - + {children} ); } +function HomeBreadcrumbItem() { + const homeHref = useBaseUrl('/'); + return ( + + 🏠 + + ); +} + export default function DocBreadcrumbs(): JSX.Element | null { const breadcrumbs = useSidebarBreadcrumbs(); @@ -60,12 +72,13 @@ export default function DocBreadcrumbs(): JSX.Element | null { )} aria-label="breadcrumbs">
      + {breadcrumbs.map((item, idx) => ( - + + + {item.label} + + ))}
    diff --git a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/styles.module.css b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/styles.module.css index 15c9e62fb20d..db445fbe1150 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/styles.module.css @@ -9,18 +9,18 @@ margin-bottom: 0.4rem; } -.breadcrumbItem { +.breadcrumbsItemLink { --ifm-breadcrumb-size-multiplier: 0.7 !important; margin-bottom: 0.4rem; background: var(--ifm-color-gray-100); } -html[data-theme='dark'] .breadcrumbItem { +html[data-theme='dark'] .breadcrumbsItemLink { background-color: var(--ifm-color-gray-900); } @media (min-width: 997px) { - .breadcrumbItem { + .breadcrumbsItemLink { --ifm-breadcrumb-size-multiplier: 0.8; } } From 6ca669793ada9ec142a1efe244720fe81fe67a4a Mon Sep 17 00:00:00 2001 From: sebastienlorber Date: Wed, 16 Feb 2022 18:32:12 +0100 Subject: [PATCH 11/12] remove nested option --- .../src/__tests__/__snapshots__/index.test.ts.snap | 8 ++++---- .../src/__tests__/options.test.ts | 2 +- .../docusaurus-plugin-content-docs/src/options.ts | 7 ++----- .../src/plugin-content-docs.d.ts | 4 ++-- .../docusaurus-theme-common/src/utils/docsUtils.tsx | 12 +++--------- 5 files changed, 12 insertions(+), 21 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap index 88fb0433601c..3fb1a48e0a71 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap @@ -242,7 +242,7 @@ exports[`simple website content 5`] = ` Object { "pluginName": Object { "pluginId": Object { - "breadcrumbs": "nested", + "breadcrumbs": true, "path": "/docs", "versions": Array [ Object { @@ -956,7 +956,7 @@ exports[`simple website content: global data 1`] = ` Object { "pluginName": Object { "pluginId": Object { - "breadcrumbs": "nested", + "breadcrumbs": true, "path": "/docs", "versions": Array [ Object { @@ -2413,7 +2413,7 @@ exports[`versioned website (community) content: global data 1`] = ` Object { "pluginName": Object { "pluginId": Object { - "breadcrumbs": "nested", + "breadcrumbs": true, "path": "/community", "versions": Array [ Object { @@ -3453,7 +3453,7 @@ exports[`versioned website content: global data 1`] = ` Object { "pluginName": Object { "pluginId": Object { - "breadcrumbs": "nested", + "breadcrumbs": true, "path": "/docs", "versions": Array [ Object { diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts index 5ef88f9e26ba..95fb65e38c9f 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts @@ -56,7 +56,7 @@ describe('normalizeDocsPluginOptions', () => { rehypePlugins: [markdownPluginsFunctionStub], beforeDefaultRehypePlugins: [], beforeDefaultRemarkPlugins: [], - breadcrumbs: 'nested', + breadcrumbs: true, showLastUpdateTime: true, showLastUpdateAuthor: true, admonitions: {}, diff --git a/packages/docusaurus-plugin-content-docs/src/options.ts b/packages/docusaurus-plugin-content-docs/src/options.ts index 520652578c2b..3e5e9f5a7a97 100644 --- a/packages/docusaurus-plugin-content-docs/src/options.ts +++ b/packages/docusaurus-plugin-content-docs/src/options.ts @@ -55,7 +55,7 @@ export const DEFAULT_OPTIONS: Omit = { editLocalizedFiles: false, sidebarCollapsible: true, sidebarCollapsed: true, - breadcrumbs: 'nested', + breadcrumbs: true, }; const VersionOptionsSchema = Joi.object({ @@ -140,10 +140,7 @@ export const OptionsSchema = Joi.object({ disableVersioning: Joi.bool().default(DEFAULT_OPTIONS.disableVersioning), lastVersion: Joi.string().optional(), versions: VersionsOptionsSchema, - breadcrumbs: Joi.alternatives( - Joi.bool(), - Joi.string().valid('nested'), - ).default(DEFAULT_OPTIONS.breadcrumbs), + breadcrumbs: Joi.bool().default(DEFAULT_OPTIONS.breadcrumbs), }); export function validateOptions({ diff --git a/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts b/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts index d205affe712c..00c4e8fda250 100644 --- a/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts +++ b/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts @@ -38,7 +38,7 @@ declare module '@docusaurus/plugin-content-docs' { showLastUpdateTime?: boolean; showLastUpdateAuthor?: boolean; numberPrefixParser: NumberPrefixParser; - breadcrumbs: boolean | 'nested'; + breadcrumbs: boolean; }; export type PathOptions = { @@ -301,7 +301,7 @@ declare module '@docusaurus/plugin-content-docs/client' { export type GlobalPluginData = { path: string; versions: GlobalVersion[]; - breadcrumbs: boolean | 'nested'; + breadcrumbs: boolean; }; export type DocVersionSuggestions = { // suggest the latest version diff --git a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx index dcf8f0d9fe4a..f02b213c32dd 100644 --- a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx +++ b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx @@ -220,17 +220,11 @@ export function getBreadcrumbs({ export function useSidebarBreadcrumbs(): PropSidebarBreadcrumbsItem[] | null { const sidebar = useDocsSidebar(); const {pathname} = useLocation(); - const breadcrumbsOption = useActivePlugin()?.pluginData?.breadcrumbs; + const breadcrumbsOption = useActivePlugin()?.pluginData.breadcrumbs; - if (!sidebar || breadcrumbsOption === false) { + if (breadcrumbsOption === false || !sidebar) { return null; } - const breadcrumbs = getBreadcrumbs({sidebar, pathname}); - - if (breadcrumbsOption === 'nested' && breadcrumbs.length < 2) { - return null; - } - - return breadcrumbs; + return getBreadcrumbs({sidebar, pathname}); } From df6ef4c432bff38479cc0840617c374173f50e1a Mon Sep 17 00:00:00 2001 From: sebastienlorber Date: Wed, 16 Feb 2022 18:46:47 +0100 Subject: [PATCH 12/12] move/refactor doc --- .../docs/api/plugins/plugin-content-docs.md | 2 ++ .../docs/api/themes/theme-configuration.md | 25 ------------------- website/docs/guides/docs/sidebar/index.md | 22 ++++++++++++++++ 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/website/docs/api/plugins/plugin-content-docs.md b/website/docs/api/plugins/plugin-content-docs.md index 0c52f97ad252..2c97cd4ab78e 100644 --- a/website/docs/api/plugins/plugin-content-docs.md +++ b/website/docs/api/plugins/plugin-content-docs.md @@ -32,6 +32,7 @@ Accepted fields: | Name | Type | Default | Description | | --- | --- | --- | --- | | `path` | `string` | `'docs'` | Path to data on filesystem relative to site dir. | +| `breadcrumbs` | `boolean` | `true` | To enable or disable the breadcrumbs on docs pages. | | `editUrl` | string \| EditUrlFunction | `undefined` | Base URL to edit your site. The final URL is computed by `editUrl + relativeDocPath`. Using a function allows more nuanced control for each file. Omitting this variable entirely will disable edit links. | | `editLocalizedFiles` | `boolean` | `false` | The edit URL will target the localized file, instead of the original unlocalized file. Ignored when `editUrl` is a function. | | `editCurrentVersion` | `boolean` | `false` | The edit URL will always target the current version doc instead of older versions. Ignored when `editUrl` is a function. | @@ -127,6 +128,7 @@ Most Docusaurus users configure this plugin through the preset options. const config = { path: 'docs', + breadcrumbs: true, // Simple use-case: string editUrl // editUrl: 'https://github.com/facebook/docusaurus/edit/main/website/', // Advanced use-case: functional editUrl diff --git a/website/docs/api/themes/theme-configuration.md b/website/docs/api/themes/theme-configuration.md index aa3a1e0a22ec..e79cf89614cf 100644 --- a/website/docs/api/themes/theme-configuration.md +++ b/website/docs/api/themes/theme-configuration.md @@ -159,31 +159,6 @@ module.exports = { }; ``` -### Breadcrumbs {#breadcrumbs} - -Breadcrumbs can be rendered on each document page, based on sidebar items. - -Accepted fields: - - - -| Name | Type | Default | Description | -| --- | --- | --- | --- | -| `breadcrumbs` | boolean \| 'nested' | `nested` | Use boolean value to strictly enable or disable all breadcrumbs, or use `'nested'` when you only want breadcrumbs visible in nested pages. | - - - -Example configuration: - -```js title="docusaurus.config.js" -module.exports = { - themeConfig: { - // highlight-next-line - breadcrumbs: 'nested', - }, -}; -``` - ## Navbar {#navbar} Accepted fields: diff --git a/website/docs/guides/docs/sidebar/index.md b/website/docs/guides/docs/sidebar/index.md index d78b0367d213..9634f4853cac 100644 --- a/website/docs/guides/docs/sidebar/index.md +++ b/website/docs/guides/docs/sidebar/index.md @@ -160,6 +160,28 @@ To pass in custom props to a swizzled sidebar item, add the optional `customProp }; ``` +## Sidebar Breadcrumbs {#sidebar-breadcrumbs} + +By default, breadcrumbs are rendered at the top, using the "sidebar path" of the current page. + +This behavior can be disabled with plugin options: + +```js title="docusaurus.config.js" +module.exports = { + presets: [ + [ + '@docusaurus/preset-classic', + { + docs: { + // highlight-next-line + breadcrumbs: false, + }, + }, + ], + ], +}; +``` + ## Complex sidebars example {#complex-sidebars-example} A real-world example from the Docusaurus site: