diff --git a/tests/unit/menu/menu.html b/tests/unit/menu/menu.html index 4970df864..49e33e7b1 100644 --- a/tests/unit/menu/menu.html +++ b/tests/unit/menu/menu.html @@ -31,7 +31,7 @@ $("#log").data("lastItem",item); $('li:eq(' + item + ') a',menu).trigger("click"); } - + @@ -41,10 +41,12 @@ - + @@ -66,6 +68,159 @@
  • Addyston
  • Adelphi
  • + + + + + + +
    diff --git a/tests/unit/menu/menu_events.js b/tests/unit/menu/menu_events.js index 2f713acf8..b949c827d 100644 --- a/tests/unit/menu/menu_events.js +++ b/tests/unit/menu/menu_events.js @@ -42,4 +42,309 @@ test( "handle blur: click", function() { $("#remove").remove(); }); +test("handle keyboard navigation on menu without scroll and without submenus", function() { + expect(12); + var element = $('#menu1').menu({ + select: function(event, ui) { + log($(ui.item[0]).text()); + }, + focus: function( event, ui ) { + log($(event.target).find(".ui-state-focus").parent().index()); + } + }); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + equals( $("#log").html(), "1,0,keydown,", "Keydown DOWN"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + equals( $("#log").html(), "0,keydown,", "Keydown UP"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + equals( $("#log").html(), "keydown,", "Keydown LEFT (no effect)"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + equals( $("#log").html(), "keydown,", "Keydown RIGHT (no effect)"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + equals( $("#log").html(), "4,keydown,", "Keydown PAGE_DOWN"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + equals( $("#log").html(), "keydown,", "Keydown PAGE_DOWN (no effect)"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + equals( $("#log").html(), "0,keydown,", "Keydown PAGE_UP"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + equals( $("#log").html(), "keydown,", "Keydown PAGE_UP (no effect)"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.END } ); + equals( $("#log").html(), "4,keydown,", "Keydown END"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.HOME } ); + equals( $("#log").html(), "0,keydown,", "Keydown HOME"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + equals( $("#log").html(), "keydown,", "Keydown ESCAPE (no effect)"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + equals( $("#log").html(), "Aberdeen,keydown,", "Keydown ENTER"); +}); + +asyncTest("handle keyboard navigation on menu without scroll and with submenus", function() { + expect(14); + var element = $('#menu2').menu({ + select: function(event, ui) { + log($(ui.item[0]).text()); + }, + focus: function( event, ui ) { + log($(event.target).find(".ui-state-focus").parent().index()); + } + }); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + equals( $("#log").html(), "1,0,keydown,", "Keydown DOWN"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + equals( $("#log").html(), "0,keydown,", "Keydown UP"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + equals( $("#log").html(), "keydown,", "Keydown LEFT (no effect)"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + + setTimeout( function() { + equals( $("#log").html(), "0,4,3,2,1,keydown,", "Keydown RIGHT (open submenu)"); + }, 50); + + setTimeout( function() { + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + equals( $("#log").html(), "4,keydown,", "Keydown LEFT (close submenu)"); + + //re-open submenu + element.simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + + setTimeout( function() { + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + equals( $("#log").html(), "2,keydown,", "Keydown PAGE_DOWN"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + equals( $("#log").html(), "keydown,", "Keydown PAGE_DOWN (no effect)"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + equals( $("#log").html(), "0,keydown,", "Keydown PAGE_UP"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + equals( $("#log").html(), "keydown,", "Keydown PAGE_UP (no effect)"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.END } ); + equals( $("#log").html(), "2,keydown,", "Keydown END"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.HOME } ); + equals( $("#log").html(), "0,keydown,", "Keydown HOME"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + equals( $("#log").html(), "4,keydown,", "Keydown ESCAPE (close submenu)"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + + setTimeout( function() { + equals( $("#log").html(), "0,keydown,", "Keydown ENTER (open submenu)"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + equals( $("#log").html(), "Ada,keydown,", "Keydown ENTER (select item)"); + + start(); + }, 200); + }, 150); + }, 100); + +}); + +test("handle keyboard navigation on menu with scroll and without submenus", function() { + expect(14); + var element = $('#menu3').menu({ + select: function(event, ui) { + log($(ui.item[0]).text()); + }, + focus: function( event, ui ) { + log($(event.target).find(".ui-state-focus").parent().index()); + } + }); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + equals( $("#log").html(), "1,0,keydown,", "Keydown DOWN"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + equals( $("#log").html(), "0,keydown,", "Keydown UP"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + equals( $("#log").html(), "keydown,", "Keydown LEFT (no effect)"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + equals( $("#log").html(), "keydown,", "Keydown RIGHT (no effect)"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + equals( $("#log").html(), "10,keydown,", "Keydown PAGE_DOWN"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + equals( $("#log").html(), "20,keydown,", "Keydown PAGE_DOWN"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + equals( $("#log").html(), "10,keydown,", "Keydown PAGE_UP"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + equals( $("#log").html(), "0,keydown,", "Keydown PAGE_UP"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + equals( $("#log").html(), "keydown,", "Keydown PAGE_UP (no effect)"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.END } ); + equals( $("#log").html(), "37,keydown,", "Keydown END"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + equals( $("#log").html(), "keydown,", "Keydown PAGE_DOWN (no effect)"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.HOME } ); + equals( $("#log").html(), "0,keydown,", "Keydown HOME"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + equals( $("#log").html(), "keydown,", "Keydown ESCAPE (no effect)"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + equals( $("#log").html(), "Aberdeen,keydown,", "Keydown ENTER"); +}); + +asyncTest("handle keyboard navigation on menu with scroll and with submenus", function() { + expect(14); + var element = $('#menu4').menu({ + select: function(event, ui) { + log($(ui.item[0]).text()); + }, + focus: function( event, ui ) { + log($(event.target).find(".ui-state-focus").parent().index()); + } + }); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + equals( $("#log").html(), "1,0,keydown,", "Keydown DOWN"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + equals( $("#log").html(), "0,keydown,", "Keydown UP"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + equals( $("#log").html(), "keydown,", "Keydown LEFT (no effect)"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + + setTimeout( function() { + equals( $("#log").html(), "0,1,keydown,", "Keydown RIGHT (open submenu)"); + }, 50); + + setTimeout( function() { + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + equals( $("#log").html(), "1,keydown,", "Keydown LEFT (close submenu)"); + + //re-open submenu + element.simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ); + + setTimeout( function() { + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + equals( $("#log").html(), "10,keydown,", "Keydown PAGE_DOWN"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + equals( $("#log").html(), "20,keydown,", "Keydown PAGE_DOWN"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + equals( $("#log").html(), "10,keydown,", "Keydown PAGE_UP"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + equals( $("#log").html(), "0,keydown,", "Keydown PAGE_UP"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.END } ); + equals( $("#log").html(), "27,keydown,", "Keydown END"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.HOME } ); + equals( $("#log").html(), "0,keydown,", "Keydown HOME"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + equals( $("#log").html(), "1,keydown,", "Keydown ESCAPE (close submenu)"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + + setTimeout( function() { + equals( $("#log").html(), "0,keydown,", "Keydown ENTER (open submenu)"); + + log("keydown",true); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + equals( $("#log").html(), "Aberdeen,keydown,", "Keydown ENTER (select item)"); + + start(); + }, 200); + }, 150); + }, 100); + +}); + })(jQuery); diff --git a/ui/jquery.ui.menu.js b/ui/jquery.ui.menu.js index 61efae7aa..ad7a9c8c1 100644 --- a/ui/jquery.ui.menu.js +++ b/ui/jquery.ui.menu.js @@ -89,6 +89,16 @@ $.widget( "ui.menu", { event.preventDefault(); event.stopImmediatePropagation(); break; + case $.ui.keyCode.HOME: + self._move( "first", "first", event ); + event.preventDefault(); + event.stopImmediatePropagation(); + break; + case $.ui.keyCode.END: + self._move( "last", "last", event ); + event.preventDefault(); + event.stopImmediatePropagation(); + break; case $.ui.keyCode.UP: self.previous( event ); event.preventDefault(); @@ -252,17 +262,17 @@ $.widget( "ui.menu", { this.blur( event ); if ( this._hasScroll() ) { - var borderTop = parseFloat( $.curCSS( this.element[0], "borderTopWidth", true ) ) || 0, - paddingTop = parseFloat( $.curCSS( this.element[0], "paddingTop", true ) ) || 0, - offset = item.offset().top - this.element.offset().top - borderTop - paddingTop, - scroll = this.element.scrollTop(), - elementHeight = this.element.height(), + var borderTop = parseFloat( $.curCSS( this.activeMenu[0], "borderTopWidth", true ) ) || 0, + paddingTop = parseFloat( $.curCSS( this.activeMenu[0], "paddingTop", true ) ) || 0, + offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop, + scroll = this.activeMenu.scrollTop(), + elementHeight = this.activeMenu.height(), itemHeight = item.height(); if ( offset < 0 ) { - this.element.scrollTop( scroll + offset ); + this.activeMenu.scrollTop( scroll + offset ); } else if ( offset + itemHeight > elementHeight ) { - this.element.scrollTop( scroll + offset - elementHeight + itemHeight ); + this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight ); } } @@ -391,11 +401,11 @@ $.widget( "ui.menu", { }, next: function(event) { - this._move( "next", ".ui-menu-item", "first", event ); + this._move( "next", "first", event ); }, previous: function(event) { - this._move( "prev", ".ui-menu-item", "last", event ); + this._move( "prev", "last", event ); }, first: function() { @@ -406,25 +416,36 @@ $.widget( "ui.menu", { return this.active && !this.active.nextAll( ".ui-menu-item" ).length; }, - _move: function( direction, edge, filter, event ) { + _move: function( direction, filter, event ) { if ( !this.active ) { - this.focus( event, this.activeMenu.children( edge )[ filter ]() ); + this.focus( event, this.activeMenu.children( ".ui-menu-item" )[ filter ]() ); return; } - var next = this.active[ direction + "All" ]( ".ui-menu-item" ).eq( 0 ); + + var next; + if ( direction === "first" || direction === "last" ) { + next = this.active[ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" ).eq( -1 ); + } else { + next = this.active[ direction + "All" ]( ".ui-menu-item" ).eq( 0 ); + } + if ( next.length ) { this.focus( event, next ); } else { - this.focus( event, this.activeMenu.children( edge )[ filter ]() ); + this.focus( event, this.activeMenu.children( ".ui-menu-item" )[ filter ]() ); } }, nextPage: function( event ) { if ( this._hasScroll() ) { - if ( !this.active || this.last() ) { + if ( !this.active ) { this.focus( event, this.activeMenu.children( ".ui-menu-item" ).first() ); return; } + if ( this.last() ) { + return; + } + var base = this.active.offset().top, height = this.element.height(), result; @@ -436,14 +457,17 @@ $.widget( "ui.menu", { this.focus( event, result ); } else { this.focus( event, this.activeMenu.children( ".ui-menu-item" ) - [ !this.active || this.last() ? "first" : "last" ]() ); + [ !this.active ? "first" : "last" ]() ); } }, previousPage: function( event ) { if ( this._hasScroll() ) { - if ( !this.active || this.first() ) { - this.focus( event, this.activeMenu.children( ".ui-menu-item" ).last() ); + if ( !this.active ) { + this.focus( event, this.activeMenu.children( ".ui-menu-item" ).first() ); + return; + } + if ( this.first() ) { return; } @@ -457,8 +481,7 @@ $.widget( "ui.menu", { this.focus( event, result ); } else { - this.focus( event, this.activeMenu.children( ".ui-menu-item" ) - [ !this.active || this.first() ? ":last" : ":first" ]() ); + this.focus( event, this.activeMenu.children( ".ui-menu-item" ).first() ); } },