diff --git a/packages/docusaurus-plugin-content-docs/src/client/__tests__/docsUtils.test.tsx b/packages/docusaurus-plugin-content-docs/src/client/__tests__/docsUtils.test.tsx index 44916fd33c99..00263ad5907d 100644 --- a/packages/docusaurus-plugin-content-docs/src/client/__tests__/docsUtils.test.tsx +++ b/packages/docusaurus-plugin-content-docs/src/client/__tests__/docsUtils.test.tsx @@ -15,6 +15,7 @@ import { useDocById, findSidebarCategory, useCurrentSidebarCategory, + useCurrentSidebarSiblings, useSidebarBreadcrumbs, isVisibleSidebarItem, } from '../docsUtils'; @@ -780,3 +781,128 @@ describe('useCurrentSidebarCategory', () => { ); }); }); + +describe('useCurrentSidebarSiblings', () => { + const createUseCurrentSidebarSiblingsMock = + (sidebar?: PropSidebar) => (location: string) => + renderHook(() => useCurrentSidebarSiblings(), { + wrapper: ({children}) => ( + + {children} + + ), + }).result.current; + + it('works for sidebar category', () => { + const category: PropSidebarItemCategory = testCategory({ + href: '/cat', + items: [testLink(), testLink()], + }); + const sidebar: PropSidebar = [ + testLink(), + testLink(), + category, + testCategory(), + ]; + + const mockUseCurrentSidebarCategory = + createUseCurrentSidebarSiblingsMock(sidebar); + + expect(mockUseCurrentSidebarCategory('/cat')).toEqual(category.items); + }); + + it('works for sidebar root', () => { + const category: PropSidebarItemCategory = testCategory({ + href: '/cat', + items: [testLink(), testLink()], + }); + const sidebar: PropSidebar = [ + testLink({href: '/rootLink'}), + testLink(), + category, + testCategory(), + ]; + + const mockUseCurrentSidebarCategory = + createUseCurrentSidebarSiblingsMock(sidebar); + + expect(mockUseCurrentSidebarCategory('/rootLink')).toEqual(sidebar); + }); + + it('works for nested sidebar category', () => { + const category2: PropSidebarItemCategory = testCategory({ + href: '/cat2', + items: [testLink(), testCategory()], + }); + const category1: PropSidebarItemCategory = testCategory({ + href: '/cat1', + items: [testLink(), testLink(), category2, testCategory()], + }); + const sidebar: PropSidebar = [ + testLink(), + testLink(), + category1, + testCategory(), + ]; + + const mockUseCurrentSidebarCategory = + createUseCurrentSidebarSiblingsMock(sidebar); + + expect(mockUseCurrentSidebarCategory('/cat2')).toEqual(category2.items); + }); + + 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 = + createUseCurrentSidebarSiblingsMock(sidebar); + + expect(mockUseCurrentSidebarCategory('/my/link/path')).toEqual( + category.items, + ); + }); + + 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 = + createUseCurrentSidebarSiblingsMock(sidebar); + + expect(mockUseCurrentSidebarCategory('/my/link/path')).toEqual( + category2.items, + ); + }); + + it('throws when sidebar is missing', () => { + const mockUseCurrentSidebarCategory = createUseCurrentSidebarSiblingsMock(); + expect(() => + mockUseCurrentSidebarCategory('/cat'), + ).toThrowErrorMatchingInlineSnapshot( + `"Unexpected: cant find current sidebar in context"`, + ); + }); +}); diff --git a/packages/docusaurus-plugin-content-docs/src/client/docsUtils.tsx b/packages/docusaurus-plugin-content-docs/src/client/docsUtils.tsx index 0fff426231a8..aa00df851034 100644 --- a/packages/docusaurus-plugin-content-docs/src/client/docsUtils.tsx +++ b/packages/docusaurus-plugin-content-docs/src/client/docsUtils.tsx @@ -132,6 +132,25 @@ export function useCurrentSidebarCategory(): PropSidebarItemCategory { return deepestCategory; } +/** + * Gets the category associated with the current location. Should only be used + * on category index pages. + */ +export function useCurrentSidebarSiblings(): PropSidebarItem[] { + const {pathname} = useLocation(); + const sidebar = useDocsSidebar(); + if (!sidebar) { + throw new Error('Unexpected: cant find current sidebar in context'); + } + const categoryBreadcrumbs = getSidebarBreadcrumbs({ + sidebarItems: sidebar.items, + pathname, + onlyCategories: true, + }); + const deepestCategory = categoryBreadcrumbs.slice(-1)[0]; + return deepestCategory?.items ?? sidebar.items; +} + const isActive = (testedPath: string | undefined, activePath: string) => typeof testedPath !== 'undefined' && isSamePath(testedPath, activePath); const containsActiveSidebarItem = ( diff --git a/packages/docusaurus-plugin-content-docs/src/client/index.ts b/packages/docusaurus-plugin-content-docs/src/client/index.ts index f60ba572faef..6734431adca7 100644 --- a/packages/docusaurus-plugin-content-docs/src/client/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/client/index.ts @@ -33,6 +33,7 @@ export { useLayoutDocsSidebar, useDocRootMetadata, useCurrentSidebarCategory, + useCurrentSidebarSiblings, filterDocCardListItems, } from './docsUtils'; diff --git a/packages/docusaurus-theme-classic/src/theme/DocCardList/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocCardList/index.tsx index a45ee6e9801f..62374763ca71 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocCardList/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocCardList/index.tsx @@ -8,15 +8,15 @@ import React, {type ReactNode} from 'react'; import clsx from 'clsx'; import { - useCurrentSidebarCategory, + useCurrentSidebarSiblings, filterDocCardListItems, } from '@docusaurus/plugin-content-docs/client'; import DocCard from '@theme/DocCard'; import type {Props} from '@theme/DocCardList'; function DocCardListForCurrentSidebarCategory({className}: Props) { - const category = useCurrentSidebarCategory(); - return ; + const items = useCurrentSidebarSiblings(); + return ; } export default function DocCardList(props: Props): ReactNode { diff --git a/website/_dogfooding/_docs tests/index.mdx b/website/_dogfooding/_docs tests/index.mdx index a97ed434eff9..8ff3cccab6e1 100644 --- a/website/_dogfooding/_docs tests/index.mdx +++ b/website/_dogfooding/_docs tests/index.mdx @@ -13,3 +13,9 @@ This Docusaurus docs plugin instance is meant to test fancy edge-cases that regu - [/tests/docs](/tests/docs) - [/tests/blog](/tests/blog) - [/tests/pages](/tests/pages) + +--- + +import DocCardList from '@theme/DocCardList'; + +