mirror of
https://github.com/jquery/jquery-ui.git
synced 2024-11-21 11:04:24 +00:00
parent
f58277a521
commit
2ebef69efe
@ -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>
|
||||
|
@ -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 () {
|
||||
|
118
ui/menu.js
118
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 ) {
|
||||
|
Loading…
Reference in New Issue
Block a user