Skip to content

Commit 67207bc

Browse files
authored
fix(theme): Fix <DocCardList> usage on docs at root of a sidebar (#10847)
Fix `<DocCardList>` usage on docs at root of a sidebar
1 parent e59355b commit 67207bc

File tree

5 files changed

+155
-3
lines changed

5 files changed

+155
-3
lines changed

packages/docusaurus-plugin-content-docs/src/client/__tests__/docsUtils.test.tsx

+126
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
useDocById,
1616
findSidebarCategory,
1717
useCurrentSidebarCategory,
18+
useCurrentSidebarSiblings,
1819
useSidebarBreadcrumbs,
1920
isVisibleSidebarItem,
2021
} from '../docsUtils';
@@ -780,3 +781,128 @@ describe('useCurrentSidebarCategory', () => {
780781
);
781782
});
782783
});
784+
785+
describe('useCurrentSidebarSiblings', () => {
786+
const createUseCurrentSidebarSiblingsMock =
787+
(sidebar?: PropSidebar) => (location: string) =>
788+
renderHook(() => useCurrentSidebarSiblings(), {
789+
wrapper: ({children}) => (
790+
<DocsSidebarProvider name="sidebarName" items={sidebar}>
791+
<StaticRouter location={location}>{children}</StaticRouter>
792+
</DocsSidebarProvider>
793+
),
794+
}).result.current;
795+
796+
it('works for sidebar category', () => {
797+
const category: PropSidebarItemCategory = testCategory({
798+
href: '/cat',
799+
items: [testLink(), testLink()],
800+
});
801+
const sidebar: PropSidebar = [
802+
testLink(),
803+
testLink(),
804+
category,
805+
testCategory(),
806+
];
807+
808+
const mockUseCurrentSidebarCategory =
809+
createUseCurrentSidebarSiblingsMock(sidebar);
810+
811+
expect(mockUseCurrentSidebarCategory('/cat')).toEqual(category.items);
812+
});
813+
814+
it('works for sidebar root', () => {
815+
const category: PropSidebarItemCategory = testCategory({
816+
href: '/cat',
817+
items: [testLink(), testLink()],
818+
});
819+
const sidebar: PropSidebar = [
820+
testLink({href: '/rootLink'}),
821+
testLink(),
822+
category,
823+
testCategory(),
824+
];
825+
826+
const mockUseCurrentSidebarCategory =
827+
createUseCurrentSidebarSiblingsMock(sidebar);
828+
829+
expect(mockUseCurrentSidebarCategory('/rootLink')).toEqual(sidebar);
830+
});
831+
832+
it('works for nested sidebar category', () => {
833+
const category2: PropSidebarItemCategory = testCategory({
834+
href: '/cat2',
835+
items: [testLink(), testCategory()],
836+
});
837+
const category1: PropSidebarItemCategory = testCategory({
838+
href: '/cat1',
839+
items: [testLink(), testLink(), category2, testCategory()],
840+
});
841+
const sidebar: PropSidebar = [
842+
testLink(),
843+
testLink(),
844+
category1,
845+
testCategory(),
846+
];
847+
848+
const mockUseCurrentSidebarCategory =
849+
createUseCurrentSidebarSiblingsMock(sidebar);
850+
851+
expect(mockUseCurrentSidebarCategory('/cat2')).toEqual(category2.items);
852+
});
853+
854+
it('works for category link item', () => {
855+
const link = testLink({href: '/my/link/path'});
856+
const category: PropSidebarItemCategory = testCategory({
857+
href: '/cat1',
858+
items: [testLink(), testLink(), link, testCategory()],
859+
});
860+
const sidebar: PropSidebar = [
861+
testLink(),
862+
testLink(),
863+
category,
864+
testCategory(),
865+
];
866+
867+
const mockUseCurrentSidebarCategory =
868+
createUseCurrentSidebarSiblingsMock(sidebar);
869+
870+
expect(mockUseCurrentSidebarCategory('/my/link/path')).toEqual(
871+
category.items,
872+
);
873+
});
874+
875+
it('works for nested category link item', () => {
876+
const link = testLink({href: '/my/link/path'});
877+
const category2: PropSidebarItemCategory = testCategory({
878+
href: '/cat2',
879+
items: [testLink(), testLink(), link, testCategory()],
880+
});
881+
const category1: PropSidebarItemCategory = testCategory({
882+
href: '/cat1',
883+
items: [testLink(), testLink(), category2, testCategory()],
884+
});
885+
const sidebar: PropSidebar = [
886+
testLink(),
887+
testLink(),
888+
category1,
889+
testCategory(),
890+
];
891+
892+
const mockUseCurrentSidebarCategory =
893+
createUseCurrentSidebarSiblingsMock(sidebar);
894+
895+
expect(mockUseCurrentSidebarCategory('/my/link/path')).toEqual(
896+
category2.items,
897+
);
898+
});
899+
900+
it('throws when sidebar is missing', () => {
901+
const mockUseCurrentSidebarCategory = createUseCurrentSidebarSiblingsMock();
902+
expect(() =>
903+
mockUseCurrentSidebarCategory('/cat'),
904+
).toThrowErrorMatchingInlineSnapshot(
905+
`"Unexpected: cant find current sidebar in context"`,
906+
);
907+
});
908+
});

