diff options
author | Alexander Schmitz <arschmitz@gmail.com> | 2014-12-03 11:24:44 -0500 |
---|---|---|
committer | Alexander Schmitz <arschmitz@gmail.com> | 2015-03-11 16:04:42 -0400 |
commit | 2ebef69efe96a5b6057bdedd8876a76661bde482 (patch) | |
tree | dec3f41e65974c19d3322d80f51c68134512d4b8 | |
parent | f58277a521ae41b1d3e054a419ef5fda85e7db21 (diff) | |
download | jquery-ui-2ebef69efe96a5b6057bdedd8876a76661bde482.tar.gz jquery-ui-2ebef69efe96a5b6057bdedd8876a76661bde482.zip |
Menu: Add classes option
Ref #7053
Ref gh-1411
-rw-r--r-- | tests/unit/menu/menu.html | 15 | ||||
-rw-r--r-- | tests/unit/menu/menu_core.js | 24 | ||||
-rw-r--r-- | ui/menu.js | 118 |
3 files changed, 88 insertions, 69 deletions
diff --git a/tests/unit/menu/menu.html b/tests/unit/menu/menu.html index 56115c334..114382587 100644 --- a/tests/unit/menu/menu.html +++ b/tests/unit/menu/menu.html @@ -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", "menu" ], @@ -52,7 +53,7 @@ <div id="qunit-fixture"> <ul class="foo" id="menu1"> - <li class="foo"><div>Aberdeen</div></li> + <li class="foo"><div>Aberdeen</div> <li class="foo"><div>Ada</div></li> <li class="foo"><div>Adamsville</div></li> <li class="foo"><div id="testID1">Addyston</div></li> @@ -332,6 +333,18 @@ <li class="foo"><div>-Saarland</div></li> </ul> +<ul class="foo" id="menu9"> + <li class="foo"> + <div>Aberdeen</div> + <ul> + <li class="foo"><div>Ada</div></li> + </ul> + </li> + <li class="foo"><div>Ada</div></li> + <li class="foo"><div>Adamsville</div></li> + <li class="foo"><div>Addyston</div></li> + <li class="foo"><div>Adelphi</div></li> +</ul> </div> </body> </html> diff --git a/tests/unit/menu/menu_core.js b/tests/unit/menu/menu_core.js index 5be717a66..955424477 100644 --- a/tests/unit/menu/menu_core.js +++ b/tests/unit/menu/menu_core.js @@ -2,13 +2,23 @@ module( "menu: core" ); -test( "markup structure", function() { - expect( 6 ); - var element = $( "#menu1" ).menu(); - ok( element.hasClass( "ui-menu" ), "main element is .ui-menu" ); - element.children().each(function( index ) { - ok( $( this ).hasClass( "ui-menu-item" ), "child " + index + " is .ui-menu-item" ); - }); +test( "markup structure", function( assert ) { + expect( 11 ); + var element = $( "#menu9" ).menu(), + items = element.children(), + firstItemChildren = items.eq( 0 ).children(); + + assert.hasClasses( element, "ui-menu ui-widget ui-widget-content" ); + assert.hasClasses( items[ 0 ], "ui-menu-item" ); + equal( items.eq( 0 ).children().length, 2, "Item has exactly 2 children when it has a sub menu" ); + assert.hasClasses( firstItemChildren[ 0 ], "ui-menu-item-wrapper" ); + assert.hasClasses( firstItemChildren[ 1 ], "ui-menu ui-widget ui-widget-content" ); + assert.hasClasses( firstItemChildren.eq( 1 ).children()[ 0 ], "ui-menu-item" ); + assert.hasClasses( firstItemChildren.eq( 1 ).children().eq( 0 ).children(), "ui-menu-item-wrapper" ); + assert.hasClasses( items[ 1 ], "ui-menu-item" ); + equal( items.eq( 1 ).children().length, 1, "Item has exactly 1 child when it does not have a sub menu" ); + assert.hasClasses( items[ 2 ], "ui-menu-item" ); + equal( items.eq( 2 ).children().length, 1, "Item has exactly 1 child when it does not have a sub menu" ); }); test( "accessibility", function () { diff --git a/ui/menu.js b/ui/menu.js index 353368cf9..6c09e336f 100644 --- a/ui/menu.js +++ b/ui/menu.js @@ -38,6 +38,7 @@ return $.widget( "ui.menu", { defaultElement: "<ul>", delay: 300, options: { + classes: {}, icons: { submenu: "ui-icon-caret-1-e" }, @@ -63,20 +64,19 @@ return $.widget( "ui.menu", { this.mouseHandled = false; this.element .uniqueId() - .addClass( "ui-menu ui-widget ui-widget-content" ) - .toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length ) .attr({ role: this.options.role, tabIndex: 0 }); if ( this.options.disabled ) { - this.element - .addClass( "ui-state-disabled" ) - .attr( "aria-disabled", "true" ); + this._addClass( null, "ui-state-disabled" ); + this.element.attr( "aria-disabled", "true" ); } + this._addClass( "ui-menu", "ui-widget ui-widget-content" ); this._on({ + // Prevent focus from sticking to links inside menu after clicking // them (focus should always stay on UL during navigation). "mousedown .ui-menu-item": function( event ) { @@ -118,8 +118,8 @@ return $.widget( "ui.menu", { var target = $( event.currentTarget ); // Remove ui-state-active class from siblings of the newly focused menu item // to avoid a jump caused by adjacent elements both having a class with a border - target.siblings().children( ".ui-state-active" ).removeClass( "ui-state-active" ); - + this._removeClass( target.siblings().children( ".ui-state-active" ), + null, "ui-state-active" ); this.focus( event, target ); }, mouseleave: "collapseAll", @@ -159,11 +159,19 @@ return $.widget( "ui.menu", { }, _destroy: function() { + var items = this.element.find( ".ui-menu-item" ) + .removeAttr( "role" ) + .removeAttr( "aria-disabled" ), + submenus = items.children( ".ui-menu-item-wrapper" ) + .removeUniqueId() + .removeAttr( "tabIndex" ) + .removeAttr( "role" ) + .removeAttr( "aria-haspopup" ); + // Destroy (sub)menus this.element .removeAttr( "aria-activedescendant" ) .find( ".ui-menu" ).addBack() - .removeClass( "ui-menu ui-widget ui-widget-content ui-menu-icons ui-front" ) .removeAttr( "role" ) .removeAttr( "tabIndex" ) .removeAttr( "aria-labelledby" ) @@ -173,26 +181,12 @@ return $.widget( "ui.menu", { .removeUniqueId() .show(); - // Destroy menu items - this.element.find( ".ui-menu-item" ) - .removeClass( "ui-menu-item" ) - .removeAttr( "role" ) - .removeAttr( "aria-disabled" ) - .children( ".ui-menu-item-wrapper" ) - .removeUniqueId() - .removeClass( "ui-menu-item-wrapper ui-state-hover" ) - .removeAttr( "tabIndex" ) - .removeAttr( "role" ) - .removeAttr( "aria-haspopup" ) - .children().each(function() { - var elem = $( this ); - if ( elem.data( "ui-menu-submenu-caret" ) ) { - elem.remove(); - } - }); - - // Destroy menu dividers - this.element.find( ".ui-menu-divider" ).removeClass( "ui-menu-divider ui-widget-content" ); + submenus.children().each(function() { + var elem = $( this ); + if ( elem.data( "ui-menu-submenu-caret" ) ) { + elem.remove(); + } + }); }, _keydown: function( event ) { @@ -286,16 +280,15 @@ return $.widget( "ui.menu", { }, refresh: function() { - var menus, items, + var menus, items, newSubmenus, newItems, newWrappers, that = this, icon = this.options.icons.submenu, submenus = this.element.find( this.options.menus ); - this.element.toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length ); + this._toggleClass( "ui-menu-icons", null, !!this.element.find( ".ui-icon" ).length ); // Initialize nested menus - submenus.filter( ":not(.ui-menu)" ) - .addClass( "ui-menu ui-widget ui-widget-content ui-front" ) + newSubmenus = submenus.filter( ":not(.ui-menu)" ) .hide() .attr({ role: this.options.role, @@ -305,16 +298,17 @@ return $.widget( "ui.menu", { .each(function() { var menu = $( this ), item = menu.prev(), - submenuCaret = $( "<span>" ) - .addClass( "ui-menu-icon ui-icon " + icon ) - .data( "ui-menu-submenu-caret", true ); + submenuCaret = $( "<span>" ).data( "ui-menu-submenu-caret", true ); + that._addClass( submenuCaret, "ui-menu-icon", "ui-icon " + icon ); item .attr( "aria-haspopup", "true" ) .prepend( submenuCaret ); menu.attr( "aria-labelledby", item.attr( "id" ) ); }); + this._addClass( newSubmenus, "ui-menu", "ui-widget ui-widget-content ui-front" ); + menus = submenus.add( this.element ); items = menus.find( this.options.items ); @@ -322,21 +316,21 @@ return $.widget( "ui.menu", { items.not( ".ui-menu-item" ).each(function() { var item = $( this ); if ( that._isDivider( item ) ) { - item.addClass( "ui-widget-content ui-menu-divider" ); + that._addClass( item, "ui-menu-divider", "ui-widget-content" ); } }); // Don't refresh list items that are already adapted - items.not( ".ui-menu-item, .ui-menu-divider" ) - .addClass( "ui-menu-item" ) - .children() - .not( ".ui-menu" ) - .addClass( "ui-menu-item-wrapper" ) - .uniqueId() - .attr({ - tabIndex: -1, - role: this._itemRole() - }); + newItems = items.not( ".ui-menu-item, .ui-menu-divider" ); + newWrappers = newItems.children() + .not( ".ui-menu" ) + .uniqueId() + .attr({ + tabIndex: -1, + role: this._itemRole() + }); + this._addClass( newItems, "ui-menu-item" ) + ._addClass( newWrappers, "ui-menu-item-wrapper" ); // Add aria-disabled attribute to any disabled menu item items.filter( ".ui-state-disabled" ).attr( "aria-disabled", "true" ); @@ -356,26 +350,27 @@ return $.widget( "ui.menu", { _setOption: function( key, value ) { if ( key === "icons" ) { - this.element.find( ".ui-menu-icon" ) - .removeClass( this.options.icons.submenu ) - .addClass( value.submenu ); + var icons = this.element.find( ".ui-menu-icon" ); + this._removeClass( icons, null, this.options.icons.submenu ) + ._addClass( icons, null, value.submenu ); } if ( key === "disabled" ) { - this.element - .toggleClass( "ui-state-disabled", !!value ) - .attr( "aria-disabled", value ); + this.element.attr( "aria-disabled", value ); + this._toggleClass( null, "ui-state-disabled", !!value ); } this._super( key, value ); }, focus: function( event, item ) { - var nested, focused; + var nested, focused, activeParent; this.blur( event, event && event.type === "focus" ); this._scrollIntoView( item ); this.active = item.first(); - focused = this.active.children( ".ui-menu-item-wrapper" ).addClass( "ui-state-active" ); + + focused = this.active.children( ".ui-menu-item-wrapper" ); + this._addClass( focused, null, "ui-state-active" ); // Only update aria-activedescendant if there's a role // otherwise we assume focus is managed elsewhere @@ -384,11 +379,11 @@ return $.widget( "ui.menu", { } // Highlight active parent menu item, if any - this.active + activeParent = this.active .parent() .closest( ".ui-menu-item" ) - .children( ".ui-menu-item-wrapper" ) - .addClass( "ui-state-active" ); + .children( ".ui-menu-item-wrapper" ); + this._addClass( activeParent, null, "ui-state-active" ); if ( event && event.type === "keydown" ) { this._close(); @@ -434,7 +429,8 @@ return $.widget( "ui.menu", { return; } - this.active.children( ".ui-menu-item-wrapper" ).removeClass( "ui-state-active" ); + this._removeClass( this.active.children( ".ui-menu-item-wrapper" ), + null, "ui-state-active" ); this.active = null; this._trigger( "blur", event, { item: this.active } ); @@ -498,14 +494,14 @@ return $.widget( "ui.menu", { startMenu = this.active ? this.active.parent() : this.element; } - startMenu + var active = startMenu .find( ".ui-menu" ) .hide() .attr( "aria-hidden", "true" ) .attr( "aria-expanded", "false" ) .end() - .find( ".ui-state-active" ).not( ".ui-menu-item-wrapper" ) - .removeClass( "ui-state-active" ); + .find( ".ui-state-active" ).not( ".ui-menu-item-wrapper" ); + this._removeClass( active, null, "ui-state-active" ); }, _closeOnDocumentClick: function( event ) { |