Make WordPress Core

Ticket #47149: 47149.2.diff

File 47149.2.diff, 11.3 KB (added by afercia, 6 years ago)
  • src/js/media/views/focus-manager.js

     
     1var $ = jQuery;
     2
    13/**
    24 * wp.media.view.FocusManager
    35 *
     
    1113var FocusManager = wp.media.View.extend(/** @lends wp.media.view.FocusManager.prototype */{
    1214
    1315        events: {
    14                 'keydown': 'constrainTabbing'
     16                'keydown': 'focusManagementMode'
    1517        },
    1618
     19        initialize: function( options ) {
     20                this.mode                    = options.mode || 'constrainTabbing';
     21                this.tabsAutomaticActivation = options.tabsAutomaticActivation || false;
     22        },
     23
     24        /**
     25         * Determines which focus management mode to use.
     26         *
     27         * @since 5.3.0
     28         *
     29         * @param {object} event jQuery event object.
     30         *
     31         * @returns {void}
     32         */
     33        focusManagementMode: function( event ) {
     34                if ( this.mode === 'constrainTabbing' ) {
     35                        this.constrainTabbing( event );
     36                }
     37
     38                if ( this.mode === 'tabsNavigation' ) {
     39                        this.tabsNavigation( event );
     40                }
     41        },
     42
    1743        /**
    1844         * Gets all the tabbable elements.
    1945         *
     
    164190         *
    165191         * @since 5.3.0
    166192         */
    167         ariaHiddenElements: []
     193        ariaHiddenElements: [],
     194
     195        /**
     196         * Holds the jQuery collection of tabs.
     197         *
     198         * @since 5.3.0
     199         */
     200        tabs: $(),
     201
     202        /**
     203         * Initializes tabs in an ARIA tabbed interface.
     204         *
     205         * @since 5.3.0
     206         *
     207         * @param {object} event jQuery event object.
     208         *
     209         * @returns {void}
     210         */
     211        initializeAriaTabs: function() {
     212                this.tabs = this.$( '[role="tab"]' );
     213
     214                // Set up initial attributes.
     215                this.tabs.attr( {
     216                        'aria-selected': false,
     217                        tabIndex: '-1'
     218                } );
     219
     220                // Set up attributes on the initially active tab.
     221                this.tabs.filter( '.active' )
     222                        .removeAttr( 'tabindex' )
     223                        .attr( 'aria-selected', 'true' );
     224        },
     225
     226        /**
     227         * Enables arrows navigation within the ARIA tabbed interface.
     228         *
     229         * @since 5.3.0
     230         *
     231         * @param {object} event jQuery event object.
     232         *
     233         * @returns {void}
     234         */
     235        tabsNavigation: function( event ) {
     236                var orientation = 'horizontal',
     237                        keys = [ 32, 35, 36, 37, 38, 39, 40 ];
     238
     239                // Return if not Spacebar, End, Home, or Arrow keys.
     240                if ( keys.indexOf( event.which ) === -1 ) {
     241                        return;
     242                }
     243
     244                // Determine navigation direction.
     245                if ( this.$el.attr( 'aria-orientation' ) === 'vertical' ) {
     246                        orientation = 'vertical';
     247                }
     248
     249                // Make Up and Down arrow keys do nothing with horizontal tabs.
     250                if ( orientation === 'horizontal' && [ 38, 40 ].indexOf( event.which ) !== -1 ) {
     251                        return;
     252                }
     253
     254                // Make Left and Right arrow keys do nothing with vertical tabs.
     255                if ( orientation === 'vertical' && [ 37, 39 ].indexOf( event.which ) !== -1 ) {
     256                        return;
     257                }
     258
     259                this.switchTabs( event, this.tabs );
     260        },
     261
     262        /**
     263         * Switch tabs in the ARIA tabbed interface.
     264         *
     265         * @since 5.3.0
     266         *
     267         * @param {object} event jQuery event object.
     268         *
     269         * @returns {void}
     270         */
     271        switchTabs: function( event ) {
     272                var key   = event.which,
     273                        index = this.tabs.index( jQuery( event.target ) ),
     274                        newIndex;
     275
     276                switch ( key ) {
     277                        // Space bar: Activate current targeted tab.
     278                        case 32: {
     279                                this.activateTab( this.tabs[ index ] );
     280                                break;
     281                        }
     282                        // End key: Activate last tab.
     283                        case 35: {
     284                                event.preventDefault();
     285                                this.activateTab( this.tabs[ this.tabs.length - 1 ] );
     286                                break;
     287                        }
     288                        // Home key: Activate first tab.
     289                        case 36: {
     290                                event.preventDefault();
     291                                this.activateTab( this.tabs[ 0 ] );
     292                                break;
     293                        }
     294                        // Left and up keys: Activate previous tab.
     295                        case 37:
     296                        case 38: {
     297                                event.preventDefault();
     298                                newIndex = ( index - 1 ) < 0 ? this.tabs.length - 1 : index - 1;
     299                                this.activateTab( this.tabs[ newIndex ] );
     300                                break;
     301                        }
     302                        // Right and down keys: Activate next tab.
     303                        case 39:
     304                        case 40: {
     305                                event.preventDefault();
     306                                newIndex = ( index + 1 ) === this.tabs.length ? 0 : index + 1;
     307                                this.activateTab( this.tabs[ newIndex ] );
     308                                break;
     309                        }
     310                }
     311        },
     312
     313        /**
     314         * Sets all the tabs to be not focusable and semantically not selected.
     315         *
     316         * @since 5.3.0
     317         *
     318         * @param {object} tabs The tabs jQuery collection.
     319         *
     320         * @returns {void}
     321         */
     322        deactivateTabs: function( tabs ) {
     323                tabs
     324                        .attr( {
     325                                'aria-selected': 'false',
     326                                tabIndex: '-1'
     327                        } );
     328        },
     329
     330        /**
     331         * Sets a single tab to be focusable and semantically selected.
     332         *
     333         * @since 5.3.0
     334         *
     335         * @param {object} tab  The tab DOM element.
     336         *
     337         * @returns {void}
     338         */
     339        activateTab: function( tab ) {
     340                if ( ! tab ) {
     341                        return;
     342                }
     343
     344                this.deactivateTabs( this.tabs );
     345
     346                // The tab is a DOM element: no need for jQuery methods.
     347                tab.focus();
     348
     349                // Handle manual activation.
     350                if ( ! this.tabsAutomaticActivation ) {
     351                        tab.removeAttribute( 'tabindex' );
     352                        tab.setAttribute( 'aria-selected', 'true' );
     353                        return;
     354                }
     355
     356                // Handle automatic activation.
     357                $( tab ).on( 'click', function() {
     358                        tab.removeAttribute( 'tabindex' );
     359                        tab.setAttribute( 'aria-selected', 'true' );
     360                } );
     361
     362                tab.click();
     363        }
    168364});
    169365
    170366module.exports = FocusManager;
  • src/js/media/views/frame/post.js

     
    257257        mainMenu: function( view ) {
    258258                view.set({
    259259                        'library-separator': new wp.media.View({
    260                                 className: 'separator',
    261                                 priority: 100
     260                                className:  'separator',
     261                                priority:   100,
     262                                attributes: {
     263                                        role: 'presentation'
     264                                }
    262265                        })
    263266                });
    264267        },
  • src/js/media/views/media-frame.js

     
    110110         */
    111111        createMenu: function( menu ) {
    112112                menu.view = new wp.media.view.Menu({
    113                         controller: this
     113                        controller: this,
     114
     115                        attributes: {
     116                                role:               'tablist',
     117                                'aria-orientation': 'vertical'
     118                        }
    114119                });
    115120        },
    116121
  • src/js/media/views/menu-item.js

     
    1111 * @augments Backbone.View
    1212 */
    1313MenuItem = wp.media.View.extend(/** @lends wp.media.view.MenuItem.prototype */{
    14         tagName:   'a',
     14        tagName:   'button',
    1515        className: 'media-menu-item',
    1616
    1717        attributes: {
    18                 href: '#'
     18                type: 'button',
     19                role: 'tab'
    1920        },
    2021
    2122        events: {
    2223                'click': '_click'
    2324        },
     25
    2426        /**
    25          * @param {Object} event
     27         * Allows to override the click event.
    2628         */
    27         _click: function( event ) {
     29        _click: function() {
    2830                var clickOverride = this.options.click;
    2931
    30                 if ( event ) {
    31                         event.preventDefault();
    32                 }
    33 
    3432                if ( clickOverride ) {
    3533                        clickOverride.call( this );
    3634                } else {
     
    4341
    4442                if ( state ) {
    4543                        this.controller.setState( state );
     44                        // Toggle the menu visibility in the responsive view.
    4645                        this.views.parent.$el.removeClass( 'visible' ); // TODO: or hide on any click, see below
    4746                }
    4847        },
     48
    4949        /**
    5050         * @returns {wp.media.view.MenuItem} returns itself to allow chaining
    5151         */
  • src/js/media/views/menu.js

     
    2020        ItemView:  MenuItem,
    2121        region:    'menu',
    2222
    23         /* TODO: alternatively hide on any click anywhere
    24         events: {
    25                 'click': 'click'
     23        attributes: {
     24                role:               'tablist',
     25                'aria-orientation': 'horizontal'
    2626        },
    2727
    28         click: function() {
    29                 this.$el.removeClass( 'visible' );
     28        initialize: function() {
     29                this._views = {};
     30
     31                this.set( _.extend( {}, this._views, this.options.views ), { silent: true });
     32                delete this.options.views;
     33
     34                if ( ! this.options.silent ) {
     35                        this.render();
     36                }
     37
     38                // Initialize the Focus Manager.
     39                this.focusManager = new wp.media.view.FocusManager( {
     40                        el:   this.el,
     41                        mode: 'tabsNavigation'
     42                } );
    3043        },
    31         */
    3244
    3345        /**
    3446         * @param {Object} options
     
    4759                 */
    4860                PriorityList.prototype.ready.apply( this, arguments );
    4961                this.visibility();
     62
     63                // Set up aria tabs initial attributes.
     64                this.focusManager.initializeAriaTabs();
    5065        },
    5166
    5267        set: function() {
     
    87102
    88103                this.deselect();
    89104                view.$el.addClass('active');
     105
     106                // Set up again the aria tabs initial attributes after the menu updates.
     107                this.focusManager.initializeAriaTabs();
    90108        },
    91109
    92110        deselect: function() {
  • src/js/media/views/router.js

     
    2020        ItemView:  wp.media.view.RouterItem,
    2121        region:    'router',
    2222
     23        attributes: {
     24                role:               'tablist',
     25                'aria-orientation': 'horizontal'
     26        },
     27
    2328        initialize: function() {
    2429                this.controller.on( 'content:render', this.update, this );
    2530                // Call 'initialize' directly on the parent class.
  • src/wp-includes/css/media-views.css

     
    553553        user-select: none;
    554554}
    555555
    556 .media-menu > a {
     556.media-menu .media-menu-item {
    557557        display: block;
     558        box-sizing: border-box;
     559        width: 100%;
    558560        position: relative;
     561        border: 0;
     562        margin: 0;
    559563        padding: 8px 20px;
    560         margin: 0;
     564        font-size: 14px;
    561565        line-height: 1.28571428;
    562         font-size: 14px;
     566        background: transparent;
    563567        color: #0073aa;
     568        text-align: left;
    564569        text-decoration: none;
     570        cursor: pointer;
    565571}
    566572
    567 .media-menu > a:hover {
    568         color: #0073aa;
     573.media-menu .media-menu-item:hover {
    569574        background: rgba(0, 0, 0, 0.04);
    570575}
    571576
    572 .media-menu > a:active {
     577.media-menu .media-menu-item:active {
     578        color: #0073aa;
    573579        outline: none;
    574580}
    575581
     
    579585        font-weight: 600;
    580586}
    581587
     588.media-menu .media-menu-item:focus {
     589        box-shadow:
     590                0 0 0 1px #5b9dd9,
     591                0 0 2px 1px rgba(30, 140, 190, 0.8);
     592        color: #124964;
     593        /* Only visible in Windows High Contrast mode */
     594        outline: 1px solid transparent;
     595}
     596
    582597.media-menu .separator {
    583598        height: 0;
    584599        margin: 12px 20px;
     
    594609        padding: 0 6px;
    595610        margin: 0;
    596611        clear: both;
    597         -webkit-user-select: none;
    598         -moz-user-select: none;
    599         -ms-user-select: none;
    600         user-select: none;
    601612}
    602613
    603 .media-router a {
    604         transition: none;
    605 }
    606 
    607 .media-router > a {
     614.media-router .media-menu-item {
    608615        position: relative;
    609616        float: left;
     617        border: 0;
     618        margin: 0;
    610619        padding: 8px 10px 9px;
    611         margin: 0;
    612620        height: 18px;
    613621        line-height: 1.28571428;
    614622        font-size: 14px;
    615623        text-decoration: none;
     624        background: transparent;
     625        cursor: pointer;
     626        transition: none;
    616627}
    617628
    618 .media-router > a:last-child {
     629.media-router .media-menu-item:last-child {
    619630        border-right: 0;
    620631}
    621632
    622 .media-router > a:active {
    623         outline: none;
     633.media-router .media-menu-item:hover,
     634.media-router .media-menu-item:active {
     635        color: #0073aa;
    624636}
    625637
    626638.media-router .active,
    627639.media-router .active:hover {
    628         color: #32373c;
     640        color: #23282d;
    629641}
    630642
     643.media-router .media-menu-item:focus {
     644        box-shadow:
     645                0 0 0 1px #5b9dd9,
     646                0 0 2px 1px rgba(30, 140, 190, 0.8);
     647        color: #124964;
     648        /* Only visible in Windows High Contrast mode */
     649        outline: 1px solid transparent;
     650}
     651
    631652.media-router .active,
    632 .media-router > a.active:last-child {
     653.media-router .media-menu-item.active:last-child {
    633654        margin: -1px -1px 0;
    634655        background: #fff;
    635656        border: 1px solid #ddd;