From 9dd1e733f3a9526344e226e07bfdbdd2bfba5ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Scott=20Gonz=C3=A1lez?= Date: Mon, 3 Nov 2014 15:06:12 -0500 Subject: [PATCH] Tabs: Suppress automatic activation when navigating with COMMAND Fixes #9621 Closes gh-1383 (cherry picked from commit 6a242ab4ebdcec265826b959fa27881dcf462c96) --- tests/unit/tabs/tabs_core.js | 355 +++++++++++++++++++---------------- ui/tabs.js | 5 +- 2 files changed, 194 insertions(+), 166 deletions(-) diff --git a/tests/unit/tabs/tabs_core.js b/tests/unit/tabs/tabs_core.js index 2e094e11b..2d18a6f1a 100644 --- a/tests/unit/tabs/tabs_core.js +++ b/tests/unit/tabs/tabs_core.js @@ -299,197 +299,224 @@ asyncTest( "keyboard support - LEFT, RIGHT, UP, DOWN, HOME, END, SPACE, ENTER", setTimeout( step1, 1 ); }); -asyncTest( "keyboard support - CTRL navigation", function() { - expect( 115 ); - var element = $( "#tabs1" ).tabs(), - tabs = element.find( ".ui-tabs-nav li" ), - panels = element.find( ".ui-tabs-panel" ), - keyCode = $.ui.keyCode; +// Navigation with CTRL and COMMAND (both behave the same) +$.each({ + ctrl: "CTRL", + meta: "COMMAND" +}, function( modifier, label ) { + asyncTest( "keyboard support - " + label + " navigation", function() { + expect( 115 ); + var element = $( "#tabs1" ).tabs(), + tabs = element.find( ".ui-tabs-nav li" ), + panels = element.find( ".ui-tabs-panel" ), + keyCode = $.ui.keyCode; - element.tabs( "instance" ).delay = 50; + element.tabs( "instance" ).delay = 50; - equal( tabs.filter( ".ui-state-focus" ).length, 0, "no tabs focused on init" ); - tabs.eq( 0 ).simulate( "focus" ); + equal( tabs.filter( ".ui-state-focus" ).length, 0, "no tabs focused on init" ); + tabs.eq( 0 ).simulate( "focus" ); - // down - function step1() { - ok( tabs.eq( 0 ).is( ".ui-state-focus" ), "first tab has focus" ); - equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); - ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); + // down + function step1() { + var eventProperties = { keyCode: keyCode.DOWN }; + eventProperties[ modifier + "Key" ] = true; - tabs.eq( 0 ).simulate( "keydown", { keyCode: keyCode.DOWN, ctrlKey: true } ); - ok( tabs.eq( 1 ).is( ".ui-state-focus" ), "DOWN moves focus to next tab" ); - ok( !tabs.eq( 0 ).is( ".ui-state-focus" ), "first tab is no longer focused" ); - equal( tabs.eq( 1 ).attr( "aria-selected" ), "false", "second tab has aria-selected=false" ); - equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); - ok( panels.eq( 1 ).is( ":hidden" ), "second panel is still hidden" ); - equal( tabs.eq( 1 ).attr( "aria-expanded" ), "false", "second tab has aria-expanded=false" ); - equal( panels.eq( 1 ).attr( "aria-hidden" ), "true", "second panel has aria-hidden=true" ); - ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); - equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); - equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + ok( tabs.eq( 0 ).is( ".ui-state-focus" ), "first tab has focus" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); - setTimeout( step2, 100 ); - } + tabs.eq( 0 ).simulate( "keydown", eventProperties ); + ok( tabs.eq( 1 ).is( ".ui-state-focus" ), "DOWN moves focus to next tab" ); + ok( !tabs.eq( 0 ).is( ".ui-state-focus" ), "first tab is no longer focused" ); + equal( tabs.eq( 1 ).attr( "aria-selected" ), "false", "second tab has aria-selected=false" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 1 ).is( ":hidden" ), "second panel is still hidden" ); + equal( tabs.eq( 1 ).attr( "aria-expanded" ), "false", "second tab has aria-expanded=false" ); + equal( panels.eq( 1 ).attr( "aria-hidden" ), "true", "second panel has aria-hidden=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); + equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); - // right - function step2() { - equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); - ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); - equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); - equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); - ok( panels.eq( 1 ).is( ":hidden" ), "second panel is hidden" ); - equal( tabs.eq( 1 ).attr( "aria-expanded" ), "false", "second tab has aria-expanded=false" ); - equal( panels.eq( 1 ).attr( "aria-hidden" ), "true", "second panel has aria-hidden=true" ); + setTimeout( step2, 100 ); + } - tabs.eq( 1 ).simulate( "keydown", { keyCode: keyCode.RIGHT, ctrlKey: true } ); - ok( tabs.eq( 2 ).is( ".ui-state-focus" ), "RIGHT moves focus to next tab" ); - equal( tabs.eq( 2 ).attr( "aria-selected" ), "false", "third tab has aria-selected=false" ); - equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); - ok( panels.eq( 2 ).is( ":hidden" ), "third panel is still hidden" ); - equal( tabs.eq( 2 ).attr( "aria-expanded" ), "false", "third tab has aria-expanded=false" ); - equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); - ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); - equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); - equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + // right + function step2() { + var eventProperties = { keyCode: keyCode.RIGHT }; + eventProperties[ modifier + "Key" ] = true; - setTimeout( step3, 100 ); - } + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); + equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + ok( panels.eq( 1 ).is( ":hidden" ), "second panel is hidden" ); + equal( tabs.eq( 1 ).attr( "aria-expanded" ), "false", "second tab has aria-expanded=false" ); + equal( panels.eq( 1 ).attr( "aria-hidden" ), "true", "second panel has aria-hidden=true" ); - // down (wrap) - function step3() { - equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); - ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); - equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); - equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); - ok( panels.eq( 2 ).is( ":hidden" ), "third panel is hidden" ); - equal( tabs.eq( 2 ).attr( "aria-expanded" ), "false", "third tab has aria-expanded=false" ); - equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); + tabs.eq( 1 ).simulate( "keydown", eventProperties ); + ok( tabs.eq( 2 ).is( ".ui-state-focus" ), "RIGHT moves focus to next tab" ); + equal( tabs.eq( 2 ).attr( "aria-selected" ), "false", "third tab has aria-selected=false" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 2 ).is( ":hidden" ), "third panel is still hidden" ); + equal( tabs.eq( 2 ).attr( "aria-expanded" ), "false", "third tab has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); + equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); - tabs.eq( 2 ).simulate( "keydown", { keyCode: keyCode.DOWN, ctrlKey: true } ); - ok( tabs.eq( 0 ).is( ".ui-state-focus" ), "DOWN wraps focus to first tab" ); - equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); - ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); - equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); - equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + setTimeout( step3, 100 ); + } - setTimeout( step4, 100 ); - } + // down (wrap) + function step3() { + var eventProperties = { keyCode: keyCode.DOWN }; + eventProperties[ modifier + "Key" ] = true; - // up (wrap) - function step4() { - equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); - ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); - equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); - equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); + equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + ok( panels.eq( 2 ).is( ":hidden" ), "third panel is hidden" ); + equal( tabs.eq( 2 ).attr( "aria-expanded" ), "false", "third tab has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); - tabs.eq( 0 ).simulate( "keydown", { keyCode: keyCode.UP, ctrlKey: true } ); - ok( tabs.eq( 2 ).is( ".ui-state-focus" ), "UP wraps focus to last tab" ); - equal( tabs.eq( 2 ).attr( "aria-selected" ), "false", "third tab has aria-selected=false" ); - equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); - ok( panels.eq( 2 ).is( ":hidden" ), "third panel is still hidden" ); - equal( tabs.eq( 2 ).attr( "aria-expanded" ), "false", "third tab has aria-expanded=false" ); - equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); - ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); - equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); - equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + tabs.eq( 2 ).simulate( "keydown", eventProperties ); + ok( tabs.eq( 0 ).is( ".ui-state-focus" ), "DOWN wraps focus to first tab" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); + equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); - setTimeout( step5, 100 ); - } + setTimeout( step4, 100 ); + } - // left - function step5() { - equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); - ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); - equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); - equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); - ok( panels.eq( 2 ).is( ":hidden" ), "third panel is hidden" ); - equal( tabs.eq( 2 ).attr( "aria-expanded" ), "false", "third tab has aria-expanded=false" ); - equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); + // up (wrap) + function step4() { + var eventProperties = { keyCode: keyCode.UP }; + eventProperties[ modifier + "Key" ] = true; - tabs.eq( 2 ).simulate( "keydown", { keyCode: keyCode.LEFT, ctrlKey: true } ); - ok( tabs.eq( 1 ).is( ".ui-state-focus" ), "LEFT moves focus to previous tab" ); - equal( tabs.eq( 1 ).attr( "aria-selected" ), "false", "second tab has aria-selected=false" ); - equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); - ok( panels.eq( 1 ).is( ":hidden" ), "second panel is still hidden" ); - equal( tabs.eq( 1 ).attr( "aria-expanded" ), "false", "second tab has aria-expanded=false" ); - equal( panels.eq( 1 ).attr( "aria-hidden" ), "true", "second panel has aria-hidden=true" ); - ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); - equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); - equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); + equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); - setTimeout( step6, 100 ); - } + tabs.eq( 0 ).simulate( "keydown", eventProperties ); + ok( tabs.eq( 2 ).is( ".ui-state-focus" ), "UP wraps focus to last tab" ); + equal( tabs.eq( 2 ).attr( "aria-selected" ), "false", "third tab has aria-selected=false" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 2 ).is( ":hidden" ), "third panel is still hidden" ); + equal( tabs.eq( 2 ).attr( "aria-expanded" ), "false", "third tab has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); + equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); - // home - function step6() { - equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); - ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); - equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); - equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); - ok( panels.eq( 1 ).is( ":hidden" ), "second panel is hidden" ); - equal( tabs.eq( 1 ).attr( "aria-expanded" ), "false", "second tab has aria-expanded=false" ); - equal( panels.eq( 1 ).attr( "aria-hidden" ), "true", "second panel has aria-hidden=true" ); + setTimeout( step5, 100 ); + } - tabs.eq( 1 ).simulate( "keydown", { keyCode: keyCode.HOME, ctrlKey: true } ); - ok( tabs.eq( 0 ).is( ".ui-state-focus" ), "HOME moves focus to first tab" ); - equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); - equal( tabs.eq( 1 ).attr( "aria-selected" ), "false", "second tab has aria-selected=false" ); - ok( panels.eq( 1 ).is( ":hidden" ), "second panel is still hidden" ); - equal( tabs.eq( 1 ).attr( "aria-expanded" ), "false", "second tab has aria-expanded=false" ); - equal( panels.eq( 1 ).attr( "aria-hidden" ), "true", "second panel has aria-hidden=true" ); - ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); - equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); - equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + // left + function step5() { + var eventProperties = { keyCode: keyCode.LEFT }; + eventProperties[ modifier + "Key" ] = true; - setTimeout( step7, 100 ); - } + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); + equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + ok( panels.eq( 2 ).is( ":hidden" ), "third panel is hidden" ); + equal( tabs.eq( 2 ).attr( "aria-expanded" ), "false", "third tab has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); - // end - function step7() { - equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); - ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); - equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); - equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + tabs.eq( 2 ).simulate( "keydown", eventProperties ); + ok( tabs.eq( 1 ).is( ".ui-state-focus" ), "LEFT moves focus to previous tab" ); + equal( tabs.eq( 1 ).attr( "aria-selected" ), "false", "second tab has aria-selected=false" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 1 ).is( ":hidden" ), "second panel is still hidden" ); + equal( tabs.eq( 1 ).attr( "aria-expanded" ), "false", "second tab has aria-expanded=false" ); + equal( panels.eq( 1 ).attr( "aria-hidden" ), "true", "second panel has aria-hidden=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); + equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); - tabs.eq( 0 ).simulate( "keydown", { keyCode: keyCode.END, ctrlKey: true } ); - ok( tabs.eq( 2 ).is( ".ui-state-focus" ), "END moves focus to last tab" ); - equal( tabs.eq( 2 ).attr( "aria-selected" ), "false", "third tab has aria-selected=false" ); - equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); - ok( panels.eq( 2 ).is( ":hidden" ), "third panel is still hidden" ); - equal( tabs.eq( 2 ).attr( "aria-expanded" ), "false", "third tab has aria-expanded=false" ); - equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); - ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); - equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); - equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + setTimeout( step6, 100 ); + } - setTimeout( step8, 100 ); - } + // home + function step6() { + var eventProperties = { keyCode: keyCode.HOME }; + eventProperties[ modifier + "Key" ] = true; - // space - function step8() { - equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); - ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); - equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); - equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); - ok( panels.eq( 2 ).is( ":hidden" ), "third panel is hidden" ); - equal( tabs.eq( 2 ).attr( "aria-expanded" ), "false", "third tab has aria-expanded=false" ); - equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); + equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + ok( panels.eq( 1 ).is( ":hidden" ), "second panel is hidden" ); + equal( tabs.eq( 1 ).attr( "aria-expanded" ), "false", "second tab has aria-expanded=false" ); + equal( panels.eq( 1 ).attr( "aria-hidden" ), "true", "second panel has aria-hidden=true" ); - tabs.eq( 2 ).simulate( "keydown", { keyCode: keyCode.SPACE } ); - equal( tabs.eq( 2 ).attr( "aria-selected" ), "true", "third tab has aria-selected=true" ); - equal( tabs.eq( 0 ).attr( "aria-selected" ), "false", "first tab has aria-selected=false" ); - ok( panels.eq( 2 ).is( ":visible" ), "third panel is visible" ); - equal( tabs.eq( 2 ).attr( "aria-expanded" ), "true", "third tab has aria-expanded=true" ); - equal( panels.eq( 2 ).attr( "aria-hidden" ), "false", "third panel has aria-hidden=false" ); - ok( panels.eq( 0 ).is( ":hidden" ), "first panel is hidden" ); - equal( tabs.eq( 0 ).attr( "aria-expanded" ), "false", "first tab has aria-expanded=false" ); - equal( panels.eq( 0 ).attr( "aria-hidden" ), "true", "first panel has aria-hidden=true" ); + tabs.eq( 1 ).simulate( "keydown", eventProperties ); + ok( tabs.eq( 0 ).is( ".ui-state-focus" ), "HOME moves focus to first tab" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + equal( tabs.eq( 1 ).attr( "aria-selected" ), "false", "second tab has aria-selected=false" ); + ok( panels.eq( 1 ).is( ":hidden" ), "second panel is still hidden" ); + equal( tabs.eq( 1 ).attr( "aria-expanded" ), "false", "second tab has aria-expanded=false" ); + equal( panels.eq( 1 ).attr( "aria-hidden" ), "true", "second panel has aria-hidden=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); + equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); - setTimeout( start, 1 ); - } + setTimeout( step7, 100 ); + } - setTimeout( step1, 1 ); + // end + function step7() { + var eventProperties = { keyCode: keyCode.END }; + eventProperties[ modifier + "Key" ] = true; + + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); + equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + + tabs.eq( 0 ).simulate( "keydown", eventProperties ); + ok( tabs.eq( 2 ).is( ".ui-state-focus" ), "END moves focus to last tab" ); + equal( tabs.eq( 2 ).attr( "aria-selected" ), "false", "third tab has aria-selected=false" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 2 ).is( ":hidden" ), "third panel is still hidden" ); + equal( tabs.eq( 2 ).attr( "aria-expanded" ), "false", "third tab has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is still visible" ); + equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + + setTimeout( step8, 100 ); + } + + // space + function step8() { + equal( tabs.eq( 0 ).attr( "aria-selected" ), "true", "first tab has aria-selected=true" ); + ok( panels.eq( 0 ).is( ":visible" ), "first panel is visible" ); + equal( tabs.eq( 0 ).attr( "aria-expanded" ), "true", "first tab has aria-expanded=true" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "false", "first panel has aria-hidden=false" ); + ok( panels.eq( 2 ).is( ":hidden" ), "third panel is hidden" ); + equal( tabs.eq( 2 ).attr( "aria-expanded" ), "false", "third tab has aria-expanded=false" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "third panel has aria-hidden=true" ); + + tabs.eq( 2 ).simulate( "keydown", { keyCode: keyCode.SPACE } ); + equal( tabs.eq( 2 ).attr( "aria-selected" ), "true", "third tab has aria-selected=true" ); + equal( tabs.eq( 0 ).attr( "aria-selected" ), "false", "first tab has aria-selected=false" ); + ok( panels.eq( 2 ).is( ":visible" ), "third panel is visible" ); + equal( tabs.eq( 2 ).attr( "aria-expanded" ), "true", "third tab has aria-expanded=true" ); + equal( panels.eq( 2 ).attr( "aria-hidden" ), "false", "third panel has aria-hidden=false" ); + ok( panels.eq( 0 ).is( ":hidden" ), "first panel is hidden" ); + equal( tabs.eq( 0 ).attr( "aria-expanded" ), "false", "first tab has aria-expanded=false" ); + equal( panels.eq( 0 ).attr( "aria-hidden" ), "true", "first panel has aria-hidden=true" ); + + setTimeout( start, 1 ); + } + + setTimeout( step1, 1 ); + }); }); asyncTest( "keyboard support - CTRL+UP, ALT+PAGE_DOWN, ALT+PAGE_UP", function() { diff --git a/ui/tabs.js b/ui/tabs.js index e45a97c72..808f53fbb 100644 --- a/ui/tabs.js +++ b/ui/tabs.js @@ -201,8 +201,9 @@ return $.widget( "ui.tabs", { clearTimeout( this.activating ); selectedIndex = this._focusNextTab( selectedIndex, goingForward ); - // Navigating with control key will prevent automatic activation - if ( !event.ctrlKey ) { + // Navigating with control/command key will prevent automatic activation + if ( !event.ctrlKey && !event.metaKey ) { + // Update aria-selected immediately so that AT think the tab is already selected. // Otherwise AT may confuse the user by stating that they need to activate the tab, // but the tab will already be activated by the time the announcement finishes.