Menu: Add support for structures other than UL/LI plus visual and unit tests

This commit is contained in:
kborchers 2011-09-14 10:29:36 -05:00
parent cedac75bcc
commit 9e617bbad1
6 changed files with 210 additions and 15 deletions

View File

@ -207,6 +207,44 @@
<li class="foo"><a class="foo" href="#">Amesville</a></li> <li class="foo"><a class="foo" href="#">Amesville</a></li>
</ul> </ul>
<div id="menu5">
<blockquote><a href="#">Aberdeen</a></blockquote>
<blockquote><a href="#">Ada</a></blockquote>
<blockquote><a href="#">Adamsville</a></blockquote>
<blockquote><a href="#">Addyston</a></blockquote>
<blockquote>
<a href="#">Delphi</a>
<div>
<blockquote><a href="#">Ada</a></blockquote>
<blockquote><a href="#">Saarland</a></blockquote>
<blockquote><a href="#">Salzburg</a></blockquote>
</div>
</blockquote>
<blockquote><a href="#">Saarland</a></blockquote>
<blockquote>
<a href="#">Salzburg</a>
<div>
<blockquote>
<a href="#">Delphi</a>
<div>
<blockquote><a href="#">Ada</a></blockquote>
<blockquote><a href="#">Saarland</a></blockquote>
<blockquote><a href="#">Salzburg</a></blockquote>
</div>
</blockquote>
<blockquote>
<a href="#">Delphi</a>
<div>
<blockquote><a href="#">Ada</a></blockquote>
<blockquote><a href="#">Saarland</a></blockquote>
<blockquote><a href="#">Salzburg</a></blockquote>
</div>
</blockquote>
<blockquote><a href="#">Perch</a></blockquote>
</div>
</blockquote>
</div>
<div id="log"></div> <div id="log"></div>
</div> </div>

View File

@ -5,6 +5,7 @@ commonWidgetTests( "menu", {
my: "left top", my: "left top",
at: "right top" at: "right top"
}, },
items: "ul",
// callbacks // callbacks
create: null create: null

View File

