Skip to content

Commit

Permalink
Navigation block: Do not toggle aria-expanded on hover when the overl…
Browse files Browse the repository at this point in the history
…ay menu is opened (#52170)

* Fix aria expanded when overlay is open

* Update deepsignal and remove old packages

* Remove unnecessary initial context

* Rename OpenBy to OpenedBy
  • Loading branch information
luisherranz authored Jul 5, 2023
1 parent b3d878a commit 09bf154
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 84 deletions.
34 changes: 16 additions & 18 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 0 additions & 4 deletions packages/block-library/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
],
"dependencies": {
"@babel/runtime": "^7.16.0",
"@preact/signals": "^1.1.3",
"@wordpress/a11y": "file:../a11y",
"@wordpress/api-fetch": "file:../api-fetch",
"@wordpress/autop": "file:../autop",
Expand Down Expand Up @@ -65,13 +64,10 @@
"change-case": "^4.1.2",
"classnames": "^2.3.1",
"colord": "^2.7.0",
"deepsignal": "^1.3.0",
"escape-html": "^1.0.3",
"fast-average-color": "^9.1.1",
"fast-deep-equal": "^3.1.3",
"memize": "^2.1.0",
"micromodal": "^0.4.10",
"preact": "^10.13.2",
"remove-accents": "^0.4.2",
"uuid": "^8.3.0"
},
Expand Down
10 changes: 5 additions & 5 deletions packages/block-library/src/navigation/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ function block_core_navigation_add_directives_to_submenu( $w, $block_attributes
) ) {
// Add directives to the parent `<li>`.
$w->set_attribute( 'data-wp-interactive', true );
$w->set_attribute( 'data-wp-context', '{ "core": { "navigation": { "isMenuOpen": { "click": false, "hover": false }, "overlay": false } } }' );
$w->set_attribute( 'data-wp-context', '{ "core": { "navigation": { "submenuOpenedBy": {}, "type": "submenu" } } }' );
$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' );
Expand Down Expand Up @@ -711,7 +711,7 @@ function render_block_core_navigation( $attributes, $content, $block ) {
if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN && $should_load_view_script ) {
$nav_element_directives = '
data-wp-interactive
data-wp-context=\'{ "core": { "navigation": { "isMenuOpen": { "click": false, "hover": false }, "overlay": true, "roleAttribute": "" } } }\'
data-wp-context=\'{ "core": { "navigation": { "overlayOpenedBy": {}, "type": "overlay", "roleAttribute": "" } } }\'
';
$open_button_directives = '
data-wp-on--click="actions.core.navigation.openMenuOnClick"
Expand All @@ -736,11 +736,11 @@ function render_block_core_navigation( $attributes, $content, $block ) {
}

$responsive_container_markup = sprintf(
'<button aria-haspopup="true" %3$s class="%6$s" data-micromodal-trigger="%1$s" %11$s>%9$s</button>
'<button aria-haspopup="true" %3$s class="%6$s" %11$s>%9$s</button>
<div class="%5$s" style="%7$s" id="%1$s" %12$s>
<div class="wp-block-navigation__responsive-close" tabindex="-1" data-micromodal-close>
<div class="wp-block-navigation__responsive-close" tabindex="-1">
<div class="wp-block-navigation__responsive-dialog" aria-label="%8$s" %13$s>
<button %4$s data-micromodal-close class="wp-block-navigation__responsive-container-close" %14$s>%10$s</button>
<button %4$s class="wp-block-navigation__responsive-container-close" %14$s>%10$s</button>
<div class="wp-block-navigation__responsive-container-content" id="%1$s-content">
%2$s
</div>
Expand Down
129 changes: 73 additions & 56 deletions packages/block-library/src/navigation/view.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,33 @@
/**
* WordPress dependencies
*/
import { store } from '@wordpress/interactivity';
import { store as wpStore } from '@wordpress/interactivity';

const focusableSelectors = [
'a[href]',
'area[href]',
'input:not([disabled]):not([type="hidden"]):not([aria-hidden])',
'select:not([disabled]):not([aria-hidden])',
'textarea:not([disabled]):not([aria-hidden])',
'button:not([disabled]):not([aria-hidden])',
'iframe',
'object',
'embed',
'[contenteditable]',
'[tabindex]:not([tabindex^="-"])',
];

const openMenu = ( { context, ref }, menuOpenedOn ) => {
context.core.navigation.isMenuOpen[ menuOpenedOn ] = true;
const openMenu = ( store, menuOpenedOn ) => {
const { context, ref, selectors } = store;
selectors.core.navigation.menuOpenBy( store )[ menuOpenedOn ] = true;
context.core.navigation.previousFocus = ref;
if ( context.core.navigation.overlay ) {
if ( context.core.navigation.type === 'overlay' ) {
// Add a `has-modal-open` class to the <html> root.
document.documentElement.classList.add( 'has-modal-open' );
}
};

const closeMenu = ( { context, selectors }, menuClosedOn ) => {
context.core.navigation.isMenuOpen[ menuClosedOn ] = false;
const closeMenu = ( store, menuClosedOn ) => {
const { context, selectors } = store;
selectors.core.navigation.menuOpenBy( store )[ menuClosedOn ] = false;
// Check if the menu is still open or not.
if ( ! selectors.core.navigation.isMenuOpen( { context } ) ) {
if ( ! selectors.core.navigation.isMenuOpen( store ) ) {
if (
context.core.navigation.modal?.contains(
window.document.activeElement
Expand All @@ -39,18 +37,19 @@ const closeMenu = ( { context, selectors }, menuClosedOn ) => {
}
context.core.navigation.modal = null;
context.core.navigation.previousFocus = null;
if ( context.core.navigation.overlay ) {
if ( context.core.navigation.type === 'overlay' ) {
document.documentElement.classList.remove( 'has-modal-open' );
}
}
};

store( {
wpStore( {
effects: {
core: {
navigation: {
initMenu: ( { context, selectors, ref } ) => {
if ( selectors.core.navigation.isMenuOpen( { context } ) ) {
initMenu: ( store ) => {
const { context, selectors, ref } = store;
if ( selectors.core.navigation.isMenuOpen( store ) ) {
const focusableElements =
ref.querySelectorAll( focusableSelectors );
context.core.navigation.modal = ref;
Expand All @@ -60,8 +59,9 @@ store( {
focusableElements[ focusableElements.length - 1 ];
}
},
focusFirstElement: ( { context, selectors, ref } ) => {
if ( selectors.core.navigation.isMenuOpen( { context } ) ) {
focusFirstElement: ( store ) => {
const { selectors, ref } = store;
if ( selectors.core.navigation.isMenuOpen( store ) ) {
ref.querySelector(
'.wp-block-navigation-item > *:first-child'
).focus();
Expand All @@ -73,60 +73,77 @@ store( {
selectors: {
core: {
navigation: {
roleAttribute: ( { context, selectors } ) =>
context.core.navigation.overlay &&
selectors.core.navigation.isMenuOpen( { context } )
roleAttribute: ( store ) => {
const { context, selectors } = store;
return context.core.navigation.type === 'overlay' &&
selectors.core.navigation.isMenuOpen( store )
? 'dialog'
: '',
: '';
},
isMenuOpen: ( { context } ) =>
// The menu is opened if either `click` or `hover` is true.
Object.values( context.core.navigation.isMenuOpen ).filter(
Boolean
).length > 0,
// The menu is opened if either `click`, `hover` or `focus` is true.
Object.values(
context.core.navigation[
context.core.navigation.type === 'overlay'
? 'overlayOpenedBy'
: 'submenuOpenedBy'
]
).filter( Boolean ).length > 0,
menuOpenBy: ( { context } ) =>
context.core.navigation[
context.core.navigation.type === 'overlay'
? 'overlayOpenedBy'
: 'submenuOpenedBy'
],
},
},
},
actions: {
core: {
navigation: {
openMenuOnHover( args ) {
openMenu( args, 'hover' );
openMenuOnHover( store ) {
const { navigation } = store.context.core;
if (
navigation.type === 'submenu' &&
// Only open on hover if the overlay is closed.
Object.values(
navigation.overlayOpenedBy || {}
).filter( Boolean ).length === 0
)
openMenu( store, 'hover' );
},
closeMenuOnHover( args ) {
closeMenu( args, 'hover' );
closeMenuOnHover( store ) {
closeMenu( store, 'hover' );
},
openMenuOnClick( args ) {
openMenu( args, 'click' );
openMenuOnClick( store ) {
openMenu( store, 'click' );
},
closeMenuOnClick( args ) {
closeMenu( args, 'click' );
closeMenuOnClick( store ) {
closeMenu( store, 'click' );
},
toggleMenuOnClick: ( args ) => {
const { context } = args;
if ( context.core.navigation.isMenuOpen.click ) {
closeMenu( args, 'click' );
toggleMenuOnClick: ( store ) => {
const { selectors } = store;
if ( selectors.core.navigation.menuOpenBy( store ).click ) {
closeMenu( store, 'click' );
} else {
openMenu( args, 'click' );
openMenu( store, 'click' );
}
},
handleMenuKeydown: ( args ) => {
const { context, event } = args;
if ( context.core.navigation.isMenuOpen.click ) {
// If Escape close the menu
if (
event?.key === 'Escape' ||
event?.keyCode === 27
) {
closeMenu( args, 'click' );
handleMenuKeydown: ( store ) => {
const { context, selectors, event } = store;
if ( selectors.core.navigation.menuOpenBy( store ).click ) {
// If Escape close the menu.
if ( event?.key === 'Escape' ) {
closeMenu( store, 'click' );
return;
}

// Trap focus if it is an overlay (main menu)
// Trap focus if it is an overlay (main menu).
if (
context.core.navigation.overlay &&
( event.key === 'Tab' || event.keyCode === 9 )
context.core.navigation.type === 'overlay' &&
event.key === 'Tab'
) {
// If shift + tab it change the direction
// If shift + tab it change the direction.
if (
event.shiftKey &&
window.document.activeElement ===
Expand All @@ -146,21 +163,21 @@ store( {
}
}
},
handleMenuFocusout: ( args ) => {
const { context, event } = args;
handleMenuFocusout: ( store ) => {
const { context, selectors, event } = store;
// If focus is outside modal, and in the document, close menu
// event.target === The element losing focus
// event.relatedTarget === The element receiving focus (if any)
// When focusout is outsite the document,
// `window.document.activeElement` doesn't change
// `window.document.activeElement` doesn't change.
if (
context.core.navigation.isMenuOpen.click &&
selectors.core.navigation.menuOpenBy( store ).click &&
! context.core.navigation.modal?.contains(
event.relatedTarget
) &&
event.target !== window.document.activeElement
) {
closeMenu( args, 'click' );
closeMenu( store, 'click' );
}
},
},
Expand Down
2 changes: 1 addition & 1 deletion packages/interactivity/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"react-native": "src/index",
"dependencies": {
"@preact/signals": "^1.1.3",
"deepsignal": "^1.3.0",
"deepsignal": "^1.3.3",
"preact": "^10.13.2"
},
"publishConfig": {
Expand Down

0 comments on commit 09bf154

Please sign in to comment.