refactor(web): extract ToggleSettingCard from three settings toggles - #1075
Merged
Conversation
…(#tech-debt) apps/web/src/components/app/chat-surface-settings.tsx, cross-repo-messaging-settings.tsx and injection-hold-settings.tsx each hand-rolled the same JSX card shell: a p-6 wrapper, an uppercase eyebrow, a max-w-2xl description paragraph, a bordered checkbox row with a title/hint pair, and a trailing role="alert" error paragraph. Only the copy and the testid differed. PR #1020 extracted the shared *hook* (useOptimisticToggleSetting) but left the shell, and the chat surface then added a fourth copy. The new ToggleSettingCard is presentational only — it does not wrap a hook, because chat-surface-settings deliberately uses a different one (useChatSurfaceSetting, React Query-backed so the tab bar re-labels without a reload). Markup, class names and testids are byte-identical to what the three files rendered before. Colocated tests cover the shell directly, since settings-pane.test.tsx stubs all three modules. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
apps/web/src/components/app/chat-surface-settings.tsx,cross-repo-messaging-settings.tsxandinjection-hold-settings.tsxeach hand-rolled the same JSX card shell. New presentationalToggleSettingCard(apps/web/src/components/app/toggle-setting-card.tsx) now owns it; the three files pass copy + state in.Why this is debt
PR #1020 extracted the shared hook (
useOptimisticToggleSetting) but left the shell duplicated across the three panels, and the chat surface then added a fourth copy — exactly the "muscle memory when writing the next one" regression the tech-debt backlog predicts. Found by the 2026-09-05 broad audit (#1060): 6 duplicated 7-line windows each way.What the included set has in common — precisely
All three rendered a byte-identical shell, differing only in the eyebrow text, description copy, title, hint and
data-testid:<div className="p-6">wrapper<div className="mb-1.5 text-[10px] uppercase tracking-widest text-muted-foreground"><p className="mb-3 max-w-2xl text-sm text-muted-foreground">description<div className="max-w-lg">→<label className="flex cursor-pointer items-center gap-3 rounded border border-border px-3 py-2.5 transition-colors hover:bg-muted/50">wrapping a<Checkbox checked onCheckedChange={(c) => setEnabled(c === true)} data-testid=…>plus amin-w-0title/hint pair{error ? <p role="alert" className="mt-3 text-sm text-destructive">Class names, DOM shape and testids are unchanged — the diff is a pure move.
errorisstringon both hooks (useOptimisticToggleSettinganduseChatSurfaceSetting), so the empty-string-means-no-error convention carried over verbatim.The card is presentational only — it does not wrap a hook, because
chat-surface-settings.tsxdeliberately uses a different one (useChatSurfaceSetting, React Query-backed so the tab bar re-labels without a reload) while the other two useuseOptimisticToggleSetting.Deliberate exclusions (near-misses left alone)
launch-guidance-settings.tsx— shares thep-6wrapper and error paragraph but its control row is materially different:<label className="flex w-fit … text-sm text-foreground">with the text inline, no card border, nomax-w-lg, no description block, anddisabled={!loaded}on the checkbox (deliberate — an unread value must not render as a confirmed "off"). Folding it in would mean adding adisabledprop plus a variant for the borderless row, i.e. designing for one caller.slack-notifications-section.tsx,ide-settings.tsx,notification-device-sections.tsx,browser-notifications-section.tsxshare the bordered<label>row but not the card: they render rows inside.map()lists or conditional blocks, some with no hint line, some with their owndisabledlogic, and none with the eyebrow/description/error shell. See the question below.Smaller-blast-radius note
The backlog's recommended prop list included
disabled?; I dropped it because none of the three included callers pass one — adding it would have been speculative surface for a component with three consumers.Validation
pnpm run check— cleanpnpm run finalize:web— build cleanpnpm run test:e2e— 198 passed / 12 skipped (includessettings.spec.ts"cross-repo messaging toggle defaults off and persists to the server")cd apps/web && pnpm vitest run— 125 files, 1841 tests passed<strong>runs in the chat description), and flipping the cross-repo toggle round-trips toGET /api/v1/app/settings/cross-repo-messaging→{"enabled":true}.Non-vacuous test proof.
settings-pane.test.tsxstubs all three modules, so the new colocatedtoggle-setting-card.test.tsxis the only real coverage. Defect probes, each reverted after:next !== true)<div>next as boolean)"indeterminate"from a click, so the coercion is unobservable at this seam. Recording it rather than writing a test that would only assert an unreachable branch.Next run
Queued: feedback-item action handlers duplicated between
diff-feedback-annotation.tsx:92-117andreviews-feedback-item.tsx:108-131(16 duplicated 7-line windows).🤖 Generated with Claude Code