From: kborchers Date: Mon, 25 Jul 2011 14:13:13 +0000 (-0500) Subject: Menu: Added Home moves to first item and End moves to last item in currently active... X-Git-Tag: 1.9m6~101^2 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=23340f1162cbe64c2dc58e97130b20c3f3562192;p=jquery-ui.git Menu: Added Home moves to first item and End moves to last item in currently active menu or submenu. Also fixed PageUp and PageDown so that they don't wrap back around to other end of menu. Also fixed scrolling to use activeMenu rather than element to all. Also added unit tests for keyboard nav --- diff --git a/tests/unit/menu/menu.html b/tests/unit/menu/menu.html index c917f5c4b..ec2c82e6b 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 4717946f1..66f874290 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() ); } },