diff --git a/packages/docusaurus-theme-classic/src/theme/DocSidebarItem/Category/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocSidebarItem/Category/index.tsx
index 9a49be27d1..8f6a2a84a7 100644
--- a/packages/docusaurus-theme-classic/src/theme/DocSidebarItem/Category/index.tsx
+++ b/packages/docusaurus-theme-classic/src/theme/DocSidebarItem/Category/index.tsx
@@ -24,13 +24,19 @@ import {
isActiveSidebarItem,
findFirstSidebarItemLink,
useDocSidebarItemsExpandedState,
+ useVisibleSidebarItems,
} from '@docusaurus/plugin-content-docs/client';
import Link from '@docusaurus/Link';
import {translate} from '@docusaurus/Translate';
import useIsBrowser from '@docusaurus/useIsBrowser';
import DocSidebarItems from '@theme/DocSidebarItems';
+import DocSidebarItemLink from '@theme/DocSidebarItem/Link';
import type {Props} from '@theme/DocSidebarItem/Category';
+import type {
+ PropSidebarItemCategory,
+ PropSidebarItemLink,
+} from '@docusaurus/plugin-content-docs';
import styles from './styles.module.css';
// If we navigate to a category and it becomes active, it should automatically
@@ -136,7 +142,48 @@ function CategoryLinkLabel({label}: {label: string}) {
);
}
-export default function DocSidebarItemCategory({
+export default function DocSidebarItemCategory(props: Props): ReactNode {
+ const visibleChildren = useVisibleSidebarItems(
+ props.item.items,
+ props.activePath,
+ );
+ if (visibleChildren.length === 0) {
+ return ;
+ } else {
+ return ;
+ }
+}
+
+function isCategoryWithHref(
+ category: PropSidebarItemCategory,
+): category is PropSidebarItemCategory & {href: string} {
+ return typeof category.href === 'string';
+}
+
+// If a category doesn't have any visible children, we render it as a link
+function DocSidebarItemCategoryEmpty({item, ...props}: Props): ReactNode {
+ // If the category has no link, we don't render anything
+ // It's not super useful to render a category you can't open nor click
+ if (!isCategoryWithHref(item)) {
+ return null;
+ }
+ // We remove props that don't make sense for a link and forward the rest
+ const {
+ type,
+ collapsed,
+ collapsible,
+ items,
+ linkUnlisted,
+ ...forwardableProps
+ } = item;
+ const linkItem: PropSidebarItemLink = {
+ type: 'link',
+ ...forwardableProps,
+ };
+ return ;
+}
+
+function DocSidebarItemCategoryCollapsible({
item,
onItemClick,
activePath,
diff --git a/project-words.txt b/project-words.txt
index 14a45f9141..e377036d12 100644
--- a/project-words.txt
+++ b/project-words.txt
@@ -85,6 +85,7 @@ Fienny
flac
Flightcontrol
Flightcontrol's
+forwardable
FOUC
Français
froms
diff --git a/website/_dogfooding/_docs tests/tests/visibility/unlisted-except-index/index.mdx b/website/_dogfooding/_docs tests/tests/visibility/unlisted-except-index/index.mdx
new file mode 100644
index 0000000000..84cb0f1feb
--- /dev/null
+++ b/website/_dogfooding/_docs tests/tests/visibility/unlisted-except-index/index.mdx
@@ -0,0 +1,7 @@
+# Unlisted Except Index
+
+This index page is listed, but the other pages are unlisted.
+
+import DocCardList from '@theme/DocCardList';
+
+
diff --git a/website/_dogfooding/_docs tests/tests/visibility/unlisted-except-index/unlisted1.mdx b/website/_dogfooding/_docs tests/tests/visibility/unlisted-except-index/unlisted1.mdx
new file mode 100644
index 0000000000..e6ef3a75fe
--- /dev/null
+++ b/website/_dogfooding/_docs tests/tests/visibility/unlisted-except-index/unlisted1.mdx
@@ -0,0 +1,7 @@
+---
+unlisted: true
+---
+
+# Unlisted 1
+
+This page is unlisted.
diff --git a/website/_dogfooding/_docs tests/tests/visibility/unlisted-except-index/unlisted2.mdx b/website/_dogfooding/_docs tests/tests/visibility/unlisted-except-index/unlisted2.mdx
new file mode 100644
index 0000000000..78a643b1c8
--- /dev/null
+++ b/website/_dogfooding/_docs tests/tests/visibility/unlisted-except-index/unlisted2.mdx
@@ -0,0 +1,7 @@
+---
+unlisted: true
+---
+
+# Unlisted 2
+
+This page is unlisted.