Files
blap/apps/web/src/accessibility/RovingTabIndex.tsx
T
rbondesson 7ff2bc3e40 Extracting RovingGridIndex from EmojiPicker and move to shared components (#33650)
* Add shared roving grid navigation provider

* Add RovingTabIndex stories and update RovingGridIndex stories

* Handle multiple toolbars/grids in on roving group

* Refactor EmojiPicker to use shared roving grid navigation

* Exclude the roving stories from playwright tests

* Fix Sonar issues
2026-05-29 08:54:58 +00:00

75 lines
3.2 KiB
TypeScript

/*
Copyright 2024 New Vector Ltd.
Copyright 2020 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import React from "react";
import {
RovingAction,
RovingGridIndexProvider as SharedRovingGridIndexProvider,
type RovingGridIndexProviderProps,
RovingTabIndexProvider as SharedRovingTabIndexProvider,
type RovingTabIndexProviderProps,
} from "@element-hq/web-shared-components";
import { getKeyBindingsManager } from "../KeyBindingsManager";
import { KeyBindingAction } from "./KeyboardShortcuts";
export { findNextSiblingElement, RovingTabIndexContext } from "@element-hq/web-shared-components";
export { checkInputableElement } from "@element-hq/web-shared-components";
export { RovingStateActionType } from "@element-hq/web-shared-components";
export { useRovingTabIndex } from "@element-hq/web-shared-components";
export type { IAction, IState } from "@element-hq/web-shared-components";
/**
* Module to simplify implementing the Roving TabIndex accessibility technique
*
* Wrap the Widget in an RovingTabIndexContextProvider
* and then for all buttons make use of useRovingTabIndex or RovingTabIndexWrapper.
* The code will keep track of which tabIndex was most recently focused and expose that information as `isActive` which
* can then be used to only set the tabIndex to 0 as expected by the roving tabindex technique.
* When the active button gets unmounted the closest button will be chosen as expected.
* Initially the first button to mount will be given active state.
*
* https://developer.mozilla.org/en-US/docs/Web/Accessibility/Keyboard-navigable_JavaScript_widgets#Technique_1_Roving_tabindex
*/
const getWebRovingAction = (ev: React.KeyboardEvent): RovingAction | undefined => {
switch (getKeyBindingsManager().getAccessibilityAction(ev)) {
case KeyBindingAction.Home:
return RovingAction.Home;
case KeyBindingAction.End:
return RovingAction.End;
case KeyBindingAction.ArrowLeft:
return RovingAction.ArrowLeft;
case KeyBindingAction.ArrowUp:
return RovingAction.ArrowUp;
case KeyBindingAction.ArrowRight:
return RovingAction.ArrowRight;
case KeyBindingAction.ArrowDown:
return RovingAction.ArrowDown;
case KeyBindingAction.Tab:
return RovingAction.Tab;
default:
return undefined;
}
};
type IRovingTabIndexProps = Omit<RovingTabIndexProviderProps, "getAction">;
type IRovingGridIndexProps = Omit<RovingGridIndexProviderProps, "getAction">;
export const RovingTabIndexProvider: React.FC<IRovingTabIndexProps> = (props) => {
return <SharedRovingTabIndexProvider {...props} getAction={getWebRovingAction} />;
};
export const RovingGridIndexProvider: React.FC<IRovingGridIndexProps> = (props) => {
return <SharedRovingGridIndexProvider {...props} getAction={getWebRovingAction} />;
};
// re-export the semantic helper components for simplicity
export { RovingTabIndexWrapper } from "./roving/RovingTabIndexWrapper";
export { RovingAccessibleButton } from "./roving/RovingAccessibleButton";