Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
WIP: Keyboard navigate datepicker in ContextMenu
Browse files Browse the repository at this point in the history
This currently works except for closing the menu when you
Tab or Shift + Tab out of the boundaries of the menu.

>  - `Tab`: Moves focus to the next element in the tab sequence, and if the item that had focus is not in a `menubar`, closes its `menu` and all open parent `menu` containers.
>
>  - `Shift + Tab`: Moves focus to the previous element in the tab sequence, and if the item that had focus is not in a `menubar`, closes its `menu` and all open parent `menu` containers.
>
> https://www.w3.org/TR/wai-aria-practices/#keyboard-interaction-12

I'm going to try to migrate this to using the RovingTabIndex which is mentioned
in a TODO comment and will probably make this more sane
  • Loading branch information
MadLittleMods committed Dec 10, 2021
1 parent 23da673 commit 9fa980c
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 8 deletions.
4 changes: 2 additions & 2 deletions src/accessibility/context_menu/MenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ export const MenuItem: React.FC<IProps> = ({ children, label, tooltip, ...props
const ariaLabel = props["aria-label"] || label;

if (tooltip) {
return <AccessibleTooltipButton {...props} role="menuitem" tabIndex={-1} aria-label={ariaLabel} title={tooltip}>
return <AccessibleTooltipButton {...props} role="menuitem" aria-label={ariaLabel} title={tooltip}>
{ children }
</AccessibleTooltipButton>;
}

return (
<AccessibleButton {...props} role="menuitem" tabIndex={-1} aria-label={ariaLabel}>
<AccessibleButton {...props} role="menuitem" aria-label={ariaLabel}>
{ children }
</AccessibleButton>
);
Expand Down
21 changes: 15 additions & 6 deletions src/components/structures/ContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ export interface IProps extends IPosition {
// it will be mounted to a container at the root of the DOM.
mountAsChild?: boolean;

// If specified, contents will be wrapped in a FocusLock, this is only needed if the context menu is being rendered
// within an existing FocusLock e.g inside a modal.
// If specified, contents will be wrapped in a FocusLock, this is only
// needed if the context menu is being rendered within an existing FocusLock
// e.g inside a modal.
focusLock?: boolean;

// Function to be called on menu close
Expand Down Expand Up @@ -180,12 +181,14 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
if (this.props.onFinished) this.props.onFinished();
};

private onMoveFocus = (element: Element, up: boolean) => {
private onMoveFocus = (element: Element, up: boolean, closeIfOutOfBounds: boolean = false) => {
console.log('onMoveFocus =================', element)
let descending = false; // are we currently descending or ascending through the DOM tree?

do {
const child = up ? element.lastElementChild : element.firstElementChild;
const sibling = up ? element.previousElementSibling : element.nextElementSibling;
console.log('onMoveFocus', child, sibling)

if (descending) {
if (child) {
Expand Down Expand Up @@ -257,18 +260,24 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
switch (ev.key) {
// XXX: this is imitating roving behaviour, it should really use the RovingTabIndex utils
// to inherit proper handling of unmount edge cases
case Key.TAB:
case Key.ESCAPE:
case Key.ARROW_LEFT: // close on left and right arrows too for when it is a context menu on a <Toolbar />
case Key.ARROW_RIGHT:
this.props.onFinished();
break;
case Key.ARROW_UP:
this.onMoveFocus(ev.target as Element, true);
this.onMoveFocus(ev.target as Element, true, true);
break;
case Key.ARROW_DOWN:
this.onMoveFocus(ev.target as Element, false);
this.onMoveFocus(ev.target as Element, false, true);
break;
// case Key.TAB:
// if(ev.shiftKey) {
// this.onMoveFocus(ev.target as Element, true);
// } else {
// this.onMoveFocus(ev.target as Element, false);
// }
// break;
case Key.HOME:
this.onMoveFocusHomeEnd(this.state.contextMenuElem, true);
break;
Expand Down
1 change: 1 addition & 0 deletions src/components/views/messages/DateSeparator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ export default class DateSeparator extends React.Component<IProps, IState> {
className="mx_DateSeparator_jumpToDateMenuOption"
label={_t("Jump to date")}
onClick={() => {}}
tabIndex={-1}
>
<form className="mx_DateSeparator_datePickerForm" onSubmit={this.onJumpToDateSubmit}>
<Field
Expand Down

0 comments on commit 9fa980c

Please sign in to comment.