From 96e5c241e1b26224c53738b590e07290db7a3e54 Mon Sep 17 00:00:00 2001 From: Corey Frang Date: Fri, 19 Aug 2011 06:03:59 -0500 Subject: Dialog: Tabbing out of a modal dialog was possible because keypress doesn't fire for tabs everywhere, switched to keyup. Added Unit Test - Caught by @DomenicDenicola - Fixes #3123 - Tabbing stops in modal dialog --- tests/unit/dialog/dialog_tickets.js | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/unit/dialog/dialog_tickets.js b/tests/unit/dialog/dialog_tickets.js index 1cfdcefea..e65f939e0 100644 --- a/tests/unit/dialog/dialog_tickets.js +++ b/tests/unit/dialog/dialog_tickets.js @@ -3,7 +3,36 @@ */ (function($) { -module("dialog: tickets"); +module( "dialog: tickets" ); + +asyncTest( "#3123: Prevent tabbing out of modal dialogs", function() { + expect( 3 ); + + var el = $( "
" ).dialog({ modal: true }), + inputs = el.find( "input" ), + widget = el.dialog( "widget" ); + + inputs.eq( 1 ).focus(); + equal( document.activeElement, inputs[1], "Focus set on second input" ); + inputs.eq( 1 ).simulate( "keyup", { keyCode: $.ui.keyCode.TAB }); + + setTimeout( checkTab, 2 ); + + function checkTab() { + ok( $.contains( widget, document.activeElement ), "Tab key event moved focus within the modal" ); + + // check shift tab + $( document.activeElement ).simulate( "keydown", { keyCode: $.ui.keyCode.TAB, shiftKey: true }); + setTimeout( checkShiftTab, 2 ); + } + + function checkShiftTab() { + ok( $.contains( widget, document.activeElement ), "Shift-Tab key event moved focus within the modal" ); + + el.remove(); + start(); + } +}); test("#4826: setting resizable false toggles resizable on dialog", function() { expect(6); -- cgit v1.2.3 From 96c2c8e639abc1894cdb1588982219f31634a164 Mon Sep 17 00:00:00 2001 From: kborchers Date: Fri, 19 Aug 2011 20:19:38 -0500 Subject: Position: Added the missing div required for the fraction tests that were added to fix the broken test --- tests/unit/position/position_deprecated.html | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'tests') diff --git a/tests/unit/position/position_deprecated.html b/tests/unit/position/position_deprecated.html index 16d88ea2e..c80490f74 100644 --- a/tests/unit/position/position_deprecated.html +++ b/tests/unit/position/position_deprecated.html @@ -55,5 +55,9 @@ elements smaller than 10px have a line-height set on them to avoid a bug in IE6
+
+
+
+ -- cgit v1.2.3 From dfe75e1b552013c440186645a7a1ae9ec3c757b5 Mon Sep 17 00:00:00 2001 From: Corey Frang Date: Sat, 20 Aug 2011 18:05:39 -0500 Subject: Dialog: Update to 96e5c24 - keyup apparently doesn't work like I thought it would everywhere, switching back to keydown. --- tests/unit/dialog/dialog_tickets.js | 2 +- ui/jquery.ui.dialog.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/unit/dialog/dialog_tickets.js b/tests/unit/dialog/dialog_tickets.js index e65f939e0..b203ca7d5 100644 --- a/tests/unit/dialog/dialog_tickets.js +++ b/tests/unit/dialog/dialog_tickets.js @@ -14,7 +14,7 @@ asyncTest( "#3123: Prevent tabbing out of modal dialogs", function() { inputs.eq( 1 ).focus(); equal( document.activeElement, inputs[1], "Focus set on second input" ); - inputs.eq( 1 ).simulate( "keyup", { keyCode: $.ui.keyCode.TAB }); + inputs.eq( 1 ).simulate( "keydown", { keyCode: $.ui.keyCode.TAB }); setTimeout( checkTab, 2 ); diff --git a/ui/jquery.ui.dialog.js b/ui/jquery.ui.dialog.js index e8107db7c..065d640fb 100644 --- a/ui/jquery.ui.dialog.js +++ b/ui/jquery.ui.dialog.js @@ -293,7 +293,7 @@ $.widget("ui.dialog", { // prevent tabbing out of modal dialogs if ( options.modal ) { - uiDialog.bind( "keyup.ui-dialog", function( event ) { + uiDialog.bind( "keydown.ui-dialog", function( event ) { if ( event.keyCode !== $.ui.keyCode.TAB ) { return; } -- cgit v1.2.3 From c5ba0535cf8fad222bf54249fb74339faafa9310 Mon Sep 17 00:00:00 2001 From: Scott González Date: Tue, 30 Aug 2011 20:45:48 -0400 Subject: Autocomplete: Fixed setting of valueMethod for textareas. Fixes #7674 - Autocomplete doesn't work with textareas. --- tests/unit/autocomplete/autocomplete.html | 1 + tests/unit/autocomplete/autocomplete_events.js | 173 ++++++++++--------------- ui/jquery.ui.autocomplete.js | 2 +- 3 files changed, 71 insertions(+), 105 deletions(-) (limited to 'tests') diff --git a/tests/unit/autocomplete/autocomplete.html b/tests/unit/autocomplete/autocomplete.html index 8dc86c621..e5987350b 100644 --- a/tests/unit/autocomplete/autocomplete.html +++ b/tests/unit/autocomplete/autocomplete.html @@ -39,6 +39,7 @@
+ diff --git a/tests/unit/autocomplete/autocomplete_events.js b/tests/unit/autocomplete/autocomplete_events.js index c6d42ddcc..6813cfa71 100644 --- a/tests/unit/autocomplete/autocomplete_events.js +++ b/tests/unit/autocomplete/autocomplete_events.js @@ -4,110 +4,75 @@ module( "autocomplete: events" ); var data = [ "Clojure", "COBOL", "ColdFusion", "Java", "JavaScript", "Scala", "Scheme" ]; -asyncTest( "all events", function() { - expect( 13 ); - var element = $( "#autocomplete" ) - .autocomplete({ - autoFocus: false, - delay: 0, - source: data, - search: function( event ) { - equal( event.originalEvent.type, "keydown", "search originalEvent" ); - }, - response: function( event, ui ) { - deepEqual( ui.content, [ - { label: "Clojure", value: "Clojure" }, - { label: "Java", value: "Java" }, - { label: "JavaScript", value: "JavaScript" } - ], "response ui.content" ); - ui.content.splice( 0, 1 ); - }, - open: function( event ) { - ok( menu.is( ":visible" ), "menu open on open" ); - }, - focus: function( event, ui ) { - equal( event.originalEvent.type, "menufocus", "focus originalEvent" ); - deepEqual( ui.item, { label: "Java", value: "Java" }, "focus ui.item" ); - }, - close: function( event ) { - equal( event.originalEvent.type, "menuselect", "close originalEvent" ); - ok( menu.is( ":hidden" ), "menu closed on close" ); - }, - select: function( event, ui ) { - equal( event.originalEvent.type, "menuselect", "select originalEvent" ); - deepEqual( ui.item, { label: "Java", value: "Java" }, "select ui.item" ); - }, - change: function( event, ui ) { - equal( event.originalEvent.type, "blur", "change originalEvent" ); - deepEqual( ui.item, { label: "Java", value: "Java" }, "chnage ui.item" ); - ok( menu.is( ":hidden" ), "menu closed on change" ); - start(); - } - }), - menu = element.autocomplete( "widget" ); - - element.focus().val( "j" ).keydown(); - setTimeout(function() { - ok( menu.is( ":visible" ), "menu is visible after delay" ); - element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); - element.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - // blurring through jQuery causes a bug in IE 6 which causes the - // autocompletechange event to occur twice - element[0].blur(); - }, 50 ); -}); - -asyncTest( "all events - contenteditable", function() { - expect( 13 ); - var element = $( "#autocomplete-contenteditable" ) - .autocomplete({ - autoFocus: false, - delay: 0, - source: data, - search: function( event ) { - equal( event.originalEvent.type, "keydown", "search originalEvent" ); - }, - response: function( event, ui ) { - deepEqual( ui.content, [ - { label: "Clojure", value: "Clojure" }, - { label: "Java", value: "Java" }, - { label: "JavaScript", value: "JavaScript" } - ], "response ui.content" ); - ui.content.splice( 0, 1 ); - }, - open: function( event ) { - ok( menu.is( ":visible" ), "menu open on open" ); - }, - focus: function( event, ui ) { - equal( event.originalEvent.type, "menufocus", "focus originalEvent" ); - deepEqual( ui.item, { label: "Java", value: "Java" }, "focus ui.item" ); - }, - close: function( event ) { - equal( event.originalEvent.type, "menuselect", "close originalEvent" ); - ok( menu.is( ":hidden" ), "menu closed on close" ); - }, - select: function( event, ui ) { - equal( event.originalEvent.type, "menuselect", "select originalEvent" ); - deepEqual( ui.item, { label: "Java", value: "Java" }, "select ui.item" ); - }, - change: function( event, ui ) { - equal( event.originalEvent.type, "blur", "change originalEvent" ); - deepEqual( ui.item, { label: "Java", value: "Java" }, "chnage ui.item" ); - ok( menu.is( ":hidden" ), "menu closed on change" ); - start(); - } - }), - menu = element.autocomplete( "widget" ); - - element.focus().text( "j" ).keydown(); - setTimeout(function() { - ok( menu.is( ":visible" ), "menu is visible after delay" ); - element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); - element.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - // TODO: blurring through jQuery causes a bug in IE 6 which causes the - // autocompletechange event to occur twice - element[0].blur(); - }, 50 ); +$.each([ + { + type: "input", + selector: "#autocomplete", + valueMethod: "val" + }, + { + type: "textarea", + selector: "#autocomplete-textarea", + valueMethod: "val" + }, + { + type: "contenteditable", + selector: "#autocomplete-contenteditable", + valueMethod: "text" + } +], function( i, settings ) { + asyncTest( "all events - " + settings.type, function() { + expect( 13 ); + var element = $( settings.selector ) + .autocomplete({ + autoFocus: false, + delay: 0, + source: data, + search: function( event ) { + equal( event.originalEvent.type, "keydown", "search originalEvent" ); + }, + response: function( event, ui ) { + deepEqual( ui.content, [ + { label: "Clojure", value: "Clojure" }, + { label: "Java", value: "Java" }, + { label: "JavaScript", value: "JavaScript" } + ], "response ui.content" ); + ui.content.splice( 0, 1 ); + }, + open: function( event ) { + ok( menu.is( ":visible" ), "menu open on open" ); + }, + focus: function( event, ui ) { + equal( event.originalEvent.type, "menufocus", "focus originalEvent" ); + deepEqual( ui.item, { label: "Java", value: "Java" }, "focus ui.item" ); + }, + close: function( event ) { + equal( event.originalEvent.type, "menuselect", "close originalEvent" ); + ok( menu.is( ":hidden" ), "menu closed on close" ); + }, + select: function( event, ui ) { + equal( event.originalEvent.type, "menuselect", "select originalEvent" ); + deepEqual( ui.item, { label: "Java", value: "Java" }, "select ui.item" ); + }, + change: function( event, ui ) { + equal( event.originalEvent.type, "blur", "change originalEvent" ); + deepEqual( ui.item, { label: "Java", value: "Java" }, "chnage ui.item" ); + ok( menu.is( ":hidden" ), "menu closed on change" ); + start(); + } + }), + menu = element.autocomplete( "widget" ); + + element.focus()[ settings.valueMethod ]( "j" ).keydown(); + setTimeout(function() { + ok( menu.is( ":visible" ), "menu is visible after delay" ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + element.simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + // blurring through jQuery causes a bug in IE 6 which causes the + // autocompletechange event to occur twice + element[0].blur(); + }, 50 ); + }); }); asyncTest( "change without selection", function() { diff --git a/ui/jquery.ui.autocomplete.js b/ui/jquery.ui.autocomplete.js index 3e0163682..b871715ba 100644 --- a/ui/jquery.ui.autocomplete.js +++ b/ui/jquery.ui.autocomplete.js @@ -51,7 +51,7 @@ $.widget( "ui.autocomplete", { suppressKeyPress, suppressInput; - this.valueMethod = this.element[ this.element.is( "input" ) ? "val" : "text" ]; + this.valueMethod = this.element[ this.element.is( "input,textarea" ) ? "val" : "text" ]; this.element .addClass( "ui-autocomplete-input" ) -- cgit v1.2.3 From 4387d19030820077ac408e373bc135807e6a6002 Mon Sep 17 00:00:00 2001 From: Scott González Date: Fri, 9 Sep 2011 19:24:10 -0400 Subject: Spinner: Default min and max options to null. --- tests/unit/spinner/spinner_core.js | 20 +++++++++++++------- tests/unit/spinner/spinner_defaults.js | 4 ++-- ui/jquery.ui.spinner.js | 8 ++++---- 3 files changed, 19 insertions(+), 13 deletions(-) (limited to 'tests') diff --git a/tests/unit/spinner/spinner_core.js b/tests/unit/spinner/spinner_core.js index 57b6b3320..34a79bb16 100644 --- a/tests/unit/spinner/spinner_core.js +++ b/tests/unit/spinner/spinner_core.js @@ -149,22 +149,28 @@ test( "reading HTML5 attributes", function() { }); test( "ARIA attributes", function() { - expect( 7 ); + expect( 9 ); var element = $( "#spin" ).val( 2 ).spinner({ min: -5, max: 5 }); equal( element.attr( "role" ), "spinbutton", "role" ); - equal( element.attr( "aria-valuemin" ), -5, "aria-valuemin" ); - equal( element.attr( "aria-valuemax" ), 5, "aria-valuemax" ); - equal( element.attr( "aria-valuenow" ), 2, "aria-valuenow" ); + equal( element.attr( "aria-valuemin" ), "-5", "aria-valuemin" ); + equal( element.attr( "aria-valuemax" ), "5", "aria-valuemax" ); + equal( element.attr( "aria-valuenow" ), "2", "aria-valuenow" ); element.spinner( "stepUp" ); - equal( element.attr( "aria-valuenow" ), 3, "stepUp 1 step changes aria-valuenow" ); + equal( element.attr( "aria-valuenow" ), "3", "stepUp 1 step changes aria-valuenow" ); element.spinner( "option", { min: -10, max: 10 } ); - equal( element.attr( "aria-valuemin" ), -10, "min option changed aria-valuemin changes" ); - equal( element.attr( "aria-valuemax" ), 10, "max option changed aria-valuemax changes" ); + equal( element.attr( "aria-valuemin" ), "-10", "min option changed aria-valuemin changes" ); + equal( element.attr( "aria-valuemax" ), "10", "max option changed aria-valuemax changes" ); + + element.spinner( "option", "min", null ); + equal( element.attr( "aria-valuemin" ), undefined, "aria-valuemin not set when no min" ); + + element.spinner( "option", "max", null ); + equal( element.attr( "aria-valuemax" ), undefined, "aria-valuemax not set when no max" ); }); test( "focus text field when pressing button", function() { diff --git a/tests/unit/spinner/spinner_defaults.js b/tests/unit/spinner/spinner_defaults.js index f155a658e..3321f8733 100644 --- a/tests/unit/spinner/spinner_defaults.js +++ b/tests/unit/spinner/spinner_defaults.js @@ -2,8 +2,8 @@ commonWidgetTests( "spinner", { defaults: { disabled: false, incremental: true, - max: Number.MAX_VALUE, - min: -Number.MAX_VALUE, + max: null, + min: null, numberFormat: null, page: 10, step: 1, diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index a5c25cd34..97ee20e54 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -30,8 +30,8 @@ $.widget( "ui.spinner", { widgetEventPrefix: "spin", options: { incremental: true, - max: Number.MAX_VALUE, - min: -Number.MAX_VALUE, + max: null, + min: null, numberFormat: null, page: 10, step: 1, @@ -247,11 +247,11 @@ $.widget( "ui.spinner", { _trimValue: function( value ) { var options = this.options; - if ( value > options.max) { + if ( options.max != null && value > options.max) { return options.max; } - if ( value < options.min ) { + if ( options.min != null && value < options.min ) { return options.min; } -- cgit v1.2.3 From 7216c08b2fd9ad5e3cb90500f2b13bed6339f2ba Mon Sep 17 00:00:00 2001 From: Scott González Date: Fri, 9 Sep 2011 20:08:51 -0400 Subject: Spinner: Added ability to specify custom incremental function. --- tests/unit/spinner/spinner_options.js | 103 +++++++++++++++++++++------------- ui/jquery.ui.spinner.js | 12 +++- 2 files changed, 74 insertions(+), 41 deletions(-) (limited to 'tests') diff --git a/tests/unit/spinner/spinner_options.js b/tests/unit/spinner/spinner_options.js index 647455fe5..48bdf96a9 100644 --- a/tests/unit/spinner/spinner_options.js +++ b/tests/unit/spinner/spinner_options.js @@ -2,6 +2,71 @@ module( "spinner: options" ); +test( "incremental, false", function() { + expect( 100 ); + + var i, diff, + prev = 0, + element = $( "#spin" ).val( prev ).spinner({ + incremental: false, + spin: function( event, ui ) { + equal( ui.value - prev, 1 ); + prev = ui.value; + } + }); + + for ( i = 0; i < 100; i++ ) { + element.simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + } +}); + +test( "incremental, true", function() { + expect( 100 ); + + function fill( num, val ) { + return $.map( new Array( num ), function() { + return val; + }); + } + + var i, diff, + prev = 0, + expected = [].concat( fill( 18, 1 ), fill( 37, 2 ), fill( 14, 3 ), + fill( 9, 4 ), fill( 6, 5 ), fill( 5, 6 ), fill ( 5, 7 ), + fill( 4, 8 ), fill( 2, 9 ) ), + element = $( "#spin" ).val( prev ).spinner({ + incremental: true, + spin: function( event, ui ) { + equal( ui.value - prev, expected[ i ] ); + prev = ui.value; + } + }); + + for ( i = 0; i < 100; i++ ) { + element.simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + } +}); + +test( "incremental, function", function() { + expect( 100 ); + + var i, + prev = 0, + element = $( "#spin" ).val( prev ).spinner({ + incremental: function( i ) { + return i; + }, + spin: function( event, ui ) { + equal( ui.value - prev, i + 1 ); + prev = ui.value; + } + }); + + for ( i = 0; i < 100; i++ ) { + element.simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + } +}); + test( "numberFormat, number", function() { expect( 2 ); var element = $( "#spin" ).val( 0 ).spinner({ numberFormat: "n" }); @@ -26,44 +91,6 @@ test( "numberFormat, currency", function() { equal( element.val(), "$1.00", "formatted after step" ); }); -/* TODO figure out how to test this properly -test("incremental - false (default)", function() { - var el = $("#spin").spinner({ incremental:false }); - - for ( var i = 1 ; i<=120 ; i++ ) { - el.simulate("keydown",{keyCode:$.ui.keyCode.UP}); - } - el.simulate("keyup",{keyCode:$.ui.keyCode.UP}); - - equals(el.val(), 120, "incremental false - keydown 120 times"); - - for ( var i = 1 ; i<=210 ; i++ ) { - el.simulate("keydown",{keyCode:$.ui.keyCode.DOWN}); - } - el.simulate("keyup",{keyCode:$.ui.keyCode.DOWN}); - - equals(el.val(), -90, "incremental false - keydown 210 times"); -}); - -test("incremental - true (default)", function() { - var el = $("#spin").spinner(); - - for ( var i = 1 ; i<=120 ; i++ ) { - el.simulate("keydown",{keyCode:$.ui.keyCode.UP}); - } - el.simulate("keyup",{keyCode:$.ui.keyCode.UP}); - - equals(el.val(), 300, "incremental true - keydown 120 times (100+20*10)"); - - for ( var i = 1 ; i<=210 ; i++ ) { - el.simulate("keydown",{keyCode:$.ui.keyCode.DOWN}); - } - el.simulate("keyup",{keyCode:$.ui.keyCode.DOWN}); - - equals(el.val(), -1800, "incremental true - keydown 210 times (300-100-100*10-10*100)"); -}); -*/ - test( "max", function() { expect( 3 ); var element = $( "#spin" ).val( 1000 ).spinner({ max: 100 }); diff --git a/ui/jquery.ui.spinner.js b/ui/jquery.ui.spinner.js index 97ee20e54..a7fb30cc3 100644 --- a/ui/jquery.ui.spinner.js +++ b/ui/jquery.ui.spinner.js @@ -233,9 +233,15 @@ $.widget( "ui.spinner", { }, _increment: function( i ) { - return this.options.incremental ? - Math.floor( i*i*i/50000 - i*i/500 + 17*i/200 + 1 ) : - 1; + var incremental = this.options.incremental; + + if ( incremental ) { + return $.isFunction( incremental ) ? + incremental( i ) : + Math.floor( i*i*i/50000 - i*i/500 + 17*i/200 + 1 ); + } + + return 1; }, _precision: function( num ) { -- cgit v1.2.3 From 94317d7aa4ed439cb825d006fb461145a6d2aa5d Mon Sep 17 00:00:00 2001 From: kborchers Date: Mon, 12 Sep 2011 08:43:49 -0500 Subject: Menu: Added autoCollapse as the default and added a unit test --- tests/unit/menu/menu_events.js | 19 ++++++++++++++++++ ui/jquery.ui.menu.js | 45 +++++++++++++++++++++++++++--------------- 2 files changed, 48 insertions(+), 16 deletions(-) (limited to 'tests') diff --git a/tests/unit/menu/menu_events.js b/tests/unit/menu/menu_events.js index 19ac11c68..ab691b782 100644 --- a/tests/unit/menu/menu_events.js +++ b/tests/unit/menu/menu_events.js @@ -41,6 +41,25 @@ test( "handle blur: click", function() { $("#remove").remove(); }); +asyncTest( "handle submenu auto collapse: mouseleave", function() { + expect( 4 ); + var $menu = $( "#menu2" ).menu(); + + $menu.find( "li:nth-child(7)" ).trigger( "mouseover" ); + setTimeout(function() { + equal( $menu.find( "ul[aria-expanded='true']" ).length, 1, "first submenu expanded" ); + $menu.find( "li:nth-child(7) li:first" ).trigger( "mouseover" ); + setTimeout(function() { + equal( $menu.find( "ul[aria-expanded='true']" ).length, 2, "second submenu expanded" ); + $menu.find( "ul[aria-expanded='true']:first" ).trigger( "mouseleave" ); + equal( $menu.find( "ul[aria-expanded='true']" ).length, 1, "second submenu collapsed" ); + $menu.trigger( "mouseleave" ); + equal( $menu.find( "ul[aria-expanded='true']" ).length, 0, "first submenu collapsed" ); + start(); + }, 400); + }, 200); +}); + test("handle keyboard navigation on menu without scroll and without submenus", function() { expect(12); var element = $('#menu1').menu({ diff --git a/ui/jquery.ui.menu.js b/ui/jquery.ui.menu.js index 27e76d909..549eb5fae 100644 --- a/ui/jquery.ui.menu.js +++ b/ui/jquery.ui.menu.js @@ -62,6 +62,8 @@ $.widget( "ui.menu", { target.siblings().children( ".ui-state-active" ).removeClass( "ui-state-active" ); this.focus( event, target ); }, + "mouseleave": "_mouseleave", + "mouseleave .ui-menu": "_mouseleave", "mouseout .ui-menu-item": "blur", "focus": function( event ) { this.focus( event, $( event.target ).children( ".ui-menu-item:first" ) ); @@ -346,21 +348,30 @@ $.widget( "ui.menu", { }, collapseAll: function( event ) { - this.element - .find( "ul" ) - .hide() - .attr( "aria-hidden", "true" ) - .attr( "aria-expanded", "false" ) - .end() - .find( "a.ui-state-active" ) - .removeClass( "ui-state-active" ); + var currentMenu = false; + if ( event ) { + var target = $( event.target ); + if ( target.is( "ui.menu" ) ) { + currentMenu = target; + } else if ( target.closest( ".ui-menu" ).length ) { + currentMenu = target.closest( ".ui-menu" ); + } + } - this.blur( event ); - this.activeMenu = this.element; + this._close( currentMenu ); + + if( !currentMenu ) { + this.blur( event ); + this.activeMenu = this.element; + } }, - _close: function() { - this.active.parent() + _close: function( startMenu ) { + if( !startMenu ) { + startMenu = this.active ? this.active.parent() : this.element; + } + + startMenu .find( "ul" ) .hide() .attr( "aria-hidden", "true" ) @@ -373,10 +384,7 @@ $.widget( "ui.menu", { collapse: function( event ) { var newItem = this.active && this.active.parents("li:not(.ui-menubar-item)").first(); if ( newItem && newItem.length ) { - this.active.parent() - .attr("aria-hidden", "true") - .attr("aria-expanded", "false") - .hide(); + this._close(); this.focus( event, newItem ); return true; } @@ -486,6 +494,11 @@ $.widget( "ui.menu", { return this.element.height() < this.element.prop( "scrollHeight" ); }, + _mouseleave: function( event ) { + this.collapseAll( event ); + this.blur(); + }, + select: function( event ) { // save active reference before collapseAll triggers blur var ui = { -- cgit v1.2.3 From d12180d1a5ae5f07f112aaeebbfc295f2fe104d8 Mon Sep 17 00:00:00 2001 From: Jörn Zaefferer Date: Mon, 12 Sep 2011 23:23:54 +0200 Subject: Widget: Tests code cleanup --- tests/unit/widget/widget_core.js | 34 +++++++++++++++++----------------- tests/unit/widget/widget_extend.js | 5 ++--- 2 files changed, 19 insertions(+), 20 deletions(-) (limited to 'tests') diff --git a/tests/unit/widget/widget_core.js b/tests/unit/widget/widget_core.js index 41ae8ffb4..0c4142539 100644 --- a/tests/unit/widget/widget_core.js +++ b/tests/unit/widget/widget_core.js @@ -571,23 +571,23 @@ test( ".widget() - overriden", function() { test( "._bind() to element (default)", function() { expect( 12 ); - var self; + var that; $.widget( "ui.testWidget", { _create: function() { - self = this; + that = this; this._bind({ keyup: this.keyup, keydown: "keydown" }); }, keyup: function( event ) { - equals( self, this ); - equals( self.element[0], event.currentTarget ); + equals( that, this ); + equals( that.element[0], event.currentTarget ); equals( "keyup", event.type ); }, keydown: function( event ) { - equals( self, this ); - equals( self.element[0], event.currentTarget ); + equals( that, this ); + equals( that.element[0], event.currentTarget ); equals( "keydown", event.type ); } }); @@ -611,23 +611,23 @@ test( "._bind() to element (default)", function() { test( "._bind() to descendent", function() { expect( 12 ); - var self; + var that; $.widget( "ui.testWidget", { _create: function() { - self = this; + that = this; this._bind( this.element.find( "strong" ), { keyup: this.keyup, keydown: "keydown" }); }, keyup: function( event ) { - equals( self, this ); - equals( self.element.find( "strong" )[0], event.currentTarget ); + equals( that, this ); + equals( that.element.find( "strong" )[0], event.currentTarget ); equals( "keyup", event.type ); }, keydown: function(event) { - equals( self, this ); - equals( self.element.find( "strong" )[0], event.currentTarget ); + equals( that, this ); + equals( that.element.find( "strong" )[0], event.currentTarget ); equals( "keydown", event.type ); } }); @@ -987,31 +987,31 @@ test( "._trigger() - instance as element", function() { $( "#widget" ).testWidget().remove(); }); }); - + test( "auto-destroy - .remove() on parent", function() { shouldDestroy( true, function() { $( "#widget" ).testWidget().parent().remove(); }); }); - + test( "auto-destroy - .remove() on child", function() { shouldDestroy( false, function() { $( "#widget" ).testWidget().children().remove(); }); }); - + test( "auto-destroy - .empty()", function() { shouldDestroy( false, function() { $( "#widget" ).testWidget().empty(); }); }); - + test( "auto-destroy - .empty() on parent", function() { shouldDestroy( true, function() { $( "#widget" ).testWidget().parent().empty(); }); }); - + test( "auto-destroy - .detach()", function() { shouldDestroy( false, function() { $( "#widget" ).testWidget().detach(); diff --git a/tests/unit/widget/widget_extend.js b/tests/unit/widget/widget_extend.js index fb78ecfb7..90e686e18 100644 --- a/tests/unit/widget/widget_extend.js +++ b/tests/unit/widget/widget_extend.js @@ -6,7 +6,6 @@ test( "$.widget.extend()", function() { optionsCopy = { xnumber2: 1, xstring2: "x", xxx: "newstring" }, merged = { xnumber1: 5, xnumber2: 1, xstring1: "peter", xstring2: "x", xxx: "newstring" }, deep1 = { foo: { bar: true } }, - deep1copy = { foo: { bar: true } }, deep2 = { foo: { baz: true }, foo2: document }, deep2copy = { foo: { baz: true }, foo2: document }, deepmerged = { foo: { bar: true, baz: true }, foo2: document }, @@ -93,10 +92,10 @@ test( "$.widget.extend()", function() { deepEqual( defaults, defaultsCopy, "Check if not modified: options1 must not be modified" ); deepEqual( options1, options1Copy, "Check if not modified: options1 must not be modified" ); deepEqual( options2, options2Copy, "Check if not modified: options2 must not be modified" ); - + var input = { key: [ 1, 2, 3 ] - } + }; var output = $.widget.extend( {}, input ); deepEqual( input, output, "don't clone arrays" ); input.key[0] = 10; -- cgit v1.2.3 From 2a6ca3fb394f2caee6ad92c4dfc76ac66553cd46 Mon Sep 17 00:00:00 2001 From: Jörn Zaefferer Date: Mon, 12 Sep 2011 23:37:14 +0200 Subject: Widget: Add a _delay method. Will be used in various places to replace setTimeout with custom binding (mostly getting rid of var self/that) --- tests/unit/widget/widget_core.js | 24 ++++++++++++++++++++++++ ui/jquery.ui.widget.js | 9 +++++++++ 2 files changed, 33 insertions(+) (limited to 'tests') diff --git a/tests/unit/widget/widget_core.js b/tests/unit/widget/widget_core.js index 0c4142539..0b272a0d5 100644 --- a/tests/unit/widget/widget_core.js +++ b/tests/unit/widget/widget_core.js @@ -1040,4 +1040,28 @@ test( "redefine", function() { equal( $.ui.testWidget.foo, "bar", "static properties remain" ); }); +asyncTest( "_delay", function() { + expect( 4 ); + var order = 0, + that; + $.widget( "ui.testWidget", { + defaultElement: null, + _create: function() { + that = this; + this._delay(function() { + strictEqual( this, that ); + equal( order, 1 ); + start(); + }, 500); + this._delay("callback"); + }, + callback: function() { + strictEqual( this, that ); + equal( order, 0 ); + order += 1; + } + }); + $( "#widget" ).testWidget(); +}); + }( jQuery ) ); diff --git a/ui/jquery.ui.widget.js b/ui/jquery.ui.widget.js index 5b7942600..729e14cf9 100644 --- a/ui/jquery.ui.widget.js +++ b/ui/jquery.ui.widget.js @@ -333,6 +333,15 @@ $.Widget.prototype = { }); }, + _delay: function( handler, delay ) { + function handlerProxy() { + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + var instance = this; + setTimeout( handlerProxy, delay || 0 ); + }, + _hoverable: function( element ) { this.hoverable = this.hoverable.add( element ); this._bind( element, { -- cgit v1.2.3 From 3a0340f4ee2d343cdebeb398da10c2a71a948a9f Mon Sep 17 00:00:00 2001 From: Jörn Zaefferer Date: Mon, 12 Sep 2011 23:47:09 +0200 Subject: Widget: return timer value from _delay --- tests/unit/widget/widget_core.js | 8 +++++--- ui/jquery.ui.widget.js | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/unit/widget/widget_core.js b/tests/unit/widget/widget_core.js index 0b272a0d5..0510da4b4 100644 --- a/tests/unit/widget/widget_core.js +++ b/tests/unit/widget/widget_core.js @@ -1041,19 +1041,21 @@ test( "redefine", function() { }); asyncTest( "_delay", function() { - expect( 4 ); + expect( 6 ); var order = 0, that; $.widget( "ui.testWidget", { defaultElement: null, _create: function() { that = this; - this._delay(function() { + var timer = this._delay(function() { strictEqual( this, that ); equal( order, 1 ); start(); }, 500); - this._delay("callback"); + ok( timer !== undefined ); + timer = this._delay("callback"); + ok( timer !== undefined ); }, callback: function() { strictEqual( this, that ); diff --git a/ui/jquery.ui.widget.js b/ui/jquery.ui.widget.js index 55b6eda5d..31328a455 100644 --- a/ui/jquery.ui.widget.js +++ b/ui/jquery.ui.widget.js @@ -339,7 +339,7 @@ $.Widget.prototype = { .apply( instance, arguments ); } var instance = this; - setTimeout( handlerProxy, delay || 0 ); + return setTimeout( handlerProxy, delay || 0 ); }, _hoverable: function( element ) { -- cgit v1.2.3 From e1ec6f8ebb509b636840dfad11aa062c9877beac Mon Sep 17 00:00:00 2001 From: Jörn Zaefferer Date: Tue, 13 Sep 2011 00:24:43 +0200 Subject: Menu: Refactor to get rid of var that. Cleanup some odd formattings and unneeded temp vars --- tests/visual/menu/menu.html | 12 ++++++------ ui/jquery.ui.menu.js | 40 +++++++++++++++++++--------------------- 2 files changed, 25 insertions(+), 27 deletions(-) (limited to 'tests') diff --git a/tests/visual/menu/menu.html b/tests/visual/menu/menu.html index 5720d04d9..195488184 100644 --- a/tests/visual/menu/menu.html +++ b/tests/visual/menu/menu.html @@ -16,7 +16,7 @@ right: 10, top: 10 }).appendTo(document.body).themeswitcher(); - + function create() { menus.menu({ select: function(event, ui) { @@ -24,8 +24,8 @@ } }); } - - var menus = $("#menu1, #menu2, #menu3, #menu4"); + + var menus = $("#menu1, #menu2, #menu3, .menu4"); create(); $("#toggle-destroy").toggle(function() { @@ -41,11 +41,11 @@ - + - + + + +
Log:
@@ -182,4 +280,4 @@ - + \ No newline at end of file diff --git a/ui/jquery.ui.menu.js b/ui/jquery.ui.menu.js index 9cb6afe32..455a12184 100644 --- a/ui/jquery.ui.menu.js +++ b/ui/jquery.ui.menu.js @@ -20,6 +20,7 @@ $.widget( "ui.menu", { defaultElement: "
    ", delay: 150, options: { + items: "ul", position: { my: "left top", at: "right top" @@ -194,7 +195,7 @@ $.widget( "ui.menu", { //destroy (sub)menus this.element .removeAttr( "aria-activedescendant" ) - .find( "ul" ) + .find( ".ui-menu" ) .andSelf() .removeClass( "ui-menu ui-widget ui-widget-content ui-corner-all" ) .removeAttr( "role" ) @@ -221,7 +222,7 @@ $.widget( "ui.menu", { refresh: function() { // 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" ) .attr( "role", "menu" ) .hide() @@ -230,7 +231,7 @@ $.widget( "ui.menu", { // don't refresh list items that are already adapted 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" ) .attr( "role", "presentation" ) .children( "a" ) @@ -273,16 +274,16 @@ $.widget( "ui.menu", { .children( "a" ) .addClass( "ui-state-focus" ) .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 - 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._close(); }, this.delay ); - var nested = $( ">ul", item ); + var nested = $( "> .ui-menu", item ); if ( nested.length && ( /^mouse/.test( event.type ) ) ) { this._startOpening(nested); } @@ -353,19 +354,19 @@ $.widget( "ui.menu", { this._close( currentMenu ); - if( !currentMenu ) { + if ( !currentMenu ) { this.blur( event ); this.activeMenu = this.element; } }, _close: function( startMenu ) { - if( !startMenu ) { + if ( !startMenu ) { startMenu = this.active ? this.active.parent() : this.element; } startMenu - .find( "ul" ) + .find( ".ui-menu" ) .hide() .attr( "aria-hidden", "true" ) .attr( "aria-expanded", "false" ) @@ -375,7 +376,7 @@ $.widget( "ui.menu", { }, 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 ) { this._close(); this.focus( event, newItem ); @@ -384,7 +385,7 @@ $.widget( "ui.menu", { }, 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 ) { this._open( newItem.parent() ); -- cgit v1.2.3 From 34a0479d1cbb0acf5d86818506deec78bfbb373b Mon Sep 17 00:00:00 2001 From: Corey Frang Date: Thu, 22 Sep 2011 13:56:53 -0500 Subject: Menu: Refactoring the collapseAll to deal with some issues selecting - Updating unit tests. Thanks @kborchers --- tests/unit/menu/menu_events.js | 8 ++++++- tests/unit/menu/menu_test_helpers.js | 2 +- ui/jquery.ui.menu.js | 46 +++++++++++++++++++----------------- 3 files changed, 32 insertions(+), 24 deletions(-) (limited to 'tests') diff --git a/tests/unit/menu/menu_events.js b/tests/unit/menu/menu_events.js index 6d1b02b87..55ec1e2ff 100644 --- a/tests/unit/menu/menu_events.js +++ b/tests/unit/menu/menu_events.js @@ -99,7 +99,7 @@ asyncTest( "handle submenu auto collapse: mouseleave", function() { }); asyncTest( "handle custom menu item submenu auto collapse: mouseleave", function() { - expect( 4 ); + expect( 5 ); var $menu = $( "#menu5" ).menu( { items: "div" } ); $menu.children( ":nth-child(7)" ).trigger( "mouseover" ); @@ -110,6 +110,11 @@ asyncTest( "handle custom menu item submenu auto collapse: mouseleave", 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.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN }); + ok( $menu.find( ".ui-state-active" ).is( "#menu5 :nth-child(7) a" ), + "down keypress selected an item from the first submenu" ); + $menu.trigger( "mouseleave" ); equal( $menu.find( "div[aria-expanded='true']" ).length, 0, "first submenu collapsed" ); start(); @@ -117,6 +122,7 @@ asyncTest( "handle custom menu item submenu auto collapse: mouseleave", function }, 200); }); + test("handle keyboard navigation on menu without scroll and without submenus", function() { expect(12); var element = $('#menu1').menu({ diff --git a/tests/unit/menu/menu_test_helpers.js b/tests/unit/menu/menu_test_helpers.js index 052c9226b..e83795e5a 100644 --- a/tests/unit/menu/menu_test_helpers.js +++ b/tests/unit/menu/menu_test_helpers.js @@ -5,7 +5,7 @@ function menu_log( message, clear ) { if ( message === undefined ) { message = $( "#log" ).data( "lastItem" ); } - $( "#log" ).prepend( message + "," ); + $( "#log" ).prepend( $.trim( message ) + "," ); } function menu_click( menu, item ) { diff --git a/ui/jquery.ui.menu.js b/ui/jquery.ui.menu.js index 455a12184..6d9db8879 100644 --- a/ui/jquery.ui.menu.js +++ b/ui/jquery.ui.menu.js @@ -62,13 +62,19 @@ $.widget( "ui.menu", { target.siblings().children( ".ui-state-active" ).removeClass( "ui-state-active" ); this.focus( event, target ); }, - "mouseleave": "_mouseleave", - "mouseleave .ui-menu": "_mouseleave", + "mouseleave": "collapseAll", + "mouseleave .ui-menu": "collapseAll", "mouseout .ui-menu-item": "blur", "focus": function( event ) { this.focus( event, $( event.target ).children( ".ui-menu-item:first" ) ); }, - "blur": "collapseAll" + blur: function( event ) { + this._delay( function() { + if ( ! $.contains( this.element[0], document.activeElement ) ) { + this.collapseAll( event ); + } + }, 0); + } }); this.refresh(); @@ -341,25 +347,25 @@ $.widget( "ui.menu", { .position( position ); }, - collapseAll: function( event ) { - var currentMenu = false; - if ( event ) { - var target = $( event.target ); - if ( target.is( "ui.menu" ) ) { - currentMenu = target; - } else if ( target.closest( ".ui-menu" ).length ) { - currentMenu = target.closest( ".ui-menu" ); - } + collapseAll: function( event, all ) { + + // if we were passed an event, look for the submenu that contains the event + var currentMenu = all ? this.element : + $( event && event.target ).closest( this.element.find( ".ui-menu" ) ); + + // if we found no valid submenu ancestor, use the main menu to close all sub menus anyway + if ( !currentMenu.length ) { + currentMenu = this.element; } this._close( currentMenu ); - if ( !currentMenu ) { - this.blur( event ); - this.activeMenu = this.element; - } + this.blur( event ); + this.activeMenu = currentMenu; }, + // With no arguments, closes the currently active menu - if nothing is active + // it closes all menus. If passed an argument, it will search for menus BELOW _close: function( startMenu ) { if ( !startMenu ) { startMenu = this.active ? this.active.parent() : this.element; @@ -487,17 +493,13 @@ $.widget( "ui.menu", { return this.element.height() < this.element.prop( "scrollHeight" ); }, - _mouseleave: function( event ) { - this.collapseAll( event ); - this.blur(); - }, - select: function( event ) { + // save active reference before collapseAll triggers blur var ui = { item: this.active }; - this.collapseAll( event ); + this.collapseAll( event, true ); this._trigger( "select", event, ui ); } }); -- cgit v1.2.3