From d6253b0dd7b2ab882c864c16413a5c67bb8a6849 Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 30 Aug 2024 14:36:37 +0900 Subject: [PATCH 1/3] Dreft for a11y --- package-lock.json | 2 +- src/components/inputs/accent-button.tsx | 3 + src/components/inputs/accent-slider.tsx | 3 + .../inputs/accent-upload-button.tsx | 4 +- src/components/menus/global.tsx | 5 +- .../panes/configure-panes/keycode.tsx | 73 +++++++++++++++++-- .../panes/configure-panes/layer-control.tsx | 45 ++++++++++-- .../panes/configure-panes/save-load.tsx | 10 ++- src/components/panes/configure.tsx | 37 ++++++++-- src/components/panes/grid.tsx | 5 +- src/components/panes/settings.tsx | 15 ++-- src/components/two-string/unit-key/keycap.tsx | 17 +++++ 12 files changed, 186 insertions(+), 33 deletions(-) diff --git a/package-lock.json b/package-lock.json index 81a199b9..816357e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "via-app", + "name": "app", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/src/components/inputs/accent-button.tsx b/src/components/inputs/accent-button.tsx index d565e1fa..a79a4860 100644 --- a/src/components/inputs/accent-button.tsx +++ b/src/components/inputs/accent-button.tsx @@ -36,6 +36,9 @@ export const AccentButton = styled(AccentButtonBase)` &:hover { filter: brightness(0.7); } + &:focus { + filter: brightness(0.7); + } `; export const AccentButtonLarge = styled(AccentButton)` font-size: 24px; diff --git a/src/components/inputs/accent-slider.tsx b/src/components/inputs/accent-slider.tsx index 3f0cb635..5e5cdec1 100644 --- a/src/components/inputs/accent-slider.tsx +++ b/src/components/inputs/accent-slider.tsx @@ -43,6 +43,7 @@ const Slider = styled.span<{$ischecked?: boolean}>` type Props = { isChecked: boolean; + id:string onChange: (val: boolean) => void; }; @@ -70,6 +71,8 @@ export function AccentSlider(props: Props) { ; children: string; + describedby?: string; + description?: string; }; export function AccentUploadButton(props: Props) { @@ -14,7 +16,7 @@ export function AccentUploadButton(props: Props) { (input.current as any).value = null; } return ( - input.current && input.current.click()}> + input.current && input.current.click()}> {props.children} { if (pane.key === 'debug' && !showDebugPane) return null; return ( - + {pane.title} diff --git a/src/components/panes/configure-panes/keycode.tsx b/src/components/panes/configure-panes/keycode.tsx index 595b369a..78fe699a 100644 --- a/src/components/panes/configure-panes/keycode.tsx +++ b/src/components/panes/configure-panes/keycode.tsx @@ -1,4 +1,4 @@ -import {FC, useState, useEffect, useMemo} from 'react'; +import {FC, useState, useEffect, useMemo, useRef} from 'react'; import styled from 'styled-components'; import {Button} from '../../inputs/button'; import {KeycodeModal} from '../../inputs/custom-keycode-modal'; @@ -73,6 +73,10 @@ const Keycode = styled(Button)<{disabled: boolean}>` border-color: var(--color_accent); transform: translate3d(0, -2px, 0); } + &:focus { + border-color: var(--color_accent); + transform: translate3d(0, -2px, 0); + } ${(props: any) => props.disabled && `cursor:not-allowed;filter:opacity(50%);`} `; @@ -213,13 +217,44 @@ export const KeycodePane: FC = () => { }; const renderCategories = () => { + const [focusedTab,setTabFocus] = useState(0); + const [didMount,mount] = useState(false); + const categoryTabRefs = useRef([]); + useEffect(()=>{didMount || mount(true)}); + useEffect(()=>{ + if (didMount){ + categoryTabRefs.current[focusedTab]?.focus(); + categoryTabRefs.current[focusedTab]?.click(); + categoryTabRefs.current[focusedTab]?.scrollIntoView({block:"nearest"}); + } + },[focusedTab]); + const availableMenus = getEnabledMenus(); + return ( - - {getEnabledMenus().map(({id, label}) => ( + + {availableMenus.map(({id, label},idx) => ( { + switch(e.code) { + case "ArrowUp": + case "ArrowLeft": + e.preventDefault(); + setTabFocus(idx - 1 > -1 ? idx - 1 : availableMenus.length-1 ) + break; + case "ArrowDown": + case "ArrowRight": + e.preventDefault(); + setTabFocus(idx + 1 < availableMenus.length ? idx + 1 : 0) + break; + } + }} + ref={(r)=>{ categoryTabRefs.current[idx] = r! }} $selected={id === selectedCategory} onClick={() => setSelectedCategory(id)} + aria-selected={id === selectedCategory} key={id} + tabIndex={id === selectedCategory ? 0 : -1} + role='tab' > {label} @@ -279,10 +314,22 @@ export const KeycodePane: FC = () => { { + switch(e.code){ + case "Space": + case "Enter": + e.currentTarget.click(); + break; + } + }} onClick={() => handleClick(code, index)} onMouseOver={() => setMouseOverDesc(title ? `${code}: ${title}` : code)} onMouseOut={() => setMouseOverDesc(null)} - > + role='button' + tabIndex={0} + aria-label={name ? name : "Nothing"} + aria-roledescription={title ? `${code}: ${title}` : code}> + {name} ); @@ -291,6 +338,16 @@ export const KeycodePane: FC = () => { const renderCustomKeycode = () => { return ( { + switch(e.code){ + case "Space": + case "Enter": + e.currentTarget.click(); + break; + } + }} + role='button' + tabIndex={0} key="customKeycode" onClick={() => selectedKey !== null && handleClick('text', 0)} onMouseOver={() => setMouseOverDesc('Enter any QMK Keycode')} @@ -313,12 +370,12 @@ export const KeycodePane: FC = () => { return !macros.isFeatureSupported ? ( renderMacroError() ) : ( - {keycodeListItems} + {keycodeListItems} ); } case 'special': { return ( - + {keycodeListItems.concat(renderCustomKeycode())} ); @@ -332,7 +389,7 @@ export const KeycodePane: FC = () => { return null; } return ( - + {selectedDefinition.customKeycodes.map((keycode, idx) => { return renderKeycode( { @@ -346,7 +403,7 @@ export const KeycodePane: FC = () => { ); } default: { - return {keycodeListItems}; + return {keycodeListItems}; } } }; diff --git a/src/components/panes/configure-panes/layer-control.tsx b/src/components/panes/configure-panes/layer-control.tsx index 6f36e8a2..eaaee2d4 100644 --- a/src/components/panes/configure-panes/layer-control.tsx +++ b/src/components/panes/configure-panes/layer-control.tsx @@ -1,4 +1,4 @@ -import {useMemo} from 'react'; +import {useEffect, useMemo, useRef, useState} from 'react'; import {useDispatch} from 'react-redux'; import {useAppSelector} from 'src/store/hooks'; import { @@ -21,7 +21,6 @@ const Label = styled.label` margin-right: 6px; `; const LayerButton = styled.button<{$selected?: boolean}>` - outline: none; font-variant-numeric: tabular-nums; border: none; background: ${(props) => @@ -39,24 +38,56 @@ const LayerButton = styled.button<{$selected?: boolean}>` color: ${(props) => props.$selected ? 'auto' : 'var(--color_label-highlighted)'}; } + &:focus { + border: none; + background: ${(props) => (props.$selected ? 'auto' : 'var(--bg_menu)')}; + color: ${(props) => + props.$selected ? 'auto' : 'var(--color_label-highlighted)'}; + } `; export const LayerControl = () => { const dispatch = useDispatch(); + const [layerTabFocus,setLayerTabFocus] = useState(0); + const [didMount,mount] = useState(false); + const layerTabRef = useRef([]); const numberOfLayers = useAppSelector(getNumberOfLayers); const selectedLayerIndex = useAppSelector(getSelectedLayerIndex); - + + useEffect(()=>{didMount || mount(true);}); + useEffect(()=>{ + if(didMount) { + layerTabRef.current[layerTabFocus]?.focus(); + layerTabRef.current[layerTabFocus]?.click(); + } + },[layerTabFocus]) const Layers = useMemo( () => new Array(numberOfLayers) .fill(0) .map((_, idx) => idx) - .map((layerLabel) => ( + .map((layerLabel,idx) => ( { + layerTabRef.current[idx] = r! + + }} + role='tab' + onKeyDown={(e)=>{ + switch(e.code) { + case "ArrowUp": + case "ArrowLeft": + setLayerTabFocus(idx - 1 > -1 ? idx - 1 : numberOfLayers-1 ) + break; + case "ArrowDown": + case "ArrowRight": + setLayerTabFocus(idx + 1 < numberOfLayers ? idx + 1 : 0) + break; + } + }} key={layerLabel} $selected={layerLabel === selectedLayerIndex} - onClick={() => dispatch(setLayer(layerLabel))} - > + onClick={() => dispatch(setLayer(layerLabel))}> {layerLabel} )), @@ -64,7 +95,7 @@ export const LayerControl = () => { ); return ( - + {Layers} diff --git a/src/components/panes/configure-panes/save-load.tsx b/src/components/panes/configure-panes/save-load.tsx index 7677254f..833ddf49 100644 --- a/src/components/panes/configure-panes/save-load.tsx +++ b/src/components/panes/configure-panes/save-load.tsx @@ -247,15 +247,17 @@ export const Pane: FC = () => { - + - Save + Save - + - Load + Load {errorMessage ? {errorMessage} : null} diff --git a/src/components/panes/configure.tsx b/src/components/panes/configure.tsx index 2167822f..023e6579 100644 --- a/src/components/panes/configure.tsx +++ b/src/components/panes/configure.tsx @@ -1,4 +1,4 @@ -import React, {useState, useEffect} from 'react'; +import React, {useState, useEffect, useRef} from 'react'; import {faPlus} from '@fortawesome/free-solid-svg-icons'; import styled from 'styled-components'; import ChippyLoader from '../chippy-loader'; @@ -219,7 +219,9 @@ const ConfigureGrid = () => { const KeyboardRows = getRowsForKeyboard(); const SelectedPane = KeyboardRows[selectedRow]?.Pane; const selectedTitle = KeyboardRows[selectedRow]?.Title; - + const [didMount,mount] = useState(false); + const [focusedTab,setTabFocus] = useState(0); + const tabRefs = useRef([]); useEffect(() => { if (selectedTitle !== 'Keymap') { dispatch(setConfigureKeyboardIsSelectable(false)); @@ -227,7 +229,16 @@ const ConfigureGrid = () => { dispatch(setConfigureKeyboardIsSelectable(true)); } }, [selectedTitle]); + + useEffect(()=>{didMount || mount(true);}) + useEffect(()=>{ // keyboard navigation + if (didMount) { + tabRefs.current[focusedTab]?.focus(); + tabRefs.current[focusedTab]?.click(); + } + },[focusedTab]); + const menuItems = (KeyboardRows || []) return ( <> { - - {(KeyboardRows || []).map( + + {menuItems.map( ({Icon, Title}: {Icon: any; Title: string}, idx: number) => ( { + switch(e.code) { + case "ArrowUp": + case "ArrowLeft": + setTabFocus(idx - 1 > -1 ? idx - 1 : menuItems.length-1 ) + break; + case "ArrowDown": + case "ArrowRight": + setTabFocus(idx + 1 < menuItems.length ? idx + 1 : 0) + break; + } + }} + ref={(r)=>{tabRefs.current[idx] = r!;}} + aria-selected={selectedRow === idx} + tabIndex={selectedRow === idx ? 0 : -1} onClick={(_) => setRow(idx)} $selected={selectedRow === idx} > @@ -272,4 +299,4 @@ const ConfigureGrid = () => { ); -}; +}; \ No newline at end of file diff --git a/src/components/panes/grid.tsx b/src/components/panes/grid.tsx index 0a1f1790..7017d1c0 100644 --- a/src/components/panes/grid.tsx +++ b/src/components/panes/grid.tsx @@ -165,4 +165,7 @@ export const SubmenuRow = styled(Row)` color: ${(props) => props.$selected ? 'var(--color_label-highlighted)' : 'var(--color_label)'}; border-radius: 12px; -`; + &:focus { + outline:auto; + } +`; \ No newline at end of file diff --git a/src/components/panes/settings.tsx b/src/components/panes/settings.tsx index 119f4587..65d94813 100644 --- a/src/components/panes/settings.tsx +++ b/src/components/panes/settings.tsx @@ -1,4 +1,4 @@ -import {useState} from 'react'; +import {useEffect, useRef, useState} from 'react'; import {Pane} from './pane'; import styled from 'styled-components'; import { @@ -87,12 +87,14 @@ export const Settings = () => { const renderModeDefaultValue = renderModeOptions.find( (opt) => opt.value === renderMode, ); + return ( - + General @@ -103,27 +105,30 @@ export const Settings = () => { - + dispatch(toggleCreatorMode())} isChecked={showDesignTab} /> - + dispatch(toggleFastRemap())} isChecked={!disableFastRemap} /> - + dispatch(toggleThemeMode())} isChecked={themeMode === 'light'} /> diff --git a/src/components/two-string/unit-key/keycap.tsx b/src/components/two-string/unit-key/keycap.tsx index 027adc1c..b3917c8d 100644 --- a/src/components/two-string/unit-key/keycap.tsx +++ b/src/components/two-string/unit-key/keycap.tsx @@ -301,6 +301,7 @@ export const Keycap: React.FC = React.memo((props) => { idx, mode, ]); + return shouldRotate ? ( = React.memo((props) => { ) : ( <> { + switch(e.code) { + case "Enter": + case "Space": + e.preventDefault(); + e.currentTarget.click(); + break; + } + }} onPointerDown={onPointerDown} onPointerOver={onPointerOver} onPointerOut={onPointerOut} + style={{ transform: `translate(${ CSSVarObject.keyWidth / 2 + From ab4ea62f81d728f6a242ff979c0e1464fe8443fa Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 30 Aug 2024 14:43:45 +0900 Subject: [PATCH 2/3] hotfix --- src/components/panes/settings.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/panes/settings.tsx b/src/components/panes/settings.tsx index 65d94813..bc27d16a 100644 --- a/src/components/panes/settings.tsx +++ b/src/components/panes/settings.tsx @@ -159,11 +159,12 @@ export const Settings = () => { - + {selectedDevice ? ( setShowDiagnostics(!showDiagnostics)} isChecked={showDiagnostics} /> From 886a2cbcc71f70d3bdcd2e278756637bfae8abc8 Mon Sep 17 00:00:00 2001 From: = <=> Date: Tue, 3 Sep 2024 08:37:56 +0900 Subject: [PATCH 3/3] hotfix2 --- src/components/inputs/accent-slider.tsx | 2 +- src/components/panes/debug.tsx | 7 ++++--- src/components/panes/design.tsx | 7 ++++--- src/components/panes/test.tsx | 6 ++++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/components/inputs/accent-slider.tsx b/src/components/inputs/accent-slider.tsx index 5e5cdec1..84d8c87f 100644 --- a/src/components/inputs/accent-slider.tsx +++ b/src/components/inputs/accent-slider.tsx @@ -43,7 +43,7 @@ const Slider = styled.span<{$ischecked?: boolean}>` type Props = { isChecked: boolean; - id:string + id?:string onChange: (val: boolean) => void; }; diff --git a/src/components/panes/debug.tsx b/src/components/panes/debug.tsx index 65fb8165..56294065 100644 --- a/src/components/panes/debug.tsx +++ b/src/components/panes/debug.tsx @@ -186,9 +186,9 @@ const TestControls = () => { - + - + @@ -266,9 +266,10 @@ export const Debug: FC = () => { Key Testing - + setShowMatrix(val)} /> diff --git a/src/components/panes/design.tsx b/src/components/panes/design.tsx index e0873eae..25ce510f 100644 --- a/src/components/panes/design.tsx +++ b/src/components/panes/design.tsx @@ -325,9 +325,10 @@ export const DesignTab: FC = () => { - + dispatch(updateDesignDefinitionVersion(val ? 'v2' : 'v3')) @@ -369,9 +370,9 @@ export const DesignTab: FC = () => { )} {definition && ( - + - { dispatch(updateShowMatrix(val)); diff --git a/src/components/panes/test.tsx b/src/components/panes/test.tsx index e6514e63..c0d0a1ee 100644 --- a/src/components/panes/test.tsx +++ b/src/components/panes/test.tsx @@ -147,9 +147,10 @@ export const Test: FC = () => { {canUseMatrixState && selectedDefinition ? ( - + { dispatch(setTestMatrixEnabled(val)); @@ -160,9 +161,10 @@ export const Test: FC = () => { ) : null} - + { dispatch(