From decb7712e0101b8a5ee3aec4a179fd162669dc41 Mon Sep 17 00:00:00 2001 From: Sarah Gerrard <98355961+LadyBluenotes@users.noreply.github.com> Date: Tue, 17 Mar 2026 12:08:20 -0700 Subject: [PATCH] fix docs navigation stability and search filter interactions --- src/components/CopyPageDropdown.tsx | 5 +- src/components/DocsLayout.tsx | 97 +++++++++++++++++++----- src/components/SearchModal.tsx | 18 +++-- src/routes/$libraryId/$version.docs.tsx | 21 ++++- src/routes/$libraryId/$version.index.tsx | 15 +++- src/routes/$libraryId/$version.tsx | 28 +++++-- src/routes/admin/audit.tsx | 1 + src/routes/admin/github-stats.tsx | 1 + src/routes/admin/logins.tsx | 1 + src/routes/admin/npm-stats.tsx | 1 + src/routes/admin/roles.$roleId.tsx | 1 + 11 files changed, 147 insertions(+), 42 deletions(-) diff --git a/src/components/CopyPageDropdown.tsx b/src/components/CopyPageDropdown.tsx index b9e5e0c4..8f4cdf73 100644 --- a/src/components/CopyPageDropdown.tsx +++ b/src/components/CopyPageDropdown.tsx @@ -10,10 +10,7 @@ import { DropdownContent, DropdownItem, } from './Dropdown' -import { - getPackageManager, - PACKAGE_MANAGERS, -} from '~/utils/markdown/installCommand' +import { getPackageManager } from '~/utils/markdown/installCommand' // Markdown icon component matching the screenshot function MarkdownIcon({ className }: { className?: string }) { diff --git a/src/components/DocsLayout.tsx b/src/components/DocsLayout.tsx index c94b8ae7..2bf17a34 100644 --- a/src/components/DocsLayout.tsx +++ b/src/components/DocsLayout.tsx @@ -534,28 +534,59 @@ export function DocsLayout({ d.status === 'active' && d.name !== 'Nozzle.io' && d.id !== 'fireship', ) - const menuItems = menuConfig.map((group, i) => { - const WrapperComp = group.collapsible ? 'details' : 'div' - const LabelComp = group.collapsible ? 'summary' : 'div' + const groupInitialOpenState = React.useMemo(() => { + return menuConfig.reduce>((acc, group, index) => { + const isChildActive = group.children.some((child) => child.to === _splat) + const key = `${index}:${String(group.label)}` - const isChildActive = group.children.some((d) => d.to === _splat) - const configGroupOpenState = - typeof group.defaultCollapsed !== 'undefined' - ? !group.defaultCollapsed // defaultCollapsed is true means the group is closed - : undefined - const isOpen = isChildActive ? true : (configGroupOpenState ?? false) + acc[key] = isChildActive + ? true + : typeof group.defaultCollapsed !== 'undefined' + ? !group.defaultCollapsed + : false - const detailsProps = group.collapsible ? { open: isOpen } : {} + return acc + }, {}) + }, [menuConfig, _splat]) - return ( - - - {group?.label} - + const [openGroups, setOpenGroups] = React.useState(groupInitialOpenState) + + React.useEffect(() => { + setOpenGroups((prev) => { + let hasChanged = false + const next = { ...prev } + + Object.entries(groupInitialOpenState).forEach(([key, isOpen]) => { + if (!(key in next)) { + next[key] = isOpen + hasChanged = true + return + } + + if (isOpen && !next[key]) { + next[key] = true + hasChanged = true + } + }) + + return hasChanged ? next : prev + }) + }, [groupInitialOpenState]) + + const menuItems = menuConfig.map((group, i) => { + const groupKey = `${i}:${String(group.label)}` + + const groupContent = ( + <> + {group.collapsible ? ( + + {group.label} + + ) : ( +
+ {group.label} +
+ )}
    {group?.children?.map((child, i) => { @@ -580,6 +611,7 @@ export function DocsLayout({ onClick={() => { detailsRef.current.removeAttribute('open') }} + preload={false} activeOptions={{ exact: true, includeHash: false, @@ -614,7 +646,32 @@ export function DocsLayout({ ) })}
- + + ) + + return group.collapsible ? ( +
{ + const nextOpen = event.currentTarget.open + setOpenGroups((prev) => + prev[groupKey] === nextOpen + ? prev + : { ...prev, [groupKey]: nextOpen }, + ) + }} + > + {groupContent} +
+ ) : ( +
+ {groupContent} +
) }) diff --git a/src/components/SearchModal.tsx b/src/components/SearchModal.tsx index eb00b314..1c7c7130 100644 --- a/src/components/SearchModal.tsx +++ b/src/components/SearchModal.tsx @@ -565,9 +565,12 @@ function LibraryRefinement() { const currentLibrary = libraries.find((l) => l.id === selectedLibrary) return ( - - -