diff --git a/src/plugins/docs/index.d.ts b/src/plugins/docs/index.d.ts index 4e4e03b0a83..572ba55a8f7 100644 --- a/src/plugins/docs/index.d.ts +++ b/src/plugins/docs/index.d.ts @@ -1,25 +1,14 @@ import { Options as DocsOptions, PluginOptions as DocsPluginOptions, - LoadedContent as DocsLoadedContent, - DocMetadata as DocsDocMetadata, } from '@docusaurus/plugin-content-docs'; import { OptionValidationContext as DocsOptionValidationContext, Validate, } from '@docusaurus/types'; -export type DocMetadata = DocsDocMetadata & { - bannerContent?: string; -}; - -export type LoadedContent = DocsLoadedContent & { - bannerContent?: string; -}; - export type PluginOptions = DocsPluginOptions & { globalSidebars: string[]; - bannerPath?: string; }; export type Options = Partial; diff --git a/src/plugins/docs/index.js b/src/plugins/docs/index.js index f35a7ee8c7a..fa2630add3d 100644 --- a/src/plugins/docs/index.js +++ b/src/plugins/docs/index.js @@ -43,57 +43,21 @@ var __importStar = __setModuleDefault(result, mod); return result; }; -var __importDefault = - (this && this.__importDefault) || - function (mod) { - return mod && mod.__esModule ? mod : { default: mod }; - }; +Object.defineProperty(exports, '__esModule', { value: true }); +exports.validateOptions = void 0; const plugin_content_docs_1 = __importStar( require('@docusaurus/plugin-content-docs'), ); -const promises_1 = __importDefault(require('fs/promises')); -const path_1 = __importDefault(require('path')); async function pluginDocs(context, options) { // Destructure to separate the Docusaurus docs plugin options // and initialize the Docusaurus docs plugin to wrap. - const { bannerPath, globalSidebars, ...docsOptions } = options; + const { globalSidebars, ...docsOptions } = options; const plugin = await (0, plugin_content_docs_1.default)(context, docsOptions); return { ...plugin, - getPathsToWatch: () => { - const pathsToWatch = plugin.getPathsToWatch(); - if (bannerPath) - pathsToWatch.push(path_1.default.resolve(context.siteDir, bannerPath)); - return pathsToWatch; - }, - loadContent: async () => { - const docsLoadedContent = await plugin.loadContent(); - return { - ...docsLoadedContent, - // Load banner content from file - bannerContent: bannerPath - ? await promises_1.default.readFile(bannerPath, { - encoding: 'utf-8', - }) - : undefined, - }; - }, - translateContent: ({ content, ...args }) => { - // Propagate banner content - const { bannerContent, ...docsContent } = content; - const docsContentLoaded = plugin.translateContent({ - content: docsContent, - ...args, - }); - return { - ...docsContentLoaded, - bannerContent, - }; - }, // Override the `contentLoaded` function to add sidebars to the // global data exposed by the Docusaurus docs plugin. contentLoaded: async ({ actions, content, ...args }) => { - const { bannerContent, ...docsContent } = content; const globalSidebarEntries = []; const createData = async (name, data) => { // Hook into the `createData` call to extract the sidebars we need. @@ -105,18 +69,7 @@ async function pluginDocs(context, options) { .filter(([sidebarId]) => globalSidebars.includes(sidebarId)) .forEach((entry) => globalSidebarEntries.push(entry)); } - return await actions.createData( - name, - // Expose banner content to be used by the DocBanner theme component - JSON.stringify( - { - ...versionMetadata, - bannerContent, - }, - null, - 2, - ), - ); + return await actions.createData(name, data); }; const setGlobalData = (data) => { actions.setGlobalData({ @@ -126,7 +79,7 @@ async function pluginDocs(context, options) { }; await plugin.contentLoaded({ ...args, - content: docsContent, + content, actions: { ...actions, createData, @@ -136,15 +89,46 @@ async function pluginDocs(context, options) { }, }; } -pluginDocs.validateOptions = ({ validate, options }) => { - const { bannerPath, globalSidebars = [], ...docsOptions } = options; +exports.default = pluginDocs; +function validateOptions({ validate, options }) { + const { versions = {}, globalSidebars = [], ...docsOptions } = options; + const versionEntries = Object.entries(versions); + if (versionEntries.length > 1) + throw 'Multiple Docusuaurus doc versions not allowed in the Wiki'; + // Handle version banner. + const versionBannerMap = {}; + const docsVersionEntries = versionEntries.map( + ([versionLabel, versionOptions]) => { + // TODO: validate banner + const { banner, ...docsVersionOptions } = versionOptions; + versionBannerMap[versionLabel] = banner; + return [versionLabel, docsVersionOptions]; + }, + ); + const validatedDocsOptions = (0, plugin_content_docs_1.validateOptions)({ + validate, + options: { + ...docsOptions, + versions: Object.fromEntries(docsVersionEntries), + }, + }); + // Re-add banner. + validatedDocsOptions.versions = Object.fromEntries( + Object.entries(validatedDocsOptions.versions).map( + ([versionLabel, versionOptions]) => { + return [ + versionLabel, + { + ...versionOptions, + banner: versionBannerMap[versionLabel], + }, + ]; + }, + ), + ); return { - ...(0, plugin_content_docs_1.validateOptions)({ - validate, - options: docsOptions, - }), + ...validatedDocsOptions, globalSidebars, - bannerPath, }; -}; -module.exports = pluginDocs; +} +exports.validateOptions = validateOptions; diff --git a/src/theme/DocBanner/index.tsx b/src/theme/DocBanner/index.tsx deleted file mode 100644 index b36a42951c4..00000000000 --- a/src/theme/DocBanner/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import { useDoc } from '@docusaurus/theme-common/internal'; -import { DocMetadata } from '../../../../src/plugins/docs'; -import ReactMarkdown from 'react-markdown'; -import './styles.css'; - -export default function DocBanner() { - const { bannerContent } = useDoc().metadata as DocMetadata; - - if (!bannerContent) return null; - - return ( - - {bannerContent} - - ); -} diff --git a/src/theme/DocBanner/styles.css b/src/theme/DocBanner/styles.css deleted file mode 100644 index 6af2f55eacf..00000000000 --- a/src/theme/DocBanner/styles.css +++ /dev/null @@ -1,7 +0,0 @@ -.doc-banner p { - margin: 0; -} - -.doc-banner p + p { - margin: var(--ifm-paragraph-margin-bottom) 0 0; -} diff --git a/src/theme/DocItem/Layout/index.tsx b/src/theme/DocItem/Layout/index.tsx deleted file mode 100644 index bd5419b2661..00000000000 --- a/src/theme/DocItem/Layout/index.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/** - * SWIZZLED VERSION: 2.4.1 - * REASONS: - * - Add custom Banner component. - */ - -import React from 'react'; -import clsx from 'clsx'; -import { useWindowSize } from '@docusaurus/theme-common'; -import { useDoc } from '@docusaurus/theme-common/internal'; -import DocItemPaginator from '@theme/DocItem/Paginator'; -import DocVersionBanner from '@theme/DocVersionBanner'; -import DocVersionBadge from '@theme/DocVersionBadge'; -import DocItemFooter from '@theme/DocItem/Footer'; -import DocItemTOCMobile from '@theme/DocItem/TOC/Mobile'; -import DocItemTOCDesktop from '@theme/DocItem/TOC/Desktop'; -import DocItemContent from '@theme/DocItem/Content'; -import DocBreadcrumbs from '@theme/DocBreadcrumbs'; -import type { Props } from '@theme/DocItem/Layout'; -import DocBanner from '@theme/DocBanner'; - -import styles from './styles.module.css'; - -/** - * Decide if the toc should be rendered, on mobile or desktop viewports - */ -function useDocTOC() { - const { frontMatter, toc } = useDoc(); - const windowSize = useWindowSize(); - - const hidden = frontMatter.hide_table_of_contents; - const canRender = !hidden && toc.length > 0; - - const mobile = canRender ? : undefined; - - const desktop = - canRender && (windowSize === 'desktop' || windowSize === 'ssr') ? ( - - ) : undefined; - - return { - hidden, - mobile, - desktop, - }; -} - -export default function DocItemLayout({ children }: Props): JSX.Element { - const docTOC = useDocTOC(); - return ( -
-
- - -
-
- - - {docTOC.mobile} - {children} - -
- -
-
- {docTOC.desktop &&
{docTOC.desktop}
} -
- ); -} diff --git a/src/theme/DocItem/Layout/styles.module.css b/src/theme/DocItem/Layout/styles.module.css deleted file mode 100644 index d5aaec1322c..00000000000 --- a/src/theme/DocItem/Layout/styles.module.css +++ /dev/null @@ -1,10 +0,0 @@ -.docItemContainer header + *, -.docItemContainer article > *:first-child { - margin-top: 0; -} - -@media (min-width: 997px) { - .docItemCol { - max-width: 75% !important; - } -} diff --git a/src/theme/DocVersionBanner/index.tsx b/src/theme/DocVersionBanner/index.tsx new file mode 100644 index 00000000000..90bbff23f15 --- /dev/null +++ b/src/theme/DocVersionBanner/index.tsx @@ -0,0 +1,200 @@ +/** + * SWIZZLED VERSION: 2.4.3 + * REASONS: + * - Remove use of siteTitle as it is irrelevant in our setup. + * - Link to our own latest version. + * - Update text pointing to latest version + * - Add `staging` banner. + */ +import React, { type ComponentType } from 'react'; +import clsx from 'clsx'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import Link from '@docusaurus/Link'; +import Translate from '@docusaurus/Translate'; +import { type GlobalVersion } from '@docusaurus/plugin-content-docs/client'; +import { ThemeClassNames } from '@docusaurus/theme-common'; +import { useDocsVersion } from '@docusaurus/theme-common/internal'; +import type { Props } from '@theme/DocVersionBanner'; +import type { + VersionBanner, + PropVersionMetadata, +} from '@docusaurus/plugin-content-docs'; +import { useWikiPreferredVersion } from '@site/src/utils/wikiPreferredVersion'; +import { useLocation } from '@docusaurus/router'; +import { + useAllLatestVersion, + useCurrentDocPlugins, +} from '@site/src/utils/wikiVersion'; + +type WikiVersionBanner = VersionBanner & 'staging'; + +type BannerLabelComponentProps = { + siteTitle: string; + versionMetadata: PropVersionMetadata; +}; + +function UnreleasedVersionLabel({ + versionMetadata, +}: BannerLabelComponentProps) { + return ( + {versionMetadata.label}, + }} + > + {'This is unreleased documentation for {versionLabel} version.'} + + ); +} + +function StagingVersionLabel({ versionMetadata }: BannerLabelComponentProps) { + return ( + {versionMetadata.label}, + }} + > + { + 'This is documentation for {versionLabel} version wich is currently running on our staging network.' + } + + ); +} + +function UnmaintainedVersionLabel({ + versionMetadata, +}: BannerLabelComponentProps) { + return ( + {versionMetadata.label}, + }} + > + { + 'This is documentation for {versionLabel}, which is no longer actively maintained.' + } + + ); +} + +const BannerLabelComponents: { + [banner in WikiVersionBanner]: ComponentType; +} = { + unreleased: UnreleasedVersionLabel, + staging: StagingVersionLabel, + unmaintained: UnmaintainedVersionLabel, +}; + +function BannerLabel(props: BannerLabelComponentProps) { + const BannerLabelComponent = + BannerLabelComponents[props.versionMetadata.banner!]; + return ; +} + +function LatestVersionSuggestionLabel({ + versionLabel, + to, + onClick, +}: { + to: string; + onClick: () => void; + versionLabel: string; +}) { + return ( + + + + latest version + + + + ), + }} + > + { + 'For the latest stable release, see the {latestVersionLink} ({versionLabel}).' + } + + ); +} + +function DocVersionBannerEnabled({ + className, + versionMetadata, +}: Props & { + versionMetadata: PropVersionMetadata; +}): JSX.Element { + const { + siteConfig: { title: siteTitle }, + } = useDocusaurusContext(); + const { pathname } = useLocation(); + const pluginIds = useCurrentDocPlugins(pathname); + + const getVersionMainDoc = (version: GlobalVersion) => + version.docs.find((doc) => doc.id === version.mainDocId)!; + + const { savePreferredVersionName } = useWikiPreferredVersion( + pathname, + pluginIds, + ); + + const latestVersionSuggestion = useAllLatestVersion(pluginIds); + const latestDocSuggestion = getVersionMainDoc(latestVersionSuggestion); + + // Try to link to same doc in latest version (not always possible), falling + // back to main doc of latest version + const latestVersionSuggestedDoc = + latestDocSuggestion ?? getVersionMainDoc(latestVersionSuggestion); + + return ( +
+
+ +
+
+ savePreferredVersionName(latestVersionSuggestion.name)} + /> +
+
+ ); +} + +export default function DocVersionBanner({ + className, +}: Props): JSX.Element | null { + const versionMetadata = useDocsVersion(); + if (versionMetadata.banner) { + return ( + + ); + } + return null; +} diff --git a/src/utils/pluginConfigGenerators.js b/src/utils/pluginConfigGenerators.js index 864841a20a6..fc7c0685bfb 100644 --- a/src/utils/pluginConfigGenerators.js +++ b/src/utils/pluginConfigGenerators.js @@ -23,7 +23,7 @@ function generatePluginConfig(pluginConfig, basePath) { return doc.versions.map((version) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { label, badges, ...rest } = version; + const { label, badges, banner, ...rest } = version; // TODO: This could be removed once we don't use points in paths anymore. const plugin_name_path = doc.routeBasePath ? doc.routeBasePath : doc.id; @@ -43,6 +43,7 @@ function generatePluginConfig(pluginConfig, basePath) { label, path: mainVersion.label === label ? undefined : label, badge: true, + banner, }, }, }