packages/docusaurus-plugin-content-docs/src/client/docsUtils.tsx

+19
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,25 @@ export function useCurrentSidebarCategory(): PropSidebarItemCategory {
132132
return deepestCategory;
133133
}
134134

135+
/**
136+
* Gets the category associated with the current location. Should only be used
137+
* on category index pages.
138+
*/
139+
export function useCurrentSidebarSiblings(): PropSidebarItem[] {
140+
const {pathname} = useLocation();
141+
const sidebar = useDocsSidebar();
142+
if (!sidebar) {
143+
throw new Error('Unexpected: cant find current sidebar in context');
144+
}
145+
const categoryBreadcrumbs = getSidebarBreadcrumbs({
146+
sidebarItems: sidebar.items,
147+
pathname,
148+
onlyCategories: true,
149+
});
150+
const deepestCategory = categoryBreadcrumbs.slice(-1)[0];
151+
return deepestCategory?.items ?? sidebar.items;
152+
}
153+
135154
const isActive = (testedPath: string | undefined, activePath: string) =>
136155
typeof testedPath !== 'undefined' && isSamePath(testedPath, activePath);
137156
const containsActiveSidebarItem = (

packages/docusaurus-plugin-content-docs/src/client/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export {
3333
useLayoutDocsSidebar,
3434
useDocRootMetadata,
3535
useCurrentSidebarCategory,
36+
useCurrentSidebarSiblings,
3637
filterDocCardListItems,
3738
} from './docsUtils';
3839

packages/docusaurus-theme-classic/src/theme/DocCardList/index.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@
88
import React, {type ReactNode} from 'react';
99
import clsx from 'clsx';
1010
import {
11-
useCurrentSidebarCategory,
11+
useCurrentSidebarSiblings,
1212
filterDocCardListItems,
1313
} from '@docusaurus/plugin-content-docs/client';
1414
import DocCard from '@theme/DocCard';
1515
import type {Props} from '@theme/DocCardList';
1616

1717
function DocCardListForCurrentSidebarCategory({className}: Props) {
18-
const category = useCurrentSidebarCategory();
19-
return <DocCardList items={category.items} className={className} />;
18+
const items = useCurrentSidebarSiblings();
19+
return <DocCardList items={items} className={className} />;
2020
}
2121

2222
export default function DocCardList(props: Props): ReactNode {

website/_dogfooding/_docs tests/index.mdx

+6
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,9 @@ This Docusaurus docs plugin instance is meant to test fancy edge-cases that regu
1313
- [/tests/docs](/tests/docs)
1414
- [/tests/blog](/tests/blog)
1515
- [/tests/pages](/tests/pages)
16+
17+
---
18+
19+
import DocCardList from '@theme/DocCardList';
20+
21+
<DocCardList />

0 commit comments

Comments
 (0)