diff --git a/src/wp-admin/js/customize-controls.js b/src/wp-admin/js/customize-controls.js index 0504891969..d9554618f2 100644 --- a/src/wp-admin/js/customize-controls.js +++ b/src/wp-admin/js/customize-controls.js @@ -5542,7 +5542,7 @@ control.inputElements = {}; control.invalidDate = false; - _.bindAll( control, 'populateSetting', 'updateDaysForMonth', 'updateMinutesForHour', 'populateDateInputs' ); + _.bindAll( control, 'populateSetting', 'updateDaysForMonth', 'populateDateInputs' ); if ( ! control.setting ) { throw new Error( 'Missing setting' ); @@ -5552,34 +5552,35 @@ var input = $( this ), component, element; component = input.data( 'component' ); element = new api.Element( input ); - if ( 'meridian' === component ) { - element.validate = function( value ) { - if ( 'am' !== value && 'pm' !== value ) { - return null; - } - return value; - }; - } else { - element.validate = function( value ) { - var val = parseInt( value, 10 ); - if ( isNaN( val ) ) { - return null; - } - return val; - }; - } - element.bind( control.populateSetting ); control.inputElements[ component ] = element; control.elements.push( element ); + + // Add invalid date error once user changes (and has blurred the input). + input.on( 'change', function() { + if ( control.invalidDate ) { + control.notifications.add( new api.Notification( 'invalid_date', { + message: api.l10n.invalidDate + } ) ); + } + } ); + + // Remove the error immediately after validity change. + input.on( 'input', _.debounce( function() { + if ( ! control.invalidDate ) { + control.notifications.remove( 'invalid_date' ); + } + } ) ); } ); control.inputElements.month.bind( control.updateDaysForMonth ); control.inputElements.year.bind( control.updateDaysForMonth ); - if ( control.params.includeTime ) { - control.inputElements.hour.bind( control.updateMinutesForHour ); - } control.populateDateInputs(); control.setting.bind( control.populateDateInputs ); + + // Start populating setting after inputs have been populated. + _.each( control.inputElements, function( element ) { + element.bind( control.populateSetting ); + } ); }, /** @@ -5629,7 +5630,7 @@ * @return {boolean} If date input fields has error. */ validateInputs: function validateInputs() { - var control = this, errorMessage, components; + var control = this, components, validityInput; control.invalidDate = false; @@ -5638,25 +5639,41 @@ components.push( 'hour', 'minute' ); } - _.each( components, function( component ) { - var element, el, max, min, value; + _.find( components, function( component ) { + var element, max, min, value; + + element = control.inputElements[ component ]; + validityInput = element.element.get( 0 ); + max = parseInt( element.element.attr( 'max' ), 10 ); + min = parseInt( element.element.attr( 'min' ), 10 ); + value = parseInt( element(), 10 ); + control.invalidDate = isNaN( value ) || value > max || value < min; if ( ! control.invalidDate ) { - element = control.inputElements[ component ]; - el = element.element.get( 0 ); - max = parseInt( element.element.attr( 'max' ), 10 ); - min = parseInt( element.element.attr( 'min' ), 10 ); - value = element(); - control.invalidDate = value > max || value < min; - errorMessage = control.invalidDate ? api.l10n.invalid + ' ' + component : ''; - - el.setCustomValidity( errorMessage ); - if ( ! control.section() || api.section.has( control.section() ) && api.section( control.section() ).expanded() ) { - _.result( el, 'reportValidity' ); - } + validityInput.setCustomValidity( '' ); } + + return control.invalidDate; } ); + if ( control.inputElements.meridian && ! control.invalidDate ) { + validityInput = control.inputElements.meridian.element.get( 0 ); + if ( 'am' !== control.inputElements.meridian.get() && 'pm' !== control.inputElements.meridian.get() ) { + control.invalidDate = true; + } else { + validityInput.setCustomValidity( '' ); + } + } + + if ( control.invalidDate ) { + validityInput.setCustomValidity( api.l10n.invalidValue ); + } else { + validityInput.setCustomValidity( '' ); + } + if ( ! control.section() || api.section.has( control.section() ) && api.section( control.section() ).expanded() ) { + _.result( validityInput, 'reportValidity' ); + } + return control.invalidDate; }, @@ -5669,44 +5686,20 @@ updateDaysForMonth: function updateDaysForMonth() { var control = this, daysInMonth, year, month, day; - month = control.inputElements.month(); - year = control.inputElements.year(); - day = control.inputElements.day(); + month = parseInt( control.inputElements.month(), 10 ); + year = parseInt( control.inputElements.year(), 10 ); + day = parseInt( control.inputElements.day(), 10 ); if ( month && year ) { daysInMonth = new Date( year, month, 0 ).getDate(); control.inputElements.day.element.attr( 'max', daysInMonth ); if ( day > daysInMonth ) { - control.inputElements.day( daysInMonth ); + control.inputElements.day( String( daysInMonth ) ); } } }, - /** - * Updates number of minutes according to the hour selected. - * - * @since 4.9.0 - * @return {void} - */ - updateMinutesForHour: function updateMinutesForHour() { - var control = this, maxHours = 24, minuteEl; - - if ( control.inputElements.meridian ) { - return; - } - - minuteEl = control.inputElements.minute.element; - - if ( maxHours === control.inputElements.hour() ) { - control.inputElements.minute( 0 ); - minuteEl.data( 'default-max', minuteEl.attr( 'max' ) ); - minuteEl.attr( 'max', '0' ); - } else if ( minuteEl.data( 'default-max' ) ) { - minuteEl.attr( 'max', minuteEl.data( 'default-max' ) ); - } - }, - /** * Populate setting value from the inputs. * @@ -5745,7 +5738,7 @@ }; getElementValue = function( component ) { - var value = control.inputElements[ component ].get(); + var value = parseInt( control.inputElements[ component ].get(), 10 ); if ( _.contains( [ 'month', 'day', 'hour', 'minute' ], component ) ) { value = pad( value, 2 ); @@ -5822,7 +5815,9 @@ } _.each( control.inputElements, function( element, component ) { - element.set( parsed[ component ] ); + if ( 'meridian' === component || parseInt( parsed[ component ], 10 ) !== parseInt( element(), 10 ) ) { + element.set( parsed[ component ] ); + } } ); return true; diff --git a/src/wp-includes/customize/class-wp-customize-date-time-control.php b/src/wp-includes/customize/class-wp-customize-date-time-control.php index 0506910b61..d1e8d12500 100644 --- a/src/wp-includes/customize/class-wp-customize-date-time-control.php +++ b/src/wp-includes/customize/class-wp-customize-date-time-control.php @@ -141,8 +141,9 @@ class WP_Customize_Date_Time_Control extends WP_Customize_Control {
- <# var maxHour = data.twelveHourFormat ? 12 : 24; #> - + <# var maxHour = data.twelveHourFormat ? 12 : 23; #> + <# var minHour = data.twelveHourFormat ? 1 : 0; #> + : diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index c43659379d..e5204a7aa0 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -599,6 +599,8 @@ function wp_default_scripts( &$scripts ) { esc_url( admin_url( 'theme-install.php' ) ) ), 'publishSettings' => __( 'Publish Settings' ), + 'invalidDate' => __( 'Invalid date.' ), + 'invalidValue' => __( 'Invalid value.' ), ) ); $scripts->add( 'customize-selective-refresh', "/wp-includes/js/customize-selective-refresh$suffix.js", array( 'jquery', 'wp-util', 'customize-preview' ), false, 1 ); diff --git a/tests/qunit/wp-admin/js/customize-controls.js b/tests/qunit/wp-admin/js/customize-controls.js index ec28ad3612..47ab5c0203 100644 --- a/tests/qunit/wp-admin/js/customize-controls.js +++ b/tests/qunit/wp-admin/js/customize-controls.js @@ -732,11 +732,15 @@ jQuery( window ).load( function (){ meridian = control.inputElements.meridian; year( '23' ); - assert.equal( typeof year(), 'number', 'Should always return integer' ); + assert.ok( control.invalidDate ); + year( '2100' ); month( '8' ); - month( 'test' ); - assert.equal( 8, month(), 'Should not accept text' ); + assert.ok( ! control.invalidDate ); + day( 'test' ); + assert.ok( control.invalidDate ); + day( '3' ); + assert.ok( ! control.invalidDate ); // Test control.parseDateTime(); control.params.twelveHourFormat = false; @@ -784,12 +788,13 @@ jQuery( window ).load( function (){ // Test control.updateDaysForMonth();. year( 2017 ); month( 2 ); + day( 28 ); + assert.ok( ! control.invalidDate ); day( 31 ); - control.updateDaysForMonth(); - assert.deepEqual( day(), 28, 'Should update to the correct days' ); + assert.ok( control.invalidDate ); day( 20 ); - assert.deepEqual( day(), 20, 'Should not update if its less the correct number of days' ); + assert.equal( day(), 20, 'Should not update if its less the correct number of days' ); // Test control.convertHourToTwentyFourHourFormat(). assert.equal( control.convertHourToTwentyFourHourFormat( 11, 'pm' ), 23 ); @@ -854,16 +859,6 @@ jQuery( window ).load( function (){ year( 2016 ); assert.notOk( control.isFutureDate() ); - /** - * Test control.updateMinutesForHour(). - * Run this at the end or else the above tests may fail. - */ - hour( 24 ); - minute( 32 ); - control.inputElements.meridian = false; // Because it works only when the time is twenty four hour format. - control.updateMinutesForHour(); - assert.deepEqual( minute(), 0 ); - // Tear Down. wp.customize.control.remove( controlId ); });