refactor: improve a11y

This commit is contained in:
Haishan 2020-08-06 16:26:01 +08:00
parent 941224c13b
commit 2c9ee574dd
12 changed files with 111 additions and 45 deletions

View file

@ -4,14 +4,16 @@ parserOptions:
project: tsconfig.json
sourceType: module
plugins:
- simple-import-sort
- jsx-a11y
extends:
- 'plugin:@typescript-eslint/recommended'
- 'prettier'
- prettier/@typescript-eslint
- react-app
plugins:
- simple-import-sort
- 'plugin:jsx-a11y/recommended'
env:
node: true

View file

@ -78,6 +78,7 @@ function APIConfig({ apiConfig, dispatch }) {
);
return (
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
<div className={s0.root} ref={contentEl} onKeyDown={handleContentOnKeyDown}>
<div className={s0.header}>
<div className={s0.icon}>

View file

@ -2,10 +2,15 @@
display: flex;
align-items: center;
&:focus {
outline: none;
}
.arrow {
display: inline-flex;
transform: rotate(0deg);
transition: transform 0.3s;
&.isOpen {
transform: rotate(180deg);
}

View file

@ -1,23 +1,40 @@
import cx from 'clsx';
import React from 'react';
import * as React from 'react';
import { ChevronDown } from 'react-feather';
import { keyCodes } from '../misc/keycode';
import Button from './Button';
import s from './CollapsibleSectionHeader.module.css';
import { SectionNameType } from './shared/Basic';
type Props = {
name: string,
type: string,
qty?: number,
toggle?: () => void,
isOpen?: boolean,
name: string;
type: string;
qty?: number;
toggle?: () => void;
isOpen?: boolean;
};
export default function Header({ name, type, toggle, isOpen, qty }: Props) {
const handleKeyDown = React.useCallback(
(e: React.KeyboardEvent) => {
e.preventDefault();
if (e.keyCode === keyCodes.Enter || e.keyCode === keyCodes.Space) {
toggle();
}
},
[toggle]
);
return (
<div className={s.header}>
<div onClick={toggle} style={{ cursor: 'pointer' }}>
<div
className={s.header}
onClick={toggle}
style={{ cursor: 'pointer' }}
tabIndex={0}
onKeyDown={handleKeyDown}
role="button"
>
<div>
<SectionNameType name={name} type={type} />
</div>

View file

@ -11,21 +11,21 @@ export default function Selection({
onChange,
}) {
return (
// TODO a11y
// tabIndex="0"
<div className={s.root}>
{optionPropsList.map((props, idx) => {
const className = cx(s.item, { [s.itemActive]: idx === selectedIndex });
const doSelect = (ev) => {
ev.preventDefault();
if (idx !== selectedIndex) onChange(idx);
};
return (
<div
key={idx}
className={className}
onClick={(ev) => {
ev.preventDefault();
if (idx !== selectedIndex) {
onChange(idx);
}
}}
tabIndex={0}
role="menuitem"
onKeyDown={doSelect}
onClick={doSelect}
>
<OptionComponent {...props} />
</div>

View file

@ -90,6 +90,7 @@
justify-content: center;
align-items: center;
outline: none;
padding: 5px;
color: var(--color-text);
border-radius: 100%;
@ -104,7 +105,6 @@
.themeSwitchContainer {
appearance: none;
outline: none;
user-select: none;
background: none;
cursor: pointer;

View file

@ -29,6 +29,7 @@ export function ClosePrevConns({
};
return (
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
<div onKeyDown={handleKeyDown}>
<h2>Close Connections?</h2>
<p>

View file

@ -9,6 +9,12 @@
flex-direction: column;
justify-content: space-between;
outline: none;
border: 1px solid transparent;
&:focus {
border: 1px solid var(--color-focus-blue);
}
max-width: 280px;
@media (--breakpoint-not-small) {
min-width: 200px;

View file

@ -1,5 +1,6 @@
import cx from 'clsx';
import * as React from 'react';
import { keyCodes } from 'src/misc/keycode';
import { getDelay, getProxies, NonProxyTypes } from '../../store/proxies';
import { connect } from '../StateProvider';
@ -76,17 +77,35 @@ function ProxySmallImpl({
}
return ret;
}, [name, latency]);
const doSelect = React.useCallback(() => {
isSelectable && onClick && onClick(name);
}, [name, onClick, isSelectable]);
const className = useMemo(() => {
return cx(s0.proxySmall, {
[s0.now]: now,
[s0.selectable]: isSelectable,
});
}, [isSelectable, now]);
const handleKeyDown = React.useCallback(
(e: React.KeyboardEvent) => {
if (e.keyCode === keyCodes.Enter) {
doSelect();
}
},
[doSelect]
);
return (
<div
title={title}
className={cx(s0.proxySmall, {
[s0.now]: now,
[s0.selectable]: isSelectable,
})}
className={className}
style={{ background: color }}
onClick={() => {
isSelectable && onClick && onClick(name);
}}
onClick={doSelect}
onKeyDown={handleKeyDown}
role={isSelectable ? 'menuitem' : ''}
/>
);
}
@ -100,16 +119,32 @@ function ProxyImpl({
onClick,
}: ProxyProps) {
const color = useMemo(() => getLabelColor(latency), [latency]);
const doSelect = React.useCallback(() => {
isSelectable && onClick && onClick(name);
}, [name, onClick, isSelectable]);
const handleKeyDown = React.useCallback(
(e: React.KeyboardEvent) => {
if (e.keyCode === keyCodes.Enter) {
doSelect();
}
},
[doSelect]
);
const className = useMemo(() => {
return cx(s0.proxy, {
[s0.now]: now,
[s0.error]: latency && latency.error,
[s0.selectable]: isSelectable,
});
}, [isSelectable, now, latency]);
return (
<div
className={cx(s0.proxy, {
[s0.now]: now,
[s0.error]: latency && latency.error,
[s0.selectable]: isSelectable,
})}
onClick={() => {
isSelectable && onClick && onClick(name);
}}
tabIndex={0}
className={className}
onClick={doSelect}
onKeyDown={handleKeyDown}
role={isSelectable ? 'menuitem' : ''}
>
<div className={s0.proxyName}>{name}</div>
<div className={s0.row}>

View file

@ -10,6 +10,7 @@ type Props = {
export default function Select({ options, selected, onChange }: Props) {
return (
// eslint-disable-next-line jsx-a11y/no-onchange
<select className={s.select} value={selected} onChange={onChange}>
{options.map(([value, name]) => (
<option key={value} value={value}>

6
src/misc/keycode.ts Normal file
View file

@ -0,0 +1,6 @@
export const keyCodes = {
Right: 39,
Left: 37,
Enter: 13,
Space: 32,
};

View file

@ -102,14 +102,6 @@ module.exports = {
},
module: {
rules: [
// {
// test: /\.tsx?$/,
// loader: 'ts-loader',
// options: {
// // disable type checker - we will use it in fork plugin
// transpileOnly: true,
// },
// },
{
test: /\.[tj]sx?$/,
exclude: /node_modules/,
@ -126,7 +118,7 @@ module.exports = {
isDev ? { loader: 'style-loader' } : MiniCssExtractPlugin.loader,
{ loader: 'css-loader' },
{ loader: 'postcss-loader', options: { plugins: postcssPlugins } },
].filter(Boolean),
],
},
{
test: /\.module\.css$/,
@ -146,7 +138,7 @@ module.exports = {
loader: 'postcss-loader',
options: { plugins: postcssPlugins },
},
].filter(Boolean),
],
},
],
},