diff options
| author | Oliver <oliver@mohlin.dev> | 2025-10-02 08:30:58 +0200 |
|---|---|---|
| committer | Tobias Järvelöv <tobias.jarvelov@mullvad.net> | 2025-10-07 11:35:59 +0200 |
| commit | 76bb0a13c190cf65d3acd749b55e4d336c5c5f5a (patch) | |
| tree | 400a1e976210724a61e69c3575989dab82d4477d | |
| parent | 1f18eece64ee462dc5ef2214ab1832c403db44f0 (diff) | |
| download | mullvadvpn-76bb0a13c190cf65d3acd749b55e4d336c5c5f5a.tar.xz mullvadvpn-76bb0a13c190cf65d3acd749b55e4d336c5c5f5a.zip | |
Select options via data-attribute instead of role
9 files changed, 30 insertions, 16 deletions
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/settings-listbox/components/input-option/components/input-option-trigger/InputOptionTrigger.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/settings-listbox/components/input-option/components/input-option-trigger/InputOptionTrigger.tsx index e7343ee11a..12e928876a 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/components/settings-listbox/components/input-option/components/input-option-trigger/InputOptionTrigger.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/components/settings-listbox/components/input-option/components/input-option-trigger/InputOptionTrigger.tsx @@ -63,6 +63,7 @@ export const InputOptionTrigger = ({ children, ...props }: InputOptionTriggerPro return ( <StyledInputOptionTrigger role="option" + data-option aria-selected={selected} tabIndex={-1} onFocus={handleFocus} diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/settings-listbox/components/split-option/components/split-option-item/SplitOptionItem.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/settings-listbox/components/split-option/components/split-option-item/SplitOptionItem.tsx index cbe431ad7e..0c1d362585 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/components/settings-listbox/components/split-option/components/split-option-item/SplitOptionItem.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/components/settings-listbox/components/split-option/components/split-option-item/SplitOptionItem.tsx @@ -4,7 +4,7 @@ export type ListBoxOptionWithNavigationProps = React.ComponentPropsWithRef<'li'> export function SplitOptionItem({ children, ...props }: ListBoxOptionWithNavigationProps) { return ( - <Listbox.Option.Trigger {...props}> + <Listbox.Option.Trigger data-option {...props}> <Listbox.Option.Item> <Listbox.Option.Content> <Listbox.Option.Group> diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/listbox/components/listbox-option/components/listbox-option-trigger/ListboxOptionTrigger.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/listbox/components/listbox-option/components/listbox-option-trigger/ListboxOptionTrigger.tsx index 383a0aac32..4c049ec695 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/listbox/components/listbox-option/components/listbox-option-trigger/ListboxOptionTrigger.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/listbox/components/listbox-option/components/listbox-option-trigger/ListboxOptionTrigger.tsx @@ -91,6 +91,7 @@ export const ListboxOptionTrigger = ({ children, ...props }: ListboxOptionTrigge return ( <StyledListItemOptionTrigger ref={triggerRef} + data-option role="option" aria-selected={selected} aria-disabled={disabled} diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/listbox/components/listbox-options/ListboxOptions.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/listbox/components/listbox-options/ListboxOptions.tsx index fbb97cd492..0a4d36ab40 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/listbox/components/listbox-options/ListboxOptions.tsx +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/listbox/components/listbox-options/ListboxOptions.tsx @@ -13,6 +13,7 @@ export function ListboxOptions({ children }: ListboxOptionsProps) { focusedIndex, optionsRef, setFocusedIndex, + selector: '[data-option="true"]:not([aria-disabled="true"])', }); const onKeyDown = React.useCallback( diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/hooks/use-focus-option-by-index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/hooks/use-focus-option-by-index.ts index bde967f9e9..bc60b0bb70 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/lib/hooks/use-focus-option-by-index.ts +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/hooks/use-focus-option-by-index.ts @@ -5,17 +5,19 @@ import { getOptions } from '../utils'; export const useFocusOptionByIndex = <T extends HTMLElement>({ optionsRef, setFocusedIndex, + selector, }: { optionsRef: React.RefObject<T | null>; setFocusedIndex: (index: number) => void; + selector: string; }) => { return React.useCallback( (index: number) => { - const options = getOptions(optionsRef.current); + const options = getOptions(optionsRef.current, selector); setFocusedIndex(index); const option = options[index]; option.focus(); }, - [optionsRef, setFocusedIndex], + [optionsRef, selector, setFocusedIndex], ); }; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/hooks/use-get-initial-focus-index.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/hooks/use-get-initial-focus-index.ts index bebd0459bf..3744725990 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/lib/hooks/use-get-initial-focus-index.ts +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/hooks/use-get-initial-focus-index.ts @@ -5,12 +5,14 @@ import { getOptions, getSelectedOptionIndex } from '../utils'; export const useGetInitialFocusIndex = <T extends HTMLElement>({ focusedIndex, optionsRef, + selector, }: { focusedIndex?: number; optionsRef: React.RefObject<T | null>; + selector: string; }) => { return React.useCallback(() => { - const options = getOptions(optionsRef.current); + const options = getOptions(optionsRef.current, selector); if (focusedIndex !== undefined) { return focusedIndex; } @@ -19,5 +21,5 @@ export const useGetInitialFocusIndex = <T extends HTMLElement>({ return selectedOptionIndex; } return 0; - }, [focusedIndex, optionsRef]); + }, [focusedIndex, optionsRef, selector]); }; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/hooks/use-handle-keyboard-navigation.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/hooks/use-handle-keyboard-navigation.ts index 82a74cfe4c..c42d4978df 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/lib/hooks/use-handle-keyboard-navigation.ts +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/hooks/use-handle-keyboard-navigation.ts @@ -4,23 +4,26 @@ import { getOptions } from '../utils'; import { useFocusOptionByIndex } from './use-focus-option-by-index'; import { useGetInitialFocusIndex } from './use-get-initial-focus-index'; -type Orientation = 'horizontal' | 'vertical'; +export type Orientation = 'horizontal' | 'vertical'; export const useHandleKeyboardNavigation = <T extends HTMLElement>({ optionsRef, focusedIndex, setFocusedIndex, orientation = 'vertical', + selector, }: { optionsRef: React.RefObject<T | null>; focusedIndex?: number; setFocusedIndex: (index: number) => void; orientation?: Orientation; + selector: string; }) => { - const getInitialFocusIndex = useGetInitialFocusIndex({ optionsRef, focusedIndex }); + const getInitialFocusIndex = useGetInitialFocusIndex({ optionsRef, focusedIndex, selector }); const focusOptionByIndex = useFocusOptionByIndex({ optionsRef, setFocusedIndex, + selector, }); const nextKey = orientation === 'vertical' ? 'ArrowDown' : 'ArrowRight'; @@ -28,7 +31,7 @@ export const useHandleKeyboardNavigation = <T extends HTMLElement>({ return React.useCallback( (event: React.KeyboardEvent) => { - const options = getOptions(optionsRef.current); + const options = getOptions(optionsRef.current, selector); const initialFocusedIndex = getInitialFocusIndex(); @@ -52,6 +55,6 @@ export const useHandleKeyboardNavigation = <T extends HTMLElement>({ focusOptionByIndex(options.length - 1); } }, - [focusOptionByIndex, getInitialFocusIndex, nextKey, optionsRef, previousKey], + [focusOptionByIndex, getInitialFocusIndex, nextKey, optionsRef, previousKey, selector], ); }; diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/hooks/use-options.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/hooks/use-options.ts index a1f41a28db..8f673c9765 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/lib/hooks/use-options.ts +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/hooks/use-options.ts @@ -1,25 +1,29 @@ import React from 'react'; import { getInitialOption, getOptions } from '../utils'; -import { useHandleKeyboardNavigation } from './use-handle-keyboard-navigation'; +import { Orientation, useHandleKeyboardNavigation } from './use-handle-keyboard-navigation'; export type UseOptionsProps<T extends HTMLElement> = { optionsRef: React.RefObject<T | null>; focusedIndex?: number; setFocusedIndex: React.Dispatch<React.SetStateAction<number | undefined>>; + selector: string; + orientation?: Orientation; }; export function useOptions<T extends HTMLElement>({ optionsRef, focusedIndex, setFocusedIndex, + selector, + orientation = 'vertical', }: UseOptionsProps<T>) { const [tabIndex, setTabIndex] = React.useState<number>(0); const handleFocus = React.useCallback( (event: React.FocusEvent) => { if (!optionsRef.current?.isSameNode(event.target)) return; - const options = getOptions(optionsRef.current); + const options = getOptions(optionsRef.current, selector); const initialOption = getInitialOption(options); if (initialOption) { @@ -28,13 +32,15 @@ export function useOptions<T extends HTMLElement>({ initialOption.focus(); } }, - [optionsRef], + [optionsRef, selector], ); const handleKeyboardNavigation = useHandleKeyboardNavigation({ optionsRef, setFocusedIndex, focusedIndex, + selector, + orientation, }); const handleBlur = React.useCallback( diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/utils/get-options.ts b/desktop/packages/mullvad-vpn/src/renderer/lib/utils/get-options.ts index d37647936c..0754a72631 100644 --- a/desktop/packages/mullvad-vpn/src/renderer/lib/utils/get-options.ts +++ b/desktop/packages/mullvad-vpn/src/renderer/lib/utils/get-options.ts @@ -1,7 +1,5 @@ -export const getOptions = (container: HTMLElement | null) => { - const options = container?.querySelectorAll<HTMLElement>( - '[role="option"]:not([aria-disabled="true"])', - ); +export const getOptions = (container: HTMLElement | null, selector: string) => { + const options = container?.querySelectorAll<HTMLElement>(selector); if (options) { return Array.from(options); |
