fix(docs): prioritize category href over links in sidebar breadcrumb search

When a sidebar link in one category points to a generated-index page URL
from another category, the breadcrumb search would incorrectly return the
link's parent category instead of the category that owns the URL.

This fix uses a two-pass approach when searching for the current sidebar
category:
1. First pass: Look for categories that directly own the URL (category.href)
2. Second pass: If not found, fall back to finding via links (for doc pages)

This ensures generated-index pages display their correct category's items
even when another category has a link pointing to the same URL.

Closes #11612
This commit is contained in:
Chesars 2025-12-11 11:48:52 -03:00
parent d4a66aa2ed
commit 3d28a9e798
2 changed files with 78 additions and 4 deletions

View File

@ -780,6 +780,43 @@ describe('useCurrentSidebarCategory', () => {
`"Unexpected: cant find current sidebar in context"`,
);
});
// Regression test for https://github.com/facebook/docusaurus/issues/11612
// When a link in Category A points to a generated-index URL owned by
// Category B, useCurrentSidebarCategory should return Category B (the owner),
// not Category A (which merely has a link pointing to it).
it('returns the category that owns the URL, not a category with a link pointing to it', () => {
// Category B is the actual owner of /category-b (its generated-index href)
const categoryB: PropSidebarItemCategory = testCategory({
label: 'Category B',
href: '/category-b',
items: [
testLink({href: '/category-b/item1', label: 'Item 1'}),
testLink({href: '/category-b/item2', label: 'Item 2'}),
],
});
// Category A contains a link that points to Category B's URL
const categoryA: PropSidebarItemCategory = testCategory({
label: 'Category A',
href: '/category-a',
items: [
testLink({href: '/category-a/doc1', label: 'Doc 1'}),
testLink({href: '/category-a/doc2', label: 'Doc 2'}),
// This link points to Category B's generated-index
testLink({href: '/category-b', label: 'Go to Category B'}),
],
});
const sidebar: PropSidebar = [categoryA, categoryB];
const mockUseCurrentSidebarCategory =
createUseCurrentSidebarCategoryMock(sidebar);
// When visiting /category-b, we should get Category B (the owner),
// not Category A (which just has a link to it)
expect(mockUseCurrentSidebarCategory('/category-b')).toEqual(categoryB);
});
});
describe('useCurrentSidebarSiblings', () => {

View File

@ -234,11 +234,40 @@ function getSidebarBreadcrumbs({
}): PropSidebarBreadcrumbsItem[] {
const breadcrumbs: PropSidebarBreadcrumbsItem[] = [];
function extract(items: PropSidebarItem[]) {
// When onlyCategories is true (e.g., for useCurrentSidebarCategory), we need
// to distinguish between:
// 1. A category that directly owns this URL (category.href === pathname)
// 2. A link that happens to point to this URL
//
// We should prefer (1) over (2) to fix the bug where a generated-index page
// shows items from the wrong category when another category has a link
// pointing to it. See https://github.com/facebook/docusaurus/issues/11612
//
// We use a two-pass approach:
// - First pass: Only look for categories that directly own the URL
// - Second pass: If not found, look for links (to support doc pages)
function extractCategoryOnly(items: PropSidebarItem[]): boolean {
for (const item of items) {
if (item.type === 'category') {
if (isSamePath(item.href, pathname)) {
breadcrumbs.unshift(item);
return true;
}
if (extractCategoryOnly(item.items)) {
breadcrumbs.unshift(item);
return true;
}
}
}
return false;
}
function extractWithLinks(items: PropSidebarItem[]): boolean {
for (const item of items) {
if (
(item.type === 'category' &&
(isSamePath(item.href, pathname) || extract(item.items))) ||
(isSamePath(item.href, pathname) || extractWithLinks(item.items))) ||
(item.type === 'link' && isSamePath(item.href, pathname))
) {
const filtered = onlyCategories && item.type !== 'category';
@ -248,11 +277,19 @@ function getSidebarBreadcrumbs({
return true;
}
}
return false;
}
extract(sidebarItems);
if (onlyCategories) {
// First try to find a category that directly owns this URL
if (!extractCategoryOnly(sidebarItems)) {
// Fall back to finding via links (for doc pages in a category)
extractWithLinks(sidebarItems);
}
} else {
// For breadcrumbs, use the original behavior (links included)
extractWithLinks(sidebarItems);
}
return breadcrumbs;
}