refactor(app): decompose SpecTabs into feature folder#8564
Conversation
- Split the 961-line SpecTabs.tsx monolith into src/sections/spec-detail/SpecTabs/ with index.tsx (tabs orchestration, tag-count state, tag chip rendering), CodeTab.tsx, SpecTab.tsx, ImplTab.tsx, QualityTab.tsx, and utils.tsx - utils.tsx keeps the module-level tag-count cache (via getter/setter to preserve its once-per-session lifetime), Md helpers, parseInlineCode, TabPanel, formatDate, and the tag→URL-param maps - QualityTab expansion state stays lifted in index.tsx (as before) and is passed down as expandedCategories/onToggleCategory props; copy state moved with the copy button into CodeTab - No behavior change: all trackEvent calls, aria attributes, and interactions preserved verbatim; existing SpecTabs.test.tsx moved into the folder unchanged - Add focused per-tab tests for the now prop-driven surfaces (QualityTab expansion contract, CodeTab copy/tracking, ImplTab date fallback, SpecTab inline-code rendering): 60→64 test files, 552→572 tests Part 9 of the frontend modernization roadmap. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Pull request overview
Refactors the Spec detail UI by decomposing the previously large SpecTabs.tsx component into a dedicated feature folder (app/src/sections/spec-detail/SpecTabs/) while keeping the public import surface (src/sections/spec-detail/SpecTabs) intact for the rest of the app.
Changes:
- Splits
SpecTabsinto smaller tab-focused components (CodeTab,SpecTab,ImplTab,QualityTab) plus shared presentation/helpers (md.tsx,utils.tsx). - Moves/expands frontend tests to cover the decomposed components and keeps the integration-style
SpecTabs.test.tsxin the new location. - Removes the old monolithic
app/src/sections/spec-detail/SpecTabs.tsx.
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| app/src/sections/spec-detail/SpecTabs/index.tsx | New SpecTabs orchestrator (tab state, tag rendering, tag count fetch/cache). |
| app/src/sections/spec-detail/SpecTabs/CodeTab.tsx | Extracted code display + clipboard copy behavior. |
| app/src/sections/spec-detail/SpecTabs/SpecTab.tsx | Extracted specification content rendering. |
| app/src/sections/spec-detail/SpecTabs/ImplTab.tsx | Extracted implementation review content + metadata line. |
| app/src/sections/spec-detail/SpecTabs/QualityTab.tsx | Extracted quality score + criteria breakdown UI. |
| app/src/sections/spec-detail/SpecTabs/md.tsx | Shared presentational components (TabPanel, headings, list items). |
| app/src/sections/spec-detail/SpecTabs/utils.tsx | Shared helpers + module-level tag-count cache getter/setter. |
| app/src/sections/spec-detail/SpecTabs/SpecTabs.test.tsx | Moved SpecTabs tests to new folder location. |
| app/src/sections/spec-detail/SpecTabs/CodeTab.test.tsx | Added unit tests for CodeTab. |
| app/src/sections/spec-detail/SpecTabs/SpecTab.test.tsx | Added unit tests for SpecTab. |
| app/src/sections/spec-detail/SpecTabs/ImplTab.test.tsx | Added unit tests for ImplTab. |
| app/src/sections/spec-detail/SpecTabs/QualityTab.test.tsx | Added unit tests for QualityTab. |
| app/src/sections/spec-detail/SpecTabs.tsx | Deleted monolithic implementation (replaced by feature folder). |
| )} | ||
|
|
||
| {/* No data message */} | ||
| {!qualityScore && (!criteriaChecklist || Object.keys(criteriaChecklist).length === 0) && ( |
There was a problem hiding this comment.
Applied — the no-data guard checks qualityScore === null now.
| export function TabPanel({ children, value, index }: TabPanelProps) { | ||
| const isOpen = value === index; | ||
| return ( | ||
| <Collapse in={isOpen}> |
There was a problem hiding this comment.
Skipping intentionally: always-mounted Collapse children is the original SpecTabs behavior moved verbatim — CodeTab's copied state lifetime and the lazy CodeHighlighter prefetch both rely on it. Changing mount semantics is a behavior change out of scope for this decomposition PR.
| <Box | ||
| onClick={() => items.length > 0 && onToggleCategory(category)} | ||
| sx={{ | ||
| display: 'flex', | ||
| alignItems: 'center', | ||
| justifyContent: 'space-between', | ||
| mb: 0.5, | ||
| cursor: items.length > 0 ? 'pointer' : 'default', | ||
| '&:hover': items.length > 0 ? { opacity: 0.8 } : {}, | ||
| }} | ||
| > |
There was a problem hiding this comment.
Applied — header is a ButtonBase with aria-expanded; item-less categories are disabled. (Pre-existing issue, fixed here as a separate labeled commit.)
… criteria headers Copilot review findings on the SpecTabs decomposition (pre-existing behavior moved verbatim, fixed in a separate commit to keep the decomposition pure): - qualityScore truthiness misclassified a legitimate score of 0 as missing (N/A display, no-data guard, Quality tab label) — explicit null checks now - QualityTab category headers were clickable plain divs; now MUI ButtonBase with aria-expanded and disabled state for item-less categories Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| overviewMode = false, | ||
| highlightedTags = [], | ||
| }: SpecTabsProps) { | ||
| // In overview mode, start with Spec tab open; in detail mode, all collapsed |
…tion (#8596) ## Summary Part 11 (final) of the frontend modernization roadmap. - **`app/ARCHITECTURE.md`**: directory layout (routes/layouts/pages/sections/components/hooks/lib/theme), conventions (`src/` alias imports, `paths.*` URL registry, `lib/api` client, theme tokens, feature folders), data flow, testing pattern, quality gates, and how-to-add recipes for pages/sections/hooks/endpoints - **`agentic/docs/project-guide.md`**: frontend section now lists the full gate (`lint`, `fm:check`, `type-check` incl. tests, `test`), mentions the dev-time checker overlay, and links the new architecture doc With this, the roadmap is complete: tooling baseline (#8519), src/ aliases (#8520), theme split (#8525), global-config (#8529), API client (#8531), routes registry (#8546), structure split (#8547), FilterBar (#8559), SpecTabs (#8564), MapPage hook (#8581). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Summary
Part 9 of the frontend modernization roadmap. Pure decomposition of the 961-line
SpecTabs.tsxintosections/spec-detail/SpecTabs/— no behavior change, import path (and SpecPage's lazy usage) unchanged.index.tsx(420): tab orchestration — tab/tag-count state, fetch, tag chips, handleTagClickCodeTab.tsx(88): code display + copy-to-clipboard (ownscopiedstate; tracking + aria preserved verbatim)SpecTab.tsx(89),ImplTab.tsx(139),QualityTab.tsx(229 — expansion state stays lifted in the parent, as before)md.tsx: presentational markdown components (TabPanel, MdHeading, MdListItem) — components-only so react-refresh stays warning-freeutils.tsx: pure helpers (parseInlineCode, formatDate, tag param maps) + the module-level tag-count cache behind getter/setter (lifetime semantics preserved)SpecTabs.test.tsxmoved unchanged (git rename, 22 tests); +4 per-tab test filesTests: 552 → 572 (+20).
Verification
yarn lint✓ (no new warnings) ·yarn fm:check✓ ·yarn type-check✓ ·yarn test572/572 ✓ ·yarn build✓🤖 Generated with Claude Code