Fix accessibility regressions (#7336)
* Fix room list roving treeview New TooltipTarget & TextWithTooltip were not roving-accessible * Fix programmatic focus management in roving tab index not triggering onFocus handler * Fix toolbar no longer handling left & right arrows * Fix roving tab index focus tracking on interactive element like context menu trigger * Fix thread list context menu roving * add comment * fix comment * Fix handling vertical arrows in the wrong direction * iterate PR * delint * tidy up
This commit is contained in:
parent
60286f6170
commit
a667677c57
8 changed files with 103 additions and 58 deletions
|
@ -131,6 +131,8 @@ export const reducer = (state: IState, action: IAction) => {
|
|||
}
|
||||
|
||||
case Type.SetFocus: {
|
||||
// if the ref doesn't change just return the same object reference to skip a re-render
|
||||
if (state.activeRef === action.payload.ref) return state;
|
||||
// update active ref
|
||||
state.activeRef = action.payload.ref;
|
||||
return { ...state };
|
||||
|
@ -194,6 +196,7 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({
|
|||
}
|
||||
|
||||
let handled = false;
|
||||
let focusRef: RefObject<HTMLElement>;
|
||||
// Don't interfere with input default keydown behaviour
|
||||
if (ev.target.tagName !== "INPUT" && ev.target.tagName !== "TEXTAREA") {
|
||||
// check if we actually have any items
|
||||
|
@ -202,7 +205,7 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({
|
|||
if (handleHomeEnd) {
|
||||
handled = true;
|
||||
// move focus to first (visible) item
|
||||
findSiblingElement(context.state.refs, 0)?.current?.focus();
|
||||
focusRef = findSiblingElement(context.state.refs, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -210,28 +213,30 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({
|
|||
if (handleHomeEnd) {
|
||||
handled = true;
|
||||
// move focus to last (visible) item
|
||||
findSiblingElement(context.state.refs, context.state.refs.length - 1, true)?.current?.focus();
|
||||
}
|
||||
break;
|
||||
|
||||
case Key.ARROW_UP:
|
||||
case Key.ARROW_RIGHT:
|
||||
if ((ev.key === Key.ARROW_UP && handleUpDown) || (ev.key === Key.ARROW_RIGHT && handleLeftRight)) {
|
||||
handled = true;
|
||||
if (context.state.refs.length > 0) {
|
||||
const idx = context.state.refs.indexOf(context.state.activeRef);
|
||||
findSiblingElement(context.state.refs, idx - 1)?.current?.focus();
|
||||
}
|
||||
focusRef = findSiblingElement(context.state.refs, context.state.refs.length - 1, true);
|
||||
}
|
||||
break;
|
||||
|
||||
case Key.ARROW_DOWN:
|
||||
case Key.ARROW_LEFT:
|
||||
if ((ev.key === Key.ARROW_DOWN && handleUpDown) || (ev.key === Key.ARROW_LEFT && handleLeftRight)) {
|
||||
case Key.ARROW_RIGHT:
|
||||
if ((ev.key === Key.ARROW_DOWN && handleUpDown) ||
|
||||
(ev.key === Key.ARROW_RIGHT && handleLeftRight)
|
||||
) {
|
||||
handled = true;
|
||||
if (context.state.refs.length > 0) {
|
||||
const idx = context.state.refs.indexOf(context.state.activeRef);
|
||||
findSiblingElement(context.state.refs, idx + 1, true)?.current?.focus();
|
||||
focusRef = findSiblingElement(context.state.refs, idx + 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Key.ARROW_UP:
|
||||
case Key.ARROW_LEFT:
|
||||
if ((ev.key === Key.ARROW_UP && handleUpDown) || (ev.key === Key.ARROW_LEFT && handleLeftRight)) {
|
||||
handled = true;
|
||||
if (context.state.refs.length > 0) {
|
||||
const idx = context.state.refs.indexOf(context.state.activeRef);
|
||||
focusRef = findSiblingElement(context.state.refs, idx - 1, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -242,7 +247,18 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({
|
|||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
}
|
||||
}, [context.state, onKeyDown, handleHomeEnd, handleUpDown, handleLeftRight]);
|
||||
|
||||
if (focusRef) {
|
||||
focusRef.current?.focus();
|
||||
// programmatic focus doesn't fire the onFocus handler, so we must do the do ourselves
|
||||
dispatch({
|
||||
type: Type.SetFocus,
|
||||
payload: {
|
||||
ref: focusRef,
|
||||
},
|
||||
});
|
||||
}
|
||||
}, [context, onKeyDown, handleHomeEnd, handleUpDown, handleLeftRight]);
|
||||
|
||||
return <RovingTabIndexContext.Provider value={context}>
|
||||
{ children({ onKeyDownHandler }) }
|
||||
|
@ -283,7 +299,7 @@ export const useRovingTabIndex = (inputRef?: Ref): [FocusHandler, boolean, Ref]
|
|||
type: Type.SetFocus,
|
||||
payload: { ref },
|
||||
});
|
||||
}, [ref, context]);
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const isActive = context.state.activeRef === ref;
|
||||
return [onFocus, isActive, ref];
|
||||
|
|
|
@ -52,7 +52,7 @@ const Toolbar: React.FC<IProps> = ({ children, ...props }) => {
|
|||
}
|
||||
};
|
||||
|
||||
return <RovingTabIndexProvider handleHomeEnd={true} onKeyDown={onKeyDown}>
|
||||
return <RovingTabIndexProvider handleHomeEnd handleLeftRight onKeyDown={onKeyDown}>
|
||||
{ ({ onKeyDownHandler }) => <div {...props} onKeyDown={onKeyDownHandler} role="toolbar">
|
||||
{ children }
|
||||
</div> }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue