From c1dd5b25180e4473244e720fa2988e81285ce1d5 Mon Sep 17 00:00:00 2001 From: Siobhan Bamber Date: Mon, 23 Oct 2023 16:09:27 +0100 Subject: [PATCH] Cherry-picked commits for WordPress 6.4 RC2 (#55481) * Focus submenu button when clicked (#55198) * Focus element manually when open submenu on click * Try using `tabindex="-1"` * Use `tabindex="-1"` also in body when a submenu is opened * Replace tabindex with event listener * Explain the tabindex on
  • * Don't store the element on hover to restore the focus later * Improve explanations * Add tests to cover webkit frontend menu interactions Safari doesn't place focus on a clicked button as expected. These tests verify that when a submenu chevron button is clicked, focus is correctly placed on that button. It also verifies that the click on the body correctly closes any open submenus, which was failing in Safari. * Focus clicked button on Safari Combining the tabindex -1 on the parent li and focusing the button on Safari, and also checking that the relatedTarget is null inside the handleMenuFocusout seems to contain the focus within the menu to not fire the handleMenuFocusout as often, and still works to click on the body to close the menu. * Added the document.addEventListener body click back in Authored by Luis Herranz . I'm just re-applying the change. * Remove tab keypresses from webkit menu interaction tests Tab keypreses on webkit playwright are really flakey (or it's something in our code that we haven't isolated) so I've split out the webkit tests to test everything I can without using a tab keypress. * Use body click instead for consistency across environments --------- Co-authored-by: Luis Herranz Co-authored-by: Jerry Jones * Make layout support compatible with enhanced pagination (#55416) * Make layout supports compatible with enhanced pagination * Use sanitize_title and add `layout` to the class name * Update default fullscreen icon for lightbox trigger (#55463) * Make duotone compatible with enhanced pagination (#55415) * Patterns: fix capabilities settings for pattern categories (#55379) Co-authored-by: Daniel Richards * Revert "Patterns: fix capabilities settings for pattern categories (#55379)" This reverts commit 6f83c92cf00e04af94847954ca7dd9acc9562298. * Image block: wrap images with hrefs in an A tag (#55470) * This commit sees what happens when we wrap the image element in an A tag in the editor. This is to ensure any inherited styles from the link element, such as border color, apply to the image. * Removing duplicate wrapper Adding disabled onClick and aria attribute * Image: Improve focus management in lightbox (#55428) * Improve focus management This commit removes the logic to set focus differently based on event.pointerType and instead sets focus on the dialog itself when the lightbox opens, and on the lightbox trigger when the lightbox closes. * Add delay before focusing when closing lightbox * Put focus back on close button when opening lightbox It turns out that placing focus on the modal was causing inconsistent behavior in Safari, so I've put the focus back on the close button when the lightbox opens, which performs predictably. I've also added a tabindex to the image, which prevents the focus ring from erroneously showing when opening the lightbox with a mouse in Chrome and Firefox. * Move focus to the dialog when opening the lightbox. * Fix SVG markup. * Consistent indentation with spaces. * Remove unnecessary tabindex --------- Co-authored-by: Andrea Fercia * Fix: - Update the title when using enhanced pagination --------- Co-authored-by: Mario Santos <34552881+SantosGuillamot@users.noreply.github.com> Co-authored-by: Luis Herranz Co-authored-by: Jerry Jones Co-authored-by: Artemio Morales Co-authored-by: Glen Davies Co-authored-by: Daniel Richards Co-authored-by: Ramon Co-authored-by: Andrea Fercia --- lib/block-supports/layout.php | 31 ++- lib/class-wp-duotone-gutenberg.php | 1 - packages/block-library/src/image/image.js | 15 +- packages/block-library/src/image/index.php | 11 +- packages/block-library/src/image/view.js | 32 ++- .../block-library/src/navigation/index.php | 7 + packages/block-library/src/navigation/view.js | 24 ++- packages/interactivity/CHANGELOG.md | 8 + packages/interactivity/src/router.js | 7 +- .../navigation-frontend-interactivity.spec.js | 184 +++++++++++++++++- .../fixtures/interactivity-utils.ts | 1 + .../interactivity/router-regions.spec.ts | 14 ++ 12 files changed, 292 insertions(+), 43 deletions(-) diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index 0fe217cf83270..80be02db68360 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -527,6 +527,26 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support return ''; } +/** + * Generates an incremental ID that is independent per each different prefix. + * + * It is similar to `wp_unique_id`, but each prefix has it's own internal ID + * counter to make each prefix independent from each other. The ID starts at 1 + * and increments on each call. The returned value is not universally unique, + * but it is unique across the life of the PHP process and it's stable per + * prefix. + * + * @param string $prefix Prefix for the returned ID. + * @return string Incremental ID per prefix. + */ +function gutenberg_incremental_id_per_prefix( $prefix = '' ) { + static $id_counters = array(); + if ( ! array_key_exists( $prefix, $id_counters ) ) { + $id_counters[ $prefix ] = 0; + } + return $prefix . (string) ++$id_counters[ $prefix ]; +} + /** * Renders the layout config to the block wrapper. * @@ -608,7 +628,16 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) { $class_names = array(); $layout_definitions = gutenberg_get_layout_definitions(); - $container_class = wp_unique_id( 'wp-container-' ); + + /* + * We use an incremental ID that is independent per prefix to make sure that + * rendering different numbers of blocks doesn't affect the IDs of other + * blocks. We need this to make the CSS class names stable across paginations + * for features like the enhanced pagination of the Query block. + */ + $container_class = gutenberg_incremental_id_per_prefix( + 'wp-container-' . sanitize_title( $block['blockName'] ) . '-layout-' + ); // Set the correct layout type for blocks using legacy content width. if ( isset( $used_layout['inherit'] ) && $used_layout['inherit'] || isset( $used_layout['contentSize'] ) && $used_layout['contentSize'] ) { diff --git a/lib/class-wp-duotone-gutenberg.php b/lib/class-wp-duotone-gutenberg.php index 270c13875787f..a77df3eacaca9 100644 --- a/lib/class-wp-duotone-gutenberg.php +++ b/lib/class-wp-duotone-gutenberg.php @@ -836,7 +836,6 @@ public static function render_duotone_support( $block_content, $block ) { $has_global_styles_duotone = array_key_exists( $block['blockName'], self::$global_styles_block_names ); if ( - empty( $block_content ) || ! $duotone_selector || ( ! $has_duotone_attribute && ! $has_global_styles_duotone ) ) { diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index 1f602c4380e88..321e19a645637 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -83,6 +83,11 @@ const scaleOptions = [ }, ]; +const disabledClickProps = { + onClick: ( event ) => event.preventDefault(), + 'aria-disabled': true, +}; + export default function Image( { temporaryURL, attributes, @@ -725,7 +730,6 @@ export default function Image( { } } /* eslint-enable no-lonely-if */ - img = ( tag to trigger any inherited link element styles */ } + { !! href ? ( + + { img } + + ) : ( + img + ) } { showCaption && ( ! RichText.isEmpty( caption ) || isSelected ) && ( - - - - - + '; @@ -322,12 +320,13 @@ function block_core_image_render_lightbox( $block_content, $block ) { data-wp-on--touchmove="actions.core.image.handleTouchMove" data-wp-on--touchend="actions.core.image.handleTouchEnd" data-wp-on--click="actions.core.image.hideLightbox" + tabindex="-1" > - + HTML; diff --git a/packages/block-library/src/image/view.js b/packages/block-library/src/image/view.js index 30d1259637e3d..331c0e79c731f 100644 --- a/packages/block-library/src/image/view.js +++ b/packages/block-library/src/image/view.js @@ -135,7 +135,7 @@ store( false ); }, - hideLightbox: async ( { context, event } ) => { + hideLightbox: async ( { context } ) => { context.core.image.hideAnimationEnabled = true; if ( context.core.image.lightboxEnabled ) { // We want to wait until the close animation is completed @@ -149,19 +149,15 @@ store( 'scroll', scrollCallback ); + // If we don't delay before changing the focus, + // the focus ring will appear on Firefox before + // the image has finished animating, which looks broken. + context.core.image.lightboxTriggerRef.focus( { + preventScroll: true, + } ); }, 450 ); context.core.image.lightboxEnabled = false; - - // We want to avoid drawing attention to the button - // after the lightbox closes for mouse and touch users. - // Note that the `event.pointerType` property returns - // as an empty string if a keyboard fired the event. - if ( event.pointerType === '' ) { - context.core.image.lastFocusedElement.focus( { - preventScroll: true, - } ); - } } }, handleKeydown: ( { context, actions, event } ) => { @@ -266,6 +262,10 @@ store( image: { initOriginImage: ( { context, ref } ) => { context.core.image.imageRef = ref; + context.core.image.lightboxTriggerRef = + ref.parentElement.querySelector( + '.lightbox-trigger' + ); if ( ref.complete ) { context.core.image.imageLoaded = true; context.core.image.imageCurrentSrc = ref.currentSrc; @@ -282,14 +282,8 @@ store( focusableElements.length - 1 ]; - // We want to avoid drawing unnecessary attention to the close - // button for mouse and touch users. Note that even if opening - // the lightbox via keyboard, the event fired is of type - // `pointerEvent`, so we need to rely on the `event.pointerType` - // property, which returns an empty string for keyboard events. - if ( context.core.image.pointerType === '' ) { - ref.querySelector( '.close-button' ).focus(); - } + // Move focus to the dialog when opening it. + ref.focus(); } }, setButtonStyles: ( { context, ref } ) => { diff --git a/packages/block-library/src/navigation/index.php b/packages/block-library/src/navigation/index.php index c4854fbc4b26d..4d9fe4a08c6bf 100644 --- a/packages/block-library/src/navigation/index.php +++ b/packages/block-library/src/navigation/index.php @@ -90,6 +90,13 @@ function block_core_navigation_add_directives_to_submenu( $w, $block_attributes $w->set_attribute( 'data-wp-effect', 'effects.core.navigation.initMenu' ); $w->set_attribute( 'data-wp-on--focusout', 'actions.core.navigation.handleMenuFocusout' ); $w->set_attribute( 'data-wp-on--keydown', 'actions.core.navigation.handleMenuKeydown' ); + + // This is a fix for Safari. Without it, Safari doesn't change the active + // element when the user clicks on a button. It can be removed once we add + // an overlay to capture the clicks, instead of relying on the focusout + // event. + $w->set_attribute( 'tabindex', '-1' ); + if ( ! isset( $block_attributes['openSubmenusOnClick'] ) || false === $block_attributes['openSubmenusOnClick'] ) { $w->set_attribute( 'data-wp-on--mouseenter', 'actions.core.navigation.openMenuOnHover' ); $w->set_attribute( 'data-wp-on--mouseleave', 'actions.core.navigation.closeMenuOnHover' ); diff --git a/packages/block-library/src/navigation/view.js b/packages/block-library/src/navigation/view.js index c0853b2814e2b..bad36f6240134 100644 --- a/packages/block-library/src/navigation/view.js +++ b/packages/block-library/src/navigation/view.js @@ -13,10 +13,14 @@ const focusableSelectors = [ '[tabindex]:not([tabindex^="-"])', ]; +// This is a fix for Safari in iOS/iPadOS. Without it, Safari doesn't focus out +// when the user taps in the body. It can be removed once we add an overlay to +// capture the clicks, instead of relying on the focusout event. +document.addEventListener( 'click', () => {} ); + const openMenu = ( store, menuOpenedOn ) => { - const { context, ref, selectors } = store; + const { context, selectors } = store; selectors.core.navigation.menuOpenedBy( store )[ menuOpenedOn ] = true; - context.core.navigation.previousFocus = ref; if ( context.core.navigation.type === 'overlay' ) { // Add a `has-modal-open` class to the root. document.documentElement.classList.add( 'has-modal-open' ); @@ -33,7 +37,7 @@ const closeMenu = ( store, menuClosedOn ) => { window.document.activeElement ) ) { - context.core.navigation.previousFocus.focus(); + context.core.navigation.previousFocus?.focus(); } context.core.navigation.modal = null; context.core.navigation.previousFocus = null; @@ -130,6 +134,8 @@ wpStore( { closeMenu( store, 'hover' ); }, openMenuOnClick( store ) { + const { context, ref } = store; + context.core.navigation.previousFocus = ref; openMenu( store, 'click' ); }, closeMenuOnClick( store ) { @@ -140,13 +146,16 @@ wpStore( { openMenu( store, 'focus' ); }, toggleMenuOnClick: ( store ) => { - const { selectors } = store; + const { selectors, context, ref } = store; + // Safari won't send focus to the clicked element, so we need to manually place it: https://bugs.webkit.org/show_bug.cgi?id=22261 + if ( window.document.activeElement !== ref ) ref.focus(); const menuOpenedBy = selectors.core.navigation.menuOpenedBy( store ); if ( menuOpenedBy.click || menuOpenedBy.focus ) { closeMenu( store, 'click' ); closeMenu( store, 'focus' ); } else { + context.core.navigation.previousFocus = ref; openMenu( store, 'click' ); } }, @@ -194,11 +203,14 @@ wpStore( { // event.relatedTarget === The element receiving focus (if any) // When focusout is outsite the document, // `window.document.activeElement` doesn't change. + + // The event.relatedTarget is null when something outside the navigation menu is clicked. This is only necessary for Safari. if ( - ! context.core.navigation.modal?.contains( + event.relatedTarget === null || + ( ! context.core.navigation.modal?.contains( event.relatedTarget ) && - event.target !== window.document.activeElement + event.target !== window.document.activeElement ) ) { closeMenu( store, 'click' ); closeMenu( store, 'focus' ); diff --git a/packages/interactivity/CHANGELOG.md b/packages/interactivity/CHANGELOG.md index 736c8660cb210..a7f3180b2172a 100644 --- a/packages/interactivity/CHANGELOG.md +++ b/packages/interactivity/CHANGELOG.md @@ -2,6 +2,14 @@ ## Unreleased +### Bug Fix + +- Update the title when using enhanced pagination. ([#55446](https://github.com/WordPress/gutenberg/pull/55446)) + +## 2.5.0 (2023-10-18) + +## 2.4.0 (2023-10-05) + ## 2.3.0 (2023-09-20) ### Enhancements diff --git a/packages/interactivity/src/router.js b/packages/interactivity/src/router.js index ee9755126994e..68d1bc677addf 100644 --- a/packages/interactivity/src/router.js +++ b/packages/interactivity/src/router.js @@ -55,8 +55,8 @@ const regionsToVdom = ( dom ) => { const id = region.getAttribute( attrName ); regions[ id ] = toVdom( region ); } ); - - return { regions }; + const title = dom.querySelector( 'title' )?.innerText; + return { regions, title }; }; // Prefetch a page. We store the promise to avoid triggering a second fetch for @@ -76,6 +76,9 @@ const renderRegions = ( page ) => { const fragment = getRegionRootFragment( region ); render( page.regions[ id ], fragment ); } ); + if ( page.title ) { + document.title = page.title; + } }; // Variable to store the current navigation. diff --git a/test/e2e/specs/editor/blocks/navigation-frontend-interactivity.spec.js b/test/e2e/specs/editor/blocks/navigation-frontend-interactivity.spec.js index 1c745d7a0e57b..2082a128780f7 100644 --- a/test/e2e/specs/editor/blocks/navigation-frontend-interactivity.spec.js +++ b/test/e2e/specs/editor/blocks/navigation-frontend-interactivity.spec.js @@ -80,6 +80,51 @@ test.describe( 'Navigation block - Frontend interactivity', () => { await expect( overlayMenuFirstElement ).toBeHidden(); await expect( openMenuButton ).toBeFocused(); } ); + + /** + * These are already tested within the Overlay Interactions test above, but Safari is flakey on the Tab + * keypresses (passes 50 - 70% of the time). Tab keypresses are testing fine manually in Safari, but not + * in the test. nce we figure out why the Tab keypresses are flakey in the test, we can + * remove this test and only rely on the Overlay Interactions test above and add a (@firefox, @webkit) + * directive to the describe() statement. https://github.com/WordPress/gutenberg/pull/55198 + */ + test( 'Overlay menu interactions in Safari (@webkit)', async ( { + page, + pageUtils, + } ) => { + await page.goto( '/' ); + const overlayMenuFirstElement = page.getByRole( 'link', { + name: 'Item 1', + } ); + const openMenuButton = page.getByRole( 'button', { + name: 'Open menu', + } ); + + const closeMenuButton = page.getByRole( 'button', { + name: 'Close menu', + } ); + + // Test: overlay menu opens on click on open menu button + await expect( overlayMenuFirstElement ).toBeHidden(); + await openMenuButton.click(); + await expect( overlayMenuFirstElement ).toBeVisible(); + + // Test: overlay menu focuses on first element after opening + await expect( overlayMenuFirstElement ).toBeFocused(); + + // Not Tested: overlay menu traps focus + + // Test: overlay menu closes on click on close menu button + await closeMenuButton.click(); + await expect( overlayMenuFirstElement ).toBeHidden(); + + // Test: overlay menu closes on ESC key + await openMenuButton.click(); + await expect( overlayMenuFirstElement ).toBeVisible(); + await pageUtils.pressKeys( 'Escape' ); + await expect( overlayMenuFirstElement ).toBeHidden(); + await expect( openMenuButton ).toBeFocused(); + } ); } ); test.describe( 'Submenu mouse and keyboard interactions', () => { @@ -133,10 +178,14 @@ test.describe( 'Navigation block - Frontend interactivity', () => { const secondLevelElement = page.getByRole( 'link', { name: 'Nested Submenu Link 1', } ); + const lastFirstLevelElement = page.getByRole( 'link', { + name: 'Complex Submenu Link 2', + } ); // Test: submenu opens on click await expect( innerElement ).toBeHidden(); await simpleSubmenuButton.click(); + await expect( simpleSubmenuButton ).toBeFocused(); await expect( innerElement ).toBeVisible(); // Test: submenu closes on click outside submenu @@ -145,10 +194,12 @@ test.describe( 'Navigation block - Frontend interactivity', () => { // Test: nested submenu opens on click await complexSubmenuButton.click(); + await expect( complexSubmenuButton ).toBeFocused(); await expect( firstLevelElement ).toBeVisible(); await expect( secondLevelElement ).toBeHidden(); await nestedSubmenuButton.click(); + await expect( nestedSubmenuButton ).toBeFocused(); await expect( firstLevelElement ).toBeVisible(); await expect( secondLevelElement ).toBeVisible(); @@ -160,6 +211,7 @@ test.describe( 'Navigation block - Frontend interactivity', () => { // Test: submenu opens on Enter keypress await simpleSubmenuButton.focus(); await pageUtils.pressKeys( 'Enter' ); + await expect( simpleSubmenuButton ).toBeFocused(); await expect( innerElement ).toBeVisible(); // Test: submenu closes on ESC key and focuses parent link @@ -168,36 +220,127 @@ test.describe( 'Navigation block - Frontend interactivity', () => { await expect( simpleSubmenuButton ).toBeFocused(); // Test: submenu closes on tab outside submenu - await simpleSubmenuButton.focus(); await pageUtils.pressKeys( 'Enter' ); + await expect( simpleSubmenuButton ).toBeFocused(); await expect( innerElement ).toBeVisible(); // Tab to first element, then tab outside the submenu. await pageUtils.pressKeys( 'Tab', { times: 2, delay: 50 } ); - await expect( innerElement ).toBeHidden(); await expect( complexSubmenuButton ).toBeFocused(); + await expect( innerElement ).toBeHidden(); // Test: only nested submenu closes on tab outside - await complexSubmenuButton.focus(); await pageUtils.pressKeys( 'Enter' ); + await expect( complexSubmenuButton ).toBeFocused(); await expect( firstLevelElement ).toBeVisible(); await expect( secondLevelElement ).toBeHidden(); await nestedSubmenuButton.click(); + await expect( nestedSubmenuButton ).toBeFocused(); await expect( firstLevelElement ).toBeVisible(); await expect( secondLevelElement ).toBeVisible(); // Tab to nested submenu first element, then tab outside the nested // submenu. await pageUtils.pressKeys( 'Tab', { times: 2, delay: 50 } ); + await expect( lastFirstLevelElement ).toBeFocused(); await expect( firstLevelElement ).toBeVisible(); await expect( secondLevelElement ).toBeHidden(); // Tab outside the complex submenu. await page.keyboard.press( 'Tab' ); await expect( firstLevelElement ).toBeHidden(); } ); + + /** + * These are already tested within the Submenu Interactions test above, but Safari is flakey on the + * Tab keypresses (passes 50 - 70% of the time). Tab keypresses are testing fine manually in Safari, + * but not in the test. Once we figure out why the Tab keypresses are flakey in the test, we can + * remove this test and only rely on the Submenu interactions test above and add a (@firefox, @webkit) + * directive to the describe() statement. https://github.com/WordPress/gutenberg/pull/55198 + */ + test( 'Submenu interactions on Safari (@webkit)', async ( { + page, + pageUtils, + } ) => { + await page.goto( '/' ); + const simpleSubmenuButton = page.getByRole( 'button', { + name: 'Simple Submenu', + } ); + const innerElement = page.getByRole( 'link', { + name: 'Simple Submenu Link 1', + } ); + const complexSubmenuButton = page.getByRole( 'button', { + name: 'Complex Submenu', + } ); + const nestedSubmenuButton = page.getByRole( 'button', { + name: 'Nested Submenu', + } ); + const firstLevelElement = page.getByRole( 'link', { + name: 'Complex Submenu Link 1', + } ); + const secondLevelElement = page.getByRole( 'link', { + name: 'Nested Submenu Link 1', + } ); + + // Test: submenu opens on click and focuses the button + await expect( innerElement ).toBeHidden(); + await simpleSubmenuButton.click(); + await expect( simpleSubmenuButton ).toBeFocused(); + await expect( innerElement ).toBeVisible(); + + // Test: a second click closes the submenu + await simpleSubmenuButton.click(); + await expect( simpleSubmenuButton ).toBeFocused(); + await expect( innerElement ).toBeHidden(); + + // Test: submenu opens on Enter keypress + await simpleSubmenuButton.focus(); + await pageUtils.pressKeys( 'Enter' ); + await expect( simpleSubmenuButton ).toBeFocused(); + await expect( innerElement ).toBeVisible(); + + // Test: submenu closes on second Enter keypress + await pageUtils.pressKeys( 'Enter' ); + await expect( innerElement ).toBeHidden(); + await expect( simpleSubmenuButton ).toBeFocused(); + + // Test: inner submenu opens on click and focuses the button + await complexSubmenuButton.click(); + await expect( complexSubmenuButton ).toBeFocused(); + await expect( firstLevelElement ).toBeVisible(); + await expect( secondLevelElement ).toBeHidden(); + // Click the inner menu button and check it opens the third level menu + await nestedSubmenuButton.click(); + await expect( nestedSubmenuButton ).toBeFocused(); + await expect( firstLevelElement ).toBeVisible(); + await expect( secondLevelElement ).toBeVisible(); + + // Click the inner menu button and check it closes the third level menu + await nestedSubmenuButton.click(); + await expect( nestedSubmenuButton ).toBeFocused(); + await expect( firstLevelElement ).toBeVisible(); + await expect( secondLevelElement ).toBeHidden(); + + // Do the same with Enter keypresses: open the third level menu + await pageUtils.pressKeys( 'Enter' ); + await expect( nestedSubmenuButton ).toBeFocused(); + await expect( firstLevelElement ).toBeVisible(); + await expect( secondLevelElement ).toBeVisible(); + + // Close the third level menu + await pageUtils.pressKeys( 'Enter' ); + await expect( nestedSubmenuButton ).toBeFocused(); + await expect( firstLevelElement ).toBeVisible(); + await expect( secondLevelElement ).toBeHidden(); + + // Close the menu via click on the body + await page.click( 'body' ); + await expect( firstLevelElement ).toBeHidden(); + + // Tests not covered: Tabbing to close menus + } ); } ); - test.describe( 'Submenus (Arrow setting)', () => { + test.describe( 'Submenus (Arrow setting) (@firefox, @webkit)', () => { test.beforeEach( async ( { admin, editor, requestUtils } ) => { await admin.visitSiteEditor( { postId: 'emptytheme//header', @@ -222,7 +365,7 @@ test.describe( 'Navigation block - Frontend interactivity', () => { await editor.saveSiteEditorEntities(); } ); - test( 'submenu opens on click in the arrow', async ( { page } ) => { + test( 'submenu click on the arrow interactions', async ( { page } ) => { await page.goto( '/' ); const arrowButton = page.getByRole( 'button', { name: 'Submenu submenu', @@ -239,19 +382,48 @@ test.describe( 'Navigation block - Frontend interactivity', () => { await expect( firstLevelElement ).toBeHidden(); await expect( secondLevelElement ).toBeHidden(); + // Open first submenu level await arrowButton.click(); + await expect( arrowButton ).toBeFocused(); await expect( firstLevelElement ).toBeVisible(); await expect( secondLevelElement ).toBeHidden(); + + // Close first submenu level, check that it closes and focus is on the arrow button + await arrowButton.click(); + await expect( arrowButton ).toBeFocused(); + // Move the mouse so the hover on the button doesn't keep the menu open + await page.mouse.move( 400, 400 ); + await expect( firstLevelElement ).toBeHidden(); + await expect( secondLevelElement ).toBeHidden(); + + // Open first submenu level one more time so we can test the nested submenu + await arrowButton.click(); + await expect( arrowButton ).toBeFocused(); + await expect( firstLevelElement ).toBeVisible(); + await expect( secondLevelElement ).toBeHidden(); + + // Nested submenu open await nestedSubmenuArrowButton.click(); + await expect( nestedSubmenuArrowButton ).toBeFocused(); await expect( firstLevelElement ).toBeVisible(); await expect( secondLevelElement ).toBeVisible(); + + // Nested submenu close + await nestedSubmenuArrowButton.click(); + await expect( nestedSubmenuArrowButton ).toBeFocused(); + // Move the mouse so the hover on the button doesn't keep the menu open + await page.mouse.move( 400, 400 ); + await expect( firstLevelElement ).toBeVisible(); + await expect( secondLevelElement ).toBeHidden(); + + // Close menu via click on the body await page.click( 'body' ); await expect( firstLevelElement ).toBeHidden(); await expect( secondLevelElement ).toBeHidden(); } ); } ); - test.describe( 'Page list block', () => { + test.describe( 'Page list block (@firefox, @webkit)', () => { test.beforeEach( async ( { admin, editor, requestUtils } ) => { const parentPage = await requestUtils.createPage( { title: 'Parent Page', diff --git a/test/e2e/specs/interactivity/fixtures/interactivity-utils.ts b/test/e2e/specs/interactivity/fixtures/interactivity-utils.ts index fc0dc4b30d664..44507ad34813e 100644 --- a/test/e2e/specs/interactivity/fixtures/interactivity-utils.ts +++ b/test/e2e/specs/interactivity/fixtures/interactivity-utils.ts @@ -47,6 +47,7 @@ export default class InteractivityUtils { content: ``, status: 'publish' as 'publish', date_gmt: '2023-01-01T00:00:00', + title: alias, }; const { link } = await this.requestUtils.createPost( payload ); diff --git a/test/e2e/specs/interactivity/router-regions.spec.ts b/test/e2e/specs/interactivity/router-regions.spec.ts index cbe66b7bd1b21..f1ea308d6c256 100644 --- a/test/e2e/specs/interactivity/router-regions.spec.ts +++ b/test/e2e/specs/interactivity/router-regions.spec.ts @@ -97,4 +97,18 @@ test.describe( 'Router regions', () => { await page.getByTestId( 'back' ).click(); await expect( nestedRegionSsr ).toHaveText( 'content from page 1' ); } ); + + test( 'Page title is updated 2', async ( { page } ) => { + await expect( page ).toHaveTitle( + 'router regions – page 1 – gutenberg' + ); + await page.getByTestId( 'next' ).click(); + await expect( page ).toHaveTitle( + 'router regions – page 2 – gutenberg' + ); + await page.getByTestId( 'back' ).click(); + await expect( page ).toHaveTitle( + 'router regions – page 1 – gutenberg' + ); + } ); } );