summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorTobias Järvelöv <tobias.jarvelov@mullvad.net>2025-11-03 17:23:57 +0100
committerTobias Järvelöv <tobias.jarvelov@mullvad.net>2025-11-03 17:23:57 +0100
commitb8c29b0ff18bd5d50981914841acdde2d44bf11f (patch)
tree16ee6c9054a83b3dabf94513b94fab7d77eb37db
parenteb9b4fdd8f0ab7031e2f3f21bfbca3cc98f988c1 (diff)
downloadmullvadvpn-poc-switch-component-form-label-integration-switches-not-focusable-des-1637.tar.xz
mullvadvpn-poc-switch-component-form-label-integration-switches-not-focusable-des-1637.zip
Proof of concept showing Switch component <-> form label integrationpoc-switch-component-form-label-integration-switches-not-focusable-des-1637
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/AriaGroup.tsx2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/ProxyForm.tsx19
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/cell/SettingsGroup.tsx3
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/switch/Switch.tsx3
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/switch/SwitchContext.tsx2
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/lib/components/switch/components/switch-trigger/SwitchTrigger.tsx17
6 files changed, 34 insertions, 12 deletions
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/AriaGroup.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/AriaGroup.tsx
index 59536e6109..eb91633ac5 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/AriaGroup.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/AriaGroup.tsx
@@ -56,6 +56,8 @@ const AriaInputContext = React.createContext<IAriaInputContext>({
},
});
+export const useAriaInputContext = () => useContext(AriaInputContext);
+
export function AriaInputGroup(props: IAriaGroupProps) {
const id = useId();
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/ProxyForm.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/ProxyForm.tsx
index 3d36fff9c9..d09c0d7393 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/ProxyForm.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/ProxyForm.tsx
@@ -14,6 +14,7 @@ import { FlexRow } from '../lib/components/flex-row';
import { Switch } from '../lib/components/switch';
import { IpAddress } from '../lib/ip';
import { useEffectEvent } from '../lib/utility-hooks';
+import { useAriaInputContext } from './AriaGroup';
import { SettingsForm, useSettingsFormSubmittable } from './cell/SettingsForm';
import { SettingsGroup } from './cell/SettingsGroup';
import { SettingsRadioGroup } from './cell/SettingsRadioGroup';
@@ -393,6 +394,18 @@ function EditSocks5Remote(props: EditProxyProps<Socks5RemoteCustomProxy>) {
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => onUpdate(ip, port, username, password), [ip, port, username, password]);
+ const SettingsAuthentication = () => {
+ const { inputId } = useAriaInputContext();
+
+ return (
+ <Switch id={inputId} checked={authentication} onCheckedChange={setAuthentication}>
+ <Switch.Trigger>
+ <Switch.Thumb />
+ </Switch.Trigger>
+ </Switch>
+ );
+ };
+
return (
<SettingsGroup title={messages.pgettext('api-access-methods-view', 'Remote Server')}>
<SettingsRow
@@ -424,11 +437,7 @@ function EditSocks5Remote(props: EditProxyProps<Socks5RemoteCustomProxy>) {
</SettingsRow>
<SettingsRow label={messages.pgettext('api-access-methods-view', 'Authentication')}>
- <Switch checked={authentication} onCheckedChange={setAuthentication}>
- <Switch.Trigger>
- <Switch.Thumb />
- </Switch.Trigger>
- </Switch>
+ <SettingsAuthentication />
</SettingsRow>
{authentication && (
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/cell/SettingsGroup.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/cell/SettingsGroup.tsx
index 777a610ef2..1cf1acd55d 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/cell/SettingsGroup.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/cell/SettingsGroup.tsx
@@ -27,6 +27,7 @@ const StyledInfoButton = styled(InfoButton)({
export const StyledSettingsGroup = styled.div({});
interface SettingsGroupContext {
+ key?: string;
setError?: (key: string, errorMessage: string) => void;
unsetError?: (key: string) => void;
}
@@ -48,7 +49,7 @@ export function useSettingsGroupContext() {
useEffect(() => () => unsetErrorImpl(), [unsetErrorImpl]);
- return { reportError, unsetError: unsetErrorImpl };
+ return { key, reportError, unsetError: unsetErrorImpl };
}
interface SettingsGroupProps {
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/switch/Switch.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/switch/Switch.tsx
index 8fe92377c9..7c222d2bb0 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/switch/Switch.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/switch/Switch.tsx
@@ -4,6 +4,7 @@ import { SwitchLabel, SwitchThumb, SwitchTrigger } from './components';
import { SwitchProvider } from './SwitchContext';
export interface SwitchProps {
+ id: string;
checked?: boolean;
onCheckedChange?: (checked: boolean) => void;
labelId?: string;
@@ -12,6 +13,7 @@ export interface SwitchProps {
}
function Switch({
+ id,
labelId: labelIdProp,
checked,
onCheckedChange,
@@ -21,6 +23,7 @@ function Switch({
const labelId = React.useId();
return (
<SwitchProvider
+ id={id}
labelId={labelIdProp ?? labelId}
checked={checked}
onCheckedChange={onCheckedChange}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/switch/SwitchContext.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/switch/SwitchContext.tsx
index 78520c0e9a..2a210d64d4 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/switch/SwitchContext.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/switch/SwitchContext.tsx
@@ -3,6 +3,7 @@ import React from 'react';
import { SwitchProps } from './Switch';
interface SwitchContextProps {
+ id: string;
labelId: string;
disabled: SwitchProps['disabled'];
checked?: SwitchProps['checked'];
@@ -20,6 +21,7 @@ export const useSwitchContext = (): SwitchContextProps => {
};
interface SwitchProviderProps {
+ id: string;
labelId: string;
disabled: SwitchProps['disabled'];
checked?: SwitchProps['checked'];
diff --git a/desktop/packages/mullvad-vpn/src/renderer/lib/components/switch/components/switch-trigger/SwitchTrigger.tsx b/desktop/packages/mullvad-vpn/src/renderer/lib/components/switch/components/switch-trigger/SwitchTrigger.tsx
index a14883e4f6..c369dad4c6 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/lib/components/switch/components/switch-trigger/SwitchTrigger.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/lib/components/switch/components/switch-trigger/SwitchTrigger.tsx
@@ -17,18 +17,23 @@ export const StyledSwitchTrigger = styled.button<{ $checked?: boolean }>`
`;
export function SwitchTrigger(props: SwitchTriggerProps) {
- const { labelId, checked, disabled, onCheckedChange } = useSwitchContext();
- const handleClick = React.useCallback(() => {
- if (onCheckedChange) {
- onCheckedChange(!checked);
- }
- }, [checked, onCheckedChange]);
+ const { id, labelId, checked, disabled, onCheckedChange } = useSwitchContext();
+ const handleClick = React.useCallback(
+ (event: React.MouseEvent) => {
+ event.preventDefault();
+ if (onCheckedChange) {
+ onCheckedChange(!checked);
+ }
+ },
+ [checked, onCheckedChange],
+ );
return (
<StyledSwitchTrigger
onClick={handleClick}
disabled={disabled}
role="switch"
+ id={id}
aria-checked={checked ? 'true' : 'false'}
aria-labelledby={labelId}
{...props}