Skip to content

Commit

Permalink
navigateRegions: Convert to TypeScript (#48632)
Browse files Browse the repository at this point in the history
* navigateRegions: Convert to TypeScript

* Add changelog

* Accept both native and synthetic events

* Fixup changelog
  • Loading branch information
mirka committed Mar 3, 2023
1 parent cae03de commit 45e038e
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 14 deletions.
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- `Guide`: Convert to TypeScript ([#47493](https://github.com/WordPress/gutenberg/pull/47493)).
- `PanelBody`: Convert to TypeScript ([#47702](https://github.com/WordPress/gutenberg/pull/47702)).
- `navigateRegions` HOC: Convert to TypeScript ([#48632](https://github.com/WordPress/gutenberg/pull/48632)).

## 23.5.0 (2023-03-01)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
useMergeRefs,
} from '@wordpress/compose';
import { isKeyboardEvent } from '@wordpress/keycodes';
import type { WPKeycodeModifier } from '@wordpress/keycodes';

const defaultShortcuts = {
previous: [
Expand All @@ -23,7 +24,7 @@ const defaultShortcuts = {
modifier: 'access',
character: 'p',
},
],
] as const,
next: [
{
modifier: 'ctrl',
Expand All @@ -33,27 +34,36 @@ const defaultShortcuts = {
modifier: 'access',
character: 'n',
},
],
] as const,
};

export function useNavigateRegions( shortcuts = defaultShortcuts ) {
const ref = useRef();
type Shortcuts = {
previous: readonly { modifier: WPKeycodeModifier; character: string }[];
next: readonly { modifier: WPKeycodeModifier; character: string }[];
};

export function useNavigateRegions( shortcuts: Shortcuts = defaultShortcuts ) {
const ref = useRef< HTMLDivElement >( null );
const [ isFocusingRegions, setIsFocusingRegions ] = useState( false );

function focusRegion( offset ) {
function focusRegion( offset: number ) {
const regions = Array.from(
ref.current.querySelectorAll( '[role="region"][tabindex="-1"]' )
ref.current?.querySelectorAll< HTMLElement >(
'[role="region"][tabindex="-1"]'
) ?? []
);
if ( ! regions.length ) {
return;
}
let nextRegion = regions[ 0 ];
// Based off the current element, use closest to determine the wrapping region since this operates up the DOM. Also, match tabindex to avoid edge cases with regions we do not want.
const selectedIndex = regions.indexOf(
ref.current.ownerDocument.activeElement.closest(
const wrappingRegion =
ref.current?.ownerDocument?.activeElement?.closest< HTMLElement >(
'[role="region"][tabindex="-1"]'
)
);
);
const selectedIndex = wrappingRegion
? regions.indexOf( wrappingRegion )
: -1;
if ( selectedIndex !== -1 ) {
let nextIndex = selectedIndex + offset;
nextIndex = nextIndex === -1 ? regions.length - 1 : nextIndex;
Expand Down Expand Up @@ -83,7 +93,7 @@ export function useNavigateRegions( shortcuts = defaultShortcuts ) {
return {
ref: useMergeRefs( [ ref, clickRef ] ),
className: isFocusingRegions ? 'is-focusing-regions' : '',
onKeyDown( event ) {
onKeyDown( event: React.KeyboardEvent< HTMLDivElement > ) {
if (
shortcuts.previous.some( ( { modifier, character } ) => {
return isKeyboardEvent[ modifier ]( event, character );
Expand All @@ -101,6 +111,32 @@ export function useNavigateRegions( shortcuts = defaultShortcuts ) {
};
}

/**
* `navigateRegions` is a React [higher-order component](https://facebook.github.io/react/docs/higher-order-components.html)
* adding keyboard navigation to switch between the different DOM elements marked as "regions" (role="region").
* These regions should be focusable (By adding a tabIndex attribute for example). For better accessibility,
* these elements must be properly labelled to briefly describe the purpose of the content in the region.
* For more details, see "Landmark Roles" in the [WAI-ARIA specification](https://www.w3.org/TR/wai-aria/)
* and "Landmark Regions" in the [ARIA Authoring Practices Guide](https://www.w3.org/WAI/ARIA/apg/practices/landmark-regions/).
*
* ```jsx
* import { navigateRegions } from '@wordpress/components';
*
* const MyComponentWithNavigateRegions = navigateRegions( () => (
* <div>
* <div role="region" tabIndex="-1" aria-label="Header">
* Header
* </div>
* <div role="region" tabIndex="-1" aria-label="Content">
* Content
* </div>
* <div role="region" tabIndex="-1" aria-label="Sidebar">
* Sidebar
* </div>
* </div>
* ) );
* ```
*/
export default createHigherOrderComponent(
( Component ) =>
( { shortcuts, ...props } ) =>
Expand Down
1 change: 0 additions & 1 deletion packages/components/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
"src/dimension-control",
"src/duotone-picker",
"src/gradient-picker",
"src/higher-order/navigate-regions",
"src/higher-order/with-fallback-styles",
"src/higher-order/with-filters",
"src/higher-order/with-focus-return",
Expand Down
4 changes: 2 additions & 2 deletions packages/keycodes/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import { isAppleOS } from './platform';
*
* @typedef {(character: string, isApple?: () => boolean) => T} WPKeyHandler
*/
/** @typedef {(event: KeyboardEvent, character: string, isApple?: () => boolean) => boolean} WPEventKeyHandler */
/** @typedef {(event: import('react').KeyboardEvent<HTMLElement> | KeyboardEvent, character: string, isApple?: () => boolean) => boolean} WPEventKeyHandler */

/** @typedef {( isApple: () => boolean ) => WPModifierPart[]} WPModifier */

Expand Down Expand Up @@ -346,7 +346,7 @@ export const shortcutAriaLabel = mapValues(
* From a given KeyboardEvent, returns an array of active modifier constants for
* the event.
*
* @param {KeyboardEvent} event Keyboard event.
* @param {import('react').KeyboardEvent<HTMLElement> | KeyboardEvent} event Keyboard event.
*
* @return {Array<WPModifierPart>} Active modifier constants.
*/
Expand Down

0 comments on commit 45e038e

Please sign in to comment.