SIGN IN SIGN UP

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