@ -21,6 +21,23 @@ test("handle click on menu", function() {
equals( $("#log").html(), "1,3,2,afterclick,1,click,", "Click order not valid."); equals( $("#log").html(), "1,3,2,afterclick,1,click,", "Click order not valid.");
}); });
test("handle click on custom item menu", function() {
expect(1);
var ac = $('#menu5').menu({
select: function(event, ui) {
menu_log();
},
items: "div"
});
menu_log("click",true);
menu_click($('#menu5'),"1");
menu_log("afterclick");
menu_click( ac,"2");
menu_click($('#menu5'),"3");
menu_click( ac,"1");
equals( $("#log").html(), "1,3,2,afterclick,1,click,", "Click order not valid.");
});
test( "handle blur: click", function() { test( "handle blur: click", function() {
expect( 4 ); expect( 4 );
var $menu = $( "#menu1" ).menu({ var $menu = $( "#menu1" ).menu({
@ -41,6 +58,27 @@ test( "handle blur: click", function() {
$("#remove").remove(); $("#remove").remove();
}); });
test( "handle blur on custom item menu: click", function() {
expect( 4 );
var $menu = $( "#menu5" ).menu({
focus: function( event, ui ) {
equal( event.originalEvent.type, "click", "focus triggered 'click'" );
equal( event.type, "menufocus", "focus event.type is 'menufocus'" );
},
blur: function( event, ui ) {
equal( event.originalEvent.type, "click", "blur triggered 'click'" );
equal( event.type, "menublur", "blur event.type is 'menublur'" );
},
items: "div"
});
menu_click($('#menu5'),"1");
$( "<a>", { id: "remove"} ).appendTo("body").trigger( "click" );
$("#remove").remove();
});
asyncTest( "handle submenu auto collapse: mouseleave", function() { asyncTest( "handle submenu auto collapse: mouseleave", function() {
expect( 4 ); expect( 4 );
var $menu = $( "#menu2" ).menu(); var $menu = $( "#menu2" ).menu();
@ -60,6 +98,25 @@ asyncTest( "handle submenu auto collapse: mouseleave", function() {
}, 200); }, 200);
}); });
asyncTest( "handle custom menu item submenu auto collapse: mouseleave", function() {
expect( 4 );
var $menu = $( "#menu5" ).menu( { items: "div" } );
$menu.children( ":nth-child(7)" ).trigger( "mouseover" );
setTimeout(function() {
equal( $menu.find( "div[aria-expanded='true']" ).length, 1, "first submenu expanded" );
$menu.children( ":nth-child(7)" ).find( "div:first" ).children( ":first" ).trigger( "mouseover" );
setTimeout(function() {
equal( $menu.find( "div[aria-expanded='true']" ).length, 2, "second submenu expanded" );
$menu.find( "div[aria-expanded='true']:first" ).trigger( "mouseleave" );
equal( $menu.find( "div[aria-expanded='true']" ).length, 1, "second submenu collapsed" );
$menu.trigger( "mouseleave" );
equal( $menu.find( "div[aria-expanded='true']" ).length, 0, "first submenu collapsed" );
start();
}, 400);
}, 200);
});
test("handle keyboard navigation on menu without scroll and without submenus", function() { test("handle keyboard navigation on menu without scroll and without submenus", function() {
expect(12); expect(12);
var element = $('#menu1').menu({ var element = $('#menu1').menu({

View File

@ -10,5 +10,5 @@ function menu_log( message, clear ) {
function menu_click( menu, item ) { function menu_click( menu, item ) {
$( "#log" ).data( "lastItem", item ); $( "#log" ).data( "lastItem", item );
menu.find( "li:eq(" + item + ") a" ).trigger( "click" ); menu.children( ":eq(" + item + ")" ).find( "a:first" ).trigger( "click" );
} }

View File

@ -23,6 +23,20 @@
$("<div/>").text("Selected: " + ui.item.text()).appendTo("#log"); $("<div/>").text("Selected: " + ui.item.text()).appendTo("#log");
} }
}); });
$("#menu5").menu({
select: function(event, ui) {
$("<div/>").text("Selected: " + ui.item.text()).appendTo("#log");
},
items: "div"
});
$("#menu6").menu({
select: function(event, ui) {
$("<div/>").text("Selected: " + ui.item.text()).appendTo("#log");
},
items: ".menuElement"
});
} }
var menus = $("#menu1, #menu2, #menu3, .menu4"); var menus = $("#menu1, #menu2, #menu3, .menu4");
@ -42,6 +56,9 @@
body { font-size:62.5%; } body { font-size:62.5%; }
.ui-menu { width: 200px; margin-bottom: 2em; } .ui-menu { width: 200px; margin-bottom: 2em; }
.menu4 { height: 200px; overflow: auto; } .menu4 { height: 200px; overflow: auto; }
.address-item { border-bottom: 1px solid #999; }
.address-header { display: block; margin-bottom: .2em; font-weight: bold; }
.address-content { display: block; margin-bottom: .2em; padding-left: 10px; }
</style> </style>
</head> </head>
<body> <body>
@ -173,6 +190,87 @@
<li><a href="#">Amesville</a></li> <li><a href="#">Amesville</a></li>
</ul> </ul>
<div id="menu5">
<blockquote><a href="#">Aberdeen</a></blockquote>
<blockquote><a href="#">Ada</a></blockquote>
<blockquote><a href="#">Adamsville</a></blockquote>
<blockquote><a href="#">Addyston</a></blockquote>
<blockquote>
<a href="#">Delphi</a>
<div>
<blockquote><a href="#">Ada</a></blockquote>
<blockquote><a href="#">Saarland</a></blockquote>
<blockquote><a href="#">Salzburg</a></blockquote>
</div>
</blockquote>
<blockquote><a href="#">Saarland</a></blockquote>
<blockquote>
<a href="#">Salzburg</a>
<div>
<blockquote>
<a href="#">Delphi</a>
<div>
<blockquote><a href="#">Ada</a></blockquote>
<blockquote><a href="#">Saarland</a></blockquote>
<blockquote><a href="#">Salzburg</a></blockquote>
</div>
</blockquote>
<blockquote>
<a href="#">Delphi</a>
<div>
<blockquote><a href="#">Ada</a></blockquote>
<blockquote><a href="#">Saarland</a></blockquote>
<blockquote><a href="#">Salzburg</a></blockquote>
</div>
</blockquote>
<blockquote><a href="#">Perch</a></blockquote>
</div>
</blockquote>
</div>
<div class="menuElement" id="menu6">
<div class="address-item">
<a href="#">
<span class="address-header">John Doe</span>
<span class="address-content">78 West Main St Apt 3A</span>
<span class="address-content">Bloomsburg, PA 12345</span>
</a>
</div>
<div class="address-item">
<a href="#">
<span class="address-header">Jane Doe</span>
<span class="address-content">78 West Main St Apt 3A</span>
<span class="address-content">Bloomsburg, PA 12345</span>
</a>
</div>
<div class="address-item">
<a href="#">
<span class="address-header">James Doe</span>
<span class="address-content">78 West Main St Apt 3A</span>
<span class="address-content">Bloomsburg, PA 12345</span>
</a>
</div>
<div class="address-item">
<a href="#">
<span class="address-header">Jenny Doe</span>
<span class="address-content">78 West Main St Apt 3A</span>
<span class="address-content">Bloomsburg, PA 12345</span>
</a>
</div>
<div class="address-item">
<a href="#">
<span class="address-header">John Doe</span>
<span class="address-content">78 West Main St Apt 3A</span>
<span class="address-content">Bloomsburg, PA 12345</span>
</a>
<div class="menuElement">
<div><a href="#">Ada</a></div>
<div><a href="#">Saarland</a></div>
<div><a href="#">Salzburg</a></div>
</div>
</div>
</div>
<div class="ui-widget" style="clear: left; margin-top:2em; font-family:Arial"> <div class="ui-widget" style="clear: left; margin-top:2em; font-family:Arial">
Log: Log:
<div id="log" style="height: 400px; width: 300px; overflow: auto;" class="ui-widget-content"></div> <div id="log" style="height: 400px; width: 300px; overflow: auto;" class="ui-widget-content"></div>

23
ui/jquery.ui.menu.js vendored
View File

@ -20,6 +20,7 @@ $.widget( "ui.menu", {
defaultElement: "<ul>", defaultElement: "<ul>",
delay: 150, delay: 150,
options: { options: {
items: "ul",
position: { position: {
my: "left top", my: "left top",
at: "right top" at: "right top"
@ -194,7 +195,7 @@ $.widget( "ui.menu", {
//destroy (sub)menus //destroy (sub)menus
this.element this.element
.removeAttr( "aria-activedescendant" ) .removeAttr( "aria-activedescendant" )
.find( "ul" ) .find( ".ui-menu" )
.andSelf() .andSelf()
.removeClass( "ui-menu ui-widget ui-widget-content ui-corner-all" ) .removeClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
.removeAttr( "role" ) .removeAttr( "role" )
@ -221,7 +222,7 @@ $.widget( "ui.menu", {
refresh: function() { refresh: function() {
// initialize nested menus // initialize nested menus
var submenus = this.element.find( "ul:not(.ui-menu)" ) var submenus = this.element.find( this.options.items + ":not( .ui-menu )" )
.addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" ) .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
.attr( "role", "menu" ) .attr( "role", "menu" )
.hide() .hide()
@ -230,7 +231,7 @@ $.widget( "ui.menu", {
// don't refresh list items that are already adapted // don't refresh list items that are already adapted
var menuId = this.menuId; var menuId = this.menuId;
submenus.add( this.element ).children( "li:not(.ui-menu-item):has(a)" ) submenus.add( this.element ).children( ":not( .ui-menu-item ):has( a )" )
.addClass( "ui-menu-item" ) .addClass( "ui-menu-item" )
.attr( "role", "presentation" ) .attr( "role", "presentation" )
.children( "a" ) .children( "a" )
@ -273,16 +274,16 @@ $.widget( "ui.menu", {
.children( "a" ) .children( "a" )
.addClass( "ui-state-focus" ) .addClass( "ui-state-focus" )
.end(); .end();
this.element.attr( "aria-activedescendant", this.active.children("a").attr("id") ); this.element.attr( "aria-activedescendant", this.active.children( "a" ).attr( "id" ) );
// highlight active parent menu item, if any // highlight active parent menu item, if any
this.active.parent().closest(".ui-menu-item").children("a:first").addClass("ui-state-active"); this.active.parent().closest( ".ui-menu-item" ).children( "a:first" ).addClass( "ui-state-active" );
this.timer = this._delay( function() { this.timer = this._delay( function() {
this._close(); this._close();
}, this.delay ); }, this.delay );
var nested = $( ">ul", item ); var nested = $( "> .ui-menu", item );
if ( nested.length && ( /^mouse/.test( event.type ) ) ) { if ( nested.length && ( /^mouse/.test( event.type ) ) ) {
this._startOpening(nested); this._startOpening(nested);
} }
@ -353,19 +354,19 @@ $.widget( "ui.menu", {
this._close( currentMenu ); this._close( currentMenu );
if( !currentMenu ) { if ( !currentMenu ) {
this.blur( event ); this.blur( event );
this.activeMenu = this.element; this.activeMenu = this.element;
} }
}, },
_close: function( startMenu ) { _close: function( startMenu ) {
if( !startMenu ) { if ( !startMenu ) {
startMenu = this.active ? this.active.parent() : this.element; startMenu = this.active ? this.active.parent() : this.element;
} }
startMenu startMenu
.find( "ul" ) .find( ".ui-menu" )
.hide() .hide()
.attr( "aria-hidden", "true" ) .attr( "aria-hidden", "true" )
.attr( "aria-expanded", "false" ) .attr( "aria-expanded", "false" )
@ -375,7 +376,7 @@ $.widget( "ui.menu", {
}, },
collapse: function( event ) { collapse: function( event ) {
var newItem = this.active && this.active.parents("li:not(.ui-menubar-item)").first(); var newItem = this.active && this.active.parent().closest( ".ui-menu-item", this.element );
if ( newItem && newItem.length ) { if ( newItem && newItem.length ) {
this._close(); this._close();
this.focus( event, newItem ); this.focus( event, newItem );
@ -384,7 +385,7 @@ $.widget( "ui.menu", {
}, },
expand: function( event ) { expand: function( event ) {
var newItem = this.active && this.active.children("ul").children("li").first(); var newItem = this.active && this.active.children( ".ui-menu " ).children( ".ui-menu-item" ).first();
if ( newItem && newItem.length ) { if ( newItem && newItem.length ) {
this._open( newItem.parent() ); this._open( newItem.parent() );