diff --git a/code/addons/docs/src/blocks/blocks/DocsContainer.tsx b/code/addons/docs/src/blocks/blocks/DocsContainer.tsx index b7070d07c374..3cae103afe80 100644 --- a/code/addons/docs/src/blocks/blocks/DocsContainer.tsx +++ b/code/addons/docs/src/blocks/blocks/DocsContainer.tsx @@ -1,5 +1,5 @@ import type { FC, PropsWithChildren } from 'react'; -import React, { useEffect } from 'react'; +import React, { useEffect, useMemo } from 'react'; import type { Renderer } from 'storybook/internal/types'; @@ -10,6 +10,7 @@ import { DocsPageWrapper } from '../components'; import { TableOfContents } from '../components/TableOfContents'; import type { DocsContextProps } from './DocsContext'; import { DocsContext } from './DocsContext'; +import { createDocsSlugger, DocsSluggerContext } from './DocsSluggerContext'; import { SourceContainer } from './SourceContainer'; import { scrollToElement } from './utils'; @@ -25,6 +26,7 @@ export const DocsContainer: FC> = ({ theme, children, }) => { + const slugger = useMemo(() => createDocsSlugger(), []); let toc; try { @@ -54,24 +56,26 @@ export const DocsContainer: FC> = ({ }); return ( - - - - - ) : null - } - > - {children} - - - - + + + + + + ) : null + } + > + {children} + + + + + ); }; diff --git a/code/addons/docs/src/blocks/blocks/DocsContext.ts b/code/addons/docs/src/blocks/blocks/DocsContext.ts index 205530f6fbfc..55d3fee7cdcf 100644 --- a/code/addons/docs/src/blocks/blocks/DocsContext.ts +++ b/code/addons/docs/src/blocks/blocks/DocsContext.ts @@ -11,10 +11,10 @@ export type { DocsContextProps }; // the React component tree. // This was specifically a problem with the Vite builder. if (globalThis && globalThis.__DOCS_CONTEXT__ === undefined) { - globalThis.__DOCS_CONTEXT__ = createContext(null); + globalThis.__DOCS_CONTEXT__ = createContext>(null!); globalThis.__DOCS_CONTEXT__.displayName = 'DocsContext'; } export const DocsContext: Context> = globalThis ? globalThis.__DOCS_CONTEXT__ - : createContext(null); + : createContext>(null!); diff --git a/code/addons/docs/src/blocks/blocks/DocsSluggerContext.ts b/code/addons/docs/src/blocks/blocks/DocsSluggerContext.ts new file mode 100644 index 000000000000..d37daf19f6cc --- /dev/null +++ b/code/addons/docs/src/blocks/blocks/DocsSluggerContext.ts @@ -0,0 +1,12 @@ +import type { Context } from 'react'; +import { createContext } from 'react'; + +import GithubSlugger from 'github-slugger'; + +export type DocsSlugger = GithubSlugger; + +export const createDocsSlugger = () => new GithubSlugger(); + +export const DocsSluggerContext: Context = createContext( + null +); diff --git a/code/addons/docs/src/blocks/blocks/Heading.tsx b/code/addons/docs/src/blocks/blocks/Heading.tsx index d0b1c06894a4..08011454ada5 100644 --- a/code/addons/docs/src/blocks/blocks/Heading.tsx +++ b/code/addons/docs/src/blocks/blocks/Heading.tsx @@ -5,6 +5,7 @@ import { H2 } from 'storybook/internal/components'; import GithubSlugger from 'github-slugger'; +import { DocsSluggerContext } from './DocsSluggerContext'; import { HeaderMdx } from './mdx'; import { withMdxComponentOverride } from './with-mdx-component-override'; @@ -12,6 +13,7 @@ export interface HeadingProps { disableAnchor?: boolean; } +// Preserve the legacy singleton when Heading is rendered outside a Docs tree. export const slugs = new GithubSlugger(); const HeadingImpl: FC> = ({ @@ -22,7 +24,8 @@ const HeadingImpl: FC> = ({ if (disableAnchor || typeof children !== 'string') { return

{children}

; } - const tagID = slugs.slug(children.toLowerCase()); + const slugger = React.useContext(DocsSluggerContext) ?? slugs; + const tagID = slugger.slug(children.toLowerCase()); return ( {children} diff --git a/code/addons/docs/src/blocks/blocks/Subheading.tsx b/code/addons/docs/src/blocks/blocks/Subheading.tsx index 177ead07d057..391eaa1c29e3 100644 --- a/code/addons/docs/src/blocks/blocks/Subheading.tsx +++ b/code/addons/docs/src/blocks/blocks/Subheading.tsx @@ -5,6 +5,7 @@ import { H3 } from 'storybook/internal/components'; import type { HeadingProps } from './Heading'; import { slugs } from './Heading'; +import { DocsSluggerContext } from './DocsSluggerContext'; import { HeaderMdx } from './mdx'; import { withMdxComponentOverride } from './with-mdx-component-override'; @@ -12,7 +13,8 @@ const SubheadingImpl: FC> = ({ children, disable if (disableAnchor || typeof children !== 'string') { return

{children}

; } - const tagID = slugs.slug(children.toLowerCase()); + const slugger = React.useContext(DocsSluggerContext) ?? slugs; + const tagID = slugger.slug(children.toLowerCase()); return ( {children} diff --git a/code/addons/docs/src/typings.d.ts b/code/addons/docs/src/typings.d.ts index 468759c8ba22..9e354f701296 100644 --- a/code/addons/docs/src/typings.d.ts +++ b/code/addons/docs/src/typings.d.ts @@ -3,8 +3,10 @@ declare module 'acorn-jsx'; declare module 'vue/dist/vue'; declare var FEATURES: import('storybook/internal/types').StorybookConfigRaw['features']; -declare var __DOCS_CONTEXT__: any; -declare var PREVIEW_URL: any; +declare var __DOCS_CONTEXT__: import('react').Context< + import('storybook/internal/types').DocsContextProps +>; +declare var PREVIEW_URL: string | undefined; declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; declare var TAGS_OPTIONS: import('storybook/internal/types').TagsOptions;