From 5a3c38e1fd90d03869b794ed63b1013ac328e577 Mon Sep 17 00:00:00 2001 From: slorber Date: Tue, 6 Oct 2020 14:55:50 +0200 Subject: [PATCH 01/12] persist docs preferred version --- .../src/theme/DocVersionSuggestions/index.tsx | 18 ++++++- .../DocsVersionDropdownNavbarItem.tsx | 14 +++++- .../useDocsPreferredVersionPersistence.ts | 50 +++++++++++++++++++ .../src/utils/useThemeConfig.ts | 4 ++ .../src/validateThemeConfig.js | 11 ++++ 5 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 packages/docusaurus-theme-classic/src/utils/useDocsPreferredVersionPersistence.ts diff --git a/packages/docusaurus-theme-classic/src/theme/DocVersionSuggestions/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocVersionSuggestions/index.tsx index 5d2168b1b2d0..935a38bb0aad 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocVersionSuggestions/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocVersionSuggestions/index.tsx @@ -13,6 +13,7 @@ import { useActiveVersion, useDocVersionSuggestions, } from '@theme/hooks/useDocs'; +import {useDocsPreferredVersionPersistence} from '../../utils/useDocsPreferredVersionPersistence'; const getVersionMainDoc = (version) => version.docs.find((doc) => doc.id === version.mainDocId); @@ -22,6 +23,11 @@ function DocVersionSuggestions(): JSX.Element { siteConfig: {title: siteTitle}, } = useDocusaurusContext(); const {pluginId} = useActivePlugin({failfast: true}); + + const preferredVersionPersistence = useDocsPreferredVersionPersistence({ + docsPluginId: pluginId, + }); + const activeVersion = useActiveVersion(pluginId); const { latestDocSuggestion, @@ -35,7 +41,7 @@ function DocVersionSuggestions(): JSX.Element { // try to link to same doc in latest version (not always possible) // fallback to main doc of latest version - const suggestedDoc = + const latestVersionSuggestedDoc = latestDocSuggestion ?? getVersionMainDoc(latestVersionSuggestion); return ( @@ -58,7 +64,15 @@ function DocVersionSuggestions(): JSX.Element {
For up-to-date documentation, see the{' '} - latest version + + preferredVersionPersistence.setVersionName( + latestVersionSuggestion.name, + ) + }> + latest version + {' '} ({latestVersionSuggestion.label}).
diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx index 71db1f6ee16a..02f66ab58050 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx @@ -13,6 +13,7 @@ import { useActiveDocContext, } from '@theme/hooks/useDocs'; import type {Props} from '@theme/NavbarItem/DocsVersionDropdownNavbarItem'; +import {useDocsPreferredVersionPersistence} from '../../utils/useDocsPreferredVersionPersistence'; const getVersionMainDoc = (version) => version.docs.find((doc) => doc.id === version.mainDocId); @@ -26,6 +27,13 @@ export default function DocsVersionDropdownNavbarItem({ const versions = useVersions(docsPluginId); const latestVersion = useLatestVersion(docsPluginId); + const preferredVersionPersistence = useDocsPreferredVersionPersistence({ + docsPluginId, + }); + const preferredVersion = versions.find( + (version) => version.name === preferredVersionPersistence.versionName, + ); + function getItems() { // 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']) @@ -45,11 +53,15 @@ export default function DocsVersionDropdownNavbarItem({ label: version.label, to: versionDoc.path, isActive: () => version === activeDocContext?.activeVersion, + onClick: () => { + preferredVersionPersistence.setVersionName(version.name); + }, }; }); } - const dropdownVersion = activeDocContext.activeVersion ?? latestVersion; + const dropdownVersion = + activeDocContext.activeVersion ?? preferredVersion ?? latestVersion; // Mobile is handled a bit differently const dropdownLabel = mobile ? 'Versions' : dropdownVersion.label; diff --git a/packages/docusaurus-theme-classic/src/utils/useDocsPreferredVersionPersistence.ts b/packages/docusaurus-theme-classic/src/utils/useDocsPreferredVersionPersistence.ts new file mode 100644 index 000000000000..1b7fc14972e1 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/utils/useDocsPreferredVersionPersistence.ts @@ -0,0 +1,50 @@ +/** + * 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 {useCallback, useEffect, useState} from 'react'; +import useThemeConfig from './useThemeConfig'; + +type DocsPreferredVersionConfig = { + docsPluginId: string | undefined; +}; + +type DocsPreferredVersionState = string | null; + +export function useDocsPreferredVersionPersistence({ + docsPluginId, +}: DocsPreferredVersionConfig) { + const { + docs: {versionPersistence}, + } = useThemeConfig(); + const storageKey = `docs-preferred-version-${docsPluginId ?? 'default'}`; + + const [versionState, setVersionState] = useState( + null, + ); + + // Init preferred version after React hydration + useEffect(() => { + if (versionPersistence === 'none') { + return; + } + + setVersionState(window.localStorage.getItem(storageKey)); + }, [storageKey]); + + const setVersionName = useCallback( + (version: string) => { + setVersionState(version); + + if (versionPersistence === 'none') { + return; + } + window.localStorage.setItem(storageKey, version); + }, + [setVersionState, storageKey], + ); + + return {versionName: versionState, setVersionName} as const; +} diff --git a/packages/docusaurus-theme-classic/src/utils/useThemeConfig.ts b/packages/docusaurus-theme-classic/src/utils/useThemeConfig.ts index db8b66a6b6d3..a9ae70f175db 100644 --- a/packages/docusaurus-theme-classic/src/utils/useThemeConfig.ts +++ b/packages/docusaurus-theme-classic/src/utils/useThemeConfig.ts @@ -7,6 +7,10 @@ import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; type ThemeConfig = { + docs: { + versionPersistence: 'localStorage' | 'none'; + }; + // TODO we should complete this theme config type over time // and share it across all themes // and use it in the Joi validation schema? diff --git a/packages/docusaurus-theme-classic/src/validateThemeConfig.js b/packages/docusaurus-theme-classic/src/validateThemeConfig.js index 6b86e99b4098..6c76f06e4ec6 100644 --- a/packages/docusaurus-theme-classic/src/validateThemeConfig.js +++ b/packages/docusaurus-theme-classic/src/validateThemeConfig.js @@ -8,6 +8,15 @@ const Joi = require('@hapi/joi'); const {URISchema} = require('@docusaurus/utils-validation'); +const DEFAULT_DOCS_CONFIG = { + versionPersistence: 'localStorage', +}; +const DocsSchema = Joi.object({ + versionPersistence: Joi.string() + .equal('localStorage', 'none') + .default(DEFAULT_DOCS_CONFIG.versionPersistence), +}).default(DEFAULT_DOCS_CONFIG); + const DEFAULT_COLOR_MODE_CONFIG = { defaultMode: 'light', disableSwitch: false, @@ -22,6 +31,7 @@ const DEFAULT_COLOR_MODE_CONFIG = { const DEFAULT_CONFIG = { colorMode: DEFAULT_COLOR_MODE_CONFIG, + docs: DEFAULT_DOCS_CONFIG, metadatas: [], prism: { additionalLanguages: [], @@ -190,6 +200,7 @@ const ThemeConfigSchema = Joi.object({ customCss: CustomCssSchema, colorMode: ColorModeSchema, image: Joi.string(), + docs: DocsSchema, metadatas: Joi.array() .items(HtmlMetadataSchema) .default(DEFAULT_CONFIG.metadatas), From 40f626f4f60a103d9f06c4fe970e2855cce93809 Mon Sep 17 00:00:00 2001 From: slorber Date: Fri, 9 Oct 2020 18:30:18 +0200 Subject: [PATCH 02/12] add proper implementation for useDocsPreferredVersion --- .../src/theme/hooks/useDocs.ts | 4 +- .../src/theme/DocVersionSuggestions/index.tsx | 10 +- .../src/theme/Layout/index.tsx | 7 +- .../DocsVersionDropdownNavbarItem.tsx | 9 +- .../DocsPreferredVersionProvider.tsx | 130 ++++++++++++++++++ .../DocsPreferredVersionStorage.ts | 44 ++++++ .../useDocsPreferredVersion.ts | 31 +++++ .../useDocsPreferredVersionPersistence.ts | 50 ------- .../src/utils/useThemeConfig.ts | 6 +- .../src/client/exports/constants.ts | 14 ++ 10 files changed, 237 insertions(+), 68 deletions(-) create mode 100644 packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx create mode 100644 packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionStorage.ts create mode 100644 packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts delete mode 100644 packages/docusaurus-theme-classic/src/utils/useDocsPreferredVersionPersistence.ts create mode 100644 packages/docusaurus/src/client/exports/constants.ts diff --git a/packages/docusaurus-plugin-content-docs/src/theme/hooks/useDocs.ts b/packages/docusaurus-plugin-content-docs/src/theme/hooks/useDocs.ts index eea342cbeb6d..bbf3cff1e81b 100644 --- a/packages/docusaurus-plugin-content-docs/src/theme/hooks/useDocs.ts +++ b/packages/docusaurus-plugin-content-docs/src/theme/hooks/useDocs.ts @@ -21,10 +21,10 @@ import { GetActivePluginOptions, } from '../../client/docsClientUtils'; -const useAllDocsData = (): Record => +export const useAllDocsData = (): Record => useAllPluginInstancesData('docusaurus-plugin-content-docs'); -const useDocsData = (pluginId: string | undefined) => +export const useDocsData = (pluginId: string | undefined) => usePluginData('docusaurus-plugin-content-docs', pluginId) as GlobalPluginData; export const useActivePlugin = (options: GetActivePluginOptions = {}) => { diff --git a/packages/docusaurus-theme-classic/src/theme/DocVersionSuggestions/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocVersionSuggestions/index.tsx index 935a38bb0aad..81f171f1f468 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocVersionSuggestions/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocVersionSuggestions/index.tsx @@ -13,7 +13,7 @@ import { useActiveVersion, useDocVersionSuggestions, } from '@theme/hooks/useDocs'; -import {useDocsPreferredVersionPersistence} from '../../utils/useDocsPreferredVersionPersistence'; +import useDocsPreferredVersion from '../../utils/docsPreferredVersion/useDocsPreferredVersion'; const getVersionMainDoc = (version) => version.docs.find((doc) => doc.id === version.mainDocId); @@ -24,9 +24,7 @@ function DocVersionSuggestions(): JSX.Element { } = useDocusaurusContext(); const {pluginId} = useActivePlugin({failfast: true}); - const preferredVersionPersistence = useDocsPreferredVersionPersistence({ - docsPluginId: pluginId, - }); + const {savePreferredVersionName} = useDocsPreferredVersion(pluginId); const activeVersion = useActiveVersion(pluginId); const { @@ -67,9 +65,7 @@ function DocVersionSuggestions(): JSX.Element { - preferredVersionPersistence.setVersionName( - latestVersionSuggestion.name, - ) + savePreferredVersionName(latestVersionSuggestion.name) }> latest version diff --git a/packages/docusaurus-theme-classic/src/theme/Layout/index.tsx b/packages/docusaurus-theme-classic/src/theme/Layout/index.tsx index 452fa26a3631..5ac0799497a0 100644 --- a/packages/docusaurus-theme-classic/src/theme/Layout/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Layout/index.tsx @@ -19,11 +19,16 @@ import Footer from '@theme/Footer'; import type {Props} from '@theme/Layout'; import './styles.css'; +import DocsPreferredVersionContextProvider from '../../utils/docsPreferredVersion/DocsPreferredVersionProvider'; function Providers({children}) { return ( - {children} + + + {children} + + ); } diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx index 02f66ab58050..f42e0de7d175 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx @@ -13,7 +13,7 @@ import { useActiveDocContext, } from '@theme/hooks/useDocs'; import type {Props} from '@theme/NavbarItem/DocsVersionDropdownNavbarItem'; -import {useDocsPreferredVersionPersistence} from '../../utils/useDocsPreferredVersionPersistence'; +import useDocsPreferredVersion from '../../utils/docsPreferredVersion/useDocsPreferredVersion'; const getVersionMainDoc = (version) => version.docs.find((doc) => doc.id === version.mainDocId); @@ -27,11 +27,8 @@ export default function DocsVersionDropdownNavbarItem({ const versions = useVersions(docsPluginId); const latestVersion = useLatestVersion(docsPluginId); - const preferredVersionPersistence = useDocsPreferredVersionPersistence({ + const {preferredVersion, savePreferredVersionName} = useDocsPreferredVersion( docsPluginId, - }); - const preferredVersion = versions.find( - (version) => version.name === preferredVersionPersistence.versionName, ); function getItems() { @@ -54,7 +51,7 @@ export default function DocsVersionDropdownNavbarItem({ to: versionDoc.path, isActive: () => version === activeDocContext?.activeVersion, onClick: () => { - preferredVersionPersistence.setVersionName(version.name); + savePreferredVersionName(version.name); }, }; }); diff --git a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx new file mode 100644 index 000000000000..12616b2f0215 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx @@ -0,0 +1,130 @@ +/** + * 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, { + createContext, + ReactNode, + useContext, + useMemo, + useState, +} from 'react'; +import useThemeConfig, {DocsVersionPersistence} from '../useThemeConfig'; + +import {useAllDocsData} from '@theme/hooks/useDocs'; +import DocsPreferredVersionStorage from './DocsPreferredVersionStorage'; + +type DocsPreferredVersionName = string | null; + +// State for a single docs plugin instance +type DocsPreferredVersionPluginState = { + preferredVersionName: DocsPreferredVersionName; +}; + +// We need to store in state/storage globally +// one preferred version per docs plugin instance +// pluginId => pluginState +type DocsPreferredVersionState = Record< + string, + DocsPreferredVersionPluginState +>; + +function useVersionPersistence(): DocsVersionPersistence { + return useThemeConfig().docs.versionPersistence; +} + +// Read local storage for all docs plugins +// Assign to each doc plugin a preferred version (if found) +function useInitialState(): DocsPreferredVersionState { + const allDocsData = useAllDocsData(); + const versionPersistence = useVersionPersistence(); + + function computeInitialState(): DocsPreferredVersionState { + // The storage value we read might be stale, + // and belong to a version that does not exist in the site anymore + // In such case, we remove the storage value to avoid downstream errors + function readPreferredVersionName( + pluginId: string, + ): DocsPreferredVersionName { + const preferredVersionNameUnsafe = DocsPreferredVersionStorage.read( + pluginId, + versionPersistence, + ); + const pluginData = allDocsData[pluginId]; + const versionExists = pluginData.versions.some( + (version) => version.name === preferredVersionNameUnsafe, + ); + if (versionExists) { + return preferredVersionNameUnsafe; + } else { + DocsPreferredVersionStorage.clear(pluginId, versionPersistence); + return null; + } + } + + const initialState: DocsPreferredVersionState = {}; + Object.keys(allDocsData).forEach((pluginId) => { + initialState[pluginId] = { + preferredVersionName: readPreferredVersionName(pluginId), + }; + }); + + return initialState; + } + + return useMemo(computeInitialState, [allDocsData]); +} + +// Value that will be accessible through context: [state,api] +function useContextValue() { + const initialState = useInitialState(); + const versionPersistence = useVersionPersistence(); + + const [state, setState] = useState(initialState); + + // Return a constant api object + const api = useMemo(() => { + function savePreferredVersion(pluginId: string, versionName: string) { + DocsPreferredVersionStorage.save( + pluginId, + versionPersistence, + versionName, + ); + setState((s) => ({ + ...s, + [pluginId]: {preferredVersionName: versionName}, + })); + } + + return { + savePreferredVersion, + }; + }, [setState]); + + return [state, api] as const; +} + +type DocsPreferredVersionContextValue = ReturnType; + +const Context = createContext(null); + +export default function DocsPreferredVersionContextProvider({ + children, +}: { + children: ReactNode; +}) { + const contextValue = useContextValue(); + return {children}; +} + +export function useDocsPreferredVersionContext(): DocsPreferredVersionContextValue { + const value = useContext(Context); + if (!value) { + throw new Error( + "Can't find docs preferred context, maybe you forgot to use the DocsPreferredVersionContextProvider ?", + ); + } + return value; +} diff --git a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionStorage.ts b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionStorage.ts new file mode 100644 index 000000000000..f9d5282bd307 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionStorage.ts @@ -0,0 +1,44 @@ +/** + * 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 {DocsVersionPersistence} from '../useThemeConfig'; + +const storageKey = (pluginId: string) => `docs-preferred-version-${pluginId}`; + +const DocsPreferredVersionStorage = { + save: ( + pluginId: string, + persistence: DocsVersionPersistence, + versionName: string, + ): void => { + if (persistence === 'none') { + // noop + } else { + window.localStorage.setItem(storageKey(pluginId), versionName); + } + }, + + read: ( + pluginId: string, + persistence: DocsVersionPersistence, + ): string | null => { + if (persistence === 'none') { + return null; + } else { + return window.localStorage.getItem(storageKey(pluginId)); + } + }, + + clear: (pluginId: string, persistence: DocsVersionPersistence): void => { + if (persistence === 'none') { + // noop + } else { + window.localStorage.removeItem(storageKey(pluginId)); + } + }, +}; + +export default DocsPreferredVersionStorage; diff --git a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts new file mode 100644 index 000000000000..544a8f6c6823 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts @@ -0,0 +1,31 @@ +/** + * 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 {useCallback} from 'react'; +import {useDocsPreferredVersionContext} from './DocsPreferredVersionProvider'; +import {useDocsData} from '@theme/hooks/useDocs'; +import {DEFAULT_PLUGIN_ID} from '@docusaurus/constants'; + +export default function useDocsPreferredVersion( + pluginId: string | undefined = DEFAULT_PLUGIN_ID, +) { + const docsData = useDocsData(pluginId); + const [state, api] = useDocsPreferredVersionContext(); + const {preferredVersionName} = state[pluginId]; + + const preferredVersion = preferredVersionName + ? docsData.versions.find((version) => version.name === preferredVersionName) + : null; + + const savePreferredVersionName = useCallback( + (versionName: string) => { + api.savePreferredVersion(pluginId, versionName); + }, + [api], + ); + + return {preferredVersion, savePreferredVersionName} as const; +} diff --git a/packages/docusaurus-theme-classic/src/utils/useDocsPreferredVersionPersistence.ts b/packages/docusaurus-theme-classic/src/utils/useDocsPreferredVersionPersistence.ts deleted file mode 100644 index 1b7fc14972e1..000000000000 --- a/packages/docusaurus-theme-classic/src/utils/useDocsPreferredVersionPersistence.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * 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 {useCallback, useEffect, useState} from 'react'; -import useThemeConfig from './useThemeConfig'; - -type DocsPreferredVersionConfig = { - docsPluginId: string | undefined; -}; - -type DocsPreferredVersionState = string | null; - -export function useDocsPreferredVersionPersistence({ - docsPluginId, -}: DocsPreferredVersionConfig) { - const { - docs: {versionPersistence}, - } = useThemeConfig(); - const storageKey = `docs-preferred-version-${docsPluginId ?? 'default'}`; - - const [versionState, setVersionState] = useState( - null, - ); - - // Init preferred version after React hydration - useEffect(() => { - if (versionPersistence === 'none') { - return; - } - - setVersionState(window.localStorage.getItem(storageKey)); - }, [storageKey]); - - const setVersionName = useCallback( - (version: string) => { - setVersionState(version); - - if (versionPersistence === 'none') { - return; - } - window.localStorage.setItem(storageKey, version); - }, - [setVersionState, storageKey], - ); - - return {versionName: versionState, setVersionName} as const; -} diff --git a/packages/docusaurus-theme-classic/src/utils/useThemeConfig.ts b/packages/docusaurus-theme-classic/src/utils/useThemeConfig.ts index a9ae70f175db..d8497b2f9451 100644 --- a/packages/docusaurus-theme-classic/src/utils/useThemeConfig.ts +++ b/packages/docusaurus-theme-classic/src/utils/useThemeConfig.ts @@ -6,9 +6,11 @@ */ import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -type ThemeConfig = { +export type DocsVersionPersistence = 'localStorage' | 'none'; + +export type ThemeConfig = { docs: { - versionPersistence: 'localStorage' | 'none'; + versionPersistence: DocsVersionPersistence; }; // TODO we should complete this theme config type over time diff --git a/packages/docusaurus/src/client/exports/constants.ts b/packages/docusaurus/src/client/exports/constants.ts new file mode 100644 index 000000000000..3e6620d667e3 --- /dev/null +++ b/packages/docusaurus/src/client/exports/constants.ts @@ -0,0 +1,14 @@ +/** + * 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. + */ + +// eslint-disable-next-line no-restricted-imports +export { + // constants were only available on node + // this makes some useful constants available to frontend/themes too + // import {DEFAULT_PLUGIN_ID} '@docusaurus/constants' + DEFAULT_PLUGIN_ID, +} from '../../constants'; From 9896fb1da15d53af10bb96d96551dc7c73cf9a44 Mon Sep 17 00:00:00 2001 From: slorber Date: Fri, 9 Oct 2020 18:30:35 +0200 Subject: [PATCH 03/12] add proper implementation for useDocsPreferredVersion --- .../utils/docsPreferredVersion/DocsPreferredVersionStorage.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionStorage.ts b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionStorage.ts index f9d5282bd307..22ac6b7001bb 100644 --- a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionStorage.ts +++ b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionStorage.ts @@ -4,6 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ + import {DocsVersionPersistence} from '../useThemeConfig'; const storageKey = (pluginId: string) => `docs-preferred-version-${pluginId}`; From babc27884b1623c0fca6d1fb20b7a4bdd91a2037 Mon Sep 17 00:00:00 2001 From: slorber Date: Fri, 9 Oct 2020 19:49:34 +0200 Subject: [PATCH 04/12] useDocsPreferredVersion => make localstorage read only after mount --- .../DocsPreferredVersionProvider.tsx | 99 +++++++++++-------- .../useDocsPreferredVersion.ts | 2 + 2 files changed, 60 insertions(+), 41 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx index 12616b2f0215..663072ac231f 100644 --- a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx +++ b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx @@ -8,6 +8,7 @@ import React, { createContext, ReactNode, useContext, + useEffect, useMemo, useState, } from 'react'; @@ -31,60 +32,76 @@ type DocsPreferredVersionState = Record< DocsPreferredVersionPluginState >; -function useVersionPersistence(): DocsVersionPersistence { - return useThemeConfig().docs.versionPersistence; +// Initial state is always null as we can't read localstorage from node SSR +function getInitialState(pluginIds: string[]): DocsPreferredVersionState { + const initialState: DocsPreferredVersionState = {}; + pluginIds.forEach((pluginId) => { + initialState[pluginId] = { + preferredVersionName: null, + }; + }); + return initialState; } -// Read local storage for all docs plugins +// Read storage for all docs plugins // Assign to each doc plugin a preferred version (if found) -function useInitialState(): DocsPreferredVersionState { - const allDocsData = useAllDocsData(); - const versionPersistence = useVersionPersistence(); - - function computeInitialState(): DocsPreferredVersionState { - // The storage value we read might be stale, - // and belong to a version that does not exist in the site anymore - // In such case, we remove the storage value to avoid downstream errors - function readPreferredVersionName( - pluginId: string, - ): DocsPreferredVersionName { - const preferredVersionNameUnsafe = DocsPreferredVersionStorage.read( - pluginId, - versionPersistence, - ); - const pluginData = allDocsData[pluginId]; - const versionExists = pluginData.versions.some( - (version) => version.name === preferredVersionNameUnsafe, - ); - if (versionExists) { - return preferredVersionNameUnsafe; - } else { - DocsPreferredVersionStorage.clear(pluginId, versionPersistence); - return null; - } +function readStorageState({ + pluginIds, + versionPersistence, + allDocsData, +}: { + pluginIds: string[]; + versionPersistence: DocsVersionPersistence; + allDocsData: any; // TODO find a way to type it :( +}): DocsPreferredVersionState { + // The storage value we read might be stale, + // and belong to a version that does not exist in the site anymore + // In such case, we remove the storage value to avoid downstream errors + function restorePluginState( + pluginId: string, + ): DocsPreferredVersionPluginState { + const preferredVersionNameUnsafe = DocsPreferredVersionStorage.read( + pluginId, + versionPersistence, + ); + const pluginData = allDocsData[pluginId]; + const versionExists = pluginData.versions.some( + (version) => version.name === preferredVersionNameUnsafe, + ); + if (versionExists) { + return {preferredVersionName: preferredVersionNameUnsafe}; + } else { + DocsPreferredVersionStorage.clear(pluginId, versionPersistence); + return {preferredVersionName: null}; } - - const initialState: DocsPreferredVersionState = {}; - Object.keys(allDocsData).forEach((pluginId) => { - initialState[pluginId] = { - preferredVersionName: readPreferredVersionName(pluginId), - }; - }); - - return initialState; } - return useMemo(computeInitialState, [allDocsData]); + const initialState: DocsPreferredVersionState = {}; + pluginIds.forEach((pluginId) => { + initialState[pluginId] = restorePluginState(pluginId); + }); + return initialState; +} + +function useVersionPersistence(): DocsVersionPersistence { + return useThemeConfig().docs.versionPersistence; } // Value that will be accessible through context: [state,api] function useContextValue() { - const initialState = useInitialState(); + const allDocsData = useAllDocsData(); const versionPersistence = useVersionPersistence(); + const pluginIds = useMemo(() => Object.keys(allDocsData), [allDocsData]); + + // Initial state is empty, as we can't read browser storage in node/SSR + const [state, setState] = useState(() => getInitialState(pluginIds)); - const [state, setState] = useState(initialState); + // On mount, we set the state read from browser storage + useEffect(() => { + setState(readStorageState({allDocsData, versionPersistence, pluginIds})); + }, [allDocsData, versionPersistence, pluginIds]); - // Return a constant api object + // The API that we expose to consumer hooks (memo for constant object) const api = useMemo(() => { function savePreferredVersion(pluginId: string, versionName: string) { DocsPreferredVersionStorage.save( diff --git a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts index 544a8f6c6823..71df67d6695f 100644 --- a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts +++ b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts @@ -9,11 +9,13 @@ import {useDocsPreferredVersionContext} from './DocsPreferredVersionProvider'; import {useDocsData} from '@theme/hooks/useDocs'; import {DEFAULT_PLUGIN_ID} from '@docusaurus/constants'; +// Note, the preferredVersion attribute will always be null before mount export default function useDocsPreferredVersion( pluginId: string | undefined = DEFAULT_PLUGIN_ID, ) { const docsData = useDocsData(pluginId); const [state, api] = useDocsPreferredVersionContext(); + const {preferredVersionName} = state[pluginId]; const preferredVersion = preferredVersionName From 645e7fed5fffade66dedc8991942c843bb1c1db6 Mon Sep 17 00:00:00 2001 From: slorber Date: Fri, 9 Oct 2020 20:05:38 +0200 Subject: [PATCH 05/12] why @docusaurus/constants can't work? --- .../utils/docsPreferredVersion/useDocsPreferredVersion.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts index 71df67d6695f..213f8f9d2d0c 100644 --- a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts +++ b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts @@ -7,7 +7,10 @@ import {useCallback} from 'react'; import {useDocsPreferredVersionContext} from './DocsPreferredVersionProvider'; import {useDocsData} from '@theme/hooks/useDocs'; -import {DEFAULT_PLUGIN_ID} from '@docusaurus/constants'; + +// TODO why this import does not work! +// import {DEFAULT_PLUGIN_ID} from '@docusaurus/constants'; +const DEFAULT_PLUGIN_ID = 'default'; // Note, the preferredVersion attribute will always be null before mount export default function useDocsPreferredVersion( From 2c1b7b4e4b880d2191a759227ac2d86077251696 Mon Sep 17 00:00:00 2001 From: slorber Date: Mon, 12 Oct 2020 16:58:41 +0200 Subject: [PATCH 06/12] fix weird TS issue when not duplicating constants --- .../utils/docsPreferredVersion/useDocsPreferredVersion.ts | 4 +--- packages/docusaurus/src/client/exports/constants.ts | 6 ++++++ yarn.lock | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts index 213f8f9d2d0c..a6a14778b2a5 100644 --- a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts +++ b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts @@ -8,9 +8,7 @@ import {useCallback} from 'react'; import {useDocsPreferredVersionContext} from './DocsPreferredVersionProvider'; import {useDocsData} from '@theme/hooks/useDocs'; -// TODO why this import does not work! -// import {DEFAULT_PLUGIN_ID} from '@docusaurus/constants'; -const DEFAULT_PLUGIN_ID = 'default'; +import {DEFAULT_PLUGIN_ID} from '@docusaurus/constants'; // Note, the preferredVersion attribute will always be null before mount export default function useDocsPreferredVersion( diff --git a/packages/docusaurus/src/client/exports/constants.ts b/packages/docusaurus/src/client/exports/constants.ts index 3e6620d667e3..48fa05bd6982 100644 --- a/packages/docusaurus/src/client/exports/constants.ts +++ b/packages/docusaurus/src/client/exports/constants.ts @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +/* // eslint-disable-next-line no-restricted-imports export { // constants were only available on node @@ -12,3 +13,8 @@ export { // import {DEFAULT_PLUGIN_ID} '@docusaurus/constants' DEFAULT_PLUGIN_ID, } from '../../constants'; + */ + +// Not duplicating the constants seems to produce +// weird TS compilation side-effects +export const DEFAULT_PLUGIN_ID = 'default'; diff --git a/yarn.lock b/yarn.lock index 2386c1bc5155..88c1e2661f29 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17689,7 +17689,7 @@ react-dev-utils@^9.1.0: strip-ansi "5.2.0" text-table "0.2.0" -react-dom@^16.8.4: +react-dom@^16.10.2, react-dom@^16.8.4: version "16.13.1" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f" integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag== @@ -17869,7 +17869,7 @@ react-waypoint@^9.0.2: prop-types "^15.0.0" react-is "^16.6.3" -react@^16.8.4: +react@^16.10.2, react@^16.8.4: version "16.13.1" resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w== From 959938bfad54223bfb5ce6b703c2d8f3cc855e10 Mon Sep 17 00:00:00 2001 From: slorber Date: Mon, 12 Oct 2020 17:03:02 +0200 Subject: [PATCH 07/12] add basic @docusaurus/constants doc --- website/docs/docusaurus-core.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/website/docs/docusaurus-core.md b/website/docs/docusaurus-core.md index dea573e06bba..f16164f56352 100644 --- a/website/docs/docusaurus-core.md +++ b/website/docs/docusaurus-core.md @@ -320,3 +320,15 @@ function MyPage() { | `ExecutionEnvironment.canUseEventListeners` | `true` if on client and has `window.addEventListener`. | | `ExecutionEnvironment.canUseIntersectionObserver` | `true` if on client and has `IntersectionObserver`. | | `ExecutionEnvironment.canUseViewport` | `true` if on client and has `window.screen`. | + +### `constants` + +A module exposing useful constants to client-side theme code. + +```jsx +import {DEFAULT_PLUGIN_ID} from '@docusaurus/constants'; +``` + +| Named export | Value | +| ------------------- | --------- | +| `DEFAULT_PLUGIN_ID` | `default` | From fc53a0467bcffaf05a40e424d3d583151ccf7ed4 Mon Sep 17 00:00:00 2001 From: slorber Date: Mon, 12 Oct 2020 17:30:28 +0200 Subject: [PATCH 08/12] attempt to fix docs-only mode where we should not call useDocs hooks --- .../src/theme/hooks/useDocs.ts | 2 ++ .../DocsPreferredVersionProvider.tsx | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useDocs.ts b/packages/docusaurus-theme-classic/src/theme/hooks/useDocs.ts index 15323456e6b7..7cd4c5edc25c 100644 --- a/packages/docusaurus-theme-classic/src/theme/hooks/useDocs.ts +++ b/packages/docusaurus-theme-classic/src/theme/hooks/useDocs.ts @@ -13,8 +13,10 @@ // note: warning can be filtered: https://github.com/facebook/docusaurus/pull/3382#issuecomment-684966924 try { module.exports = require('@theme-init/hooks/useDocs'); + exports.docsPluginEnabled = true; } catch (e) { module.exports = {}; + exports.docsPluginEnabled = false; } /* diff --git a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx index 663072ac231f..aaba17f389ab 100644 --- a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx +++ b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx @@ -14,7 +14,7 @@ import React, { } from 'react'; import useThemeConfig, {DocsVersionPersistence} from '../useThemeConfig'; -import {useAllDocsData} from '@theme/hooks/useDocs'; +import {docsPluginEnabled, useAllDocsData} from '@theme/hooks/useDocs'; import DocsPreferredVersionStorage from './DocsPreferredVersionStorage'; type DocsPreferredVersionName = string | null; @@ -131,6 +131,22 @@ export default function DocsPreferredVersionContextProvider({ children, }: { children: ReactNode; +}) { + if (docsPluginEnabled) { + return ( + + {children} + + ); + } else { + return children; + } +} + +function DocsPreferredVersionContextProviderUnsafe({ + children, +}: { + children: ReactNode; }) { const contextValue = useContextValue(); return {children}; From 6191bb0277fa2e2fbc09033b078952f97bfd0ba6 Mon Sep 17 00:00:00 2001 From: slorber Date: Mon, 12 Oct 2020 17:37:14 +0200 Subject: [PATCH 09/12] attempt to fix docs-only mode where we should not call useDocs hooks --- .../utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx index aaba17f389ab..e44a897d8fcb 100644 --- a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx +++ b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx @@ -139,7 +139,7 @@ export default function DocsPreferredVersionContextProvider({ ); } else { - return children; + return <>children; } } From 80d1fba33b5185f4bf430aef656af0b5b71a628d Mon Sep 17 00:00:00 2001 From: slorber Date: Mon, 12 Oct 2020 17:51:49 +0200 Subject: [PATCH 10/12] fix children --- .../utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx index e44a897d8fcb..f6af96d3e094 100644 --- a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx +++ b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx @@ -139,7 +139,7 @@ export default function DocsPreferredVersionContextProvider({ ); } else { - return <>children; + return <>{children}; } } From 15424bb7d7ea9cd3001eda5c52cd2ac11b3f473d Mon Sep 17 00:00:00 2001 From: slorber Date: Mon, 12 Oct 2020 18:18:37 +0200 Subject: [PATCH 11/12] encapsulate hacky isDocsPluginEnabled in docsUtils --- .../src/theme/hooks/useDocs.ts | 2 -- .../DocsPreferredVersionProvider.tsx | 6 ++++-- .../docusaurus-theme-classic/src/utils/docsUtils.ts | 11 +++++++++++ 3 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 packages/docusaurus-theme-classic/src/utils/docsUtils.ts diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useDocs.ts b/packages/docusaurus-theme-classic/src/theme/hooks/useDocs.ts index 7cd4c5edc25c..15323456e6b7 100644 --- a/packages/docusaurus-theme-classic/src/theme/hooks/useDocs.ts +++ b/packages/docusaurus-theme-classic/src/theme/hooks/useDocs.ts @@ -13,10 +13,8 @@ // note: warning can be filtered: https://github.com/facebook/docusaurus/pull/3382#issuecomment-684966924 try { module.exports = require('@theme-init/hooks/useDocs'); - exports.docsPluginEnabled = true; } catch (e) { module.exports = {}; - exports.docsPluginEnabled = false; } /* diff --git a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx index f6af96d3e094..f98d7030efc3 100644 --- a/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx +++ b/packages/docusaurus-theme-classic/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx @@ -13,8 +13,10 @@ import React, { useState, } from 'react'; import useThemeConfig, {DocsVersionPersistence} from '../useThemeConfig'; +import {isDocsPluginEnabled} from '../docsUtils'; + +import {useAllDocsData} from '@theme/hooks/useDocs'; -import {docsPluginEnabled, useAllDocsData} from '@theme/hooks/useDocs'; import DocsPreferredVersionStorage from './DocsPreferredVersionStorage'; type DocsPreferredVersionName = string | null; @@ -132,7 +134,7 @@ export default function DocsPreferredVersionContextProvider({ }: { children: ReactNode; }) { - if (docsPluginEnabled) { + if (isDocsPluginEnabled) { return ( {children} diff --git a/packages/docusaurus-theme-classic/src/utils/docsUtils.ts b/packages/docusaurus-theme-classic/src/utils/docsUtils.ts new file mode 100644 index 000000000000..eba2c7cd2f6e --- /dev/null +++ b/packages/docusaurus-theme-classic/src/utils/docsUtils.ts @@ -0,0 +1,11 @@ +/** + * 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 {useAllDocsData} from '@theme/hooks/useDocs'; + +// TODO not ideal, see also "useDocs" +export const isDocsPluginEnabled: boolean = !!useAllDocsData; From d9c4d9ba8a8762d27b65524ad34a285c5f2d078e Mon Sep 17 00:00:00 2001 From: slorber Date: Mon, 12 Oct 2020 18:32:51 +0200 Subject: [PATCH 12/12] use same priority order for all navbar items: activeVersion ?? preferredVersion ?? latestVersion --- .../src/theme/NavbarItem/DocNavbarItem.tsx | 6 ++++-- .../src/theme/NavbarItem/DocsVersionNavbarItem.tsx | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocNavbarItem.tsx index 602843f06c85..90b73d560a40 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocNavbarItem.tsx @@ -10,6 +10,7 @@ import DefaultNavbarItem from './DefaultNavbarItem'; import {useLatestVersion, useActiveDocContext} from '@theme/hooks/useDocs'; import clsx from 'clsx'; import type {Props} from '@theme/NavbarItem/DocNavbarItem'; +import useDocsPreferredVersion from '../../utils/docsPreferredVersion/useDocsPreferredVersion'; export default function DocNavbarItem({ docId, @@ -18,10 +19,11 @@ export default function DocNavbarItem({ docsPluginId, ...props }: Props): JSX.Element { - const latestVersion = useLatestVersion(docsPluginId); const {activeVersion, activeDoc} = useActiveDocContext(docsPluginId); + const {preferredVersion} = useDocsPreferredVersion(docsPluginId); + const latestVersion = useLatestVersion(docsPluginId); - const version = activeVersion ?? latestVersion; + const version = activeVersion ?? preferredVersion ?? latestVersion; const doc = version.docs.find((versionDoc) => versionDoc.id === docId); if (!doc) { diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionNavbarItem.tsx index 9a82a31ffba8..bab774760eca 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocsVersionNavbarItem.tsx @@ -9,6 +9,7 @@ import React from 'react'; import DefaultNavbarItem from './DefaultNavbarItem'; import {useActiveVersion, useLatestVersion} from '@theme/hooks/useDocs'; import type {Props} from '@theme/NavbarItem/DocsVersionNavbarItem'; +import useDocsPreferredVersion from '../../utils/docsPreferredVersion/useDocsPreferredVersion'; const getVersionMainDoc = (version) => version.docs.find((doc) => doc.id === version.mainDocId); @@ -20,8 +21,9 @@ export default function DocsVersionNavbarItem({ ...props }: Props): JSX.Element { const activeVersion = useActiveVersion(docsPluginId); + const {preferredVersion} = useDocsPreferredVersion(docsPluginId); const latestVersion = useLatestVersion(docsPluginId); - const version = activeVersion ?? latestVersion; + const version = activeVersion ?? preferredVersion ?? latestVersion; const label = staticLabel ?? version.label; const path = staticTo ?? getVersionMainDoc(version).path; return ;