Validated form controls: attach validation errors to the interactive element - #81305
Validated form controls: attach validation errors to the interactive element#81305dhasilva wants to merge 4 commits into
Conversation
Adds an optional `getInteractiveTarget` resolver to `ControlWithError`, and uses it in `ValidatedFormTokenField`. Controls that validate through a hidden delegate element previously attached the validity message's `aria-describedby` to the delegate, so screen reader users never heard the error on the element they actually focus. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ement The group container is the correct target for a group-level description, and unlike the active option it is present regardless of interaction state. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Warning: Type of PR label mismatch To merge this PR, it requires exactly 1 label indicating the type of PR. Other labels are optional and not being checked here.
Read more about Type labels in Gutenberg. Don't worry if you don't have the required permissions to add labels; the PR reviewer should be able to help with the task. |
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
What
For validated controls that use a visually hidden element as a validation delegate —
ValidatedFormTokenField,ValidatedToggleGroupControl, andValidatedCustomSelectControl— the error message'saria-describedbywas attached to the delegate rather than to the element the user actually focuses. A screen reader user who moves focus back to the control never hears the error.Fixes #76741.
How
ControlWithErrorgains an optional resolver:Only the
aria-describedbyeffect consults it, falling back togetValidityTarget(). Every other use ofgetValidityTarget()— theinvalidlisteners,setCustomValidity(), the native popover suppression,data-validity-visible— is unchanged, because those genuinely belong on the delegate, which is what the Constraint Validation API acts on.Each delegate control already contained the selector it needed inside its
onFocushandler. Those are now module-level constants shared by both the focus delegation and the description, so the two cannot drift.ValidatedToggleGroupControlis the exception and uses two distinct selectors. Focus belongs on the option holding the roving tabindex ([data-active-item="true"]), but the description belongs on the group container thatAriakit.RadioGrouprenders: a group-level description is what a screen reader announces on entering the group, and unlike the active item that element is present regardless of interaction state.Notes for reviewers
ValidatedCustomSelectControlis included even though it is not currently exported anywhere — it is the third delegate-based control and the issue names it explicitly.tabIndex={-1}and visually hidden, so nothing reads itsaria-describedby.it.skipintest/toggle-group-control.tsxis intentionally left skipped. It covers a different pre-existing bug —BaseControlnot associating thehelpprop — which this PR does not address.Testing instructions
npm run test:unit -- packages/components/src/validated-form-controls