Refactor docs nav into tabbed sections#904
Conversation
✅ Deploy Preview for tanstack ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThis PR adds a tabbed docs navigation: canonical tab IDs and types, helpers to group and resolve tabs from menu items and pathnames, schema changes to accept optional ChangesTabbed Docs Navigation System
Sequence DiagramsequenceDiagram
participant User as User/Browser
participant Layout as DocsLayout
participant TabUtil as Tab Utilities
participant Menu as Menu Renderer
participant Sidebar as Sidebar Renderer
User->>Layout: Navigate to /docs/some/path
Layout->>TabUtil: getTabbedMenuConfig(menuConfig)
TabUtil->>Layout: tabbedMenuConfig
Layout->>TabUtil: getActiveDocsNavTabId({pathname, relativePathname, isExample})
TabUtil->>Layout: activeTabId
Layout->>Layout: visibleMenuConfig = tabbedMenuConfig[activeTabId] || menuConfig
Layout->>Menu: Render docsTabs (highlight activeTabId)
Layout->>Sidebar: Render sidebar from visibleMenuConfig
Menu->>User: Tab strip with active tab
Sidebar->>User: Filtered sidebar menu
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/utils/docsNavTabs.ts`:
- Around line 115-117: The current firstItem selection (variable firstItem using
groups.flatMap(group => group.children).find(child =>
!child.to.startsWith('http'))) can pick non-doc entries like ".."; change the
predicate to only select children whose to target is an internal docs path
(e.g., child.to.startsWith('/docs') or otherwise matches your docs root) and
still exclude external URLs, so update the find used to set firstItem to require
both not child.to.startsWith('http') and child.to.startsWith('/docs') (or the
project-specific docs prefix) so tabs always open into /docs.
- Around line 138-143: active-tab detection uses strict equality (child.to ===
relativePathname) which fails for absolute or prefixed internal paths (like
/$libraryId/$version/docs/..., ./framework, ../x). Update the matching logic in
the block that computes activeGroup and activeChild to normalize both strings
(e.g., remove leading slashes, resolve ./ and ../, and strip dynamic prefixes
like $libraryId/$version) and then compare in a tolerant way (for example
compare normalized values or check endsWith for the route suffix). Locate the
comparisons against menuConfig children (symbols: activeGroup, activeChild,
menuConfig, relativePathname, child.to) and replace the strict equality check
with a normalized/tolerant path comparison helper so special/absolute internal
paths correctly match their tab entries.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4ae98a77-2d6b-4d59-9366-bb82cc1c9701
📒 Files selected for processing (3)
src/components/DocsLayout.tsxsrc/utils/config.tssrc/utils/docsNavTabs.ts
| firstItem: groups | ||
| .flatMap((group) => group.children) | ||
| .find((child) => !child.to.startsWith('http')), |
There was a problem hiding this comment.
Avoid non-doc tab targets for firstItem.
On Line 115, selecting the first non-HTTP child can choose entries like .. (Home), which can send users out of /docs when they click a docs tab.
Proposed fix
+function isDocsTabTarget(to: string) {
+ if (to.startsWith('http')) return false
+ if (to === '..' || to === './framework') return false
+ if (to.startsWith('/')) return to.includes('/docs/')
+ return true
+}
+
export function getTabbedMenuConfig(menuConfig: MenuItem[]) {
return docsNavTabs
@@
...tab,
groups,
firstItem: groups
.flatMap((group) => group.children)
- .find((child) => !child.to.startsWith('http')),
+ .find((child) => isDocsTabTarget(child.to)),
}
})
.filter((tab) => tab.groups.length)
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/utils/docsNavTabs.ts` around lines 115 - 117, The current firstItem
selection (variable firstItem using groups.flatMap(group =>
group.children).find(child => !child.to.startsWith('http'))) can pick non-doc
entries like ".."; change the predicate to only select children whose to target
is an internal docs path (e.g., child.to.startsWith('/docs') or otherwise
matches your docs root) and still exclude external URLs, so update the find used
to set firstItem to require both not child.to.startsWith('http') and
child.to.startsWith('/docs') (or the project-specific docs prefix) so tabs
always open into /docs.
| const activeGroup = menuConfig.find((group) => | ||
| group.children.some((child) => child.to === relativePathname), | ||
| ) | ||
| const activeChild = activeGroup?.children.find( | ||
| (child) => child.to === relativePathname, | ||
| ) |
There was a problem hiding this comment.
Active-tab detection misses special/absolute internal paths.
On Line 139, strict child.to === relativePathname won’t match entries like /$libraryId/$version/docs/..., .., or ./framework, so tab resolution can fall back to defaults and filter the wrong groups.
Proposed fix
+function isChildPathMatch({
+ childTo,
+ pathname,
+ relativePathname,
+}: {
+ childTo: string
+ pathname: string
+ relativePathname: string
+}) {
+ if (childTo === relativePathname) return true
+
+ if (childTo === '..') {
+ return /^\/[^/]+\/[^/]+\/?$/.test(pathname)
+ }
+
+ if (childTo === './framework') {
+ return (
+ pathname.includes('/docs/framework') &&
+ !/\/docs\/framework\/[^/]+/.test(pathname)
+ )
+ }
+
+ if (childTo.includes('/$libraryId/$version/docs/')) {
+ const suffix = childTo.split('/docs/')[1]
+ return Boolean(suffix) && pathname.includes(`/docs/${suffix}`)
+ }
+
+ return false
+}
+
export function getActiveDocsNavTabId({
@@
- const activeGroup = menuConfig.find((group) =>
- group.children.some((child) => child.to === relativePathname),
- )
+ const activeGroup = menuConfig.find((group) =>
+ group.children.some((child) =>
+ isChildPathMatch({ childTo: child.to, pathname, relativePathname }),
+ ),
+ )
const activeChild = activeGroup?.children.find(
- (child) => child.to === relativePathname,
+ (child) =>
+ isChildPathMatch({ childTo: child.to, pathname, relativePathname }),
)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const activeGroup = menuConfig.find((group) => | |
| group.children.some((child) => child.to === relativePathname), | |
| ) | |
| const activeChild = activeGroup?.children.find( | |
| (child) => child.to === relativePathname, | |
| ) | |
| function isChildPathMatch({ | |
| childTo, | |
| pathname, | |
| relativePathname, | |
| }: { | |
| childTo: string | |
| pathname: string | |
| relativePathname: string | |
| }) { | |
| if (childTo === relativePathname) return true | |
| if (childTo === '..') { | |
| return /^\/[^/]+\/[^/]+\/?$/.test(pathname) | |
| } | |
| if (childTo === './framework') { | |
| return ( | |
| pathname.includes('/docs/framework') && | |
| !/\/docs\/framework\/[^/]+/.test(pathname) | |
| ) | |
| } | |
| if (childTo.includes('/$libraryId/$version/docs/')) { | |
| const suffix = childTo.split('/docs/')[1] | |
| return Boolean(suffix) && pathname.includes(`/docs/${suffix}`) | |
| } | |
| return false | |
| } | |
| export function getActiveDocsNavTabId({ | |
| const activeGroup = menuConfig.find((group) => | |
| group.children.some((child) => | |
| isChildPathMatch({ childTo: child.to, pathname, relativePathname }), | |
| ), | |
| ) | |
| const activeChild = activeGroup?.children.find( | |
| (child) => | |
| isChildPathMatch({ childTo: child.to, pathname, relativePathname }), | |
| ) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/utils/docsNavTabs.ts` around lines 138 - 143, active-tab detection uses
strict equality (child.to === relativePathname) which fails for absolute or
prefixed internal paths (like /$libraryId/$version/docs/..., ./framework, ../x).
Update the matching logic in the block that computes activeGroup and activeChild
to normalize both strings (e.g., remove leading slashes, resolve ./ and ../, and
strip dynamic prefixes like $libraryId/$version) and then compare in a tolerant
way (for example compare normalized values or check endsWith for the route
suffix). Locate the comparisons against menuConfig children (symbols:
activeGroup, activeChild, menuConfig, relativePathname, child.to) and replace
the strict equality check with a normalized/tolerant path comparison helper so
special/absolute internal paths correctly match their tab entries.

Add docs-level navigation tabs for getting started, tutorials, guides, API, examples, and community links. Extract tab grouping logic out of
DocsLayoutand add optional config metadata for explicit tab assignment while preserving fallback classification for existing docs configs.Summary by CodeRabbit
New Features
Style