️(frontend) announce selected state to SR in select and menu list

Add visually hidden "selected" text for screen readers.
This commit is contained in:
Cyril
2026-03-04 14:56:07 +01:00
committed by aleb_the_flash
parent 9c009839f0
commit 3d7aec2b4a
7 changed files with 31 additions and 3 deletions

View File

@@ -12,6 +12,7 @@ and this project adheres to
- 🩹(frontend) remove incorrect reference to ProConnect on the prejoin #1080 - 🩹(frontend) remove incorrect reference to ProConnect on the prejoin #1080
- ✨(frontend) add Ctrl+Shift+/ to open shortcuts settings #1050 - ✨(frontend) add Ctrl+Shift+/ to open shortcuts settings #1050
- ♿(frontend) announce selected state to screen readers #1081
### Changed ### Changed

View File

@@ -25,6 +25,7 @@
"heading": "Überprüfen Sie Ihren Meeting-Code", "heading": "Überprüfen Sie Ihren Meeting-Code",
"body": "Stellen Sie sicher, dass Sie den richtigen Meeting-Code in der URL eingegeben haben. Beispiel:" "body": "Stellen Sie sicher, dass Sie den richtigen Meeting-Code in der URL eingegeben haben. Beispiel:"
}, },
"selected": "ausgewählt",
"submit": "OK", "submit": "OK",
"footer": { "footer": {
"links": { "links": {

View File

@@ -25,6 +25,7 @@
"heading": "Verify your meeting code", "heading": "Verify your meeting code",
"body": "Check that you have entered the correct meeting code in the URL. Example:" "body": "Check that you have entered the correct meeting code in the URL. Example:"
}, },
"selected": "selected",
"submit": "OK", "submit": "OK",
"footer": { "footer": {
"links": { "links": {

View File

@@ -25,6 +25,7 @@
"heading": "Vérifier votre code de réunion", "heading": "Vérifier votre code de réunion",
"body": "Vérifiez que vous avez saisi le code de réunion correct dans l'URL. Exemple :" "body": "Vérifiez que vous avez saisi le code de réunion correct dans l'URL. Exemple :"
}, },
"selected": "sélectionné",
"submit": "OK", "submit": "OK",
"footer": { "footer": {
"links": { "links": {

View File

@@ -24,6 +24,7 @@
"notFound": { "notFound": {
"heading": "Pagina niet gevonden" "heading": "Pagina niet gevonden"
}, },
"selected": "geselecteerd",
"submit": "OK", "submit": "OK",
"footer": { "footer": {
"links": { "links": {

View File

@@ -1,5 +1,7 @@
import { ReactNode } from 'react' import { ReactNode } from 'react'
import { Menu, MenuProps, MenuItem } from 'react-aria-components' import { Menu, MenuProps, MenuItem } from 'react-aria-components'
import { useTranslation } from 'react-i18next'
import { VisuallyHidden } from '@/styled-system/jsx'
import { menuRecipe } from '@/primitives/menuRecipe.ts' import { menuRecipe } from '@/primitives/menuRecipe.ts'
import type { RecipeVariantProps } from '@/styled-system/types' import type { RecipeVariantProps } from '@/styled-system/types'
@@ -19,6 +21,7 @@ export const MenuList = <T extends string | number = string>({
} & MenuProps<unknown> & } & MenuProps<unknown> &
RecipeVariantProps<typeof menuRecipe>) => { RecipeVariantProps<typeof menuRecipe>) => {
const [variantProps] = menuRecipe.splitVariantProps(menuProps) const [variantProps] = menuRecipe.splitVariantProps(menuProps)
const { t } = useTranslation('global')
const classes = menuRecipe({ const classes = menuRecipe({
extraPadding: true, extraPadding: true,
variant: variant, variant: variant,
@@ -39,11 +42,19 @@ export const MenuList = <T extends string | number = string>({
className={classes.item} className={classes.item}
key={value} key={value}
id={value as string} id={value as string}
textValue={typeof label === 'string' ? label : undefined}
onAction={() => { onAction={() => {
onAction(value as T) onAction(value as T)
}} }}
> >
{label} {({ isSelected }) => (
<>
{label}
{isSelected && (
<VisuallyHidden>, {t('selected')}</VisuallyHidden>
)}
</>
)}
</MenuItem> </MenuItem>
) )
})} })}

View File

@@ -1,5 +1,5 @@
import { type ReactNode } from 'react' import { type ReactNode } from 'react'
import { styled } from '@/styled-system/jsx' import { styled, VisuallyHidden } from '@/styled-system/jsx'
import { RemixiconComponentType, RiArrowDropDownLine } from '@remixicon/react' import { RemixiconComponentType, RiArrowDropDownLine } from '@remixicon/react'
import { import {
Button, Button,
@@ -9,6 +9,7 @@ import {
SelectProps as RACSelectProps, SelectProps as RACSelectProps,
SelectValue, SelectValue,
} from 'react-aria-components' } from 'react-aria-components'
import { useTranslation } from 'react-i18next'
import { Box } from './Box' import { Box } from './Box'
import { StyledPopover } from './Popover' import { StyledPopover } from './Popover'
import { menuRecipe } from '@/primitives/menuRecipe.ts' import { menuRecipe } from '@/primitives/menuRecipe.ts'
@@ -110,6 +111,7 @@ export const Select = <T extends string | number>({
...props ...props
}: SelectProps<T>) => { }: SelectProps<T>) => {
const IconComponent = iconComponent const IconComponent = iconComponent
const { t } = useTranslation('global')
return ( return (
<RACSelect {...props}> <RACSelect {...props}>
{label} {label}
@@ -138,8 +140,18 @@ export const Select = <T extends string | number>({
} }
id={item.value} id={item.value}
key={item.value} key={item.value}
textValue={
typeof item.label === 'string' ? item.label : undefined
}
> >
{item.label} {({ isSelected }) => (
<>
{item.label}
{isSelected && (
<VisuallyHidden>, {t('selected')}</VisuallyHidden>
)}
</>
)}
</ListBoxItem> </ListBoxItem>
))} ))}
</ListBox> </ListBox>