Skip to content

fix(bulma-ui): name a composed DateRangeInputBase's group from a Field label - #1015

Merged
allxsmith merged 4 commits into
mainfrom
fix/1005-daterangebase-group
Oct 10, 2026
Merged

allxsmith merged 4 commits into
mainfrom
fix/1005-daterangebase-group

Conversation

@allxsmith

Copy link
Copy Markdown
Owner

The DateRangeInputBase piece left over from #968.

<Field label="Stay"><DateRangeInput /></Field> names the range's group "Stay": the wrapper points its role="group" at the Field's label through aria-labelledby. DateRangeInputBase composed the same way, in a labeled Field and Control, didn't. Its Start date and End date inputs kept their own names, but the group had no name at all.

Before changing anything I rendered it on main with Testing Library, next to the wrapper and Radios in the same spot. The base's group had no aria-labelledby on the popover path, the native path or inline, while the wrapper and Radios both pointed at the label's id. An aria-label on the base did name it, so nothing was in the way of the caller's own name.

Now the base calls useAutoLabelledBy with no label of its own and its remaining props as callerProps, the way Radios, Checkboxes, Rate and the wrapper do, and puts the result on its group root ahead of the props spread. That covers every mode, inline and native included, since the group renders in all of them. It's different from the single-input picker bases, which skip the Field inline because they have no input to give the id to. Here the Field's label names the group, not an input, and the group is still there. Each input keeps its own name.

A caller's aria-label or aria-labelledby still wins, as #974 set for groups, and an aria-labelledby key counts even when it's undefined, since it spreads over the group's own. That's also why the wrapper doesn't change: it always hands the base an aria-labelledby key of its own, so the base leaves the group to it and the wrapper keeps deciding between its own label and a surrounding Field's.

About the label's for: in this spot it still matches nothing. That's true today for the wrapper, Radios and every other group in a labeled Field too, and the Field page already says so. The group pattern doesn't avoid it, and the general fix, letting content that takes no for tell its Field, is #1004. Giving the start input the Field's id would have matched the for, but it would tie the label to one end of the range rather than to the group, and the base would behave differently from the wrapper.

Also in here:

  • The Field label TSDoc, the Field page's accessibility notes and the DateRangeInput page's accessibility notes count a composed DateRangeInputBase among the groups a labeled Field names, inline or not. The bestax-form skill says the same.
  • TSDoc on DateRangeInputBase saying it takes a surrounding Field's label and that a caller's name wins, and a line in src/form/CLAUDE.md for the next base that is a group.
  • The composed pickers Field story gets a DateRangeInputBase under a "Stay" label.
  • Tests: the base in Field > Control is named by the label on the popover path, the native path and inline, and each input keeps its own name. A caller's aria-label or aria-labelledby wins, and so does an undefined aria-labelledby key, which is what the wrapper passes. The base takes no name from an unlabeled Field. The wrapper still names its group from the outer Field's label, or from its own. The Field suite's shared group tests (named in a Field and in a Control, caller names winning, an undefined aria-label passed through) now run over the base as well.

pnpm all passes locally.

Fixes #1005

…d label

DateRangeInputBase composed in a labeled Field now points its role="group"
root's aria-labelledby at that Field's label, through useAutoLabelledBy,
the way the DateRangeInput wrapper already does. It does it in every mode,
inline and native included, since the group renders in all of them, and
each input keeps its own "Start date" or "End date" name.

An aria-label or aria-labelledby the caller gives the group still wins.
The wrapper always passes an aria-labelledby key of its own, so the base
leaves the group to it and the wrapper's naming doesn't change.
The DateRangeInput and Field pages and the bestax-form skill now count a
DateRangeInputBase composed in a labeled Field among the groups the label
names through aria-labelledby, inline or not.
@coderabbitai

coderabbitai Bot commented Oct 10, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 50 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository: allxsmith/bestax/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 3436cf76-3ce5-486d-8bbb-c79da8f890f9

📥 Commits

Reviewing files that changed from the base of the PR and between 980d442 and a680a57.


📒 Files selected for processing (12)
  • bestax-mcp/data/components/DateRangeInput.json
  • bestax-mcp/data/components/Field.json
  • bulma-ui/src/form/CLAUDE.md
  • bulma-ui/src/form/DateRangeInputBase.tsx
  • bulma-ui/src/form/Field.stories.tsx
  • bulma-ui/src/form/Field.tsx
  • bulma-ui/src/form/FormContext.tsx
  • bulma-ui/src/form/__tests__/DateRangeInput.test.tsx
  • bulma-ui/src/form/__tests__/Field.test.tsx
  • docs/docs/api/form/datetime/daterangeinput.md
  • docs/docs/api/form/field.md
  • skills/bestax-form/SKILL.md

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment

Preview URL: https://741aaa40.bestax.pages.dev

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deep review — 0 blocking · 0 advisory

# Severity Area Finding Location
— — — No blocking defects found. —

