]> source.dussan.org Git - jquery-ui.git/commitdiff
Accordion: Enhance refresh to allow adding/removing panels. Fixes #4672 - Accordion...
authorDavid Petersen <public@petersendidit.com>
Mon, 12 Nov 2012 02:37:56 +0000 (21:37 -0500)
committerMike Sherov <mike.sherov@gmail.com>
Mon, 12 Nov 2012 02:38:21 +0000 (21:38 -0500)
tests/unit/accordion/accordion_methods.js
ui/jquery.ui.accordion.js

index f8f556fa78fa86e98d3a03d14e531f7232dcc6a3..b1123b8284701b82b55803d4d2ae3bdc8783428f 100644 (file)
@@ -30,7 +30,7 @@ test( "enable/disable", function() {
 });
 
 test( "refresh", function() {
-       expect( 6 );
+       expect( 17 );
        var element = $( "#navigation" )
                .parent()
                        .height( 300 )
@@ -43,6 +43,67 @@ test( "refresh", function() {
        element.parent().height( 500 );
        element.accordion( "refresh" );
        equalHeight( element, 455 );
+
+       element = $( "#list1" );
+       element.accordion();
+       state( element, 1, 0, 0 );
+
+       // disable panel via markup
+       element.find( "h3.bar" ).eq( 1 ).addClass( "ui-state-disabled" );
+       element.accordion( "refresh" );
+       state( element, 1, 0, 0 );
+
+       // don't add multiple icons
+       element.accordion( "refresh" );
+       equal( element.find( ".ui-accordion-header-icon" ).length, 3 );
+
+       // add a panel
+       element
+               .append("<h3 class='bar' id='new_1'>new 1</h3>")
+               .append("<div class='foo' id='new_1_panel'>new 1</div>");
+       element.accordion( "refresh" );
+       state( element, 1, 0, 0, 0 );
+
+       // remove all tabs
+       element.find( "h3.bar, div.foo" ).remove();
+       element.accordion( "refresh" );
+       state( element );
+       equal( element.accordion( "option", "active" ), false, "no active accordion panel" );
+
+       // add panels
+       element
+               .append("<h3 class='bar' id='new_2'>new 2</h3>")
+               .append("<div class='foo' id='new_2_panel'>new 2</div>")
+               .append("<h3 class='bar' id='new_3'>new 3</h3>")
+               .append("<div class='foo' id='new_3_panel'>new 3</div>")
+               .append("<h3 class='bar' id='new_4'>new 4</h3>")
+               .append("<div class='foo' id='new_4_panel'>new 4</div>")
+               .append("<h3 class='bar' id='new_5'>new 5</h3>")
+               .append("<div class='foo' id='new_5_panel'>new 5</div>");
+       element.accordion( "refresh" );
+       state( element, 1, 0, 0, 0 );
+
+       // activate third tab
+       element.accordion( "option", "active", 2 );
+       state( element, 0, 0, 1, 0 );
+
+       // remove fourth panel, third panel should stay active
+       element.find( "h3.bar" ).eq( 3 ).remove();
+       element.find( "div.foo" ).eq( 3 ).remove();
+       element.accordion( "refresh" );
+       state( element, 0, 0, 1 );
+
+       // remove third (active) panel, second panel should become active
+       element.find( "h3.bar" ).eq( 2 ).remove();
+       element.find( "div.foo" ).eq( 2 ).remove();
+       element.accordion( "refresh" );
+       state( element, 0, 1 );
+
+       // remove first panel, previously active panel (now first) should stay active
+       element.find( "h3.bar" ).eq( 0 ).remove();
+       element.find( "div.foo" ).eq( 0 ).remove();
+       element.accordion( "refresh" );
+       state( element, 1 );
 });
 
 test( "widget", function() {
index 17ce155a87f0394483f2a62031010848fee1a1c0..4ebc5b5493086c705b80eb71fd97a72e479bbf60 100644 (file)
@@ -43,95 +43,23 @@ $.widget( "ui.accordion", {
        },
 
        _create: function() {
-               var accordionId = this.accordionId = "ui-accordion-" +
-                               (this.element.attr( "id" ) || ++uid),
-                       options = this.options;
-
+               var options = this.options;
                this.prevShow = this.prevHide = $();
-               this.element.addClass( "ui-accordion ui-widget ui-helper-reset" );
-
-               this.headers = this.element.find( options.header )
-                       .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" );
-               this._hoverable( this.headers );
-               this._focusable( this.headers );
-
-               this.headers.next()
-                       .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" )
-                       .hide();
+               this.element.addClass( "ui-accordion ui-widget ui-helper-reset" )
+                       // ARIA
+                       .attr( "role", "tablist" );
 
                // don't allow collapsible: false and active: false / null
                if ( !options.collapsible && (options.active === false || options.active == null) ) {
                        options.active = 0;
                }
+
+               this._processPanels();
                // handle negative values
                if ( options.active < 0 ) {
                        options.active += this.headers.length;
                }
-               this.active = this._findActive( options.active )
-                       .addClass( "ui-accordion-header-active ui-state-active" )
-                       .toggleClass( "ui-corner-all ui-corner-top" );
-               this.active.next()
-                       .addClass( "ui-accordion-content-active" )
-                       .show();
-
-               this._createIcons();
-               this.refresh();
-
-               // ARIA
-               this.element.attr( "role", "tablist" );
-
-               this.headers
-                       .attr( "role", "tab" )
-                       .each(function( i ) {
-                               var header = $( this ),
-                                       headerId = header.attr( "id" ),
-                                       panel = header.next(),
-                                       panelId = panel.attr( "id" );
-                               if ( !headerId ) {
-                                       headerId = accordionId + "-header-" + i;
-                                       header.attr( "id", headerId );
-                               }
-                               if ( !panelId ) {
-                                       panelId = accordionId + "-panel-" + i;
-                                       panel.attr( "id", panelId );
-                               }
-                               header.attr( "aria-controls", panelId );
-                               panel.attr( "aria-labelledby", headerId );
-                       })
-                       .next()
-                               .attr( "role", "tabpanel" );
-
-               this.headers
-                       .not( this.active )
-                       .attr({
-                               "aria-selected": "false",
-                               tabIndex: -1
-                       })
-                       .next()
-                               .attr({
-                                       "aria-expanded": "false",
-                                       "aria-hidden": "true"
-                               })
-                               .hide();
-
-               // make sure at least one header is in the tab order
-               if ( !this.active.length ) {
-                       this.headers.eq( 0 ).attr( "tabIndex", 0 );
-               } else {
-                       this.active.attr({
-                               "aria-selected": "true",
-                               tabIndex: 0
-                       })
-                       .next()
-                               .attr({
-                                       "aria-expanded": "true",
-                                       "aria-hidden": "false"
-                               });
-               }
-
-               this._on( this.headers, { keydown: "_keydown" });
-               this._on( this.headers.next(), { keydown: "_panelKeyDown" });
-               this._setupEvents( options.event );
+               this._refresh();
        },
 
        _getCreateEventData: function() {
@@ -283,9 +211,114 @@ $.widget( "ui.accordion", {
        },
 
        refresh: function() {
+               var options = this.options;
+               this._processPanels();
+
+               // was collapsed or no panel
+               if ( ( options.active === false && options.collapsible === true ) || !this.headers.length ) {
+                       options.active = false;
+                       this.active = $();
+               // active false only when collapsible is true
+               } if ( options.active === false ) {
+                       this._activate( 0 );
+               // was active, but active panel is gone
+               } else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
+                       // all remaining panel are disabled
+                       if ( this.headers.length === this.headers.find(".ui-state-disabled").length ) {
+                               options.active = false;
+                               this.active = $();
+                       // activate previous panel
+                       } else {
+                               this._activate( Math.max( 0, options.active - 1 ) );
+                       }
+               // was active, active panel still exists
+               } else {
+                       // make sure active index is correct
+                       options.active = this.headers.index( this.active );
+               }
+
+               this._destroyIcons();
+
+               this._refresh();
+       },
+
+       _processPanels: function() {
+               this.headers = this.element.find( this.options.header )
+                       .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" );
+
+               this.headers.next()
+                       .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" )
+                       .filter(":not(.ui-accordion-content-active)")
+                       .hide();
+       },
+
+       _refresh: function() {
                var maxHeight,
-                       heightStyle = this.options.heightStyle,
-                       parent = this.element.parent();
+                       options = this.options,
+                       heightStyle = options.heightStyle,
+                       parent = this.element.parent(),
+                       accordionId = this.accordionId = "ui-accordion-" +
+                               (this.element.attr( "id" ) || ++uid);
+
+               this.active = this._findActive( options.active )
+                       .addClass( "ui-accordion-header-active ui-state-active" )
+                       .toggleClass( "ui-corner-all ui-corner-top" );
+               this.active.next()
+                       .addClass( "ui-accordion-content-active" )
+                       .show();
+
+               this.headers
+                       .attr( "role", "tab" )
+                       .each(function( i ) {
+                               var header = $( this ),
+                                       headerId = header.attr( "id" ),
+                                       panel = header.next(),
+                                       panelId = panel.attr( "id" );
+                               if ( !headerId ) {
+                                       headerId = accordionId + "-header-" + i;
+                                       header.attr( "id", headerId );
+                               }
+                               if ( !panelId ) {
+                                       panelId = accordionId + "-panel-" + i;
+                                       panel.attr( "id", panelId );
+                               }
+                               header.attr( "aria-controls", panelId );
+                               panel.attr( "aria-labelledby", headerId );
+                       })
+                       .next()
+                               .attr( "role", "tabpanel" );
+
+               this.headers
+                       .not( this.active )
+                       .attr({
+                               "aria-selected": "false",
+                               tabIndex: -1
+                       })
+                       .next()
+                               .attr({
+                                       "aria-expanded": "false",
+                                       "aria-hidden": "true"
+                               })
+                               .hide();
+
+               // make sure at least one header is in the tab order
+               if ( !this.active.length ) {
+                       this.headers.eq( 0 ).attr( "tabIndex", 0 );
+               } else {
+                       this.active.attr({
+                               "aria-selected": "true",
+                               tabIndex: 0
+                       })
+                       .next()
+                               .attr({
+                                       "aria-expanded": "true",
+                                       "aria-hidden": "false"
+                               });
+               }
+
+               this._createIcons();
+
+               this._setupEvents( options.event );
 
                if ( heightStyle === "fill" ) {
                        maxHeight = parent.height();
@@ -342,14 +375,20 @@ $.widget( "ui.accordion", {
        },
 
        _setupEvents: function( event ) {
-               var events = {};
-               if ( !event ) {
-                       return;
+               var events = {
+                       keydown: "_keydown"
+               };
+               if ( event ) {
+                       $.each( event.split(" "), function( index, eventName ) {
+                               events[ eventName ] = "_eventHandler";
+                       });
                }
-               $.each( event.split(" "), function( index, eventName ) {
-                       events[ eventName ] = "_eventHandler";
-               });
+
+               this._off( this.headers.add( this.headers.next() ) );
                this._on( this.headers, events );
+               this._on( this.headers.next(), { keydown: "_panelKeyDown" });
+               this._hoverable( this.headers );
+               this._focusable( this.headers );
        },
 
        _eventHandler: function( event ) {