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 () {
defaultElement: "<ul>",
delay: 300,
options: {
+ classes: {},
icons: {
submenu: "ui-icon-caret-1-e"
},
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 ) {
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",
},
_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" )
.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 ) {
},
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,
.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 );
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" );
_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
}
// 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();
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 } );
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 ) {