Customize: Constrain focus when overlay notification is displayed.

* Restore previously-focused element when overlay notifications are dismissed.
* Allow notifications to be dismissed via keyboard.

Amends [41667].
See #42110, #35210, #39896.


git-svn-id: https://develop.svn.wordpress.org/trunk@41803 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Weston Ruter 2017-10-10 03:33:57 +00:00
parent a4dab604fb
commit 52ef606823
2 changed files with 50 additions and 7 deletions

View File

@ -78,6 +78,8 @@
api.Values.prototype.initialize.call( collection, options );
_.bindAll( collection, 'constrainFocus' );
// Keep track of the order in which the notifications were added for sorting purposes.
collection._addedIncrement = 0;
collection._addedOrder = {};
@ -188,9 +190,9 @@
*/
render: function() {
var collection = this,
notifications, hadOverlayNotification = false, hasOverlayNotification,
notifications, hadOverlayNotification = false, hasOverlayNotification, overlayNotifications = [],
previousNotificationsByCode = {},
listElement;
listElement, focusableElements;
// Short-circuit if there are no container to render into.
if ( ! collection.container || ! collection.container.length ) {
@ -226,14 +228,15 @@
wp.a11y.speak( notification.message, 'assertive' );
}
notificationContainer = $( notification.render() );
notification.container = notificationContainer;
listElement.append( notificationContainer ); // @todo Consider slideDown() as enhancement.
// @todo Constraing focus in notificationContainer if notification.extended( api.OverlayNotification ).
if ( notification.extended( api.OverlayNotification ) ) {
overlayNotifications.push( notification );
}
});
hasOverlayNotification = Boolean( overlayNotifications.length );
hasOverlayNotification = Boolean( _.find( notifications, function( notification ) {
return notification.extended( api.OverlayNotification );
} ) );
if ( collection.previousNotifications ) {
hadOverlayNotification = Boolean( _.find( collection.previousNotifications, function( notification ) {
return notification.extended( api.OverlayNotification );
@ -243,11 +246,47 @@
if ( hasOverlayNotification !== hadOverlayNotification ) {
$( document.body ).toggleClass( 'customize-loading', hasOverlayNotification );
collection.container.toggleClass( 'has-overlay-notifications', hasOverlayNotification );
if ( hasOverlayNotification ) {
collection.previousActiveElement = document.activeElement;
$( document ).on( 'keydown', collection.constrainFocus );
} else {
$( document ).off( 'keydown', collection.constrainFocus );
}
}
if ( hasOverlayNotification ) {
collection.focusContainer = overlayNotifications[ overlayNotifications.length - 1 ].container;
collection.focusContainer.prop( 'tabIndex', -1 );
focusableElements = collection.focusContainer.find( ':focusable' );
if ( focusableElements.length ) {
focusableElements.first().focus();
} else {
collection.focusContainer.focus();
}
} else if ( collection.previousActiveElement ) {
$( collection.previousActiveElement ).focus();
collection.previousActiveElement = null;
}
collection.previousNotifications = notifications;
collection.previousContainer = collection.container;
collection.trigger( 'rendered' );
},
/**
* Constrain focus on focus container.
*
* @since 4.9.0
*
* @param {jQuery.Event} event - Event.
* @returns {void}
*/
constrainFocus: function constrainFocus( event ) {
var collection = this;
if ( ! collection.focusContainer || collection.focusContainer.is( event.target ) || $.contains( collection.focusContainer[0], event.target[0] ) ) {
return;
}
collection.focusContainer.focus();
}
});

View File

@ -885,7 +885,11 @@ window.wp = window.wp || {};
container = $( notification.template( data ) );
if ( notification.dismissible ) {
container.find( '.notice-dismiss' ).on( 'click', function() {
container.find( '.notice-dismiss' ).on( 'click keydown', function( event ) {
if ( 'keydown' === event.type && 13 !== event.which ) {
return;
}
if ( notification.parent ) {
notification.parent.remove( notification.code );
} else {