From 0f431a683968e9e9d30120eb3bf09a3f13da8814 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Fri, 16 May 2014 00:19:32 +0200 Subject: [PATCH 01/26] Datepicker: Introduce value option Change status caching, fix existing value related methods, introduce $.date construction with date object, selected property is null by default, add selected getter --- external/date.js | 7 + tests/unit/datepicker/datepicker_core.js | 477 ++++++++------------ tests/unit/datepicker/datepicker_methods.js | 47 +- ui/datepicker.js | 85 ++-- 4 files changed, 256 insertions(+), 360 deletions(-) diff --git a/external/date.js b/external/date.js index 06a67553205..8df3f63de92 100644 --- a/external/date.js +++ b/external/date.js @@ -26,9 +26,13 @@ $.date = function( date, globalFormat ) { if ( typeof date === "string" && date.length ) { this.dateObject = Globalize.parseDate( date, globalFormat ); } + if ( $.type( date ) === "date" ) { + this.dateObject = date; + } this.dateObject = this.dateObject || new Date(); this.globalFormat = globalFormat; + this.selected = null; }; $.date.prototype = { @@ -180,6 +184,9 @@ $.date.prototype = { this.selected = this.clone(); return this; }, + selectedDate: function() { + return this.selected.date(); + }, clone: function() { var date = this.dateObject; return new $.date( new Date( date.getFullYear(), date.getMonth(), diff --git a/tests/unit/datepicker/datepicker_core.js b/tests/unit/datepicker/datepicker_core.js index 189f03d932a..5031fb33bff 100644 --- a/tests/unit/datepicker/datepicker_core.js +++ b/tests/unit/datepicker/datepicker_core.js @@ -64,7 +64,7 @@ asyncTest( "baseStructure", function() { inp.datepicker( "close" ).datepicker( "destroy" ); step2(); - }); + }, 50 ); } function step2() { @@ -217,269 +217,223 @@ asyncTest( "baseStructure", function() { step1(); }); -test( "Keyboard handling", function() { +asyncTest( "Keyboard handling: input", function() { expect( 9 ); var input = $( "#datepicker" ).datepicker(), - instance = input.datepicker( "instance" ), - date = new Date(); - - input.datepicker( "open" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke enter" ); - - // Enter = Select today's date by default - input.val( "1/1/14" ).datepicker( "open" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), - "Keystroke enter - preset" ); - - // Control + Home = Change the calendar to the current month - input.val( "1/1/14" ).datepicker( "open" ) - .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.HOME } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Keystroke ctrl+home" ); - - // Control + End = Close the calendar and clear the input - input.val( "1/1/14" ).datepicker( "open" ) - .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.END } ); - equal( input.val(), "", "Keystroke ctrl+end" ); - - input.val( "" ).datepicker( "open" ); - ok( instance.isOpen, "datepicker is open before escape" ); - input.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); - ok( !instance.isOpen, "escape closes the datepicker" ); - - input.val( "1/1/14" ).datepicker( "open" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), - "Keystroke esc - preset" ); - - input.val( "1/1/14" ).datepicker( "open" ) - .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), - "Keystroke esc - abandoned" ); - - input.val( "1/2/14" ) - .simulate( "keyup" ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 2 ), - "Picker updated as user types into input" ); - - input.datepicker( "destroy" ); -}); - -asyncTest( "keyboard handling", function() { - expect( 14 ); - var picker, - input = $( "#datepicker" ), - date = new Date(); + picker, instance; function step1() { - input.datepicker(); + TestHelpers.datepicker.init( input ); picker = input.datepicker( "widget" ); + ok( !picker.is( ":visible" ), "datepicker closed" ); - input.val( "" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + input.val( "" ).simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { ok( picker.is( ":visible" ), "Keystroke down opens datepicker" ); input.datepicker( "destroy" ); step2(); - }); + }, 100 ); } function step2() { - input.datepicker(); + TestHelpers.datepicker.init( input ); picker = input.datepicker( "widget" ); + ok( !picker.is( ":visible" ), "datepicker closed" ); - input.val( "" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + input.val( "" ).simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); setTimeout(function() { ok( picker.is( ":visible" ), "Keystroke up opens datepicker" ); input.datepicker( "destroy" ); step3(); - }); + }, 100 ); } function step3() { - input.datepicker() - .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + TestHelpers.datepicker.init( input ); + instance = input.datepicker( "instance" ); - setTimeout(function() { - $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - date = new Date( 2013, 12 - 1, 31 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, - "Keystroke left to switch to previous day" ); + input + .val( "" ) + .datepicker( "open" ); + ok( instance.isOpen, "datepicker is open before escape" ); - input.datepicker( "destroy" ); - step4(); - }, 100 ); - } + input.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + ok( !instance.isOpen, "escape closes the datepicker" ); - function step4() { - input.datepicker() + input .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + .datepicker( "open" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), + "Keystroke esc - preset" ); - setTimeout(function() { - $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - date = new Date( 2014, 1 - 1, 2 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, - "Keystroke right to switch to next day" ); - - input.datepicker( "destroy" ); - step5(); - }, 100 ); + input + .val( "1/1/14" ) + .datepicker( "open" ) + .simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), + "Keystroke esc - abandoned" ); + + input + .val( "1/2/14" ) + .simulate( "keyup" ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 2 ), + "Picker updated as user types into input" ); + + input.datepicker( "destroy" ); + start(); } - function step5() { - input.datepicker() - .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + step1(); +}); - setTimeout(function() { - $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.UP } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - date = new Date( 2013, 12 - 1, 25 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, - "Keystroke up to move to the previous week" ); +asyncTest( "keyboard handling: calendar", function() { + expect( 7 ); - input.datepicker( "destroy" ); - step6(); - }, 100 ); - } + var input = $( "#datepicker" ); - function step6() { - input.datepicker() - .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + function step1() { + input.val( "1/1/14" ); + TestHelpers.datepicker.init( input ); + input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { - $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - date = new Date( 2014, 1 - 1, 8 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, - "Keystroke down to move to the next week" ); - - input.datepicker( "destroy" ); - step7(); + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.datepicker.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2013, 12 - 1, 31 ), + "Keystroke left to switch to previous day" + ); + input.datepicker( "destroy" ); + step2(); + }, 50 ); }, 100 ); } - function step7() { - input.datepicker() - .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + function step2() { + input.val( "1/1/14" ); + TestHelpers.datepicker.init( input ); + input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ) .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - date = new Date( 2013, 12 - 1, 1 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, - "Keystroke Page Up moves date to previous month" ); + TestHelpers.datepicker.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2014, 1 - 1, 2 ), + "Keystroke right to switch to next day" + ); input.datepicker( "destroy" ); - step8(); + step3(); }, 100 ); } - function step8() { - input.datepicker() - .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + function step3() { + input.val( "1/1/14" ); + TestHelpers.datepicker.init( input ); + input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { - $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP, altKey: true } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - date = new Date( 2013, 1 - 1, 1 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, - "Keystroke Page Up + Ctrl moves date to previous year" ); - - input.datepicker( "destroy" ); - step9(); + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.datepicker.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2013, 12 - 1, 25 ), + "Keystroke up to move to the previous week" + ); + input.datepicker( "destroy" ); + step4(); + }, 50 ); }, 100 ); } - function step9() { - input.datepicker() - .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + function step4() { + input.val( "1/1/14" ); + TestHelpers.datepicker.init( input ); + input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { - $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - date = new Date( 2014, 2 - 1, 1 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, - "Keystroke Page Down moves date to next month" ); - - input.datepicker( "destroy" ); - step10(); + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + + TestHelpers.datepicker.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2014, 1 - 1, 8 ), + "Keystroke down to move to the next week" + ); + input.datepicker( "destroy" ); + step5(); + }, 50 ); }, 100 ); } - function step10() { - input.datepicker() - .val( "1/1/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + function step5() { + input.val( "1/1/14" ); + TestHelpers.datepicker.init( input ); + input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { - $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN, altKey: true } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - date = new Date( 2015, 1 - 1, 1 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, - "Keystroke Page Down + Ctrl moves date to next year" ); - - input.datepicker( "destroy" ); - step11(); + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.datepicker.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2014, 2 - 1, 1 ), + "Keystroke Page Down moves date to next month" + ); + input.datepicker( "destroy" ); + step6(); + }, 50 ); }, 100 ); } - // Check for moving to short months - function step11() { - input.datepicker() - .val( "3/31/14" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + function step6() { + input.val( "1/1/14" ); + TestHelpers.datepicker.init( input ); + input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { - $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - date = new Date( 2014, 2 - 1, 28 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, - "Keystroke Page Up and short months" ); - - input.datepicker( "destroy" ); - step12(); + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN, altKey: true } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.datepicker.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2015, 1 - 1, 1 ), + "Keystroke Page Down + Ctrl moves date to next year" + ); + input.datepicker( "destroy" ); + step7(); + }, 50 ); }, 100 ); } - function step12() { - input.datepicker() - .val( "1/30/16" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + // Check for moving to short months + function step7() { + input.val( "3/31/14" ); + TestHelpers.datepicker.init( input ); + input.simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); setTimeout(function() { - $( ":focus" ) - .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ) - .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); - date = new Date( 2016, 2 - 1, 29 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, - "Keystroke Page Down and leap years" ); - - input.datepicker( "destroy" ); - start(); + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.datepicker.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2014, 2 - 1, 28 ), + "Keystroke Page Up and short months" + ); + input.datepicker( "destroy" ); + start(); + }, 50 ); }, 100 ); } @@ -502,110 +456,37 @@ asyncTest( "keyboard handling", function() { "Keystroke pgdn step 2" ); */ -test( "mouse", function() { - expect( 13 ); - var input = $( "#datepicker" ).datepicker(), - picker = input.datepicker( "widget" ), - inline = $( "#inline" ).datepicker, - date = new Date(); - - input.val( "" ).datepicker( "open" ); - $( ".ui-datepicker-calendar tbody a:contains(10)", picker ).simulate( "mousedown", {} ); - date.setDate( 10 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Mouse click" ); - - input.val( "2/4/08" ).datepicker( "open" ); - $( ".ui-datepicker-calendar tbody a:contains(12)", picker ).simulate( "mousedown", {} ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 12 ), - "Mouse click - preset" ) ; - - input.val( "" ).datepicker( "open" ); - $( "button.ui-datepicker-close", picker ).simulate( "click", {} ); - ok( input.datepicker( "valueAsDate" ) == null, "Mouse click - close" ); - - input.val( "2/4/08" ).datepicker( "open" ); - $( "button.ui-datepicker-close", picker ).simulate( "click", {} ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 4 ), - "Mouse click - close + preset" ); - - input.val( "2/4/08" ).datepicker( "open" ); - $( "a.ui-datepicker-prev", picker ).simulate( "click", {} ); - $( "button.ui-datepicker-close", picker ).simulate( "click", {} ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 4 ), - "Mouse click - abandoned" ); - - // Current/previous/next - input.val( "" ).datepicker( "open" ); - $( ".ui-datepicker-current", picker ).simulate( "click", {} ); - date.setDate( new Date().getDate() ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date, "Mouse click - current" ); - - input.val( "2/4/08" ).datepicker( "open" ); - $( ".ui-datepicker-prev", picker ).simulate( "click" ); - $( ".ui-datepicker-calendar tbody a:contains(16)", picker ).simulate( "mousedown" ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 1 - 1, 16 ), - "Mouse click - previous" ); - - input.val( "2/4/08" ).datepicker( "open" ); - $( ".ui-datepicker-next", picker ).simulate( "click" ); - $( ".ui-datepicker-calendar tbody a:contains(18)", picker ).simulate( "mousedown" ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 3 - 1, 18 ), - "Mouse click - next" ); - - /* - // TODO: Re-add when min and max options are introduced. - // Previous/next with minimum/maximum - input.datepicker( "option", { - minDate: new Date( 2008, 2 - 1, 2 ), - maxDate: new Date( 2008, 2 - 1, 26 ) - }).val( "2/4/08" ).datepicker( "open" ); - $( ".ui-datepicker-prev", picker ).simulate( "click" ); - $( ".ui-datepicker-calendar tbody a:contains(16)", picker ).simulate( "mousedown" ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 16 ), - "Mouse click - previous + min/max" ); - - input.val( "2/4/08" ).datepicker( "open" ); - $( ".ui-datepicker-next", picker ).simulate( "click" ); - $( ".ui-datepicker-calendar tbody a:contains(18)", picker ).simulate( "mousedown" ); - TestHelpers.datepicker.equalsDate(input.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 18 ), - "Mouse click - next + min/max" ); - */ - - // Inline - inline = TestHelpers.datepicker.init( "#inline" ); - picker = $( ".ui-datepicker-inline", inline ); - date = new Date(); - inline.datepicker( "valueAsDate", date ); - $( ".ui-datepicker-calendar tbody a:contains(10)", picker ).simulate( "mousedown", {} ); - date.setDate( 10 ); - TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), date, "Mouse click inline" ); - - inline.datepicker( "option", { showButtonPanel: true } ) - .datepicker( "valueAsDate", new Date( 2008, 2 - 1, 4 )); - $( ".ui-datepicker-calendar tbody a:contains(12)", picker ).simulate( "mousedown", {} ); - TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), new Date( 2008, 2 - 1, 12 ), - "Mouse click inline - preset" ); - - inline.datepicker( "option", { showButtonPanel: true } ); - $( ".ui-datepicker-current", picker ).simulate( "click", {} ); - $( ".ui-datepicker-calendar tbody a:contains(14)", picker ).simulate( "mousedown", {} ); - date.setDate( 14 ); - TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), date, "Mouse click inline - current" ); - - inline.datepicker( "valueAsDate", new Date( 2008, 2 - 1, 4) ); - $( ".ui-datepicker-prev", picker ).simulate( "click" ); - $( ".ui-datepicker-calendar tbody a:contains(16)", picker ).simulate( "mousedown" ); - TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), new Date( 2008, 1 - 1, 16 ), - "Mouse click inline - previous" ); - - inline.datepicker( "valueAsDate", new Date( 2008, 2 - 1, 4) ); - $( ".ui-datepicker-next", picker ).simulate( "click" ); - $( ".ui-datepicker-calendar tbody a:contains(18)", picker ).simulate( "mousedown" ); - TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), new Date( 2008, 3 - 1, 18 ), - "Mouse click inline - next" ); - - input.datepicker( "destroy" ); - inline.datepicker( "destroy" ); +asyncTest( "mouse", function() { + expect( 3 ); + + var input = TestHelpers.datepicker.init( $( "#datepicker" ).val( "" ) ), + picker = input.datepicker( "widget" ); + + input.datepicker( "open" ); + + setTimeout(function() { + input.simulate( "click" ); + ok( input.datepicker( "valueAsDate" ) === null, "Mouse click - close" ); + + input.val( "2/4/08" ).datepicker( "refresh" ).datepicker( "open" ); + input.simulate( "click" ); + TestHelpers.datepicker.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2008, 2 - 1, 4 ), + "Mouse click - close + preset" + ); + + input.val( "2/4/08" ).datepicker( "refresh" ).datepicker( "open" ); + $( "a.ui-calendar-prev", picker ).simulate( "click", {} ); + input.simulate( "click" ); + TestHelpers.datepicker.equalsDate( + input.datepicker( "valueAsDate" ), + new Date( 2008, 2 - 1, 4 ), + "Mouse click - abandoned" + ); + + start(); + }, 100 ); }); })( jQuery ); diff --git a/tests/unit/datepicker/datepicker_methods.js b/tests/unit/datepicker/datepicker_methods.js index 7efea106918..c69f15dac4d 100644 --- a/tests/unit/datepicker/datepicker_methods.js +++ b/tests/unit/datepicker/datepicker_methods.js @@ -64,54 +64,43 @@ test( "open", function() { }); test( "value", function() { - expect( 6 ); + expect( 4 ); + var input = $( "#datepicker" ).datepicker(), - picker = input.datepicker( "widget" ), - inline = $( "#inline" ).datepicker(); + picker = input.datepicker( "widget" ); input.datepicker( "value", "1/1/14" ); equal( input.val(), "1/1/14", "input's value set" ); - ok( picker.find( "a[data-timestamp]:first" ).hasClass( "ui-state-focus" ), - "first day marked as selected" ); + + input.datepicker( "open" ); + ok( picker.find( "a[data-timestamp]:first" ).hasClass( "ui-state-active" ), "first day marked as selected" ); equal( input.datepicker( "value" ), "1/1/14", "getter" ); input.val( "abc" ); - equal( input.datepicker( "value" ), "abc", - "Invalid values should be returned without formatting." ); - - inline.datepicker( "value", "1/1/14" ); - ok( inline.find( "a[data-timestamp]:first" ).hasClass( "ui-state-focus" ), - "first day marked as selected" ); - equal( inline.datepicker( "value" ), "1/1/14", "getter" ); - - input.datepicker( "destroy" ); - inline.datepicker( "destroy" ); + equal( input.datepicker( "value" ), "abc", "Invalid values should be returned without formatting." ); }); test( "valueAsDate", function() { expect( 6 ); - var input = $( "#datepicker" ).datepicker(), + + var input = TestHelpers.datepicker.init( "#datepicker" ), picker = input.datepicker( "widget" ), - inline = $( "#inline" ).datepicker(); + date1 = new Date( 2008, 6 - 1, 4 ); input.datepicker( "valueAsDate", new Date( 2014, 0, 1 ) ); - equal( input.val(), "1/1/14", "input's value set" ); - ok( picker.find( "a[data-timestamp]:first" ).hasClass( "ui-state-focus" ), - "first day marked as selected" ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), - new Date( 2014, 0, 1 ), "getter" ); + equal( input.val(), "1/1/14", "Input's value set" ); + ok( picker.find( "a[data-timestamp]:first" ).hasClass( "ui-state-active" ), "First day marked as selected" ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), new Date( 2014, 0, 1 ), "Getter" ); input.val( "a/b/c" ); equal( input.datepicker( "valueAsDate" ), null, "Invalid dates return null" ); - inline.datepicker( "valueAsDate", new Date( 2014, 0, 1 ) ); - ok( inline.find( "a[data-timestamp]:first" ).hasClass( "ui-state-focus" ), - "first day marked as selected" ); - TestHelpers.datepicker.equalsDate( inline.datepicker( "valueAsDate" ), - new Date( 2014, 0, 1 ), "getter" ); + input.val( "" ).datepicker( "destroy" ); + input = TestHelpers.datepicker.init( "#datepicker" ); - input.datepicker( "destroy" ); - inline.datepicker( "destroy" ); + ok(input.datepicker( "valueAsDate" ) === null, "Set date - default" ); + input.datepicker( "valueAsDate", date1 ); + TestHelpers.datepicker.equalsDate(input.datepicker( "valueAsDate" ), date1, "Set date - 2008-06-04" ); }); test( "isValid", function() { diff --git a/ui/datepicker.js b/ui/datepicker.js index f2dfa2d1594..f564bf9a39b 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -45,6 +45,7 @@ $.widget( "ui.datepicker", { showWeek: false, show: true, hide: true, + value: null, // callbacks beforeOpen: null, @@ -54,17 +55,22 @@ $.widget( "ui.datepicker", { }, _create: function() { - this.date = $.date( null, this.options.dateFormat ); - - this.date.eachDay = this.options.eachDay; this.id = "ui-datepicker-" + idIncrement; idIncrement++; + if ( this.element.is( "input" ) ) { + if ( !this.options.value && this.isValid() ) { + this.options.value = this._getParsedValue(); + } this._createPicker(); } else { this.inline = true; this.picker = this.element; } + + this.date = $.date( this.options.value, this.options.dateFormat ).select(); + this.date.eachDay = this.options.eachDay; + this._on( this.picker, { "click .ui-datepicker-prev": function( event ) { event.preventDefault(); @@ -106,7 +112,7 @@ $.widget( "ui.datepicker", { _handleKeydown: function( event ) { if ( jQuery.inArray( event.keyCode, [ 13, 33, 34, 35, 36, 37, 38, 39, 40 ] ) === -1 ) { - //only interested navigation keys + // only interested navigation keys return; } event.preventDefault(); @@ -171,7 +177,7 @@ $.widget( "ui.datepicker", { _createPicker: function() { this.picker = $( "
" ) .addClass( "ui-front" ) - // TODO add a ui-datepicker-popup class, move position:absolte to that + // TODO add a ui-datepicker-popup class, move position:absolute to that .css( "position", "absolute" ) .uniqueId() .hide(); @@ -217,6 +223,7 @@ $.widget( "ui.datepicker", { } } break; + // TODO this is not in specs, keep? case $.ui.keyCode.END: if ( event.ctrlKey ) { this.element.val( "" ); @@ -230,12 +237,12 @@ $.widget( "ui.datepicker", { }, keyup: function() { if ( this.isValid() && !this.inline ) { - this.date.setTime( this.element.val() ).select(); + this.date.setTime( this._getParsedValue() ).select(); this.refresh(); } }, mousedown: function( event ) { - if (this.isOpen) { + if ( this.isOpen ) { suppressExpandOnFocus = true; this.close(); return; @@ -312,6 +319,7 @@ $.widget( "ui.datepicker", { return element; }, + _createTmpl: function() { this._createDatepicker(); this.picker.find( "button" ).button(); @@ -323,6 +331,7 @@ $.widget( "ui.datepicker", { this.picker.find( ".ui-datepicker" ).css( "display", "block" ); this.grid = this.picker.find( ".ui-datepicker-calendar" ); }, + _createDatepicker: function() { var multiClasses = [], pickerHtml = ""; @@ -345,6 +354,7 @@ $.widget( "ui.datepicker", { .html( pickerHtml ) .appendTo( this.picker ); }, + _buildMultiplePicker: function() { var headerClass, html = "", @@ -572,8 +582,8 @@ $.widget( "ui.datepicker", { // with the prev and next links would cause loss of focus issues because the links being // interacted with will disappear while focused. refresh: function() { - //determine which day gridcell to focus after refresh - //TODO: Prevent disabled cells from being focused + // determine which day gridcell to focus after refresh + // TODO: Prevent disabled cells from being focused if ( this.options.numberOfMonths === 1 ) { this.grid = $( this._buildGrid() ); $( ".ui-datepicker-title", this.picker ).html( this._buildTitle() ); @@ -605,10 +615,6 @@ $.widget( "ui.datepicker", { return; } - // TODO explain this - this.date = $.date( this.element.val(), this.options.dateFormat ); - this.date.eachDay = this.options.eachDay; - this.date.select(); this.refresh(); this.picker @@ -649,46 +655,38 @@ $.widget( "ui.datepicker", { }, _buildPosition: function() { - return $.extend( {}, { - of: this.element - }, this.options.position ); + return $.extend( { of: this.element }, this.options.position ); }, _select: function( event, time ) { - this.date.setTime( time ).select(); - this.refresh(); + this._setOption( "value", new Date( time ) ); if ( !this.inline ) { - this.element.val( this.date.format() ); this.close(); this._focusTrigger(); } this._trigger( "select", event, { // TODO replace with value option to initialise and read - date: this.date.format() + date: this.value() }); }, - _value: function( value ) { - this.date.setTime( value ).select(); - if ( !this.inline ) { - this.element.val( this.date.format() ); - } - this.refresh(); - }, - value: function( value ) { if ( arguments.length ) { - this._value( value ); + this._setOption( "value", Globalize.parseDate( value, this.options.dateFormat ) ); } else { - return this.isValid() ? this.date.format() : this.element.val(); + if ( this.inline ) { + return Globalize.format( this.date.selected(), this.options.dateFormat ); + } else { + return this.element.val(); + } } }, valueAsDate: function( value ) { if ( arguments.length ) { - this._value( value ); + this._setOption( "value", value ); } else { - return this.isValid() ? this.date.date() : null; + return this.option( "value" ); } }, @@ -697,7 +695,7 @@ $.widget( "ui.datepicker", { return true; } - return Globalize.parseDate( this.element.val(), this.options.dateFormat ) !== null; + return this._getParsedValue() !== null; }, _destroy: function() { @@ -715,7 +713,28 @@ $.widget( "ui.datepicker", { return this.picker; }, + _getParsedValue: function() { + return Globalize.parseDate( this.element.val(), this.options.dateFormat ); + }, + + option: function( key ) { + if ( arguments.length === 0 || ( arguments.length === 1 && key === "value" ) ) { + this.options.value = this.inline ? this.date.selected() : this._getParsedValue(); + } + return this._superApply( arguments ); + }, + _setOption: function( key, value ) { + if ( key === "value" ) { + if ( $.type( value ) === "date" ) { + this.date.setTime( value.getTime() ).select(); + this.refresh(); + if ( !this.inline ) { + this.element.val( this.date.format() ); + } + } + } + this._super( key, value ); if ( key === "appendTo" ) { From 6d393eae7c8afa2a885630dc5dc5e8ffe5980f0a Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Tue, 3 Jun 2014 23:18:51 +0200 Subject: [PATCH 02/26] Calendar: Add calendar widget Add calendar widget by copying and renaming datepicker widget files. Remove datepicker functionality, options and methods from Calendar. Remove calendar functionality, options and methods from Datepicker. Adjust tests due to split and changed specification. Remove duplicated demo files and fix some demos. Simplify calendar generation, use CSS instead of inline styles. Fix destroy method. Make use of uniqueId method. Fix focus highlighting when month is changed. Add version property. Add common unit tests. Fix input keyboard handling. --- Gruntfile.js | 1 + build/tasks/testswarm.js | 1 + demos/{datepicker => calendar}/buttonbar.html | 8 +- .../inline.html => calendar/default.html} | 10 +- .../dropdown-month-year.html | 8 +- demos/calendar/index.html | 22 + demos/calendar/localization.html | 41 + demos/{datepicker => calendar}/min-max.html | 8 +- .../multiple-calendars.html | 10 +- demos/calendar/multiple-months.html | 33 + .../other-months.html | 10 +- demos/{datepicker => calendar}/show-week.html | 10 +- demos/datepicker/alt-field.html | 5 +- demos/datepicker/animation.html | 1 + demos/datepicker/date-formats.html | 26 +- demos/datepicker/date-range.html | 49 - demos/datepicker/default.html | 1 + demos/datepicker/icon-trigger.html | 1 + demos/datepicker/index.html | 14 +- demos/datepicker/localization.html | 1 + demos/index.html | 1 + tests/unit/all.html | 1 + tests/unit/calendar/all.html | 26 + tests/unit/calendar/calendar.html | 46 + tests/unit/calendar/calendar_common.js | 14 + tests/unit/calendar/calendar_core.js | 370 ++++++ tests/unit/calendar/calendar_events.js | 150 +++ tests/unit/calendar/calendar_methods.js | 78 ++ tests/unit/calendar/calendar_options.js | 351 +++++ tests/unit/calendar/calendar_test_helpers.js | 22 + tests/unit/datepicker/datepicker.html | 5 +- tests/unit/datepicker/datepicker_common.js | 22 +- tests/unit/datepicker/datepicker_core.js | 406 +----- tests/unit/datepicker/datepicker_methods.js | 29 +- tests/unit/datepicker/datepicker_options.js | 1129 +---------------- .../datepicker/datepicker_test_helpers.js | 2 +- tests/unit/index.html | 1 + themes/base/base.css | 1 + themes/base/calendar.css | 178 +++ themes/base/datepicker.css | 172 +-- ui/calendar.js | 481 +++++++ ui/datepicker.js | 570 ++------- 42 files changed, 2039 insertions(+), 2276 deletions(-) rename demos/{datepicker => calendar}/buttonbar.html (84%) rename demos/{datepicker/inline.html => calendar/default.html} (69%) rename demos/{datepicker => calendar}/dropdown-month-year.html (82%) create mode 100644 demos/calendar/index.html create mode 100644 demos/calendar/localization.html rename demos/{datepicker => calendar}/min-max.html (81%) rename demos/{datepicker => calendar}/multiple-calendars.html (75%) create mode 100644 demos/calendar/multiple-months.html rename demos/{datepicker => calendar}/other-months.html (77%) rename demos/{datepicker => calendar}/show-week.html (77%) delete mode 100644 demos/datepicker/date-range.html create mode 100644 tests/unit/calendar/all.html create mode 100644 tests/unit/calendar/calendar.html create mode 100644 tests/unit/calendar/calendar_common.js create mode 100644 tests/unit/calendar/calendar_core.js create mode 100644 tests/unit/calendar/calendar_events.js create mode 100644 tests/unit/calendar/calendar_methods.js create mode 100644 tests/unit/calendar/calendar_options.js create mode 100644 tests/unit/calendar/calendar_test_helpers.js create mode 100644 themes/base/calendar.css create mode 100644 ui/calendar.js diff --git a/Gruntfile.js b/Gruntfile.js index b8610b6f641..3d9a11963db 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -28,6 +28,7 @@ var "core", "accordion", "autocomplete", + "calendar", "button", "datepicker", "dialog", diff --git a/build/tasks/testswarm.js b/build/tasks/testswarm.js index a147b8f573c..0d500ef09dd 100644 --- a/build/tasks/testswarm.js +++ b/build/tasks/testswarm.js @@ -14,6 +14,7 @@ var versions = { "Accordion": "accordion/accordion.html", "Autocomplete": "autocomplete/autocomplete.html", "Button": "button/button.html", + "Calendar": "calendar/calendar.html", "Core": "core/core.html", "Core_deprecated": "core/core_deprecated.html", "Datepicker": "datepicker/datepicker.html", diff --git a/demos/datepicker/buttonbar.html b/demos/calendar/buttonbar.html similarity index 84% rename from demos/datepicker/buttonbar.html rename to demos/calendar/buttonbar.html index 446bc9fe495..498fe1041f4 100644 --- a/demos/datepicker/buttonbar.html +++ b/demos/calendar/buttonbar.html @@ -2,7 +2,7 @@ - jQuery UI Datepicker - Display button bar + jQuery UI Calendar - Display button bar @@ -11,12 +11,12 @@ + - @@ -11,21 +11,21 @@ + - -Date:
+
-

Display the datepicker embedded in the page instead of in an overlay. Simply call .datepicker() on a div instead of an input.

+

The calendar is a widget for selecting a date using a visual calendar representation.

diff --git a/demos/datepicker/dropdown-month-year.html b/demos/calendar/dropdown-month-year.html similarity index 82% rename from demos/datepicker/dropdown-month-year.html rename to demos/calendar/dropdown-month-year.html index c6102917f43..050a89157ff 100644 --- a/demos/datepicker/dropdown-month-year.html +++ b/demos/calendar/dropdown-month-year.html @@ -2,7 +2,7 @@ - jQuery UI Datepicker - Display month & year menus + jQuery UI Calendar - Display month & year menus @@ -11,12 +11,12 @@ + - + + + + + + + + + + + + + +
+ + +
+

Localize the calendar calendar language and format (English / Western formatting is the default). The calendar includes built-in support for languages that read right-to-left, such as Arabic and Hebrew.

+
+ + diff --git a/demos/datepicker/min-max.html b/demos/calendar/min-max.html similarity index 81% rename from demos/datepicker/min-max.html rename to demos/calendar/min-max.html index 6dcc16a48bc..c449bbdd55c 100644 --- a/demos/datepicker/min-max.html +++ b/demos/calendar/min-max.html @@ -2,7 +2,7 @@ - jQuery UI Datepicker - Restrict date range + jQuery UI Calendar - Restrict date range @@ -11,18 +11,18 @@ + - -

Date:

+

Restrict the range of selectable dates with the minDate and maxDate options. Set the beginning and end dates as actual dates (new Date(2009, 1 - 1, 26)), as a numeric offset from today (-20), or as a string of periods and units ('+1M +10D'). For the last, use 'D' for days, 'W' for weeks, 'M' for months, or 'Y' for years.

diff --git a/demos/datepicker/multiple-calendars.html b/demos/calendar/multiple-calendars.html similarity index 75% rename from demos/datepicker/multiple-calendars.html rename to demos/calendar/multiple-calendars.html index 3d8278cd625..f67609d143e 100644 --- a/demos/datepicker/multiple-calendars.html +++ b/demos/calendar/multiple-calendars.html @@ -2,7 +2,7 @@ - jQuery UI Datepicker - Display multiple months + jQuery UI Calendar - Display multiple months @@ -11,12 +11,12 @@ + - + + + + + + + + + + + + + +
+ +
+

Set the numberOfMonths option to an integer of 2 or more to show multiple months in a single calendar.

+
+ + diff --git a/demos/datepicker/other-months.html b/demos/calendar/other-months.html similarity index 77% rename from demos/datepicker/other-months.html rename to demos/calendar/other-months.html index bfb3f1af36f..0228c62df5b 100644 --- a/demos/datepicker/other-months.html +++ b/demos/calendar/other-months.html @@ -2,7 +2,7 @@ - jQuery UI Datepicker - Dates in other months + jQuery UI Calendar - Dates in other months @@ -11,12 +11,12 @@ + - @@ -11,12 +11,12 @@ + - + + diff --git a/demos/datepicker/date-formats.html b/demos/datepicker/date-formats.html index 2c5045b6f33..4e9346456b9 100644 --- a/demos/datepicker/date-formats.html +++ b/demos/datepicker/date-formats.html @@ -12,28 +12,22 @@ + - - - - - - - - - - - - - - - - - - -
-

Select the date range to search for.

-
- - diff --git a/demos/datepicker/default.html b/demos/datepicker/default.html index be0c453c2b3..b2439b29ab3 100644 --- a/demos/datepicker/default.html +++ b/demos/datepicker/default.html @@ -12,6 +12,7 @@ + + diff --git a/demos/datepicker/index.html b/demos/datepicker/index.html index d9c8dfc10da..5587564831f 100644 --- a/demos/datepicker/index.html +++ b/demos/datepicker/index.html @@ -9,19 +9,11 @@ diff --git a/demos/datepicker/localization.html b/demos/datepicker/localization.html index 61aa7c9a2da..701e5c36b66 100644 --- a/demos/datepicker/localization.html +++ b/demos/datepicker/localization.html @@ -11,6 +11,7 @@ + diff --git a/demos/index.html b/demos/index.html index f37874a4481..c4a0fdf9501 100644 --- a/demos/index.html +++ b/demos/index.html @@ -11,6 +11,7 @@
  • accordion
  • autocomplete
  • button
  • +
  • calendar
  • datepicker
  • dialog
  • draggable
  • diff --git a/tests/unit/all.html b/tests/unit/all.html index f8a4636d051..85e21077412 100644 --- a/tests/unit/all.html +++ b/tests/unit/all.html @@ -21,6 +21,7 @@ "button/button.html", "core/core.html", "core/core_deprecated.html", + "calendar/calendar.html", "datepicker/datepicker.html", "dialog/dialog.html", "draggable/draggable.html", diff --git a/tests/unit/calendar/all.html b/tests/unit/calendar/all.html new file mode 100644 index 00000000000..5b22b047024 --- /dev/null +++ b/tests/unit/calendar/all.html @@ -0,0 +1,26 @@ + + + + + jQuery UI Calendar Test Suite + + + + + + + + + + + + + +
    +
    + +
    + + diff --git a/tests/unit/calendar/calendar.html b/tests/unit/calendar/calendar.html new file mode 100644 index 00000000000..ed536414fbc --- /dev/null +++ b/tests/unit/calendar/calendar.html @@ -0,0 +1,46 @@ + + + + + jQuery UI Calendar Test Suite + + + + + + + + + + + + + + + + + + + + + + +
    +
    + +
    +
    + +
    + + diff --git a/tests/unit/calendar/calendar_common.js b/tests/unit/calendar/calendar_common.js new file mode 100644 index 00000000000..f3a01171ba5 --- /dev/null +++ b/tests/unit/calendar/calendar_common.js @@ -0,0 +1,14 @@ +TestHelpers.commonWidgetTests( "calendar", { + defaults: { + dateFormat: { date: "short" }, + disabled: false, + eachDay: $.noop, + numberOfMonths: 1, + showWeek: false, + value: null, + + // callbacks + create: null, + select: null + } +}); diff --git a/tests/unit/calendar/calendar_core.js b/tests/unit/calendar/calendar_core.js new file mode 100644 index 00000000000..c9198ac87b7 --- /dev/null +++ b/tests/unit/calendar/calendar_core.js @@ -0,0 +1,370 @@ +(function( $ ) { + +module( "calendar: core" ); + +TestHelpers.testJshint( "calendar" ); + +test( "baseStructure", function() { + expect( 22 ); + + var header, title, table, thead, week, child, buttonpane, + element = $( "#calendar" ).calendar(), + dp = element.calendar( "widget" ); + + function step1() { + ok( !dp.is( ".ui-calendar-rtl" ), "Structure - not right-to-left" ); + ok( !dp.is( ".ui-calendar-multi" ), "Structure - not multi-month" ); + equal( dp.children().length, 3, "Structure - child count (header, calendar)" ); + + header = dp.children( ":first" ); + ok( header.is( "div.ui-calendar-header" ), "Structure - header division" ); + equal( header.children().length, 3, "Structure - header child count" ); + ok( header.children( ":first" ).is( ".ui-calendar-prev" ) && header.children( ":first" ).html() !== "", "Structure - prev link" ); + ok( header.children( ":eq(1)" ).is( ".ui-calendar-next" ) && header.children( ":eq(1)" ).html() !== "", "Structure - next link" ); + + title = header.children( ":last" ).children( ":first" ); + ok( title.is( "div.ui-calendar-title" ) && title.html() !== "", "Structure - title division" ); + equal( title.children().length, 2, "Structure - title child count" ); + ok( title.children( ":first" ).is( "span.ui-calendar-month" ) && title.children( ":first" ).text() !== "", "Structure - month text" ); + ok( title.children( ":last" ).is( "span.ui-calendar-year" ) && title.children( ":last" ).text() !== "", "Structure - year text" ); + + table = dp.children( ":eq(1)" ); + ok( table.is( "table.ui-calendar-calendar" ), "Structure - month table" ); + ok( table.children( ":first" ).is( "thead" ), "Structure - month table thead" ); + + thead = table.children( ":first" ).children( ":first" ); + ok( thead.is( "tr" ), "Structure - month table title row" ); + equal( thead.find( "th" ).length, 7, "Structure - month table title cells" ); + ok( table.children( ":eq(1)" ).is( "tbody" ), "Structure - month table body" ); + ok( table.children( ":eq(1)" ).children( "tr" ).length >= 4, "Structure - month table week count" ); + + week = table.children( ":eq(1)" ).children( ":first" ); + ok( week.is( "tr" ), "Structure - month table week row" ); + equal( week.children().length, 7, "Structure - week child count" ); + + element.calendar( "destroy" ); + + step2(); + } + + function step2() { + // Multi-month 2 + element = $( "#calendar" ).calendar( { numberOfMonths: 2 } ); + dp = element.calendar( "widget" ); + + ok( dp.is( ".ui-calendar-multi" ), "Structure multi [2] - multi-month" ); + equal( dp.children().length, 4, "Structure multi [2] - child count" ); + + child = dp.children( ":eq(2)" ); + ok( child.is( "div.ui-calendar-row-break" ), "Structure multi [2] - row break" ); + + element.calendar( "destroy" ); + } + + step1(); +}); + +test( "Localization", function() { + expect( 5 ); + + var defaultLocale = Globalize.locale(), + element = $( "#calendar" ), + date = new Date( 2014, 0, 1 ), + initCalendar = function() { + element + .calendar() + .calendar( "valueAsDate", date ); + }, + testLocalization = function( message ) { + equal( element.find( ".ui-calendar-month" ).text(), "Januar", message + "titlebar year" ); + equal( element.find( "thead th:first" ).text(), "Mo.", message + "teader first day" ); + equal( element.find( "thead th:last" ).text(), "So.", message + "header last day" ); + equal( element.find( ".ui-calendar-prev" ).text(), "", message + "header next" ); + }; + + Globalize.locale( "de-DE" ); + initCalendar(); + testLocalization( "Init: " ); + element.calendar( "destroy" ); + + Globalize.locale( defaultLocale.locale ); +}); + +asyncTest( "keyboard handling", function() { + expect( 10 ); + + var element = $( "#calendar" ); + + function step1() { + element.calendar({ value: new Date( 2014, 1 - 1, 1 ) }); + + TestHelpers.calendar.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.LEFT } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2013, 12 - 1, 31 ), + "Keystroke left to switch to previous day" + ); + element.calendar( "destroy" ); + step2(); + }, 50 ); + } + + function step2() { + element.calendar({ value: new Date( 2014, 1 - 1, 1 ) }); + + TestHelpers.calendar.focusGrid( element ) + .simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2014, 1 - 1, 2 ), + "Keystroke right to switch to next day" + ); + step3(); + } + + function step3() { + element.calendar({ value: new Date( 2014, 1 - 1, 1 ) }); + + TestHelpers.calendar.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.UP } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2013, 12 - 1, 25 ), + "Keystroke up to move to the previous week" + ); + element.calendar( "destroy" ); + step4(); + }, 50 ); + } + + function step4() { + element.calendar({ value: new Date( 2014, 1 - 1, 1 ) }); + + TestHelpers.calendar.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2014, 1 - 1, 8 ), + "Keystroke down to move to the next week" + ); + element.calendar( "destroy" ); + step5(); + }, 50 ); + } + + function step5() { + element.calendar({ value: new Date( 2014, 1 - 1, 1 ) }); + + TestHelpers.calendar.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2013, 12 - 1, 1 ), + "Keystroke Page Up moves date to previous month" + ); + element.calendar( "destroy" ); + step6(); + }, 50 ); + } + + function step6() { + element.calendar({ value: new Date( 2014, 1 - 1, 1 ) }); + + TestHelpers.calendar.focusGrid( element ) + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP, altKey: true } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2013, 1 - 1, 1 ), + "Keystroke Page Up + Ctrl moves date to previous year" + ); + element.calendar( "destroy" ); + step7(); + }, 50 ); + } + + function step7() { + element.calendar({ value: new Date( 2014, 1 - 1, 1 ) }); + + TestHelpers.calendar.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2014, 2 - 1, 1 ), + "Keystroke Page Down moves date to next month" + ); + element.calendar( "destroy" ); + step8(); + }, 50 ); + } + + function step8() { + element.calendar({ value: new Date( 2014, 1 - 1, 1 ) }); + + TestHelpers.calendar.focusGrid( element ) + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN, altKey: true } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2015, 1 - 1, 1 ), + "Keystroke Page Down + Ctrl moves date to next year" + ); + element.calendar( "destroy" ); + step9(); + }, 50 ); + } + + // Check for moving to short months + function step9() { + element.calendar({ value: new Date( 2014, 3 - 1, 31 ) }); + + TestHelpers.calendar.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2014, 2 - 1, 28 ), + "Keystroke Page Up and short months" + ); + element.calendar( "destroy" ); + step10(); + }, 50 ); + } + + function step10() { + element.calendar({ value: new Date( 2016, 1 - 1, 30 ) }); + + TestHelpers.calendar.focusGrid( element ).simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2016, 2 - 1, 29 ), + "Keystroke Page Down and leap years" + ); + element.calendar( "destroy" ); + start(); + }, 50 ); + } + + step1(); +}); + +/* + // TODO: Re-add tests if we implement a stepMonths option + input.calendar( "option", { stepMonths: 2, gotoCurrent: false } ) + .calendar( "close" ).val( "02/04/2008" ).calendar( "open" ) + .late( "keydown", { keyCode: $.ui.keyCode.PAGE_UP } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.datepicker.equalsDate( input.calendar( "valueAsDate" ), new Date( 2007, 12 - 1, 4 ), + "Keystroke pgup step 2" ); + + input.val( "02/04/2008" ).calendar( "open" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.PAGE_DOWN } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.datepicker.equalsDate( input.calendar( "valueAsDate" ), new Date( 2008, 4 - 1, 4 ), + "Keystroke pgdn step 2" ); + */ + +// TODO: implement +test( "ARIA", function() { + expect( 0 ); +}); + +asyncTest( "mouse", function() { + expect( 6 ); + + var element = $( "#calendar" ).calendar(), + date = new Date(); + + function step1() { + $( "tbody a:contains(10)", element ).simulate( "mousedown", {} ); + date.setDate( 10 ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + date, + "Mouse click" + ); + + element.calendar( "option", "value", new Date( 2008, 2 - 1, 4) ); + $( ".ui-calendar-calendar tbody a:contains(12)", element ).simulate( "mousedown" ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2008, 2 - 1, 12 ), + "Mouse click - preset" + ); + + // Previous/next + element.calendar( "option", "value", new Date( 2008, 2 - 1, 4) ); + $( ".ui-calendar-prev", element ).simulate( "click" ); + $( ".ui-calendar-calendar tbody a:contains(16)", element ).simulate( "mousedown" ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2008, 1 - 1, 16 ), + "Mouse click - previous" + ); + + element.calendar( "option", "value", new Date( 2008, 2 - 1, 4) ); + $( ".ui-calendar-next", element ).simulate( "click" ); + $( ".ui-calendar-calendar tbody a:contains(18)", element ).simulate( "mousedown" ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2008, 3 - 1, 18 ), + "Mouse click - next" + ); + + step2(); + } + + // Previous/next with minimum/maximum + function step2() { + element.calendar( "destroy" ); + element.calendar({ + value: new Date( 2008, 3 - 1, 4), + min: new Date( 2008, 2 - 1, 2 ), + max: new Date( 2008, 2 - 1, 26 ) + }); + + $( ".ui-calendar-prev", element ).simulate( "click" ); + $( "tbody a:contains(16)", element ).simulate( "mousedown" ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2008, 2 - 1, 16 ), + "Mouse click - previous + min/max" + ); + step3(); + } + + function step3() { + element.calendar( "destroy" ); + element.calendar({ + value: new Date( 2008, 1 - 1, 4), + min: new Date( 2008, 2 - 1, 2 ), + max: new Date( 2008, 2 - 1, 26 ) + }); + + $( ".ui-calendar-next", element ).simulate( "click" ); + $( "tbody a:contains(18)", element ).simulate( "mousedown" ); + TestHelpers.calendar.equalsDate( + element.calendar( "valueAsDate" ), + new Date( 2008, 2 - 1, 18 ), + "Mouse click - next + min/max" + ); + start(); + } + + step1(); +}); + +})( jQuery ); diff --git a/tests/unit/calendar/calendar_events.js b/tests/unit/calendar/calendar_events.js new file mode 100644 index 00000000000..2d03801fadb --- /dev/null +++ b/tests/unit/calendar/calendar_events.js @@ -0,0 +1,150 @@ +// The implement of events is completely changing therefore these tests are no longer directly +// relevant. Leaving them around commented out so we can ensure the functionality is replicated. +// For example: +// TODO: In the old implementation the Enter key select's today's date when the has +// focus and is empty. Do we want to replicate this behavior in the rewrite? +/* + +(function( $ ) { + +module( "calendar: events" ); + +test( "beforeOpen", function() { + expect( 0 ); +}); + +test( "close", function() { + expect( 0 ); +}); + +test( "open", function() { + expect( 0 ); +}); + +test( "select", function() { + expect( 0 ); +}); + +var selectedThis = null, +selectedDate = null, +selectedInst = null; + +function callback(date, inst) { + selectedThis = this; + selectedDate = date; + selectedInst = inst; +} + +function callback2(year, month, inst) { + selectedThis = this; + selectedDate = year + "/" + month; + selectedInst = inst; +} + +test( "events", function() { + expect( 26 ); + var dateStr, newMonthYear, inp2, + inp = TestHelpers.calendar.init( "#inp", {onSelect: callback}), + date = new Date(); + // onSelect + inp.val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + equal(selectedThis, inp[0], "Callback selected this" ); + equal(selectedInst, $.data(inp[0], TestHelpers.calendar.PROP_NAME), "Callback selected inst" ); + equal(selectedDate, $.calendar.formatDate( "mm/dd/yy", date), + "Callback selected date" ); + inp.val( "" ).calendar( "show" ). + simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.DOWN}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setDate(date.getDate() + 7); + equal(selectedDate, $.calendar.formatDate( "mm/dd/yy", date), + "Callback selected date - ctrl+down" ); + inp.val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ESCAPE}); + equal(selectedDate, $.calendar.formatDate( "mm/dd/yy", date), + "Callback selected date - esc" ); + dateStr = "02/04/2008"; + inp.val(dateStr).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + equal(dateStr, selectedDate, + "onSelect is called after enter keydown" ); + // onChangeMonthYear + inp.calendar( "option", {onChangeMonthYear: callback2, onSelect: null}). + val( "" ).calendar( "show" ); + newMonthYear = function(date) { + return date.getFullYear() + "/" + (date.getMonth() + 1); + }; + date = new Date(); + date.setDate(1); + inp.simulate( "keydown", {keyCode: $.ui.keyCode.PAGE_UP}); + date.setMonth(date.getMonth() - 1); + equal(selectedThis, inp[0], "Callback change month/year this" ); + equal(selectedInst, $.data(inp[0], TestHelpers.calendar.PROP_NAME), "Callback change month/year inst" ); + equal(selectedDate, newMonthYear(date), + "Callback change month/year date - pgup" ); + inp.simulate( "keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}); + date.setMonth(date.getMonth() + 1); + equal(selectedDate, newMonthYear(date), + "Callback change month/year date - pgdn" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}); + date.setFullYear(date.getFullYear() - 1); + equal(selectedDate, newMonthYear(date), + "Callback change month/year date - ctrl+pgup" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.HOME}); + date.setFullYear(date.getFullYear() + 1); + equal(selectedDate, newMonthYear(date), + "Callback change month/year date - ctrl+home" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}); + date.setFullYear(date.getFullYear() + 1); + equal(selectedDate, newMonthYear(date), + "Callback change month/year date - ctrl+pgdn" ); + inp.calendar( "setDate", new Date(2007, 1 - 1, 26)); + equal(selectedDate, "2007/1", "Callback change month/year date - setDate" ); + selectedDate = null; + inp.calendar( "setDate", new Date(2007, 1 - 1, 12)); + ok(selectedDate == null, "Callback change month/year date - setDate no change" ); + // onChangeMonthYear step by 2 + inp.calendar( "option", {stepMonths: 2}). + calendar( "hide" ).val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.PAGE_UP}); + date.setMonth(date.getMonth() - 14); + equal(selectedDate, newMonthYear(date), + "Callback change month/year by 2 date - pgup" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}); + date.setMonth(date.getMonth() - 12); + equal(selectedDate, newMonthYear(date), + "Callback change month/year by 2 date - ctrl+pgup" ); + inp.simulate( "keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}); + date.setMonth(date.getMonth() + 2); + equal(selectedDate, newMonthYear(date), + "Callback change month/year by 2 date - pgdn" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}); + date.setMonth(date.getMonth() + 12); + equal(selectedDate, newMonthYear(date), + "Callback change month/year by 2 date - ctrl+pgdn" ); + // onClose + inp.calendar( "option", {onClose: callback, onChangeMonthYear: null, stepMonths: 1}). + val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ESCAPE}); + equal(selectedThis, inp[0], "Callback close this" ); + equal(selectedInst, $.data(inp[0], TestHelpers.calendar.PROP_NAME), "Callback close inst" ); + equal(selectedDate, "", "Callback close date - esc" ); + inp.val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + equal(selectedDate, $.calendar.formatDate( "mm/dd/yy", new Date()), + "Callback close date - enter" ); + inp.val( "02/04/2008" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ESCAPE}); + equal(selectedDate, "02/04/2008", "Callback close date - preset" ); + inp.val( "02/04/2008" ).calendar( "show" ). + simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.END}); + equal(selectedDate, "", "Callback close date - ctrl+end" ); + + inp2 = TestHelpers.calendar.init( "#inp2" ); + inp2.calendar().calendar( "option", {onClose: callback}).calendar( "show" ); + inp.calendar( "show" ); + equal(selectedThis, inp2[0], "Callback close this" ); +}); + +})( jQuery ); + */ diff --git a/tests/unit/calendar/calendar_methods.js b/tests/unit/calendar/calendar_methods.js new file mode 100644 index 00000000000..ce53be1c119 --- /dev/null +++ b/tests/unit/calendar/calendar_methods.js @@ -0,0 +1,78 @@ +(function( $ ) { + +module( "calendar: methods" ); + +test( "destroy", function() { + expect( 4 ); + + var element = $( "#calendar" ).calendar(); + + ok( element.calendar( "instance" ), "instance created" ); + element.calendar( "destroy" ); + ok( !element.calendar( "instance" ), "instance removed" ); + ok( !element.attr( "role" ), "role attribute removed" ); + ok( !element.attr( "aria-labelledby" ), "aria-labelledby attribute removed" ); +}); + +test( "enable / disable", function() { + expect( 6 ); + + var element = $( "#calendar" ).calendar(); + + ok( !element.calendar( "option", "disabled" ), "initially enabled" ); + ok( !element.hasClass( "ui-calendar-disabled" ), "does not have disabled class name" ); + + element.calendar( "disable" ); + ok( element.calendar( "option", "disabled" ), "disabled option is set" ); + ok( element.hasClass( "ui-calendar-disabled" ), "calendar has disabled class name" ); + + element.calendar( "enable" ); + ok( !element.calendar( "option", "disabled" ), "enabled after enable() call" ); + ok( !element.hasClass( "ui-calendar-disabled" ), "no longer has disabled class name" ); +}); + +test( "widget", function() { + expect( 1 ); + + var element = $( "#calendar" ).calendar(), + widget = element.calendar( "widget" ); + + deepEqual( widget[ 0 ], element[ 0 ] ); +}); + +test( "value", function() { + expect( 3 ); + var element = $( "#calendar" ).calendar(); + + element.calendar( "value", "1/1/14" ); + ok( element.find( "a[data-timestamp]:first" ).hasClass( "ui-state-active" ), "first day marked as selected" ); + equal( element.calendar( "value" ), "1/1/14", "getter" ); + + element.calendar( "value", "abc" ); + equal( element.calendar( "value" ), "1/1/14", "Setting invalid values should be ignored." ); +}); + +test( "valueAsDate", function() { + expect( 5 ); + + var element = $( "#calendar" ).calendar(), + date1 = new Date( 2008, 6 - 1, 4 ), + date2 = new Date(); + + element.calendar( "valueAsDate", new Date( 2014, 0, 1 ) ); + ok( element.find( "a[data-timestamp]:first" ).hasClass( "ui-state-active" ), "First day marked as selected" ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), new Date( 2014, 0, 1 ), "Getter" ); + + element.calendar( "destroy" ); + + element.calendar(); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), new Date(), "Set date - default" ); + + element.calendar( "valueAsDate", date1 ); + TestHelpers.calendar.equalsDate(element.calendar( "valueAsDate" ), date1, "Set date - 2008-06-04" ); + + element.calendar( "valueAsDate", date1, date2 ); + TestHelpers.calendar.equalsDate(element.calendar( "valueAsDate" ), date1, "Set date - two dates" ); +}); + +})( jQuery ); diff --git a/tests/unit/calendar/calendar_options.js b/tests/unit/calendar/calendar_options.js new file mode 100644 index 00000000000..ea8483bbcd1 --- /dev/null +++ b/tests/unit/calendar/calendar_options.js @@ -0,0 +1,351 @@ +(function( $ ) { + +module( "calendar: options" ); + +test( "dateFormat", function() { + expect( 2 ); + var element = $( "#calendar" ).calendar({ + value: "1/1/14" + }), + firstDayLink = element.calendar( "widget" ).find( "td[id]:first a" ); + + firstDayLink.trigger( "mousedown" ); + equal( element.calendar( "value" ), "1/1/14", "default formatting" ); + + element.calendar( "option", "dateFormat", { date: "full" } ); + equal( element.calendar( "value" ), "Wednesday, January 1, 2014", "updated formatting" ); + + element.calendar( "destroy" ); +}); + +test( "eachDay", function() { + expect( 5 ); + var timestamp, + input = $( "#calendar" ).calendar(), + picker = input.calendar( "widget" ), + firstCell = picker.find( "td[id]:first" ); + + equal( firstCell.find( "a" ).length, 1, "days are selectable by default" ); + timestamp = parseInt( firstCell.find( "a" ).attr( "data-timestamp" ), 10 ); + equal( new Date( timestamp ).getDate(), 1, "first available day is the 1st by default" ); + + // Do not render the 1st of the month + input.calendar( "option", "eachDay", function( day ) { + if ( day.date === 1 ) { + day.render = false; + } + }); + firstCell = picker.find( "td[id]:first" ); + timestamp = parseInt( firstCell.find( "a" ).attr( "data-timestamp" ), 10 ); + equal( new Date( timestamp ).getDate(), 2, "first available day is the 2nd" ); + + // Display the 1st of the month but make it not selectable. + input.calendar( "option", "eachDay", function( day ) { + if ( day.date === 1 ) { + day.selectable = false; + } + }); + firstCell = picker.find( "td[id]:first" ); + equal( firstCell.find( "a" ).length, 0, "the 1st is not selectable" ); + + input.calendar( "option", "eachDay", function( day ) { + if ( day.date === 1 ) { + day.extraClasses = "ui-custom"; + } + }); + ok( picker.find( "td[id]:first a" ).hasClass( "ui-custom" ), "extraClasses applied" ); + + input.calendar( "destroy" ); +}); + +test( "numberOfMonths", function() { + // TODO implement this + expect( 0 ); +}); + +test( "showWeek", function() { + expect( 7 ); + var input = $( "#calendar" ).calendar(), + container = input.calendar( "widget" ); + + equal( container.find( "thead th" ).length, 7, "just 7 days, no column cell" ); + equal( container.find( ".ui-calendar-week-col" ).length, 0, + "no week column cells present" ); + input.calendar( "destroy" ); + + input = $( "#calendar" ).calendar({ showWeek: true }); + container = input.calendar( "widget" ); + equal( container.find( "thead th" ).length, 8, "7 days + a column cell" ); + ok( container.find( "thead th:first" ).is( ".ui-calendar-week-col" ), + "first cell should have ui-datepicker-week-col class name" ); + equal( container.find( ".ui-calendar-week-col" ).length, + container.find( "tr" ).length, "one week cell for each week" ); + input.calendar( "destroy" ); + + input = $( "#calendar" ).calendar(); + container = input.calendar( "widget" ); + equal( container.find( "thead th" ).length, 7, "no week column" ); + input.calendar( "option", "showWeek", true ); + equal( container.find( "thead th" ).length, 8, "supports changing option after init" ); +}); + +/* +// TODO: Rewrite for value option +test( "defaultDate", function() { + expect( 16 ); + var inp = TestHelpers.calendar.init( "#inp" ), + date = new Date(); + inp.val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.calendar.equalsDate(inp.calendar( "getDate" ), date, "Default date null" ); + + // Numeric values + inp.calendar( "option", {defaultDate: -2}). + datepicker( "hide" ).val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setDate(date.getDate() - 2); + TestHelpers.calendar.equalsDate(inp.calendar( "getDate" ), date, "Default date -2" ); + + date = new Date(); + inp.calendar( "option", {defaultDate: 3}). + datepicker( "hide" ).val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setDate(date.getDate() + 3); + TestHelpers.calendar.equalsDate(inp.calendar( "getDate" ), date, "Default date 3" ); + + date = new Date(); + inp.calendar( "option", {defaultDate: 1 / "a"}). + datepicker( "hide" ).val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.calendar.equalsDate(inp.calendar( "getDate" ), date, "Default date NaN" ); + + // String offset values + inp.calendar( "option", {defaultDate: "-1d"}). + datepicker( "hide" ).val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setDate(date.getDate() - 1); + TestHelpers.calendar.equalsDate(inp.calendar( "getDate" ), date, "Default date -1d" ); + inp.calendar( "option", {defaultDate: "+3D"}). + datepicker( "hide" ).val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setDate(date.getDate() + 4); + TestHelpers.calendar.equalsDate(inp.calendar( "getDate" ), date, "Default date +3D" ); + inp.calendar( "option", {defaultDate: " -2 w "}). + datepicker( "hide" ).val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + date = new Date(); + date.setDate(date.getDate() - 14); + TestHelpers.calendar.equalsDate(inp.calendar( "getDate" ), date, "Default date -2 w" ); + inp.calendar( "option", {defaultDate: "+1 W"}). + datepicker( "hide" ).val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setDate(date.getDate() + 21); + TestHelpers.calendar.equalsDate(inp.calendar( "getDate" ), date, "Default date +1 W" ); + inp.calendar( "option", {defaultDate: " -1 m "}). + datepicker( "hide" ).val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + date = TestHelpers.calendar.addMonths(new Date(), -1); + TestHelpers.calendar.equalsDate(inp.calendar( "getDate" ), date, "Default date -1 m" ); + inp.calendar( "option", {defaultDate: "+2M"}). + datepicker( "hide" ).val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + date = TestHelpers.calendar.addMonths(new Date(), 2); + TestHelpers.calendar.equalsDate(inp.calendar( "getDate" ), date, "Default date +2M" ); + inp.calendar( "option", {defaultDate: "-2y"}). + datepicker( "hide" ).val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + date = new Date(); + date.setFullYear(date.getFullYear() - 2); + TestHelpers.calendar.equalsDate(inp.calendar( "getDate" ), date, "Default date -2y" ); + inp.calendar( "option", {defaultDate: "+1 Y "}). + datepicker( "hide" ).val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + date.setFullYear(date.getFullYear() + 3); + TestHelpers.calendar.equalsDate(inp.calendar( "getDate" ), date, "Default date +1 Y" ); + inp.calendar( "option", {defaultDate: "+1M +10d"}). + datepicker( "hide" ).val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + date = TestHelpers.calendar.addMonths(new Date(), 1); + date.setDate(date.getDate() + 10); + TestHelpers.calendar.equalsDate(inp.calendar( "getDate" ), date, "Default date +1M +10d" ); + // String date values + inp.calendar( "option", {defaultDate: "07/04/2007"}). + datepicker( "hide" ).val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + date = new Date(2007, 7 - 1, 4); + TestHelpers.calendar.equalsDate(inp.calendar( "getDate" ), date, "Default date 07/04/2007" ); + inp.calendar( "option", {dateFormat: "yy-mm-dd", defaultDate: "2007-04-02"}). + datepicker( "hide" ).val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + date = new Date(2007, 4 - 1, 2); + TestHelpers.calendar.equalsDate(inp.calendar( "getDate" ), date, "Default date 2007-04-02" ); + // Date value + date = new Date(2007, 1 - 1, 26); + inp.calendar( "option", {dateFormat: "mm/dd/yy", defaultDate: date}). + datepicker( "hide" ).val( "" ).calendar( "show" ). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.calendar.equalsDate(inp.calendar( "getDate" ), date, "Default date 01/26/2007" ); + }); + */ + +test( "min / max", function() { + expect( 0 ); + + /* + // TODO CTRL + PgUp / PgDn is not implemented yet, see wiki + var date, + inp = TestHelpers.calendar.init( "#calendar" ), + dp = inp.calendar( "widget" ), + lastYear = new Date( 2007, 6 - 1, 4 ), + nextYear = new Date( 2009, 6 - 1, 4 ), + minDate = new Date( 2008, 2 - 1, 29 ), + maxDate = new Date( 2008, 12 - 1, 7 ); + + inp.val( "06/04/2008" ).calendar( "refresh" ).calendar( "open" ); + inp.simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP } ). + simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( inp.calendar( "valueAsDate" ), lastYear, "Min/max - null, null - ctrl+pgup" ); + + inp.val( "06/04/2008" ).calendar( "refresh" ).calendar( "open" ); + inp.simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN } ). + simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( inp.calendar( "valueAsDate" ), nextYear, "Min/max - null, null - ctrl+pgdn" ); + + inp.calendar( "option", { min: minDate } ). + datepicker( "close" ).val( "06/04/2008" ).calendar( "refresh" ).calendar( "open" ); + inp.simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP } ). + simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate( inp.calendar( "valueAsDate" ), minDate, "Min/max - 02/29/2008, null - ctrl+pgup" ); + + inp.val( "06/04/2008" ).calendar( "refresh" ).calendar( "open" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.calendar.equalsDate(inp.calendar( "getDate" ), nextYear, "Min/max - 02/29/2008, null - ctrl+pgdn" ); + + inp.calendar( "option", { max: maxDate } ). + datepicker( "hide" ).val( "06/04/2008" ).calendar( "open" ); + inp.simulate( "keydown", { ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP } ). + simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + TestHelpers.calendar.equalsDate(inp.calendar( "getDate" ), minDate, "Min/max - 02/29/2008, 12/07/2008 - ctrl+pgup" ); + + inp.val( "06/04/2008" ).calendar( "open" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.calendar.equalsDate(inp.calendar( "valueAsDate" ), maxDate, "Min/max - 02/29/2008, 12/07/2008 - ctrl+pgdn" ); + + inp.calendar( "option", {minDate: null}). + datepicker( "hide" ).val( "06/04/2008" ).calendar( "open" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.calendar.equalsDate(inp.calendar( "valueAsDate" ), lastYear, "Min/max - null, 12/07/2008 - ctrl+pgup" ); + + inp.val( "06/04/2008" ).calendar( "open" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.calendar.equalsDate(inp.calendar( "valueAsDate" ), maxDate, "Min/max - null, 12/07/2008 - ctrl+pgdn" ); + + // Relative dates + date = new Date(); + date.setDate(date.getDate() - 7); + inp.calendar( "option", {minDate: "-1w", maxDate: "+1 M +10 D "}). + datepicker( "hide" ).val( "" ).calendar( "open" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.calendar.equalsDate(inp.calendar( "valueAsDate" ), date, "Min/max - -1w, +1 M +10 D - ctrl+pgup" ); + + date = TestHelpers.calendar.addMonths(new Date(), 1); + date.setDate(date.getDate() + 10); + inp.val( "" ).calendar( "show" ); + inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}). + simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); + TestHelpers.calendar.equalsDate(inp.calendar( "valueAsDate" ), date, "Min/max - -1w, +1 M +10 D - ctrl+pgdn" ); + */ +}); + +/* +// TODO: Move this to $.date, Globalize or calendar widget +test( "daylightSaving", function() { + expect( 25 ); + var inp = TestHelpers.calendar.init( "#inp" ), + dp = $( "#ui-datepicker-div" ); + ok(true, "Daylight saving - " + new Date()); + // Australia, Sydney - AM change, southern hemisphere + inp.val( "04/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(6) a", dp).simulate( "click" ); + equal(inp.val(), "04/05/2008", "Daylight saving - Australia 04/05/2008" ); + inp.val( "04/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(7) a", dp).simulate( "click" ); + equal(inp.val(), "04/06/2008", "Daylight saving - Australia 04/06/2008" ); + inp.val( "04/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(8) a", dp).simulate( "click" ); + equal(inp.val(), "04/07/2008", "Daylight saving - Australia 04/07/2008" ); + inp.val( "10/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(6) a", dp).simulate( "click" ); + equal(inp.val(), "10/04/2008", "Daylight saving - Australia 10/04/2008" ); + inp.val( "10/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(7) a", dp).simulate( "click" ); + equal(inp.val(), "10/05/2008", "Daylight saving - Australia 10/05/2008" ); + inp.val( "10/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(8) a", dp).simulate( "click" ); + equal(inp.val(), "10/06/2008", "Daylight saving - Australia 10/06/2008" ); + // Brasil, Brasilia - midnight change, southern hemisphere + inp.val( "02/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(20) a", dp).simulate( "click" ); + equal(inp.val(), "02/16/2008", "Daylight saving - Brasil 02/16/2008" ); + inp.val( "02/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(21) a", dp).simulate( "click" ); + equal(inp.val(), "02/17/2008", "Daylight saving - Brasil 02/17/2008" ); + inp.val( "02/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(22) a", dp).simulate( "click" ); + equal(inp.val(), "02/18/2008", "Daylight saving - Brasil 02/18/2008" ); + inp.val( "10/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(13) a", dp).simulate( "click" ); + equal(inp.val(), "10/11/2008", "Daylight saving - Brasil 10/11/2008" ); + inp.val( "10/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(14) a", dp).simulate( "click" ); + equal(inp.val(), "10/12/2008", "Daylight saving - Brasil 10/12/2008" ); + inp.val( "10/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(15) a", dp).simulate( "click" ); + equal(inp.val(), "10/13/2008", "Daylight saving - Brasil 10/13/2008" ); + // Lebanon, Beirut - midnight change, northern hemisphere + inp.val( "03/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(34) a", dp).simulate( "click" ); + equal(inp.val(), "03/29/2008", "Daylight saving - Lebanon 03/29/2008" ); + inp.val( "03/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(35) a", dp).simulate( "click" ); + equal(inp.val(), "03/30/2008", "Daylight saving - Lebanon 03/30/2008" ); + inp.val( "03/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(36) a", dp).simulate( "click" ); + equal(inp.val(), "03/31/2008", "Daylight saving - Lebanon 03/31/2008" ); + inp.val( "10/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(27) a", dp).simulate( "click" ); + equal(inp.val(), "10/25/2008", "Daylight saving - Lebanon 10/25/2008" ); + inp.val( "10/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(28) a", dp).simulate( "click" ); + equal(inp.val(), "10/26/2008", "Daylight saving - Lebanon 10/26/2008" ); + inp.val( "10/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(29) a", dp).simulate( "click" ); + equal(inp.val(), "10/27/2008", "Daylight saving - Lebanon 10/27/2008" ); + // US, Eastern - AM change, northern hemisphere + inp.val( "03/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(13) a", dp).simulate( "click" ); + equal(inp.val(), "03/08/2008", "Daylight saving - US 03/08/2008" ); + inp.val( "03/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(14) a", dp).simulate( "click" ); + equal(inp.val(), "03/09/2008", "Daylight saving - US 03/09/2008" ); + inp.val( "03/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(15) a", dp).simulate( "click" ); + equal(inp.val(), "03/10/2008", "Daylight saving - US 03/10/2008" ); + inp.val( "11/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(6) a", dp).simulate( "click" ); + equal(inp.val(), "11/01/2008", "Daylight saving - US 11/01/2008" ); + inp.val( "11/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(7) a", dp).simulate( "click" ); + equal(inp.val(), "11/02/2008", "Daylight saving - US 11/02/2008" ); + inp.val( "11/01/2008" ).calendar( "show" ); + $( ".ui-calendar-calendar td:eq(8) a", dp).simulate( "click" ); + equal(inp.val(), "11/03/2008", "Daylight saving - US 11/03/2008" ); + }); + */ + +})(jQuery); diff --git a/tests/unit/calendar/calendar_test_helpers.js b/tests/unit/calendar/calendar_test_helpers.js new file mode 100644 index 00000000000..7fb80bb58e9 --- /dev/null +++ b/tests/unit/calendar/calendar_test_helpers.js @@ -0,0 +1,22 @@ +TestHelpers.calendar = { + addMonths: function( date, offset ) { + var maxDay = 32 - new Date( date.getFullYear(), date.getMonth() + offset, 32 ).getDate(); + date.setDate( Math.min( date.getDate(), maxDay ) ); + date.setMonth( date.getMonth() + offset ); + return date; + }, + equalsDate: function( d1, d2, message ) { + if ( !d1 || !d2 ) { + ok( false, message + " - missing date" ); + return; + } + d1 = new Date( d1.getFullYear(), d1.getMonth(), d1.getDate() ); + d2 = new Date( d2.getFullYear(), d2.getMonth(), d2.getDate() ); + equal( d1.toString(), d2.toString(), message ); + }, + focusGrid: function( element ) { + element.find( "table:tabbable" ).simulate( "focus" ); + + return $( ":focus" ); + } +}; \ No newline at end of file diff --git a/tests/unit/datepicker/datepicker.html b/tests/unit/datepicker/datepicker.html index f55dd97900c..c43eee8d959 100644 --- a/tests/unit/datepicker/datepicker.html +++ b/tests/unit/datepicker/datepicker.html @@ -14,11 +14,12 @@ @@ -25,7 +32,7 @@
    -

    Restrict the range of selectable dates with the minDate and maxDate options. Set the beginning and end dates as actual dates (new Date(2009, 1 - 1, 26)), as a numeric offset from today (-20), or as a string of periods and units ('+1M +10D'). For the last, use 'D' for days, 'W' for weeks, 'M' for months, or 'Y' for years.

    +

    Restrict the range of selectable dates with the min and max options. Set the beginning and end dates as actual dates (new Date(2009, 1 - 1, 26)).

    diff --git a/tests/unit/calendar/calendar_common.js b/tests/unit/calendar/calendar_common.js index f3a01171ba5..49f8b5f05f6 100644 --- a/tests/unit/calendar/calendar_common.js +++ b/tests/unit/calendar/calendar_common.js @@ -3,6 +3,8 @@ TestHelpers.commonWidgetTests( "calendar", { dateFormat: { date: "short" }, disabled: false, eachDay: $.noop, + max: null, + min: null, numberOfMonths: 1, showWeek: false, value: null, diff --git a/tests/unit/calendar/calendar_methods.js b/tests/unit/calendar/calendar_methods.js index ce53be1c119..53c8514b984 100644 --- a/tests/unit/calendar/calendar_methods.js +++ b/tests/unit/calendar/calendar_methods.js @@ -53,9 +53,10 @@ test( "value", function() { }); test( "valueAsDate", function() { - expect( 5 ); + expect( 12 ); - var element = $( "#calendar" ).calendar(), + var minDate, maxDate, dateAndTimeToSet, dateAndTimeClone, + element = $( "#calendar" ).calendar(), date1 = new Date( 2008, 6 - 1, 4 ), date2 = new Date(); @@ -73,6 +74,36 @@ test( "valueAsDate", function() { element.calendar( "valueAsDate", date1, date2 ); TestHelpers.calendar.equalsDate(element.calendar( "valueAsDate" ), date1, "Set date - two dates" ); + + // With minimum/maximum + element = $( "#calendar" ).calendar(); + date1 = new Date( 2008, 1 - 1, 4 ); + date2 = new Date( 2008, 6 - 1, 4 ); + minDate = new Date( 2008, 2 - 1, 29 ); + maxDate = new Date( 2008, 3 - 1, 28 ); + + element.calendar( "option", { min: minDate } ).calendar( "valueAsDate", date2 ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), date2, "Set date min/max - value > min" ); + + element.calendar( "valueAsDate", date1 ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), date2, "Set date min/max - value < min" ); + + element.calendar( "option", { max: maxDate, min: null } ).calendar( "valueAsDate", date1 ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), date1, "Set date min/max - value < max" ); + + element.calendar( "valueAsDate", date2 ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), date1, "Set date min/max - value > max" ); + + element.calendar( "option", { min: minDate } ).calendar( "valueAsDate", date1 ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), date1, "Set date min/max - value < min" ); + + element.calendar( "valueAsDate", date2 ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), date1, "Set date min/max - value > max" ); + + dateAndTimeToSet = new Date( 2008, 3 - 1, 28, 1, 11, 0 ); + dateAndTimeClone = new Date( 2008, 3 - 1, 28, 1, 11, 0 ); + element.calendar( "valueAsDate", dateAndTimeToSet ); + equal( dateAndTimeToSet.getTime(), dateAndTimeClone.getTime(), "Date object passed should not be changed by valueAsDate" ); }); })( jQuery ); diff --git a/tests/unit/calendar/calendar_options.js b/tests/unit/calendar/calendar_options.js index ea8483bbcd1..a5e08226653 100644 --- a/tests/unit/calendar/calendar_options.js +++ b/tests/unit/calendar/calendar_options.js @@ -189,7 +189,7 @@ test( "defaultDate", function() { */ test( "min / max", function() { - expect( 0 ); + expect( 7 ); /* // TODO CTRL + PgUp / PgDn is not implemented yet, see wiki @@ -260,7 +260,47 @@ test( "min / max", function() { simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); TestHelpers.calendar.equalsDate(inp.calendar( "valueAsDate" ), date, "Min/max - -1w, +1 M +10 D - ctrl+pgdn" ); */ -}); + + // With existing date + var element = $( "#calendar" ).calendar(), + minDate = new Date( 2008, 2 - 1, 29 ), + maxDate = new Date( 2008, 12 - 1, 7 ); + + element + .calendar( "option", { min: minDate } ) + .calendar( "value", "6/4/08" ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value > min" ); + + element + .calendar( "option", { min: minDate } ) + .calendar( "value", "1/4/08" ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value < min" ); + + element + .calendar( "option", { min: null } ) + .calendar( "value", "6/4/08" ) + .calendar( "option", { max: maxDate } ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value < max" ); + + element + .calendar( "option", { max: maxDate } ) + .calendar( "value", "1/4/09" ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - setDate > max" ); + + element + .calendar( "option", { min: minDate, max: maxDate } ) + .calendar( "value", "1/4/08" ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value < min" ); + + element + .calendar( "option", { min: minDate, max: maxDate } ) + .calendar( "value", "6/4/08" ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value > min, < max" ); + + element + .calendar( "option", { min: minDate, max: maxDate } ) + .calendar( "value", "1/4/09" ); + TestHelpers.calendar.equalsDate( element.calendar( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value > max" );}); /* // TODO: Move this to $.date, Globalize or calendar widget diff --git a/tests/unit/datepicker/datepicker_common.js b/tests/unit/datepicker/datepicker_common.js index c7cce60cb27..4a2ece05da8 100644 --- a/tests/unit/datepicker/datepicker_common.js +++ b/tests/unit/datepicker/datepicker_common.js @@ -4,6 +4,8 @@ TestHelpers.commonWidgetTests( "datepicker", { dateFormat: { date: "short" }, disabled: false, eachDay: $.noop, + max: null, + min: null, numberOfMonths: 1, position: { my: "left top", diff --git a/tests/unit/datepicker/datepicker_methods.js b/tests/unit/datepicker/datepicker_methods.js index 91417196c4f..533df4bcb92 100644 --- a/tests/unit/datepicker/datepicker_methods.js +++ b/tests/unit/datepicker/datepicker_methods.js @@ -70,11 +70,13 @@ test( "value", function() { }); test( "valueAsDate", function() { - expect( 6 ); + expect( 13 ); - var input = TestHelpers.datepicker.init( "#datepicker" ), + var minDate, maxDate, dateAndTimeToSet, dateAndTimeClone, + input = TestHelpers.datepicker.init( "#datepicker" ), picker = input.datepicker( "widget" ), - date1 = new Date( 2008, 6 - 1, 4 ); + date1 = new Date( 2008, 6 - 1, 4 ), + date2 = new Date(); input.datepicker( "valueAsDate", new Date( 2014, 0, 1 ) ); equal( input.val(), "1/1/14", "Input's value set" ); @@ -90,6 +92,36 @@ test( "valueAsDate", function() { ok(input.datepicker( "valueAsDate" ) === null, "Set date - default" ); input.datepicker( "valueAsDate", date1 ); TestHelpers.datepicker.equalsDate(input.datepicker( "valueAsDate" ), date1, "Set date - 2008-06-04" ); + + // With minimum/maximum + input = TestHelpers.datepicker.init( "#datepicker" ); + date1 = new Date( 2008, 1 - 1, 4 ); + date2 = new Date( 2008, 6 - 1, 4 ); + minDate = new Date( 2008, 2 - 1, 29 ); + maxDate = new Date( 2008, 3 - 1, 28 ); + + input.val( "" ).datepicker( "option", { min: minDate } ).datepicker( "valueAsDate", date2 ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date2, "Set date min/max - value > min" ); + + input.datepicker( "valueAsDate", date1 ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date2, "Set date min/max - value < min" ); + + input.val( "" ).datepicker( "option", { max: maxDate, min: null } ).datepicker( "valueAsDate", date1 ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date1, "Set date min/max - value < max" ); + + input.datepicker( "valueAsDate", date2 ); + TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date1, "Set date min/max - value > max" ); + + input.val( "" ).datepicker( "option", { min: minDate } ).datepicker( "valueAsDate", date1 ); + ok( input.datepicker( "valueAsDate" ) === null, "Set date min/max - value < min" ); + + input.datepicker( "valueAsDate", date2 ); + ok( input.datepicker( "valueAsDate" ) === null, "Set date min/max - value > max" ); + + dateAndTimeToSet = new Date( 2008, 3 - 1, 28, 1, 11, 0 ); + dateAndTimeClone = new Date( 2008, 3 - 1, 28, 1, 11, 0 ); + input.datepicker( "valueAsDate", dateAndTimeToSet ); + equal( dateAndTimeToSet.getTime(), dateAndTimeClone.getTime(), "Date object passed should not be changed by valueAsDate" ); }); test( "isValid", function() { diff --git a/tests/unit/datepicker/datepicker_options.js b/tests/unit/datepicker/datepicker_options.js index b1842d9cac0..17d59339a3b 100644 --- a/tests/unit/datepicker/datepicker_options.js +++ b/tests/unit/datepicker/datepicker_options.js @@ -149,6 +149,42 @@ test( "showWeek", function() { input.datepicker( "option", "showWeek", true ); equal( container.find( "thead th" ).length, 8, "supports changing option after init" ); }); + +test( "min / max", function() { + expect( 14 ); + + var inp = TestHelpers.datepicker.init( "#datepicker" ), + minDate = new Date( 2008, 2 - 1, 29 ), + maxDate = new Date( 2008, 12 - 1, 7 ); + + inp.val( "6/4/08" ).datepicker( "option", { min: minDate } ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value > min" ); + ok( inp.datepicker( "isValid" ) ); + + inp.datepicker( "option", { min: null } ).val( "1/4/08" ).datepicker( "option", { min: minDate } ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2008, 1 - 1, 4 ), "Min/max - value < min" ); + ok( !inp.datepicker( "isValid" ) ); + + inp.datepicker( "option", { min: null } ).val( "6/4/08" ).datepicker( "option", { max: maxDate } ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value < max" ); + ok( inp.datepicker( "isValid" ) ); + + inp.datepicker( "option", { max: null } ).val( "1/4/09" ).datepicker( "option", { max: maxDate } ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2009, 1 - 1, 4 ), "Min/max - setDate > max" ); + ok( !inp.datepicker( "isValid" ) ); + + inp.datepicker( "option", { max: null } ).val( "1/4/08" ).datepicker( "option", { min: minDate, max: maxDate } ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2008, 1 - 1, 4 ), "Min/max - value < min" ); + ok( !inp.datepicker( "isValid" ) ); + + inp.datepicker( "option", { max: null } ).val( "6/4/08" ).datepicker( "option", { min: minDate, max: maxDate } ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value > min, < max" ); + ok( inp.datepicker( "isValid" ) ); + + inp.datepicker( "option", { max: null } ).val( "1/4/09" ).datepicker( "option", { min: minDate, max: maxDate } ); + TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2009, 1 - 1, 4 ), "Min/max - value > max" ); + ok( !inp.datepicker( "isValid" ) ); +}); test( "Ticket 7602: Stop datepicker from appearing with beforeOpen event handler", function() { expect( 3 ); diff --git a/ui/calendar.js b/ui/calendar.js index 32a6aa1b5dc..9ec84e544ac 100644 --- a/ui/calendar.js +++ b/ui/calendar.js @@ -32,6 +32,8 @@ return $.widget( "ui.calendar", { dateFormat: { date: "short" }, // TODO review eachDay: $.noop, + max: null, + min: null, numberOfMonths: 1, showWeek: false, value: null, @@ -308,30 +310,33 @@ return $.widget( "ui.calendar", { }, _buildDayCell: function( day ) { - var contents = "", - idAttribute = day.render ? ( "id=" + this.id + "-" + day.date ) : "", - ariaSelectedAttribute = "aria-selected=" + ( day.current ? "true" : "false" ), - ariaDisabledAttribute = day.selectable ? "" : "aria-disabled=true"; + var content = "", + attributes = [ + "role='gridcell'", + "aria-selected='" + day.current ? true : false + "'" + ], + selectable = ( day.selectable && this._isValid( new Date( day.timestamp ) ) ); if ( day.render ) { - if ( day.selectable ) { - contents = this._buildDayLink( day ); - } else { - contents = this._buildDayDisplay( day ); + attributes.push( "id='" + this.id + "-" + day.date + "'" ); + + if ( !selectable ) { + attributes.push( "aria-disabled='true'" ); + attributes.push( "class='ui-state-disabled'" ); } + + content = this._buildDayElement( day, selectable ); } - return "" + - contents + - ""; + return "" + content + ""; }, - _buildDayLink: function( day ) { - var link, - classes = [ "ui-state-default" ], - labels = Globalize.translate( "datepicker" ); + _buildDayElement: function( day, selectable ) { + var classes = [ "ui-state-default" ], + labels = Globalize.translate( "datepicker" ), + content = ""; - if ( day === this.date ) { + if ( day === this.date && selectable ) { classes.push( "ui-state-focus" ); } if ( day.current ) { @@ -340,33 +345,23 @@ return $.widget( "ui.calendar", { if ( day.today ) { classes.push( "ui-state-highlight" ); } + // ToDo Explain and document this if ( day.extraClasses ) { classes.push( day.extraClasses.split( " " ) ); } - link = "" + - day.date + ""; - if ( day.today ) { - link += ", " + labels.currentText + ""; + classes = " class='" + classes.join( " " ) + "'"; + if ( selectable ) { + content = "" + day.date + ""; + } else { + content = "" + day.date + ""; } - return link; - }, - - _buildDayDisplay: function( day ) { - var classes = []; - - if ( day.current ) { - classes.push( "ui-state-active" ); - } if ( day.today ) { - classes.push( "ui-state-highlight" ); - } - if ( day.extraClasses ) { - classes.push( day.extraClasses.split( " " ) ); + content += ", " + labels.currentText + ""; } - return "" + day.date + ""; + return content; }, _buildButtons: function() { @@ -418,7 +413,6 @@ return $.widget( "ui.calendar", { _select: function( event, time ) { this._setOption( "value", new Date( time ) ); this._trigger( "select", event, { - // TODO replace with value option to initialise and read date: this.value() }); }, @@ -439,6 +433,26 @@ return $.widget( "ui.calendar", { } }, + _isValid: function( value ) { + if ( $.type( value ) !== "date" ) { + return false; + } + + if ( $.type( this.options.max ) === "date" ) { + if ( value > this.options.max ) { + return false; + } + } + + if ( $.type( this.options.min ) === "date" ) { + if ( value < this.options.min ) { + return false; + } + } + + return true; + }, + _destroy: function() { this.element .removeClass( "ui-calendar ui-widget ui-widget-content ui-helper-clearfix ui-corner-all ui-calendar-multi" ) @@ -455,21 +469,29 @@ return $.widget( "ui.calendar", { _setOption: function( key, value ) { if ( key === "value" ) { - if ( value instanceof Date ) { + if ( this._isValid( value ) ) { this.date.setTime( value.getTime() ).select(); this.refresh(); } } + if ( key === "max" || key === "min" ) { + if ( $.type( value ) === "date" || value === null ) { + this._super( key, value ); + this.refresh(); + } + return; + } + this._super( key, value ); if ( key === "eachDay" ) { - this.date.eachDay = this.options.eachDay; + this.date.eachDay = value; this.refresh(); } if ( key === "dateFormat" ) { - this.date.setFormat( this.options.dateFormat ); + this.date.setFormat( value ); } if ( key === "showWeek" ) { diff --git a/ui/datepicker.js b/ui/datepicker.js index f4c77b71b99..e0bf8ffc786 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -35,6 +35,8 @@ $.widget( "ui.datepicker", { appendTo: null, dateFormat: { date: "short" }, eachDay: $.noop, + max: null, + min: null, numberOfMonths: 1, position: { my: "left top", @@ -52,9 +54,23 @@ $.widget( "ui.datepicker", { }, _create: function() { + if ( $.type( this.options.max ) === "string" ) { + this.options.max = Globalize.parseDate( this.options.max , { pattern: "yyyy-MM-dd" } ); + } + if ( $.type( this.options.min ) === "string" ) { + this.options.min = Globalize.parseDate( this.options.min , { pattern: "yyyy-MM-dd" } ); + } + this._createCalendar(); }, + _getCreateOptions: function() { + return { + max: this.element.attr( "max" ), + min: this.element.attr( "min" ) + }; + }, + _createCalendar: function() { var that = this; @@ -67,6 +83,8 @@ $.widget( "ui.datepicker", { .calendar({ dateFormat: this.options.dateFormat, eachDay: this.options.eachDay, + max: this.options.max, + min: this.options.min, numberOfMonths: this.options.numberOfMonths, showWeek: this.options.showWeek, value: this._getParsedValue(), @@ -275,11 +293,7 @@ $.widget( "ui.datepicker", { value: function( value ) { if ( arguments.length ) { - var date = Globalize.parseDate( value, this.options.dateFormat ); - if ( $.type( date ) === "date" ) { - this.valueAsDate( date ); - this.element.val( value ); - } + this.valueAsDate( Globalize.parseDate( value , this.options.dateFormat ) ); } else { return ( this._getParsedValue() !== null ) ? this.element.val() : null; } @@ -287,7 +301,7 @@ $.widget( "ui.datepicker", { valueAsDate: function( value ) { if ( arguments.length ) { - if ( $.type( value ) === "date" ) { + if ( this.calendarInstance._isValid( value ) ) { this.calendarInstance.valueAsDate( value ); this.element.val( Globalize.format( value, this.options.dateFormat ) ); } @@ -297,7 +311,7 @@ $.widget( "ui.datepicker", { }, isValid: function() { - return this._getParsedValue() !== null; + return this.calendarInstance._isValid( this._getParsedValue() ); }, _destroy: function() { @@ -313,7 +327,7 @@ $.widget( "ui.datepicker", { }, _getParsedValue: function() { - return Globalize.parseDate( this.element.val(), this.options.dateFormat ); + return Globalize.parseDate( this.element.val() , this.options.dateFormat ); }, _setOption: function( key, value ) { @@ -336,5 +350,4 @@ $.widget( "ui.datepicker", { } } }); - })); From 69736ca2dfa45eb4f895dfbcab76d0b523366fa8 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Wed, 4 Jun 2014 23:40:39 +0200 Subject: [PATCH 04/26] Calendar: Remove select callback reference --- ui/calendar.js | 4 +--- ui/datepicker.js | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/ui/calendar.js b/ui/calendar.js index 9ec84e544ac..c694a68097b 100644 --- a/ui/calendar.js +++ b/ui/calendar.js @@ -412,9 +412,7 @@ return $.widget( "ui.calendar", { _select: function( event, time ) { this._setOption( "value", new Date( time ) ); - this._trigger( "select", event, { - date: this.value() - }); + this._trigger( "select", event ); }, value: function( value ) { diff --git a/ui/datepicker.js b/ui/datepicker.js index e0bf8ffc786..1276adb5868 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -88,11 +88,11 @@ $.widget( "ui.datepicker", { numberOfMonths: this.options.numberOfMonths, showWeek: this.options.showWeek, value: this._getParsedValue(), - select: function( event, data ) { - that.element.val( data.date ); + select: function( event ) { + that.element.val( that.calendarInstance.value() ); that.close(); that._focusTrigger(); - that._trigger( "select", event, data); + that._trigger( "select", event ); } }) .calendar( "instance" ); From a6bc8b9a68f1219dc24c72695154091c219ab9bc Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Thu, 5 Jun 2014 02:39:46 +0200 Subject: [PATCH 05/26] Datepicker: Code clean up for events --- ui/datepicker.js | 202 ++++++++++++++++++++++++----------------------- 1 file changed, 103 insertions(+), 99 deletions(-) diff --git a/ui/datepicker.js b/ui/datepicker.js index 1276adb5868..caef1366f49 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -62,6 +62,10 @@ $.widget( "ui.datepicker", { } this._createCalendar(); + + this._on( this._inputEvents ); + this._on( this.calendar, this._calendarEvents ); + this._on( this.document, this._documentEvents ); }, _getCreateOptions: function() { @@ -102,116 +106,116 @@ $.widget( "ui.datepicker", { this.element .attr( "aria-haspopup", "true" ) .attr( "aria-owns", this.calendar.attr( "id" ) ); + }, - this._on({ - keydown: function( event ) { - switch ( event.keyCode ) { - case $.ui.keyCode.TAB: - // Waiting for close() will make popup hide too late, which breaks tab key behavior - this.calendar.hide(); + _inputEvents: { + keydown: function( event ) { + switch ( event.keyCode ) { + case $.ui.keyCode.TAB: + // Waiting for close() will make popup hide too late, which breaks tab key behavior + this.calendar.hide(); + this.close( event ); + break; + case $.ui.keyCode.ESCAPE: + if ( this.isOpen ) { this.close( event ); - break; - case $.ui.keyCode.ESCAPE: + } + break; + case $.ui.keyCode.ENTER: + this.calendarInstance._handleKeydown( event ); + break; + case $.ui.keyCode.DOWN: + case $.ui.keyCode.UP: + clearTimeout( this.closeTimer ); + this._delay( function() { + this.open( event ); + this.calendarInstance.grid.focus( 1 ); + }, 1 ); + break; + case $.ui.keyCode.HOME: + if ( event.ctrlKey ) { + this.valueAsDate( new Date() ); + event.preventDefault(); if ( this.isOpen ) { - this.close( event ); - } - break; - case $.ui.keyCode.ENTER: - this.calendarInstance._handleKeydown( event ); - break; - case $.ui.keyCode.DOWN: - case $.ui.keyCode.UP: - clearTimeout( this.closeTimer ); - this._delay( function() { - this.open( event ); - this.calendarInstance.grid.focus( 1 ); - }, 1 ); - break; - case $.ui.keyCode.HOME: - if ( event.ctrlKey ) { - this.valueAsDate( new Date() ); - event.preventDefault(); - if ( this.isOpen ) { this.calendarInstance.refresh(); - } else { - this.open( event ); - } - } - break; - // TODO this is not in specs, keep? - case $.ui.keyCode.END: - if ( event.ctrlKey ) { - this.element.val( "" ); - event.preventDefault(); - if ( this.isOpen ) { - this.close( event ); - } - } - break; - } - }, - keyup: function() { - if ( this.isValid() ) { - this.valueAsDate( this._getParsedValue() ); - } - }, - mousedown: function( event ) { - if ( this.isOpen ) { - suppressExpandOnFocus = true; - this.close(); - return; - } - this.open( event ); - clearTimeout( this.closeTimer ); - }, - focus: function( event ) { - if ( !suppressExpandOnFocus ) { - this._delay( function() { - if ( !this.isOpen ) { + } else { this.open( event ); } - }, 1); - } + } + break; + // TODO this is not in specs, keep? + case $.ui.keyCode.END: + if ( event.ctrlKey ) { + this.element.val( "" ); + event.preventDefault(); + if ( this.isOpen ) { + this.close( event ); + } + } + break; + } + }, + keyup: function() { + if ( this.isValid() ) { + this.valueAsDate( this._getParsedValue() ); + } + }, + mousedown: function( event ) { + if ( this.isOpen ) { + suppressExpandOnFocus = true; + this.close(); + return; + } + this.open( event ); + clearTimeout( this.closeTimer ); + }, + focus: function( event ) { + if ( !suppressExpandOnFocus ) { this._delay( function() { - suppressExpandOnFocus = false; - }, 100 ); - }, - blur: function() { - suppressExpandOnFocus = false; + if ( !this.isOpen ) { + this.open( event ); + } + }, 1); } - }); - - this._on( this.calendar, { - focusout: function( event ) { - // use a timer to allow click to clear it and letting that - // handle the closing instead of opening again - // also allows tabbing inside the calendar without it closing - this.closeTimer = this._delay( function() { - this.close( event ); - }, 150 ); - }, - focusin: function() { - clearTimeout( this.closeTimer ); - }, - mouseup: function() { - clearTimeout( this.closeTimer ); - }, - // TODO on TAB (or shift TAB), make sure it ends up on something useful in DOM order - keyup: function( event ) { - if ( event.keyCode === $.ui.keyCode.ESCAPE && this.calendar.is( ":visible" ) ) { - this.close( event ); - this._focusTrigger(); - } + this._delay( function() { + suppressExpandOnFocus = false; + }, 100 ); + }, + blur: function() { + suppressExpandOnFocus = false; + } + }, + + _calendarEvents: { + focusout: function( event ) { + // use a timer to allow click to clear it and letting that + // handle the closing instead of opening again + // also allows tabbing inside the calendar without it closing + this.closeTimer = this._delay( function() { + this.close( event ); + }, 150 ); + }, + focusin: function() { + clearTimeout( this.closeTimer ); + }, + mouseup: function() { + clearTimeout( this.closeTimer ); + }, + // TODO on TAB (or shift TAB), make sure it ends up on something useful in DOM order + keyup: function( event ) { + if ( event.keyCode === $.ui.keyCode.ESCAPE && this.calendar.is( ":visible" ) ) { + this.close( event ); + this._focusTrigger(); } - }); + } + }, - this._on( this.document, { - click: function( event ) { - if ( this.isOpen && !$( event.target ).closest( this.element.add( this.calendar ) ).length ) { - this.close( event ); - } + _documentEvents: { + click: function( event ) { + if ( this.isOpen && !$( event.target ).closest( this.element.add( this.calendar ) ).length ) { + this.close( event ); } - }); + } }, _appendTo: function() { From b7f5d1379ec4c9e43b223a3e971d00706dc06faf Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Thu, 5 Jun 2014 20:32:27 +0200 Subject: [PATCH 06/26] Datepicker: Remove unwanted keyboard shortcut Remove shortcut for closing the calendar and erasing the date (CTRL+END) --- ui/datepicker.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/ui/datepicker.js b/ui/datepicker.js index caef1366f49..81f9d99315f 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -143,16 +143,6 @@ $.widget( "ui.datepicker", { } } break; - // TODO this is not in specs, keep? - case $.ui.keyCode.END: - if ( event.ctrlKey ) { - this.element.val( "" ); - event.preventDefault(); - if ( this.isOpen ) { - this.close( event ); - } - } - break; } }, keyup: function() { From 6fc0e99099245c25c775a064ab79662717d014f1 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Thu, 12 Jun 2014 14:36:45 +0200 Subject: [PATCH 07/26] Datepicker: Simplify usage of calendar options and avoid duplications --- ui/datepicker.js | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/ui/datepicker.js b/ui/datepicker.js index 81f9d99315f..bca9e600d3e 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -26,23 +26,19 @@ } }(function( $ ) { -// TODO move this to the instance -var suppressExpandOnFocus = false; +var widget, + calendarOptions = [ "dateFormat", "eachDay", "max", "min", "numberOfMonths", "showWeek" ], + // TODO move this to the instance? + suppressExpandOnFocus = false; -$.widget( "ui.datepicker", { +widget = $.widget( "ui.datepicker", { version: "@VERSION", options: { appendTo: null, - dateFormat: { date: "short" }, - eachDay: $.noop, - max: null, - min: null, - numberOfMonths: 1, position: { my: "left top", at: "left bottom" }, - showWeek: false, show: true, hide: true, @@ -84,13 +80,7 @@ $.widget( "ui.datepicker", { // Initialize calendar widget this.calendarInstance = this.calendar - .calendar({ - dateFormat: this.options.dateFormat, - eachDay: this.options.eachDay, - max: this.options.max, - min: this.options.min, - numberOfMonths: this.options.numberOfMonths, - showWeek: this.options.showWeek, + .calendar( $.extend( {}, this.options, { value: this._getParsedValue(), select: function( event ) { that.element.val( that.calendarInstance.value() ); @@ -98,7 +88,7 @@ $.widget( "ui.datepicker", { that._focusTrigger(); that._trigger( "select", event ); } - }) + }) ) .calendar( "instance" ); this._setHiddenPicker(); @@ -327,7 +317,7 @@ $.widget( "ui.datepicker", { _setOption: function( key, value ) { this._super( key, value ); - if ( $.inArray( key, [ "showWeek", "numberOfMonths", "dateFormat", "eachDay", "min", "max" ] ) !== -1 ) { + if ( $.inArray( key, calendarOptions ) !== -1 ) { this.calendarInstance._setOption( key, value ); } @@ -344,4 +334,11 @@ $.widget( "ui.datepicker", { } } }); + +$.each( calendarOptions, function( index, option ) { + $.ui.datepicker.prototype.options[ option ] = $.ui.calendar.prototype.options[ option ]; +}); + +return widget; + })); From d8cff5e7158fe3972e178d2ebb0cfcf74f866754 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Tue, 17 Jun 2014 00:46:08 +0200 Subject: [PATCH 08/26] Datepicker tests: Add open and close unit tests --- tests/unit/datepicker/datepicker_events.js | 43 ++++++++++++++++++--- tests/unit/datepicker/datepicker_methods.js | 23 +++++++---- 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/tests/unit/datepicker/datepicker_events.js b/tests/unit/datepicker/datepicker_events.js index 9ee254e5351..ac3d41c13d6 100644 --- a/tests/unit/datepicker/datepicker_events.js +++ b/tests/unit/datepicker/datepicker_events.js @@ -14,15 +14,48 @@ test( "beforeOpen", function() { }); test( "close", function() { - expect( 0 ); + expect( 4 ); + + var shouldFire, + input = TestHelpers.datepicker.init( "#datepicker", { + close: function() { + ok( shouldFire, "close event fired" ); + } + }); + + shouldFire = false; + input.datepicker( "open" ); + shouldFire = true; + input.datepicker( "close" ); + + shouldFire = false; + input.datepicker( "open" ); + shouldFire = true; + $( "body" ).trigger( "mousedown" ); + + shouldFire = false; + input.datepicker( "open" ); + shouldFire = true; + input.simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + + shouldFire = false; + input.datepicker( "open" ); + shouldFire = true; + input.datepicker( "widget" ).find( "tbody tr:first a:first" ).simulate( "mousedown" ); }); test( "open", function() { - expect( 0 ); -}); + expect( 2 ); -test( "select", function() { - expect( 0 ); + var input = TestHelpers.datepicker.init( "#datepicker", { + open: function() { + ok( true, "open event fired on open" ); + ok( widget.is( ":visible" ), "calendar open on open" ); + } + }), + widget = input.datepicker( "widget" ); + + input.datepicker( "open" ); }); var selectedThis = null, diff --git a/tests/unit/datepicker/datepicker_methods.js b/tests/unit/datepicker/datepicker_methods.js index 533df4bcb92..6215d302212 100644 --- a/tests/unit/datepicker/datepicker_methods.js +++ b/tests/unit/datepicker/datepicker_methods.js @@ -42,14 +42,23 @@ test( "widget", function() { actual.remove(); }); -test( "close", function() { - // TODO: implement this - expect( 0 ); -}); +test( "open / close", function() { + expect( 7 ); + + var input = TestHelpers.datepicker.initNewInput({ show: false, hide: false }), + calendar = input.datepicker( "widget" ); -test( "open", function() { - // TODO: implement this - expect( 0 ); + ok( calendar.is( ":hidden" ), "calendar hidden on init" ); + + input.datepicker( "open" ); + ok( calendar.is( ":visible" ), "open: calendar visible" ); + equal( calendar.attr( "aria-hidden" ), "false", "open: calendar aria-hidden" ); + equal( calendar.attr( "aria-expanded" ), "true", "close: calendar aria-expanded" ); + + input.datepicker( "close" ); + ok( !calendar.is( ":visible" ), "close: calendar hidden" ); + equal( calendar.attr( "aria-hidden" ), "true", "close: calendar aria-hidden" ); + equal( calendar.attr( "aria-expanded" ), "false", "close: calendar aria-expanded" ); }); test( "value", function() { From 03a26ff11fa63d20e8a7a662a64850af0ae08521 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Tue, 17 Jun 2014 16:25:24 +0200 Subject: [PATCH 09/26] Datepicker: Improve document click event --- ui/datepicker.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ui/datepicker.js b/ui/datepicker.js index bca9e600d3e..fc4f4f6e1bb 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -191,8 +191,12 @@ widget = $.widget( "ui.datepicker", { }, _documentEvents: { - click: function( event ) { - if ( this.isOpen && !$( event.target ).closest( this.element.add( this.calendar ) ).length ) { + mousedown: function( event ) { + if ( !this.isOpen ) { + return; + } + + if ( !$( event.target ).closest( this.element.add( this.calendar ) ).length ) { this.close( event ); } } From d443defa540ac7fa11741f2e9bb5417849ef2397 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Tue, 17 Jun 2014 19:05:06 +0200 Subject: [PATCH 10/26] Calendar: Fix broken day table cell attributes --- ui/calendar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/calendar.js b/ui/calendar.js index c694a68097b..b5272ca6259 100644 --- a/ui/calendar.js +++ b/ui/calendar.js @@ -313,7 +313,7 @@ return $.widget( "ui.calendar", { var content = "", attributes = [ "role='gridcell'", - "aria-selected='" + day.current ? true : false + "'" + "aria-selected='" + ( day.current ? true : false ) + "'" ], selectable = ( day.selectable && this._isValid( new Date( day.timestamp ) ) ); From 1c33a3af91d43f5bd72169c135cc84055a8c3e7d Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Tue, 17 Jun 2014 19:53:08 +0200 Subject: [PATCH 11/26] Datepicker tests: Rewrite event unit tests --- tests/unit/datepicker/datepicker_events.js | 207 +++++++----------- .../datepicker/datepicker_test_helpers.js | 6 +- 2 files changed, 81 insertions(+), 132 deletions(-) diff --git a/tests/unit/datepicker/datepicker_events.js b/tests/unit/datepicker/datepicker_events.js index ac3d41c13d6..ff5438cd2f1 100644 --- a/tests/unit/datepicker/datepicker_events.js +++ b/tests/unit/datepicker/datepicker_events.js @@ -1,16 +1,32 @@ -// The implement of events is completely changing therefore these tests are no longer directly -// relevant. Leaving them around commented out so we can ensure the functionality is replicated. -// For example: -// TODO: In the old implementation the Enter key select's today's date when the has -// focus and is empty. Do we want to replicate this behavior in the rewrite? -/* - (function( $ ) { module( "datepicker: events" ); test( "beforeOpen", function() { - expect( 0 ); + expect( 3 ); + + var input = TestHelpers.datepicker.init( "#datepicker", { + beforeOpen: function() { + ok( true, "beforeOpen event fired before open" ); + ok( input.datepicker( "widget" ).is( ":hidden" ), "calendar hidden on beforeOpen" ); + }, + open: function() { + ok( input.datepicker( "widget" ).is( ":visible" ), "calendar open on open" ); + } + }); + + input + .datepicker( "open" ) + .datepicker( "close" ) + .datepicker( "option", { + beforeOpen: function() { + return false; + }, + open: function() { + ok( false, "calendar should not open when openBefore is canceled" ); + } + }) + .datepicker( "open" ); }); test( "close", function() { @@ -58,126 +74,61 @@ test( "open", function() { input.datepicker( "open" ); }); -var selectedThis = null, -selectedDate = null, -selectedInst = null; - -function callback(date, inst) { - selectedThis = this; - selectedDate = date; - selectedInst = inst; -} - -function callback2(year, month, inst) { - selectedThis = this; - selectedDate = year + "/" + month; - selectedInst = inst; -} - -test( "events", function() { - expect( 26 ); - var dateStr, newMonthYear, inp2, - inp = TestHelpers.datepicker.init( "#inp", {onSelect: callback}), - date = new Date(); - // onSelect - inp.val( "" ).datepicker( "show" ). - simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); - equal(selectedThis, inp[0], "Callback selected this" ); - equal(selectedInst, $.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Callback selected inst" ); - equal(selectedDate, $.datepicker.formatDate( "mm/dd/yy", date), - "Callback selected date" ); - inp.val( "" ).datepicker( "show" ). - simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.DOWN}). - simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); - date.setDate(date.getDate() + 7); - equal(selectedDate, $.datepicker.formatDate( "mm/dd/yy", date), - "Callback selected date - ctrl+down" ); - inp.val( "" ).datepicker( "show" ). - simulate( "keydown", {keyCode: $.ui.keyCode.ESCAPE}); - equal(selectedDate, $.datepicker.formatDate( "mm/dd/yy", date), - "Callback selected date - esc" ); - dateStr = "02/04/2008"; - inp.val(dateStr).datepicker( "show" ). - simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); - equal(dateStr, selectedDate, - "onSelect is called after enter keydown" ); - // onChangeMonthYear - inp.datepicker( "option", {onChangeMonthYear: callback2, onSelect: null}). - val( "" ).datepicker( "show" ); - newMonthYear = function(date) { - return date.getFullYear() + "/" + (date.getMonth() + 1); - }; - date = new Date(); - date.setDate(1); - inp.simulate( "keydown", {keyCode: $.ui.keyCode.PAGE_UP}); - date.setMonth(date.getMonth() - 1); - equal(selectedThis, inp[0], "Callback change month/year this" ); - equal(selectedInst, $.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Callback change month/year inst" ); - equal(selectedDate, newMonthYear(date), - "Callback change month/year date - pgup" ); - inp.simulate( "keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}); - date.setMonth(date.getMonth() + 1); - equal(selectedDate, newMonthYear(date), - "Callback change month/year date - pgdn" ); - inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}); - date.setFullYear(date.getFullYear() - 1); - equal(selectedDate, newMonthYear(date), - "Callback change month/year date - ctrl+pgup" ); - inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.HOME}); - date.setFullYear(date.getFullYear() + 1); - equal(selectedDate, newMonthYear(date), - "Callback change month/year date - ctrl+home" ); - inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}); - date.setFullYear(date.getFullYear() + 1); - equal(selectedDate, newMonthYear(date), - "Callback change month/year date - ctrl+pgdn" ); - inp.datepicker( "setDate", new Date(2007, 1 - 1, 26)); - equal(selectedDate, "2007/1", "Callback change month/year date - setDate" ); - selectedDate = null; - inp.datepicker( "setDate", new Date(2007, 1 - 1, 12)); - ok(selectedDate == null, "Callback change month/year date - setDate no change" ); - // onChangeMonthYear step by 2 - inp.datepicker( "option", {stepMonths: 2}). - datepicker( "hide" ).val( "" ).datepicker( "show" ). - simulate( "keydown", {keyCode: $.ui.keyCode.PAGE_UP}); - date.setMonth(date.getMonth() - 14); - equal(selectedDate, newMonthYear(date), - "Callback change month/year by 2 date - pgup" ); - inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_UP}); - date.setMonth(date.getMonth() - 12); - equal(selectedDate, newMonthYear(date), - "Callback change month/year by 2 date - ctrl+pgup" ); - inp.simulate( "keydown", {keyCode: $.ui.keyCode.PAGE_DOWN}); - date.setMonth(date.getMonth() + 2); - equal(selectedDate, newMonthYear(date), - "Callback change month/year by 2 date - pgdn" ); - inp.simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.PAGE_DOWN}); - date.setMonth(date.getMonth() + 12); - equal(selectedDate, newMonthYear(date), - "Callback change month/year by 2 date - ctrl+pgdn" ); - // onClose - inp.datepicker( "option", {onClose: callback, onChangeMonthYear: null, stepMonths: 1}). - val( "" ).datepicker( "show" ). - simulate( "keydown", {keyCode: $.ui.keyCode.ESCAPE}); - equal(selectedThis, inp[0], "Callback close this" ); - equal(selectedInst, $.data(inp[0], TestHelpers.datepicker.PROP_NAME), "Callback close inst" ); - equal(selectedDate, "", "Callback close date - esc" ); - inp.val( "" ).datepicker( "show" ). - simulate( "keydown", {keyCode: $.ui.keyCode.ENTER}); - equal(selectedDate, $.datepicker.formatDate( "mm/dd/yy", new Date()), - "Callback close date - enter" ); - inp.val( "02/04/2008" ).datepicker( "show" ). - simulate( "keydown", {keyCode: $.ui.keyCode.ESCAPE}); - equal(selectedDate, "02/04/2008", "Callback close date - preset" ); - inp.val( "02/04/2008" ).datepicker( "show" ). - simulate( "keydown", {ctrlKey: true, keyCode: $.ui.keyCode.END}); - equal(selectedDate, "", "Callback close date - ctrl+end" ); - - inp2 = TestHelpers.datepicker.init( "#inp2" ); - inp2.datepicker().datepicker( "option", {onClose: callback}).datepicker( "show" ); - inp.datepicker( "show" ); - equal(selectedThis, inp2[0], "Callback close this" ); +asyncTest( "select", function() { + expect( 4 ); + + var input = TestHelpers.datepicker.init( "#datepicker", { + select: function( event ) { + ok( true, "select event fired " + message ); + equal( + event.originalEvent.type, + "calendarselect", + "select originalEvent " + message + ); + } + }), + widget = input.datepicker( "widget" ), + message = ""; + + function step1() { + message = "on calendar cell click"; + input + .simulate( "focus" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + setTimeout(function() { + widget.find( "tbody tr:first a:first" ).simulate( "mousedown" ); + input.datepicker( "close" ); + step2(); + }, 100 ); + } + + function step2() { + message = "on calendar cell enter"; + input + .simulate( "focus" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + setTimeout(function() { + $( ":focus" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.RIGHT } ) + .simulate( "keydown", { keyCode: $.ui.keyCode.ENTER } ); + input.datepicker( "close" ); + step3(); + }, 100 ); + } + + function step3() { + message = "on calendar escape (not expected)"; + input + .simulate( "focus" ) + .simulate( "keydown", { keyCode: $.ui.keyCode.DOWN } ); + setTimeout(function() { + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.ESCAPE } ); + input.datepicker( "close" ); + start(); + }, 100 ); + } + + step1(); }); })( jQuery ); - */ diff --git a/tests/unit/datepicker/datepicker_test_helpers.js b/tests/unit/datepicker/datepicker_test_helpers.js index 95f13a24621..f62e086b974 100644 --- a/tests/unit/datepicker/datepicker_test_helpers.js +++ b/tests/unit/datepicker/datepicker_test_helpers.js @@ -15,14 +15,12 @@ TestHelpers.datepicker = { equal( d1.toString(), d2.toString(), message ); }, init: function( id, options ) { - options = $.extend( { show: false }, options || {} ); + options = $.extend( { show: false, hide: false }, options || {} ); return $( id ).datepicker( options ); }, initNewInput: function( options ) { options = $.extend( { show: false, hide: false }, options || {} ); return $( "" ).datepicker( options ) .appendTo( "#qunit-fixture" ); - }, - onFocus: TestHelpers.onFocus, - PROP_NAME: "datepicker" + } }; \ No newline at end of file From 57d86fc07724b3d09a069134042e0f19c15e17fd Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Wed, 18 Jun 2014 01:35:51 +0200 Subject: [PATCH 12/26] Calendar: Fix multiple calendar styles --- themes/base/calendar.css | 8 +++++--- themes/base/datepicker.css | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/themes/base/calendar.css b/themes/base/calendar.css index 608afed4465..af889551672 100644 --- a/themes/base/calendar.css +++ b/themes/base/calendar.css @@ -109,15 +109,16 @@ } /* with multiple calendars */ -.ui-calendar.ui-calendar-multi { - width: 100%; +.ui-calendar-multi { + width: auto; + display: inline-block; } .ui-calendar-multi .ui-calendar-group { float: left; } .ui-calendar-multi .ui-calendar-group table { width: 95%; - margin: 0 auto .4em; + margin: 0 2.5% .4em; } .ui-calendar-multi-2 .ui-calendar-group { width: 50%; @@ -176,3 +177,4 @@ border-right-width: 0; border-left-width: 1px; } + diff --git a/themes/base/datepicker.css b/themes/base/datepicker.css index 4fde1fdcf56..3bc59f651d4 100644 --- a/themes/base/datepicker.css +++ b/themes/base/datepicker.css @@ -9,6 +9,6 @@ * http://api.jqueryui.com/datepicker/#theming */ .ui-datepicker { - display: none; - position: absolute; + display: none; + position: absolute; } \ No newline at end of file From 6e57cdd6eb0b96b720f06af97bb6bcc35c32bec8 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Wed, 18 Jun 2014 01:44:13 +0200 Subject: [PATCH 13/26] Calendar: Fix German localization --- external/localization.js | 6 +++--- tests/unit/calendar/calendar_core.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/external/localization.js b/external/localization.js index 98d8f813052..4fd436aca00 100644 --- a/external/localization.js +++ b/external/localization.js @@ -2767,10 +2767,10 @@ var regions = { "dateFormat": "d" }, "de": { - "closeText": "schlie\u00dfen", - "prevText": "<zur\u00fcck", + "closeText": "Schlie\u00dfen", + "prevText": "<Zur\u00fcck", "nextText": "Vor>", - "currentText": "heute", + "currentText": "Heute", "weekHeader": "Wo", "dateFormat": "d" }, diff --git a/tests/unit/calendar/calendar_core.js b/tests/unit/calendar/calendar_core.js index c9198ac87b7..99e2fa3bb69 100644 --- a/tests/unit/calendar/calendar_core.js +++ b/tests/unit/calendar/calendar_core.js @@ -79,7 +79,7 @@ test( "Localization", function() { equal( element.find( ".ui-calendar-month" ).text(), "Januar", message + "titlebar year" ); equal( element.find( "thead th:first" ).text(), "Mo.", message + "teader first day" ); equal( element.find( "thead th:last" ).text(), "So.", message + "header last day" ); - equal( element.find( ".ui-calendar-prev" ).text(), "", message + "header next" ); }; From f980cb8aa6679c9ce4443f1dbb2982664e39d50c Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Wed, 18 Jun 2014 02:05:42 +0200 Subject: [PATCH 14/26] Datepicker: Fix localization demo --- demos/calendar/localization.html | 13 ++++++++----- demos/datepicker/localization.html | 14 +++++++++----- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/demos/calendar/localization.html b/demos/calendar/localization.html index caf4622cf7f..eaca4617c92 100644 --- a/demos/calendar/localization.html +++ b/demos/calendar/localization.html @@ -16,12 +16,15 @@ diff --git a/demos/datepicker/localization.html b/demos/datepicker/localization.html index 701e5c36b66..01f90531eca 100644 --- a/demos/datepicker/localization.html +++ b/demos/datepicker/localization.html @@ -17,11 +17,15 @@ From a66a8c5c3fbb4435a1c70d7fd0334e7a8f3f082a Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Wed, 18 Jun 2014 02:31:31 +0200 Subject: [PATCH 15/26] Datepicker: Several minor code improvements Several minor code improvements and make suppressExpandOnFocus an internal variable --- ui/datepicker.js | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/ui/datepicker.js b/ui/datepicker.js index fc4f4f6e1bb..6ba46129b0f 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -27,9 +27,7 @@ }(function( $ ) { var widget, - calendarOptions = [ "dateFormat", "eachDay", "max", "min", "numberOfMonths", "showWeek" ], - // TODO move this to the instance? - suppressExpandOnFocus = false; + calendarOptions = [ "dateFormat", "eachDay", "max", "min", "numberOfMonths", "showWeek" ]; widget = $.widget( "ui.datepicker", { version: "@VERSION", @@ -50,6 +48,8 @@ widget = $.widget( "ui.datepicker", { }, _create: function() { + this.suppressExpandOnFocus = false; + if ( $.type( this.options.max ) === "string" ) { this.options.max = Globalize.parseDate( this.options.max , { pattern: "yyyy-MM-dd" } ); } @@ -93,9 +93,10 @@ widget = $.widget( "ui.datepicker", { this._setHiddenPicker(); - this.element - .attr( "aria-haspopup", "true" ) - .attr( "aria-owns", this.calendar.attr( "id" ) ); + this.element.attr({ + "aria-haspopup": true, + "aria-owns": this.calendar.attr( "id" ) + }); }, _inputEvents: { @@ -142,7 +143,7 @@ widget = $.widget( "ui.datepicker", { }, mousedown: function( event ) { if ( this.isOpen ) { - suppressExpandOnFocus = true; + this.suppressExpandOnFocus = true; this.close(); return; } @@ -150,19 +151,17 @@ widget = $.widget( "ui.datepicker", { clearTimeout( this.closeTimer ); }, focus: function( event ) { - if ( !suppressExpandOnFocus ) { + if ( !this.suppressExpandOnFocus && !this.isOpen ) { this._delay( function() { - if ( !this.isOpen ) { - this.open( event ); - } + this.open( event ); }, 1); } this._delay( function() { - suppressExpandOnFocus = false; + this.suppressExpandOnFocus = false; }, 100 ); }, blur: function() { - suppressExpandOnFocus = false; + this.suppressExpandOnFocus = false; } }, @@ -223,7 +222,7 @@ widget = $.widget( "ui.datepicker", { }, _focusTrigger: function() { - suppressExpandOnFocus = true; + this.suppressExpandOnFocus = true; this.element.focus(); }, @@ -241,14 +240,14 @@ widget = $.widget( "ui.datepicker", { } this.calendarInstance.refresh(); - this.calendar - .attr( "aria-hidden", "false" ) - .attr( "aria-expanded", "true" ) + .attr({ + "aria-hidden": false, + "aria-expanded": true + }) .show() .position( this._buildPosition() ) .hide(); - this._show( this.calendar, this.options.show ); // take trigger out of tab order to allow shift-tab to skip trigger @@ -270,9 +269,10 @@ widget = $.widget( "ui.datepicker", { }, _setHiddenPicker: function() { - this.calendar - .attr( "aria-hidden", "true" ) - .attr( "aria-expanded", "false" ); + this.calendar.attr({ + "aria-hidden": true, + "aria-expanded": false + }); }, _buildPosition: function() { @@ -305,9 +305,7 @@ widget = $.widget( "ui.datepicker", { _destroy: function() { this.calendarInstance.destroy(); this.calendar.remove(); - this.element - .removeAttr( "aria-haspopup" ) - .removeAttr( "aria-owns" ); + this.element.removeAttr( "aria-haspopup aria-owns" ); }, widget: function() { @@ -338,7 +336,7 @@ widget = $.widget( "ui.datepicker", { } } }); - + $.each( calendarOptions, function( index, option ) { $.ui.datepicker.prototype.options[ option ] = $.ui.calendar.prototype.options[ option ]; }); From e32c4a726f6a2858db7e2cad7fe784e8bddf5eef Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Wed, 18 Jun 2014 02:49:30 +0200 Subject: [PATCH 16/26] Calendar: Fix hover event setting and removing --- ui/calendar.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/calendar.js b/ui/calendar.js index b5272ca6259..33c201bb10e 100644 --- a/ui/calendar.js +++ b/ui/calendar.js @@ -73,8 +73,7 @@ return $.widget( "ui.calendar", { "keydown .ui-calendar-calendar": "_handleKeydown" }); - // TODO use hoverable (no delegation support)? convert to _on? - this.element.delegate( ".ui-calendar-header a, .ui-calendar-calendar a", "mouseenter.calendar mouseleave.calendar", function() { + this.element.on( "mouseenter.calendar mouseleave.calendar", ".ui-calendar-header a, .ui-calendar-calendar a", function() { $( this ).toggleClass( "ui-state-hover" ); }); @@ -453,6 +452,7 @@ return $.widget( "ui.calendar", { _destroy: function() { this.element + .off( ".calendar" ) .removeClass( "ui-calendar ui-widget ui-widget-content ui-helper-clearfix ui-corner-all ui-calendar-multi" ) .removeAttr( "role aria-labelledby" ) .removeUniqueId() From 34c1a324b6ff04aec082828ee581f57b2e14c05a Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Thu, 19 Jun 2014 15:12:27 +0200 Subject: [PATCH 17/26] Calendar: Improve code style and clean up --- ui/calendar.js | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/ui/calendar.js b/ui/calendar.js index 33c201bb10e..27a054c26b5 100644 --- a/ui/calendar.js +++ b/ui/calendar.js @@ -12,7 +12,7 @@ if ( typeof define === "function" && define.amd ) { // AMD. Register as an anonymous module. - // ToDo Add globalize and $.date? + // TODO: Add globalize and $.date? define([ "jquery", "./core", @@ -30,7 +30,7 @@ return $.widget( "ui.calendar", { version: "@VERSION", options: { dateFormat: { date: "short" }, - // TODO review + // TODO: review eachDay: $.noop, max: null, min: null, @@ -65,8 +65,8 @@ return $.widget( "ui.calendar", { }, "mousedown .ui-calendar-calendar a": function( event ) { event.preventDefault(); - // TODO exclude clicks on lead days or handle them correctly - // TODO store/read more then just date, also required for multi month picker + // TODO: exclude clicks on lead days or handle them correctly + // TODO: store/read more then just date, also required for multi month picker this._select( event, $( event.currentTarget ).data( "timestamp" ) ); this.grid.focus( 1 ); }, @@ -133,12 +133,12 @@ return $.widget( "ui.calendar", { }, _setActiveDescendant: function() { - var newId = this.id + "-" + this.date.day(), - newCell = $( "#" + newId ); + var id = this.id + "-" + this.date.day(); - this.grid.attr( "aria-activedescendant", newId ); - this.grid.find( ".ui-state-focus" ).removeClass( "ui-state-focus" ); - newCell.children( "a" ).addClass( "ui-state-focus" ); + this.grid + .attr( "aria-activedescendant", id ) + .removeClass( "ui-state-focus" ); + $( "#" + id + " a" ).addClass( "ui-state-focus" ); }, _createCalendar: function() { @@ -173,7 +173,7 @@ return $.widget( "ui.calendar", { i = 0; for ( i; i < months.length; i++ ) { - // TODO Shouldn't we pass date as a parameter to build* fns instead of setting this.date? + // TODO: Shouldn't we pass date as a parameter to build* fns instead of setting this.date? this.date = months[ i ]; headerClass = months[ i ].first ? "ui-corner-left" : months[ i ].last ? "ui-corner-right" : ""; @@ -305,6 +305,7 @@ return $.widget( "ui.calendar", { for ( i; i < week.days.length; i++ ) { cells += this._buildDayCell( week.days[i] ); } + return "" + cells + ""; }, @@ -344,7 +345,7 @@ return $.widget( "ui.calendar", { if ( day.today ) { classes.push( "ui-state-highlight" ); } - // ToDo Explain and document this + // TODO: Explain and document this if ( day.extraClasses ) { classes.push( day.extraClasses.split( " " ) ); } @@ -403,12 +404,6 @@ return $.widget( "ui.calendar", { $( this.element ).find( ".ui-state-focus" ).not( ":first" ).removeClass( "ui-state-focus" ); }, - _setHiddenPicker: function() { - this.element - .attr( "aria-hidden", "true" ) - .attr( "aria-expanded", "false" ); - }, - _select: function( event, time ) { this._setOption( "value", new Date( time ) ); this._trigger( "select", event ); @@ -462,6 +457,7 @@ return $.widget( "ui.calendar", { if ( arguments.length === 0 || ( arguments.length === 1 && key === "value" ) ) { this.options.value = this.date.selectedDate(); } + return this._superApply( arguments ); }, From c649c85ec18e712084aa998d526194343fc6a7c0 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Thu, 19 Jun 2014 17:37:37 +0200 Subject: [PATCH 18/26] Calendar: Focus class is not removed when using arrow keys --- ui/calendar.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/calendar.js b/ui/calendar.js index 27a054c26b5..932e2995254 100644 --- a/ui/calendar.js +++ b/ui/calendar.js @@ -137,8 +137,9 @@ return $.widget( "ui.calendar", { this.grid .attr( "aria-activedescendant", id ) + .find( ".ui-state-focus" ) .removeClass( "ui-state-focus" ); - $( "#" + id + " a" ).addClass( "ui-state-focus" ); + $( "#" + id + " > a" ).addClass( "ui-state-focus" ); }, _createCalendar: function() { From c34f013a9fa3fb42e4aa94e8e127d934728acb65 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Thu, 19 Jun 2014 17:42:21 +0200 Subject: [PATCH 19/26] Datepicker: Remove unwanted CTRL+HOME shortcut --- ui/datepicker.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/ui/datepicker.js b/ui/datepicker.js index 6ba46129b0f..dda5583f091 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -123,17 +123,6 @@ widget = $.widget( "ui.datepicker", { this.calendarInstance.grid.focus( 1 ); }, 1 ); break; - case $.ui.keyCode.HOME: - if ( event.ctrlKey ) { - this.valueAsDate( new Date() ); - event.preventDefault(); - if ( this.isOpen ) { - this.calendarInstance.refresh(); - } else { - this.open( event ); - } - } - break; } }, keyup: function() { From 5182ba506ab50c50a22aa7f21415fddd348aeb18 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Thu, 19 Jun 2014 18:36:57 +0200 Subject: [PATCH 20/26] Datepicker: Remove support for enter key on input --- ui/datepicker.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/ui/datepicker.js b/ui/datepicker.js index dda5583f091..8938bd428c7 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -112,9 +112,6 @@ widget = $.widget( "ui.datepicker", { this.close( event ); } break; - case $.ui.keyCode.ENTER: - this.calendarInstance._handleKeydown( event ); - break; case $.ui.keyCode.DOWN: case $.ui.keyCode.UP: clearTimeout( this.closeTimer ); From 338baee93b9c0453fff8e7c9443de5c01fe5b77c Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Thu, 19 Jun 2014 18:59:28 +0200 Subject: [PATCH 21/26] Datepicker: Improve localization handling, code style --- tests/unit/calendar/calendar_core.js | 8 ++- ui/calendar.js | 84 ++++++++++++++-------------- 2 files changed, 49 insertions(+), 43 deletions(-) diff --git a/tests/unit/calendar/calendar_core.js b/tests/unit/calendar/calendar_core.js index 99e2fa3bb69..b918ffdffd2 100644 --- a/tests/unit/calendar/calendar_core.js +++ b/tests/unit/calendar/calendar_core.js @@ -65,7 +65,7 @@ test( "baseStructure", function() { }); test( "Localization", function() { - expect( 5 ); + expect( 10 ); var defaultLocale = Globalize.locale(), element = $( "#calendar" ), @@ -88,6 +88,12 @@ test( "Localization", function() { testLocalization( "Init: " ); element.calendar( "destroy" ); + Globalize.locale( defaultLocale.locale ); + initCalendar(); + Globalize.locale( "de-DE" ); + element.calendar( "refresh" ); + testLocalization( "After init: " ); + Globalize.locale( defaultLocale.locale ); }); diff --git a/ui/calendar.js b/ui/calendar.js index 932e2995254..41187a0794d 100644 --- a/ui/calendar.js +++ b/ui/calendar.js @@ -44,6 +44,7 @@ return $.widget( "ui.calendar", { _create: function() { this.id = this.element.uniqueId().attr( "id" ); + this.labels = Globalize.translate( "datepicker" ); this.date = $.date( this.options.value, this.options.dateFormat ).select(); this.date.eachDay = this.options.eachDay; @@ -73,7 +74,7 @@ return $.widget( "ui.calendar", { "keydown .ui-calendar-calendar": "_handleKeydown" }); - this.element.on( "mouseenter.calendar mouseleave.calendar", ".ui-calendar-header a, .ui-calendar-calendar a", function() { + this.element.on( "mouseenter.calendar mouseleave.calendar", ".ui-calendar-header button, .ui-calendar-calendar a", function() { $( this ).toggleClass( "ui-state-hover" ); }); @@ -161,8 +162,6 @@ return $.widget( "ui.calendar", { }) .html( pickerHtml ); - this.element.find( "button" ).button(); - this.grid = this.element.find( ".ui-calendar-calendar" ); }, @@ -204,41 +203,39 @@ return $.widget( "ui.calendar", { _buildHeader: function() { return "
    " + - this._buildPreviousLink() + - this._buildNextLink() + - this._buildTitlebar() + - "
    "; + this._buildPreviousLink() + + this._buildNextLink() + + this._buildTitlebar() + + "
    "; }, _buildPreviousLink: function() { - var labels = Globalize.translate( "datepicker" ); - - return ""; }, _buildNextLink: function() { - var labels = Globalize.translate( "datepicker" ); - - return ""; }, _buildTitlebar: function() { - var labels = Globalize.translate( "datepicker" ); - return "
    " + - "
    " + - this._buildTitle() + - "
    " + - ", " + labels.datePickerRole + "" + - "
    "; + "
    " + + this._buildTitle() + + "
    " + + ", " + + this.labels.datePickerRole + + "" + + "
    "; }, _buildTitle: function() { @@ -252,39 +249,40 @@ return $.widget( "ui.calendar", { _buildGrid: function() { return "" + - this._buildGridHeading() + - this._buildGridBody() + - "
    "; + "aria-labelledby='" + this.id + "-month-lbl' tabindex='0' " + + "aria-activedescendant='" + this.id + "-" + this.date.day() + "'>" + + this._buildGridHeading() + + this._buildGridBody() + + ""; }, _buildGridHeading: function() { var cells = "", - i = 0, - labels = Globalize.translate( "datepicker" ); + i = 0; if ( this.options.showWeek ) { - cells += "" + labels.weekHeader + ""; + cells += "" + this.labels.weekHeader + ""; } for ( i; i < this.date.weekdays().length; i++ ) { cells += this._buildGridHeaderCell( this.date.weekdays()[i] ); } return "" + - "" + cells + "" + - ""; + "" + cells + "" + + ""; }, _buildGridHeaderCell: function( day ) { return "" + - "" + - day.shortname + - "" + - ""; + "" + + day.shortname + + "" + + ""; }, _buildGridBody: function() { - // this.date.days() is not cached, and it has O(n^2) complexity. It is run O(n) times. So, it equals O(n^3). Not good at all. Caching. + // TODO: this.date.days() is not cached, and it has O(n^2) complexity. It is run O(n) times. + // So, it equals O(n^3). Not good at all. Caching. var days = this.date.days(), i = 0, rows = ""; @@ -334,7 +332,6 @@ return $.widget( "ui.calendar", { _buildDayElement: function( day, selectable ) { var classes = [ "ui-state-default" ], - labels = Globalize.translate( "datepicker" ), content = ""; if ( day === this.date && selectable ) { @@ -359,18 +356,16 @@ return $.widget( "ui.calendar", { } if ( day.today ) { - content += ", " + labels.currentText + ""; + content += ", " + this.labels.currentText + ""; } return content; }, _buildButtons: function() { - var labels = Globalize.translate( "datepicker" ); - return "
    " + - "" + - "
    "; + "" + + ""; }, // Refreshing the entire calendar during interaction confuses screen readers, specifically @@ -379,12 +374,17 @@ return $.widget( "ui.calendar", { // with the prev and next links would cause loss of focus issues because the links being // interacted with will disappear while focused. refresh: function() { + this.labels = Globalize.translate( "datepicker" ); // determine which day gridcell to focus after refresh // TODO: Prevent disabled cells from being focused if ( this.options.numberOfMonths === 1 ) { this.grid = $( this._buildGrid() ); $( ".ui-calendar-title", this.element ).html( this._buildTitle() ); $( ".ui-calendar-calendar", this.element ).replaceWith( this.grid ); + $( ".ui-calendar-prev", this.element ).attr( "title", this.labels.prevText ) + .children().html( this.labels.prevText ); + $( ".ui-calendar-next", this.element ).attr( "title", this.labels.nextText ) + .children().html( this.labels.nextText ); } else { this._refreshMultiplePicker(); } From a7412cf2691f76e3de81e33ea732ffb0b2826d72 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Fri, 20 Jun 2014 00:13:20 +0200 Subject: [PATCH 22/26] Calendar: Add buttons option --- demos/calendar/buttonbar.html | 8 +- tests/unit/calendar/calendar_common.js | 1 + tests/unit/calendar/calendar_core.js | 25 ++++- tests/unit/calendar/calendar_options.js | 105 +++++++++++++++++++ tests/unit/calendar/calendar_test_helpers.js | 4 +- tests/unit/datepicker/datepicker_common.js | 1 + tests/unit/datepicker/datepicker_core.js | 2 +- tests/unit/datepicker/datepicker_options.js | 18 +++- ui/calendar.js | 72 +++++++++++-- ui/datepicker.js | 6 +- 10 files changed, 221 insertions(+), 21 deletions(-) diff --git a/demos/calendar/buttonbar.html b/demos/calendar/buttonbar.html index 498fe1041f4..7ed390b25d1 100644 --- a/demos/calendar/buttonbar.html +++ b/demos/calendar/buttonbar.html @@ -17,7 +17,11 @@ @@ -27,7 +31,7 @@
    -

    Display a button for selecting Today's date and a Done button for closing the calendar with the boolean showButtonPanel option. Each button is enabled by default when the bar is displayed, but can be turned off with additional options. Button text is customizable.

    +

    Display a button for selecting Today's date with the buttons option.

    diff --git a/tests/unit/calendar/calendar_common.js b/tests/unit/calendar/calendar_common.js index 49f8b5f05f6..a669fdfc2c1 100644 --- a/tests/unit/calendar/calendar_common.js +++ b/tests/unit/calendar/calendar_common.js @@ -1,5 +1,6 @@ TestHelpers.commonWidgetTests( "calendar", { defaults: { + buttons: [], dateFormat: { date: "short" }, disabled: false, eachDay: $.noop, diff --git a/tests/unit/calendar/calendar_core.js b/tests/unit/calendar/calendar_core.js index b918ffdffd2..49278345753 100644 --- a/tests/unit/calendar/calendar_core.js +++ b/tests/unit/calendar/calendar_core.js @@ -5,7 +5,7 @@ module( "calendar: core" ); TestHelpers.testJshint( "calendar" ); test( "baseStructure", function() { - expect( 22 ); + expect( 26 ); var header, title, table, thead, week, child, buttonpane, element = $( "#calendar" ).calendar(), @@ -14,7 +14,7 @@ test( "baseStructure", function() { function step1() { ok( !dp.is( ".ui-calendar-rtl" ), "Structure - not right-to-left" ); ok( !dp.is( ".ui-calendar-multi" ), "Structure - not multi-month" ); - equal( dp.children().length, 3, "Structure - child count (header, calendar)" ); + equal( dp.children().length, 2, "Structure - child count (header, calendar)" ); header = dp.children( ":first" ); ok( header.is( "div.ui-calendar-header" ), "Structure - header division" ); @@ -42,18 +42,33 @@ test( "baseStructure", function() { ok( week.is( "tr" ), "Structure - month table week row" ); equal( week.children().length, 7, "Structure - week child count" ); - element.calendar( "destroy" ); - step2(); } function step2() { + element.calendar( "option", "buttons", { + "test": function() {}, + "test button": function() {} + }); + + equal( dp.children().length, 3, "Structure buttons - child count (header, calendar, buttonpane)" ); + + buttonpane = dp.children( ".ui-calendar-buttonpane" ); + equal( buttonpane.children( "div.ui-calendar-buttonset" ).length, 1, "Structure buttons - buttonset" ); + equal( buttonpane.find( "button.ui-button:first" ).text(), "test", "Structure buttons - buttonset" ); + equal( buttonpane.find( "button.ui-button:eq(1)" ).text(), "test button", "Structure buttons - buttonset" ); + + element.calendar( "destroy" ); + step3(); + } + + function step3() { // Multi-month 2 element = $( "#calendar" ).calendar( { numberOfMonths: 2 } ); dp = element.calendar( "widget" ); ok( dp.is( ".ui-calendar-multi" ), "Structure multi [2] - multi-month" ); - equal( dp.children().length, 4, "Structure multi [2] - child count" ); + equal( dp.children().length, 3, "Structure multi [2] - child count" ); child = dp.children( ":eq(2)" ); ok( child.is( "div.ui-calendar-row-break" ), "Structure multi [2] - row break" ); diff --git a/tests/unit/calendar/calendar_options.js b/tests/unit/calendar/calendar_options.js index a5e08226653..df3ef80d3c4 100644 --- a/tests/unit/calendar/calendar_options.js +++ b/tests/unit/calendar/calendar_options.js @@ -2,6 +2,111 @@ module( "calendar: options" ); +test("buttons", function() { + expect( 21 ); + + var btn, i, newButtons, + buttons = { + "Ok": function( event ) { + ok(true, "button click fires callback" ); + equal( this, element[ 0 ], "context of callback" ); + equal( event.target, btn[ 0 ], "event target" ); + }, + "Cancel": function( event ) { + ok( true, "button click fires callback" ); + equal( this, element[ 0 ], "context of callback" ); + equal( event.target, btn[ 1 ], "event target" ); + } + }, + element = $( "#calendar" ).calendar({ buttons: buttons }); + + btn = element.calendar( "widget" ).find( ".ui-calendar-buttonpane button" ); + equal( btn.length, 2, "number of buttons" ); + + i = 0; + $.each( buttons, function( key ) { + equal( btn.eq( i ).text(), key, "text of button " + ( i + 1 ) ); + i++; + }); + + ok( btn.parent().hasClass( "ui-calendar-buttonset" ), "buttons in container" ); + ok( + element.calendar( "widget" ).hasClass( "ui-calendar-buttons" ), + "calendar wrapper adds class about having buttons" + ); + + btn.trigger( "click" ); + + newButtons = { + "Close": function( event ) { + ok( true, "button click fires callback" ); + equal( this, element[ 0 ], "context of callback" ); + equal( event.target, btn[ 0 ], "event target" ); + } + }; + + deepEqual( + element.calendar( "option", "buttons" ), + buttons, + ".calendar('option', 'buttons') getter" + ); + element.calendar( "option", "buttons", newButtons ); + deepEqual( + element.calendar( "option", "buttons" ), + newButtons, + ".calendar('option', 'buttons', ...) setter" + ); + + btn = element.calendar( "widget" ).find( ".ui-calendar-buttonpane button" ); + equal( btn.length, 1, "number of buttons after setter" ); + btn.trigger( "click" ); + + i = 0; + $.each( newButtons, function( key ) { + equal( btn.eq( i ).text(), key, "text of button " + ( i + 1 ) ); + i += 1; + }); + + element.calendar( "option", "buttons", null ); + btn = element.calendar( "widget" ).find( ".ui-calendar-buttonpane button" ); + equal( btn.length, 0, "all buttons have been removed" ); + equal( element.find( ".ui-calendar-buttonset").length, 0, "buttonset has been removed" ); + equal( element.parent().hasClass( "ui-calendar-buttons" ), false, "dialog wrapper removes class about having buttons" ); + + element.remove(); +}); + +test( "buttons - advanced", function() { + expect( 7 ); + + var buttons, + element = $( "#calendar" ).calendar({ + buttons: [{ + text: "a button", + "class": "additional-class", + id: "my-button-id", + click: function() { + equal(this, element[ 0 ], "correct context"); + }, + icons: { + primary: "ui-icon-cancel" + }, + showText: false + }] + }); + + buttons = element.calendar( "widget" ).find( ".ui-calendar-buttonpane button" ); + equal( buttons.length, 1, "correct number of buttons" ); + equal( buttons.attr( "id" ), "my-button-id", "correct id" ); + equal (buttons.text(), "a button", "correct label" ); + ok( buttons.hasClass( "additional-class" ), "additional classes added" ); + deepEqual( buttons.button( "option", "icons" ), { primary: "ui-icon-cancel", secondary: null } ); + equal( buttons.button( "option", "text" ), false ); + buttons.click(); + + element.remove(); +}); + test( "dateFormat", function() { expect( 2 ); var element = $( "#calendar" ).calendar({ diff --git a/tests/unit/calendar/calendar_test_helpers.js b/tests/unit/calendar/calendar_test_helpers.js index 7fb80bb58e9..032a2227ac6 100644 --- a/tests/unit/calendar/calendar_test_helpers.js +++ b/tests/unit/calendar/calendar_test_helpers.js @@ -15,7 +15,9 @@ TestHelpers.calendar = { equal( d1.toString(), d2.toString(), message ); }, focusGrid: function( element ) { - element.find( "table:tabbable" ).simulate( "focus" ); + element.find( ":tabbable" ).last().simulate( "focus" ); + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.TAB } ); + $( ":focus" ).simulate( "keydown", { keyCode: $.ui.keyCode.TAB } ); return $( ":focus" ); } diff --git a/tests/unit/datepicker/datepicker_common.js b/tests/unit/datepicker/datepicker_common.js index 4a2ece05da8..fb4f8d340fd 100644 --- a/tests/unit/datepicker/datepicker_common.js +++ b/tests/unit/datepicker/datepicker_common.js @@ -1,6 +1,7 @@ TestHelpers.commonWidgetTests( "datepicker", { defaults: { appendTo: null, + buttons: [], dateFormat: { date: "short" }, disabled: false, eachDay: $.noop, diff --git a/tests/unit/datepicker/datepicker_core.js b/tests/unit/datepicker/datepicker_core.js index 04371308c31..fc46be9481b 100644 --- a/tests/unit/datepicker/datepicker_core.js +++ b/tests/unit/datepicker/datepicker_core.js @@ -29,7 +29,7 @@ asyncTest( "baseStructure", function() { setTimeout(function() { ok( widget.is( ":visible" ), "Datepicker visible" ); - equal( widget.children().length, 3, "Child count" ); + equal( widget.children().length, 2, "Child count" ); ok( widget.is( ".ui-calendar" ), "Class ui-calendar" ); ok( widget.is( ".ui-datepicker" ), "Class ui-datepicker" ); ok( widget.is( ".ui-front" ), "Class ui-front" ); diff --git a/tests/unit/datepicker/datepicker_options.js b/tests/unit/datepicker/datepicker_options.js index 17d59339a3b..07a3205a24b 100644 --- a/tests/unit/datepicker/datepicker_options.js +++ b/tests/unit/datepicker/datepicker_options.js @@ -40,6 +40,22 @@ test( "appendTo", function() { input.datepicker( "destroy" ); }); +test("buttons", function() { + expect( 3 ); + + var btn, + buttons = { + "Ok": function() {}, + "Cancel": function() {} + }, + element = $( "#datepicker" ).datepicker({ buttons: buttons }); + + btn = element.datepicker( "widget" ).find( ".ui-calendar-buttonpane button" ); + equal( btn.length, 2, "number of buttons" ); + ok( btn.parent().hasClass( "ui-calendar-buttonset" ), "buttons in container"); + ok( element.datepicker( "widget" ).hasClass( "ui-calendar-buttons" ), "calendar wrapper adds class about having buttons" ); +}); + test( "dateFormat", function() { expect( 2 ); var input = $( "#datepicker" ).val( "1/1/14" ).datepicker(), @@ -149,7 +165,7 @@ test( "showWeek", function() { input.datepicker( "option", "showWeek", true ); equal( container.find( "thead th" ).length, 8, "supports changing option after init" ); }); - + test( "min / max", function() { expect( 14 ); diff --git a/ui/calendar.js b/ui/calendar.js index 41187a0794d..792c0487f6b 100644 --- a/ui/calendar.js +++ b/ui/calendar.js @@ -13,6 +13,7 @@ // AMD. Register as an anonymous module. // TODO: Add globalize and $.date? + // TODO: Keep button even if its optional? define([ "jquery", "./core", @@ -29,6 +30,7 @@ return $.widget( "ui.calendar", { version: "@VERSION", options: { + buttons: [], dateFormat: { date: "short" }, // TODO: review eachDay: $.noop, @@ -60,10 +62,6 @@ return $.widget( "ui.calendar", { this.date.adjust( "M", this.options.numberOfMonths ); this.refresh(); }, - "click .ui-calendar-current": function( event ) { - event.preventDefault(); - this._select( event, new Date().getTime() ); - }, "mousedown .ui-calendar-calendar a": function( event ) { event.preventDefault(); // TODO: exclude clicks on lead days or handle them correctly @@ -148,7 +146,7 @@ return $.widget( "ui.calendar", { pickerHtml = ""; if ( this.options.numberOfMonths === 1 ) { - pickerHtml = this._buildHeader() + this._buildGrid() + this._buildButtons(); + pickerHtml = this._buildHeader() + this._buildGrid(); } else { pickerHtml = this._buildMultiplePicker(); classes += " ui-calendar-multi"; @@ -162,6 +160,8 @@ return $.widget( "ui.calendar", { }) .html( pickerHtml ); + this._createButtonPane(); + this.grid = this.element.find( ".ui-calendar-calendar" ); }, @@ -194,7 +194,6 @@ return $.widget( "ui.calendar", { } html += "
    "; - html += this._buildButtons(); this.date = currentDate; @@ -362,10 +361,58 @@ return $.widget( "ui.calendar", { return content; }, - _buildButtons: function() { - return "
    " + - "" + - "
    "; + _createButtonPane: function() { + this.buttonPane = $( "
    " ) + .addClass( "ui-calendar-buttonpane ui-widget-content ui-helper-clearfix" ); + + this.buttonSet = $( "
    " ) + .addClass( "ui-calendar-buttonset" ) + .appendTo( this.buttonPane ); + + this._createButtons(); + }, + + _createButtons: function() { + var that = this, + buttons = this.options.buttons; + + // if we already have a button pane, remove it + this.buttonPane.remove(); + this.buttonSet.empty(); + + if ( $.isEmptyObject( buttons ) || ( $.isArray( buttons ) && !buttons.length ) ) { + this.element.removeClass( "ui-calendar-buttons" ); + return; + } + + $.each( buttons, function( name, props ) { + var click, buttonOptions; + props = $.isFunction( props ) ? + { click: props, text: name } : + props; + // Default to a non-submitting button + props = $.extend( { type: "button" }, props ); + // Change the context for the click callback to be the main element + click = props.click; + props.click = function() { + click.apply( that._buttonClickContext(), arguments ); + }; + buttonOptions = { + icons: props.icons, + text: props.showText + }; + delete props.icons; + delete props.showText; + $( "", props ) + .button( buttonOptions ) + .appendTo( that.buttonSet ); + }); + this.element.addClass( "ui-calendar-buttons" ); + this.buttonPane.appendTo( this.element ); + }, + + _buttonClickContext: function() { + return this.element[ 0 ]; }, // Refreshing the entire calendar during interaction confuses screen readers, specifically @@ -385,6 +432,7 @@ return $.widget( "ui.calendar", { .children().html( this.labels.prevText ); $( ".ui-calendar-next", this.element ).attr( "title", this.labels.nextText ) .children().html( this.labels.nextText ); + this._createButtons(); } else { this._refreshMultiplePicker(); } @@ -480,6 +528,10 @@ return $.widget( "ui.calendar", { this._super( key, value ); + if ( key === "buttons" ) { + this._createButtons(); + } + if ( key === "eachDay" ) { this.date.eachDay = value; this.refresh(); diff --git a/ui/datepicker.js b/ui/datepicker.js index 8938bd428c7..5a99ef6287c 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -27,7 +27,7 @@ }(function( $ ) { var widget, - calendarOptions = [ "dateFormat", "eachDay", "max", "min", "numberOfMonths", "showWeek" ]; + calendarOptions = [ "buttons", "dateFormat", "eachDay", "max", "min", "numberOfMonths", "showWeek" ]; widget = $.widget( "ui.datepicker", { version: "@VERSION", @@ -91,6 +91,10 @@ widget = $.widget( "ui.datepicker", { }) ) .calendar( "instance" ); + this.calendarInstance._buttonClickContext = function() { + return that.element[ 0 ]; + }; + this._setHiddenPicker(); this.element.attr({ From 9cba1801330250958a72d33d0e0245651a8e9532 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Wed, 2 Jul 2014 19:22:49 +0200 Subject: [PATCH 23/26] Calendar: Adjust files to match reorganization of external directory --- tests/unit/date/all.html | 22 +++++++++++----------- tests/unit/date/date.html | 6 +++--- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/unit/date/all.html b/tests/unit/date/all.html index ca2d66e37d9..8f31592f14b 100644 --- a/tests/unit/date/all.html +++ b/tests/unit/date/all.html @@ -1,20 +1,20 @@ - - jQuery UI Date Test Suite + + jQuery UI Date Test Suite - + - - - - - + + + + + - + diff --git a/tests/unit/date/date.html b/tests/unit/date/date.html index d759fdeaee7..b2dd2c41e22 100644 --- a/tests/unit/date/date.html +++ b/tests/unit/date/date.html @@ -5,14 +5,14 @@ jQuery UI Date Test Suite - - + + TestHelpers.loadResources({ js: [ - "external/globalize.js", + "external/globalize/globalize.js", "external/localization.js", "external/date.js" ] From fde83f2813104f2b524dac0d6b063e525a0086ec Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Fri, 29 Aug 2014 21:19:30 +0200 Subject: [PATCH 24/26] Datepicker tests: Remove pass-through options unit tests Add Test if these options are set on the underlying calendar instance. --- tests/unit/datepicker/datepicker_methods.js | 38 +---- tests/unit/datepicker/datepicker_options.js | 148 +++----------------- 2 files changed, 25 insertions(+), 161 deletions(-) diff --git a/tests/unit/datepicker/datepicker_methods.js b/tests/unit/datepicker/datepicker_methods.js index 6215d302212..5891e6bb1d7 100644 --- a/tests/unit/datepicker/datepicker_methods.js +++ b/tests/unit/datepicker/datepicker_methods.js @@ -79,13 +79,11 @@ test( "value", function() { }); test( "valueAsDate", function() { - expect( 13 ); + expect( 6 ); - var minDate, maxDate, dateAndTimeToSet, dateAndTimeClone, - input = TestHelpers.datepicker.init( "#datepicker" ), + var input = TestHelpers.datepicker.init( "#datepicker" ), picker = input.datepicker( "widget" ), - date1 = new Date( 2008, 6 - 1, 4 ), - date2 = new Date(); + date1 = new Date( 2008, 6 - 1, 4 ); input.datepicker( "valueAsDate", new Date( 2014, 0, 1 ) ); equal( input.val(), "1/1/14", "Input's value set" ); @@ -101,36 +99,6 @@ test( "valueAsDate", function() { ok(input.datepicker( "valueAsDate" ) === null, "Set date - default" ); input.datepicker( "valueAsDate", date1 ); TestHelpers.datepicker.equalsDate(input.datepicker( "valueAsDate" ), date1, "Set date - 2008-06-04" ); - - // With minimum/maximum - input = TestHelpers.datepicker.init( "#datepicker" ); - date1 = new Date( 2008, 1 - 1, 4 ); - date2 = new Date( 2008, 6 - 1, 4 ); - minDate = new Date( 2008, 2 - 1, 29 ); - maxDate = new Date( 2008, 3 - 1, 28 ); - - input.val( "" ).datepicker( "option", { min: minDate } ).datepicker( "valueAsDate", date2 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date2, "Set date min/max - value > min" ); - - input.datepicker( "valueAsDate", date1 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date2, "Set date min/max - value < min" ); - - input.val( "" ).datepicker( "option", { max: maxDate, min: null } ).datepicker( "valueAsDate", date1 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date1, "Set date min/max - value < max" ); - - input.datepicker( "valueAsDate", date2 ); - TestHelpers.datepicker.equalsDate( input.datepicker( "valueAsDate" ), date1, "Set date min/max - value > max" ); - - input.val( "" ).datepicker( "option", { min: minDate } ).datepicker( "valueAsDate", date1 ); - ok( input.datepicker( "valueAsDate" ) === null, "Set date min/max - value < min" ); - - input.datepicker( "valueAsDate", date2 ); - ok( input.datepicker( "valueAsDate" ) === null, "Set date min/max - value > max" ); - - dateAndTimeToSet = new Date( 2008, 3 - 1, 28, 1, 11, 0 ); - dateAndTimeClone = new Date( 2008, 3 - 1, 28, 1, 11, 0 ); - input.datepicker( "valueAsDate", dateAndTimeToSet ); - equal( dateAndTimeToSet.getTime(), dateAndTimeClone.getTime(), "Date object passed should not be changed by valueAsDate" ); }); test( "isValid", function() { diff --git a/tests/unit/datepicker/datepicker_options.js b/tests/unit/datepicker/datepicker_options.js index 07a3205a24b..ccf1f205a50 100644 --- a/tests/unit/datepicker/datepicker_options.js +++ b/tests/unit/datepicker/datepicker_options.js @@ -40,76 +40,34 @@ test( "appendTo", function() { input.datepicker( "destroy" ); }); -test("buttons", function() { - expect( 3 ); - - var btn, - buttons = { - "Ok": function() {}, - "Cancel": function() {} +test( "Pass-through options", function() { + expect( 8 ); + + var options = { + "buttons": { "Test": $.noop }, + "dateFormat": { date: "full" }, + "eachDay": function( day ) { day; }, + "max": new Date( 2000, 0, 1 ), + "min": new Date( 2000, 0, 2 ), + "numberOfMonths": 3, + "showWeek": true }, - element = $( "#datepicker" ).datepicker({ buttons: buttons }); + input = $( "#datepicker" ).val( "1/1/14" ).datepicker(), + datepickerInstance = input.datepicker( "instance" ); - btn = element.datepicker( "widget" ).find( ".ui-calendar-buttonpane button" ); - equal( btn.length, 2, "number of buttons" ); - ok( btn.parent().hasClass( "ui-calendar-buttonset" ), "buttons in container"); - ok( element.datepicker( "widget" ).hasClass( "ui-calendar-buttons" ), "calendar wrapper adds class about having buttons" ); -}); + $.each( options, function( key, value ) { + input.datepicker( "option", key, value ); -test( "dateFormat", function() { - expect( 2 ); - var input = $( "#datepicker" ).val( "1/1/14" ).datepicker(), - picker = input.datepicker( "widget" ), - firstDayLink = picker.find( "td[id]:first a" ); - - input.datepicker( "open" ); - firstDayLink.trigger( "mousedown" ); - equal( input.val(), "1/1/14", "default formatting" ); - - input.datepicker( "option", "dateFormat", { date: "full" } ); - equal( input.val(), "Wednesday, January 1, 2014", "updated formatting" ); - - input.datepicker( "destroy" ); -}); + deepEqual( + datepickerInstance.calendar.calendar( "option", key ), + value, + "option " + key + ": correct value" + ); -test( "eachDay", function() { - expect( 5 ); - var timestamp, - input = $( "#datepicker" ).datepicker(), - picker = input.datepicker( "widget" ), - firstCell = picker.find( "td[id]:first" ); - - equal( firstCell.find( "a" ).length, 1, "days are selectable by default" ); - timestamp = parseInt( firstCell.find( "a" ).attr( "data-timestamp" ), 10 ); - equal( new Date( timestamp ).getDate(), 1, "first available day is the 1st by default" ); - - // Do not render the 1st of the month - input.datepicker( "option", "eachDay", function( day ) { - if ( day.date === 1 ) { - day.render = false; + if ( key === "dateFormat" ) { + equal( input.val(), "Wednesday, January 1, 2014", "option " + key + ": updated format" ); } }); - firstCell = picker.find( "td[id]:first" ); - timestamp = parseInt( firstCell.find( "a" ).attr( "data-timestamp" ), 10 ); - equal( new Date( timestamp ).getDate(), 2, "first available day is the 2nd" ); - - // Display the 1st of the month but make it not selectable. - input.datepicker( "option", "eachDay", function( day ) { - if ( day.date === 1 ) { - day.selectable = false; - } - }); - firstCell = picker.find( "td[id]:first" ); - equal( firstCell.find( "a" ).length, 0, "the 1st is not selectable" ); - - input.datepicker( "option", "eachDay", function( day ) { - if ( day.date === 1 ) { - day.extraClasses = "ui-custom"; - } - }); - ok( picker.find( "td[id]:first a" ).hasClass( "ui-custom" ), "extraClasses applied" ); - - input.datepicker( "destroy" ); }); asyncTest( "position", function() { @@ -140,68 +98,6 @@ asyncTest( "position", function() { }); }); -test( "showWeek", function() { - expect( 7 ); - var input = $( "#datepicker" ).datepicker(), - container = input.datepicker( "widget" ); - - equal( container.find( "thead th" ).length, 7, "just 7 days, no column cell" ); - equal( container.find( ".ui-calendar-week-col" ).length, 0, - "no week column cells present" ); - input.datepicker( "destroy" ); - - input = $( "#datepicker" ).datepicker({ showWeek: true }); - container = input.datepicker( "widget" ); - equal( container.find( "thead th" ).length, 8, "7 days + a column cell" ); - ok( container.find( "thead th:first" ).is( ".ui-calendar-week-col" ), - "first cell should have ui-calendar-week-col class name" ); - equal( container.find( ".ui-calendar-week-col" ).length, - container.find( "tr" ).length, "one week cell for each week" ); - input.datepicker( "destroy" ); - - input = $( "#datepicker" ).datepicker(); - container = input.datepicker( "widget" ); - equal( container.find( "thead th" ).length, 7, "no week column" ); - input.datepicker( "option", "showWeek", true ); - equal( container.find( "thead th" ).length, 8, "supports changing option after init" ); -}); - -test( "min / max", function() { - expect( 14 ); - - var inp = TestHelpers.datepicker.init( "#datepicker" ), - minDate = new Date( 2008, 2 - 1, 29 ), - maxDate = new Date( 2008, 12 - 1, 7 ); - - inp.val( "6/4/08" ).datepicker( "option", { min: minDate } ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value > min" ); - ok( inp.datepicker( "isValid" ) ); - - inp.datepicker( "option", { min: null } ).val( "1/4/08" ).datepicker( "option", { min: minDate } ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2008, 1 - 1, 4 ), "Min/max - value < min" ); - ok( !inp.datepicker( "isValid" ) ); - - inp.datepicker( "option", { min: null } ).val( "6/4/08" ).datepicker( "option", { max: maxDate } ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value < max" ); - ok( inp.datepicker( "isValid" ) ); - - inp.datepicker( "option", { max: null } ).val( "1/4/09" ).datepicker( "option", { max: maxDate } ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2009, 1 - 1, 4 ), "Min/max - setDate > max" ); - ok( !inp.datepicker( "isValid" ) ); - - inp.datepicker( "option", { max: null } ).val( "1/4/08" ).datepicker( "option", { min: minDate, max: maxDate } ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2008, 1 - 1, 4 ), "Min/max - value < min" ); - ok( !inp.datepicker( "isValid" ) ); - - inp.datepicker( "option", { max: null } ).val( "6/4/08" ).datepicker( "option", { min: minDate, max: maxDate } ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2008, 6 - 1, 4 ), "Min/max - value > min, < max" ); - ok( inp.datepicker( "isValid" ) ); - - inp.datepicker( "option", { max: null } ).val( "1/4/09" ).datepicker( "option", { min: minDate, max: maxDate } ); - TestHelpers.datepicker.equalsDate( inp.datepicker( "valueAsDate" ), new Date( 2009, 1 - 1, 4 ), "Min/max - value > max" ); - ok( !inp.datepicker( "isValid" ) ); -}); - test( "Ticket 7602: Stop datepicker from appearing with beforeOpen event handler", function() { expect( 3 ); From 6a1e8bc9a97974030de082fa566ae369c36db3a3 Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Sat, 30 Aug 2014 02:06:52 +0200 Subject: [PATCH 25/26] Calendar: Use _on for link hover events --- ui/calendar.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ui/calendar.js b/ui/calendar.js index 792c0487f6b..1c2f8207d68 100644 --- a/ui/calendar.js +++ b/ui/calendar.js @@ -69,16 +69,18 @@ return $.widget( "ui.calendar", { this._select( event, $( event.currentTarget ).data( "timestamp" ) ); this.grid.focus( 1 ); }, + "mouseenter .ui-calendar-header button, .ui-calendar-calendar a": "_hover", + "mouseleave .ui-calendar-header button, .ui-calendar-calendar a": "_hover", "keydown .ui-calendar-calendar": "_handleKeydown" }); - this.element.on( "mouseenter.calendar mouseleave.calendar", ".ui-calendar-header button, .ui-calendar-calendar a", function() { - $( this ).toggleClass( "ui-state-hover" ); - }); - this._createCalendar(); }, + _hover: function( event ) { + $( event.currentTarget ).toggleClass( "ui-state-hover" ); + }, + _handleKeydown: function( event ) { if ( jQuery.inArray( event.keyCode, [ 13, 33, 34, 35, 36, 37, 38, 39, 40 ] ) === -1 ) { // only interested navigation keys From 5a6596dcfe6a5f9f67bf78b4b32b135cb7f1a13c Mon Sep 17 00:00:00 2001 From: Felix Nagel Date: Mon, 1 Sep 2014 15:36:38 +0200 Subject: [PATCH 26/26] Datepicker: Add missing handling for disabled option --- tests/unit/calendar/calendar_methods.js | 13 ++++----- tests/unit/datepicker/datepicker_methods.js | 29 ++++++++++++--------- tests/unit/datepicker/datepicker_options.js | 3 ++- ui/calendar.js | 6 +++++ ui/datepicker.js | 13 ++++++++- 5 files changed, 43 insertions(+), 21 deletions(-) diff --git a/tests/unit/calendar/calendar_methods.js b/tests/unit/calendar/calendar_methods.js index 53c8514b984..72a1e11bb89 100644 --- a/tests/unit/calendar/calendar_methods.js +++ b/tests/unit/calendar/calendar_methods.js @@ -15,20 +15,21 @@ test( "destroy", function() { }); test( "enable / disable", function() { - expect( 6 ); + expect( 8 ); var element = $( "#calendar" ).calendar(); - ok( !element.calendar( "option", "disabled" ), "initially enabled" ); - ok( !element.hasClass( "ui-calendar-disabled" ), "does not have disabled class name" ); - element.calendar( "disable" ); ok( element.calendar( "option", "disabled" ), "disabled option is set" ); - ok( element.hasClass( "ui-calendar-disabled" ), "calendar has disabled class name" ); + ok( element.hasClass( "ui-calendar-disabled" ), "has disabled widget class name" ); + ok( element.hasClass( "ui-state-disabled" ), "has disabled state class name" ); + equal( element.attr( "aria-disabled" ), "true", "has ARIA disabled" ); element.calendar( "enable" ); ok( !element.calendar( "option", "disabled" ), "enabled after enable() call" ); - ok( !element.hasClass( "ui-calendar-disabled" ), "no longer has disabled class name" ); + ok( !element.hasClass( "ui-calendar-disabled" ), "no longer has disabled widget class name" ); + ok( !element.hasClass( "ui-state-disabled" ), "no longer has disabled state class name" ); + equal( element.attr( "aria-disabled" ), "false", "no longer has ARIA disabled" ); }); test( "widget", function() { diff --git a/tests/unit/datepicker/datepicker_methods.js b/tests/unit/datepicker/datepicker_methods.js index 5891e6bb1d7..8f4ba291d61 100644 --- a/tests/unit/datepicker/datepicker_methods.js +++ b/tests/unit/datepicker/datepicker_methods.js @@ -17,21 +17,24 @@ test( "destroy", function() { }); test( "enable / disable", function() { - expect( 6 ); - - var inp = TestHelpers.datepicker.init( "#datepicker" ), - dp = inp.datepicker( "widget" ); - - ok( !inp.datepicker( "option", "disabled" ), "initially enabled" ); - ok( !dp.hasClass( "ui-datepicker-disabled" ), "does not have disabled class name" ); + expect( 10 ); - inp.datepicker( "disable" ); - ok( inp.datepicker( "option", "disabled" ), "disabled option is set" ); - ok( dp.hasClass( "ui-datepicker-disabled" ), "datepicker has disabled class name" ); + var input = TestHelpers.datepicker.init( "#datepicker" ), + calendar = input.datepicker( "widget" ); - inp.datepicker( "enable" ); - ok( !inp.datepicker( "option", "disabled" ), "enabled after enable() call" ); - ok( !dp.hasClass( "ui-datepicker-disabled" ), "no longer has disabled class name" ); + input.datepicker( "disable" ); + ok( input.datepicker( "option", "disabled" ), "disabled option is set" ); + ok( calendar.hasClass( "ui-datepicker-disabled" ), "has disabled widget class name" ); + ok( input.hasClass( "ui-state-disabled" ), "has disabled state class name" ); + equal( input.attr( "aria-disabled" ), "true", "has ARIA disabled" ); + equal( input.attr( "disabled" ), "disabled", "input disabled" ); + + input.datepicker( "enable" ); + ok( !input.datepicker( "option", "disabled" ), "enabled after enable() call" ); + ok( !calendar.hasClass( "ui-datepicker-disabled" ), "no longer has disabled widget class name" ); + ok( !input.hasClass( "ui-state-disabled" ), "no longer has disabled state class name" ); + equal( input.attr( "aria-disabled" ), "false", "no longer has ARIA disabled" ); + equal( input.attr( "disabled" ), undefined, "input no longer disabled" ); }); test( "widget", function() { diff --git a/tests/unit/datepicker/datepicker_options.js b/tests/unit/datepicker/datepicker_options.js index ccf1f205a50..80c42f1a4e9 100644 --- a/tests/unit/datepicker/datepicker_options.js +++ b/tests/unit/datepicker/datepicker_options.js @@ -41,11 +41,12 @@ test( "appendTo", function() { }); test( "Pass-through options", function() { - expect( 8 ); + expect( 9 ); var options = { "buttons": { "Test": $.noop }, "dateFormat": { date: "full" }, + "disabled": true, "eachDay": function( day ) { day; }, "max": new Date( 2000, 0, 1 ), "min": new Date( 2000, 0, 2 ), diff --git a/ui/calendar.js b/ui/calendar.js index 1c2f8207d68..7bc4abbf3dc 100644 --- a/ui/calendar.js +++ b/ui/calendar.js @@ -534,6 +534,12 @@ return $.widget( "ui.calendar", { this._createButtons(); } + if ( key === "disabled" ) { + this.element + .toggleClass( "ui-state-disabled", value ) + .attr( "aria-disabled", value ); + } + if ( key === "eachDay" ) { this.date.eachDay = value; this.refresh(); diff --git a/ui/datepicker.js b/ui/datepicker.js index 5a99ef6287c..e38c5140990 100644 --- a/ui/datepicker.js +++ b/ui/datepicker.js @@ -27,7 +27,7 @@ }(function( $ ) { var widget, - calendarOptions = [ "buttons", "dateFormat", "eachDay", "max", "min", "numberOfMonths", "showWeek" ]; + calendarOptions = [ "buttons", "dateFormat", "disabled", "eachDay", "max", "min", "numberOfMonths", "showWeek" ]; widget = $.widget( "ui.datepicker", { version: "@VERSION", @@ -321,6 +321,17 @@ widget = $.widget( "ui.datepicker", { this.element.val( this.calendarInstance.value() ); } + if ( key === "disabled" ) { + this.element + .prop( "disabled", value ) + .toggleClass( "ui-state-disabled", value ) + .attr( "aria-disabled", value ); + + if ( value ) { + this.close(); + } + } + if ( key === "position" ) { this.calendar.position( this._buildPosition() ); }