Tabs: Add classes option

Ref #7053
Ref gh-1411
This commit is contained in:
Alexander Schmitz 2014-12-03 11:28:56 -05:00
parent 28dccda377
commit aaddfbfa8b
6 changed files with 112 additions and 63 deletions

View File

@ -9,6 +9,7 @@
<script src="../../../external/qunit/qunit.js"></script>
<script src="../../../external/jquery-simulate/jquery.simulate.js"></script>
<script src="../testsuite.js"></script>
<script src="../../../external/qunit-assert-classes/qunit-assert-classes.js"></script>
<script>
TestHelpers.loadResources({
css: [ "core", "tabs" ],

View File

@ -1,7 +1,12 @@
TestHelpers.commonWidgetTests( "tabs", {
defaults: {
active: null,
classes: {},
classes: {
"ui-tabs": "ui-corner-all",
"ui-tabs-nav": "ui-corner-all",
"ui-tab": "ui-corner-top",
"ui-tabs-panel": "ui-corner-bottom"
},
collapsible: false,
disabled: false,
event: "click",

View File

@ -4,13 +4,32 @@ var state = TestHelpers.tabs.state;
module( "tabs: core" );
test( "markup structure", function() {
expect( 3 );
var element = $( "#tabs1" ).tabs();
ok( element.hasClass( "ui-tabs" ), "main element is .ui-tabs" );
ok( element.find( "ul" ).hasClass( "ui-tabs-nav" ), "list item is .ui-tabs-nav" );
equal( element.find( ".ui-tabs-panel" ).length, 3,
".ui-tabs-panel elements exist, correct number" );
test( "markup structure", function( assert ) {
expect( 17 );
var element = $( "#tabs1" ).tabs(),
tabList = element.find( "ul, ol" ),
tabs = tabList.find( "li" ),
active = tabs.eq( 0 ),
anchors = tabs.find( "a" ),
panels = element.find( ".ui-tabs-panel" );
assert.hasClasses( element, "ui-tabs ui-widget ui-widget-content" );
assert.lacksClasses( element, "ui-tabs-collapsible" );
assert.hasClasses( tabList, "ui-tabs-nav ui-widget-header" );
equal( tabList.length, 1, "The widget contains exactly one tab list" );
assert.hasClasses( tabs[ 0 ], "ui-tab" );
assert.hasClasses( tabs[ 1 ], "ui-tab" );
assert.hasClasses( tabs[ 2 ], "ui-tab" );
equal( tabs.length, 3, "There are exactly three tabs" );
assert.hasClasses( anchors[ 0 ], "ui-tabs-anchor" );
assert.hasClasses( anchors[ 1 ], "ui-tabs-anchor" );
assert.hasClasses( anchors[ 2 ], "ui-tabs-anchor" );
equal( anchors.length, 3, "There are exactly 3 anchors" );
assert.hasClasses( active, "ui-tabs-active" );
assert.hasClasses( panels[ 0 ], "ui-tabs-panel ui-widget-content" );
assert.hasClasses( panels[ 1 ], "ui-tabs-panel ui-widget-content" );
assert.hasClasses( panels[ 2 ], "ui-tabs-panel ui-widget-content" );
equal( panels.length, 3, "There are exactly 3 tab panels" );
});
$.each({
@ -129,18 +148,21 @@ test( "accessibility", function() {
equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "inactive panel has aria-hidden=true" );
});
asyncTest( "accessibility - ajax", function() {
expect( 4 );
asyncTest( "accessibility - ajax", function( assert ) {
expect( 6 );
var element = $( "#tabs2" ).tabs(),
tab = element.find( ".ui-tabs-nav li" ).eq( 3 ),
panel = $( "#custom-id" );
equal( panel.attr( "aria-live" ), "polite", "remote panel has aria-live" );
equal( panel.attr( "aria-busy" ), null, "does not have aria-busy on init" );
element.tabs( "option", "active", 3 );
assert.hasClasses( tab, "ui-tabs-loading" );
equal( panel.attr( "aria-busy" ), "true", "panel has aria-busy during load" );
element.one( "tabsload", function() {
setTimeout(function() {
equal( panel.attr( "aria-busy" ), null, "panel does not have aria-busy after load" );
assert.lacksClasses( tab, "ui-tabs-loading" );
start();
}, 1 );
});

View File

@ -15,6 +15,22 @@ test( "destroy", function() {
});
});
asyncTest( "destroy - ajax", function() {
expect( 1 );
domEqual( "#tabs2", function( done ) {
var element = $( "#tabs2" ).tabs({
load: function() {
setTimeout(function() {
element.tabs( "destroy" );
done();
start();
});
}
});
element.tabs( "option", "active", 2 );
});
});
test( "enable", function() {
expect( 8 );

View File

@ -107,28 +107,14 @@ test( "active - mismatched tab/panel order", function() {
location.hash = "#";
});
test( "{ collapsible: false }", function() {
expect( 4 );
var element = $( "#tabs1" ).tabs({
active: 1
});
element.tabs( "option", "active", false );
equal( element.tabs( "option", "active" ), 1 );
state( element, 0, 1, 0 );
element.find( ".ui-state-active .ui-tabs-anchor" ).eq( 1 ).click();
equal( element.tabs( "option", "active" ), 1 );
state( element, 0, 1, 0 );
});
test( "{ collapsible: true }", function() {
expect( 6 );
test( "collapsible", function() {
expect( 13 );
var element = $( "#tabs1" ).tabs({
active: 1,
collapsible: true
});
ok( element.hasClass( "ui-tabs-collapsible" ), "main element is .ui-tabs-collapsible" );
element.tabs( "option", "active", false );
equal( element.tabs( "option", "active" ), false );
@ -141,6 +127,24 @@ test( "{ collapsible: true }", function() {
element.find( ".ui-state-active .ui-tabs-anchor" ).click();
equal( element.tabs( "option", "active" ), false );
state( element, 0, 0, 0 );
element.tabs( "option", "collapsible", false );
ok( !element.hasClass( "ui-tabs-collapsible" ), "main element is not .ui-tabs-collapsible" );
element.tabs( "option", "collapsible", true );
ok( element.hasClass( "ui-tabs-collapsible" ), "main element is .ui-tabs-collapsible" );
element.tabs({
active: 1,
collapsible: false
});
element.tabs( "option", "active", false );
equal( element.tabs( "option", "active" ), 1 );
state( element, 0, 1, 0 );
element.find( ".ui-state-active .ui-tabs-anchor" ).eq( 1 ).click();
equal( element.tabs( "option", "active" ), 1 );
state( element, 0, 1, 0 );
});
test( "disabled", function() {

View File

@ -37,6 +37,12 @@ return $.widget( "ui.tabs", {
delay: 300,
options: {
active: null,
classes: {
"ui-tabs": "ui-corner-all",
"ui-tabs-nav": "ui-corner-all",
"ui-tab": "ui-corner-top",
"ui-tabs-panel": "ui-corner-bottom"
},
collapsible: false,
event: "click",
heightStyle: "content",
@ -77,9 +83,8 @@ return $.widget( "ui.tabs", {
this.running = false;
this.element
.addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" )
.toggleClass( "ui-tabs-collapsible", options.collapsible );
this._addClass( "ui-tabs", "ui-widget ui-widget-content" );
this._toggleClass( "ui-tabs-collapsible", null, options.collapsible );
this._processTabs();
options.active = this._initialActive();
@ -286,7 +291,8 @@ return $.widget( "ui.tabs", {
this._super( key, value);
if ( key === "collapsible" ) {
this.element.toggleClass( "ui-tabs-collapsible", value );
this._toggleClass( "ui-tabs-collapsible", null, value );
// Setting collapsible: false while collapsed; open first panel
if ( !value && this.options.active === false ) {
this._activate( 0 );
@ -362,12 +368,12 @@ return $.widget( "ui.tabs", {
this.tabs.eq( 0 ).attr( "tabIndex", 0 );
} else {
this.active
.addClass( "ui-tabs-active ui-state-active" )
.attr({
"aria-selected": "true",
"aria-expanded": "true",
tabIndex: 0
});
this._addClass( this.active, "ui-tabs-active", "ui-state-active" );
this._getPanelForTab( this.active )
.show()
.attr({
@ -382,11 +388,12 @@ return $.widget( "ui.tabs", {
prevAnchors = this.anchors,
prevPanels = this.panels;
this.tablist = this._getList()
.addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
.attr( "role", "tablist" )
this.tablist = this._getList().attr( "role", "tablist" );
this._addClass( this.tablist, "ui-tabs-nav",
"ui-helper-reset ui-helper-clearfix ui-widget-header" );
// Prevent users from focusing disabled tabs via click
// Prevent users from focusing disabled tabs via click
this.tablist
.delegate( "> li", "mousedown" + this.eventNamespace, function( event ) {
if ( $( this ).is( ".ui-state-disabled" ) ) {
event.preventDefault();
@ -406,20 +413,20 @@ return $.widget( "ui.tabs", {
});
this.tabs = this.tablist.find( "> li:has(a[href])" )
.addClass( "ui-state-default ui-corner-top" )
.attr({
role: "tab",
tabIndex: -1
});
this._addClass( this.tabs, "ui-tab", "ui-state-default" );
this.anchors = this.tabs.map(function() {
return $( "a", this )[ 0 ];
})
.addClass( "ui-tabs-anchor" )
.attr({
role: "presentation",
tabIndex: -1
});
this._addClass( this.anchors, "ui-tabs-anchor" );
this.panels = $();
@ -461,9 +468,8 @@ return $.widget( "ui.tabs", {
panel.attr( "aria-labelledby", anchorId );
});
this.panels
.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
.attr( "role", "tabpanel" );
this.panels.attr( "role", "tabpanel" );
this._addClass( this.panels, "ui-tabs-panel", "ui-widget-content" );
// Avoid memory leaks (#10056)
if ( prevTabs ) {
@ -481,11 +487,12 @@ return $.widget( "ui.tabs", {
_createPanel: function( id ) {
return $( "<div>" )
.attr( "id", id )
.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
.data( "ui-tabs-destroy", true );
},
_setupDisabled: function( disabled ) {
var currentItem, li, i;
if ( $.isArray( disabled ) ) {
if ( !disabled.length ) {
disabled = false;
@ -495,15 +502,14 @@ return $.widget( "ui.tabs", {
}
// disable tabs
for ( var i = 0, li; ( li = this.tabs[ i ] ); i++ ) {
for ( i = 0; ( li = this.tabs[ i ] ); i++ ) {
currentItem = $( li );
if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
$( li )
.addClass( "ui-state-disabled" )
.attr( "aria-disabled", "true" );
currentItem.attr( "aria-disabled", "true" );
this._addClass( currentItem, null, "ui-state-disabled" );
} else {
$( li )
.removeClass( "ui-state-disabled" )
.removeAttr( "aria-disabled" );
currentItem.removeAttr( "aria-disabled" );
this._removeClass( currentItem, null, "ui-state-disabled" );
}
}
@ -629,7 +635,7 @@ return $.widget( "ui.tabs", {
}
function show() {
eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
that._addClass( eventData.newTab.closest( "li" ), "ui-tabs-active", "ui-state-active" );
if ( toShow.length && that.options.show ) {
that._show( toShow, that.options.show, complete );
@ -642,11 +648,13 @@ return $.widget( "ui.tabs", {
// start out by hiding, then showing, then completing
if ( toHide.length && this.options.hide ) {
this._hide( toHide, this.options.hide, function() {
eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
that._removeClass( eventData.oldTab.closest( "li" ),
"ui-tabs-active", "ui-state-active" );
show();
});
} else {
eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
this._removeClass( eventData.oldTab.closest( "li" ),
"ui-tabs-active", "ui-state-active" );
toHide.hide();
show();
}
@ -716,27 +724,20 @@ return $.widget( "ui.tabs", {
this.xhr.abort();
}
this.element.removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" );
this.tablist
.removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
.removeAttr( "role" );
.removeAttr( "role" )
.unbind( this.eventNamespace );
this.anchors
.removeClass( "ui-tabs-anchor" )
.removeAttr( "role" )
.removeAttr( "tabIndex" )
.removeUniqueId();
this.tablist.unbind( this.eventNamespace );
this.tabs.add( this.panels ).each(function() {
if ( $.data( this, "ui-tabs-destroy" ) ) {
$( this ).remove();
} else {
$( this )
.removeClass( "ui-state-default ui-state-active ui-state-disabled " +
"ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel" )
.removeAttr( "tabIndex" )
.removeAttr( "aria-live" )
.removeAttr( "aria-busy" )
@ -827,7 +828,7 @@ return $.widget( "ui.tabs", {
that.panels.stop( false, true );
}
tab.removeClass( "ui-tabs-loading" );
that._removeClass( tab, "ui-tabs-loading" );
panel.removeAttr( "aria-busy" );
if ( jqXHR === that.xhr ) {
@ -846,7 +847,7 @@ return $.widget( "ui.tabs", {
// jQuery <1.8 returns false if the request is canceled in beforeSend,
// but as of 1.8, $.ajax() always returns a jqXHR object.
if ( this.xhr && this.xhr.statusText !== "canceled" ) {
tab.addClass( "ui-tabs-loading" );
this._addClass( tab, "ui-tabs-loading" );
panel.attr( "aria-busy", "true" );
this.xhr