feat(theme): ability to use <DocCardList> without items prop, on any doc page (#8008)

This commit is contained in:
Sébastien Lorber 2022-08-26 17:42:59 +02:00 committed by GitHub
parent ff8ef774d6
commit c811d6249e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 191 additions and 67 deletions

View file

@ -28,7 +28,10 @@ export {createStorageSlot, listStorageKeys} from './utils/storageUtils';
export {useContextualSearchFilters} from './utils/searchUtils';
export {useCurrentSidebarCategory} from './utils/docsUtils';
export {
useCurrentSidebarCategory,
filterDocCardListItems,
} from './utils/docsUtils';
export {usePluralForm} from './utils/usePluralForm';

View file

@ -441,26 +441,87 @@ describe('useCurrentSidebarCategory', () => {
</DocsSidebarProvider>
),
}).result.current;
it('works', () => {
const category: PropSidebarItemCategory = {
type: 'category',
label: 'Category',
it('works for sidebar category', () => {
const category: PropSidebarItemCategory = testCategory({
href: '/cat',
collapsible: true,
collapsed: false,
items: [
{type: 'link', href: '/cat/foo', label: 'Foo'},
{type: 'link', href: '/cat/bar', label: 'Bar'},
{type: 'link', href: '/baz', label: 'Baz'},
],
};
const mockUseCurrentSidebarCategory = createUseCurrentSidebarCategoryMock([
{type: 'link', href: '/cat/fake', label: 'Fake'},
});
const sidebar: PropSidebar = [
testLink(),
testLink(),
category,
]);
testCategory(),
];
const mockUseCurrentSidebarCategory =
createUseCurrentSidebarCategoryMock(sidebar);
expect(mockUseCurrentSidebarCategory('/cat')).toEqual(category);
});
it('works for nested sidebar category', () => {
const category2: PropSidebarItemCategory = testCategory({
href: '/cat2',
});
const category1: PropSidebarItemCategory = testCategory({
href: '/cat1',
items: [testLink(), testLink(), category2, testCategory()],
});
const sidebar: PropSidebar = [
testLink(),
testLink(),
category1,
testCategory(),
];
const mockUseCurrentSidebarCategory =
createUseCurrentSidebarCategoryMock(sidebar);
expect(mockUseCurrentSidebarCategory('/cat2')).toEqual(category2);
});
it('works for category link item', () => {
const link = testLink({href: '/my/link/path'});
const category: PropSidebarItemCategory = testCategory({
href: '/cat1',
items: [testLink(), testLink(), link, testCategory()],
});
const sidebar: PropSidebar = [
testLink(),
testLink(),
category,
testCategory(),
];
const mockUseCurrentSidebarCategory =
createUseCurrentSidebarCategoryMock(sidebar);
expect(mockUseCurrentSidebarCategory('/my/link/path')).toEqual(category);
});
it('works for nested category link item', () => {
const link = testLink({href: '/my/link/path'});
const category2: PropSidebarItemCategory = testCategory({
href: '/cat2',
items: [testLink(), testLink(), link, testCategory()],
});
const category1: PropSidebarItemCategory = testCategory({
href: '/cat1',
items: [testLink(), testLink(), category2, testCategory()],
});
const sidebar: PropSidebar = [
testLink(),
testLink(),
category1,
testCategory(),
];
const mockUseCurrentSidebarCategory =
createUseCurrentSidebarCategoryMock(sidebar);
expect(mockUseCurrentSidebarCategory('/my/link/path')).toEqual(category2);
});
it('throws for non-category index page', () => {
const category: PropSidebarItemCategory = {
type: 'category',

View file

@ -110,15 +110,18 @@ export function useCurrentSidebarCategory(): PropSidebarItemCategory {
if (!sidebar) {
throw new Error('Unexpected: cant find current sidebar in context');
}
const category = findSidebarCategory(sidebar.items, (item) =>
isSamePath(item.href, pathname),
);
if (!category) {
const categoryBreadcrumbs = getSidebarBreadcrumbs({
sidebarItems: sidebar.items,
pathname,
onlyCategories: true,
});
const deepestCategory = categoryBreadcrumbs.slice(-1)[0];
if (!deepestCategory) {
throw new Error(
`${pathname} is not associated with a category. useCurrentSidebarCategory() should only be used on category index pages.`,
);
}
return category;
return deepestCategory;
}
const isActive = (testedPath: string | undefined, activePath: string) =>
@ -149,6 +152,55 @@ export function isActiveSidebarItem(
return false;
}
function getSidebarBreadcrumbs(param: {
sidebarItems: PropSidebar;
pathname: string;
onlyCategories: true;
}): PropSidebarItemCategory[];
function getSidebarBreadcrumbs(param: {
sidebarItems: PropSidebar;
pathname: string;
}): PropSidebarBreadcrumbsItem[];
/**
* Get the sidebar the breadcrumbs for a given pathname
* Ordered from top to bottom
*/
function getSidebarBreadcrumbs({
sidebarItems,
pathname,
onlyCategories = false,
}: {
sidebarItems: PropSidebar;
pathname: string;
onlyCategories?: boolean;
}): PropSidebarBreadcrumbsItem[] {
const breadcrumbs: PropSidebarBreadcrumbsItem[] = [];
function extract(items: PropSidebarItem[]) {
for (const item of items) {
if (
(item.type === 'category' &&
(isSamePath(item.href, pathname) || extract(item.items))) ||
(item.type === 'link' && isSamePath(item.href, pathname))
) {
const filtered = onlyCategories && item.type !== 'category';
if (!filtered) {
breadcrumbs.unshift(item);
}
return true;
}
}
return false;
}
extract(sidebarItems);
return breadcrumbs;
}
/**
* Gets the breadcrumbs of the current doc page, based on its sidebar location.
* Returns `null` if there's no sidebar or breadcrumbs are disabled.
@ -157,31 +209,10 @@ export function useSidebarBreadcrumbs(): PropSidebarBreadcrumbsItem[] | null {
const sidebar = useDocsSidebar();
const {pathname} = useLocation();
const breadcrumbsOption = useActivePlugin()?.pluginData.breadcrumbs;
if (breadcrumbsOption === false || !sidebar) {
return null;
}
const breadcrumbs: PropSidebarBreadcrumbsItem[] = [];
function extract(items: PropSidebar) {
for (const item of items) {
if (
(item.type === 'category' &&
(isSamePath(item.href, pathname) || extract(item.items))) ||
(item.type === 'link' && isSamePath(item.href, pathname))
) {
breadcrumbs.push(item);
return true;
}
}
return false;
}
extract(sidebar.items);
return breadcrumbs.reverse();
return getSidebarBreadcrumbs({sidebarItems: sidebar.items, pathname});
}
/**
@ -330,3 +361,18 @@ export function useDocRootMetadata({route}: DocRootProps): null | {
sidebarItems,
};
}
/**
* Filter categories that don't have a link.
* @param items
*/
export function filterDocCardListItems(
items: PropSidebarItem[],
): PropSidebarItem[] {
return items.filter((item) => {
if (item.type === 'category') {
return !!findFirstCategoryLink(item);
}
return true;
});
}