From 544045f4222289fe0a4f3f91b734b8308b0d5d7d Mon Sep 17 00:00:00 2001 From: Oleksiy Gapotchenko Date: Sat, 18 Jan 2025 16:43:36 +0100 Subject: [PATCH 01/27] feat(theme-classic): implemented version configurations for "docsVersionDropdown" navbar item --- .../docusaurus-theme-classic/src/options.ts | 15 +++++ .../src/theme-classic.d.ts | 11 +++ .../DocsVersionDropdownNavbarItem.tsx | 67 +++++++++++++++++-- 3 files changed, 88 insertions(+), 5 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/options.ts b/packages/docusaurus-theme-classic/src/options.ts index 6827c069bcbb..4f19d7cf3b56 100644 --- a/packages/docusaurus-theme-classic/src/options.ts +++ b/packages/docusaurus-theme-classic/src/options.ts @@ -8,6 +8,10 @@ import {themes} from 'prism-react-renderer'; import {Joi, URISchema} from '@docusaurus/utils-validation'; import type {Options, PluginOptions} from '@docusaurus/theme-classic'; +import type { + PropVersionItem, + PropVersionItems, +} from '@theme/NavbarItem/DocsVersionDropdownNavbarItem'; import type {ThemeConfig} from '@docusaurus/theme-common'; import type { ThemeConfigValidationContext, @@ -204,12 +208,23 @@ const DropdownNavbarItemSchema = NavbarItemBaseSchema.append({ items: Joi.array().items(DropdownSubitemSchema).required(), }); +const DocsVersionNameSchema = Joi.string().min(1); + const DocsVersionDropdownNavbarItemSchema = NavbarItemBaseSchema.append({ type: Joi.string().equal('docsVersionDropdown').required(), docsPluginId: Joi.string(), dropdownActiveClassDisabled: Joi.boolean(), dropdownItemsBefore: Joi.array().items(DropdownSubitemSchema).default([]), dropdownItemsAfter: Joi.array().items(DropdownSubitemSchema).default([]), + versions: Joi.alternatives().try( + Joi.array().items(DocsVersionNameSchema), + Joi.object().pattern( + DocsVersionNameSchema, + Joi.object({ + label: Joi.string().min(1), + }), + ), + ), }); const LocaleDropdownNavbarItemSchema = NavbarItemBaseSchema.append({ diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index bef2dc5a1401..233e7d49fdf5 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -1257,11 +1257,22 @@ declare module '@theme/NavbarItem/DocsVersionDropdownNavbarItem' { import type {Props as DropdownNavbarItemProps} from '@theme/NavbarItem/DropdownNavbarItem'; import type {LinkLikeNavbarItemProps} from '@theme/NavbarItem'; + type PropVersionItem = { + readonly label?: string; + }; + + type PropVersionItems = { + readonly [version: string]: PropVersionItem; + }; + + type PropVersions = string[] | PropVersionItems; + export interface Props extends DropdownNavbarItemProps { readonly docsPluginId?: string; readonly dropdownActiveClassDisabled?: boolean; readonly dropdownItemsBefore: LinkLikeNavbarItemProps[]; readonly dropdownItemsAfter: LinkLikeNavbarItemProps[]; + readonly versions?: PropVersions; } export default function DocsVersionDropdownNavbarItem( diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx index 4d8c479cf472..fb332003ab95 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx @@ -16,7 +16,11 @@ import {translate} from '@docusaurus/Translate'; import {useLocation} from '@docusaurus/router'; import DefaultNavbarItem from '@theme/NavbarItem/DefaultNavbarItem'; import DropdownNavbarItem from '@theme/NavbarItem/DropdownNavbarItem'; -import type {Props} from '@theme/NavbarItem/DocsVersionDropdownNavbarItem'; +import type { + Props, + PropVersions, + PropVersionItem, +} from '@theme/NavbarItem/DocsVersionDropdownNavbarItem'; import type {LinkLikeNavbarItemProps} from '@theme/NavbarItem'; import type { GlobalVersion, @@ -24,6 +28,31 @@ import type { ActiveDocContext, } from '@docusaurus/plugin-content-docs/client'; +type VersionConfiguration = {name: string} & PropVersionItem; + +function getVersionConfigurations( + versions: PropVersions, +): VersionConfiguration[] { + if (versions instanceof Array) { + return versions.map((name): VersionConfiguration => { + return {name}; + }); + } else { + return Object.entries(versions).map( + ([name, version]): VersionConfiguration => { + return {name, ...version}; + }, + ); + } +} + +function getVersionConfigurationName(version: GlobalVersion): string { + // 'version.name' has special conventions for current and next versions, + // that's why we use 'version.label' as a canonical version name that can be + // referenced in configuration. + return version.label; +} + function getVersionMainDoc(version: GlobalVersion): GlobalDoc { return version.docs.find((doc) => doc.id === version.mainDocId)!; } @@ -46,17 +75,40 @@ export default function DocsVersionDropdownNavbarItem({ dropdownActiveClassDisabled, dropdownItemsBefore, dropdownItemsAfter, + versions: staticVersions, ...props }: Props): ReactNode { + // Prepare version configurations + const versionConfigurations = staticVersions + ? new Map( + getVersionConfigurations(staticVersions).map((configuration) => [ + configuration.name, + configuration, + ]), + ) + : undefined; + + // Build version list + let versions = useVersions(docsPluginId); + if (versionConfigurations) { + // Keep configured versions only + versions = versions.filter((version) => + versionConfigurations.has(getVersionConfigurationName(version)), + ); + } + + // Build item list const {search, hash} = useLocation(); const activeDocContext = useActiveDocContext(docsPluginId); - const versions = useVersions(docsPluginId); const {savePreferredVersionName} = useDocsPreferredVersion(docsPluginId); - function versionToLink(version: GlobalVersion): LinkLikeNavbarItemProps { + function versionToLink( + version: GlobalVersion, + versionConfiguration?: VersionConfiguration, + ): LinkLikeNavbarItemProps { const targetDoc = getVersionTargetDoc(version, activeDocContext); return { - label: version.label, + label: versionConfiguration?.label ?? version.label, // preserve ?search#hash suffix on version switches to: `${targetDoc.path}${search}${hash}`, isActive: () => version === activeDocContext.activeVersion, @@ -66,7 +118,12 @@ export default function DocsVersionDropdownNavbarItem({ const items: LinkLikeNavbarItemProps[] = [ ...dropdownItemsBefore, - ...versions.map(versionToLink), + ...versions.map((x) => + versionToLink( + x, + versionConfigurations?.get(getVersionConfigurationName(x)), + ), + ), ...dropdownItemsAfter, ]; From 36a35e5988d90fbec9876811042c8c0cf19740de Mon Sep 17 00:00:00 2001 From: Oleksiy Gapotchenko Date: Sat, 18 Jan 2025 18:00:24 +0100 Subject: [PATCH 02/27] docs(theme-classic): added docs for versions configuration of "docsVersionDropdown" navbar item --- .../docs/api/themes/theme-configuration.mdx | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/website/docs/api/themes/theme-configuration.mdx b/website/docs/api/themes/theme-configuration.mdx index 7be1ba2b64ad..49669af30b75 100644 --- a/website/docs/api/themes/theme-configuration.mdx +++ b/website/docs/api/themes/theme-configuration.mdx @@ -597,6 +597,7 @@ Accepted fields: | `dropdownItemsAfter` | [LinkLikeItem](#navbar-dropdown)[] | `[]` | Add additional dropdown items at the end of the dropdown. | | `docsPluginId` | `string` | `'default'` | The ID of the docs plugin that the doc versioning belongs to. | | `dropdownActiveClassDisabled` | `boolean` | `false` | Do not add the link active class when browsing docs. | +| `versions` | `string[] | object` | `undefined` | Specify a custom list of versions to include in the dropdown, or an object with more detailed version configurations. | ```mdx-code-block @@ -623,6 +624,92 @@ export default { }; ``` +By default, the version dropdown displays all the versions provided by the docs. +However, sometimes it may be beneficial to restrict the number of displayed versions to declutter the UI. +To achieve that, it is possible to specify a custom list of versions that should be offered in the dropdown: + +```js title="docusaurus.config.js" +export default { + themeConfig: { + navbar: { + items: [ + { + type: 'docsVersionDropdown', + position: 'left', + // highlight-start + versions: ['2024.3', '2024.2', '2024.1', '2023.4'], + // highlight-end + }, + ], + }, + }, +}; +``` + +Some versioning schemes may benefit from an even more detailed configuration of displayed versions. +For example, if you are using semantic versioning, you may have versions such as: + +``` +2.1.1 +2.1.0 +2.0.1 +2.0.0 +1.0.1 +1.0.0 +``` + +And maybe you want to have in the dropdown: + +``` +2.1 +2.0 +1.0 +``` + +Or maybe just: + +``` +1.x +2.x +``` + +To achieve that, you can specify a custom configuration for every version that should be displayed in the dropdown: + +```js title="docusaurus.config.js" +export default { + themeConfig: { + navbar: { + items: [ + { + type: 'docsVersionDropdown', + position: 'left', + // highlight-start + versions: { + "1.0.1": {label: "1.x"}, + "2.1.1": {label: "2.x"}, + }, + // highlight-end + }, + ], + }, + }, +}; +``` + +The fields accepted by the custom version configuration: + +```mdx-code-block + +``` + +| Name | Type | Default | Description | +| --- | --- | --- | --- | +| `label` | `string` | Optional | The name to be shown for this version. | + +```mdx-code-block + +``` + #### Navbar docs version {#navbar-docs-version} If you use docs with versioning, this special navbar item type will link to the active/browsed version of your doc (depends on the current URL), and fallback to the latest version. From 5e8032b5bfa1549bdd7cb3b8f100a4ab43a03855 Mon Sep 17 00:00:00 2001 From: Oleksiy Gapotchenko Date: Sat, 18 Jan 2025 18:49:20 +0100 Subject: [PATCH 03/27] feat(theme-classic): preserve order of version configurations in "docsVersionDropdown" navbar item --- .../DocsVersionDropdownNavbarItem.tsx | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx index fb332003ab95..626b5242256c 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx @@ -53,6 +53,11 @@ function getVersionConfigurationName(version: GlobalVersion): string { return version.label; } +type ConfiguredVersion = { + version: GlobalVersion; + configuration?: VersionConfiguration; +}; + function getVersionMainDoc(version: GlobalVersion): GlobalDoc { return version.docs.find((doc) => doc.id === version.mainDocId)!; } @@ -78,23 +83,33 @@ export default function DocsVersionDropdownNavbarItem({ versions: staticVersions, ...props }: Props): ReactNode { - // Prepare version configurations - const versionConfigurations = staticVersions - ? new Map( - getVersionConfigurations(staticVersions).map((configuration) => [ - configuration.name, - configuration, - ]), - ) - : undefined; - // Build version list - let versions = useVersions(docsPluginId); - if (versionConfigurations) { - // Keep configured versions only - versions = versions.filter((version) => - versionConfigurations.has(getVersionConfigurationName(version)), + const versions = useVersions(docsPluginId); + + let configuredVersions: ConfiguredVersion[]; + if (staticVersions) { + // Keep only versions specified in configuration, reorder them accordingly + const versionMap = new Map( + versions.map((version) => [ + getVersionConfigurationName(version), + version, + ]), ); + + configuredVersions = []; + for (const configuration of getVersionConfigurations(staticVersions)) { + let version = versionMap.get(configuration.name); + if (!version) { + // A version configuration references a non-existing version, ignore it + continue; + } + configuredVersions.push({version, configuration}); + } + } else { + // The versions are not configured + configuredVersions = versions.map((version): ConfiguredVersion => { + return {version}; + }); } // Build item list @@ -118,12 +133,7 @@ export default function DocsVersionDropdownNavbarItem({ const items: LinkLikeNavbarItemProps[] = [ ...dropdownItemsBefore, - ...versions.map((x) => - versionToLink( - x, - versionConfigurations?.get(getVersionConfigurationName(x)), - ), - ), + ...configuredVersions.map((x) => versionToLink(x.version, x.configuration)), ...dropdownItemsAfter, ]; From 0a02b4750ab0786fc3b47f0a749acd0f7d39c163 Mon Sep 17 00:00:00 2001 From: Oleksiy Gapotchenko Date: Sat, 18 Jan 2025 19:27:01 +0100 Subject: [PATCH 04/27] docs(theme-classic): fixed docs formatting issue for versions configuration of "docsVersionDropdown" navbar item --- website/docs/api/themes/theme-configuration.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/api/themes/theme-configuration.mdx b/website/docs/api/themes/theme-configuration.mdx index 49669af30b75..03e09ecc4bf0 100644 --- a/website/docs/api/themes/theme-configuration.mdx +++ b/website/docs/api/themes/theme-configuration.mdx @@ -597,7 +597,7 @@ Accepted fields: | `dropdownItemsAfter` | [LinkLikeItem](#navbar-dropdown)[] | `[]` | Add additional dropdown items at the end of the dropdown. | | `docsPluginId` | `string` | `'default'` | The ID of the docs plugin that the doc versioning belongs to. | | `dropdownActiveClassDisabled` | `boolean` | `false` | Do not add the link active class when browsing docs. | -| `versions` | `string[] | object` | `undefined` | Specify a custom list of versions to include in the dropdown, or an object with more detailed version configurations. | +| `versions` | string[] \| object | `undefined` | Specify a custom list of versions to include in the dropdown, or an object with more detailed version configurations. | ```mdx-code-block From c791a47b79c7c1c8e0794fec1333f8837a5273de Mon Sep 17 00:00:00 2001 From: Oleksiy Gapotchenko Date: Sat, 18 Jan 2025 19:37:13 +0100 Subject: [PATCH 05/27] docs(theme-classic): improved wording in docs for versions configuration of "docsVersionDropdown" navbar item --- website/docs/api/themes/theme-configuration.mdx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/website/docs/api/themes/theme-configuration.mdx b/website/docs/api/themes/theme-configuration.mdx index 03e09ecc4bf0..681fbff7edf4 100644 --- a/website/docs/api/themes/theme-configuration.mdx +++ b/website/docs/api/themes/theme-configuration.mdx @@ -597,7 +597,7 @@ Accepted fields: | `dropdownItemsAfter` | [LinkLikeItem](#navbar-dropdown)[] | `[]` | Add additional dropdown items at the end of the dropdown. | | `docsPluginId` | `string` | `'default'` | The ID of the docs plugin that the doc versioning belongs to. | | `dropdownActiveClassDisabled` | `boolean` | `false` | Do not add the link active class when browsing docs. | -| `versions` | string[] \| object | `undefined` | Specify a custom list of versions to include in the dropdown, or an object with more detailed version configurations. | +| `versions` | string[] \| object | `undefined` | Specify a custom list of versions to include in the dropdown, or specify an object with detailed version configurations. | ```mdx-code-block @@ -658,7 +658,7 @@ For example, if you are using semantic versioning, you may have versions such as 1.0.0 ``` -And maybe you want to have in the dropdown: +And maybe you want to have them in the dropdown as: ``` 2.1 @@ -666,7 +666,7 @@ And maybe you want to have in the dropdown: 1.0 ``` -Or maybe just: +or maybe just: ``` 1.x @@ -685,8 +685,8 @@ export default { position: 'left', // highlight-start versions: { - "1.0.1": {label: "1.x"}, - "2.1.1": {label: "2.x"}, + '1.0.1': {label: '1.x'}, + '2.1.1': {label: '2.x'}, }, // highlight-end }, @@ -704,7 +704,7 @@ The fields accepted by the custom version configuration: | Name | Type | Default | Description | | --- | --- | --- | --- | -| `label` | `string` | Optional | The name to be shown for this version. | +| `label` | `string` | Optional | The custom name to be shown for this version. | ```mdx-code-block From 8e2120243afc845c76ab6b249895adc69b3efac7 Mon Sep 17 00:00:00 2001 From: Oleksiy Gapotchenko Date: Sat, 18 Jan 2025 19:45:16 +0100 Subject: [PATCH 06/27] docs(theme-classic): more precise docs for versions configuration of "docsVersionDropdown" navbar item --- website/docs/api/themes/theme-configuration.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/api/themes/theme-configuration.mdx b/website/docs/api/themes/theme-configuration.mdx index 681fbff7edf4..9e4c674091ab 100644 --- a/website/docs/api/themes/theme-configuration.mdx +++ b/website/docs/api/themes/theme-configuration.mdx @@ -704,7 +704,7 @@ The fields accepted by the custom version configuration: | Name | Type | Default | Description | | --- | --- | --- | --- | -| `label` | `string` | Optional | The custom name to be shown for this version. | +| `label` | `string` | Optional | The custom name to be shown in the dropdown for this version. | ```mdx-code-block From fd8f2db06bbc3b30de6f2a81ac4fc4712256719e Mon Sep 17 00:00:00 2001 From: Oleksiy Gapotchenko Date: Sun, 19 Jan 2025 14:28:23 +0100 Subject: [PATCH 07/27] docs(theme-classic): fixed anchor for versions configuration docs of "docsVersionDropdown" navbar item --- website/docs/api/themes/theme-configuration.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/api/themes/theme-configuration.mdx b/website/docs/api/themes/theme-configuration.mdx index 9e4c674091ab..688787162bdb 100644 --- a/website/docs/api/themes/theme-configuration.mdx +++ b/website/docs/api/themes/theme-configuration.mdx @@ -699,7 +699,7 @@ export default { The fields accepted by the custom version configuration: ```mdx-code-block - + ``` | Name | Type | Default | Description | From 10976ad1b82d992b5ea6e3a697c943f06acb3691 Mon Sep 17 00:00:00 2001 From: Oleksiy Gapotchenko Date: Sun, 19 Jan 2025 15:14:43 +0100 Subject: [PATCH 08/27] docs(theme-classic): simplified example code for versions configuration docs of "docsVersionDropdown" navbar item --- website/docs/api/themes/theme-configuration.mdx | 2 -- 1 file changed, 2 deletions(-) diff --git a/website/docs/api/themes/theme-configuration.mdx b/website/docs/api/themes/theme-configuration.mdx index 688787162bdb..637e22a7ab1b 100644 --- a/website/docs/api/themes/theme-configuration.mdx +++ b/website/docs/api/themes/theme-configuration.mdx @@ -635,7 +635,6 @@ export default { items: [ { type: 'docsVersionDropdown', - position: 'left', // highlight-start versions: ['2024.3', '2024.2', '2024.1', '2023.4'], // highlight-end @@ -682,7 +681,6 @@ export default { items: [ { type: 'docsVersionDropdown', - position: 'left', // highlight-start versions: { '1.0.1': {label: '1.x'}, From bcae6cb5965b1825e0445772a8428d5afbf8e37f Mon Sep 17 00:00:00 2001 From: Oleksiy Gapotchenko Date: Sun, 19 Jan 2025 16:02:42 +0100 Subject: [PATCH 09/27] feat(theme-classic): use formal version identifiers for version configurations in "docsVersionDropdown" navbar item --- .../DocsVersionDropdownNavbarItem.tsx | 79 +++++++++++-------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx index 626b5242256c..b977bf4935f4 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx @@ -46,18 +46,52 @@ function getVersionConfigurations( } } -function getVersionConfigurationName(version: GlobalVersion): string { - // 'version.name' has special conventions for current and next versions, - // that's why we use 'version.label' as a canonical version name that can be - // referenced in configuration. - return version.label; -} - type ConfiguredVersion = { version: GlobalVersion; configuration?: VersionConfiguration; }; +function configureVersions( + versions: GlobalVersion[], + staticVersions?: PropVersions, +): ConfiguredVersion[] { + if (staticVersions) { + // The versions are configured. + + // Collect all the versions we have + const versionMap = new Map( + versions.map((version) => [version.name, version]), + ); + + // Add secondary version identifiers to make the configuration process + // more natural to a user + for (const version of versions) { + // 'version.name' has special conventions for current and next versions, + // that's why we use 'version.label' as a secondary version identifier + // that can be referenced in configuration. + const label = version.label; + if (!versionMap.has(label)) versionMap.set(label, version); + } + + // Keep only versions specified in configuration, reorder them accordingly + const configuredVersions: ConfiguredVersion[] = []; + for (const configuration of getVersionConfigurations(staticVersions)) { + let version = versionMap.get(configuration.name); + if (!version) { + // A version configuration references a non-existing version, ignore it + continue; + } + configuredVersions.push({version, configuration}); + } + return configuredVersions; + } else { + // The versions are not configured + return versions.map((version): ConfiguredVersion => { + return {version}; + }); + } +} + function getVersionMainDoc(version: GlobalVersion): GlobalDoc { return version.docs.find((doc) => doc.id === version.mainDocId)!; } @@ -84,33 +118,10 @@ export default function DocsVersionDropdownNavbarItem({ ...props }: Props): ReactNode { // Build version list - const versions = useVersions(docsPluginId); - - let configuredVersions: ConfiguredVersion[]; - if (staticVersions) { - // Keep only versions specified in configuration, reorder them accordingly - const versionMap = new Map( - versions.map((version) => [ - getVersionConfigurationName(version), - version, - ]), - ); - - configuredVersions = []; - for (const configuration of getVersionConfigurations(staticVersions)) { - let version = versionMap.get(configuration.name); - if (!version) { - // A version configuration references a non-existing version, ignore it - continue; - } - configuredVersions.push({version, configuration}); - } - } else { - // The versions are not configured - configuredVersions = versions.map((version): ConfiguredVersion => { - return {version}; - }); - } + const configuredVersions = configureVersions( + useVersions(docsPluginId), + staticVersions, + ); // Build item list const {search, hash} = useLocation(); From 9e166c37179bbd33a405feec35e4a0b297220e07 Mon Sep 17 00:00:00 2001 From: Oleksiy Gapotchenko Date: Sun, 19 Jan 2025 16:14:13 +0100 Subject: [PATCH 10/27] feat(theme-classic): implementation refactoring of version configurations in "docsVersionDropdown" navbar item --- .../src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx index b977bf4935f4..a55d58ca6e08 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx @@ -56,7 +56,7 @@ function configureVersions( staticVersions?: PropVersions, ): ConfiguredVersion[] { if (staticVersions) { - // The versions are configured. + // The versions are configured // Collect all the versions we have const versionMap = new Map( @@ -68,7 +68,7 @@ function configureVersions( for (const version of versions) { // 'version.name' has special conventions for current and next versions, // that's why we use 'version.label' as a secondary version identifier - // that can be referenced in configuration. + // that can be referenced in configuration const label = version.label; if (!versionMap.has(label)) versionMap.set(label, version); } @@ -76,7 +76,7 @@ function configureVersions( // Keep only versions specified in configuration, reorder them accordingly const configuredVersions: ConfiguredVersion[] = []; for (const configuration of getVersionConfigurations(staticVersions)) { - let version = versionMap.get(configuration.name); + const version = versionMap.get(configuration.name); if (!version) { // A version configuration references a non-existing version, ignore it continue; From a5afe09a6e1d9b71ccb78d3bd762912c954029fd Mon Sep 17 00:00:00 2001 From: Oleksiy Gapotchenko Date: Sun, 19 Jan 2025 23:21:13 +0100 Subject: [PATCH 11/27] feat(theme-classic): version configurations in "docsVersionDropdown" navbar item: code refactoring --- .../src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx index a55d58ca6e08..d116d9455879 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx @@ -33,7 +33,7 @@ type VersionConfiguration = {name: string} & PropVersionItem; function getVersionConfigurations( versions: PropVersions, ): VersionConfiguration[] { - if (versions instanceof Array) { + if (Array.isArray(versions)) { return versions.map((name): VersionConfiguration => { return {name}; }); From 88e306c0692c3db6f571f733336576dce299ea19 Mon Sep 17 00:00:00 2001 From: Oleksiy Gapotchenko Date: Mon, 20 Jan 2025 12:47:03 +0100 Subject: [PATCH 12/27] feat(theme): add versions attribute to docsVersionDropdown navbar item: inline options schema --- packages/docusaurus-theme-classic/src/options.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/options.ts b/packages/docusaurus-theme-classic/src/options.ts index 4f19d7cf3b56..3f43b888971d 100644 --- a/packages/docusaurus-theme-classic/src/options.ts +++ b/packages/docusaurus-theme-classic/src/options.ts @@ -208,8 +208,6 @@ const DropdownNavbarItemSchema = NavbarItemBaseSchema.append({ items: Joi.array().items(DropdownSubitemSchema).required(), }); -const DocsVersionNameSchema = Joi.string().min(1); - const DocsVersionDropdownNavbarItemSchema = NavbarItemBaseSchema.append({ type: Joi.string().equal('docsVersionDropdown').required(), docsPluginId: Joi.string(), @@ -217,9 +215,9 @@ const DocsVersionDropdownNavbarItemSchema = NavbarItemBaseSchema.append({ dropdownItemsBefore: Joi.array().items(DropdownSubitemSchema).default([]), dropdownItemsAfter: Joi.array().items(DropdownSubitemSchema).default([]), versions: Joi.alternatives().try( - Joi.array().items(DocsVersionNameSchema), + Joi.array().items(Joi.string().min(1)), Joi.object().pattern( - DocsVersionNameSchema, + Joi.string().min(1), Joi.object({ label: Joi.string().min(1), }), From 9088e6c78c83917570dfaaeb760a7a819e795fb1 Mon Sep 17 00:00:00 2001 From: Oleksiy Gapotchenko Date: Mon, 20 Jan 2025 14:03:51 +0100 Subject: [PATCH 13/27] docs(theme): added "configuring versioning representation" section to the versioning guide --- website/docs/guides/docs/versioning.mdx | 72 +++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/website/docs/guides/docs/versioning.mdx b/website/docs/guides/docs/versioning.mdx index 08fab227b542..4575f0dea2d8 100644 --- a/website/docs/guides/docs/versioning.mdx +++ b/website/docs/guides/docs/versioning.mdx @@ -271,6 +271,78 @@ These links would all look for an appropriate version to link to, in the followi 2. **Preferred version**: the version that the user last viewed. If there's no history, fall back to... 3. **Latest version**: the default version that we navigate to, configured by the `lastVersion` option. +## Configuring versioning representation {#configuring-versioning-representation} + +By default, the [`docsVersionDropdown`](../../api/themes/theme-configuration.mdx#navbar-docs-version-dropdown) displays all the versions provided by the docs. +However, sometimes it may be beneficial to restrict the number of displayed versions to simplify the user experience. +To achieve that, it is possible to specify a custom list of versions that should be offered in the dropdown: + +```js title="docusaurus.config.js" +export default { + themeConfig: { + navbar: { + items: [ + { + type: 'docsVersionDropdown', + // highlight-start + versions: ['2024.3', '2024.2', '2024.1', '2023.4'], + // highlight-end + }, + ], + }, + }, +}; +``` + +Some versioning schemes may benefit from an even more detailed configuration of displayed versions. +For example, if you are using semantic versioning, you may have versions such as: + +``` +2.1.1 +2.1.0 +2.0.1 +2.0.0 +1.0.1 +1.0.0 +``` + +And maybe you want to have them in the dropdown as: + +``` +2.1 +2.0 +1.0 +``` + +or maybe just: + +``` +1.x +2.x +``` + +To achieve that, you can specify a custom configuration for every version that should be displayed in the dropdown: + +```js title="docusaurus.config.js" +export default { + themeConfig: { + navbar: { + items: [ + { + type: 'docsVersionDropdown', + // highlight-start + versions: { + '1.0.1': {label: '1.x'}, + '2.1.1': {label: '2.x'}, + }, + // highlight-end + }, + ], + }, + }, +}; +``` + ## Recommended practices {#recommended-practices} ### Version your documentation only when needed {#version-your-documentation-only-when-needed} From f74b63813599bca52974155a4517660a363960a7 Mon Sep 17 00:00:00 2001 From: Oleksiy Gapotchenko Date: Mon, 20 Jan 2025 14:28:35 +0100 Subject: [PATCH 14/27] docs(theme): add versions attribute to docsVersionDropdown navbar item: cleaned up reference docs --- .../docs/api/themes/theme-configuration.mdx | 97 +++---------------- website/docs/guides/docs/versioning.mdx | 2 +- 2 files changed, 13 insertions(+), 86 deletions(-) diff --git a/website/docs/api/themes/theme-configuration.mdx b/website/docs/api/themes/theme-configuration.mdx index 637e22a7ab1b..8e1e952d0d29 100644 --- a/website/docs/api/themes/theme-configuration.mdx +++ b/website/docs/api/themes/theme-configuration.mdx @@ -597,12 +597,23 @@ Accepted fields: | `dropdownItemsAfter` | [LinkLikeItem](#navbar-dropdown)[] | `[]` | Add additional dropdown items at the end of the dropdown. | | `docsPluginId` | `string` | `'default'` | The ID of the docs plugin that the doc versioning belongs to. | | `dropdownActiveClassDisabled` | `boolean` | `false` | Do not add the link active class when browsing docs. | -| `versions` | string[] \| object | `undefined` | Specify a custom list of versions to include in the dropdown, or specify an object with detailed version configurations. | +| `versions` | `DropdownVersions` | `undefined` | Specify a custom list of versions to include in the dropdown. See [the versioning guide](../../versioning#configuring-representation) for details. | ```mdx-code-block ``` +Types: + +```ts +type DropdownVersion = { + /** The custom name to be shown in the dropdown for this version. */ + label?: string; +}; + +type DropdownVersions = string[] | {[versionName: string]: DropdownVersion}; +``` + Example configuration: ```js title="docusaurus.config.js" @@ -624,90 +635,6 @@ export default { }; ``` -By default, the version dropdown displays all the versions provided by the docs. -However, sometimes it may be beneficial to restrict the number of displayed versions to declutter the UI. -To achieve that, it is possible to specify a custom list of versions that should be offered in the dropdown: - -```js title="docusaurus.config.js" -export default { - themeConfig: { - navbar: { - items: [ - { - type: 'docsVersionDropdown', - // highlight-start - versions: ['2024.3', '2024.2', '2024.1', '2023.4'], - // highlight-end - }, - ], - }, - }, -}; -``` - -Some versioning schemes may benefit from an even more detailed configuration of displayed versions. -For example, if you are using semantic versioning, you may have versions such as: - -``` -2.1.1 -2.1.0 -2.0.1 -2.0.0 -1.0.1 -1.0.0 -``` - -And maybe you want to have them in the dropdown as: - -``` -2.1 -2.0 -1.0 -``` - -or maybe just: - -``` -1.x -2.x -``` - -To achieve that, you can specify a custom configuration for every version that should be displayed in the dropdown: - -```js title="docusaurus.config.js" -export default { - themeConfig: { - navbar: { - items: [ - { - type: 'docsVersionDropdown', - // highlight-start - versions: { - '1.0.1': {label: '1.x'}, - '2.1.1': {label: '2.x'}, - }, - // highlight-end - }, - ], - }, - }, -}; -``` - -The fields accepted by the custom version configuration: - -```mdx-code-block - -``` - -| Name | Type | Default | Description | -| --- | --- | --- | --- | -| `label` | `string` | Optional | The custom name to be shown in the dropdown for this version. | - -```mdx-code-block - -``` - #### Navbar docs version {#navbar-docs-version} If you use docs with versioning, this special navbar item type will link to the active/browsed version of your doc (depends on the current URL), and fallback to the latest version. diff --git a/website/docs/guides/docs/versioning.mdx b/website/docs/guides/docs/versioning.mdx index 4575f0dea2d8..36dbbd0d6855 100644 --- a/website/docs/guides/docs/versioning.mdx +++ b/website/docs/guides/docs/versioning.mdx @@ -271,7 +271,7 @@ These links would all look for an appropriate version to link to, in the followi 2. **Preferred version**: the version that the user last viewed. If there's no history, fall back to... 3. **Latest version**: the default version that we navigate to, configured by the `lastVersion` option. -## Configuring versioning representation {#configuring-versioning-representation} +## Configuring representation of versions {#configuring-representation} By default, the [`docsVersionDropdown`](../../api/themes/theme-configuration.mdx#navbar-docs-version-dropdown) displays all the versions provided by the docs. However, sometimes it may be beneficial to restrict the number of displayed versions to simplify the user experience. From 1f3ec765044db76b9a3454554589d39d2b1a8e1c Mon Sep 17 00:00:00 2001 From: Oleksiy Gapotchenko Date: Mon, 20 Jan 2025 15:15:16 +0100 Subject: [PATCH 15/27] docs(theme): add versions attribute to docsVersionDropdown navbar item: fixed link --- website/docs/api/themes/theme-configuration.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/api/themes/theme-configuration.mdx b/website/docs/api/themes/theme-configuration.mdx index 8e1e952d0d29..1ef840c69467 100644 --- a/website/docs/api/themes/theme-configuration.mdx +++ b/website/docs/api/themes/theme-configuration.mdx @@ -597,7 +597,7 @@ Accepted fields: | `dropdownItemsAfter` | [LinkLikeItem](#navbar-dropdown)[] | `[]` | Add additional dropdown items at the end of the dropdown. | | `docsPluginId` | `string` | `'default'` | The ID of the docs plugin that the doc versioning belongs to. | | `dropdownActiveClassDisabled` | `boolean` | `false` | Do not add the link active class when browsing docs. | -| `versions` | `DropdownVersions` | `undefined` | Specify a custom list of versions to include in the dropdown. See [the versioning guide](../../versioning#configuring-representation) for details. | +| `versions` | `DropdownVersions` | `undefined` | Specify a custom list of versions to include in the dropdown. See [the versioning guide](../../../versioning#configuring-representation) for details. | ```mdx-code-block From 8cbdf68334424b2c3a0653875d8a936716ff82d0 Mon Sep 17 00:00:00 2001 From: Oleksiy Gapotchenko Date: Sun, 26 Jan 2025 21:04:29 +0100 Subject: [PATCH 16/27] docs(theme): add versions attribute to docsVersionDropdown navbar item: changed title of the added section --- website/docs/api/themes/theme-configuration.mdx | 2 +- website/docs/guides/docs/versioning.mdx | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/website/docs/api/themes/theme-configuration.mdx b/website/docs/api/themes/theme-configuration.mdx index 1ef840c69467..6d201cac6b6a 100644 --- a/website/docs/api/themes/theme-configuration.mdx +++ b/website/docs/api/themes/theme-configuration.mdx @@ -597,7 +597,7 @@ Accepted fields: | `dropdownItemsAfter` | [LinkLikeItem](#navbar-dropdown)[] | `[]` | Add additional dropdown items at the end of the dropdown. | | `docsPluginId` | `string` | `'default'` | The ID of the docs plugin that the doc versioning belongs to. | | `dropdownActiveClassDisabled` | `boolean` | `false` | Do not add the link active class when browsing docs. | -| `versions` | `DropdownVersions` | `undefined` | Specify a custom list of versions to include in the dropdown. See [the versioning guide](../../../versioning#configuring-representation) for details. | +| `versions` | `DropdownVersions` | `undefined` | Specify a custom list of versions to include in the dropdown. See [the versioning guide](../../guides/docs/versioning.mdx#docsVersionDropdown) for details. | ```mdx-code-block diff --git a/website/docs/guides/docs/versioning.mdx b/website/docs/guides/docs/versioning.mdx index 36dbbd0d6855..f915549d30c3 100644 --- a/website/docs/guides/docs/versioning.mdx +++ b/website/docs/guides/docs/versioning.mdx @@ -271,11 +271,9 @@ These links would all look for an appropriate version to link to, in the followi 2. **Preferred version**: the version that the user last viewed. If there's no history, fall back to... 3. **Latest version**: the default version that we navigate to, configured by the `lastVersion` option. -## Configuring representation of versions {#configuring-representation} +## `docsVersionDropdown` {#docsVersionDropdown} -By default, the [`docsVersionDropdown`](../../api/themes/theme-configuration.mdx#navbar-docs-version-dropdown) displays all the versions provided by the docs. -However, sometimes it may be beneficial to restrict the number of displayed versions to simplify the user experience. -To achieve that, it is possible to specify a custom list of versions that should be offered in the dropdown: +By default, the [`docsVersionDropdown`](../../api/themes/theme-configuration.mdx#navbar-docs-version-dropdown) displays all the versions provided by the docs. However, sometimes it may be beneficial to restrict the number of displayed versions to simplify the user experience. To achieve that, it is possible to specify a custom list of versions that should be offered in the dropdown: ```js title="docusaurus.config.js" export default { @@ -294,8 +292,7 @@ export default { }; ``` -Some versioning schemes may benefit from an even more detailed configuration of displayed versions. -For example, if you are using semantic versioning, you may have versions such as: +Some versioning schemes may benefit from an even more detailed configuration of displayed versions. For example, if you are using semantic versioning, you may have versions such as: ``` 2.1.1 From afd9b8348ba3786e2b15e2d30715978a082a2515 Mon Sep 17 00:00:00 2001 From: Oleksiy Gapotchenko Date: Mon, 27 Jan 2025 12:37:27 +0100 Subject: [PATCH 17/27] feat(theme): add versions attribute to docsVersionDropdown navbar item: use version names only --- .../theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx | 10 ---------- website/docs/guides/docs/versioning.mdx | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx index d116d9455879..41f152733665 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx @@ -63,16 +63,6 @@ function configureVersions( versions.map((version) => [version.name, version]), ); - // Add secondary version identifiers to make the configuration process - // more natural to a user - for (const version of versions) { - // 'version.name' has special conventions for current and next versions, - // that's why we use 'version.label' as a secondary version identifier - // that can be referenced in configuration - const label = version.label; - if (!versionMap.has(label)) versionMap.set(label, version); - } - // Keep only versions specified in configuration, reorder them accordingly const configuredVersions: ConfiguredVersion[] = []; for (const configuration of getVersionConfigurations(staticVersions)) { diff --git a/website/docs/guides/docs/versioning.mdx b/website/docs/guides/docs/versioning.mdx index f915549d30c3..7449a2025243 100644 --- a/website/docs/guides/docs/versioning.mdx +++ b/website/docs/guides/docs/versioning.mdx @@ -283,7 +283,7 @@ export default { { type: 'docsVersionDropdown', // highlight-start - versions: ['2024.3', '2024.2', '2024.1', '2023.4'], + versions: ['current', '2024.2', '2024.1', '2023.4'], // highlight-end }, ], From 70f367a7bca8cb6dc1871eb7efc5879ee5b5041b Mon Sep 17 00:00:00 2001 From: Oleksiy Gapotchenko Date: Mon, 27 Jan 2025 13:54:20 +0100 Subject: [PATCH 18/27] feat(theme): add versions attribute to docsVersionDropdown navbar item: tests for new options --- .../src/__tests__/options.test.ts | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/packages/docusaurus-theme-classic/src/__tests__/options.test.ts b/packages/docusaurus-theme-classic/src/__tests__/options.test.ts index a228aba6f820..ac6c1162de36 100644 --- a/packages/docusaurus-theme-classic/src/__tests__/options.test.ts +++ b/packages/docusaurus-theme-classic/src/__tests__/options.test.ts @@ -827,6 +827,74 @@ describe('themeConfig', () => { ); }); }); + + describe('docsVersionDropdown', () => { + describe('versions', () => { + it('accepts array of strings', () => { + const config = { + navbar: { + items: [ + { + type: 'docsVersionDropdown', + versions: ['current', '1.0'], + }, + ], + }, + }; + testValidateThemeConfig(config); + }); + + it('rejects array of non-strings', () => { + const config = { + navbar: { + items: [ + { + type: 'docsVersionDropdown', + versions: [1, 2], + }, + ], + }, + }; + expect(() => + testValidateThemeConfig(config), + ).toThrowErrorMatchingInlineSnapshot( + `""navbar.items[0].versions[0]" must be a string"`, + ); + }); + + it('accepts dictionary of version objects', () => { + const config = { + navbar: { + items: [ + { + type: 'docsVersionDropdown', + versions: {current: {}, '1.0': {label: '1.x'}}, + }, + ], + }, + }; + testValidateThemeConfig(config); + }); + + it('rejects dictionary of invalid objects', () => { + const config = { + navbar: { + items: [ + { + type: 'docsVersionDropdown', + versions: {current: {}, '1.0': {invalid: '1.x'}}, + }, + ], + }, + }; + expect(() => + testValidateThemeConfig(config), + ).toThrowErrorMatchingInlineSnapshot( + `""navbar.items[0].versions.1.0.invalid" is not allowed"`, + ); + }); + }); + }); }); describe('validateOptions', () => { From 578bcca72a9769a6eb31da1fbedae46c3c445b36 Mon Sep 17 00:00:00 2001 From: Oleksiy Gapotchenko Date: Tue, 28 Jan 2025 01:51:36 +0100 Subject: [PATCH 19/27] feat(theme): add versions attribute to docsVersionDropdown navbar item: simplified code --- .../DocsVersionDropdownNavbarItem.tsx | 73 +++++++++---------- 1 file changed, 34 insertions(+), 39 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx index 41f152733665..9dc6cc621bb0 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx @@ -28,34 +28,16 @@ import type { ActiveDocContext, } from '@docusaurus/plugin-content-docs/client'; -type VersionConfiguration = {name: string} & PropVersionItem; - -function getVersionConfigurations( - versions: PropVersions, -): VersionConfiguration[] { - if (Array.isArray(versions)) { - return versions.map((name): VersionConfiguration => { - return {name}; - }); - } else { - return Object.entries(versions).map( - ([name, version]): VersionConfiguration => { - return {name, ...version}; - }, - ); - } -} - -type ConfiguredVersion = { +type VersionItem = { version: GlobalVersion; - configuration?: VersionConfiguration; + config?: PropVersionItem; }; -function configureVersions( +function getDropdownVersions( versions: GlobalVersion[], - staticVersions?: PropVersions, -): ConfiguredVersion[] { - if (staticVersions) { + configs?: PropVersions, +): VersionItem[] { + if (configs) { // The versions are configured // Collect all the versions we have @@ -63,21 +45,34 @@ function configureVersions( versions.map((version) => [version.name, version]), ); - // Keep only versions specified in configuration, reorder them accordingly - const configuredVersions: ConfiguredVersion[] = []; - for (const configuration of getVersionConfigurations(staticVersions)) { - const version = versionMap.get(configuration.name); + function getVersionItem( + name: string, + config?: PropVersionItem, + ): VersionItem | undefined { + const version = versionMap.get(name); if (!version) { // A version configuration references a non-existing version, ignore it - continue; + return undefined; } - configuredVersions.push({version, configuration}); + return {version, config}; } - return configuredVersions; + + // Keep only versions specified in configuration, reorder them accordingly + let versionItems: (VersionItem | undefined)[]; + if (Array.isArray(configs)) { + versionItems = configs.map((name) => getVersionItem(name, undefined)); + } else { + versionItems = Object.entries(configs).map(([name, config]) => + getVersionItem(name, config), + ); + } + + // Filter out ignored items + return versionItems.filter((x) => x !== undefined); } else { // The versions are not configured - return versions.map((version): ConfiguredVersion => { - return {version}; + return versions.map((version) => { + return {version, config: undefined}; }); } } @@ -104,13 +99,13 @@ export default function DocsVersionDropdownNavbarItem({ dropdownActiveClassDisabled, dropdownItemsBefore, dropdownItemsAfter, - versions: staticVersions, + versions: versionConfigs, ...props }: Props): ReactNode { // Build version list - const configuredVersions = configureVersions( + const dropdownVersions = getDropdownVersions( useVersions(docsPluginId), - staticVersions, + versionConfigs, ); // Build item list @@ -120,11 +115,11 @@ export default function DocsVersionDropdownNavbarItem({ function versionToLink( version: GlobalVersion, - versionConfiguration?: VersionConfiguration, + config?: PropVersionItem, ): LinkLikeNavbarItemProps { const targetDoc = getVersionTargetDoc(version, activeDocContext); return { - label: versionConfiguration?.label ?? version.label, + label: config?.label ?? version.label, // preserve ?search#hash suffix on version switches to: `${targetDoc.path}${search}${hash}`, isActive: () => version === activeDocContext.activeVersion, @@ -134,7 +129,7 @@ export default function DocsVersionDropdownNavbarItem({ const items: LinkLikeNavbarItemProps[] = [ ...dropdownItemsBefore, - ...configuredVersions.map((x) => versionToLink(x.version, x.configuration)), + ...dropdownVersions.map((item) => versionToLink(item.version, item.config)), ...dropdownItemsAfter, ]; From 76b8062e1d43e899da928ed929d2adc67dbac10c Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 30 Jan 2025 16:13:05 +0100 Subject: [PATCH 20/27] simplify docsVersionDropdown docs --- website/docs/guides/docs/versioning.mdx | 41 ++++++------------------- 1 file changed, 9 insertions(+), 32 deletions(-) diff --git a/website/docs/guides/docs/versioning.mdx b/website/docs/guides/docs/versioning.mdx index 7449a2025243..196f7a379077 100644 --- a/website/docs/guides/docs/versioning.mdx +++ b/website/docs/guides/docs/versioning.mdx @@ -258,7 +258,7 @@ See [docs plugin configuration](../../api/plugins/plugin-content-docs.mdx#config ## Navbar items {#navbar-items} -We offer several navbar items to help you quickly set up navigation without worrying about versioned routes. +We offer several docs navbar items to help you quickly set up navigation without worrying about versioned routes. - [`doc`](../../api/themes/theme-configuration.mdx#navbar-doc-link): a link to a doc. - [`docSidebar`](../../api/themes/theme-configuration.mdx#navbar-doc-sidebar): a link to the first item in a sidebar. @@ -273,7 +273,9 @@ These links would all look for an appropriate version to link to, in the followi ## `docsVersionDropdown` {#docsVersionDropdown} -By default, the [`docsVersionDropdown`](../../api/themes/theme-configuration.mdx#navbar-docs-version-dropdown) displays all the versions provided by the docs. However, sometimes it may be beneficial to restrict the number of displayed versions to simplify the user experience. To achieve that, it is possible to specify a custom list of versions that should be offered in the dropdown: +By default, the [`docsVersionDropdown`](../../api/themes/theme-configuration.mdx#navbar-docs-version-dropdown) displays a dropdown with all the available docs versions. + +The `versions` attribute allows you to display a subset of the available docs versions in a given order: ```js title="docusaurus.config.js" export default { @@ -283,7 +285,7 @@ export default { { type: 'docsVersionDropdown', // highlight-start - versions: ['current', '2024.2', '2024.1', '2023.4'], + versions: ['current', '3.0', '2.0'], // highlight-end }, ], @@ -292,33 +294,7 @@ export default { }; ``` -Some versioning schemes may benefit from an even more detailed configuration of displayed versions. For example, if you are using semantic versioning, you may have versions such as: - -``` -2.1.1 -2.1.0 -2.0.1 -2.0.0 -1.0.1 -1.0.0 -``` - -And maybe you want to have them in the dropdown as: - -``` -2.1 -2.0 -1.0 -``` - -or maybe just: - -``` -1.x -2.x -``` - -To achieve that, you can specify a custom configuration for every version that should be displayed in the dropdown: +Passing a `versions` object, lets you override the display label of each version: ```js title="docusaurus.config.js" export default { @@ -329,8 +305,9 @@ export default { type: 'docsVersionDropdown', // highlight-start versions: { - '1.0.1': {label: '1.x'}, - '2.1.1': {label: '2.x'}, + current: {label: 'Version 4.0'}, + '3.0': {label: 'Version 3.0'}, + '2.0': {label: 'Version 2.0'}, }, // highlight-end }, From 98a5b4f689d053d6c95bf8b6adcc2c43b6905c2c Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 30 Jan 2025 16:15:17 +0100 Subject: [PATCH 21/27] simplify docsVersionDropdown docs --- website/docs/api/themes/theme-configuration.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/api/themes/theme-configuration.mdx b/website/docs/api/themes/theme-configuration.mdx index 6d201cac6b6a..7349ed7983de 100644 --- a/website/docs/api/themes/theme-configuration.mdx +++ b/website/docs/api/themes/theme-configuration.mdx @@ -607,7 +607,7 @@ Types: ```ts type DropdownVersion = { - /** The custom name to be shown in the dropdown for this version. */ + /** Allows you to provide a custom display label for each version. */ label?: string; }; From 2654ab90931fc19818bc734cb61a254fa7ce49a0 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 30 Jan 2025 16:27:11 +0100 Subject: [PATCH 22/27] extract useVersionItems() hook --- .../DocsVersionDropdownNavbarItem.tsx | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx index 9dc6cc621bb0..adf6ff7c791f 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx @@ -33,7 +33,7 @@ type VersionItem = { config?: PropVersionItem; }; -function getDropdownVersions( +function getVersionItems( versions: GlobalVersion[], configs?: PropVersions, ): VersionItem[] { @@ -45,6 +45,7 @@ function getDropdownVersions( versions.map((version) => [version.name, version]), ); + // eslint-disable-next-line no-inner-declarations function getVersionItem( name: string, config?: PropVersionItem, @@ -77,6 +78,16 @@ function getDropdownVersions( } } +function useVersionItems({ + docsPluginId, + configs, +}: { + docsPluginId: Props['docsPluginId']; + configs: Props['versions']; +}): VersionItem[] { + return getVersionItems(useVersions(docsPluginId), configs); +} + function getVersionMainDoc(version: GlobalVersion): GlobalDoc { return version.docs.find((doc) => doc.id === version.mainDocId)!; } @@ -99,24 +110,18 @@ export default function DocsVersionDropdownNavbarItem({ dropdownActiveClassDisabled, dropdownItemsBefore, dropdownItemsAfter, - versions: versionConfigs, + versions: configs, ...props }: Props): ReactNode { - // Build version list - const dropdownVersions = getDropdownVersions( - useVersions(docsPluginId), - versionConfigs, - ); - - // Build item list const {search, hash} = useLocation(); const activeDocContext = useActiveDocContext(docsPluginId); const {savePreferredVersionName} = useDocsPreferredVersion(docsPluginId); + const versionItems = useVersionItems({docsPluginId, configs}); - function versionToLink( - version: GlobalVersion, - config?: PropVersionItem, - ): LinkLikeNavbarItemProps { + function versionItemToLink({ + version, + config, + }: VersionItem): LinkLikeNavbarItemProps { const targetDoc = getVersionTargetDoc(version, activeDocContext); return { label: config?.label ?? version.label, @@ -129,7 +134,7 @@ export default function DocsVersionDropdownNavbarItem({ const items: LinkLikeNavbarItemProps[] = [ ...dropdownItemsBefore, - ...dropdownVersions.map((item) => versionToLink(item.version, item.config)), + ...versionItems.map(versionItemToLink), ...dropdownItemsAfter, ]; From ad24578764354b9e3c96bec1be3f22b6f8fd597f Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 30 Jan 2025 16:43:30 +0100 Subject: [PATCH 23/27] simplify DocsVersionDropdown impl, fail fast --- .../DocsVersionDropdownNavbarItem.tsx | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx index adf6ff7c791f..c42b0660e6e0 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx @@ -45,33 +45,26 @@ function getVersionItems( versions.map((version) => [version.name, version]), ); - // eslint-disable-next-line no-inner-declarations - function getVersionItem( + const toVersionItem = ( name: string, config?: PropVersionItem, - ): VersionItem | undefined { + ): VersionItem => { const version = versionMap.get(name); if (!version) { - // A version configuration references a non-existing version, ignore it - return undefined; + throw new Error(`No docs version exist for name '${name}', please verify your 'docsVersionDropdown' navbar item versions config. +Available version names:\n- ${versions.map((v) => `${v.name}`).join('\n- ')}`); } return {version, config}; - } + }; - // Keep only versions specified in configuration, reorder them accordingly - let versionItems: (VersionItem | undefined)[]; if (Array.isArray(configs)) { - versionItems = configs.map((name) => getVersionItem(name, undefined)); + return configs.map((name) => toVersionItem(name, undefined)); } else { - versionItems = Object.entries(configs).map(([name, config]) => - getVersionItem(name, config), + return Object.entries(configs).map(([name, config]) => + toVersionItem(name, config), ); } - - // Filter out ignored items - return versionItems.filter((x) => x !== undefined); } else { - // The versions are not configured return versions.map((version) => { return {version, config: undefined}; }); @@ -85,7 +78,8 @@ function useVersionItems({ docsPluginId: Props['docsPluginId']; configs: Props['versions']; }): VersionItem[] { - return getVersionItems(useVersions(docsPluginId), configs); + const versions = useVersions(docsPluginId); + return getVersionItems(versions, configs); } function getVersionMainDoc(version: GlobalVersion): GlobalDoc { From 38b581d6eb72436b48b2b59301caeb9fcc08c929 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 30 Jan 2025 16:44:30 +0100 Subject: [PATCH 24/27] import fix --- packages/docusaurus-theme-classic/src/options.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus-theme-classic/src/options.ts b/packages/docusaurus-theme-classic/src/options.ts index 3f43b888971d..b92de965996c 100644 --- a/packages/docusaurus-theme-classic/src/options.ts +++ b/packages/docusaurus-theme-classic/src/options.ts @@ -7,11 +7,11 @@ import {themes} from 'prism-react-renderer'; import {Joi, URISchema} from '@docusaurus/utils-validation'; -import type {Options, PluginOptions} from '@docusaurus/theme-classic'; import type { PropVersionItem, PropVersionItems, } from '@theme/NavbarItem/DocsVersionDropdownNavbarItem'; +import type {Options, PluginOptions} from '@docusaurus/theme-classic'; import type {ThemeConfig} from '@docusaurus/theme-common'; import type { ThemeConfigValidationContext, From ec22efa257411f96decff6651dcbea449616b116 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 30 Jan 2025 16:46:58 +0100 Subject: [PATCH 25/27] docs version dropdown: versions should have at least one version --- .../src/__tests__/options.test.ts | 36 +++++++++++++++++++ .../docusaurus-theme-classic/src/options.ts | 16 +++++---- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/__tests__/options.test.ts b/packages/docusaurus-theme-classic/src/__tests__/options.test.ts index ac6c1162de36..0d21ee617c6e 100644 --- a/packages/docusaurus-theme-classic/src/__tests__/options.test.ts +++ b/packages/docusaurus-theme-classic/src/__tests__/options.test.ts @@ -844,6 +844,24 @@ describe('themeConfig', () => { testValidateThemeConfig(config); }); + it('rejects empty array of strings', () => { + const config = { + navbar: { + items: [ + { + type: 'docsVersionDropdown', + versions: [], + }, + ], + }, + }; + expect(() => + testValidateThemeConfig(config), + ).toThrowErrorMatchingInlineSnapshot( + `""navbar.items[0].versions" must contain at least 1 items"`, + ); + }); + it('rejects array of non-strings', () => { const config = { navbar: { @@ -876,6 +894,24 @@ describe('themeConfig', () => { testValidateThemeConfig(config); }); + it('rejects empty dictionary of objects', () => { + const config = { + navbar: { + items: [ + { + type: 'docsVersionDropdown', + versions: {}, + }, + ], + }, + }; + expect(() => + testValidateThemeConfig(config), + ).toThrowErrorMatchingInlineSnapshot( + `""navbar.items[0].versions" must have at least 1 key"`, + ); + }); + it('rejects dictionary of invalid objects', () => { const config = { navbar: { diff --git a/packages/docusaurus-theme-classic/src/options.ts b/packages/docusaurus-theme-classic/src/options.ts index b92de965996c..f4f84b5b1fbc 100644 --- a/packages/docusaurus-theme-classic/src/options.ts +++ b/packages/docusaurus-theme-classic/src/options.ts @@ -215,13 +215,15 @@ const DocsVersionDropdownNavbarItemSchema = NavbarItemBaseSchema.append({ dropdownItemsBefore: Joi.array().items(DropdownSubitemSchema).default([]), dropdownItemsAfter: Joi.array().items(DropdownSubitemSchema).default([]), versions: Joi.alternatives().try( - Joi.array().items(Joi.string().min(1)), - Joi.object().pattern( - Joi.string().min(1), - Joi.object({ - label: Joi.string().min(1), - }), - ), + Joi.array().items(Joi.string().min(1)).min(1), + Joi.object() + .pattern( + Joi.string().min(1), + Joi.object({ + label: Joi.string().min(1), + }), + ) + .min(1), ), }); From 9bda5d73c27ea0effb1cdbfc64f91eee10c958c1 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 30 Jan 2025 17:12:01 +0100 Subject: [PATCH 26/27] docs version dropdown should not display a version that is not in the provided config --- .../DocsVersionDropdownNavbarItem.tsx | 45 +++++++++++++------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx index c42b0660e6e0..6a94566a8b13 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx @@ -30,7 +30,7 @@ import type { type VersionItem = { version: GlobalVersion; - config?: PropVersionItem; + label: string; }; function getVersionItems( @@ -38,8 +38,6 @@ function getVersionItems( configs?: PropVersions, ): VersionItem[] { if (configs) { - // The versions are configured - // Collect all the versions we have const versionMap = new Map( versions.map((version) => [version.name, version]), @@ -54,7 +52,7 @@ function getVersionItems( throw new Error(`No docs version exist for name '${name}', please verify your 'docsVersionDropdown' navbar item versions config. Available version names:\n- ${versions.map((v) => `${v.name}`).join('\n- ')}`); } - return {version, config}; + return {version, label: config?.label ?? version.label}; }; if (Array.isArray(configs)) { @@ -65,9 +63,7 @@ Available version names:\n- ${versions.map((v) => `${v.name}`).join('\n- ')}`); ); } } else { - return versions.map((version) => { - return {version, config: undefined}; - }); + return versions.map((version) => ({version, label: version.label})); } } @@ -98,6 +94,25 @@ function getVersionTargetDoc( ); } +// The version item to use for the "dropdown button" +function useDisplayedVersionItem({ + docsPluginId, + versionItems, +}: { + docsPluginId: Props['docsPluginId']; + versionItems: VersionItem[]; +}): VersionItem { + const candidates = useDocsVersionCandidates(docsPluginId); + const displayedVersion = + candidates.find((candidate) => + versionItems.some((vi) => vi.version === candidate), + ) ?? versionItems[0]!.version; + const displayedVersionItem = versionItems.find( + (vi) => vi.version === displayedVersion, + )!; + return displayedVersionItem; +} + export default function DocsVersionDropdownNavbarItem({ mobile, docsPluginId, @@ -111,14 +126,18 @@ export default function DocsVersionDropdownNavbarItem({ const activeDocContext = useActiveDocContext(docsPluginId); const {savePreferredVersionName} = useDocsPreferredVersion(docsPluginId); const versionItems = useVersionItems({docsPluginId, configs}); + const displayedVersionItem = useDisplayedVersionItem({ + docsPluginId, + versionItems, + }); function versionItemToLink({ version, - config, + label, }: VersionItem): LinkLikeNavbarItemProps { const targetDoc = getVersionTargetDoc(version, activeDocContext); return { - label: config?.label ?? version.label, + label, // preserve ?search#hash suffix on version switches to: `${targetDoc.path}${search}${hash}`, isActive: () => version === activeDocContext.activeVersion, @@ -132,8 +151,6 @@ export default function DocsVersionDropdownNavbarItem({ ...dropdownItemsAfter, ]; - const dropdownVersion = useDocsVersionCandidates(docsPluginId)[0]; - // Mobile dropdown is handled a bit differently const dropdownLabel = mobile && items.length > 1 @@ -143,11 +160,13 @@ export default function DocsVersionDropdownNavbarItem({ description: 'The label for the navbar versions dropdown on mobile view', }) - : dropdownVersion.label; + : displayedVersionItem.label; + const dropdownTo = mobile && items.length > 1 ? undefined - : getVersionTargetDoc(dropdownVersion, activeDocContext).path; + : getVersionTargetDoc(displayedVersionItem.version, activeDocContext) + .path; // We don't want to render a version dropdown with 0 or 1 item. If we build // the site with a single docs version (onlyIncludeVersions: ['1.0.0']), From 5475e3b93cfcabd9a0eb738d44171d36b6e83aa8 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 30 Jan 2025 17:23:02 +0100 Subject: [PATCH 27/27] simpler impl for useDisplayedVersionItem --- .../NavbarItem/DocsVersionDropdownNavbarItem.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx index 6a94566a8b13..1e2d0bbf7a19 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx @@ -102,15 +102,12 @@ function useDisplayedVersionItem({ docsPluginId: Props['docsPluginId']; versionItems: VersionItem[]; }): VersionItem { + // The order of the candidates matters! const candidates = useDocsVersionCandidates(docsPluginId); - const displayedVersion = - candidates.find((candidate) => - versionItems.some((vi) => vi.version === candidate), - ) ?? versionItems[0]!.version; - const displayedVersionItem = versionItems.find( - (vi) => vi.version === displayedVersion, - )!; - return displayedVersionItem; + const candidateItems = candidates + .map((candidate) => versionItems.find((vi) => vi.version === candidate)) + .filter((vi) => vi !== undefined); + return candidateItems[0] ?? versionItems[0]!; } export default function DocsVersionDropdownNavbarItem({