From 6446ff7bd0d4aa7a8d0091a8e96d09c691c8981b Mon Sep 17 00:00:00 2001 From: Jerry Jones Date: Wed, 19 Apr 2023 11:45:46 -0500 Subject: [PATCH] Fix multiple tooltips showing on NavigableToolbars (#49644) * Add useKeyboardShortcut prop to NavigableToolbar and useToolbarFocus The 'core/block-editor/focus-toolbar' shortcut (alt + F10) selects the active navigable toolbar's fi rst tabbable item, but when there are multiple navigable toolbars, it places focus on all of the act ive navigable toolbar components. This results in an odd state of two tooltips being open. This comm it scaffolds the ability to disable the keyboard shortcut from applying. * Conditionally useKeyboardFocusShortcut in HeaderToolbar I couldn't find a reliable way of determining if there is a block toolbar showing, so I approximated it by checking if there are no selected blocks or we're not in 'edit' mode. If one of those conditions applies, then a focusable block toolbar won't be present (there may other instances I'm missing though), and we can enable the shortcut for the header toolbar. * Remove accidentally added line break * Check all the conditions when a toolbar might be showing I hope I'm missing a reliable isBlockToolbarShowing() instead of doingn all these checks, but I didn't see a function like that. Maybe it's worth adding? * Remove unnecesary line break * Change logic to use isBlockWithToolbarSelected instead of isUnmodifiedDefaultBlock We don't care if it's an unmodified default block or not in the condition to use the shortcut for the top level toolbar, we need to know if there's a chance the toolbar is showing or not. This simplifies the logic so anytime a block with a toolbar is selected, we set maybeBlockToolbarShowing to true, then check the extra condition of a fixed toolbar with ANY block selected. * Start on e2e tests to cover various scenarios * e2e test: should focus the top level toolbar when in select mode * ede test: should focus the top level toolbar when on an empty block * ede test: grouping into describe test sections * Scaffold top toolbar mode e2e tests * e2e tests: Top Toolbar in edit mode * e2e tests: Top toolbar + select mode * Refactor toggle fixed toolbar to playwright editor class * Refactor: consolodate e2e locators * Refactor: Combine tests into fewer cases to save execution time There were 12 tests and each time had to go through all of the test set-up. I combined the tests into groups based into view and mode for four total tests. This cuts the test time down by about half, (18s for the previous 12 tests vs 9s for these 4 tests). This is recommended by Playwright Best Practices of "write fewer tests but longer tests": https://playwright.dev/docs/best-practices#write-fewer-tests-but-longer-tests. I think the time saved per commit is worth having more assertions per test. * Use POM-style ToolbarUtils for e2e test * Improve naming of isFixed to setIsFixedToolbar * Improve naming to shouldUseKeyboardFocusShortcut * Simplify logic for setting isBlockWithToolbarSelected * Rename toggleFixedToolbar util to setIsFixedToolbar * Refactor shouldShowContextualToolbar and canFocusHiddenToolbar into useShouldContextualToolbarShow hook In order to decide if the block toolbar or document toolbar should be focused from the alt+F10 shortcut, we need to do duplicate the same code in both toolbar wrappers. This moves them into a shared hook to keep things consistent. * Export useShouldContextualToolbarShow * Add hasClientId check, as we can't focus a block toolbar without a block ID * Implement useShouldContextualToolbarShow in header toolbar * Add tests for smaller viewports * Update e2e tests to address new unified toolbar view * Make useShouldContextualToolbarShow private --- .../block-tools/selected-block-popover.js | 55 +--- .../src/components/navigable-toolbar/index.js | 15 +- packages/block-editor/src/private-apis.js | 2 + .../use-should-contextual-toolbar-show.js | 75 +++++ .../src/editor/index.ts | 4 + .../src/editor/set-is-fixed-toolbar.ts | 21 ++ .../components/header/header-toolbar/index.js | 31 +- .../various/shortcut-focus-toolbar.spec.js | 269 ++++++++++++++++++ 8 files changed, 422 insertions(+), 50 deletions(-) create mode 100644 packages/block-editor/src/utils/use-should-contextual-toolbar-show.js create mode 100644 packages/e2e-test-utils-playwright/src/editor/set-is-fixed-toolbar.ts create mode 100644 test/e2e/specs/editor/various/shortcut-focus-toolbar.spec.js diff --git a/packages/block-editor/src/components/block-tools/selected-block-popover.js b/packages/block-editor/src/components/block-tools/selected-block-popover.js index b4d14296e823d4..6e3c77fe1ca186 100644 --- a/packages/block-editor/src/components/block-tools/selected-block-popover.js +++ b/packages/block-editor/src/components/block-tools/selected-block-popover.js @@ -10,7 +10,6 @@ import { useRef, useEffect } from '@wordpress/element'; import { isUnmodifiedDefaultBlock } from '@wordpress/blocks'; import { useDispatch, useSelect } from '@wordpress/data'; import { useShortcut } from '@wordpress/keyboard-shortcuts'; -import { useViewportMatch } from '@wordpress/compose'; /** * Internal dependencies @@ -22,26 +21,20 @@ import BlockPopover from '../block-popover'; import useBlockToolbarPopoverProps from './use-block-toolbar-popover-props'; import Inserter from '../inserter'; import { unlock } from '../../lock-unlock'; +import { privateApis } from '../../private-apis'; function selector( select ) { const { __unstableGetEditorMode, - isMultiSelecting, hasMultiSelection, isTyping, - isBlockInterfaceHidden, - getSettings, getLastMultiSelectedBlockClientId, } = unlock( select( blockEditorStore ) ); return { editorMode: __unstableGetEditorMode(), hasMultiSelection: hasMultiSelection(), - isMultiSelecting: isMultiSelecting(), isTyping: isTyping(), - isBlockInterfaceHidden: isBlockInterfaceHidden(), - hasFixedToolbar: getSettings().hasFixedToolbar, - isDistractionFree: getSettings().isDistractionFree, lastClientId: hasMultiSelection() ? getLastMultiSelectedBlockClientId() : null, @@ -52,21 +45,16 @@ function SelectedBlockPopover( { clientId, rootClientId, isEmptyDefaultBlock, - showContents, // we may need to mount an empty popover because we reuse capturingClientId, __unstablePopoverSlot, __unstableContentRef, } ) { - const { - editorMode, - hasMultiSelection, - isMultiSelecting, - isTyping, - isBlockInterfaceHidden, - hasFixedToolbar, - isDistractionFree, - lastClientId, - } = useSelect( selector, [] ); + const { editorMode, hasMultiSelection, isTyping, lastClientId } = useSelect( + selector, + [] + ); + + const { useShouldContextualToolbarShow } = unlock( privateApis ); const isInsertionPointVisible = useSelect( ( select ) => { const { @@ -85,8 +73,10 @@ function SelectedBlockPopover( { }, [ clientId ] ); - const isLargeViewport = useViewportMatch( 'medium' ); const isToolbarForced = useRef( false ); + const { shouldShowContextualToolbar, canFocusHiddenToolbar } = + useShouldContextualToolbarShow( clientId ); + const { stopTyping } = useDispatch( blockEditorStore ); const showEmptyBlockSideInserter = @@ -94,20 +84,6 @@ function SelectedBlockPopover( { const shouldShowBreadcrumb = ! hasMultiSelection && ( editorMode === 'navigation' || editorMode === 'zoom-out' ); - const shouldShowContextualToolbar = - editorMode === 'edit' && - ! hasFixedToolbar && - isLargeViewport && - ! isMultiSelecting && - ! showEmptyBlockSideInserter && - ! isTyping && - ! isBlockInterfaceHidden; - const canFocusHiddenToolbar = - editorMode === 'edit' && - ! shouldShowContextualToolbar && - ! hasFixedToolbar && - ! isDistractionFree && - ! isEmptyDefaultBlock; useShortcut( 'core/block-editor/focus-toolbar', @@ -179,7 +155,7 @@ function SelectedBlockPopover( { resize={ false } { ...popoverProps } > - { shouldShowContextualToolbar && showContents && ( + { shouldShowContextualToolbar && ( { + if ( shouldUseKeyboardFocusShortcut ) { + focusToolbar(); + } + }; + // Focus on toolbar when pressing alt+F10 when the toolbar is visible. - useShortcut( 'core/block-editor/focus-toolbar', focusToolbar ); + useShortcut( 'core/block-editor/focus-toolbar', focusToolbarViaShortcut ); useEffect( () => { if ( initialFocusOnMount ) { @@ -147,6 +154,7 @@ function useToolbarFocus( function NavigableToolbar( { children, focusOnMount, + shouldUseKeyboardFocusShortcut = true, __experimentalInitialIndex: initialIndex, __experimentalOnIndexChange: onIndexChange, ...props @@ -159,7 +167,8 @@ function NavigableToolbar( { focusOnMount, isAccessibleToolbar, initialIndex, - onIndexChange + onIndexChange, + shouldUseKeyboardFocusShortcut ); if ( isAccessibleToolbar ) { diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js index 81fb256c1f2833..95681bb1943538 100644 --- a/packages/block-editor/src/private-apis.js +++ b/packages/block-editor/src/private-apis.js @@ -10,6 +10,7 @@ import ResizableBoxPopover from './components/resizable-box-popover'; import { ComposedPrivateInserter as PrivateInserter } from './components/inserter'; import { PrivateListView } from './components/list-view'; import BlockInfo from './components/block-info-slot-fill'; +import { useShouldContextualToolbarShow } from './utils/use-should-contextual-toolbar-show'; /** * Private @wordpress/block-editor APIs. @@ -24,4 +25,5 @@ lock( privateApis, { PrivateListView, ResizableBoxPopover, BlockInfo, + useShouldContextualToolbarShow, } ); diff --git a/packages/block-editor/src/utils/use-should-contextual-toolbar-show.js b/packages/block-editor/src/utils/use-should-contextual-toolbar-show.js new file mode 100644 index 00000000000000..c07e6fc07be605 --- /dev/null +++ b/packages/block-editor/src/utils/use-should-contextual-toolbar-show.js @@ -0,0 +1,75 @@ +/** + * WordPress dependencies + */ +import { useSelect } from '@wordpress/data'; +import { isUnmodifiedDefaultBlock } from '@wordpress/blocks'; +import { useViewportMatch } from '@wordpress/compose'; + +/** + * Internal dependencies + */ +import { store as blockEditorStore } from '../store'; +import { unlock } from '../lock-unlock'; + +/** + * Returns true if the contextual block toolbar should show, or false if it should be hidden. + * + * @param {string} clientId The client ID of the block. + * + * @return {boolean} Whether the block toolbar is hidden. + */ +export function useShouldContextualToolbarShow( clientId ) { + const isLargeViewport = useViewportMatch( 'medium' ); + + const { shouldShowContextualToolbar, canFocusHiddenToolbar } = useSelect( + ( select ) => { + const { + __unstableGetEditorMode, + isMultiSelecting, + isTyping, + isBlockInterfaceHidden, + getBlock, + getSettings, + isNavigationMode, + } = unlock( select( blockEditorStore ) ); + + const isEditMode = __unstableGetEditorMode() === 'edit'; + const hasFixedToolbar = getSettings().hasFixedToolbar; + const isDistractionFree = getSettings().isDistractionFree; + const hasClientId = !! clientId; + const isEmptyDefaultBlock = isUnmodifiedDefaultBlock( + getBlock( clientId ) || {} + ); + + const _shouldShowContextualToolbar = + isEditMode && + ! hasFixedToolbar && + ( ! isDistractionFree || isNavigationMode() ) && + isLargeViewport && + ! isMultiSelecting() && + ! isTyping() && + hasClientId && + ! isEmptyDefaultBlock && + ! isBlockInterfaceHidden(); + + const _canFocusHiddenToolbar = + isEditMode && + hasClientId && + ! _shouldShowContextualToolbar && + ! hasFixedToolbar && + ! isDistractionFree && + ! isEmptyDefaultBlock; + + return { + shouldShowContextualToolbar: _shouldShowContextualToolbar, + canFocusHiddenToolbar: _canFocusHiddenToolbar, + }; + }, + [ clientId, isLargeViewport ] + ); + + return { + shouldShowContextualToolbar, + canFocusHiddenToolbar, + }; +} diff --git a/packages/e2e-test-utils-playwright/src/editor/index.ts b/packages/e2e-test-utils-playwright/src/editor/index.ts index 68ad10bd107024..395fbb4f98b696 100644 --- a/packages/e2e-test-utils-playwright/src/editor/index.ts +++ b/packages/e2e-test-utils-playwright/src/editor/index.ts @@ -18,6 +18,7 @@ import { selectBlocks } from './select-blocks'; import { setContent } from './set-content'; import { showBlockToolbar } from './show-block-toolbar'; import { saveSiteEditorEntities } from './site-editor'; +import { setIsFixedToolbar } from './set-is-fixed-toolbar'; import { transformBlockTo } from './transform-block-to'; type EditorConstructorProps = { @@ -68,6 +69,9 @@ export class Editor { setContent: typeof setContent = setContent.bind( this ); /** @borrows showBlockToolbar as this.showBlockToolbar */ showBlockToolbar: typeof showBlockToolbar = showBlockToolbar.bind( this ); + /** @borrows setIsFixedToolbar as this.setIsFixedToolbar */ + setIsFixedToolbar: typeof setIsFixedToolbar = + setIsFixedToolbar.bind( this ); /** @borrows transformBlockTo as this.transformBlockTo */ transformBlockTo: typeof transformBlockTo = transformBlockTo.bind( this ); } diff --git a/packages/e2e-test-utils-playwright/src/editor/set-is-fixed-toolbar.ts b/packages/e2e-test-utils-playwright/src/editor/set-is-fixed-toolbar.ts new file mode 100644 index 00000000000000..2f66fac8692618 --- /dev/null +++ b/packages/e2e-test-utils-playwright/src/editor/set-is-fixed-toolbar.ts @@ -0,0 +1,21 @@ +/** + * Internal dependencies + */ +import type { Editor } from './index'; + +/** + * Toggles the fixed toolbar option. + * + * @param this + * @param isFixed Boolean value true/false for on/off. + */ +export async function setIsFixedToolbar( this: Editor, isFixed: boolean ) { + await this.page.evaluate( ( _isFixed ) => { + const { select, dispatch } = window.wp.data; + const isCurrentlyFixed = + select( 'core/edit-post' ).isFeatureActive( 'fixedToolbar' ); + if ( isCurrentlyFixed !== _isFixed ) { + dispatch( 'core/edit-post' ).toggleFeature( 'fixedToolbar' ); + } + }, isFixed ); +} diff --git a/packages/edit-post/src/components/header/header-toolbar/index.js b/packages/edit-post/src/components/header/header-toolbar/index.js index 55ffd265a96cfa..e28591f78e601f 100644 --- a/packages/edit-post/src/components/header/header-toolbar/index.js +++ b/packages/edit-post/src/components/header/header-toolbar/index.js @@ -8,6 +8,7 @@ import { NavigableToolbar, ToolSelector, store as blockEditorStore, + privateApis as blockEditorPrivateApis, } from '@wordpress/block-editor'; import { EditorHistoryRedo, @@ -23,6 +24,7 @@ import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; * Internal dependencies */ import { store as editPostStore } from '../../../store'; +import { unlock } from '../../../private-apis'; const preventDefault = ( event ) => { event.preventDefault(); @@ -39,15 +41,27 @@ function HeaderToolbar() { showIconLabels, isListViewOpen, listViewShortcut, + selectedBlockId, + hasFixedToolbar, } = useSelect( ( select ) => { - const { hasInserterItems, getBlockRootClientId, getBlockSelectionEnd } = - select( blockEditorStore ); + const { + hasInserterItems, + getBlockRootClientId, + getBlockSelectionEnd, + getSelectedBlockClientId, + getFirstMultiSelectedBlockClientId, + getSettings, + } = select( blockEditorStore ); const { getEditorSettings } = select( editorStore ); const { getEditorMode, isFeatureActive, isListViewOpened } = select( editPostStore ); const { getShortcutRepresentation } = select( keyboardShortcutsStore ); return { + hasFixedToolbar: getSettings().hasFixedToolbar, + selectedBlockId: + getSelectedBlockClientId() || + getFirstMultiSelectedBlockClientId(), // This setting (richEditingEnabled) should not live in the block editor's setting. isInserterEnabled: getEditorMode() === 'visual' && @@ -64,9 +78,19 @@ function HeaderToolbar() { ), }; }, [] ); + + const { useShouldContextualToolbarShow } = unlock( blockEditorPrivateApis ); + const isLargeViewport = useViewportMatch( 'medium' ); const isWideViewport = useViewportMatch( 'wide' ); - + const { shouldShowContextualToolbar, canFocusHiddenToolbar } = + useShouldContextualToolbarShow( selectedBlockId ); + // If there's a block toolbar to be focused, disable the focus shortcut for the document toolbar. + // There's a fixed block toolbar when the fixed toolbar option is enabled or when the browser width is less than the large viewport. + const blockToolbarCanBeFocused = + shouldShowContextualToolbar || + canFocusHiddenToolbar || + ( ( hasFixedToolbar || ! isLargeViewport ) && selectedBlockId ); /* translators: accessibility text for the editor toolbar */ const toolbarAriaLabel = __( 'Document tools' ); @@ -114,6 +138,7 @@ function HeaderToolbar() {
{ + await use( new ToolbarUtils( { page, pageUtils } ) ); + }, +} ); + +test.describe( 'Focus toolbar shortcut (alt + F10)', () => { + test.beforeEach( async ( { admin } ) => { + await admin.createNewPost(); + } ); + + test( 'Focuses correct toolbar in default view options in edit mode', async ( { + editor, + page, + toolbarUtils, + } ) => { + // Test: Focus the top level toolbar from title + await toolbarUtils.moveToToolbarShortcut(); + await expect( toolbarUtils.documentToolbarButton ).toBeFocused(); + + // Test: Focus document toolbar from empty block + await editor.insertBlock( { name: 'core/paragraph' } ); + await toolbarUtils.moveToToolbarShortcut(); + await expect( toolbarUtils.documentToolbarButton ).toBeFocused(); + + // Test: Focus block toolbar from block content when block toolbar isn't visible + await editor.insertBlock( { name: 'core/paragraph' } ); + await page.keyboard.type( + "Focus to block toolbar when block toolbar isn't visible" + ); + await toolbarUtils.moveToToolbarShortcut(); + await expect( toolbarUtils.blockToolbarParagraphButton ).toBeFocused(); + await expect( toolbarUtils.documentToolbarTooltip ).not.toBeVisible(); + + // Test: Focus block toolbar from block content when block toolbar is visible + await editor.insertBlock( { name: 'core/paragraph' } ); + await page.keyboard.type( + 'Focus to block toolbar when block toolbar is visible' + ); + // We need to force the toolbar to show. Otherwise, the bug from + // https://github.com/WordPress/gutenberg/pull/49644 won't surface in the e2e tests. + await editor.showBlockToolbar(); + await toolbarUtils.moveToToolbarShortcut(); + await expect( toolbarUtils.blockToolbarParagraphButton ).toBeFocused(); + await expect( toolbarUtils.documentToolbarTooltip ).not.toBeVisible(); + } ); + + test( 'Focuses correct toolbar in default view options in select mode', async ( { + editor, + page, + toolbarUtils, + } ) => { + // Test: Focus the document toolbar from title + await toolbarUtils.useSelectMode(); + await toolbarUtils.moveToToolbarShortcut(); + await expect( toolbarUtils.documentToolbarButton ).toBeFocused(); + + // Test: Focus the top level toolbar from empty block + await editor.insertBlock( { name: 'core/paragraph' } ); + await toolbarUtils.useSelectMode(); + await toolbarUtils.moveToToolbarShortcut(); + await expect( toolbarUtils.documentToolbarButton ).toBeFocused(); + + // Test: Focus the top level toolbar from paragraph block + await editor.insertBlock( { name: 'core/paragraph' } ); + await page.keyboard.type( + 'Focus top level toolbar from paragraph block in select mode.' + ); + await toolbarUtils.useSelectMode(); + await toolbarUtils.moveToToolbarShortcut(); + await expect( toolbarUtils.documentToolbarButton ).toBeFocused(); + } ); + + test.describe( 'In Top Toolbar option:', () => { + test.beforeEach( async ( { editor } ) => { + // Ensure the fixed toolbar option is on + await editor.setIsFixedToolbar( true ); + } ); + + test( 'Focuses the correct toolbar in edit mode', async ( { + editor, + page, + toolbarUtils, + } ) => { + // Test: Focus the document toolbar from title + await toolbarUtils.moveToToolbarShortcut(); + await expect( toolbarUtils.documentToolbarButton ).toBeFocused(); + + // Test: Focus the block toolbar from empty block + await editor.insertBlock( { name: 'core/paragraph' } ); + await toolbarUtils.moveToToolbarShortcut(); + await expect( + toolbarUtils.blockToolbarShowDocumentButton + ).toBeFocused(); + await expect( + toolbarUtils.documentToolbarTooltip + ).not.toBeVisible(); + + // Test: Focus the block toolbar from paragraph block with content + await editor.insertBlock( { name: 'core/paragraph' } ); + await page.keyboard.type( + 'Focus the block toolbar from paragraph block with content' + ); + await toolbarUtils.moveToToolbarShortcut(); + await expect( + toolbarUtils.blockToolbarShowDocumentButton + ).toBeFocused(); + await expect( + toolbarUtils.documentToolbarTooltip + ).not.toBeVisible(); + } ); + + test( 'Focuses the correct toolbar in select mode', async ( { + editor, + page, + toolbarUtils, + } ) => { + // Test: Focus the document toolbar from title + await toolbarUtils.useSelectMode(); + await toolbarUtils.moveToToolbarShortcut(); + await expect( toolbarUtils.documentToolbarButton ).toBeFocused(); + + // Test: Focus the block toolbar from empty block + await editor.insertBlock( { name: 'core/paragraph' } ); + await toolbarUtils.useSelectMode(); + await toolbarUtils.moveToToolbarShortcut(); + await expect( + toolbarUtils.blockToolbarShowDocumentButton + ).toBeFocused(); + await expect( + toolbarUtils.documentToolbarTooltip + ).not.toBeVisible(); + + // Test: Focus the block toolbar from paragraph in select mode + await editor.insertBlock( { name: 'core/paragraph' } ); + await page.keyboard.type( + 'Focus the block toolbar from paragraph in select mode' + ); + await toolbarUtils.useSelectMode(); + await toolbarUtils.moveToToolbarShortcut(); + await expect( + toolbarUtils.blockToolbarShowDocumentButton + ).toBeFocused(); + await expect( + toolbarUtils.documentToolbarTooltip + ).not.toBeVisible(); + } ); + } ); + + test.describe( 'Smaller than large viewports', () => { + test.use( { + // Make the viewport small enough to trigger the fixed toolbar + viewport: { + width: 700, + height: 700, + }, + } ); + + test.beforeEach( async ( { editor } ) => { + // Ensure the fixed toolbar option is off + await editor.setIsFixedToolbar( false ); + } ); + + test( 'Focuses the correct toolbar in edit mode', async ( { + editor, + page, + toolbarUtils, + } ) => { + // Test: Focus the document toolbar from title + await toolbarUtils.moveToToolbarShortcut(); + await expect( toolbarUtils.documentToolbarButton ).toBeFocused(); + + // Test: Focus the block toolbar from empty block + await editor.insertBlock( { name: 'core/paragraph' } ); + await toolbarUtils.moveToToolbarShortcut(); + await expect( + toolbarUtils.blockToolbarParagraphButton + ).toBeFocused(); + await expect( + toolbarUtils.documentToolbarTooltip + ).not.toBeVisible(); + + // Test: Focus the block toolbar from paragraph block with content + await editor.insertBlock( { name: 'core/paragraph' } ); + await page.keyboard.type( + 'Focus the block toolbar from paragraph block with content' + ); + await toolbarUtils.moveToToolbarShortcut(); + await expect( + toolbarUtils.blockToolbarParagraphButton + ).toBeFocused(); + await expect( + toolbarUtils.documentToolbarTooltip + ).not.toBeVisible(); + } ); + + test( 'Focuses the correct toolbar in select mode', async ( { + editor, + page, + toolbarUtils, + } ) => { + // Test: Focus the document toolbar from title + await toolbarUtils.useSelectMode(); + await toolbarUtils.moveToToolbarShortcut(); + await expect( toolbarUtils.documentToolbarButton ).toBeFocused(); + + // Test: Focus the block toolbar from empty block + await editor.insertBlock( { name: 'core/paragraph' } ); + await toolbarUtils.useSelectMode(); + await toolbarUtils.moveToToolbarShortcut(); + await expect( + toolbarUtils.blockToolbarParagraphButton + ).toBeFocused(); + await expect( + toolbarUtils.documentToolbarTooltip + ).not.toBeVisible(); + + // Test: Focus the block toolbar from paragraph in select mode + await editor.insertBlock( { name: 'core/paragraph' } ); + await page.keyboard.type( + 'Focus the block toolbar from paragraph in select mode' + ); + await toolbarUtils.useSelectMode(); + await toolbarUtils.moveToToolbarShortcut(); + await expect( + toolbarUtils.blockToolbarParagraphButton + ).toBeFocused(); + await expect( + toolbarUtils.documentToolbarTooltip + ).not.toBeVisible(); + } ); + } ); +} ); + +class ToolbarUtils { + constructor( { page, pageUtils } ) { + this.page = page; + this.pageUtils = pageUtils; + + this.documentToolbarButton = this.page.getByRole( 'button', { + name: 'Toggle block inserter', + exact: true, + } ); + this.documentToolbarTooltip = this.page.locator( + 'text=Toggle block inserter' + ); + this.blockToolbarParagraphButton = this.page.getByRole( 'button', { + name: 'Paragraph', + exact: true, + } ); + this.blockToolbarShowDocumentButton = this.page.getByRole( 'button', { + name: 'Show document tools', + exact: true, + } ); + } + + async useSelectMode() { + await this.page.keyboard.press( 'Escape' ); + } + + async moveToToolbarShortcut() { + await this.pageUtils.pressKeys( 'alt+F10' ); + } +}