From 45485d634117c647f5e553ea92b30583828fed82 Mon Sep 17 00:00:00 2001 From: Shaun Struwig <41984034+Blargian@users.noreply.github.com> Date: Tue, 18 Nov 2025 14:55:38 +0100 Subject: [PATCH 1/3] improvements to page versioning --- .../ClientVersionDropdown.js | 95 ++++++++++++++++++- src/theme/ClientVersionDropdown/Version.js | 47 +++++++++ 2 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 src/theme/ClientVersionDropdown/Version.js diff --git a/src/theme/ClientVersionDropdown/ClientVersionDropdown.js b/src/theme/ClientVersionDropdown/ClientVersionDropdown.js index b85eef7bea5..39b30cefdc0 100644 --- a/src/theme/ClientVersionDropdown/ClientVersionDropdown.js +++ b/src/theme/ClientVersionDropdown/ClientVersionDropdown.js @@ -13,6 +13,22 @@ const ClientVersionDropdown = (props) => { const buttonRef = useRef(null); const dropdownRef = useRef(null); + // Store header IDs for each version + // Structure: { 0: ['header-1', 'header-2'], 1: ['header-3', 'header-4'] } + const [versionHeaders, setVersionHeaders] = useState({}); + + // Support both old API (snippet) and new API (children) + const usingChildren = props.children && React.Children.count(props.children) > 0; + const childrenArray = usingChildren ? React.Children.toArray(props.children) : []; + + // Callback for Version components to report their headers + const handleHeadersCollected = (versionIndex, headerIds) => { + setVersionHeaders(prev => ({ + ...prev, + [versionIndex]: headerIds + })); + }; + // Find version from URL parameter on initial load useEffect(() => { const searchParams = new URLSearchParams(location.search); @@ -135,8 +151,81 @@ const ClientVersionDropdown = (props) => { const selectedVersion = props.versions[selectedVersionIndex]; - // Get the MDXContent component - const MDXContent = selectedVersion.snippet; + // Effect to filter TOC based on selected version + useEffect(() => { + if (!usingChildren) return; + + const filterTOC = () => { + // Get the header IDs for the currently selected version + const activeVersionHeaderIds = versionHeaders[selectedVersionIndex] || []; + + if (activeVersionHeaderIds.length === 0) { + // Headers not collected yet, try again later + return; + } + + // Find TOC - try multiple selectors for Docusaurus compatibility + const tocLinks = document.querySelectorAll( + '.table-of-contents a, .theme-doc-toc-desktop a, [class*="tocDesktop"] a, .toc a' + ); + + tocLinks.forEach(link => { + const href = link.getAttribute('href'); + if (href && href.startsWith('#')) { + const headerId = href.substring(1); + const linkParent = link.parentElement; //
  • element + + // Show TOC link only if its header ID is in the active version's headers + if (activeVersionHeaderIds.includes(headerId)) { + linkParent.style.display = ''; + } else { + linkParent.style.display = 'none'; + } + } + }); + }; + + // Run immediately + filterTOC(); + + // Re-run after a delay to catch dynamically rendered TOC items + const timer = setTimeout(filterTOC, 200); + + return () => clearTimeout(timer); + }, [selectedVersionIndex, usingChildren, versionHeaders]); + + // Render content based on API being used + const renderContent = () => { + if (usingChildren) { + // New API: render children with show/hide based on selection + // Clone children and inject version props + return childrenArray.map((child, index) => { + const isVisible = index === selectedVersionIndex; + + // Clone the child to add versionIndex and callback props + const clonedChild = React.cloneElement(child, { + versionIndex: index, + isVisible: isVisible, + onHeadersCollected: handleHeadersCollected + }); + + return ( +
    + {clonedChild} +
    + ); + }); + } else { + // Old API: render snippet + const MDXContent = selectedVersion.snippet; + return MDXContent && typeof MDXContent === 'function' && ; + } + }; return ( <> @@ -154,7 +243,7 @@ const ClientVersionDropdown = (props) => { {renderDropdown()}
    - {MDXContent && typeof MDXContent === 'function' && } + {renderContent()}
    ); diff --git a/src/theme/ClientVersionDropdown/Version.js b/src/theme/ClientVersionDropdown/Version.js new file mode 100644 index 00000000000..7f82d01ed1f --- /dev/null +++ b/src/theme/ClientVersionDropdown/Version.js @@ -0,0 +1,47 @@ +import React, { useEffect, useRef } from 'react'; + +/** + * Version wrapper component for use with ClientVersionDropdown. + * Collects all heading IDs within this version for TOC filtering. + * The parent ClientVersionDropdown component handles showing/hiding based on selection. + */ +const Version = ({ children, versionIndex, isVisible, onHeadersCollected }) => { + const containerRef = useRef(null); + + useEffect(() => { + // Collect heading IDs from this version + const collectHeaders = () => { + if (containerRef.current && typeof versionIndex !== 'undefined') { + const headings = containerRef.current.querySelectorAll('h1, h2, h3, h4, h5, h6'); + const headerIds = []; + + headings.forEach(heading => { + if (heading.id) { + headerIds.push(heading.id); + // Mark heading with version index for reference + heading.setAttribute('data-version-index', versionIndex); + } + }); + + // Notify parent component of the headers in this version + if (onHeadersCollected && headerIds.length > 0) { + onHeadersCollected(versionIndex, headerIds); + } + } + }; + + // Run immediately and after a short delay to catch late-rendered content + collectHeaders(); + const timer = setTimeout(collectHeaders, 100); + + return () => clearTimeout(timer); + }, [versionIndex, children, onHeadersCollected]); + + return ( +
    + {children} +
    + ); +}; + +export default Version; \ No newline at end of file From 64d3991cb7533e131f7131a0ee464917963bf0e7 Mon Sep 17 00:00:00 2001 From: Shaun Struwig <41984034+Blargian@users.noreply.github.com> Date: Tue, 18 Nov 2025 15:02:36 +0100 Subject: [PATCH 2/3] update docs --- contribute/style-guide.md | 56 +++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/contribute/style-guide.md b/contribute/style-guide.md index ae583549474..d9e5b69191a 100644 --- a/contribute/style-guide.md +++ b/contribute/style-guide.md @@ -331,7 +331,53 @@ which may need versioned documentation, we use the following custom ``` -### How to use it +### How to use it + +The `ClientVersionDropdown` component supports two APIs: + +#### API 1: Inline Content (Recommended) + +This approach keeps all content in the main file, which allows Docusaurus to properly generate the table of contents (TOC) for all versions. + +```js +import ClientVersionDropdown from '@theme/ClientVersionDropdown/ClientVersionDropdown' +import Version from '@theme/ClientVersionDropdown/Version' + + + + +## Environment requirements + +Your v0.8+ content here... + + + +## Environment requirements {#v07-environment-requirements} + +Your v0.7.x content here... + + + +``` + +**Important Notes:** +- All content is placed directly in the main `.mdx` file +- Each `` block contains the content for one version +- The order of `` blocks must match the order in the `versions` array +- **Make header IDs unique** across versions using explicit anchor IDs (e.g., `{#v07-environment-requirements}`) +- The TOC will show only headers from the currently selected version +- The component will display the first version as 'selected' by default + +#### API 2: External Snippets (Legacy) + +This approach uses separate snippet files for each version. Note that this method has limitations with TOC generation. Versioned folders are structured as follows: @@ -352,9 +398,9 @@ Versioned folders are structured as follows: ``` * The content for each version is placed in a snippet. For example `_v0_7.mdx` - * Snippets begin with `_` + * Snippets begin with `_` * Snippets do not contain front-matter - * These snippets import any components they may need (See `_v0_7.mdx` for example) + * These snippets import any components they may need * They should be .mdx files * There is a single page for all versions. For example `client.mdx` * This page contains frontmatter @@ -370,8 +416,8 @@ import ClientVersionDropdown from '@theme/ClientVersionDropdown/ClientVersionDro Also import the two snippets: ```js -import v07 from './_v0_7.mdx' -import v08 from './_v0_8.mdx' +import v07 from './_snippets/_v0_7.mdx' +import v08 from './_snippets/_v0_8.mdx' ``` Pass it an array of objects representing versions and their respective snippets: From 9c1718d0b578075d2e450059e601c843ee63039f Mon Sep 17 00:00:00 2001 From: Shaun Struwig <41984034+Blargian@users.noreply.github.com> Date: Tue, 18 Nov 2025 15:19:37 +0100 Subject: [PATCH 3/3] newline --- src/theme/ClientVersionDropdown/Version.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/theme/ClientVersionDropdown/Version.js b/src/theme/ClientVersionDropdown/Version.js index 7f82d01ed1f..cfe2733c834 100644 --- a/src/theme/ClientVersionDropdown/Version.js +++ b/src/theme/ClientVersionDropdown/Version.js @@ -44,4 +44,4 @@ const Version = ({ children, versionIndex, isVisible, onHeadersCollected }) => { ); }; -export default Version; \ No newline at end of file +export default Version;