Make WordPress Core


Ignore:
Timestamp:
02/26/2024 12:50:22 AM (13 months ago)
Author:
joedolson
Message:

Toolbar: Accessibility: Keyboard navigation for screen readers.

Change the admin toolbar to have role="menu" and support opening for screen readers. Remove screen reader only log out link and collapse duplicate profile links into one link. This is an imperfect solution to a complex problem in the adminbar, but the lack of screen reader access to submenus is a major accessibility problem, and this fix provides access, even if the mechanism is imperfect.

Screen reader log out added in [21452].

Props abletec, Cheffheid, sabernhardt, alexstine, joedolson, afercia, sparklingrobots, danieltj, swissspidy, netweb, dionysous.
Fixes #34668, #43633.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/adminbar.php

    r56227 r57708  
    9898        $node_my_account   = $wp_admin_bar->get_node( 'my-account' );
    9999        $node_user_info    = $wp_admin_bar->get_node( 'user-info' );
    100         $node_edit_profile = $wp_admin_bar->get_node( 'edit-profile' );
    101100
    102101        // Site menu points to the home page instead of the admin URL.
     
    106105        $this->assertFalse( $node_my_account->href );
    107106        $this->assertFalse( $node_user_info->href );
    108         $this->assertNull( $node_edit_profile );
    109107    }
    110108
     
    117115
    118116        wp_set_current_user( self::$editor_id );
     117
     118        $wp_admin_bar = $this->get_standard_admin_bar();
     119
     120        $node_site_name  = $wp_admin_bar->get_node( 'site-name' );
     121        $node_my_account = $wp_admin_bar->get_node( 'my-account' );
     122        $node_user_info  = $wp_admin_bar->get_node( 'user-info' );
     123
     124        // Site menu points to the admin URL.
     125        $this->assertSame( admin_url( '/' ), $node_site_name->href );
     126
     127        $profile_url = admin_url( 'profile.php' );
     128
     129        // Profile URLs point to profile.php.
     130        $this->assertSame( $profile_url, $node_my_account->href );
     131        $this->assertSame( $profile_url, $node_user_info->href );
     132    }
     133
     134    /**
     135     * @ticket 25162
     136     * @group multisite
     137     * @group ms-required
     138     */
     139    public function test_admin_bar_contains_correct_links_for_users_with_no_role_on_blog() {
     140        $blog_id = self::factory()->blog->create(
     141            array(
     142                'user_id' => self::$admin_id,
     143            )
     144        );
     145
     146        $this->assertTrue( user_can( self::$admin_id, 'read' ) );
     147        $this->assertTrue( user_can( self::$editor_id, 'read' ) );
     148
     149        $this->assertTrue( is_user_member_of_blog( self::$admin_id, $blog_id ) );
     150        $this->assertFalse( is_user_member_of_blog( self::$editor_id, $blog_id ) );
     151
     152        wp_set_current_user( self::$editor_id );
     153
     154        switch_to_blog( $blog_id );
    119155
    120156        $wp_admin_bar = $this->get_standard_admin_bar();
     
    123159        $node_my_account   = $wp_admin_bar->get_node( 'my-account' );
    124160        $node_user_info    = $wp_admin_bar->get_node( 'user-info' );
    125         $node_edit_profile = $wp_admin_bar->get_node( 'edit-profile' );
    126 
    127         // Site menu points to the admin URL.
    128         $this->assertSame( admin_url( '/' ), $node_site_name->href );
    129 
    130         $profile_url = admin_url( 'profile.php' );
    131 
    132         // Profile URLs point to profile.php.
    133         $this->assertSame( $profile_url, $node_my_account->href );
    134         $this->assertSame( $profile_url, $node_user_info->href );
    135         $this->assertSame( $profile_url, $node_edit_profile->href );
     161
     162        // Get primary blog.
     163        $primary = get_active_blog_for_user( self::$editor_id );
     164        $this->assertIsObject( $primary );
     165
     166        // No Site menu as the user isn't a member of this blog.
     167        $this->assertNull( $node_site_name );
     168
     169        $primary_profile_url = get_admin_url( $primary->blog_id, 'profile.php' );
     170
     171        // Ensure the user's primary blog is not the same as the main site.
     172        $this->assertNotEquals( $primary_profile_url, admin_url( 'profile.php' ) );
     173
     174        // Profile URLs should go to the user's primary blog.
     175        $this->assertSame( $primary_profile_url, $node_my_account->href );
     176        $this->assertSame( $primary_profile_url, $node_user_info->href );
     177
     178        restore_current_blog();
    136179    }
    137180
     
    141184     * @group ms-required
    142185     */
    143     public function test_admin_bar_contains_correct_links_for_users_with_no_role_on_blog() {
     186    public function test_admin_bar_contains_correct_links_for_users_with_no_role_on_network() {
     187        $this->assertTrue( user_can( self::$admin_id, 'read' ) );
     188        $this->assertFalse( user_can( self::$no_role_id, 'read' ) );
     189
    144190        $blog_id = self::factory()->blog->create(
    145191            array(
     
    148194        );
    149195
    150         $this->assertTrue( user_can( self::$admin_id, 'read' ) );
    151         $this->assertTrue( user_can( self::$editor_id, 'read' ) );
    152 
    153196        $this->assertTrue( is_user_member_of_blog( self::$admin_id, $blog_id ) );
    154         $this->assertFalse( is_user_member_of_blog( self::$editor_id, $blog_id ) );
    155 
    156         wp_set_current_user( self::$editor_id );
     197        $this->assertFalse( is_user_member_of_blog( self::$no_role_id, $blog_id ) );
     198        $this->assertTrue( is_user_member_of_blog( self::$no_role_id, get_current_blog_id() ) );
     199
     200        // Remove `$nobody` from the current blog, so they're not a member of any blog.
     201        $removed = remove_user_from_blog( self::$no_role_id, get_current_blog_id() );
     202
     203        $this->assertTrue( $removed );
     204        $this->assertFalse( is_user_member_of_blog( self::$no_role_id, get_current_blog_id() ) );
     205
     206        wp_set_current_user( self::$no_role_id );
    157207
    158208        switch_to_blog( $blog_id );
     
    163213        $node_my_account   = $wp_admin_bar->get_node( 'my-account' );
    164214        $node_user_info    = $wp_admin_bar->get_node( 'user-info' );
    165         $node_edit_profile = $wp_admin_bar->get_node( 'edit-profile' );
    166 
    167         // Get primary blog.
    168         $primary = get_active_blog_for_user( self::$editor_id );
    169         $this->assertIsObject( $primary );
    170 
    171         // No Site menu as the user isn't a member of this blog.
    172         $this->assertNull( $node_site_name );
    173 
    174         $primary_profile_url = get_admin_url( $primary->blog_id, 'profile.php' );
    175 
    176         // Ensure the user's primary blog is not the same as the main site.
    177         $this->assertNotEquals( $primary_profile_url, admin_url( 'profile.php' ) );
    178 
    179         // Profile URLs should go to the user's primary blog.
    180         $this->assertSame( $primary_profile_url, $node_my_account->href );
    181         $this->assertSame( $primary_profile_url, $node_user_info->href );
    182         $this->assertSame( $primary_profile_url, $node_edit_profile->href );
    183 
    184         restore_current_blog();
    185     }
    186 
    187     /**
    188      * @ticket 25162
    189      * @group multisite
    190      * @group ms-required
    191      */
    192     public function test_admin_bar_contains_correct_links_for_users_with_no_role_on_network() {
    193         $this->assertTrue( user_can( self::$admin_id, 'read' ) );
    194         $this->assertFalse( user_can( self::$no_role_id, 'read' ) );
    195 
    196         $blog_id = self::factory()->blog->create(
    197             array(
    198                 'user_id' => self::$admin_id,
    199             )
    200         );
    201 
    202         $this->assertTrue( is_user_member_of_blog( self::$admin_id, $blog_id ) );
    203         $this->assertFalse( is_user_member_of_blog( self::$no_role_id, $blog_id ) );
    204         $this->assertTrue( is_user_member_of_blog( self::$no_role_id, get_current_blog_id() ) );
    205 
    206         // Remove `$nobody` from the current blog, so they're not a member of any blog.
    207         $removed = remove_user_from_blog( self::$no_role_id, get_current_blog_id() );
    208 
    209         $this->assertTrue( $removed );
    210         $this->assertFalse( is_user_member_of_blog( self::$no_role_id, get_current_blog_id() ) );
    211 
    212         wp_set_current_user( self::$no_role_id );
    213 
    214         switch_to_blog( $blog_id );
    215 
    216         $wp_admin_bar = $this->get_standard_admin_bar();
    217 
    218         $node_site_name    = $wp_admin_bar->get_node( 'site-name' );
    219         $node_my_account   = $wp_admin_bar->get_node( 'my-account' );
    220         $node_user_info    = $wp_admin_bar->get_node( 'user-info' );
    221         $node_edit_profile = $wp_admin_bar->get_node( 'edit-profile' );
    222215
    223216        // Get primary blog.
     
    235228        $this->assertSame( $user_profile_url, $node_my_account->href );
    236229        $this->assertSame( $user_profile_url, $node_user_info->href );
    237         $this->assertSame( $user_profile_url, $node_edit_profile->href );
    238230
    239231        restore_current_blog();
     
    285277                    'id' => 'test-node',
    286278                ),
    287                 '<div class="ab-item ab-empty-item">',
     279                '<div class="ab-item ab-empty-item" role="menuitem">',
    288280            ),
    289281            array(
     
    293285                    'meta' => array( 'tabindex' => '' ),
    294286                ),
    295                 '<div class="ab-item ab-empty-item">',
     287                '<div class="ab-item ab-empty-item" role="menuitem">',
    296288            ),
    297289            array(
     
    301293                    'meta' => array( 'tabindex' => '1' ),
    302294                ),
    303                 '<div class="ab-item ab-empty-item" tabindex="1">',
     295                '<div class="ab-item ab-empty-item" tabindex="1" role="menuitem">',
    304296            ),
    305297            array(
     
    309301                    'meta' => array( 'tabindex' => '-1' ),
    310302                ),
    311                 '<div class="ab-item ab-empty-item" tabindex="-1">',
     303                '<div class="ab-item ab-empty-item" tabindex="-1" role="menuitem">',
    312304            ),
    313305            array(
     
    317309                    'meta' => array( 'tabindex' => '0' ),
    318310                ),
    319                 '<div class="ab-item ab-empty-item" tabindex="0">',
     311                '<div class="ab-item ab-empty-item" tabindex="0" role="menuitem">',
    320312            ),
    321313            array(
     
    325317                    'meta' => array( 'tabindex' => 0 ),
    326318                ),
    327                 '<div class="ab-item ab-empty-item" tabindex="0">',
     319                '<div class="ab-item ab-empty-item" tabindex="0" role="menuitem">',
    328320            ),
    329321            array(
     
    333325                    'meta' => array( 'tabindex' => 2 ),
    334326                ),
    335                 '<div class="ab-item ab-empty-item" tabindex="2">',
     327                '<div class="ab-item ab-empty-item" tabindex="2" role="menuitem">',
    336328            ),
    337329            array(
     
    341333                    'meta' => array( 'tabindex' => false ),
    342334                ),
    343                 '<div class="ab-item ab-empty-item">',
     335                '<div class="ab-item ab-empty-item" role="menuitem">',
    344336            ),
    345337        );
Note: See TracChangeset for help on using the changeset viewer.