Skip to content

Commit aaddfbf

Browse files
committed
Tabs: Add classes option
Ref #7053 Ref gh-1411
1 parent 28dccda commit aaddfbf

File tree

6 files changed

+112
-63
lines changed

6 files changed

+112
-63
lines changed

tests/unit/tabs/tabs.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<script src="../../../external/qunit/qunit.js"></script>
1010
<script src="../../../external/jquery-simulate/jquery.simulate.js"></script>
1111
<script src="../testsuite.js"></script>
12+
<script src="../../../external/qunit-assert-classes/qunit-assert-classes.js"></script>
1213
<script>
1314
TestHelpers.loadResources({
1415
css: [ "core", "tabs" ],

tests/unit/tabs/tabs_common.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
TestHelpers.commonWidgetTests( "tabs", {
22
defaults: {
33
active: null,
4-
classes: {},
4+
classes: {
5+
"ui-tabs": "ui-corner-all",
6+
"ui-tabs-nav": "ui-corner-all",
7+
"ui-tab": "ui-corner-top",
8+
"ui-tabs-panel": "ui-corner-bottom"
9+
},
510
collapsible: false,
611
disabled: false,
712
event: "click",

tests/unit/tabs/tabs_core.js

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,32 @@ var state = TestHelpers.tabs.state;
44

55
module( "tabs: core" );
66

7-
test( "markup structure", function() {
8-
expect( 3 );
9-
var element = $( "#tabs1" ).tabs();
10-
ok( element.hasClass( "ui-tabs" ), "main element is .ui-tabs" );
11-
ok( element.find( "ul" ).hasClass( "ui-tabs-nav" ), "list item is .ui-tabs-nav" );
12-
equal( element.find( ".ui-tabs-panel" ).length, 3,
13-
".ui-tabs-panel elements exist, correct number" );
7+
test( "markup structure", function( assert ) {
8+
expect( 17 );
9+
var element = $( "#tabs1" ).tabs(),
10+
tabList = element.find( "ul, ol" ),
11+
tabs = tabList.find( "li" ),
12+
active = tabs.eq( 0 ),
13+
anchors = tabs.find( "a" ),
14+
panels = element.find( ".ui-tabs-panel" );
15+
16+
assert.hasClasses( element, "ui-tabs ui-widget ui-widget-content" );
17+
assert.lacksClasses( element, "ui-tabs-collapsible" );
18+
assert.hasClasses( tabList, "ui-tabs-nav ui-widget-header" );
19+
equal( tabList.length, 1, "The widget contains exactly one tab list" );
20+
assert.hasClasses( tabs[ 0 ], "ui-tab" );
21+
assert.hasClasses( tabs[ 1 ], "ui-tab" );
22+
assert.hasClasses( tabs[ 2 ], "ui-tab" );
23+
equal( tabs.length, 3, "There are exactly three tabs" );
24+
assert.hasClasses( anchors[ 0 ], "ui-tabs-anchor" );
25+
assert.hasClasses( anchors[ 1 ], "ui-tabs-anchor" );
26+
assert.hasClasses( anchors[ 2 ], "ui-tabs-anchor" );
27+
equal( anchors.length, 3, "There are exactly 3 anchors" );
28+
assert.hasClasses( active, "ui-tabs-active" );
29+
assert.hasClasses( panels[ 0 ], "ui-tabs-panel ui-widget-content" );
30+
assert.hasClasses( panels[ 1 ], "ui-tabs-panel ui-widget-content" );
31+
assert.hasClasses( panels[ 2 ], "ui-tabs-panel ui-widget-content" );
32+
equal( panels.length, 3, "There are exactly 3 tab panels" );
1433
});
1534

1635
$.each({
@@ -129,18 +148,21 @@ test( "accessibility", function() {
129148
equal( panels.eq( 2 ).attr( "aria-hidden" ), "true", "inactive panel has aria-hidden=true" );
130149
});
131150

132-
asyncTest( "accessibility - ajax", function() {
133-
expect( 4 );
151+
asyncTest( "accessibility - ajax", function( assert ) {
152+
expect( 6 );
134153
var element = $( "#tabs2" ).tabs(),
154+
tab = element.find( ".ui-tabs-nav li" ).eq( 3 ),
135155
panel = $( "#custom-id" );
136156

137157
equal( panel.attr( "aria-live" ), "polite", "remote panel has aria-live" );
138158
equal( panel.attr( "aria-busy" ), null, "does not have aria-busy on init" );
139159
element.tabs( "option", "active", 3 );
160+
assert.hasClasses( tab, "ui-tabs-loading" );
140161
equal( panel.attr( "aria-busy" ), "true", "panel has aria-busy during load" );
141162
element.one( "tabsload", function() {
142163
setTimeout(function() {
143164
equal( panel.attr( "aria-busy" ), null, "panel does not have aria-busy after load" );
165+
assert.lacksClasses( tab, "ui-tabs-loading" );
144166
start();
145167
}, 1 );
146168
});

tests/unit/tabs/tabs_methods.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,22 @@ test( "destroy", function() {
1515
});
1616
});
1717

18+
asyncTest( "destroy - ajax", function() {
19+
expect( 1 );
20+
domEqual( "#tabs2", function( done ) {
21+
var element = $( "#tabs2" ).tabs({
22+
load: function() {
23+
setTimeout(function() {
24+
element.tabs( "destroy" );
25+
done();
26+
start();
27+
});
28+
}
29+
});
30+
element.tabs( "option", "active", 2 );
31+
});
32+
});
33+
1834
test( "enable", function() {
1935
expect( 8 );
2036

tests/unit/tabs/tabs_options.js

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -107,28 +107,14 @@ test( "active - mismatched tab/panel order", function() {
107107
location.hash = "#";
108108
});
109109

110-
test( "{ collapsible: false }", function() {
111-
expect( 4 );
112-
113-
var element = $( "#tabs1" ).tabs({
114-
active: 1
115-
});
116-
element.tabs( "option", "active", false );
117-
equal( element.tabs( "option", "active" ), 1 );
118-
state( element, 0, 1, 0 );
119-
120-
element.find( ".ui-state-active .ui-tabs-anchor" ).eq( 1 ).click();
121-
equal( element.tabs( "option", "active" ), 1 );
122-
state( element, 0, 1, 0 );
123-
});
124-
125-
test( "{ collapsible: true }", function() {
126-
expect( 6 );
110+
test( "collapsible", function() {
111+
expect( 13 );
127112

128113
var element = $( "#tabs1" ).tabs({
129114
active: 1,
130115
collapsible: true
131116
});
117+
ok( element.hasClass( "ui-tabs-collapsible" ), "main element is .ui-tabs-collapsible" );
132118

133119
element.tabs( "option", "active", false );
134120
equal( element.tabs( "option", "active" ), false );
@@ -141,6 +127,24 @@ test( "{ collapsible: true }", function() {
141127
element.find( ".ui-state-active .ui-tabs-anchor" ).click();
142128
equal( element.tabs( "option", "active" ), false );
143129
state( element, 0, 0, 0 );
130+
131+
element.tabs( "option", "collapsible", false );
132+
ok( !element.hasClass( "ui-tabs-collapsible" ), "main element is not .ui-tabs-collapsible" );
133+
134+
element.tabs( "option", "collapsible", true );
135+
ok( element.hasClass( "ui-tabs-collapsible" ), "main element is .ui-tabs-collapsible" );
136+
137+
element.tabs({
138+
active: 1,
139+
collapsible: false
140+
});
141+
element.tabs( "option", "active", false );
142+
equal( element.tabs( "option", "active" ), 1 );
143+
state( element, 0, 1, 0 );
144+
145+
element.find( ".ui-state-active .ui-tabs-anchor" ).eq( 1 ).click();
146+
equal( element.tabs( "option", "active" ), 1 );
147+
state( element, 0, 1, 0 );
144148
});
145149

146150
test( "disabled", function() {

ui/tabs.js

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ return $.widget( "ui.tabs", {
3737
delay: 300,
3838
options: {
3939
active: null,
40+
classes: {
41+
"ui-tabs": "ui-corner-all",
42+
"ui-tabs-nav": "ui-corner-all",
43+
"ui-tab": "ui-corner-top",
44+
"ui-tabs-panel": "ui-corner-bottom"
45+
},
4046
collapsible: false,
4147
event: "click",
4248
heightStyle: "content",
@@ -77,9 +83,8 @@ return $.widget( "ui.tabs", {
7783

7884
this.running = false;
7985

80-
this.element
81-
.addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" )
82-
.toggleClass( "ui-tabs-collapsible", options.collapsible );
86+
this._addClass( "ui-tabs", "ui-widget ui-widget-content" );
87+
this._toggleClass( "ui-tabs-collapsible", null, options.collapsible );
8388

8489
this._processTabs();
8590
options.active = this._initialActive();
@@ -286,7 +291,8 @@ return $.widget( "ui.tabs", {
286291
this._super( key, value);
287292

288293
if ( key === "collapsible" ) {
289-
this.element.toggleClass( "ui-tabs-collapsible", value );
294+
this._toggleClass( "ui-tabs-collapsible", null, value );
295+
290296
// Setting collapsible: false while collapsed; open first panel
291297
if ( !value && this.options.active === false ) {
292298
this._activate( 0 );
@@ -362,12 +368,12 @@ return $.widget( "ui.tabs", {
362368
this.tabs.eq( 0 ).attr( "tabIndex", 0 );
363369
} else {
364370
this.active
365-
.addClass( "ui-tabs-active ui-state-active" )
366371
.attr({
367372
"aria-selected": "true",
368373
"aria-expanded": "true",
369374
tabIndex: 0
370375
});
376+
this._addClass( this.active, "ui-tabs-active", "ui-state-active" );
371377
this._getPanelForTab( this.active )
372378
.show()
373379
.attr({
@@ -382,11 +388,12 @@ return $.widget( "ui.tabs", {
382388
prevAnchors = this.anchors,
383389
prevPanels = this.panels;
384390

385-
this.tablist = this._getList()
386-
.addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
387-
.attr( "role", "tablist" )
391+
this.tablist = this._getList().attr( "role", "tablist" );
392+
this._addClass( this.tablist, "ui-tabs-nav",
393+
"ui-helper-reset ui-helper-clearfix ui-widget-header" );
388394

389-
// Prevent users from focusing disabled tabs via click
395+
// Prevent users from focusing disabled tabs via click
396+
this.tablist
390397
.delegate( "> li", "mousedown" + this.eventNamespace, function( event ) {
391398
if ( $( this ).is( ".ui-state-disabled" ) ) {
392399
event.preventDefault();
@@ -406,20 +413,20 @@ return $.widget( "ui.tabs", {
406413
});
407414

408415
this.tabs = this.tablist.find( "> li:has(a[href])" )
409-
.addClass( "ui-state-default ui-corner-top" )
410416
.attr({
411417
role: "tab",
412418
tabIndex: -1
413419
});
420+
this._addClass( this.tabs, "ui-tab", "ui-state-default" );
414421

415422
this.anchors = this.tabs.map(function() {
416423
return $( "a", this )[ 0 ];
417424
})
418-
.addClass( "ui-tabs-anchor" )
419425
.attr({
420426
role: "presentation",
421427
tabIndex: -1
422428
});
429+
this._addClass( this.anchors, "ui-tabs-anchor" );
423430

424431
this.panels = $();
425432

@@ -461,9 +468,8 @@ return $.widget( "ui.tabs", {
461468
panel.attr( "aria-labelledby", anchorId );
462469
});
463470

464-
this.panels
465-
.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
466-
.attr( "role", "tabpanel" );
471+
this.panels.attr( "role", "tabpanel" );
472+
this._addClass( this.panels, "ui-tabs-panel", "ui-widget-content" );
467473

468474
// Avoid memory leaks (#10056)
469475
if ( prevTabs ) {
@@ -481,11 +487,12 @@ return $.widget( "ui.tabs", {
481487
_createPanel: function( id ) {
482488
return $( "<div>" )
483489
.attr( "id", id )
484-
.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
485490
.data( "ui-tabs-destroy", true );
486491
},
487492

488493
_setupDisabled: function( disabled ) {
494+
var currentItem, li, i;
495+
489496
if ( $.isArray( disabled ) ) {
490497
if ( !disabled.length ) {
491498
disabled = false;
@@ -495,15 +502,14 @@ return $.widget( "ui.tabs", {
495502
}
496503

497504
// disable tabs
498-
for ( var i = 0, li; ( li = this.tabs[ i ] ); i++ ) {
505+
for ( i = 0; ( li = this.tabs[ i ] ); i++ ) {
506+
currentItem = $( li );
499507
if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
500-
$( li )
501-
.addClass( "ui-state-disabled" )
502-
.attr( "aria-disabled", "true" );
508+
currentItem.attr( "aria-disabled", "true" );
509+
this._addClass( currentItem, null, "ui-state-disabled" );
503510
} else {
504-
$( li )
505-
.removeClass( "ui-state-disabled" )
506-
.removeAttr( "aria-disabled" );
511+
currentItem.removeAttr( "aria-disabled" );
512+
this._removeClass( currentItem, null, "ui-state-disabled" );
507513
}
508514
}
509515

@@ -629,7 +635,7 @@ return $.widget( "ui.tabs", {
629635
}
630636

631637
function show() {
632-
eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
638+
that._addClass( eventData.newTab.closest( "li" ), "ui-tabs-active", "ui-state-active" );
633639

634640
if ( toShow.length && that.options.show ) {
635641
that._show( toShow, that.options.show, complete );
@@ -642,11 +648,13 @@ return $.widget( "ui.tabs", {
642648
// start out by hiding, then showing, then completing
643649
if ( toHide.length && this.options.hide ) {
644650
this._hide( toHide, this.options.hide, function() {
645-
eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
651+
that._removeClass( eventData.oldTab.closest( "li" ),
652+
"ui-tabs-active", "ui-state-active" );
646653
show();
647654
});
648655
} else {
649-
eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
656+
this._removeClass( eventData.oldTab.closest( "li" ),
657+
"ui-tabs-active", "ui-state-active" );
650658
toHide.hide();
651659
show();
652660
}
@@ -716,27 +724,20 @@ return $.widget( "ui.tabs", {
716724
this.xhr.abort();
717725
}
718726

719-
this.element.removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" );
720-
721727
this.tablist
722-
.removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
723-
.removeAttr( "role" );
728+
.removeAttr( "role" )
729+
.unbind( this.eventNamespace );
724730

725731
this.anchors
726-
.removeClass( "ui-tabs-anchor" )
727732
.removeAttr( "role" )
728733
.removeAttr( "tabIndex" )
729734
.removeUniqueId();
730735

731-
this.tablist.unbind( this.eventNamespace );
732-
733736
this.tabs.add( this.panels ).each(function() {
734737
if ( $.data( this, "ui-tabs-destroy" ) ) {
735738
$( this ).remove();
736739
} else {
737740
$( this )
738-
.removeClass( "ui-state-default ui-state-active ui-state-disabled " +
739-
"ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel" )
740741
.removeAttr( "tabIndex" )
741742
.removeAttr( "aria-live" )
742743
.removeAttr( "aria-busy" )
@@ -827,7 +828,7 @@ return $.widget( "ui.tabs", {
827828
that.panels.stop( false, true );
828829
}
829830

830-
tab.removeClass( "ui-tabs-loading" );
831+
that._removeClass( tab, "ui-tabs-loading" );
831832
panel.removeAttr( "aria-busy" );
832833

833834
if ( jqXHR === that.xhr ) {
@@ -846,7 +847,7 @@ return $.widget( "ui.tabs", {
846847
// jQuery <1.8 returns false if the request is canceled in beforeSend,
847848
// but as of 1.8, $.ajax() always returns a jqXHR object.
848849
if ( this.xhr && this.xhr.statusText !== "canceled" ) {
849-
tab.addClass( "ui-tabs-loading" );
850+
this._addClass( tab, "ui-tabs-loading" );
850851
panel.attr( "aria-busy", "true" );
851852

852853
this.xhr

0 commit comments

Comments
 (0)