Repository navigation
refactor(frontend): move mobile nav and header projection to a module - #1340
Conversation
…ader projection app.ts is more than twice the 500-line ceiling in CLAUDE.md, and the mobile drawer plus the topbar-to-drawer projection is its largest self-contained chunk. This commit copies that code verbatim into its own bounded-context module, frontend/src/nav/mobile-nav.ts; the next commit switches app.ts and the tests over and deletes the original, so the move stays reviewable as two small diffs. The only edit is exporting MOBILE_NAV_QUERY so the breakpoint is part of the module's public surface. Refs LeanerCloud#215 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… copy Removes setupMobileNav, syncHeaderPlacement and their constants from app.ts (1052 -> 849 lines) and imports setupMobileNav from the new module instead. The jest suite now imports from the module directly, and the comments in index.html and responsive.css that pointed at app.ts name the new home. No behavior change: the code is the same, it is only called from a new file. Refs LeanerCloud#215 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Issue LeanerCloud#215 asks for the extracted module to expose a typed API rather than loose functions. setupMobileNav now returns a MobileNavHandle with open(), close() and isOpen(), backed by the same openDrawer/closeDrawer transitions the hamburger, overlay and Escape key use, or null when the page has no drawer to wire. app.ts ignores the return value, so startup behavior is unchanged. Adds jest coverage for the null case and for the handle driving the same aria-expanded, inert and focus changes as the hamburger. Refs LeanerCloud#215 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reached
This review includes 5 billable files and costs up to $1.25. Or wait 59 minutes for your next included review. View limit details
Comment |
Conflicts:
- frontend/src/app.ts: import block. Kept both this branch's
`import { setupMobileNav } from './nav/mobile-nav'` and main's
`import { renderInitError } from './init-error'` (LeanerCloud#1338). No main commit
touched the moved mobile-nav code, so nav/mobile-nav.ts needs no re-apply.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts: - frontend/src/app.ts: import block only. This branch imports setupMobileNav from ./nav/mobile-nav; main (LeanerCloud#190, purchasing section links) added initPurchasingNav from ./purchasing-nav at the same spot. Kept both imports. Main's other app.ts changes (initPurchasingNav() call, LeanerCloud#331 close-button cleanup) merged cleanly and are untouched by the mobile-nav extraction. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Independent review (IAmJSD's agent, fresh context, given only the diff, the issue and the PR text): APPROVE Blocking:
|
|
Local verification at
|
|
Marked ready and merged by IAmJSD's agent, at IAmJSD's request (IAmJSD reviewed the before/after screenshots; cristim is traveling), after the independent review and local verification above, all at |
Conflicts: - frontend/src/app.ts: main (LeanerCloud#1340) moved setupMobileNav and syncHeaderPlacement into frontend/src/nav/mobile-nav.ts. Kept both imports (wireTablistKeyboard from this branch, setupMobileNav from main) and took main's removal of the inline drawer code. - frontend/src/nav/mobile-nav.ts (merged cleanly, edited by hand): ported this branch's drawer change into the moved module. closeDrawer takes returnFocus 'always' | 'if-inside', and a sidebar link click closes with 'if-inside' so focus stays on the new page heading. The MobileNavHandle.close() main added wraps closeDrawer() so it keeps its always-return-focus behavior. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts: - frontend/src/app.ts: import block. Kept this branch's setOpenAccountsHandler import (first-run empty state, LeanerCloud#756) and main's setupMobileNav import from nav/mobile-nav (LeanerCloud#1340). The rest of app.ts merged cleanly; main's removal of the inline drawer code stands and the setOpenAccountsHandler wiring is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts: - frontend/src/app.ts: import block. Kept this branch's fanout-retry import (showFanOutRetry, fanOutRetryLabel, FanOutBucketOutcome) and main's setupMobileNav import from nav/mobile-nav (LeanerCloud#1340). handleFanOutExecute merged cleanly: main's combined bulk-approval totals and upfront-naming confirm label (LeanerCloud#1362) now sit alongside this branch's retry-label restore in the finally block. A retry passes only the narrowed failed buckets, so the confirm totals cover just the requests being resent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
frontend/src/app.tswas 1052 lines, more than twice the 500-line ceiling inCLAUDE.md, and the mobile drawer plus the topbar-to-drawer projection was its largest self-contained chunk. This PR moves that code verbatim intofrontend/src/nav/mobile-nav.tsand gives it a small typed API. There is no user-visible change.Refs #215
Partial:
app.tsis now 850 lines, still over 500. The acceptance criteria allow that if the remaining excess becomes its own follow-up; the next extraction target is named under "Notes for review" but no follow-up issue is filed yet.What changed
dd32b27d: copiesMOBILE_NAV_QUERY,DRAWER_PROJECTED_IDS,syncHeaderPlacementandsetupMobileNav(with its inneropenDrawer/closeDrawer/applyBreakpointStatetransitions) verbatim into the newfrontend/src/nav/mobile-nav.ts. The only edit is exportingMOBILE_NAV_QUERY.9c33555e:app.tsimportssetupMobileNavfrom the module and drops the inline copy (1052 to 850 lines).__tests__/mobile-nav.test.tsimports from the module, and the comments inindex.html(the#sidebar-extrasslot) andstyles/responsive.cssthat namedapp.tsnow namenav/mobile-nav.ts.d6e7fded:setupMobileNav()returns aMobileNavHandle(open(),close(),isOpen()) backed by the same transitions the hamburger, overlay and Escape key use, ornullwhen the page has no drawer.app.tsignores the return value, so startup behavior is unchanged. Two new jest tests cover it.Why this approach
It follows the extraction target the issue proposes. Returning a small handle meets the "typed API" ask without changing startup behavior; a class or barrel object would have meant rewriting the code instead of moving it. The work is split into copy, switch and handle commits so each stays under 400 lines and the move can be checked line by line.
Screenshots
No visible change: this is a pure refactor.
Verification
Run on this branch's head
d6e7fdedon top of maina14dc8f2:mobile-nav.test.tsre-run with--randomize: 24/24, including 2 new tests (setupMobileNavreturnsnullwith nothing to wire; the handle'sopen/closedrive the samearia-expanded,inertand focus changes as the hamburger).e2e-local.sh: 157/157, including the unchangedmobile-header-drawer.spec.ts.scripts/test-git-secrets-allowlist.sh: 137 pass, 0 fail.Notes for review
app.tsis still 850 lines. The obvious next target is the purchase-execution flow (handleExecutePurchase, the fan-out bucket submit anddescribeDuplicateSubmit, about 450 lines), which could move to something likefrontend/src/purchase/execute.ts;setupFeedbackLink(about 65 lines) could also move out.MobileNavHandleis new API surface that nothing uses yet. IfsetupMobileNavshould keep returningvoid, the third commit can be dropped and the first two still stand.Overlaps
setupMobileNavinsideapp.ts, so it will conflict: its hunks have to be re-applied tonav/mobile-nav.ts.__tests__/mobile-nav.test.ts, a small conflict next to the re-pointed import.import { closeModal } from './modal';inapp.ts: a trivial import-block conflict.Those PRs will be opened alongside this one and merged in sequence, with conflicts resolved on the later branch.
Note for the UI owner
This changes UI code that @cristim owns. IAmJSD reviewed the before/after screenshots and asked for it to be merged; any design point you disagree with can be a follow-up.
🤖 Generated with Claude Code