Make WordPress Core

Changeset 59265


Ignore:
Timestamp:
10/21/2024 07:53:10 PM (23 months ago)
Author:
joedolson
Message:

Menus: A11y: Set the parent and order using select fields.

Add select inputs to allow users to set the parent and position of items in the menu settings. Fixes a significant problem for screen reader users that makes updating menus extremely tedious, since the options for moving items do not explicitly set a position. This is also a significant improvement for all users manipulating large menus.

This could easily be considered an enhancement, but while it is a minor enhancement for most users, it is transformative for screen reader users in managing menus, moving that interface from nearly unusable to very manageable.

Props javad2000, audrasjb, juliemoynat, williamalexander, rcreators, milamj, joedolson.
Fixes #43305.

Location:
trunk/src
Files:
4 edited

Legend:

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

    r58306 r59265  
    217217                                                        t.find( '.button-controls .select-all' ).prop( 'checked', false );
    218218                                                        t.find( '.button-controls .spinner' ).removeClass( 'is-active' );
     219                                                        t.updateParentDropdown();
     220                                                        t.updateOrderDropdown();
    219221                                                });
    220222                                        });
     
    289291                                        });
    290292                                        return this;
     293                                },
     294                                updateParentDropdown : function() {
     295                                        return this.each(function(){
     296                                                var menuItems = $( '#menu-to-edit li' ),
     297                                                        parentDropdowns = $( '.edit-menu-item-parent' );
     298
     299                                                $.each( parentDropdowns, function() {
     300                                                        var parentDropdown = $( this ),
     301                                                                $html = '',
     302                                                                $selected = '',
     303                                                                currentItemID = parentDropdown.closest( 'li.menu-item' ).find( '.menu-item-data-db-id' ).val(),
     304                                                                currentparentID = parentDropdown.closest( 'li.menu-item' ).find( '.menu-item-data-parent-id' ).val(),
     305                                                                currentItem = parentDropdown.closest( 'li.menu-item' ),
     306                                                                currentMenuItemChild = currentItem.childMenuItems(),
     307                                                                excludeMenuItem = [ currentItemID ];
     308
     309                                                        if ( currentMenuItemChild.length > 0 ) {
     310                                                                $.each( currentMenuItemChild, function(){
     311                                                                        var childItem = $(this),
     312                                                                                childID = childItem.find( '.menu-item-data-db-id' ).val();
     313
     314                                                                        excludeMenuItem.push( childID );
     315                                                                });
     316                                                        }
     317
     318                                                        if ( currentparentID == 0 ) {
     319                                                                $selected = 'selected';
     320                                                        }
     321
     322                                                        $html += '<option ' + $selected + ' value="0">No Parent</option>';
     323
     324                                                        $.each( menuItems, function() {
     325                                                                var menuItem = $(this),
     326                                                                $selected = '',
     327                                                                menuID = menuItem.find( '.menu-item-data-db-id' ).val(),
     328                                                                menuTitle = menuItem.find( '.edit-menu-item-title' ).val();
     329
     330                                                                if ( ! excludeMenuItem.includes( menuID ) ) {
     331                                                                        if ( currentparentID == menuID ) {
     332                                                                                $selected = 'selected';
     333                                                                        }
     334                                                                        $html += '<option ' + $selected + ' value="' + menuID + '">' + menuTitle + '</option>';
     335                                                                }
     336                                                        });
     337
     338                                                        parentDropdown.html( $html );
     339                                                });
     340                                               
     341                                        });
     342                                },
     343                                updateOrderDropdown : function() {
     344                                        return this.each( function() {
     345                                                var itemPosition,
     346                                                        orderDropdowns = $( '.edit-menu-item-order' );
     347
     348                                                $.each( orderDropdowns, function() {
     349                                                        var orderDropdown = $( this ),
     350                                                                menuItem = orderDropdown.closest( 'li.menu-item' ).first(),
     351                                                                depth = menuItem.menuItemDepth(),
     352                                                                isPrimaryMenuItem = ( 0 === depth ),
     353                                                                $html = '',
     354                                                                $selected = '';
     355
     356                                                        if ( isPrimaryMenuItem ) {
     357                                                                var primaryItems = $( '.menu-item-depth-0' ),
     358                                                                        totalMenuItems = primaryItems.length;
     359
     360                                                                itemPosition = primaryItems.index( menuItem ) + 1;
     361
     362                                                                for ( let i = 1; i < totalMenuItems + 1; i++ ) {
     363                                                                        $selected = '';
     364                                                                        if ( i == itemPosition ) {
     365                                                                                $selected = 'selected';
     366                                                                        }
     367                                                                        $html += '<option ' + $selected + ' value="' + i + '">' + i + ' of ' + totalMenuItems + '</option>';
     368                                                                }
     369
     370                                                        } else {
     371                                                                var parentItem = menuItem.prevAll( '.menu-item-depth-' + parseInt( depth - 1, 10 ) ).first(),
     372                                                                        parentItemId = parentItem.find( '.menu-item-data-db-id' ).val(),
     373                                                                        subItems = $( '.menu-item .menu-item-data-parent-id[value="' + parentItemId + '"]' ),
     374                                                                        totalSubMenuItems = subItems.length;
     375
     376                                                                itemPosition = $( subItems.parents('.menu-item').get().reverse() ).index( menuItem ) + 1;
     377
     378                                                                for ( let i = 1; i < totalSubMenuItems + 1; i++ ) {
     379                                                                        $selected = '';
     380                                                                        if ( i == itemPosition ) {
     381                                                                                $selected = 'selected';
     382                                                                        }
     383                                                                        $html += '<option ' + $selected + ' value="' + i + '">' + i + ' of ' + totalSubMenuItems + '</option>';
     384                                                                }
     385
     386                                                        }
     387
     388                                                        orderDropdown.html( $html );
     389                                                });
     390                                               
     391                                        });
    291392                                }
    292393                        });
     
    298399
    299400                moveMenuItem : function( $this, dir ) {
    300 
    301401                        var items, newItemPosition, newDepth,
    302402                                menuItems = $( '#menu-to-edit li' ),
     
    401501                        api.refreshKeyboardAccessibility();
    402502                        api.refreshAdvancedAccessibility();
     503                        thisItem.updateParentDropdown();
     504                        thisItem.updateOrderDropdown();
    403505
    404506                        if ( a11ySpeech ) {
     
    432534                                }
    433535                        });
     536
     537                        // Set menu parents data for all menu items.
     538                        menu.updateParentDropdown();
     539
     540                        // Set menu order data for all menu items.
     541                        menu.updateOrderDropdown();
     542
     543                        // Update menu item parent when value is changed.
     544                        menu.on( 'change', '.edit-menu-item-parent', function() {
     545                                api.changeMenuParent( $( this ) );
     546                        });
     547                       
     548                        // Update menu item order when value is changed.
     549                        menu.on( 'change', '.edit-menu-item-order', function() {
     550                                api.changeMenuOrder( $( this ) );
     551                        });
     552                },
     553
     554                /**
     555                 * changeMenuParent( [parentDropdown] )
     556                 *
     557                 * @since 6.7.0
     558                 *
     559                 * @param {object} parentDropdown select field
     560                 */
     561                changeMenuParent : function( parentDropdown ) {
     562                        var menuItemNewPosition,
     563                                menuItems = $( '#menu-to-edit li' ),
     564                                $this = $( parentDropdown ),
     565                                newParentID = $this.val(),
     566                                menuItem = $this.closest( 'li.menu-item' ).first(),
     567                                menuItemOldDepth = menuItem.menuItemDepth(),
     568                                menuItemChildren = menuItem.childMenuItems(),
     569                                menuItemNoChildren = parseInt( menuItem.childMenuItems().length, 10 ),
     570                                parentItem = $( '#menu-item-' + newParentID ),
     571                                parentItemDepth = parentItem.menuItemDepth(),
     572                                menuItemNewDepth = parseInt( parentItemDepth ) + 1;
     573
     574                        if ( newParentID == 0 ) {
     575                                menuItemNewDepth = 0;
     576                        }
     577
     578                        menuItem.find( '.menu-item-data-parent-id' ).val( newParentID );
     579                        menuItem.moveHorizontally( menuItemNewDepth, menuItemOldDepth );
     580
     581                        if ( menuItemNoChildren > 0 ) {
     582                                menuItem = menuItem.add( menuItemChildren );
     583                        }
     584                        menuItem.detach();
     585
     586                        menuItems = $( '#menu-to-edit li' );
     587
     588                        var     parentItemPosition = parseInt( parentItem.index(), 10 ),
     589                                parentItemNoChild = parseInt( parentItem.childMenuItems().length, 10 );
     590
     591                        if ( parentItemNoChild > 0 ){
     592                                menuItemNewPosition = parentItemPosition + parentItemNoChild;
     593                        } else {
     594                                menuItemNewPosition = parentItemPosition;
     595                        }
     596
     597                        if ( newParentID == 0 ) {
     598                                menuItemNewPosition = menuItems.length - 1;
     599                        }
     600
     601                        menuItem.insertAfter( menuItems.eq( menuItemNewPosition ) ).updateParentMenuItemDBId().updateParentDropdown().updateOrderDropdown();
     602
     603                        api.registerChange();
     604                        api.refreshKeyboardAccessibility();
     605                        api.refreshAdvancedAccessibility();
     606                        $this.trigger( 'focus' );
     607                        wp.a11y.speak( menus.parentUpdated, 'polite' );
     608                },
     609
     610                /**
     611                 * changeMenuOrder( [OrderDropdown] )
     612                 *
     613                 * @since 6.7.0
     614                 *
     615                 * @param {object} orderDropdown select field
     616                 */
     617                changeMenuOrder : function( orderDropdown ) {
     618                        var menuItems = $( '#menu-to-edit li' ),
     619                                $this = $( orderDropdown ),
     620                                newOrderID = parseInt( $this.val(), 10),
     621                                menuItem = $this.closest( 'li.menu-item' ).first(),
     622                                menuItemChildren = menuItem.childMenuItems(),
     623                                menuItemNoChildren = menuItemChildren.length,
     624                                menuItemCurrentPosition = parseInt( menuItem.index(), 10 ),
     625                                parentItemID = menuItem.find( '.menu-item-data-parent-id' ).val(),
     626                                subItems = $( '.menu-item .menu-item-data-parent-id[value="' + parentItemID + '"]' ),
     627                                currentItemAtPosition = $(subItems[newOrderID - 1]).closest( 'li.menu-item' );
     628
     629                        if ( menuItemNoChildren > 0 ) {
     630                                menuItem = menuItem.add( menuItemChildren );
     631                        }
     632
     633                        var currentItemNoChildren = currentItemAtPosition.childMenuItems().length,
     634                                currentItemPosition = parseInt( currentItemAtPosition.index(), 10 );
     635
     636                        menuItems = $( '#menu-to-edit li' );
     637
     638                        var     menuItemNewPosition = currentItemPosition;
     639
     640                        if(menuItemCurrentPosition > menuItemNewPosition){
     641                                menuItemNewPosition = currentItemPosition;
     642                                menuItem.detach().insertBefore( menuItems.eq( menuItemNewPosition ) ).updateOrderDropdown();
     643                        } else {
     644                                menuItemNewPosition = menuItemNewPosition + currentItemNoChildren;
     645                                menuItem.detach().insertAfter( menuItems.eq( menuItemNewPosition ) ).updateOrderDropdown();
     646                        }
     647
     648                        api.registerChange();
     649                        api.refreshKeyboardAccessibility();
     650                        api.refreshAdvancedAccessibility();
     651                        $this.trigger( 'focus' );
     652                        wp.a11y.speak( menus.orderUpdated, 'polite' );
    434653                },
    435654
     
    738957                                        api.refreshKeyboardAccessibility();
    739958                                        api.refreshAdvancedAccessibility();
     959                                        ui.item.updateParentDropdown();
     960                                        ui.item.updateOrderDropdown();
    740961                                        api.refreshAdvancedAccessibilityOfItem( ui.item.find( 'a.item-edit' ) );
    741962                                },
     
    9891210                                        wp.a11y.speak( deletionSpeech, 'polite' );
    9901211                                        that.disableBulkSelection();
     1212                                        menus.updateParentDropdown();
     1213                                        menus.updateOrderDropdown();
    9911214                                }
    9921215                        });
     
    15281751                                        api.refreshAdvancedAccessibility();
    15291752                                        wp.a11y.speak( menus.itemRemoved );
     1753                                        menus.updateParentDropdown();
     1754                                        menus.updateOrderDropdown();
    15301755                                });
    15311756                },
  • trunk/src/wp-admin/css/nav-menus.css

    r58747 r59265  
    828828}
    829829
    830 .menu-item-settings .description-thin,
    831 .menu-item-settings .description-wide {
    832         margin-right: 10px;
    833         float: left;
    834 }
    835 
    836 .description-thin {
    837         width: calc(50% - 5px);
    838 }
    839 
    840 .menu-item-settings .description-thin + .description-thin {
    841         margin-right: 0;
    842 }
    843 
    844 .description-wide {
    845         width: 100%;
     830.description-group {
     831        display: flex;
     832        column-gap: 10px;
     833}
     834
     835.description-group > * {
     836        flex-grow: 1;
    846837}
    847838
     
    952943
    953944        .menu-item-bar .menu-item-handle,
    954         .menu-item-settings,
    955         .description-wide {
     945        .menu-item-settings {
    956946                width: auto;
    957947        }
     
    961951        }
    962952
    963         .menu-item-settings .description-thin,
    964         .menu-item-settings .description-wide {
    965                 width: 100%;
     953        .menu-item-settings .description-group {
     954                display: block;
    966955        }
    967956
  • trunk/src/wp-admin/includes/class-walker-nav-menu-edit.php

    r56586 r59265  
    219219                                        </label>
    220220                                </p>
    221                                 <p class="field-css-classes description description-thin">
    222                                         <label for="edit-menu-item-classes-<?php echo $item_id; ?>">
    223                                                 <?php _e( 'CSS Classes (optional)' ); ?><br />
    224                                                 <input type="text" id="edit-menu-item-classes-<?php echo $item_id; ?>" class="widefat code edit-menu-item-classes" name="menu-item-classes[<?php echo $item_id; ?>]" value="<?php echo esc_attr( implode( ' ', $menu_item->classes ) ); ?>" />
    225                                         </label>
    226                                 </p>
    227                                 <p class="field-xfn description description-thin">
    228                                         <label for="edit-menu-item-xfn-<?php echo $item_id; ?>">
    229                                                 <?php _e( 'Link Relationship (XFN)' ); ?><br />
    230                                                 <input type="text" id="edit-menu-item-xfn-<?php echo $item_id; ?>" class="widefat code edit-menu-item-xfn" name="menu-item-xfn[<?php echo $item_id; ?>]" value="<?php echo esc_attr( $menu_item->xfn ); ?>" />
    231                                         </label>
    232                                 </p>
     221                                <div class="description-group">
     222                                        <p class="field-css-classes description description-thin">
     223                                                <label for="edit-menu-item-classes-<?php echo $item_id; ?>">
     224                                                        <?php _e( 'CSS Classes (optional)' ); ?><br />
     225                                                        <input type="text" id="edit-menu-item-classes-<?php echo $item_id; ?>" class="widefat code edit-menu-item-classes" name="menu-item-classes[<?php echo $item_id; ?>]" value="<?php echo esc_attr( implode( ' ', $menu_item->classes ) ); ?>" />
     226                                                </label>
     227                                        </p>
     228                                        <p class="field-xfn description description-thin">
     229                                                <label for="edit-menu-item-xfn-<?php echo $item_id; ?>">
     230                                                        <?php _e( 'Link Relationship (XFN)' ); ?><br />
     231                                                        <input type="text" id="edit-menu-item-xfn-<?php echo $item_id; ?>" class="widefat code edit-menu-item-xfn" name="menu-item-xfn[<?php echo $item_id; ?>]" value="<?php echo esc_attr( $menu_item->xfn ); ?>" />
     232                                                </label>
     233                                        </p>
     234                                </div>
    233235                                <p class="field-description description description-wide">
    234236                                        <label for="edit-menu-item-description-<?php echo $item_id; ?>">
     
    238240                                        </label>
    239241                                </p>
     242
     243                                <?php
     244                                /**
     245                                 * Update parent and order of menu item using select inputs.
     246                                 *
     247                                 * @since 6.7.0
     248                                 */
     249                                ?>
     250       
     251                                <div class="field-move-combo description-group">
     252                                        <p class="description description-wide">
     253                                                <label for="edit-menu-item-parent-<?php echo $item_id; ?>">
     254                                                        <?php _e( 'Menu Parent' ); ?>
     255                                                </label>
     256                                                <select class="edit-menu-item-parent widefat" id="edit-menu-item-parent-<?php echo $item_id; ?>" name="menu-item-parent[<?php echo $item_id; ?>]">
     257                                                </select>
     258                                        </p>
     259                                        <p class="description description-wide">
     260                                                <label for="edit-menu-item-order-<?php echo $item_id; ?>">
     261                                                        <?php _e( 'Menu Order' ); ?>
     262                                                </label>
     263                                                <select class="edit-menu-item-order widefat" id="edit-menu-item-order-<?php echo $item_id; ?>" name="menu-item-order[<?php echo $item_id; ?>]">
     264                                                </select>
     265                                        </p>
     266                                </div>
    240267
    241268                                <?php
  • trunk/src/wp-admin/nav-menus.php

    r58306 r59265  
    598598        'movedLeft'               => __( 'Menu item moved out of submenu' ),
    599599        'movedRight'              => __( 'Menu item is now a sub-item' ),
     600        'parentUpdated'           => __( 'Menu parent updated' ),
     601        'orderUpdated'            => __( 'Menu order updated' ),
    600602);
    601603wp_localize_script( 'nav-menu', 'menus', $nav_menus_l10n );
Note: See TracChangeset for help on using the changeset viewer.