From a7bd0a3c46c6a6d844b4e786079f826dab675d4b Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 9 Aug 2026 20:51:57 +0000 Subject: [PATCH] fix(fields): name the multiselect chip group by IDREF instead of an inert `for` (#3975) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The residual of #3961, not a regression of it. That issue's probe audited six widgets; re-running the same probe over the whole widget map afterwards — on the tree that already carried #3961's fix — put `multiselect` on the byte-identical failure shape as `checkboxes`: checkboxes for=(none) ownId=…-group-label byLabelText=1(div[role=group]) multiselect for=…-form-item ownId=(none) byLabelText=0 `MultiSelectField` keeps the host's id on the chip row's wrapper `div`, so the host label's `for` "resolved" to a `div` — where `label for` is inert HTML (`HTMLLabelElement.control` is null: it activates nothing and contributes no accessible name). Visually a "Tags" label; a screen reader heard only "Alpha" / "Beta" and nothing about what the set was for. No new mechanism — #3961's declaration, applied to one more widget: - `FIELD_TYPES_GROUP_LABELLED` gains `multiselect`, so the form renderer publishes its label's `id` and drops the dead `for`; - the chip row answers `role="group"` when (and only when) it was handed an `aria-labelledby`, the same conditional `CheckboxesField` uses. Pins, both directions. The two files fail apart by design and were measured apart: dropping the set entry turns `group-labelling-declaration.test.ts` red (the per-type case + the "exactly the audited set" assertion) while the e2e file stays GREEN — it registers widgets raw with `labelling: 'group'` hardcoded in its own `beforeAll`, so it structurally cannot see the real declaration. Dropping the `role` inverts that: four e2e cases red, the declaration test green. Neither half is covered by the other's test, which is why both exist. Unchanged on purpose: each chip keeps its own accessible name from its text content (an `aria-labelledby` on a chip would OVERRIDE it, the same override this family refuses for address's street box), and standalone rendering — the grid's inline cell editor, a bare SDUI node — emits no role and no IDREF. --- .changeset/multiselect-group-label-3975.md | 31 ++++++++++++ .../composite-group-label-e2e.test.tsx | 48 ++++++++++++++++++- .../group-labelling-declaration.test.ts | 15 +++++- packages/fields/src/index.tsx | 19 ++++++-- .../fields/src/widgets/MultiSelectField.tsx | 10 ++++ 5 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 .changeset/multiselect-group-label-3975.md diff --git a/.changeset/multiselect-group-label-3975.md b/.changeset/multiselect-group-label-3975.md new file mode 100644 index 000000000..f11b53501 --- /dev/null +++ b/.changeset/multiselect-group-label-3975.md @@ -0,0 +1,31 @@ +--- +'@object-ui/fields': patch +--- + +A form-hosted `multiselect` field is now NAMED by its visible label. It was the +residual of objectui#3961: that issue's probe audited six widgets and fixed +them, and re-running the same probe over the whole widget map afterwards put +`multiselect` on the byte-identical failure shape as `checkboxes` — the host's +`id` kept, but on the chip row's wrapper `div`, where a `