Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature / Selectable A11y] Improve basic use case a11y #3070

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
"@types/react-beautiful-dnd": "^10.1.0",
"@types/react-input-autosize": "^2.0.2",
"@types/react-virtualized": "^9.18.7",
"@types/react-virtualized-auto-sizer": "^1.0.0",
"@types/react-window": "^1.8.1",
"chroma-js": "^2.0.4",
"classnames": "^2.2.5",
"highlight.js": "^9.12.0",
Expand All @@ -67,6 +69,8 @@
"react-input-autosize": "^2.2.2",
"react-is": "~16.3.0",
"react-virtualized": "^9.21.2",
"react-virtualized-auto-sizer": "^1.0.2",
"react-window": "^1.8.5",
"resize-observer-polyfill": "^1.5.0",
"tabbable": "^3.0.0",
"uuid": "^3.1.0"
Expand Down
42 changes: 23 additions & 19 deletions src/components/selectable/selectable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import React, {
createRef,
Fragment,
ReactElement,
KeyboardEvent,
} from 'react';
import classNames from 'classnames';
import { CommonProps, ExclusiveUnion } from '../common';
Expand All @@ -14,13 +15,9 @@ import { EuiSelectableList } from './selectable_list';
import { EuiLoadingChart } from '../loading';
import { getMatchingOptions } from './matching_options';
import { comboBoxKeyCodes } from '../../services';
import { TAB } from '../../services/key_codes';
import { EuiI18n } from '../i18n';
import { EuiSelectableOption } from './selectable_option';
import {
EuiSelectableOptionsListProps,
EuiSelectableSingleOptionProps,
} from './selectable_list/selectable_list';
import { EuiSelectableOptionsListProps } from './selectable_list/selectable_list';

type RequiredEuiSelectableOptionsListProps = Omit<
EuiSelectableOptionsListProps,
Expand Down Expand Up @@ -80,7 +77,7 @@ export type EuiSelectableProps = Omit<
* `true`: only allows one selection
* `always`: can and must have only one selection
*/
singleSelection?: EuiSelectableSingleOptionProps;
singleSelection?: EuiSelectableOptionsListProps['singleSelection'];
/**
* Allows marking options as `checked='off'` as well as `'on'`
*/
Expand Down Expand Up @@ -174,7 +171,19 @@ export class EuiSelectable extends Component<
return this.state.activeOptionIndex != null;
};

