Make WordPress Core

Changeset 58306


Ignore:
Timestamp:
06/03/2024 08:33:03 PM (3 months ago)
Author:
joedolson
Message:

Menus: Accessibility: Improve screen reader text for edit button.

Change the edit menu item toggle to communicate more context about the item to be edited. Make edit text consistent between Customizer menu editor and admin menu editor.

The menu position is conveyed only visually, using indentation, because there are no organizational semantics in either editor. This change helps provide screen reader users with consistent contextual information about the order, position, and parent of the current item.

Props joedolson, rcreators, afercia, mohonchandra.
Fixes #60673, See #60672.

Location:
trunk/src
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/js/_enqueues/lib/nav-menu.js

    r54855 r58306  
    450450
    451451            var thisLink, thisLinkText, primaryItems, itemPosition, title,
    452                 parentItem, parentItemId, parentItemName, subItems,
     452                parentItem, parentItemId, parentItemName, subItems, totalSubItems,
    453453                $this = $( itemToRefresh ),
    454454                menuItem = $this.closest( 'li.menu-item' ).first(),
     
    456456                isPrimaryMenuItem = ( 0 === depth ),
    457457                itemName = $this.closest( '.menu-item-handle' ).find( '.menu-item-title' ).text(),
     458                menuItemType = $this.closest( '.menu-item-handle' ).find( '.item-controls' ).find( '.item-type' ).text(),
    458459                position = parseInt( menuItem.index(), 10 ),
    459460                prevItemDepth = ( isPrimaryMenuItem ) ? depth : parseInt( depth - 1, 10 ),
     
    504505                itemPosition = primaryItems.index( menuItem ) + 1,
    505506                totalMenuItems = primaryItems.length,
    506 
    507507                // String together help text for primary menu items.
    508                 title = menus.menuFocus.replace( '%1$s', itemName ).replace( '%2$d', itemPosition ).replace( '%3$d', totalMenuItems );
     508                title = menus.menuFocus.replace( '%1$s', itemName ).replace( '%2$s', menuItemType ).replace( '%3$d', itemPosition ).replace( '%4$d', totalMenuItems );
    509509            } else {
    510510                parentItem = menuItem.prevAll( '.menu-item-depth-' + parseInt( depth - 1, 10 ) ).first(),
     
    512512                parentItemName = parentItem.find( '.menu-item-title' ).text(),
    513513                subItems = $( '.menu-item .menu-item-data-parent-id[value="' + parentItemId + '"]' ),
     514                totalSubItems = subItems.length,
    514515                itemPosition = $( subItems.parents('.menu-item').get().reverse() ).index( menuItem ) + 1;
    515516
    516517                // String together help text for sub menu items.
    517                 title = menus.subMenuFocus.replace( '%1$s', itemName ).replace( '%2$d', itemPosition ).replace( '%3$s', parentItemName );
     518                if ( depth < 2 ) {
     519                    title = menus.subMenuFocus.replace( '%1$s', itemName ).replace( '%2$s', menuItemType ).replace( '%3$d', itemPosition ).replace( '%4$d', totalSubItems ).replace( '%5$s', parentItemName );
     520                } else {
     521                    title = menus.subMenuMoreDepthFocus.replace( '%1$s', itemName ).replace( '%2$s', menuItemType ).replace( '%3$d', itemPosition ).replace( '%4$d', totalSubItems ).replace( '%5$s', parentItemName ).replace( '%6$d', depth );
     522                }
    518523            }
    519524
     
    733738                    api.refreshKeyboardAccessibility();
    734739                    api.refreshAdvancedAccessibility();
     740                    api.refreshAdvancedAccessibilityOfItem( ui.item.find( 'a.item-edit' ) );
    735741                },
    736742                change: function(e, ui) {
  • trunk/src/js/_enqueues/wp/customize/nav-menus.js

    r57746 r58306  
    33 */
    44
    5 /* global _wpCustomizeNavMenusSettings, wpNavMenu, console */
     5/* global menus, _wpCustomizeNavMenusSettings, wpNavMenu, console */
    66( function( api, wp, $ ) {
    77    'use strict';
     
    11331133                wpNavMenu.menuList.attr( 'id', 'menu-to-edit' ).addClass( 'menu' );
    11341134
     1135                api.Menus.MenuItemControl.prototype.initAccessibility();
     1136
    11351137                _.each( api.section( section.id ).controls(), function( control ) {
    11361138                    if ( 'nav_menu_item' === control.params.type ) {
     
    15761578
    15771579        /**
     1580         * Set up the initial state of the screen reader accessibility information for menu items.
     1581         *
     1582         * @since 6.6.0
     1583         */
     1584        initAccessibility: function() {
     1585            var control = this,
     1586                menu = $( '#menu-to-edit' );
     1587
     1588            // Refresh the accessibility when the user comes close to the item in any way.
     1589            menu.on( 'mouseenter.refreshAccessibility focus.refreshAccessibility touchstart.refreshAccessibility', '.menu-item', function(){
     1590                control.refreshAdvancedAccessibilityOfItem( $( this ).find( 'button.item-edit' ) );
     1591            } );
     1592
     1593            // We have to update on click as well because we might hover first, change the item, and then click.
     1594            menu.on( 'click', 'button.item-edit', function() {
     1595                control.refreshAdvancedAccessibilityOfItem( $( this ) );
     1596            } );
     1597        },
     1598
     1599        /**
     1600         * refreshAdvancedAccessibilityOfItem( [itemToRefresh] )
     1601         *
     1602         * Refreshes advanced accessibility buttons for one menu item.
     1603         * Shows or hides buttons based on the location of the menu item.
     1604         *
     1605         * @param {Object} itemToRefresh The menu item that might need its advanced accessibility buttons refreshed
     1606         *
     1607         * @since 6.6.0
     1608         */
     1609        refreshAdvancedAccessibilityOfItem: function( itemToRefresh ) {
     1610            // Only refresh accessibility when necessary.
     1611            if ( true !== $( itemToRefresh ).data( 'needs_accessibility_refresh' ) ) {
     1612                return;
     1613            }
     1614
     1615            var primaryItems, itemPosition, title,
     1616                parentItem, parentItemId, parentItemName, subItems, totalSubItems,
     1617                $this = $( itemToRefresh ),
     1618                menuItem = $this.closest( 'li.menu-item' ).first(),
     1619                depth = menuItem.menuItemDepth(),
     1620                isPrimaryMenuItem = ( 0 === depth ),
     1621                itemName = $this.closest( '.menu-item-handle' ).find( '.menu-item-title' ).text(),
     1622                menuItemType = $this.closest( '.menu-item-handle' ).find( '.item-type' ).text(),
     1623                totalMenuItems = $( '#menu-to-edit li' ).length;
     1624
     1625            if ( isPrimaryMenuItem ) {
     1626                primaryItems = $( '.menu-item-depth-0' ),
     1627                itemPosition = primaryItems.index( menuItem ) + 1,
     1628                totalMenuItems = primaryItems.length,
     1629                // String together help text for primary menu items.
     1630                title = menus.menuFocus.replace( '%1$s', itemName ).replace( '%2$s', menuItemType ).replace( '%3$d', itemPosition ).replace( '%4$d', totalMenuItems );
     1631            } else {
     1632                parentItem = menuItem.prevAll( '.menu-item-depth-' + parseInt( depth - 1, 10 ) ).first(),
     1633                parentItemId = parentItem.find( '.menu-item-data-db-id' ).val(),
     1634                parentItemName = parentItem.find( '.menu-item-title' ).text(),
     1635                subItems = $( '.menu-item .menu-item-data-parent-id[value="' + parentItemId + '"]' ),
     1636                totalSubItems = subItems.length,
     1637                itemPosition = $( subItems.parents( '.menu-item' ).get().reverse() ).index( menuItem ) + 1;
     1638
     1639                // String together help text for sub menu items.
     1640                if ( depth < 2 ) {
     1641                    title = menus.subMenuFocus.replace( '%1$s', itemName ).replace( '%2$s', menuItemType ).replace( '%3$d', itemPosition ).replace( '%4$d', totalSubItems ).replace( '%5$s', parentItemName );
     1642                } else {
     1643                    title = menus.subMenuMoreDepthFocus.replace( '%1$s', itemName ).replace( '%2$s', menuItemType ).replace( '%3$d', itemPosition ).replace( '%4$d', totalSubItems ).replace( '%5$s', parentItemName ).replace( '%6$d', depth );
     1644                }
     1645            }
     1646
     1647            $this.find( '.screen-reader-text' ).text( title );
     1648
     1649            // Mark this item's accessibility as refreshed.
     1650            $this.data( 'needs_accessibility_refresh', false );
     1651        },
     1652
     1653        /**
    15781654         * Override the embed() method to do nothing,
    15791655         * so that the control isn't embedded on load,
     
    16081684            control.renderContent();
    16091685            control.deferred.embedded.resolve(); // This triggers control.ready().
     1686           
     1687            // Mark all menu items as unprocessed.
     1688            $( 'button.item-edit' ).data( 'needs_accessibility_refresh', true );
    16101689        },
    16111690
     
    16801759                } else if ( isMoveLeft ) {
    16811760                    control.moveLeft();
    1682                     if ( 1 === control.params.depth ) {
    1683                         control.container.find( '.is-submenu' ).hide();
    1684                     } else {
    1685                         control.container.find( '.is-submenu' ).show();
    1686                     }
    16871761                } else if ( isMoveRight ) {
    16881762                    control.moveRight();
    16891763                    control.params.depth += 1;
    1690                     if ( 0 === control.params.depth ) {
    1691                         control.container.find( '.is-submenu' ).hide();
    1692                     } else {
    1693                         control.container.find( '.is-submenu' ).show();
    1694                     }
    1695                 }
    1696 
     1764                }
     1765               
    16971766                moveBtn.focus(); // Re-focus after the container was moved.
     1767
     1768                // Mark all menu items as unprocessed.
     1769                $( 'button.item-edit' ).data( 'needs_accessibility_refresh', true );
    16981770            } );
    16991771        },
     
    27272799                        menuItemControl.setting.set( setting );
    27282800                    });
     2801
     2802                    // Mark all menu items as unprocessed.
     2803                    $( 'button.item-edit' ).data( 'needs_accessibility_refresh', true );
    27292804                });
    27302805
  • trunk/src/wp-admin/nav-menus.php

    r56600 r58306  
    581581    /* translators: %s: Previous item name. */
    582582    'outFrom'                 => __( 'Out from under %s' ),
    583     /* translators: 1: Item name, 2: Item position, 3: Total number of items. */
    584     'menuFocus'               => __( '%1$s. Menu item %2$d of %3$d.' ),
    585     /* translators: 1: Item name, 2: Item position, 3: Parent item name. */
    586     'subMenuFocus'            => __( '%1$s. Sub item number %2$d under %3$s.' ),
     583    /* translators: 1: Item name, 2: Item type, 3: Item index, 4: Total items. */
     584    'menuFocus'               => __( 'Edit %1$s (%2$s, %3$d of %4$d)' ),
     585    /* translators: 1: Item name, 2: Item type, 3: Item index, 4: Total items, 5: Item parent. */
     586    'subMenuFocus'            => __( 'Edit %1$s (%2$s, sub-item %3$d of %4$d under %5$s)' ),
     587    /* translators: 1: Item name, 2: Item type, 3: Item index, 4: Total items, 5: Item parent, 6: Item depth. */
     588    'subMenuMoreDepthFocus'   => __( 'Edit %1$s (%2$s, sub-item %3$d of %4$d under %5$s, level %6$d)' ),
    587589    /* translators: %s: Item name. */
    588590    'menuItemDeletion'        => __( 'item %s' ),
  • trunk/src/wp-includes/class-wp-customize-nav-menus.php

    r58146 r58306  
    563563            /* translators: %s: Previous item name. */
    564564            'outFrom'                 => __( 'Out from under %s' ),
    565             /* translators: 1: Item name, 2: Item position, 3: Total number of items. */
    566             'menuFocus'               => __( '%1$s. Menu item %2$d of %3$d.' ),
    567             /* translators: 1: Item name, 2: Item position, 3: Parent item name. */
    568             'subMenuFocus'            => __( '%1$s. Sub item number %2$d under %3$s.' ),
     565            /* translators: 1: Item name, 2: Item type, 3: Item index, 4: Total items. */
     566            'menuFocus'               => __( 'Edit %1$s (%2$s, %3$d of %4$d)' ),
     567            /* translators: 1: Item name, 2: Item type, 3: Item index, 4: Total items, 5: Item parent. */
     568            'subMenuFocus'            => __( 'Edit %1$s (%2$s, sub-item %3$d of %4$d under %5$s)' ),
     569            /* translators: 1: Item name, 2: Item type, 3: Item index, 4: Total items, 5: Item parent, 6: Item depth. */
     570            'subMenuMoreDepthFocus'   => __( 'Edit %1$s (%2$s, sub-item %3$d of %4$d under %5$s, level %6$d)' ),
    569571        );
    570572        wp_localize_script( 'nav-menu', 'menus', $nav_menus_l10n );
  • trunk/src/wp-includes/customize/class-wp-customize-nav-menu-item-control.php

    r57746 r58306  
    7878                <span class="item-controls">
    7979                    <button type="button" class="button-link item-edit" aria-expanded="false"><span class="screen-reader-text">
    80                     <?php
    81                         /* translators: 1: Title of a menu item, 2: Type of a menu item. */
    82                         printf( __( 'Edit menu item: %1$s (%2$s)' ), '{{ data.title || data.original_title || wp.customize.Menus.data.l10n.untitled }}', '{{ data.item_type_label }}' );
    83                     ?>
     80                    <# if ( 0 === data.depth ) { #>
     81                        <?php
     82                        /* translators: 1: Title of a menu item, 2: Type of a menu item. 3: Item index, 4: Total items. */
     83                        printf( __( 'Edit %1$s (%2$s, %3$d of %4$d)' ), '{{ data.title || data.original_title || wp.customize.Menus.data.l10n.untitled }}', '{{ data.item_type_label }}', '', '' );
     84                        ?>
     85                    <# } else if ( 1 === data.depth ) { #>
     86                        <?php
     87                            /* translators: 1: Title of a menu item, 2: Type of a menu item, 3, Item index, 4, Total items, 5: Item parent. */
     88                            printf( __( 'Edit %1$s (%2$s, sub-item %3$d of %4$d under %5$s)' ), '{{ data.title || data.original_title || wp.customize.Menus.data.l10n.untitled }}', '{{ data.item_type_label }}', '', '', '' );
     89                        ?>
     90                    <# } else { #>
     91                        <?php
     92                            /* translators: 1: Title of a menu item, 2: Type of a menu item, 3, Item index, 4, Total items, 5: Item parent, 6: Item depth. */
     93                            printf( __( 'Edit %1$s (%2$s, sub-item %3$d of %4$d under %5$s, level %6$s)' ), '{{ data.title || data.original_title || wp.customize.Menus.data.l10n.untitled }}', '{{ data.item_type_label }}', '', '', '', '{{data.depth}}' );
     94                        ?>
     95                    <# } #>
    8496                    </span><span class="toggle-indicator" aria-hidden="true"></span></button>
    8597                    <button type="button" class="button-link item-delete submitdelete deletion"><span class="screen-reader-text">
Note: See TracChangeset for help on using the changeset viewer.