mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-30 09:27:04 +02:00
feat(theme-classic, plugin-docs): sidebar item level-specific className + allow customization (#5642)
* Initial work * Complete function * Avoid duplication * More dedupe * Make everything constants * Change casing & docs
This commit is contained in:
parent
f6ec757aa0
commit
eaacb0e98a
14 changed files with 79 additions and 11 deletions
|
@ -29,6 +29,7 @@ const DocFrontMatterSchema = Joi.object<DocFrontMatter>({
|
|||
slug: Joi.string(),
|
||||
sidebar_label: Joi.string(),
|
||||
sidebar_position: Joi.number(),
|
||||
sidebar_class_name: Joi.string(),
|
||||
tags: FrontMatterTagsSchema,
|
||||
pagination_label: Joi.string(),
|
||||
custom_edit_url: URISchema.allow('', null),
|
||||
|
|
|
@ -30,6 +30,7 @@ declare module '@docusaurus/plugin-content-docs-types' {
|
|||
};
|
||||
|
||||
type PropsSidebarItemBase = {
|
||||
className?: string;
|
||||
customProps?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ Available document ids are:
|
|||
type: 'link',
|
||||
label: sidebarLabel || item.label || title,
|
||||
href: permalink,
|
||||
className: item.className,
|
||||
customProps: item.customProps,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -30,6 +30,7 @@ export type CategoryMetadatasFile = {
|
|||
position?: number;
|
||||
collapsed?: boolean;
|
||||
collapsible?: boolean;
|
||||
className?: string;
|
||||
|
||||
// TODO should we allow "items" here? how would this work? would an "autogenerated" type be allowed?
|
||||
// This mkdocs plugin do something like that: https://github.com/lukasgeiter/mkdocs-awesome-pages-plugin/
|
||||
|
@ -44,6 +45,7 @@ const CategoryMetadatasFileSchema = Joi.object<CategoryMetadatasFile>({
|
|||
position: Joi.number(),
|
||||
collapsed: Joi.boolean(),
|
||||
collapsible: Joi.boolean(),
|
||||
className: Joi.string(),
|
||||
});
|
||||
|
||||
// TODO I now believe we should read all the category metadata files ahead of time: we may need this metadata to customize docs metadata
|
||||
|
@ -177,6 +179,9 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
|
|||
...(doc.frontMatter.sidebar_label && {
|
||||
label: doc.frontMatter.sidebar_label,
|
||||
}),
|
||||
...(doc.frontMatter.sidebar_class_name && {
|
||||
className: doc.frontMatter.sidebar_class_name,
|
||||
}),
|
||||
...(typeof doc.sidebarPosition !== 'undefined' && {
|
||||
position: doc.sidebarPosition,
|
||||
}),
|
||||
|
@ -205,6 +210,7 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
|
|||
const collapsible =
|
||||
categoryMetadatas?.collapsible ?? options.sidebarCollapsible;
|
||||
const collapsed = categoryMetadatas?.collapsed ?? options.sidebarCollapsed;
|
||||
const className = categoryMetadatas?.className;
|
||||
|
||||
return {
|
||||
type: 'category',
|
||||
|
@ -213,6 +219,7 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
|
|||
collapsed,
|
||||
collapsible,
|
||||
...(typeof position !== 'undefined' && {position}),
|
||||
...(typeof className !== 'undefined' && {className}),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -311,5 +318,5 @@ function sortSidebarItems(
|
|||
['asc'],
|
||||
);
|
||||
|
||||
return sortedSidebarItems.map(({position: _removed, ...item}) => item);
|
||||
return sortedSidebarItems.map(({position, ...item}) => item);
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ type SidebarItemCategoryJSON = SidebarItemBase & {
|
|||
items: SidebarItemJSON[];
|
||||
collapsed?: boolean;
|
||||
collapsible?: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
type SidebarItemAutogeneratedJSON = SidebarItemBase & {
|
||||
|
@ -100,8 +101,7 @@ function assertItem<K extends string>(
|
|||
keys: K[],
|
||||
): asserts item is Record<K, unknown> {
|
||||
const unknownKeys = Object.keys(item).filter(
|
||||
// @ts-expect-error: key is always string
|
||||
(key) => !keys.includes(key as string) && key !== 'type',
|
||||
(key) => !keys.includes(key as K) && key !== 'type',
|
||||
);
|
||||
|
||||
if (unknownKeys.length) {
|
||||
|
@ -121,6 +121,7 @@ function assertIsCategory(
|
|||
'label',
|
||||
'collapsed',
|
||||
'collapsible',
|
||||
'className',
|
||||
'customProps',
|
||||
]);
|
||||
if (typeof item.label !== 'string') {
|
||||
|
@ -150,6 +151,14 @@ function assertIsCategory(
|
|||
`Error loading ${JSON.stringify(item)}: "collapsible" must be a boolean.`,
|
||||
);
|
||||
}
|
||||
if (
|
||||
typeof item.className !== 'undefined' &&
|
||||
typeof item.className !== 'string'
|
||||
) {
|
||||
throw new Error(
|
||||
`Error loading ${JSON.stringify(item)}: "className" must be a string.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function assertIsAutogenerated(
|
||||
|
@ -173,24 +182,33 @@ function assertIsAutogenerated(
|
|||
function assertIsDoc(
|
||||
item: Record<string, unknown>,
|
||||
): asserts item is SidebarItemDoc {
|
||||
assertItem(item, ['id', 'label', 'customProps']);
|
||||
assertItem(item, ['id', 'label', 'className', 'customProps']);
|
||||
if (typeof item.id !== 'string') {
|
||||
throw new Error(
|
||||
`Error loading ${JSON.stringify(item)}: "id" must be a string.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (item.label && typeof item.label !== 'string') {
|
||||
if (typeof item.label !== 'undefined' && typeof item.label !== 'string') {
|
||||
throw new Error(
|
||||
`Error loading ${JSON.stringify(item)}: "label" must be a string.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
typeof item.className !== 'undefined' &&
|
||||
typeof item.className !== 'string'
|
||||
) {
|
||||
throw new Error(
|
||||
`Error loading ${JSON.stringify(item)}: "className" must be a string.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function assertIsLink(
|
||||
item: Record<string, unknown>,
|
||||
): asserts item is SidebarItemLink {
|
||||
assertItem(item, ['href', 'label', 'customProps']);
|
||||
assertItem(item, ['href', 'label', 'className', 'customProps']);
|
||||
if (typeof item.href !== 'string') {
|
||||
throw new Error(
|
||||
`Error loading ${JSON.stringify(item)}: "href" must be a string.`,
|
||||
|
@ -201,6 +219,14 @@ function assertIsLink(
|
|||
`Error loading ${JSON.stringify(item)}: "label" must be a string.`,
|
||||
);
|
||||
}
|
||||
if (
|
||||
typeof item.className !== 'undefined' &&
|
||||
typeof item.className !== 'string'
|
||||
) {
|
||||
throw new Error(
|
||||
`Error loading ${JSON.stringify(item)}: "className" must be a string.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -105,6 +105,7 @@ export type PluginOptions = MetadataOptions &
|
|||
};
|
||||
|
||||
export type SidebarItemBase = {
|
||||
className?: string;
|
||||
customProps?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
|
@ -217,6 +218,7 @@ export type DocFrontMatter = {
|
|||
slug?: string;
|
||||
sidebar_label?: string;
|
||||
sidebar_position?: number;
|
||||
sidebar_class_name?: string;
|
||||
pagination_label?: string;
|
||||
custom_edit_url?: string | null;
|
||||
parse_number_prefixes?: boolean;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue