Make WordPress Core

Changeset 41803


Ignore:
Timestamp:
10/10/2017 03:33:57 AM (6 years ago)
Author:
westonruter
Message:

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.

Location:
trunk/src
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/js/customize-controls.js

    r41799 r41803  
    7878
    7979            api.Values.prototype.initialize.call( collection, options );
     80
     81            _.bindAll( collection, 'constrainFocus' );
    8082
    8183            // Keep track of the order in which the notifications were added for sorting purposes.
     
    189191        render: function() {
    190192            var collection = this,
    191                 notifications, hadOverlayNotification = false, hasOverlayNotification,
     193                notifications, hadOverlayNotification = false, hasOverlayNotification, overlayNotifications = [],
    192194                previousNotificationsByCode = {},
    193                 listElement;
     195                listElement, focusableElements;
    194196
    195197            // Short-circuit if there are no container to render into.
     
    227229                }
    228230                notificationContainer = $( notification.render() );
     231                notification.container = notificationContainer;
    229232                listElement.append( notificationContainer ); // @todo Consider slideDown() as enhancement.
    230233
    231                 // @todo Constraing focus in notificationContainer if notification.extended( api.OverlayNotification ).
    232             });
    233 
    234             hasOverlayNotification = Boolean( _.find( notifications, function( notification ) {
    235                 return notification.extended( api.OverlayNotification );
    236             } ) );
     234                if ( notification.extended( api.OverlayNotification ) ) {
     235                    overlayNotifications.push( notification );
     236                }
     237            });
     238            hasOverlayNotification = Boolean( overlayNotifications.length );
     239
    237240            if ( collection.previousNotifications ) {
    238241                hadOverlayNotification = Boolean( _.find( collection.previousNotifications, function( notification ) {
     
    244247                $( document.body ).toggleClass( 'customize-loading', hasOverlayNotification );
    245248                collection.container.toggleClass( 'has-overlay-notifications', hasOverlayNotification );
     249                if ( hasOverlayNotification ) {
     250                    collection.previousActiveElement = document.activeElement;
     251                    $( document ).on( 'keydown', collection.constrainFocus );
     252                } else {
     253                    $( document ).off( 'keydown', collection.constrainFocus );
     254                }
     255            }
     256
     257            if ( hasOverlayNotification ) {
     258                collection.focusContainer = overlayNotifications[ overlayNotifications.length - 1 ].container;
     259                collection.focusContainer.prop( 'tabIndex', -1 );
     260                focusableElements = collection.focusContainer.find( ':focusable' );
     261                if ( focusableElements.length ) {
     262                    focusableElements.first().focus();
     263                } else {
     264                    collection.focusContainer.focus();
     265                }
     266            } else if ( collection.previousActiveElement ) {
     267                $( collection.previousActiveElement ).focus();
     268                collection.previousActiveElement = null;
    246269            }
    247270
     
    249272            collection.previousContainer = collection.container;
    250273            collection.trigger( 'rendered' );
     274        },
     275
     276        /**
     277         * Constrain focus on focus container.
     278         *
     279         * @since 4.9.0
     280         *
     281         * @param {jQuery.Event} event - Event.
     282         * @returns {void}
     283         */
     284        constrainFocus: function constrainFocus( event ) {
     285            var collection = this;
     286            if ( ! collection.focusContainer || collection.focusContainer.is( event.target ) || $.contains( collection.focusContainer[0], event.target[0] ) ) {
     287                return;
     288            }
     289            collection.focusContainer.focus();
    251290        }
    252291    });
  • trunk/src/wp-includes/js/customize-base.js

    r41726 r41803  
    886886
    887887            if ( notification.dismissible ) {
    888                 container.find( '.notice-dismiss' ).on( 'click', function() {
     888                container.find( '.notice-dismiss' ).on( 'click keydown', function( event ) {
     889                    if ( 'keydown' === event.type && 13 !== event.which ) {
     890                        return;
     891                    }
     892
    889893                    if ( notification.parent ) {
    890894                        notification.parent.remove( notification.code );
Note: See TracChangeset for help on using the changeset viewer.