Overall: The change is sound and tightly scoped: DateRangeInputBase now calls useAutoLabelledBy with no label of its own and renders aria-labelledby ahead of {...cleanRest} on all three of its role="group" roots (native bulma-ui/src/form/DateRangeInputBase.tsx:603, inline :676, popover :725) — exactly the placement Radios.tsx:151, Checkboxes.tsx:149 and Rate.tsx:550 already use, so the caller-wins and presence-of-aria-labelledby semantics from #974 come for free rather than being re-implemented. The riskiest part was a regression in the DateRangeInput wrapper, since the wrapper's naming previously worked only through the spread — on main the base emitted no aria-labelledby of its own at all (verified: git show origin/main:.../DateRangeInputBase.tsx has zero matches, which is also the bug #1005 reports). That is closed by the wrapper always passing an aria-labelledby key, which the hook's presence check honours, and by the new wrapper test covering both the outer-Field and own-label paths. A human should focus on whether naming the group is the right answer for inline, where the group wraps only a calendar and no input — the PR argues it is because the group element exists in that mode, unlike the single-input bases, which I confirmed render a roleless <div> inline (DateInputBase.tsx:586).

Verified here: DateRangeInput.test.tsx + Field.test.tsx (255 passed) and typecheck:tests (clean). This session's sandbox refused file writes, so I could not add scratch probe tests; the scenarios below were settled by tracing the real code paths plus the repo's own suites, and the React 18/19 matrix jobs are green on this SHA.

Residual risk:

  • Another group component still missing the hook. Refuted — role="group"/"radiogroup" appears only in Radios, Checkboxes, Rate and DateRangeInputBase (plus prose in the DateRangeInput wrapper), and all four now call useAutoLabelledBy. The failure class is closed for groups.
  • callerProps: rest diverging from the spread cleanRest. Refuted — useBulmaClasses destructures only Bulma helper props and never touches aria-* (no aria match anywhere in helpers/useBulmaClasses.tsx), so the presence of aria-label/aria-labelledby is identical in both objects.
  • Id churn from the extra useId(). Refuted — reactId/popoverId are computed before the new hook call, and the hook's own generated id is unused when label is undefined, so no existing id or aria-controls target shifts. No early return precedes the hook, so hook order is stable in every mode.
  • A render path left uncovered. Refuted — the component has exactly three returns (useNative, inline, default); all three carry the attribute and all three are asserted by the new it.each in DateRangeInput.test.tsx.
  • grouped/hasAddons, or a labelProps.htmlFor the caller wired, leaving the group unnamed. By design and unchanged: Field publishes the label element id only when targetId is set (Field.tsx:335), the same gate every other group sits behind, and docs/docs/api/form/field.md:501 states it.
  • Open, not fixed here: the Field's <label for> still points at a generated id nothing adopts, so clicking the label focuses neither input and a validator sees a dangling for. Pre-existing for every group in a labeled Field, stated on the Field page, and tracked by #1004 — and still strictly better than main, where the composed base got no name at all.

🏄 Four lines of attribute plus one hook call, dropped right into the lane the other groups were already surfing — no wipeouts, the wrapper never even wobbled, and the tests ride all three modes. Clean set, good to go.

@github-actions github-actions Bot added the review-converged Deep review converged: no blocking findings, all review threads resolved, checks green label Oct 10, 2026
@allxsmith allxsmith added the needs-human-review Loop converged (or contested): awaiting owner review + manual merge label Oct 10, 2026
@github-actions github-actions Bot removed the review-converged Deep review converged: no blocking findings, all review threads resolved, checks green label Oct 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📸 Story screenshots at handoff — a680a57

6 stories (light + dark) from this PR's changed files under bulma-ui/src/ (workflow run · artifact story-screenshots-pr-1015, 30-day retention).

form-field--default · Form/Field · Default
Light Dark
form-field--default light form-field--default dark
form-field--with-help-text · Form/Field · With Help Text
Light Dark
form-field--with-help-text light form-field--with-help-text dark
form-field--labels-custom-controls · Form/Field · Labels Custom Controls
Light Dark
form-field--labels-custom-controls light form-field--labels-custom-controls dark
form-field--labels-composed-pickers · Form/Field · Labels Composed Pickers
Light Dark
form-field--labels-composed-pickers light form-field--labels-composed-pickers dark
form-field--validation-states · Form/Field · Validation States
Light Dark
form-field--validation-states light form-field--validation-states dark
form-field--label-sizes · Form/Field · Label Sizes
Light Dark
form-field--label-sizes light form-field--label-sizes dark
> ✂ 13 more matched stories skipped by the cap: `form-field--horizontal`, `form-field--horizontal-explicit-body`, `form-field--horizontal-narrow`, `form-field--grouped`, `form-field--grouped-centered`, `form-field--grouped-right`, `form-field--grouped-expanded`, `form-field--grouped-multiline`, `form-field--addons`, `form-field--addons-with-select`, … — full set in the artifact.

@github-actions github-actions Bot added the review-converged Deep review converged: no blocking findings, all review threads resolved, checks green label Oct 10, 2026
@allxsmith
allxsmith merged commit 2981fad into main Oct 10, 2026
48 checks passed
@allxsmith
allxsmith deleted the fix/1005-daterangebase-group branch October 10, 2026 13:30
@bestax-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 5.27.7 🎉

The release is available on:

Your semantic-release bot 📦🚀

allxsmith added a commit that referenced this pull request Oct 10, 2026
The merge of main took this page whole from the branch side, which
dropped #1015's addition to the hand-written group bullet.
@bestax-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 1.14.4 🎉

The release is available on:

Your semantic-release bot 📦🚀

@bestax-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 2.25.3 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

deep-review needs-human-review Loop converged (or contested): awaiting owner review + manual merge released review-converged Deep review converged: no blocking findings, all review threads resolved, checks green

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DateRangeInputBase: a surrounding Field's label doesn't name its group

1 participant