fix(a11y): associate form errors with their fields instead of toast-only announcements (#14701)
* fix(a11y): associate form errors with their fields across auth and settings forms Server rejections and validation failures were announced only through the transient toast (or, for API-key creation, swallowed entirely) and never programmatically associated with the fields in error (WCAG 1.3.1 / 3.3.1 / 3.3.3). Roll the existing auth-form pattern (aria-invalid + aria-describedby + inline role="alert" text) into every flagged form: - Login / Sign-up: rejected submissions now render the error inline inside the form card and mark the credential fields invalid; editing a field clears it. - Global variables modal: create/update rejections (duplicate name, invalid provider key) surface inline and mark the name/value inputs. - API keys modal: a failed key creation no longer shows an empty generated-key screen with no message — it returns to the form and surfaces the error inline on the name field. - MCP server modal: the existing role="alert" banner gets an id and the offending inputs (empty required fields, invalid JSON textarea) point at it. - Model providers: fields get real label association (htmlFor/aria-labelledby; single-variable providers had a completely unlabeled key field) and the tooltip-only validation error gains visible inline text tied to the inputs. - Knowledge base create: the name/files/metadata error spans get ids and role="alert", and the name input, embedding-model and DB-provider comboboxes reference their error text. - Change password: mismatch/server errors render inline and the three fields gain accessible names (they were placeholder-only). Plumbing: CustomInputPopover and ModelTrigger/DBProviderInput now forward inputProps / ariaDescribedBy+ariaInvalid so callers can wire the association. * fix(a11y): move the API-key failure message to form level and soften 5xx detail A failed key creation is the action's failure, not the Description field's — the message now renders above the modal actions instead of under the name input, the input keeps aria-describedby but is no longer marked aria-invalid, and raw 5xx detail is replaced with the translated errors.errorGeneratingApiKey message (4xx details still pass through). * fix(a11y): apply review findings — misplaced password error, Radix describedby clobber, stale modal errors Adversarial review of the form-error-semantics work surfaced: - The profile-picture PATCH failure was also populating the password card's error (a duplicated onError edit) — an avatar failure announced three invalid password fields. Reverted that handler to toast-only. - PasswordForm's inputProps always carried aria keys (undefined when clean), which clobbers the aria-describedby Radix Form.Control wires to its valueMissing messages through Slot merging. Now built conditionally; verified in-browser that the three valueMissing messages resolve again. - GlobalVariableModal kept a stale serverError across close/reopen (the component stays mounted), re-announcing an old failure; cleared on open. - ProviderConfigurationForm: htmlFor no longer dangles for options-backed variables (multiselect renders no id), and aria-invalid is scoped to required fields so an invalid credential no longer blames optional inputs. - ModelTrigger's setup-provider branch now carries aria-describedby too. - secretKeyModal: changing the expiry date clears the stale error like the name field does. New PasswordForm a11y test (real Radix form, no mock) pins the server-error association and clean no-error state; the Radix valueMissing path is only observable in a real browser and is covered by the harness captures (password-form--value-missing / --mismatch in the evidence bundle). * test(a11y): expect the rejection message on both surfaces in auth specs The login/sign-up rejection now renders inline (associated with the fields) in addition to the toast, so the single-element getByText assertions hit a strict-mode violation. Assert the count and the inline node explicitly.
V
Viktor Avelino committed
ba99f3e491f639faeecef7893a54d9ba4b00fc1d
Parent: a4e794a
Committed by GitHub <noreply@github.com>
on 8/24/2026, 1:45:57 PM