fix(a11y): name every select by its visible label, or by an aria-label

Merged
DO
1d ago
+108-86

Proposed changes (including videos or screenshots)

Follow-up to #43061. Fuselage's Select is now a select-only combobox that a <label htmlFor> names like any other input, so the call sites no longer need workarounds, and the ones that had no name get one.

  • Visible label → htmlFor + id. Selects that sat under a FieldLabel without htmlFor, or that were wired with aria-labelledby, now use <FieldLabel htmlFor={id}> + <Select id={id}>: timestamp picker (Format, Timezone), custom user status, role form, omnichannel analytics, canned responses, queue list, real-time monitoring, chats filters (status and custom fields), SLA policies, room notification preferences, and the onboarding Request trial and Create cloud workspace forms.
  • No visible label → aria-label. Threads, Team channels, ABAC rooms, contact history and call history filters, the moderation date range, the color setting editor and the timespan setting unit.
  • aria-controls removed from the Threads, Files and Members filters. On a combobox it points at its own listbox, which Select already sets; the search inputs keep theirs.
  • AutoTranslate: the label pointed at translate-to, but the select's id was language.
  • Outgoing webhook event trigger: its hint id went to aria-description instead of aria-describedby, so the hint was never announced.

After this, no Select passes aria-labelledby or aria-controls, and every Select has a name. The few that spread their props (SenderSelect, RecipientSelect, the app logs filters) receive it from their callers.

Issue(s)

ARCH-2588
Follow-up to #43061.

Steps to test or reproduce

  1. Open a room's Threads, Files or Members list, and a team's Team channels. With a screen reader, or in the browser's accessibility tree, the filter select is announced with a name (Filter, File type, Status) instead of only its value.
  2. Open Administration → Status and presence, create a custom status, then click the Presence label. The select takes the focus, and it is announced as "Presence".
  3. In a room, open Auto translate and click Translate to. The language select takes the focus.
  4. Open Administration → Integrations and create an outgoing integration (/admin/integrations/new/outgoing). The Event trigger select announces its hint.

Further comments

  • No new tests: the changes are accessible-name wiring, and the existing client tests pass. Happy to add some if a reviewer wants them.
  • Typecheck is clean for apps/meteor and onboarding-ui.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Accessibility
    • Improved screen-reader names and label associations for selectors across administration, omnichannel, room and team views, call history, timestamp settings, and onboarding forms.
    • Corrected how the outgoing webhook event selector references its hint.
    • Removed aria-controls associations from select controls in room file, member, and thread views.
    • Added accessible labels for color-editor and time-unit settings.