feat: docs plugin options sidebarCollapsible + sidebarCollapsed (#5203)

* Add prop

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* Add `collapsible` option to sidebar item

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* Add eslint-ignore

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* Move new page

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* Allow in autogenerated

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* Fix tests

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* Move config options to plugin-docs

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* Make non-collapsible items always expanded

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* docs versioning cli should receive a single options object

* Update cli.test.ts

* revert validateCategoryMetadataFile change

* remove theme usage of themeConfig.sidebarCollapsible

* better handling of sidebar item category inconsistencies + add warning message

* Update snapshot

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* Handle plugin option inconsistencies

* improve doc for new sidebarCollapsible doc options

* remove warning in fixSidebarItemInconsistencies as it will be annoyed for versioned sites, as "collapsed" is already persisted in sidebar json files

Co-authored-by: slorber <lorber.sebastien@gmail.com>
This commit is contained in:
Joshua Chen 2021-07-23 20:24:36 +08:00 committed by GitHub
parent b38c35a36d
commit 24156efcfb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 487 additions and 109 deletions

View file

@ -25,6 +25,7 @@ import {
SidebarItemsGeneratorVersion,
NumberPrefixParser,
SidebarItemsGeneratorOption,
SidebarOptions,
PluginOptions,
} from './types';
import {mapValues, flatten, flatMap, difference, pick, memoize} from 'lodash';
@ -38,6 +39,7 @@ type SidebarItemCategoryJSON = SidebarItemBase & {
label: string;
items: SidebarItemJSON[];
collapsed?: boolean;
collapsible?: boolean;
};
type SidebarItemAutogeneratedJSON = SidebarItemBase & {
@ -74,18 +76,17 @@ function isCategoryShorthand(
return typeof item !== 'string' && !item.type;
}
// categories are collapsed by default, unless user set collapsed = false
export const DefaultCategoryCollapsedValue = true;
/**
* Convert {category1: [item1,item2]} shorthand syntax to long-form syntax
*/
function normalizeCategoryShorthand(
sidebar: SidebarCategoryShorthandJSON,
options: SidebarOptions,
): SidebarItemCategoryJSON[] {
return Object.entries(sidebar).map(([label, items]) => ({
type: 'category',
collapsed: DefaultCategoryCollapsedValue,
collapsed: options.sidebarCollapsed,
collapsible: options.sidebarCollapsible,
label,
items,
}));
@ -115,7 +116,13 @@ function assertItem<K extends string>(
function assertIsCategory(
item: Record<string, unknown>,
): asserts item is SidebarItemCategoryJSON {
assertItem(item, ['items', 'label', 'collapsed', 'customProps']);
assertItem(item, [
'items',
'label',
'collapsed',
'collapsible',
'customProps',
]);
if (typeof item.label !== 'string') {
throw new Error(
`Error loading ${JSON.stringify(item)}: "label" must be a string.`,
@ -135,6 +142,14 @@ function assertIsCategory(
`Error loading ${JSON.stringify(item)}: "collapsed" must be a boolean.`,
);
}
if (
typeof item.collapsible !== 'undefined' &&
typeof item.collapsible !== 'boolean'
) {
throw new Error(
`Error loading ${JSON.stringify(item)}: "collapsible" must be a boolean.`,
);
}
}
function assertIsAutogenerated(
@ -192,7 +207,10 @@ function assertIsLink(
* Normalizes recursively item and all its children. Ensures that at the end
* each item will be an object with the corresponding type.
*/
function normalizeItem(item: SidebarItemJSON): UnprocessedSidebarItem[] {
function normalizeItem(
item: SidebarItemJSON,
options: SidebarOptions,
): UnprocessedSidebarItem[] {
if (typeof item === 'string') {
return [
{
@ -202,16 +220,21 @@ function normalizeItem(item: SidebarItemJSON): UnprocessedSidebarItem[] {
];
}
if (isCategoryShorthand(item)) {
return flatMap(normalizeCategoryShorthand(item), normalizeItem);
return flatMap(normalizeCategoryShorthand(item, options), (subitem) =>
normalizeItem(subitem, options),
);
}
switch (item.type) {
case 'category':
assertIsCategory(item);
return [
{
collapsed: DefaultCategoryCollapsedValue,
...item,
items: flatMap(item.items, normalizeItem),
items: flatMap(item.items, (subItem) =>
normalizeItem(subItem, options),
),
collapsible: item.collapsible ?? options.sidebarCollapsible,
collapsed: item.collapsed ?? options.sidebarCollapsed,
},
];
case 'autogenerated':
@ -238,16 +261,24 @@ function normalizeItem(item: SidebarItemJSON): UnprocessedSidebarItem[] {
}
}
function normalizeSidebar(sidebar: SidebarJSON): UnprocessedSidebar {
function normalizeSidebar(
sidebar: SidebarJSON,
options: SidebarOptions,
): UnprocessedSidebar {
const normalizedSidebar: SidebarItemJSON[] = Array.isArray(sidebar)
? sidebar
: normalizeCategoryShorthand(sidebar);
: normalizeCategoryShorthand(sidebar, options);
return flatMap(normalizedSidebar, normalizeItem);
return flatMap(normalizedSidebar, (subitem) =>
normalizeItem(subitem, options),
);
}
function normalizeSidebars(sidebars: SidebarsJSON): UnprocessedSidebars {
return mapValues(sidebars, normalizeSidebar);
function normalizeSidebars(
sidebars: SidebarsJSON,
options: SidebarOptions,
): UnprocessedSidebars {
return mapValues(sidebars, (subitem) => normalizeSidebar(subitem, options));
}
export const DefaultSidebars: UnprocessedSidebars = {
@ -276,6 +307,7 @@ export function resolveSidebarPathOption(
// Note: sidebarFilePath must be absolute, use resolveSidebarPathOption
export function loadSidebars(
sidebarFilePath: string | false | undefined,
options: SidebarOptions,
): UnprocessedSidebars {
// false => no sidebars
if (sidebarFilePath === false) {
@ -297,7 +329,7 @@ export function loadSidebars(
// We don't want sidebars to be cached because of hot reloading.
const sidebarJson = importFresh(sidebarFilePath) as SidebarsJSON;
return normalizeSidebars(sidebarJson);
return normalizeSidebars(sidebarJson, options);
}
export function toSidebarItemsGeneratorDoc(
@ -317,19 +349,44 @@ export function toSidebarItemsGeneratorVersion(
return pick(version, ['versionName', 'contentPath']);
}
// Handle the generation of autogenerated sidebar items
export function fixSidebarItemInconsistencies(item: SidebarItem): SidebarItem {
function fixCategoryInconsistencies(
category: SidebarItemCategory,
): SidebarItemCategory {
// A non-collapsible category can't be collapsed!
if (!category.collapsible && category.collapsed) {
return {
...category,
collapsed: false,
};
}
return category;
}
if (item.type === 'category') {
return {
...fixCategoryInconsistencies(item),
items: item.items.map(fixSidebarItemInconsistencies),
};
}
return item;
}
// Handle the generation of autogenerated sidebar items and other post-processing checks
export async function processSidebar({
sidebarItemsGenerator,
numberPrefixParser,
unprocessedSidebar,
docs,
version,
options,
}: {
sidebarItemsGenerator: SidebarItemsGeneratorOption;
numberPrefixParser: NumberPrefixParser;
unprocessedSidebar: UnprocessedSidebar;
docs: DocMetadataBase[];
version: VersionMetadata;
options: SidebarOptions;
}): Promise<Sidebar> {
// Just a minor lazy transformation optimization
const getSidebarItemsGeneratorDocsAndVersion = memoize(() => ({
@ -337,14 +394,16 @@ export async function processSidebar({
version: toSidebarItemsGeneratorVersion(version),
}));
async function processRecursive(
async function handleAutoGeneratedItems(
item: UnprocessedSidebarItem,
): Promise<SidebarItem[]> {
if (item.type === 'category') {
return [
{
...item,
items: (await Promise.all(item.items.map(processRecursive))).flat(),
items: (
await Promise.all(item.items.map(handleAutoGeneratedItems))
).flat(),
},
];
}
@ -354,12 +413,17 @@ export async function processSidebar({
numberPrefixParser,
defaultSidebarItemsGenerator: DefaultSidebarItemsGenerator,
...getSidebarItemsGeneratorDocsAndVersion(),
options,
});
}
return [item];
}
return (await Promise.all(unprocessedSidebar.map(processRecursive))).flat();
const processedSidebar = (
await Promise.all(unprocessedSidebar.map(handleAutoGeneratedItems))
).flat();
return processedSidebar.map(fixSidebarItemInconsistencies);
}
export async function processSidebars({
@ -368,12 +432,14 @@ export async function processSidebars({
unprocessedSidebars,
docs,
version,
options,
}: {
sidebarItemsGenerator: SidebarItemsGeneratorOption;
numberPrefixParser: NumberPrefixParser;
unprocessedSidebars: UnprocessedSidebars;
docs: DocMetadataBase[];
version: VersionMetadata;
options: SidebarOptions;
}): Promise<Sidebars> {
return combinePromises(
mapValues(unprocessedSidebars, (unprocessedSidebar) =>
@ -383,6 +449,7 @@ export async function processSidebars({
unprocessedSidebar,
docs,
version,
options,
}),
),
);