#708 Expand Sidebar Nav Items Into In-Page Section Links - #716
Conversation
DOM-discovered (option C): a section opts in with id + data-section-nav, and a client hook enumerates them under the active nav item, scroll-spies with an IntersectionObserver rooted on <main>, and syncs the URL hash via replaceState. Manage Positions is the only page opted in. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
b-at-neu
left a comment
There was a problem hiding this comment.
Code Review — Cycle 1 · needs revision
4 open — 4 🟡 Low (see inline)
Compress oversized comments to fit the one-line bar, drop the ticket provenance reference, and unify desktop/mobile section-link history behavior so back only needs one press per navigation. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Revision — Cycle 1fixed R1-L1, R1-L2, R1-L3, R1-L4 · b5d4e12 |
Human feedback — post-approval revisionSimplify the section-nav hash ids. Currently Rename the three section ids to the bare Update |
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Revision — human feedback (post-approval)Addressed #716 (comment): renamed the three Verification: prettier, eslint, tsc all clean; |
b-at-neu
left a comment
There was a problem hiding this comment.
Code Review — Cycle 3 · approved
1 open — 1 🟡 Low (see inline)
| {draft.length > 0 && ( | ||
| <PositionGroup | ||
| headingId="managed-positions-draft-heading" | ||
| sectionId="draft" |
There was a problem hiding this comment.
R3-L1 🟡 Low — this commit renamed the hash id from managed-positions-draft to draft, but the PR body's testing-plan step still says "Reload with #managed-positions-draft in the URL". Fix: update that step to #draft (and the sibling #open/#closed mentions if any).
Closes #708
Summary
id+data-section-nav="<Label>"on its outermost<section>, so the nav can never point at a group the page didn't actually render.useSectionNav) enumerates opted-in sections under<main>, scroll-spies with anIntersectionObserverrooted on<main>(not the window), and keeps the URL hash in sync viahistory.replaceState(neverrouter.replace)./manage/positionsis the only page opted in for this ticket;ManagedPositionsSection'sOpen/Closed/Draftgroups now carry the opt-in attributes.Changes
lib/types.ts—SectionNavItem,SectionVisibility.lib/section-nav.ts(new) — pure helpers (buildSectionNavItems,selectActiveSectionId) plus the selector/root-margin constants; no React/DOM import, sotests/unitcan exercise them directly.components/layouts/use-section-nav.ts(new) — client hook: DOM discovery,IntersectionObserverscroll-spy,MutationObserverfor streamed/late-appearing sections, hash sync, initial-hash scroll-on-load.components/layouts/section-nav-list.tsx(new) — the nested<ul>of anchors; handles the Sheet's close-then-scroll sequencing so the scroll never fights the exit animation.components/layouts/nav-list.tsx— calls the hook once, renders the nested list inside the active item's<li>only, adds the missingaria-current="page"on the nav<Link>.components/layouts/app-shell.tsx—id="main-content"andscroll-smooth motion-reduce:scroll-autoon<main>(the actual scroll container).components/features/managed-positions-section.tsx—PositionGroupgrowssectionIdand renders the opt-in attributes +scroll-mt-6.docs/DESIGN.md— the opt-in pattern documented under §5.tests/unit/section-nav.test.ts(new).Testing plan
/manage/positions— Open, Closed and Draft appear indented under the active Manage Positions item.prefers-reduced-motionin DevTools → Rendering — clicks jump instantly instead of smooth-scrolling.#managed-positions-draftin the URL — the page lands on Draft.<main>shifts when the sub-nav appears (hydration-only render, no layout shift).Automated checks
npm run prettier:checknpm run eslint:checknpm run tsc:checknpm run test:unit(401 passed;tests/unit/email-delivery-events.test.tsfails in this sandbox only becauseDATABASE_URLisn't set — pre-existing, reproduced identically on a clean checkout before this change, unrelated to this PR)Notes
/manage/positionsopts in./manage/applications/[id](needsSectionCardto grow anid) and the edit page (after Redesign The Edit Position Page #666 collapses its tabs) are follow-ups; the discovery helper already handles streamed/conditional sections for when those land.NavListinstances (desktop sidebar + mobile Sheet) run the hook simultaneously while the Sheet is open, so there can briefly be twoIntersectionObservers over the same elements — cheap, and not worth a client context provider under a server-rendered shell (per plan).scroll-smoothon<main>applies to every anchor/scrollIntoViewinside the app shell, not just this feature.