From e556fec5953377a0109704ed2fa245e58c9b6bb7 Mon Sep 17 00:00:00 2001 From: Dominik Schilling Date: Tue, 12 Jul 2016 11:18:00 +0000 Subject: [PATCH] Toolbar: Allow 0 as a value for the `tabindex` property of a menu item. To enhance accessibility for items without a link you can now define `tabindex="0"`, which makes descendant dropdowns accessible. Props joedolson, afercia, ocean90. Fixes #32495. git-svn-id: https://develop.svn.wordpress.org/trunk@38035 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-admin-bar.php | 9 +-- src/wp-includes/css/admin-bar.css | 2 + tests/phpunit/tests/adminbar.php | 93 +++++++++++++++++++++++++- 3 files changed, 99 insertions(+), 5 deletions(-) diff --git a/src/wp-includes/class-wp-admin-bar.php b/src/wp-includes/class-wp-admin-bar.php index 75ed317a63..49986ac225 100644 --- a/src/wp-includes/class-wp-admin-bar.php +++ b/src/wp-includes/class-wp-admin-bar.php @@ -477,8 +477,9 @@ class WP_Admin_Bar { $is_parent = ! empty( $node->children ); $has_link = ! empty( $node->href ); - $tabindex = isset( $node->meta['tabindex'] ) ? (int) $node->meta['tabindex'] : ''; - $aria_attributes = $tabindex ? 'tabindex="' . $tabindex . '"' : ''; + // Allow only numeric values, then casted to integers, and allow a tabindex value of `0` for a11y. + $tabindex = ( isset( $node->meta['tabindex'] ) && is_numeric( $node->meta['tabindex'] ) ) ? (int) $node->meta['tabindex'] : ''; + $aria_attributes = ( '' !== $tabindex ) ? ' tabindex="' . $tabindex . '"' : ''; $menuclass = ''; @@ -497,7 +498,7 @@ class WP_Admin_Bar {
  • > href="href ) ?>" href="href ) ?>"meta['onclick'] ) ) : ?> onclick="meta['onclick'] ); ?>">
    meta['title'] ) ) : ?> title="meta['title'] ); ?>" a, #wpadminbar .quicklinks .menupop.hover ul li a:hover, #wpadminbar .quicklinks .menupop.hover ul li a:focus, +#wpadminbar .quicklinks .menupop.hover ul li div[tabindex]:hover, +#wpadminbar .quicklinks .menupop.hover ul li div[tabindex]:focus, #wpadminbar.nojs .quicklinks .menupop:hover ul li a:hover, #wpadminbar.nojs .quicklinks .menupop:hover ul li a:focus, #wpadminbar li:hover .ab-icon:before, diff --git a/tests/phpunit/tests/adminbar.php b/tests/phpunit/tests/adminbar.php index a2f61f23df..8b17eeece6 100644 --- a/tests/phpunit/tests/adminbar.php +++ b/tests/phpunit/tests/adminbar.php @@ -132,7 +132,6 @@ class Tests_AdminBar extends WP_UnitTestCase { $this->assertEquals( $profile_url, $node_my_account->href ); $this->assertEquals( $profile_url, $node_user_info->href ); $this->assertEquals( $profile_url, $node_edit_profile->href ); - } /** @@ -254,4 +253,96 @@ class Tests_AdminBar extends WP_UnitTestCase { return $wp_admin_bar; } + /** + * @ticket 32495 + * + * @dataProvider data_admin_bar_nodes_with_tabindex_meta + * + * @param array $node_data The data for a node, passed to `WP_Admin_Bar::add_node()`. + * @param string $expected_html The expected HTML when admin menu is rendered. + */ + public function test_admin_bar_with_tabindex_meta( $node_data, $expected_html ) { + $admin_bar = new WP_Admin_Bar(); + $admin_bar->add_node( $node_data ); + $admin_bar_html = get_echo( array( $admin_bar, 'render' ) ); + $this->assertContains( $expected_html, $admin_bar_html ); + } + + /** + * Data provider for test_admin_bar_with_tabindex_meta(). + * + * @return array { + * @type array { + * @type array $node_data The data for a node, passed to `WP_Admin_Bar::add_node()`. + * @type string $expected_html The expected HTML when admin bar is rendered. + * } + * } + */ + public function data_admin_bar_nodes_with_tabindex_meta() { + return array( + array( + // No tabindex. + array( + 'id' => 'test-node', + ), + '
    ', + ), + array( + // Empty string. + array( + 'id' => 'test-node', + 'meta' => array( 'tabindex' => '' ), + ), + '
    ', + ), + array( + // Integer 1 as string. + array( + 'id' => 'test-node', + 'meta' => array( 'tabindex' => '1' ), + ), + '
    ', + ), + array( + // Integer -1 as string. + array( + 'id' => 'test-node', + 'meta' => array( 'tabindex' => '-1' ), + ), + '
    ', + ), + array( + // Integer 0 as string. + array( + 'id' => 'test-node', + 'meta' => array( 'tabindex' => '0' ), + ), + '
    ', + ), + array( + // Integer, 0. + array( + 'id' => 'test-node', + 'meta' => array( 'tabindex' => 0 ), + ), + '
    ', + ), + array( + // Integer, 2. + array( + 'id' => 'test-node', + 'meta' => array( 'tabindex' => 2 ), + ), + '
    ', + ), + array( + // Boolean, false + array( + 'id' => 'test-node', + 'meta' => array( 'tabindex' => false ), + ), + '
    ', + ), + ); + } }