Skip to content

Commit

Permalink
Escape moves focus to editor region when in select mode (#62196)
Browse files Browse the repository at this point in the history
An escape keypress previously toggled the state between editing and select mode. This returns the behavior to the previous implementation of Escape clearing block focus and returning focus to the wrapping region, if available, showing a blue outline. If no region is available, it will focus the editor iframe or content region.
  • Loading branch information
jeryj authored Jun 26, 2024
1 parent 4b49d83 commit 37a60b2
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import clsx from 'clsx';
import { dragHandle, trash } from '@wordpress/icons';
import { Button, Flex, FlexItem, ToolbarButton } from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';
import { useEffect, useRef } from '@wordpress/element';
import { forwardRef, useEffect } from '@wordpress/element';
import {
BACKSPACE,
DELETE,
Expand Down Expand Up @@ -48,10 +48,11 @@ import Shuffle from '../block-toolbar/shuffle';
*
* @param {string} props Component props.
* @param {string} props.clientId Client ID of block.
* @param {Object} ref Reference to the component.
*
* @return {Component} The component to be rendered.
*/
function BlockSelectionButton( { clientId, rootClientId } ) {
function BlockSelectionButton( { clientId, rootClientId }, ref ) {
const selected = useSelect(
( select ) => {
const {
Expand Down Expand Up @@ -125,7 +126,6 @@ function BlockSelectionButton( { clientId, rootClientId } ) {
canMove,
} = selected;
const { setNavigationMode, removeBlock } = useDispatch( blockEditorStore );
const ref = useRef();

// Focus the breadcrumb in navigation mode.
useEffect( () => {
Expand Down Expand Up @@ -164,11 +164,6 @@ function BlockSelectionButton( { clientId, rootClientId } ) {
const isEnter = keyCode === ENTER;
const isSpace = keyCode === SPACE;
const isShift = event.shiftKey;
if ( isEscape && editorMode === 'navigation' ) {
setNavigationMode( false );
event.preventDefault();
return;
}

if ( keyCode === BACKSPACE || keyCode === DELETE ) {
removeBlock( clientId );
Expand Down Expand Up @@ -368,4 +363,4 @@ function BlockSelectionButton( { clientId, rootClientId } ) {
);
}

export default BlockSelectionButton;
export default forwardRef( BlockSelectionButton );
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
*/
import clsx from 'clsx';

/**
* WordPress dependencies
*/
import { forwardRef } from '@wordpress/element';

/**
* Internal dependencies
*/
Expand All @@ -11,10 +16,7 @@ import { PrivateBlockPopover } from '../block-popover';
import useBlockToolbarPopoverProps from './use-block-toolbar-popover-props';
import useSelectedBlockToolProps from './use-selected-block-tool-props';

export default function BlockToolbarBreadcrumb( {
clientId,
__unstableContentRef,
} ) {
function BlockToolbarBreadcrumb( { clientId, __unstableContentRef }, ref ) {
const {
capturingClientId,
isInsertionPointVisible,
Expand All @@ -38,9 +40,12 @@ export default function BlockToolbarBreadcrumb( {
{ ...popoverProps }
>
<BlockSelectionButton
ref={ ref }
clientId={ clientId }
rootClientId={ rootClientId }
/>
</PrivateBlockPopover>
);
}

export default forwardRef( BlockToolbarBreadcrumb );
38 changes: 37 additions & 1 deletion packages/block-editor/src/components/block-tools/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export default function BlockTools( {
} = useShowBlockTools();

const {
clearSelectedBlock,
duplicateBlocks,
removeBlocks,
replaceBlocks,
Expand All @@ -92,6 +93,8 @@ export default function BlockTools( {
expandBlock,
} = unlock( useDispatch( blockEditorStore ) );

const blockSelectionButtonRef = useRef();

function onKeyDown( event ) {
if ( event.defaultPrevented ) {
return;
Expand Down Expand Up @@ -152,6 +155,39 @@ export default function BlockTools( {
// block so that focus is directed back to the beginning of the selection.
// In effect, to the user this feels like deselecting the multi-selection.
selectBlock( clientIds[ 0 ] );
} else if (
clientIds.length === 1 &&
event.target === blockSelectionButtonRef?.current
) {
event.preventDefault();
clearSelectedBlock();
// If there are multiple editors, we need to find the iframe that contains our contentRef to make sure
// we're focusing the region that contains this editor.
const editorCanvas =
Array.from(
document
.querySelectorAll( 'iframe[name="editor-canvas"]' )
.values()
).find( ( iframe ) => {
// Find the iframe that contains our contentRef
const iframeDocument =
iframe.contentDocument ||
iframe.contentWindow.document;

return (
iframeDocument ===
__unstableContentRef.current.ownerDocument
);
} ) ?? __unstableContentRef.current;

// The region is provivided by the editor, not the block-editor.
// We should send focus to the region if one is available to reuse the
// same interface for navigating landmarks. If no region is available,
// use the canvas instead.
const focusableWrapper =
editorCanvas?.closest( '[role="region"]' ) ?? editorCanvas;

focusableWrapper.focus();
}
} else if ( isMatch( 'core/block-editor/collapse-list-view', event ) ) {
// If focus is currently within a text field, such as a rich text block or other editable field,
Expand Down Expand Up @@ -182,7 +218,6 @@ export default function BlockTools( {
}
}
}

const blockToolbarRef = usePopoverScroll( __unstableContentRef );
const blockToolbarAfterRef = usePopoverScroll( __unstableContentRef );

Expand Down Expand Up @@ -213,6 +248,7 @@ export default function BlockTools( {

{ showBreadcrumb && (
<BlockToolbarBreadcrumb
ref={ blockSelectionButtonRef }
__unstableContentRef={ __unstableContentRef }
clientId={ clientId }
/>
Expand Down
38 changes: 25 additions & 13 deletions packages/components/src/higher-order/navigate-regions/style.scss
Original file line number Diff line number Diff line change
@@ -1,22 +1,35 @@
// Allow the position to be easily overridden to e.g. fixed.

@mixin region-selection-outline {
outline: 4px solid $components-color-accent;
outline-offset: -4px;
}

@mixin region-selection-focus {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
content: "";
pointer-events: none;
@include region-selection-outline;
z-index: z-index(".is-focusing-regions {region} :focus::after");
}

[role="region"] {
position: relative;

// Handles the focus when we programatically send focus to this region
&.interface-interface-skeleton__content:focus-visible::after {
@include region-selection-focus;
}
}

.is-focusing-regions {
[role="region"]:focus::after {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
content: "";
pointer-events: none;
outline: 4px solid $components-color-accent;
outline-offset: -4px;
z-index: z-index(".is-focusing-regions {region} :focus::after");
@include region-selection-focus;
}

// Fixes for edge cases.
// Some of the regions are currently used for layout purposes as 'interface skeleton'
// items. When they're absolutely positioned or when they contain absolutely
Expand All @@ -33,7 +46,6 @@
.interface-interface-skeleton__actions .editor-layout__toggle-publish-panel,
.interface-interface-skeleton__actions .editor-layout__toggle-entities-saved-states-panel,
.editor-post-publish-panel {
outline: 4px solid $components-color-accent;
outline-offset: -4px;
@include region-selection-outline;
}
}
14 changes: 6 additions & 8 deletions test/e2e/specs/editor/various/writing-flow.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -958,7 +958,7 @@ test.describe( 'Writing Flow (@firefox, @webkit)', () => {
<!-- /wp:table -->` );
} );

test( 'escape should toggle between edit and navigation modes', async ( {
test( 'escape should set select mode and then focus the canvas', async ( {
page,
writingFlowUtils,
} ) => {
Expand All @@ -975,15 +975,13 @@ test.describe( 'Writing Flow (@firefox, @webkit)', () => {
.poll( writingFlowUtils.getActiveBlockName )
.toBe( 'core/paragraph' );

// Second escape Toggles back to Edit Mode
// Second escape should send focus to the canvas
await page.keyboard.press( 'Escape' );
// The navigation button should be hidden.
await expect( navigationButton ).toBeHidden();
const blockToolbar = page.getByLabel( 'Block tools' );

await expect( blockToolbar ).toBeVisible();
await expect
.poll( writingFlowUtils.getActiveBlockName )
.toBe( 'core/paragraph' );
await expect(
page.getByRole( 'region', { name: 'Editor content' } )
).toBeFocused();
} );

// Checks for regressions of https://github.com/WordPress/gutenberg/issues/40091.
Expand Down

1 comment on commit 37a60b2

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flaky tests detected in 37a60b2.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/9683912897
📝 Reported issues:

Please sign in to comment.