Epic-privacy toggle has no accessible name — Switch primitive drops aria-labelledby
Found while building the 6.14.9 epic-privacy E2E (MOTIR-439).
The epic-privacy toggle on the issue-detail page has NO accessible name — a WCAG 2.1 §4.1.2 (Name, Role, Value) defect on a form control.
Root cause (rung-2, shipped code):
app/(authed)/issues/[key]/_components/EpicPrivacyControl.tsxnames its switch viaaria-labelledby={labelId}(the id of the visible "Make this epic private" label span).- But the shared
components/ui/Switch.tsxprimitive only accepts anaria-labelprop — it destructures a FIXED prop set and does NOT forwardaria-labelledby(or any other aria-*) to the underlyingrole="switch"button. - Net: the
aria-labelledbyis silently dropped, so the rendered switch isrole=switchwith an EMPTY accessible name. Confirmed in the Playwright a11y snapshot: the node is bare- switch(no name), with the label only present as adjacent text.
Impact: a screen-reader user hears an unnamed toggle; getByRole("switch", { name }) cannot target it (the E2E falls back to the page-unique getByRole("switch")).
Fix options:
- Add
aria-labelledby?: stringtoSwitchPropsand forward it to the button (mirrors the existingaria-labelpass-through) — thenEpicPrivacyControlworks as written; OR - In
EpicPrivacyControl, passaria-label={t("epicPrivacyLabel")}to the Switch (the prop the primitive already supports) instead ofaria-labelledby.
Option 1 is the more general fix (other call sites may want label association). Add a vitest/RTL assertion that the toggle has an accessible name, and the 6.14.9 E2E can then re-tighten to getByRole("switch", { name: "Make this epic private" }).
Acceptance criteria
- The epic-privacy toggle exposes the accessible name "Make this epic private" (via the chosen mechanism);
getByRole("switch", { name })resolves it. - A unit/RTL test asserts the toggle has a non-empty accessible name.
- No visual change; AA + shape/colour token rules unchanged.
Context refs
components/ui/Switch.tsx(the primitive — onlyaria-labelis forwarded).app/(authed)/issues/[key]/_components/EpicPrivacyControl.tsx(passesaria-labelledby).tests/e2e/epic-privacy-flow.spec.ts(6.14.9 — currently selects the switch by role alone, with a comment pointing here).