diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 56652db93e69c4..3e4e32ffdf955f 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -7,6 +7,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)). - `withFallbackStyles` HOC: Convert to TypeScript ([#48720](https://github.com/WordPress/gutenberg/pull/48720)). +- `navigateRegions` HOC: Convert to TypeScript ([#48632](https://github.com/WordPress/gutenberg/pull/48632)). ## 23.5.0 (2023-03-01) diff --git a/packages/components/src/higher-order/navigate-regions/index.js b/packages/components/src/higher-order/navigate-regions/index.tsx similarity index 53% rename from packages/components/src/higher-order/navigate-regions/index.js rename to packages/components/src/higher-order/navigate-regions/index.tsx index 5b5a7737897313..f2b9c31bfbe957 100644 --- a/packages/components/src/higher-order/navigate-regions/index.js +++ b/packages/components/src/higher-order/navigate-regions/index.tsx @@ -8,6 +8,7 @@ import { useMergeRefs, } from '@wordpress/compose'; import { isKeyboardEvent } from '@wordpress/keycodes'; +import type { WPKeycodeModifier } from '@wordpress/keycodes'; const defaultShortcuts = { previous: [ @@ -23,7 +24,7 @@ const defaultShortcuts = { modifier: 'access', character: 'p', }, - ], + ] as const, next: [ { modifier: 'ctrl', @@ -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; @@ -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 ); @@ -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( () => ( + *
+ *
+ * Header + *
+ *
+ * Content + *
+ *
+ * Sidebar + *
+ *
+ * ) ); + * ``` + */ export default createHigherOrderComponent( ( Component ) => ( { shortcuts, ...props } ) => diff --git a/packages/components/tsconfig.json b/packages/components/tsconfig.json index 82d8668d3b15ec..b8c3c53144f005 100644 --- a/packages/components/tsconfig.json +++ b/packages/components/tsconfig.json @@ -49,7 +49,6 @@ "src/dimension-control", "src/duotone-picker", "src/gradient-picker", - "src/higher-order/navigate-regions", "src/higher-order/with-filters", "src/higher-order/with-focus-return", "src/higher-order/with-notices", diff --git a/packages/edit-site/src/components/block-editor/editor-canvas.js b/packages/edit-site/src/components/block-editor/editor-canvas.js index 4a4e84939cd845..4489b78ef520f8 100644 --- a/packages/edit-site/src/components/block-editor/editor-canvas.js +++ b/packages/edit-site/src/components/block-editor/editor-canvas.js @@ -44,7 +44,8 @@ function EditorCanvas( { enableResizing, settings, children, ...props } ) { // Forming a "block formatting context" to prevent margin collapsing. // @see https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context `.is-root-container { display: flow-root; } - body { position: relative; }` + body { position: relative; + ${ canvasMode === 'view' ? 'cursor: pointer;' : '' }}}` } { enableResizing && (