From 1460e06d211bfcaeed231e3c0a8644ec38b38ab7 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 11 Apr 2025 17:21:19 +0200 Subject: [PATCH] implement TitleFormatterProvider --- .../docusaurus-theme-common/src/internal.ts | 5 +- .../__tests__/titleFormatterUtils.test.tsx | 10 +-- .../src/utils/titleFormatterUtils.tsx | 82 ++++++++++++++++--- 3 files changed, 79 insertions(+), 18 deletions(-) diff --git a/packages/docusaurus-theme-common/src/internal.ts b/packages/docusaurus-theme-common/src/internal.ts index d18237e888..b1c116a0c6 100644 --- a/packages/docusaurus-theme-common/src/internal.ts +++ b/packages/docusaurus-theme-common/src/internal.ts @@ -43,7 +43,10 @@ export { export {DEFAULT_SEARCH_TAG} from './utils/searchUtils'; -export {useTitleFormatter} from './utils/titleFormatterUtils'; +export { + TitleFormatterProvider, + useTitleFormatter, +} from './utils/titleFormatterUtils'; export {useLocationChange} from './utils/useLocationChange'; diff --git a/packages/docusaurus-theme-common/src/utils/__tests__/titleFormatterUtils.test.tsx b/packages/docusaurus-theme-common/src/utils/__tests__/titleFormatterUtils.test.tsx index 091117e46a..3ede353b77 100644 --- a/packages/docusaurus-theme-common/src/utils/__tests__/titleFormatterUtils.test.tsx +++ b/packages/docusaurus-theme-common/src/utils/__tests__/titleFormatterUtils.test.tsx @@ -5,12 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -import {DefaultTitleFormatter} from '../titleFormatterUtils'; +import {TitleFormatterFnDefault} from '../titleFormatterUtils'; -describe('DefaultTitleFormatter', () => { +describe('TitleFormatterFnDefault', () => { it('works', () => { expect( - DefaultTitleFormatter({ + TitleFormatterFnDefault({ title: 'a page', siteTitle: 'my site', titleDelimiter: '·', @@ -20,7 +20,7 @@ describe('DefaultTitleFormatter', () => { it('ignores empty title', () => { expect( - DefaultTitleFormatter({ + TitleFormatterFnDefault({ title: ' ', siteTitle: 'my site', titleDelimiter: '·', @@ -33,7 +33,7 @@ describe('DefaultTitleFormatter', () => { // By default it's preferable to avoid duplicate siteTitle // See also https://github.com/facebook/docusaurus/issues/5878#issuecomment-961505856 expect( - DefaultTitleFormatter({ + TitleFormatterFnDefault({ title: 'my site', siteTitle: 'my site', titleDelimiter: '·', diff --git a/packages/docusaurus-theme-common/src/utils/titleFormatterUtils.tsx b/packages/docusaurus-theme-common/src/utils/titleFormatterUtils.tsx index bbea718021..41da671d5b 100644 --- a/packages/docusaurus-theme-common/src/utils/titleFormatterUtils.tsx +++ b/packages/docusaurus-theme-common/src/utils/titleFormatterUtils.tsx @@ -5,21 +5,48 @@ * LICENSE file in the root directory of this source tree. */ +import {createContext, useContext, useMemo} from 'react'; +import type {ReactNode} from 'react'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import {ReactContextError} from './reactUtils'; type TitleFormatterParams = { - title: string | undefined; + /** + * The page title to format + * Usually provided through component + * But also when using useTitleFormatter().format(title) + */ + title: string; + /** + * The siteConfig.title value + */ siteTitle: string; + /** + * The siteConfig.titleDelimiter value + */ titleDelimiter: string; }; -type TitleFormatterFn = (params: TitleFormatterParams) => string; +/** + * This is the full formatting function + * Can be customized through React context with the provider + */ +export type TitleFormatterFn = (params: TitleFormatterParams) => string; -export const DefaultTitleFormatter: TitleFormatterFn = ({ +/** + * The default formatter is provided in params for convenience + */ +export type TitleFormatterFnWithDefaultFormatter = ( + params: TitleFormatterParams & { + defaultFormatter: (params: TitleFormatterParams) => string; + }, +) => string; + +export const TitleFormatterFnDefault: TitleFormatterFn = ({ title, siteTitle, titleDelimiter, -}: TitleFormatterParams): string => { +}): string => { const trimmedTitle = title?.trim(); if (!trimmedTitle || trimmedTitle === siteTitle) { return siteTitle; @@ -27,16 +54,47 @@ export const DefaultTitleFormatter: TitleFormatterFn = ({ return `${trimmedTitle} ${titleDelimiter} ${siteTitle}`; }; -type TitleFormatterUtils = {format: (title?: string) => string}; +/** + * This is the simpler API exposed to theme/users + */ +type TitleFormatter = {format: (title: string) => string}; + +const TitleFormatterContext = createContext(null); + +export function TitleFormatterProvider({ + formatter, + children, +}: { + children: ReactNode; + formatter: TitleFormatterFnWithDefaultFormatter; +}): ReactNode { + const {siteConfig} = useDocusaurusContext(); + const {title: siteTitle, titleDelimiter} = siteConfig; + const value: TitleFormatter = useMemo(() => { + return { + format: (title: string) => + formatter({ + title, + siteTitle, + titleDelimiter, + defaultFormatter: TitleFormatterFnDefault, + }), + }; + }, [formatter, siteTitle, titleDelimiter]); + return ( + + {children} + + ); +} /** * Returns a function to format the page title */ -export function useTitleFormatter(): TitleFormatterUtils { - const {siteConfig} = useDocusaurusContext(); - const formatter = DefaultTitleFormatter; - const {title: siteTitle, titleDelimiter} = siteConfig; - return { - format: (title) => formatter({title, siteTitle, titleDelimiter}), - }; +export function useTitleFormatter(): TitleFormatter { + const value = useContext(TitleFormatterContext); + if (value === null) { + throw new ReactContextError('TitleFormatterProvider'); + } + return value; }