onKeyDown = (e: any) => {
onFocus = () => {
const firstSelected = this.state.visibleOptions.findIndex(
option => option.checked && !option.disabled
);

if (firstSelected > -1) {
this.setState({ activeOptionIndex: firstSelected });
} else {
this.incrementActiveOptionIndex(1);
}
};

onKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {
const optionsList = this.optionsListRef.current;

switch (e.keyCode) {
Expand All @@ -199,15 +208,6 @@ export class EuiSelectable extends Component<
}
break;

case TAB:
// Disallow tabbing when the user is navigating the options.
// TODO: Can we force the tab to the next sibling element?
if (this.hasActiveOption()) {
e.preventDefault();
e.stopPropagation();
}
break;

default:
if (this.props.onKeyDown) {
this.props.onKeyDown(e);
Expand Down Expand Up @@ -239,10 +239,12 @@ export class EuiSelectable extends Component<
}
}

// Group titles are included in option list but are not selectable
// Skip group title options
// Group titles and disabled options are included in option list but are not selectable
const direction = amount > 0 ? 1 : -1;
while (visibleOptions[nextActiveOptionIndex].isGroupLabel) {
while (
visibleOptions[nextActiveOptionIndex].isGroupLabel ||
visibleOptions[nextActiveOptionIndex].disabled
) {
nextActiveOptionIndex = nextActiveOptionIndex + direction;

if (nextActiveOptionIndex < 0) {
Expand Down Expand Up @@ -377,6 +379,7 @@ export class EuiSelectable extends Component<
renderOption={renderOption}
height={height}
allowExclusions={allowExclusions}
searchable={searchable}
{...listProps}
/>
);
Expand All @@ -386,6 +389,7 @@ export class EuiSelectable extends Component<
className={classes}
onKeyDown={this.onKeyDown}
onBlur={this.onContainerBlur}
onFocus={this.onFocus}
{...rest}>
{children && children(list, search)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,7 @@ exports[`EuiSelectableListItem is rendered 1`] = `
>
<div
style="overflow:visible;width:0"
>
<div
aria-label="grid"
aria-readonly="true"
class="ReactVirtualized__Grid ReactVirtualized__List euiSelectableList__list"
id="htmlId"
role="listbox"
style="box-sizing:border-box;direction:ltr;height:192px;position:relative;width:0;-webkit-overflow-scrolling:touch;will-change:transform;overflow-x:hidden;overflow-y:hidden"
tabindex="0"
/>
</div>
/>
</div>
`;

Expand All @@ -28,17 +18,7 @@ exports[`EuiSelectableListItem props activeOptionIndex 1`] = `
>
<div
style="overflow:visible;width:0"
>
<div
aria-label="grid"
aria-readonly="true"
class="ReactVirtualized__Grid ReactVirtualized__List euiSelectableList__list"
id="htmlId"
role="listbox"
style="box-sizing:border-box;direction:ltr;height:192px;position:relative;width:0;-webkit-overflow-scrolling:touch;will-change:transform;overflow-x:hidden;overflow-y:hidden"
tabindex="0"
/>
</div>
/>
</div>
`;

Expand All @@ -48,17 +28,7 @@ exports[`EuiSelectableListItem props allowExclusions 1`] = `
>
<div
style="overflow:visible;width:0"
>
<div
aria-label="grid"
aria-readonly="true"
class="ReactVirtualized__Grid ReactVirtualized__List euiSelectableList__list"
id="htmlId"
role="listbox"
style="box-sizing:border-box;direction:ltr;height:192px;position:relative;width:0;-webkit-overflow-scrolling:touch;will-change:transform;overflow-x:hidden;overflow-y:hidden"
tabindex="0"
/>
</div>
/>
</div>
`;

Expand All @@ -68,17 +38,7 @@ exports[`EuiSelectableListItem props bordered 1`] = `
>
<div
style="overflow:visible;width:0"
>
<div
aria-label="grid"
aria-readonly="true"
class="ReactVirtualized__Grid ReactVirtualized__List euiSelectableList__list"
id="htmlId"
role="listbox"
style="box-sizing:border-box;direction:ltr;height:192px;position:relative;width:0;-webkit-overflow-scrolling:touch;will-change:transform;overflow-x:hidden;overflow-y:hidden"
tabindex="0"
/>
</div>
/>
</div>
`;

Expand All @@ -88,17 +48,7 @@ exports[`EuiSelectableListItem props height is forced 1`] = `
>
<div
style="overflow:visible;width:0"
>
<div
aria-label="grid"
aria-readonly="true"
class="ReactVirtualized__Grid ReactVirtualized__List euiSelectableList__list"
id="htmlId"
role="listbox"
style="box-sizing:border-box;direction:ltr;height:200px;position:relative;width:0;-webkit-overflow-scrolling:touch;will-change:transform;overflow-x:hidden;overflow-y:hidden"
tabindex="0"
/>
</div>
/>
</div>
`;

Expand All @@ -108,17 +58,7 @@ exports[`EuiSelectableListItem props height is full 1`] = `
>
<div
style="overflow:visible;height:0;width:0"
>
<div
aria-label="grid"
aria-readonly="true"
class="ReactVirtualized__Grid ReactVirtualized__List euiSelectableList__list"
id="htmlId"
role="listbox"
style="box-sizing:border-box;direction:ltr;height:0;position:relative;width:0;-webkit-overflow-scrolling:touch;will-change:transform;overflow-x:hidden;overflow-y:auto"
tabindex="0"
/>
</div>
/>
</div>
`;

Expand All @@ -128,17 +68,7 @@ exports[`EuiSelectableListItem props renderOption 1`] = `
>
<div
style="overflow:visible;width:0"
>
<div
aria-label="grid"
aria-readonly="true"
class="ReactVirtualized__Grid ReactVirtualized__List euiSelectableList__list"
id="htmlId"
role="listbox"
style="box-sizing:border-box;direction:ltr;height:192px;position:relative;width:0;-webkit-overflow-scrolling:touch;will-change:transform;overflow-x:hidden;overflow-y:hidden"
tabindex="0"
/>
</div>
/>
</div>
`;

Expand All @@ -148,17 +78,7 @@ exports[`EuiSelectableListItem props rowHeight 1`] = `
>
<div
style="overflow:visible;width:0"
>
<div
aria-label="grid"
aria-readonly="true"
class="ReactVirtualized__Grid ReactVirtualized__List euiSelectableList__list"
id="htmlId"
role="listbox"
style="box-sizing:border-box;direction:ltr;height:120px;position:relative;width:0;-webkit-overflow-scrolling:touch;will-change:transform;overflow-x:hidden;overflow-y:hidden"
tabindex="0"
/>
</div>
/>
</div>
`;

Expand All @@ -168,17 +88,7 @@ exports[`EuiSelectableListItem props searchValue 1`] = `
>
<div
style="overflow:visible;width:0"
>
<div
aria-label="grid"
aria-readonly="true"
class="ReactVirtualized__Grid ReactVirtualized__List euiSelectableList__list"
id="htmlId"
role="listbox"
style="box-sizing:border-box;direction:ltr;height:192px;position:relative;width:0;-webkit-overflow-scrolling:touch;will-change:transform;overflow-x:hidden;overflow-y:hidden"
tabindex="0"
/>
</div>
/>
</div>
`;

Expand All @@ -188,17 +98,7 @@ exports[`EuiSelectableListItem props searchValue 2`] = `
>
<div
style="overflow:visible;width:0"
>
<div
aria-label="grid"
aria-readonly="true"
class="ReactVirtualized__Grid ReactVirtualized__List euiSelectableList__list"
id="htmlId"
role="listbox"
style="box-sizing:border-box;direction:ltr;height:192px;position:relative;width:0;-webkit-overflow-scrolling:touch;will-change:transform;overflow-x:hidden;overflow-y:hidden"
tabindex="0"
/>
</div>
/>
</div>
`;

Expand All @@ -208,17 +108,7 @@ exports[`EuiSelectableListItem props showIcons can be turned off 1`] = `
>
<div
style="overflow:visible;width:0"
>
<div
aria-label="grid"
aria-readonly="true"
class="ReactVirtualized__Grid ReactVirtualized__List euiSelectableList__list"
id="htmlId"
role="listbox"
style="box-sizing:border-box;direction:ltr;height:192px;position:relative;width:0;-webkit-overflow-scrolling:touch;will-change:transform;overflow-x:hidden;overflow-y:hidden"
tabindex="0"
/>
</div>
/>
</div>
`;

Expand All @@ -228,17 +118,7 @@ exports[`EuiSelectableListItem props singleSelection can be forced so that at le
>
<div
style="overflow:visible;width:0"
>
<div
aria-label="grid"
aria-readonly="true"
class="ReactVirtualized__Grid ReactVirtualized__List euiSelectableList__list"
id="htmlId"
role="listbox"
style="box-sizing:border-box;direction:ltr;height:192px;position:relative;width:0;-webkit-overflow-scrolling:touch;will-change:transform;overflow-x:hidden;overflow-y:hidden"
tabindex="0"
/>
</div>
/>
</div>
`;

Expand All @@ -248,17 +128,7 @@ exports[`EuiSelectableListItem props singleSelection can be turned on 1`] = `
>
<div
style="overflow:visible;width:0"
>
<div
aria-label="grid"
aria-readonly="true"
class="ReactVirtualized__Grid ReactVirtualized__List euiSelectableList__list"
id="htmlId"
role="listbox"
style="box-sizing:border-box;direction:ltr;height:192px;position:relative;width:0;-webkit-overflow-scrolling:touch;will-change:transform;overflow-x:hidden;overflow-y:hidden"
tabindex="0"
/>
</div>
/>
</div>
`;

Expand All @@ -268,16 +138,6 @@ exports[`EuiSelectableListItem props visibleOptions 1`] = `
>
<div
style="overflow:visible;width:0"
>
<div
aria-label="grid"
aria-readonly="true"
class="ReactVirtualized__Grid ReactVirtualized__List euiSelectableList__list"
id="htmlId"
role="listbox"
style="box-sizing:border-box;direction:ltr;height:128px;position:relative;width:0;-webkit-overflow-scrolling:touch;will-change:transform;overflow-x:hidden;overflow-y:hidden"
tabindex="0"
/>
</div>
/>
</div>
`;
Loading