Create a generic ARIA toolbar component which works with existing roving tab index context
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
parent
87069a9856
commit
8703bc1abc
4 changed files with 89 additions and 11 deletions
|
@ -47,7 +47,7 @@ const DOCUMENT_POSITION_PRECEDING = 2;
|
|||
|
||||
type Ref = RefObject<HTMLElement>;
|
||||
|
||||
interface IState {
|
||||
export interface IState {
|
||||
activeRef: Ref;
|
||||
refs: Ref[];
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ interface IProps {
|
|||
children(renderProps: {
|
||||
onKeyDownHandler(ev: React.KeyboardEvent);
|
||||
});
|
||||
onKeyDown?(ev: React.KeyboardEvent);
|
||||
onKeyDown?(ev: React.KeyboardEvent, state: IState);
|
||||
}
|
||||
|
||||
export const RovingTabIndexProvider: React.FC<IProps> = ({children, handleHomeEnd, onKeyDown}) => {
|
||||
|
@ -193,7 +193,7 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({children, handleHomeEn
|
|||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
} else if (onKeyDown) {
|
||||
return onKeyDown(ev);
|
||||
return onKeyDown(ev, state);
|
||||
}
|
||||
}, [context.state, onKeyDown, handleHomeEnd]);
|
||||
|
||||
|
|
69
src/accessibility/Toolbar.tsx
Normal file
69
src/accessibility/Toolbar.tsx
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
|
||||
import {IState, RovingTabIndexProvider} from "./RovingTabIndex";
|
||||
import {Key} from "../Keyboard";
|
||||
|
||||
interface IProps extends Omit<React.HTMLProps<HTMLDivElement>, "onKeyDown"> {
|
||||
}
|
||||
|
||||
// This component implements the Toolbar design pattern from the WAI-ARIA Authoring Practices guidelines.
|
||||
// https://www.w3.org/TR/wai-aria-practices-1.1/#toolbar
|
||||
// All buttons passed in children must use RovingTabIndex to set `onFocus`, `isActive`, `ref`
|
||||
const Toolbar: React.FC<IProps> = ({children, ...props}) => {
|
||||
const onKeyDown = (ev: React.KeyboardEvent, state: IState) => {
|
||||
const target = ev.target as HTMLElement;
|
||||
let handled = true;
|
||||
|
||||
switch (ev.key) {
|
||||
case Key.ARROW_UP:
|
||||
case Key.ARROW_DOWN:
|
||||
if (target.hasAttribute('aria-haspopup')) {
|
||||
target.click();
|
||||
}
|
||||
break;
|
||||
|
||||
case Key.ARROW_LEFT:
|
||||
case Key.ARROW_RIGHT:
|
||||
if (state.refs.length > 0) {
|
||||
const i = state.refs.findIndex(r => r === state.activeRef);
|
||||
const delta = ev.key === Key.ARROW_RIGHT ? 1 : -1;
|
||||
state.refs.slice((i + delta) % state.refs.length)[0].current.focus();
|
||||
}
|
||||
break;
|
||||
|
||||
// HOME and END are handled by RovingTabIndexProvider
|
||||
|
||||
default:
|
||||
handled = false;
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
return <RovingTabIndexProvider handleHomeEnd={true} onKeyDown={onKeyDown}>
|
||||
{({onKeyDownHandler}) => <div {...props} onKeyDown={onKeyDownHandler} role="toolbar">
|
||||
{ children }
|
||||
</div>}
|
||||
</RovingTabIndexProvider>;
|
||||
};
|
||||
|
||||
export default Toolbar;
|
Loading…
Add table
Add a link
Reference in a new issue