From b8d23ae616ff6a6ff7f303132e02ad29000e8402 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Wed, 20 May 2026 14:13:38 +0100 Subject: [PATCH] Name labeled toggle controls --- .../src/components/shared/Toggle.test.tsx | 11 ++++++++ .../src/components/shared/Toggle.tsx | 28 +++++++++++++------ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/frontend-modern/src/components/shared/Toggle.test.tsx b/frontend-modern/src/components/shared/Toggle.test.tsx index 3b346a41f4..124c8994b6 100644 --- a/frontend-modern/src/components/shared/Toggle.test.tsx +++ b/frontend-modern/src/components/shared/Toggle.test.tsx @@ -51,4 +51,15 @@ describe('Toggle', () => { expect(screen.getByText('Enabled')).toBeInTheDocument(); expect(screen.getByText('Turns it on')).toBeInTheDocument(); }); + + it('uses the visible label and description for the primitive button semantics', () => { + render(() => ( + Enabled} description={Turns it on} /> + )); + + const button = screen.getByRole('button', { name: 'Enabled' }); + expect(button).toHaveAttribute('aria-labelledby'); + expect(button).toHaveAttribute('aria-describedby'); + expect(button).toHaveAccessibleDescription('Turns it on'); + }); }); diff --git a/frontend-modern/src/components/shared/Toggle.tsx b/frontend-modern/src/components/shared/Toggle.tsx index 6c3fe38a68..bd2c588bf6 100644 --- a/frontend-modern/src/components/shared/Toggle.tsx +++ b/frontend-modern/src/components/shared/Toggle.tsx @@ -1,4 +1,4 @@ -import { JSX } from 'solid-js'; +import { JSX, createUniqueId } from 'solid-js'; import { getToggleContainerClass, getToggleDescriptionClass, @@ -9,16 +9,15 @@ import { type ToggleSize, } from '@/components/shared/toggleModel'; export type { ToggleChangeEvent } from '@/components/shared/toggleModel'; -import { - type ToggleRuntimeProps, - useToggleState, -} from '@/components/shared/useToggleState'; +import { type ToggleRuntimeProps, useToggleState } from '@/components/shared/useToggleState'; interface BaseToggleProps extends ToggleRuntimeProps { size?: ToggleSize; class?: string; title?: string; ariaLabel?: string; + ariaLabelledBy?: string; + ariaDescribedBy?: string; } export function TogglePrimitive(props: BaseToggleProps): JSX.Element { @@ -34,6 +33,8 @@ export function TogglePrimitive(props: BaseToggleProps): JSX.Element { title={props.title} aria-pressed={props.checked ? 'true' : 'false'} aria-label={props.ariaLabel} + aria-labelledby={props.ariaLabel ? undefined : props.ariaLabelledBy} + aria-describedby={props.ariaDescribedBy} > @@ -50,14 +51,25 @@ interface LabeledToggleProps extends BaseToggleProps { export function Toggle(props: LabeledToggleProps) { const size = resolveToggleSize(props.size, 'md'); + const labelId = createUniqueId(); + const descriptionId = createUniqueId(); return (
- + {(props.label || props.description) && ( - {props.label} - {props.description && {props.description}} + {props.label && {props.label}} + {props.description && ( + + {props.description} + + )} )}