{ ! hasDefaultEditorCanvasView ? (
getEditorCanvasContainerTitle( editorCanvasView )
) : (
diff --git a/packages/edit-site/src/components/header-edit-mode/style.scss b/packages/edit-site/src/components/header-edit-mode/style.scss
index 26b1716a28b865..3885b0a61d68c6 100644
--- a/packages/edit-site/src/components/header-edit-mode/style.scss
+++ b/packages/edit-site/src/components/header-edit-mode/style.scss
@@ -15,6 +15,17 @@ $header-toolbar-min-width: 335px;
.edit-site-header-edit-mode__start {
display: flex;
border: none;
+ align-items: center;
+ flex-shrink: 2;
+ // We need this to be overflow hidden so the block toolbar can
+ // overflow scroll. If the overflow is visible, flexbox allows
+ // the toolbar to grow outside of the allowed container space.
+ overflow: hidden;
+ // Take up the full height of the header so the border focus
+ // is visible on toolbar buttons.
+ height: 100%;
+ // Allow focus ring to be fully visible on furthest right button.
+ padding-right: 2px;
}
.edit-site-header-edit-mode__end {
@@ -37,6 +48,35 @@ $header-toolbar-min-width: 335px;
}
}
+.edit-site-header-edit-mode__background-style {
+ position: absolute; // Remove it from the flex calculations so space-between works appropriately
+}
+
+// Hacks to allow the block toolbar popover to slide behind the header
+.edit-site-layout:not(.has-fixed-toolbar) {
+ // Create a full-width and height background that floats over the whole div
+ .edit-site-header-edit-mode__background-style {
+ top: 0;
+ left: 0;
+ bottom: -1px; // -1px to overlap the header border
+ right: 0;
+ background: $white;
+ border-bottom: $border-width solid $gray-200;
+ }
+
+ // We want everything except the parent of the selected-block-tools-wrapper and
+ // the selected-block-tools-wrapper
+ .edit-site-header-edit-mode__start > *:not(.selected-block-tools-wrapper),
+ .edit-site-header-edit-mode > *:not(.edit-site-header-edit-mode__start) {
+ z-index: 4;
+ }
+
+ // The block tools slide behind our floating header background
+ .selected-block-tools-wrapper {
+ z-index: 2;
+ }
+}
+
.edit-site-header-edit-mode__toolbar {
display: flex;
align-items: center;
@@ -70,6 +110,21 @@ $header-toolbar-min-width: 335px;
}
}
+.edit-site-header-edit-mode .block-editor-block-contextual-toolbar.is-fixed {
+ position: fixed;
+ top: $header-height; // +1px to avoid overlap with the header border
+ left: 0;
+ width: 100%;
+
+ @include break-medium() {
+ width: auto;
+ position: relative;
+ top: 0;
+ // remove the border
+ border: none;
+ }
+}
+
/**
* Buttons on the right side
*/
@@ -99,7 +154,7 @@ $header-toolbar-min-width: 335px;
}
}
-.edit-site-header-edit-mode__start {
+.edit-site-header-edit-mode__document-toolbar {
display: flex;
border: none;
@@ -183,7 +238,26 @@ $header-toolbar-min-width: 335px;
padding: 0 $grid-unit-10;
}
- .edit-site-header-edit-mode__start .edit-site-header-edit-mode__toolbar > * + * {
+ .edit-site-header-edit-mode__document-toolbar .edit-site-header-edit-mode__toolbar > * + * {
margin-left: $grid-unit-10;
}
}
+
+.has-fixed-toolbar {
+ .selected-block-tools-wrapper {
+ flex-shrink: 4;
+ overflow-x: scroll;
+
+ &.is-collapsed {
+ display: none;
+ }
+ }
+
+ .edit-site-header-edit-mode__center.is-collapsed {
+ display: none;
+ }
+}
+
+.edit-site-header-edit-mode__block-tools-toggle {
+ margin-left: 2px; // Allow focus ring to be fully visible
+}
diff --git a/packages/edit-site/src/components/layout/index.js b/packages/edit-site/src/components/layout/index.js
index c03ad2cd05ecd8..1ba9bfe1100c83 100644
--- a/packages/edit-site/src/components/layout/index.js
+++ b/packages/edit-site/src/components/layout/index.js
@@ -28,6 +28,7 @@ import {
import { store as preferencesStore } from '@wordpress/preferences';
import {
privateApis as blockEditorPrivateApis,
+ store as blockEditorStore,
useBlockCommands,
} from '@wordpress/block-editor';
import { privateApis as routerPrivateApis } from '@wordpress/router';
@@ -83,11 +84,13 @@ export default function Layout() {
canvasMode,
previousShortcut,
nextShortcut,
+ hasBlockSelected,
} = useSelect( ( select ) => {
const { getAllShortcutKeyCombinations } = select(
keyboardShortcutsStore
);
const { getCanvasMode } = unlock( select( editSiteStore ) );
+ const { getBlockSelectionStart } = unlock( select( blockEditorStore ) );
return {
canvasMode: getCanvasMode(),
previousShortcut: getAllShortcutKeyCombinations(
@@ -104,6 +107,7 @@ export default function Layout() {
'core/edit-site',
'distractionFree'
),
+ hasBlockSelected: !! getBlockSelectionStart(),
};
}, [] );
const isEditing = canvasMode === 'edit';
@@ -185,6 +189,7 @@ export default function Layout() {
'is-full-canvas': isFullCanvas,
'is-edit-mode': isEditing,
'has-fixed-toolbar': hasFixedToolbar,
+ 'is-block-toolbar-visible': hasBlockSelected,
}
) }
>
diff --git a/packages/edit-site/src/components/layout/style.scss b/packages/edit-site/src/components/layout/style.scss
index 11c7bdeeaf2a19..27ad972ded03d4 100644
--- a/packages/edit-site/src/components/layout/style.scss
+++ b/packages/edit-site/src/components/layout/style.scss
@@ -40,6 +40,15 @@
z-index: z-index(".edit-site-layout__header-container");
}
+// Make room for the header when a block is selected.
+.is-block-toolbar-visible .edit-site-layout__header-container {
+ padding-bottom: $admin-bar-height-big + 1px;
+
+ @include break-medium() {
+ padding-bottom: 0;
+ }
+}
+
.edit-site-layout__header {
height: $header-height;
display: flex;
@@ -249,23 +258,6 @@
}
}
-.edit-site-layout.has-fixed-toolbar {
- // making the header be lower than the content
- // so the fixed toolbar can be positioned on top of it
- // but only on desktop
- @include break-medium() {
- .edit-site-layout__canvas-container {
- z-index: 5;
- }
- .edit-site-site-hub {
- z-index: 4;
- }
- .edit-site-layout__header:focus-within {
- z-index: 3;
- }
- }
-}
-
.is-edit-mode.is-distraction-free {
.edit-site-layout__header-container {
diff --git a/packages/edit-widgets/src/components/header/index.js b/packages/edit-widgets/src/components/header/index.js
index 0308c2c2171e24..a279a0c8e7c3bc 100644
--- a/packages/edit-widgets/src/components/header/index.js
+++ b/packages/edit-widgets/src/components/header/index.js
@@ -1,9 +1,19 @@
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+
/**
* WordPress dependencies
*/
import { useSelect, useDispatch } from '@wordpress/data';
import { __, _x } from '@wordpress/i18n';
-import { Button, ToolbarItem, VisuallyHidden } from '@wordpress/components';
+import {
+ Button,
+ Slot,
+ ToolbarItem,
+ VisuallyHidden,
+} from '@wordpress/components';
import {
NavigableToolbar,
store as blockEditorStore,
@@ -13,6 +23,7 @@ import { PinnedItems } from '@wordpress/interface';
import { listView, plus } from '@wordpress/icons';
import { useCallback, useRef } from '@wordpress/element';
import { useViewportMatch } from '@wordpress/compose';
+import { store as preferencesStore } from '@wordpress/preferences';
/**
* Internal dependencies
@@ -31,6 +42,15 @@ function Header( { setListViewToggleElement } ) {
const isMediumViewport = useViewportMatch( 'medium' );
const inserterButton = useRef();
const widgetAreaClientId = useLastSelectedWidgetArea();
+ const { isFixedToolbarActive } = useSelect(
+ ( select ) => ( {
+ isFixedToolbarActive: !! select( preferencesStore ).get(
+ 'core/edit-widgets',
+ 'fixedToolbar'
+ ),
+ } ),
+ []
+ );
const isLastSelectedWidgetAreaOpen = useSelect(
( select ) =>
select( editWidgetsStore ).getIsWidgetAreaOpen(
@@ -88,7 +108,13 @@ function Header( { setListViewToggleElement } ) {
return (
<>
-
+
+ { /* Floating header style to be able to hide the block popovers behind the header */ }
+
{ isMediumViewport && (
@@ -103,48 +129,58 @@ function Header( { setListViewToggleElement } ) {
{ __( 'Widgets' ) }
) }
-
- {
- event.preventDefault();
- } }
- onClick={ handleClick }
- icon={ plus }
- /* translators: button label text should, if possible, be under 16
- characters. */
- label={ _x(
- 'Toggle block inserter',
- 'Generic label for block inserter button'
+
+ {
+ event.preventDefault();
+ } }
+ onClick={ handleClick }
+ icon={ plus }
+ /* translators: button label text should, if possible, be under 16
+ characters. */
+ label={ _x(
+ 'Toggle block inserter',
+ 'Generic label for block inserter button'
+ ) }
+ />
+ { isMediumViewport && (
+ <>
+
+
+
+ >
) }
+
+
- { isMediumViewport && (
- <>
-
-
-
- >
- ) }
-
+
diff --git a/packages/edit-widgets/src/components/header/style.scss b/packages/edit-widgets/src/components/header/style.scss
index 64a9f124bb7502..26a3e226717b4f 100644
--- a/packages/edit-widgets/src/components/header/style.scss
+++ b/packages/edit-widgets/src/components/header/style.scss
@@ -9,6 +9,33 @@
@include break-small {
overflow: visible;
}
+
+ .edit-widgets-header__background-style {
+ position: absolute; // Remove it from the flex calculations so space-between works appropriately
+ }
+
+ &:not(.has-fixed-toolbar) {
+ .edit-widgets-header__background-style {
+ top: 0;
+ left: 0;
+ bottom: -1px; // -1px to overlap the header border
+ right: 0;
+ background: $white;
+ border-bottom: $border-width solid $gray-200;
+ z-index: 3; // This z-index needs to be higher than the block popover but lower than the other items within the block toolbar
+ }
+
+ // We want everything except the parent of the selected-block-tools-wrapper and the selected-block-tools-wrapper to have a higher z-index than the floating background-style
+ & > *:not(.edit-widgets-header__navigable-toolbar-wrapper),
+ .edit-widgets-header__navigable-toolbar-wrapper > *:not(.edit-widgets-header__menubar),
+ .edit-widgets-header__menubar > *:not(.selected-block-tools-wrapper) {
+ z-index: 4;
+ }
+
+ .selected-block-tools-wrapper {
+ z-index: 2;
+ }
+ }
}
.edit-widgets-header__navigable-toolbar-wrapper {
@@ -86,3 +113,24 @@
}
}
}
+
+.edit-widgets-header__menubar {
+ display: flex;
+ flex-grow: 1;
+ align-items: center;
+ flex-wrap: nowrap;
+}
+
+.edit-widgets-header__menubar .block-editor-block-contextual-toolbar.is-fixed {
+ position: fixed;
+ top: $admin-bar-height-big + $header-height + 1px; // +1px to avoid overlap with the header border
+ left: 0;
+ width: 100%;
+
+ @include break-medium() {
+ width: auto;
+ position: static;
+ // remove the border
+ border: none;
+ }
+}
diff --git a/packages/edit-widgets/src/components/layout/style.scss b/packages/edit-widgets/src/components/layout/style.scss
index fe1edbae232951..1aed3d3eefc86f 100644
--- a/packages/edit-widgets/src/components/layout/style.scss
+++ b/packages/edit-widgets/src/components/layout/style.scss
@@ -22,15 +22,3 @@
height: 100%;
}
}
-
-.blocks-widgets-container {
- // making the header be lower than the content
- // so the fixed toolbar can be positioned on top of it
- // but only on desktop
- @include break-medium() {
- .interface-interface-skeleton__header:not(:focus-within) {
- z-index: 19;
- }
- }
-
-}
diff --git a/packages/edit-widgets/src/components/widget-areas-block-editor-content/style.scss b/packages/edit-widgets/src/components/widget-areas-block-editor-content/style.scss
index 062214ef147bf1..35493cad130cfd 100644
--- a/packages/edit-widgets/src/components/widget-areas-block-editor-content/style.scss
+++ b/packages/edit-widgets/src/components/widget-areas-block-editor-content/style.scss
@@ -34,101 +34,3 @@
}
}
}
-
-// Fixed contextual toolbar
-@include editor-left(".edit-widgets-block-editor .block-editor-block-contextual-toolbar.is-fixed");
-
-
-.edit-widgets-block-editor .block-editor-block-contextual-toolbar.is-fixed {
- position: sticky;
- top: 0;
- z-index: z-index(".block-editor-block-popover");
- display: block;
- width: 100%;
-
- // on desktop and tablet viewports the toolbar is fixed
- // on top of interface header
- $toolbar-margin: $grid-unit-80 * 3 - 2 * $grid-unit + $grid-unit-05;
-
- @include break-medium() {
- // leave room for block inserter, undo and redo, list view
- margin-left: $toolbar-margin;
- // position on top of interface header
- position: fixed;
- top: $admin-bar-height;
- // Don't fill up when empty
- min-height: initial;
- // remove the border
- border-bottom: none;
- // has to be flex for collapse button to fit
- display: flex;
-
- // Mimic the height of the parent, vertically align center, and provide a max-height.
- height: $header-height;
- align-items: center;
-
-
- // on tablet viewports the toolbar is fixed
- // on top of interface header and covers the whole header
- // except for the inserter on the left
- width: calc(100% - #{$toolbar-margin});
-
- &.is-collapsed {
- width: initial;
- }
-
- &:empty {
- width: initial;
- }
-
- .is-fullscreen-mode & {
- // leave room for block inserter, undo and redo, list view
- // and some margin left
- margin-left: $grid-unit-80 * 4 - 2 * $grid-unit;
-
- top: 0;
-
- &.is-collapsed {
- width: initial;
- }
-
- &:empty {
- width: initial;
- }
- }
-
- .show-icon-labels & {
- margin-left: $grid-unit-80 + 2 * $grid-unit; // inserter and margin
- width: calc(100% + 40px - #{$toolbar-margin}); //there are no undo, redo and list view buttons
-
- .is-fullscreen-mode & {
- margin-left: $grid-unit * 18; // site hub, inserter and margin
- }
- }
-
- .blocks-widgets-container & {
- margin-left: $grid-unit-80 * 2.4;
-
- &.is-collapsed {
- margin-left: $grid-unit-80 * 4.2;
- }
- }
- }
-
- // on desktop viewports the toolbar is fixed
- // on top of interface header and leaves room
- // for the block inserter the publish button
- @include break-large() {
- width: auto;
- .show-icon-labels & {
- width: auto; //there are no undo, redo and list view buttons
- }
-
- .is-fullscreen-mode & {
- // in full screen mode we need to account for
- // the combined with of the tools at the right of the header and the margin left
- // of the toolbar which includes four buttons
- width: calc(100% - 280px - #{4 * $grid-unit-80});
- }
- }
-}
diff --git a/test/e2e/specs/editor/blocks/image.spec.js b/test/e2e/specs/editor/blocks/image.spec.js
index db3ff72e3ab6eb..e1db27095f4587 100644
--- a/test/e2e/specs/editor/blocks/image.spec.js
+++ b/test/e2e/specs/editor/blocks/image.spec.js
@@ -140,7 +140,7 @@ test.describe( 'Image', () => {
// Add caption and navigate to inline toolbar.
await editor.clickBlockToolbarButton( 'Add caption' );
- await pageUtils.pressKeys( 'shift+Tab' );
+ await pageUtils.pressKeys( 'alt+F10' );
expect(
await page.evaluate( () =>
document.activeElement.getAttribute( 'aria-label' )
diff --git a/test/e2e/specs/editor/various/keyboard-navigable-blocks.spec.js b/test/e2e/specs/editor/various/keyboard-navigable-blocks.spec.js
index 080abe011206a7..273307126e64a3 100644
--- a/test/e2e/specs/editor/various/keyboard-navigable-blocks.spec.js
+++ b/test/e2e/specs/editor/various/keyboard-navigable-blocks.spec.js
@@ -6,8 +6,8 @@
const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' );
test.use( {
- KeyboardNavigableBlocks: async ( { editor, page, pageUtils }, use ) => {
- await use( new KeyboardNavigableBlocks( { editor, page, pageUtils } ) );
+ KeyboardNavigableBlocks: async ( { page }, use ) => {
+ await use( new KeyboardNavigableBlocks( { page } ) );
},
} );
@@ -16,11 +16,12 @@ test.describe( 'Order of block keyboard navigation', () => {
await admin.createNewPost();
} );
- test( 'permits tabbing through paragraph blocks in the expected order', async ( {
+ test( 'permits arrowing through paragraph blocks in the expected order', async ( {
editor,
- KeyboardNavigableBlocks,
page,
} ) => {
+ // Add a title
+ await page.keyboard.type( 'Post Title' );
const paragraphBlocks = [ 'Paragraph 0', 'Paragraph 1', 'Paragraph 2' ];
// Create 3 paragraphs blocks with some content.
@@ -29,58 +30,31 @@ test.describe( 'Order of block keyboard navigation', () => {
await page.keyboard.type( paragraphBlock );
}
- // Select the middle block.
- await page.keyboard.press( 'ArrowUp' );
- await editor.showBlockToolbar();
- await KeyboardNavigableBlocks.navigateToContentEditorTop();
- await KeyboardNavigableBlocks.tabThroughParagraphBlock( 'Paragraph 1' );
-
- // Repeat the same steps to ensure that there is no change introduced in how the focus is handled.
- // This prevents the previous regression explained in: https://github.com/WordPress/gutenberg/issues/11773.
- await KeyboardNavigableBlocks.navigateToContentEditorTop();
- await KeyboardNavigableBlocks.tabThroughParagraphBlock( 'Paragraph 1' );
- } );
-
- test( 'allows tabbing in navigation mode if no block is selected', async ( {
- editor,
- KeyboardNavigableBlocks,
- page,
- } ) => {
- const paragraphBlocks = [ '0', '1' ];
+ // Focus should be on the last pargraph block.
+ const activeElement = editor.canvas.locator( ':focus' );
+ await expect( activeElement ).toHaveText( 'Paragraph 2' );
- // Create 2 paragraphs blocks with some content.
- for ( const paragraphBlock of paragraphBlocks ) {
- await editor.insertBlock( { name: 'core/paragraph' } );
- await page.keyboard.type( paragraphBlock );
- }
+ await page.keyboard.press( 'ArrowUp' );
+ await expect( activeElement ).toHaveText( 'Paragraph 1' );
- // Clear the selected block.
- const paragraph = editor.canvas
- .locator( '[data-type="core/paragraph"]' )
- .getByText( '1' );
- const box = await paragraph.boundingBox();
- await page.mouse.click( box.x - 1, box.y );
+ await page.keyboard.press( 'ArrowUp' );
+ await expect( activeElement ).toHaveText( 'Paragraph 0' );
- await page.keyboard.press( 'Tab' );
- await KeyboardNavigableBlocks.expectLabelToHaveFocus( 'Add title' );
+ await page.keyboard.press( 'ArrowUp' );
+ await expect( activeElement ).toHaveText( 'Post Title' );
- await page.keyboard.press( 'Tab' );
- await KeyboardNavigableBlocks.expectLabelToHaveFocus(
- 'Paragraph Block. Row 1. 0'
- );
+ // Go back down
+ await page.keyboard.press( 'ArrowDown' );
+ await expect( activeElement ).toHaveText( 'Paragraph 0' );
- await page.keyboard.press( 'Tab' );
- await KeyboardNavigableBlocks.expectLabelToHaveFocus(
- 'Paragraph Block. Row 2. 1'
- );
+ await page.keyboard.press( 'ArrowDown' );
+ await expect( activeElement ).toHaveText( 'Paragraph 1' );
- await page.keyboard.press( 'Tab' );
- await KeyboardNavigableBlocks.expectLabelToHaveFocus(
- 'Post (selected)'
- );
+ await page.keyboard.press( 'ArrowDown' );
+ await expect( activeElement ).toHaveText( 'Paragraph 2' );
} );
- test( 'allows tabbing in navigation mode if no block is selected (reverse)', async ( {
+ test( 'allows tabbing in navigation mode if no block is selected', async ( {
editor,
KeyboardNavigableBlocks,
page,
@@ -94,19 +68,12 @@ test.describe( 'Order of block keyboard navigation', () => {
await page.keyboard.type( paragraphBlock );
}
- // Clear the selected block.
- const paragraph = editor.canvas
- .locator( '[data-type="core/paragraph"]' )
- .getByText( '1' );
- const box = await paragraph.boundingBox();
- await page.mouse.click( box.x - 1, box.y );
-
- // Put focus behind the block list.
- await page.evaluate( () => {
- document
- .querySelector( '.interface-interface-skeleton__sidebar' )
- .focus();
- } );
+ // Clear the selected block and switch to Select mode
+ await page.keyboard.press( 'Escape' );
+ // Move focus into the sidebar
+ await page.keyboard.press( 'Tab' );
+ const activeElement = page.locator( ':focus' );
+ await expect( activeElement ).toHaveText( 'Post' );
await pageUtils.pressKeys( 'shift+Tab' );
await KeyboardNavigableBlocks.expectLabelToHaveFocus( 'Add block' );
@@ -128,6 +95,17 @@ test.describe( 'Order of block keyboard navigation', () => {
await pageUtils.pressKeys( 'shift+Tab' );
await KeyboardNavigableBlocks.expectLabelToHaveFocus( 'Add title' );
+
+ // Make sure it works tabbing back through in sequence
+ await pageUtils.pressKeys( 'Tab' );
+ await KeyboardNavigableBlocks.expectLabelToHaveFocus(
+ 'Paragraph Block. Row 1. 0'
+ );
+
+ await pageUtils.pressKeys( 'Tab' );
+ await KeyboardNavigableBlocks.expectLabelToHaveFocus(
+ 'Paragraph Block. Row 2. 1'
+ );
} );
test( 'should navigate correctly with multi selection', async ( {
@@ -158,9 +136,14 @@ test.describe( 'Order of block keyboard navigation', () => {
'Multiple selected blocks'
);
- await pageUtils.pressKeys( 'shift+Tab' );
+ await pageUtils.pressKeys( 'alt+F10' );
await page.keyboard.press( 'ArrowRight' );
await KeyboardNavigableBlocks.expectLabelToHaveFocus( 'Move up' );
+ // Return focus to the editor
+ await page.keyboard.press( 'Escape' );
+ await KeyboardNavigableBlocks.expectLabelToHaveFocus(
+ 'Multiple selected blocks'
+ );
} );
test( 'allows the first element within a block to receive focus', async ( {
@@ -181,27 +164,31 @@ test.describe( 'Order of block keyboard navigation', () => {
test( 'allows the block wrapper to gain focus for a group block instead of the first element', async ( {
editor,
- KeyboardNavigableBlocks,
+ page,
} ) => {
// Insert a group block.
await editor.insertBlock( { name: 'core/group' } );
- // Select the default, selected Group layout from the variation picker.
- const groupButton = editor.canvas.locator(
- 'button[aria-label="Group: Gather blocks in a container."]'
+
+ const activeElement = editor.canvas.locator( ':focus' );
+
+ await expect( activeElement ).toHaveAttribute(
+ 'aria-label',
+ 'Group: Gather blocks in a container.'
);
- await groupButton.click();
+ await page.keyboard.press( 'Enter' );
// If active label matches, that means focus did not change from group block wrapper.
- await KeyboardNavigableBlocks.expectLabelToHaveFocus( 'Block: Group' );
+ await expect( activeElement ).toHaveAttribute(
+ 'aria-label',
+ 'Block: Group'
+ );
} );
} );
class KeyboardNavigableBlocks {
- constructor( { editor, page, pageUtils } ) {
- this.editor = editor;
+ constructor( { page } ) {
this.page = page;
- this.pageUtils = pageUtils;
}
async expectLabelToHaveFocus( label ) {
@@ -216,61 +203,6 @@ class KeyboardNavigableBlocks {
expect( ariaLabel ).toBe( label );
}
-
- async navigateToContentEditorTop() {
- // Use 'Ctrl+`' to return to the top of the editor.
- await this.pageUtils.pressKeys( 'ctrl+`', { times: 5 } );
- }
-
- async tabThroughParagraphBlock( paragraphText ) {
- await this.tabThroughBlockToolbar();
-
- await this.page.keyboard.press( 'Tab' );
- await this.expectLabelToHaveFocus( 'Block: Paragraph' );
-
- const activeElement = this.editor.canvas.locator( ':focus' );
-
- await expect( activeElement ).toHaveText( paragraphText );
-
- await this.page.keyboard.press( 'Tab' );
- await this.expectLabelToHaveFocus( 'Post' );
-
- // Need to shift+tab here to end back in the block. If not, we'll be in the next region and it will only require 4 region jumps instead of 5.
- await this.pageUtils.pressKeys( 'shift+Tab' );
- await this.expectLabelToHaveFocus( 'Block: Paragraph' );
- }
-
- async tabThroughBlockToolbar() {
- await this.page.keyboard.press( 'Tab' );
- await this.expectLabelToHaveFocus( 'Paragraph' );
-
- await this.page.keyboard.press( 'ArrowRight' );
- await this.expectLabelToHaveFocus( 'Move up' );
-
- await this.page.keyboard.press( 'ArrowRight' );
- await this.expectLabelToHaveFocus( 'Move down' );
-
- await this.page.keyboard.press( 'ArrowRight' );
- await this.expectLabelToHaveFocus( 'Align text' );
-
- await this.page.keyboard.press( 'ArrowRight' );
- await this.expectLabelToHaveFocus( 'Bold' );
-
- await this.page.keyboard.press( 'ArrowRight' );
- await this.expectLabelToHaveFocus( 'Italic' );
-
- await this.page.keyboard.press( 'ArrowRight' );
- await this.expectLabelToHaveFocus( 'Link' );
-
- await this.page.keyboard.press( 'ArrowRight' );
- await this.expectLabelToHaveFocus( 'More' );
-
- await this.page.keyboard.press( 'ArrowRight' );
- await this.expectLabelToHaveFocus( 'Options' );
-
- await this.page.keyboard.press( 'ArrowRight' );
- await this.expectLabelToHaveFocus( 'Paragraph' );
- }
}
/* eslint-enable playwright/expect-expect */
diff --git a/test/e2e/specs/editor/various/navigable-toolbar.spec.js b/test/e2e/specs/editor/various/navigable-toolbar.spec.js
index abdb1800d150a2..d549a1a242b21b 100644
--- a/test/e2e/specs/editor/various/navigable-toolbar.spec.js
+++ b/test/e2e/specs/editor/various/navigable-toolbar.spec.js
@@ -3,6 +3,12 @@
*/
const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' );
+test.use( {
+ BlockToolbarUtils: async ( { page, pageUtils }, use ) => {
+ await use( new BlockToolbarUtils( { page, pageUtils } ) );
+ },
+} );
+
test.describe( 'Block Toolbar', () => {
test.beforeEach( async ( { admin } ) => {
await admin.createNewPost();
@@ -44,20 +50,94 @@ test.describe( 'Block Toolbar', () => {
} );
expect( scrollTopBefore ).toBe( scrollTopAfter );
} );
- } );
- test( 'should focus with Shift+Tab', async ( {
- editor,
- page,
- pageUtils,
- } ) => {
- await editor.insertBlock( { name: 'core/paragraph' } );
- await page.keyboard.type( 'a' );
- await pageUtils.pressKeys( 'shift+Tab' );
- await expect(
- page
- .getByRole( 'toolbar', { name: 'Block Tools' } )
- .getByRole( 'button', { name: 'Paragraph' } )
- ).toBeFocused();
+ test( 'can navigate to the block toolbar and back to block using the keyboard', async ( {
+ BlockToolbarUtils,
+ editor,
+ page,
+ pageUtils,
+ } ) => {
+ // Test navigating to block toolbar
+ await editor.insertBlock( { name: 'core/paragraph' } );
+ await page.keyboard.type( 'Paragraph' );
+ await BlockToolbarUtils.focusBlockToolbar();
+ await BlockToolbarUtils.expectLabelToHaveFocus( 'Paragraph' );
+ // // Navigate to Align Text
+ await page.keyboard.press( 'ArrowRight' );
+ await BlockToolbarUtils.expectLabelToHaveFocus( 'Align text' );
+ // // Open the dropdown
+ await page.keyboard.press( 'Enter' );
+ await BlockToolbarUtils.expectLabelToHaveFocus( 'Align text left' );
+ await page.keyboard.press( 'ArrowDown' );
+ await BlockToolbarUtils.expectLabelToHaveFocus(
+ 'Align text center'
+ );
+ await page.keyboard.press( 'Escape' );
+ await BlockToolbarUtils.expectLabelToHaveFocus( 'Align text' );
+
+ // Navigate to the Bold item. Testing items via the fills within the block toolbar are especially important
+ await page.keyboard.press( 'ArrowRight' );
+ await BlockToolbarUtils.expectLabelToHaveFocus( 'Bold' );
+
+ await BlockToolbarUtils.focusBlock();
+ await BlockToolbarUtils.expectLabelToHaveFocus(
+ 'Block: Paragraph'
+ );
+
+ await BlockToolbarUtils.focusBlockToolbar();
+ await BlockToolbarUtils.expectLabelToHaveFocus( 'Bold' );
+
+ await BlockToolbarUtils.focusBlock();
+
+ // Try selecting text and navigating to block toolbar
+ await pageUtils.pressKeys( 'Shift+ArrowLeft', {
+ times: 4,
+ delay: 50,
+ } );
+ expect(
+ await editor.canvas
+ .locator( ':root' )
+ .evaluate( () => window.getSelection().toString() )
+ ).toBe( 'raph' );
+
+ // Go back to the toolbar and apply a formatting option
+ await BlockToolbarUtils.focusBlockToolbar();
+ await BlockToolbarUtils.expectLabelToHaveFocus( 'Bold' );
+ await page.keyboard.press( 'Enter' );
+ // Should focus the selected text again
+ expect(
+ await editor.canvas
+ .locator( ':root' )
+ .evaluate( () => window.getSelection().toString() )
+ ).toBe( 'raph' );
+ } );
} );
} );
+
+class BlockToolbarUtils {
+ constructor( { page, pageUtils } ) {
+ this.page = page;
+ this.pageUtils = pageUtils;
+ }
+
+ async focusBlockToolbar() {
+ await this.pageUtils.pressKeys( 'alt+F10' );
+ }
+
+ async focusBlock() {
+ await this.pageUtils.pressKeys( 'Escape' );
+ }
+
+ async expectLabelToHaveFocus( label ) {
+ const ariaLabel = await this.page.evaluate( () => {
+ const { activeElement } =
+ document.activeElement.contentDocument ?? document;
+ return (
+ activeElement.getAttribute( 'aria-label' ) ||
+ activeElement.innerText
+ );
+ } );
+
+ expect( ariaLabel ).toBe( label );
+ }
+}
diff --git a/test/e2e/specs/editor/various/preview.spec.js b/test/e2e/specs/editor/various/preview.spec.js
index cfec384adba9bf..4783eb5b9b056c 100644
--- a/test/e2e/specs/editor/various/preview.spec.js
+++ b/test/e2e/specs/editor/various/preview.spec.js
@@ -324,11 +324,13 @@ class PreviewUtils {
}
async toggleCustomFieldsOption( shouldBeChecked ) {
- // Open preferences dialog.
-
- await this.page.click(
- 'role=region[name="Editor top bar"i] >> role=button[name="Options"i]'
- );
+ // Open preferences dialog. We need the `.last()` because if a block is selected, there are multiple Options buttons within the header.
+ await this.page
+ .locator(
+ 'role=region[name="Editor top bar"i] >> role=button[name="Options"i]'
+ )
+ .last()
+ .click();
await this.page.click( 'role=menuitem[name="Preferences"i]' );
// Navigate to panels section.
diff --git a/test/e2e/specs/editor/various/rich-text.spec.js b/test/e2e/specs/editor/various/rich-text.spec.js
index 2969a33d254852..0373bd47354b16 100644
--- a/test/e2e/specs/editor/various/rich-text.spec.js
+++ b/test/e2e/specs/editor/various/rich-text.spec.js
@@ -647,7 +647,7 @@ test.describe( 'RichText', () => {
await page.keyboard.press( 'Escape' );
// Navigate to the block.
- await page.keyboard.press( 'Tab' );
+ await page.keyboard.press( 'Escape' );
// Copy the colored text.
await pageUtils.pressKeys( 'primary+c' );
diff --git a/test/e2e/specs/editor/various/toolbar-roving-tabindex.spec.js b/test/e2e/specs/editor/various/toolbar-roving-tabindex.spec.js
index e706dfc3607dc3..7ae5379f0e8240 100644
--- a/test/e2e/specs/editor/various/toolbar-roving-tabindex.spec.js
+++ b/test/e2e/specs/editor/various/toolbar-roving-tabindex.spec.js
@@ -133,19 +133,8 @@ test.describe( 'Toolbar roving tabindex', () => {
await page.keyboard.press( 'ArrowRight' );
await ToolbarRovingTabindexUtils.expectLabelToHaveFocus( 'Move up' );
await pageUtils.pressKeys( 'Tab' );
- await pageUtils.pressKeys( 'shift+Tab' );
- await ToolbarRovingTabindexUtils.expectLabelToHaveFocus( 'Move up' );
- } );
-
- test( 'can reach toolbar items with arrow keys after pressing alt+F10', async ( {
- page,
- pageUtils,
- ToolbarRovingTabindexUtils,
- } ) => {
await pageUtils.pressKeys( 'alt+F10' );
- await page.keyboard.press( 'ArrowRight' );
- await page.keyboard.press( 'ArrowRight' );
- await ToolbarRovingTabindexUtils.expectLabelToHaveFocus( 'Bold' );
+ await ToolbarRovingTabindexUtils.expectLabelToHaveFocus( 'Move up' );
} );
} );
@@ -167,9 +156,9 @@ class ToolbarRovingTabindexUtils {
await this.expectLabelToHaveFocus( currentBlockTitle );
await this.page.keyboard.press( 'ArrowRight' );
await this.expectLabelToHaveFocus( 'Move up' );
- await this.pageUtils.pressKeys( 'Tab' );
+ await this.pageUtils.pressKeys( 'Escape' );
await this.expectLabelToHaveFocus( currentBlockLabel );
- await this.pageUtils.pressKeys( 'shift+Tab' );
+ await this.pageUtils.pressKeys( 'alt+F10' );
await this.expectLabelToHaveFocus( 'Move up' );
}
@@ -200,7 +189,7 @@ class ToolbarRovingTabindexUtils {
await this.expectLabelToHaveFocus( 'Block: Group' );
await this.page.keyboard.press( 'ArrowRight' );
await this.expectLabelToHaveFocus( currentBlockLabel );
- await this.pageUtils.pressKeys( 'shift+Tab' );
+ await this.pageUtils.pressKeys( 'alt+F10' );
await this.expectLabelToHaveFocus( 'Select Group' );
await this.page.keyboard.press( 'ArrowRight' );
await this.expectLabelToHaveFocus( currentBlockTitle );
diff --git a/test/e2e/specs/site-editor/writing-flow.spec.js b/test/e2e/specs/site-editor/writing-flow.spec.js
index 8ee6ce0e565572..3bb6b4aa8ba328 100644
--- a/test/e2e/specs/site-editor/writing-flow.spec.js
+++ b/test/e2e/specs/site-editor/writing-flow.spec.js
@@ -13,7 +13,7 @@ test.describe( 'Site editor writing flow', () => {
} );
// Check for regressions of https://github.com/WordPress/gutenberg/issues/41811.
- test( 'allows shift tabbing to the block toolbar from the first block', async ( {
+ test( 'allows shift tabbing out of the editor canvas from the first block', async ( {
admin,
editor,
page,
@@ -32,11 +32,11 @@ test.describe( 'Site editor writing flow', () => {
await expect( siteTitleBlock ).toBeVisible();
await editor.selectBlocks( siteTitleBlock );
- // Shift tab to the toolbar.
+ // Shift tab to the first tabstop outside of the editor canvas
await pageUtils.pressKeys( 'shift+Tab' );
- const blockToolbarButton = page.locator(
- 'role=toolbar[name="Block tools"i] >> role=button[name="Site Title"i]'
- );
+ const blockToolbarButton = page.getByRole( 'button', {
+ name: 'Open save panel',
+ } );
await expect( blockToolbarButton ).toBeFocused();
} );