diff options
| author | Oliver <oliver@mohlin.dev> | 2025-09-19 11:58:41 +0200 |
|---|---|---|
| committer | Tobias Järvelöv <tobias.jarvelov@mullvad.net> | 2025-09-22 12:35:44 +0200 |
| commit | 856d41f836de3983d435c8cdc9deadba2d669b8c (patch) | |
| tree | 8b88d576a8c549c28a255b791a6a75b9021a70d7 | |
| parent | c263968b301e94856b79f807a6df8cf720f2b071 (diff) | |
| download | mullvadvpn-856d41f836de3983d435c8cdc9deadba2d669b8c.tar.xz mullvadvpn-856d41f836de3983d435c8cdc9deadba2d669b8c.zip | |
Improve accessibility when navigating to setting via feature indicator
10 files changed, 41 insertions, 16 deletions
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/settings-accordion/SettingsAccordion.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/settings-accordion/SettingsAccordion.tsx index 7df1931bbf..389a4c4a92 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/components/settings-accordion/SettingsAccordion.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/components/settings-accordion/SettingsAccordion.tsx @@ -17,6 +17,7 @@ function SettingsAccordion({ accordionId, anchorId, ...props }: SettingsAccordio const initialExpanded = location.state.expandedSections[accordionId]; const [expanded, setExpanded] = React.useState(initialExpanded); const { ref, animation } = useScrollToListItem(anchorId); + const titleId = React.useId(); const handleOnExpandedChange = React.useCallback( (value: boolean) => { @@ -35,9 +36,12 @@ function SettingsAccordion({ accordionId, anchorId, ...props }: SettingsAccordio return ( <Accordion ref={ref} + tabIndex={-1} animation={animation} expanded={expanded} onExpandedChange={handleOnExpandedChange} + titleId={titleId} + aria-labelledby={titleId} {...props} /> ); diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/settings-list-item/SettingsListItem.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/settings-list-item/SettingsListItem.tsx index 268f56c9b6..0c505226ef 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/components/settings-list-item/SettingsListItem.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/components/settings-list-item/SettingsListItem.tsx @@ -4,12 +4,15 @@ import { ListItem, ListItemProps } from '../../lib/components/list-item'; export type SettingsListItemProps = ListItemProps & { anchorId?: ScrollToAnchorId; + labelId?: string; }; -function SettingsListItem({ anchorId, ...props }: SettingsListItemProps) { +function SettingsListItem({ labelId, anchorId, ...props }: SettingsListItemProps) { const { ref, animation } = useScrollToListItem(anchorId); - return <ListItem ref={ref} animation={animation} {...props} />; + return ( + <ListItem ref={ref} aria-labelledby={labelId} tabIndex={-1} animation={animation} {...props} /> + ); } const SettingsListItemNamespace = Object.assign(SettingsListItem, { diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/settings-listbox/SettingsListbox.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/settings-listbox/SettingsListbox.tsx index 0fca7d4357..3b15c84c6a 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/components/settings-listbox/SettingsListbox.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/components/settings-listbox/SettingsListbox.tsx @@ -1,3 +1,5 @@ +import React from 'react'; + import { ScrollToAnchorId } from '../../../shared/ipc-types'; import { useScrollToListItem } from '../../hooks'; import { Listbox, ListboxProps } from '../../lib/components/listbox'; @@ -9,8 +11,19 @@ export type SettingsListboxProps<T> = Omit<ListboxProps<T>, 'animation'> & { function SettingsListbox<T>({ anchorId, ...props }: SettingsListboxProps<T>) { const { ref, animation } = useScrollToListItem(anchorId); + const labelId = React.useId(); - return <Listbox ref={ref} animation={animation} {...props} />; + return ( + <Listbox + ref={ref} + tabIndex={-1} + role="region" + labelId={labelId} + aria-labelledby={labelId} + animation={animation} + {...props} + /> + ); } const SettingsListboxNamespace = Object.assign(SettingsListbox, { diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/settings-toggle-list-item/SettingsToggleListItem.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/settings-toggle-list-item/SettingsToggleListItem.tsx index e1156772fe..334f1d405c 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/components/settings-toggle-list-item/SettingsToggleListItem.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/components/settings-toggle-list-item/SettingsToggleListItem.tsx @@ -25,12 +25,14 @@ function SettingsToggleListItem({ ...props }: SettingsToggleListItemProps) { const descriptionId = React.useId(); + const labelId = React.useId(); return ( <SettingsToggleListItemProvider descriptionId={descriptionId}> - <SettingsListItem disabled={disabled} {...props}> + <SettingsListItem labelId={labelId} disabled={disabled} {...props}> <SettingsListItem.Item> <SettingsListItem.Content> <Switch + labelId={labelId} checked={checked} onCheckedChange={onCheckedChange} disabled={disabled} diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/open-vpn-settings/components/mss-fix-setting/MssFixSetting.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/open-vpn-settings/components/mss-fix-setting/MssFixSetting.tsx index 01900b5955..d56b3ddf58 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/components/views/open-vpn-settings/components/mss-fix-setting/MssFixSetting.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/open-vpn-settings/components/mss-fix-setting/MssFixSetting.tsx @@ -71,7 +71,7 @@ export function MssFixSetting() { ); return ( - <SettingsListItem anchorId="mss-fix-setting"> + <SettingsListItem anchorId="mss-fix-setting" aria-labelledby={labelId}> <SettingsListItem.Item> <SettingsListItem.Content> <SettingsListItem.Label id={labelId}> diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/vpn-settings/components/dns-blocker-settings/DnsBlockerSetting.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/vpn-settings/components/dns-blocker-settings/DnsBlockerSetting.tsx index 2c086d4a5b..52e0d6d28a 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/components/views/vpn-settings/components/dns-blocker-settings/DnsBlockerSetting.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/vpn-settings/components/dns-blocker-settings/DnsBlockerSetting.tsx @@ -26,6 +26,7 @@ export function DnsBlockerSettings() { <SettingsAccordion accordionId="dns-blocker-setting" anchorId="dns-blocker-setting" + aria-label={messages.pgettext('vpn-settings-view', 'DNS content blockers')} disabled={dns.state === 'custom'}> <SettingsAccordion.Header> <SettingsAccordion.Title> diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/views/wireguard-settings/components/mtu-setting/MtuSetting.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/views/wireguard-settings/components/mtu-setting/MtuSetting.tsx index d1fd17e81b..fc4d8f6a27 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/components/views/wireguard-settings/components/mtu-setting/MtuSetting.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/components/views/wireguard-settings/components/mtu-setting/MtuSetting.tsx @@ -79,7 +79,7 @@ export function MtuSetting() { ); return ( - <SettingsListItem anchorId="mtu-setting"> + <SettingsListItem anchorId="mtu-setting" aria-labelledby={labelId}> <SettingsListItem.Item> <SettingsListItem.Content> <SettingsListItem.Label id={labelId}> diff --git a/desktop/packages/mullvad-vpn/src/renderer/hooks/index.ts b/desktop/packages/mullvad-vpn/src/renderer/hooks/index.ts index a152881c63..2a4f1dfdaf 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/hooks/index.ts +++ b/desktop/packages/mullvad-vpn/src/renderer/hooks/index.ts @@ -7,7 +7,7 @@ export * from './useHasAppUpgradeVerifiedInstallerPath'; export * from './useIsDefaultActiveElementAfterMount'; export * from './useIsPlatformLinux'; export * from './useMeasure'; -export * from './useScrollToAnchor'; +export * from './useScrollToReference'; export * from './useScrollToListItem'; export * from './useInitialFocus'; export * from './useFocusReference'; diff --git a/desktop/packages/mullvad-vpn/src/renderer/hooks/useScrollToListItem.ts b/desktop/packages/mullvad-vpn/src/renderer/hooks/useScrollToListItem.ts index 91c1064d90..34b42cdfc5 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/hooks/useScrollToListItem.ts +++ b/desktop/packages/mullvad-vpn/src/renderer/hooks/useScrollToListItem.ts @@ -3,9 +3,10 @@ import React from 'react'; import { ScrollToAnchorId } from '../../shared/ipc-types'; import { ListItemAnimation } from '../lib/components/list-item'; import { useHistory } from '../lib/history'; -import { useScrollToReference } from '.'; +import { useFocusReference } from './useFocusReference'; +import { useScrollToReference } from './useScrollToReference'; -export const useScrollToListItem = <T extends Element = HTMLDivElement>( +export const useScrollToListItem = <T extends HTMLElement = HTMLDivElement>( id?: ScrollToAnchorId, ): { ref?: React.RefObject<T | null>; @@ -16,13 +17,13 @@ export const useScrollToListItem = <T extends Element = HTMLDivElement>( const { location } = history; const { state } = location; - const anchorId = state?.options?.find((option) => option.type === 'scroll-to-anchor')?.id; - const scroll = id === anchorId && anchorId !== undefined; + const scrollToAnchorOption = state?.options?.find((option) => option.type === 'scroll-to-anchor'); + const shouldScroll = scrollToAnchorOption && scrollToAnchorOption.id === id; const handleScrolled = React.useCallback(() => { const options = state?.options?.filter((option) => { if (option.type === 'scroll-to-anchor') { - return option.id !== anchorId; + return option.id !== scrollToAnchorOption?.id; } return true; @@ -32,11 +33,12 @@ export const useScrollToListItem = <T extends Element = HTMLDivElement>( ...state, options, }); - }, [anchorId, history, location, state]); + }, [history, location, scrollToAnchorOption?.id, state]); - useScrollToReference(ref, scroll, handleScrolled); + useScrollToReference(ref, shouldScroll, handleScrolled); + useFocusReference(ref, shouldScroll); - if (anchorId === undefined) { + if (scrollToAnchorOption === undefined) { return { ref: undefined, animation: undefined, @@ -44,6 +46,6 @@ export const useScrollToListItem = <T extends Element = HTMLDivElement>( } return { ref, - animation: scroll ? 'flash' : 'dim', + animation: shouldScroll ? 'flash' : 'dim', }; }; diff --git a/desktop/packages/mullvad-vpn/src/renderer/hooks/useScrollToAnchor.ts b/desktop/packages/mullvad-vpn/src/renderer/hooks/useScrollToReference.ts index fd8919edef..fd8919edef 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/hooks/useScrollToAnchor.ts +++ b/desktop/packages/mullvad-vpn/src/renderer/hooks/useScrollToReference.ts |
