Skip to content

Commit

Permalink
Cherry-picked commits for WordPress 6.4 RC2 (#55481)
Browse files Browse the repository at this point in the history
* 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 <li>

* 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 <[email protected]>. 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 <[email protected]>
Co-authored-by: Jerry Jones <[email protected]>

* 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 <[email protected]>

* Revert "Patterns: fix capabilities settings for pattern categories (#55379)"

This reverts commit 6f83c92.

* 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 <a href /> 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 <[email protected]>

* Fix: - Update the title when using enhanced pagination

---------

Co-authored-by: Mario Santos <[email protected]>
Co-authored-by: Luis Herranz <[email protected]>
Co-authored-by: Jerry Jones <[email protected]>
Co-authored-by: Artemio Morales <[email protected]>
Co-authored-by: Glen Davies <[email protected]>
Co-authored-by: Daniel Richards <[email protected]>
Co-authored-by: Ramon <[email protected]>
Co-authored-by: Andrea Fercia <[email protected]>
  • Loading branch information
9 people authored Oct 23, 2023
1 parent 9ef7560 commit c1dd5b2
Show file tree
Hide file tree
Showing 12 changed files with 292 additions and 43 deletions.
31 changes: 30 additions & 1 deletion lib/block-supports/layout.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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'] ) {
Expand Down
1 change: 0 additions & 1 deletion lib/class-wp-duotone-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
) {
Expand Down
15 changes: 13 additions & 2 deletions packages/block-library/src/image/image.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ const scaleOptions = [
},
];

const disabledClickProps = {
onClick: ( event ) => event.preventDefault(),
'aria-disabled': true,
};

export default function Image( {
temporaryURL,
attributes,
Expand Down Expand Up @@ -725,7 +730,6 @@ export default function Image( {
}
}
/* eslint-enable no-lonely-if */

img = (
<ResizableBox
style={ {
Expand Down Expand Up @@ -784,7 +788,14 @@ export default function Image( {
{ /* Hide controls during upload to avoid component remount,
which causes duplicated image upload. */ }
{ ! temporaryURL && controls }
{ img }
{ /* If the image has a href, wrap in an <a /> tag to trigger any inherited link element styles */ }
{ !! href ? (
<a href={ href } { ...disabledClickProps }>
{ img }
</a>
) : (
img
) }
{ showCaption &&
( ! RichText.isEmpty( caption ) || isSelected ) && (
<RichText
Expand Down
11 changes: 5 additions & 6 deletions packages/block-library/src/image/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ function block_core_image_render_lightbox( $block_content, $block ) {
$button =
$img[0]
. '<button
class="lightbox-trigger"
type="button"
aria-haspopup="dialog"
aria-label="' . esc_attr( $aria_label ) . '"
Expand All @@ -243,11 +244,8 @@ function block_core_image_render_lightbox( $block_content, $block ) {
data-wp-style--top="context.core.image.imageButtonTop"
style="background: #000"
>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M9 5H5V9" stroke="#FFFFFF" stroke-width="1.5"/>
<path d="M15 19L19 19L19 15" stroke="#FFFFFF" stroke-width="1.5"/>
<path d="M15 5H19V9" stroke="#FFFFFF" stroke-width="1.5"/>
<path d="M9 19L5 19L5 15" stroke="#FFFFFF" stroke-width="1.5"/>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false">
<Path stroke="#FFFFFF" d="M6 4a2 2 0 0 0-2 2v3h1.5V6a.5.5 0 0 1 .5-.5h3V4H6Zm3 14.5H6a.5.5 0 0 1-.5-.5v-3H4v3a2 2 0 0 0 2 2h3v-1.5Zm6 1.5v-1.5h3a.5.5 0 0 0 .5-.5v-3H20v3a2 2 0 0 1-2 2h-3Zm3-16a2 2 0 0 1 2 2v3h-1.5V6a.5.5 0 0 0-.5-.5h-3V4h3Z" />
</svg>
</button>';

Expand Down Expand Up @@ -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"
>
<button type="button" aria-label="$close_button_label" style="fill: $close_button_color" class="close-button" data-wp-on--click="actions.core.image.hideLightbox">
$close_button_icon
</button>
<div class="lightbox-image-container">$initial_image_content</div>
<div class="lightbox-image-container">$enlarged_image_content</div>
<div class="lightbox-image-container">$enlarged_image_content</div>
<div class="scrim" style="background-color: $background_color" aria-hidden="true"></div>
</div>
HTML;
Expand Down
32 changes: 13 additions & 19 deletions packages/block-library/src/image/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 } ) => {
Expand Down Expand Up @@ -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;
Expand All @@ -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 } ) => {
Expand Down
7 changes: 7 additions & 0 deletions packages/block-library/src/navigation/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' );
Expand Down
24 changes: 18 additions & 6 deletions packages/block-library/src/navigation/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 <html> root.
document.documentElement.classList.add( 'has-modal-open' );
Expand All @@ -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;
Expand Down Expand Up @@ -130,6 +134,8 @@ wpStore( {
closeMenu( store, 'hover' );
},
openMenuOnClick( store ) {
const { context, ref } = store;
context.core.navigation.previousFocus = ref;
openMenu( store, 'click' );
},
closeMenuOnClick( store ) {
Expand All @@ -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' );
}
},
Expand Down Expand Up @@ -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' );
Expand Down
8 changes: 8 additions & 0 deletions packages/interactivity/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 5 additions & 2 deletions packages/interactivity/src/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down
Loading

1 comment on commit c1dd5b2

@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 c1dd5b2.
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/6615239827
📝 Reported issues:

Please sign in to comment.