Adopt platform-bible-react components in place of raw HTML (Tier 1) - #168
Conversation
|
Warning Review limit reached
Next review available in: 3 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughNative buttons and modal form elements are migrated to ChangesPlatform component migration
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related issues
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 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 |
alex-rawlings-yyc
left a comment
There was a problem hiding this comment.
Everything looks good except that some button sizes have changed. Here's how to bring them back:
Icon buttons render oversized after the <button> → Button migration
The icons inside the migrated Buttons are being resized by the platform component, so several icon buttons render noticeably larger than the raw <button>s they replaced.
Root cause. buttonVariants' base class string (paranext-core/lib/platform-bible-react/src/components/shadcn-ui/button.tsx:16) ends with:
tw:[&_svg:not([class*=size-])]:size-4
That's a descendant rule the platform applies to any child SVG whose class attribute doesn't contain the substring size-. It's how shadcn gives icon buttons a consistent icon size, and each size variant overrides it (xs/icon-xs → size-3, sm → size-3.5).
Our icons are sized with tw:h-3 tw:w-3. That string contains no size-, so the :not() guard matches and the platform rule applies. It also wins on specificity — the compiled selector is .tw\:[&_svg...]\:size-4 svg:not([class*=size-]) at (0,2,1), versus .tw\:h-3 at (0,1,0). Our width/height silently lose.
Fix. Size the icons with the size-* shorthand instead of separate h-*/w-*, which opts them out of the platform's guard:
-<Link2 className="tw:h-3 tw:w-3" />
+<Link2 className="tw:size-3" />Eight sites across five files:
| File | Line | Icon |
|---|---|---|
TokenLinkIcon.tsx |
264 | Unlink2 → tw:size-3 |
TokenLinkIcon.tsx |
333 | Link2 → tw:size-3 |
PhraseStripParts.tsx |
166 | Merge → tw:size-3 |
PhraseStripParts.tsx |
234 | Split → tw:size-3 |
PhraseBox.tsx |
375 | Trash2 → tw:size-3 |
SegmentListView.tsx |
85 | Merge → tw:size-3 tw:rotate-90 |
SegmentListView.tsx |
389 | LocateFixed → tw:size-4 |
ViewOptionsDropdown.tsx |
185 | Settings → tw:size-4 |
Note the last three are the same latent bug even where the button already passes a size variant (icon-xs/icon-sm) — the variant just swaps which forced size wins, so the icon's own h-*/w-* is still being ignored.
Worth knowing for the rest of the migration: any lucide-react icon placed inside a platform Button must use size-*, never h-*/w-*, or the platform silently overrides it. Might be worth a line in AGENTS.md next to the existing component-preference note.
Also flagging: the Jest mock (__mocks__/platform-bible-react.tsx) renders Button as a plain <button> and explicitly ignores variant and size. That's reasonable for behavioral tests, but it means the suite can't catch sizing regressions of this kind — worth knowing before relying on green tests for the remaining tiers.
@alex-rawlings-yyc reviewed 16 files and all commit messages, and made 1 comment.
Reviewable status:complete! all files reviewed, all discussions resolved (waiting on imnasnainaec).
Swap raw <button>/<input>/<textarea>/<label> for Button/Input/Textarea/Label across the confirm-bar controls, the morpheme editor, icon buttons, and the Create/Save As/Project Metadata modals; delete the now-duplicated pill-btn-*, modal-form-*, and icon-button utilities; extend the jest mock (Input/Textarea plus the extra Button props the icon buttons rely on); and note the component preference in AGENTS.md. Tier 2/3 (gloss inputs, WipeModal radios, ViewOptionsDropdown Popover, the combobox) are deferred. lint clean; 1578 tests pass; coverage 100%. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The deleted modal-form-label utility supplied mb-1 between each label and its field; the migrated Label component has no default margin, so CreateProjectModal and SaveAsProjectModal fields lost their gap.
Icons inside migrated Button components were losing their h-*/w-* sizing to buttonVariants' `[&_svg:not([class*=size-])]:size-4` rule, which wins on specificity. Switching to the size-* shorthand opts them out of that guard. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
4405f68 to
e82324d
Compare
|
Alt-hold grows rows by ~16px in segview Holding Alt in SegmentView (contview off) expands the rows to make room for the split markers. The strip shouldn't reflow just from revealing them. Cause. The pattern to follow is already right above it. The link-icon row (~line 370) never unmounts — it stays in flow at Suggested fix — keep the wrapper unconditional, gate only the marker inside it: const splittable = altHeld && !straddledBoundaryRefs.has(nextTokenRef);
return (
<span className="tw:inline-flex tw:min-h-4 tw:items-center">
{splittable && (
<SplitMarker
label={boundarySplitLabel}
onSplit={() => { /* unchanged */ }}
/>
)}
</span>
);The earlier Also fixes a non-Alt case: a slot suppressed by the not-mid-phrase guard collapses today as well, so rows can differ in height from one another even with Alt held down. The same change stabilizes those. The Verified locally: 1578 tests pass, 100% coverage on this file, lint clean. Note this predates this PR — it came in with the split-UI work, not the |
alex-rawlings-yyc
left a comment
There was a problem hiding this comment.
@alex-rawlings-yyc reviewed 4 files and all commit messages.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on imnasnainaec).
alex-rawlings-yyc
left a comment
There was a problem hiding this comment.
@imnasnainaec I threw in one more nit that can be ignored and deferred to a new issue if we just want to merge this in.
@alex-rawlings-yyc made 1 comment.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on imnasnainaec).
The Button mock now throws if a descendant SVG uses h-*/w-* without size-, since buttonVariants silently overrides that sizing in production and jsdom can't reproduce the visual regression otherwise. Turning that check on caught three more instances the review's site list missed (ArcOverlay's Link2Off, TokenChip's X and Plus) plus the pre-existing ViewOptionsDropdown Settings icon; all four are fixed the same way as the earlier batch. AGENTS.md documents the size-* rule. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
alex-rawlings-yyc
left a comment
There was a problem hiding this comment.
@alex-rawlings-yyc reviewed 5 files and all commit messages.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on imnasnainaec).
BoundaryControl unmounted the whole split-marker row for intra-segment slots whenever the marker wasn't shown (Alt up, or the not-mid-phrase guard), so those rows collapsed to zero height and remounted at min-h-4 purely from Alt going up/down — growing the strip unevenly, since rows with a live boundary never move (their merge button's wrapper reserves that height permanently). The wrapper now always renders; only the marker inside it is conditional, matching the link-icon row's existing mount-and-fade pattern. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
assertNoOversizedIconClassName only recognized the h-*/w-*-without-
size- className pattern, missing icons sized via lucide's numeric
size prop instead of a Tailwind class -- but buttonVariants overrides
that the same way, since SVG width/height attributes lose to any
matching CSS rule. Extended the guard to also throw on a numeric size
prop without a size- class, which caught the only two such instances
in src: ProjectMetadataModal's delete Trash2 (size={13}) and
SelectInterlinearProjectModal's info Info icon (size={15}). Both
predate this PR. Fixed with tw:size-[13px]/tw:size-[15px] to keep
their exact original dimensions.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
alex-rawlings-yyc
left a comment
There was a problem hiding this comment.
@alex-rawlings-yyc reviewed 4 files and all commit messages, and resolved 1 discussion.
Reviewable status:complete! all files reviewed, all discussions resolved (waiting on imnasnainaec).
The className check required both tw:h-* and tw:w-* before throwing, but buttonVariants overrides an SVG's size the same way whether one or both dimensions are set -- so a hypothetical single-dimension regression (e.g. tw:h-3 alone) would have slipped through undetected. Changed the check to OR. All 1578 tests still pass: every icon site in this codebase already pairs h-*/w-* together, so nothing currently relies on the narrower behavior. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
alex-rawlings-yyc
left a comment
There was a problem hiding this comment.
@alex-rawlings-yyc reviewed 1 file and all commit messages.
Reviewable status:complete! all files reviewed, all discussions resolved (waiting on imnasnainaec).
PR #168 migrated this popover's buttons to platform-bible-react `Button` but left the breakdown text field and its label as raw HTML. Bring them in line with the convention the three project modals already follow, so the field picks up the host's focus ring, disabled styling, and dark-mode tokens instead of the hand-rolled approximations. Drops the classes `Input` already supplies (rounded, border, border-input, px/py, text-sm) and keeps only `w-full` and the `font-mono` that the morpheme forms want. `Label` defaults to text-sm, so the popover's smaller `text-xs` stays as an explicit override. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PR #168 migrated this popover's buttons to platform-bible-react `Button` but left the breakdown text field and its label as raw HTML. Bring them in line with the convention the three project modals already follow, so the field picks up the host's focus ring, disabled styling, and dark-mode tokens instead of the hand-rolled approximations. Drops the classes `Input` already supplies (rounded, border, border-input, px/py, text-sm) and keeps only `w-full` and the `font-mono` that the morpheme forms want. `Label` defaults to text-sm, so the popover's smaller `text-xs` stays as an explicit override. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Resolves Tier 1 of #165.
What & why
platform-bible-reactships equivalents for several elements the WebView hand-rolled as raw HTML (paired with bespoketw:utilities that duplicated their styling). Migrating to the platform components gives visual/behavioral consistency with the host app, theming, and accessibility for free, and lets us delete the duplicated CSS.Changes (Tier 1)
<button>→Button— mapped the oldpill-btn-*/icon-buttonclasses tovariant/size, preserving everydata-testid,aria-label,tabIndex, event handler, anddisabledrule. Icons inside migrated buttons are sized withsize-*(e.g.tw:size-3), noth-*/w-*—buttonVariantsforces any child SVG lacking asize-class tosize-4, soh-*/w-*alone is silently overridden:EditPhraseControls,UnlinkPhraseConfirmMorphemeEditorDelete / Cancel / DoneContinuousViewnav arrows,PhraseBoxedit/unlink,TokenChipremove / define-morphemes / suggestion-add,TokenLinkIconlink/unlink,ArcOverlaysplit,SegmentListViewmerge & scroll-to-active,PhraseStripPartsboundaryMorphemeBox's morpheme-form button intentionally stays raw<button>— it shares itsclassNamewith sibling plain<span>grid cells so the row stays pixel-aligned; wrapping it inButtonwould add shadcn's border/rounding/focus-ring/disabled-opacity chrome and break that parity.ViewOptionsDropdown's settings icon,ProjectMetadataModal's delete icon, andSelectInterlinearProjectModal's info icon (all alreadyButtons predating this PR) had the same icon-oversizing bug — via anh-*/w-*className for the first, a numericsizeprop for the other two — and are fixed alongside the rest (the latter two astw:size-[13px]/tw:size-[15px]to preserve their original dimensions), though none of those components are otherwise touched here.ViewOptionsDropdownis slated for aPopoverconversion in Tier 3.<input>/<textarea>/<label>→Input/Textarea/Label:CreateProjectModal,SaveAsProjectModal(droppedmodal-form-*)ProjectMetadataModal— kept its distinctsection-label/modal-metadata-inputstyling (the issue grouped it with themodal-form-*modals, but it actually uses different utilities, so those are intentionally retained)CSS cleanup — deleted the now-unused utilities:
pill-btn-primary,pill-btn-secondary,pill-btn-destructive,modal-form-input,modal-form-label,icon-button.Tests — extended
__mocks__/platform-bible-react.tsxwithInput/Textareastubs and taught theButtonstub to forward the extra props the icon buttons rely on (tabIndex,style,title, mouse handlers,aria-controls/aria-hidden). The stub also throws if a descendant SVG uses anh-*orw-*className withoutsize-, or a numericsizeprop without asize-className, so the icon-oversizing footgun above fails a test instead of shipping silently. No product test needed changing.Docs — added an AGENTS.md “Components” note to prefer
platform-bible-reactcomponents over raw HTML, including thesize-*icon-sizing rule.Also included: boundary-row reflow fix (unrelated to the Button migration)
PhraseStripParts.tsx'sBoundaryControlused to unmount its whole reserved-height row for an intra-segment slot whenever the split marker wasn't shown (Alt up, or the not-mid-phrase guard), so that row collapsed to zero height and remounted atmin-h-4purely from Alt going up/down — growing the SegmentView strip unevenly, since rows with a live boundary never move (their merge button's wrapper reserves that height permanently). The wrapper now always renders; only the marker inside it is conditional, matching the link-icon row's existing mount-and-fade pattern.This bug predates the Button migration (it came in with earlier split-UI work) and is unrelated to it — flagging it here explicitly since it's riding along on this branch rather than shipping as its own PR.
Deferred, with rationale (the issue's acceptance criteria permits deferring Tier 2/3)
field-sizing: content) and theWipeModalradio group. PlatformInputdoes forwardstyle/className/ref, so the gloss inputs are technically feasible, but they carry bespoke ARIA-combobox and auto-size behavior that deserves a focused pass.ViewOptionsDropdown→Popover(a clean standalone cleanup, good as its own PR) and theSuggestionDropdown/TokenChipcombobox (the issue itself notes this "may warrant its own issue").🤖 Generated with Claude Code
Devin: https://app.devin.ai/review/sillsdev/interlinearizer-extension/pull/168
This change is
Summary by CodeRabbit
Style
size-*classes.Documentation