diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/validation.test.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/validation.test.ts index 49ac30fdfe..28a0d0eb19 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/validation.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/validation.test.ts @@ -47,6 +47,42 @@ describe('validateSidebars', () => { }); }); +describe('html item type', () => { + test('requires a value', () => { + const sidebars: SidebarsConfig = { + sidebar1: [ + { + // @ts-expect-error - test missing value + type: 'html', + }, + ], + }; + expect(() => validateSidebars(sidebars)) + .toThrowErrorMatchingInlineSnapshot(` + "{ + \\"type\\": \\"html\\", + \\"value\\" [1]: -- missing -- + } +  + [1] \\"value\\" is required" + `); + }); + + test('accepts valid values', () => { + const sidebars: SidebarsConfig = { + sidebar1: [ + { + type: 'html', + value: '

Hello, World!

', + defaultStyle: true, + className: 'foo', + }, + ], + }; + validateSidebars(sidebars); + }); +}); + describe('validateCategoryMetadataFile', () => { // TODO add more tests diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/types.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/types.ts index 331887d122..e141e55464 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars/types.ts @@ -27,6 +27,12 @@ export type SidebarItemDoc = SidebarItemBase & { id: string; }; +export type SidebarItemHtml = SidebarItemBase & { + type: 'html'; + value: string; + defaultStyle?: boolean; +}; + export type SidebarItemLink = SidebarItemBase & { type: 'link'; href: string; @@ -87,6 +93,7 @@ export type SidebarCategoriesShorthand = { export type SidebarItemConfig = | SidebarItemDoc + | SidebarItemHtml | SidebarItemLink | SidebarItemAutogenerated | SidebarItemCategoryConfig @@ -108,6 +115,7 @@ export type NormalizedSidebarItemCategory = Expand< export type NormalizedSidebarItem = | SidebarItemDoc + | SidebarItemHtml | SidebarItemLink | NormalizedSidebarItemCategory | SidebarItemAutogenerated; @@ -131,6 +139,7 @@ export type SidebarItemCategoryWithGeneratedIndex = export type SidebarItem = | SidebarItemDoc + | SidebarItemHtml | SidebarItemLink | SidebarItemCategory; @@ -158,7 +167,12 @@ export type PropSidebarItemLink = SidebarItemLink & { docId?: string; }; -export type PropSidebarItem = PropSidebarItemLink | PropSidebarItemCategory; +export type PropSidebarItemHtml = SidebarItemHtml; + +export type PropSidebarItem = + | PropSidebarItemLink + | PropSidebarItemCategory + | PropSidebarItemHtml; export type PropSidebar = PropSidebarItem[]; export type PropSidebars = { [sidebarId: string]: PropSidebar; diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/validation.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/validation.ts index ac00bf455c..7eb814172e 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars/validation.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars/validation.ts @@ -12,6 +12,7 @@ import type { SidebarItemBase, SidebarItemAutogenerated, SidebarItemDoc, + SidebarItemHtml, SidebarItemLink, SidebarItemCategoryConfig, SidebarItemCategoryLink, @@ -48,6 +49,12 @@ const sidebarItemDocSchema = sidebarItemBaseSchema.append({ label: Joi.string(), }); +const sidebarItemHtmlSchema = sidebarItemBaseSchema.append({ + type: 'html', + value: Joi.string().required(), + defaultStyle: Joi.boolean().default(false), +}); + const sidebarItemLinkSchema = sidebarItemBaseSchema.append({ type: 'link', href: URISchema.required(), @@ -117,6 +124,7 @@ const sidebarItemSchema: Joi.Schema = Joi.object() is: Joi.string().valid('doc', 'ref').required(), then: sidebarItemDocSchema, }, + {is: 'html', then: sidebarItemHtmlSchema}, {is: 'autogenerated', then: sidebarItemAutogeneratedSchema}, {is: 'category', then: sidebarItemCategorySchema}, { diff --git a/packages/docusaurus-theme-classic/src/theme/DocSidebarItem/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocSidebarItem/index.tsx index 543d526319..462ebc12e9 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocSidebarItem/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocSidebarItem/index.tsx @@ -32,6 +32,7 @@ import type { import styles from './styles.module.css'; import useIsBrowser from '@docusaurus/useIsBrowser'; +import type {SidebarItemHtml} from '@docusaurus/plugin-content-docs/src/sidebars/types'; export default function DocSidebarItem({ item, @@ -40,6 +41,8 @@ export default function DocSidebarItem({ switch (item.type) { case 'category': return ; + case 'html': + return ; case 'link': default: return ; @@ -216,6 +219,29 @@ function DocSidebarItemCategory({ ); } +function DocSidebarItemHtml({ + item, + level, + index, +}: Props & {item: SidebarItemHtml}) { + const {value, defaultStyle, className} = item; + return ( +
  • + ); +} + function DocSidebarItemLink({ item, onItemClick, diff --git a/packages/docusaurus-theme-classic/src/theme/DocSidebarItem/styles.module.css b/packages/docusaurus-theme-classic/src/theme/DocSidebarItem/styles.module.css index e197c843f8..0d397f8170 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocSidebarItem/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/DocSidebarItem/styles.module.css @@ -17,4 +17,9 @@ .menuLinkText.hasHref { cursor: pointer; } + + .menuHtmlItem { + padding: var(--ifm-menu-link-padding-vertical) + var(--ifm-menu-link-padding-horizontal); + } } diff --git a/packages/docusaurus-theme-common/src/utils/__tests__/docsUtils.test.tsx b/packages/docusaurus-theme-common/src/utils/__tests__/docsUtils.test.tsx index 0979ade52f..06fd3686d2 100644 --- a/packages/docusaurus-theme-common/src/utils/__tests__/docsUtils.test.tsx +++ b/packages/docusaurus-theme-common/src/utils/__tests__/docsUtils.test.tsx @@ -226,9 +226,11 @@ describe('docsUtils', () => { testCategory({ href: undefined, items: [ + {type: 'html', value: '

    test1

    '}, testCategory({ href: undefined, items: [ + {type: 'html', value: '

    test2

    '}, testCategory({ href: '/itemPath', }), diff --git a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx index 676ccee827..2ba9be962f 100644 --- a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx +++ b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx @@ -123,12 +123,13 @@ export function findFirstCategoryLink( for (const subItem of item.items) { if (subItem.type === 'link') { return subItem.href; - } - if (subItem.type === 'category') { + } else if (subItem.type === 'category') { const categoryLink = findFirstCategoryLink(subItem); if (categoryLink) { return categoryLink; } + } else if (subItem.type === 'html') { + // skip } else { throw new Error( `Unexpected category item type for ${JSON.stringify(subItem)}`, diff --git a/website/_dogfooding/docs-tests-sidebars.js b/website/_dogfooding/docs-tests-sidebars.js index 8be3194694..ec54a32d02 100644 --- a/website/_dogfooding/docs-tests-sidebars.js +++ b/website/_dogfooding/docs-tests-sidebars.js @@ -71,6 +71,33 @@ const sidebars = { }, ], }, + { + type: 'category', + label: 'HTML items tests', + items: [ + // title + { + type: 'html', + value: 'Some Text', + defaultStyle: true, + }, + // Divider + { + type: 'html', + value: + '', + }, + // Image + { + type: 'html', + defaultStyle: true, + value: ` + Powered by + Docusaurus Logo + `, + }, + ], + }, ], anotherSidebar: ['dummy'], }; diff --git a/website/docs/guides/docs/sidebar/items.md b/website/docs/guides/docs/sidebar/items.md index c41785664f..248f684f9c 100644 --- a/website/docs/guides/docs/sidebar/items.md +++ b/website/docs/guides/docs/sidebar/items.md @@ -11,6 +11,7 @@ We have introduced three types of item types in the example in the previous sect - **[Link](#sidebar-item-link)**: link to any internal or external page - **[Category](#sidebar-item-category)**: creates a dropdown of sidebar items - **[Autogenerated](autogenerated.md)**: generate a sidebar slice automatically +- **[HTML](#sidebar-item-html)**: renders pure HTML in the item's position - **[\*Ref](multiple-sidebars.md#sidebar-item-ref)**: link to a doc page, without making the item take part in navigation generation ## Doc: link to a doc {#sidebar-item-doc} @@ -160,6 +161,55 @@ module.exports = { ::: +## HTML: render custom markup {#sidebar-item-html} + +Use the `html` type to render custom HTML within the item's `
  • ` tag. + +This can be useful for inserting custom items such as dividers, section titles, ads, and images. + +```ts +type SidebarItemHtml = { + type: 'html'; + value: string; + defaultStyle?: boolean; // Use default menu item styles + className?: string; +}; +``` + +Example: + +```js title="sidebars.js" +module.exports = { + myHtmlSidebar: [ + // highlight-start + { + type: 'html', + value: 'Sponsor', // The HTML to be rendered + defaultStyle: true, // Use the default menu item styling + }, + // highlight-end + ], +}; +``` + +:::tip + +The menu item is already wrapped in an `
  • ` tag, so if your custom item is simple, such as a title, just supply a string as the value and use the `className` property to style it: + +```js title="sidebars.js" +module.exports = { + myHtmlSidebar: [ + { + type: 'html', + value: 'Core concepts', + className: 'sidebar-title', + }, + ], +}; +``` + +::: + ### Category links {#category-link} With category links, clicking on a category can navigate you to another page.