fix(tp-date-input): validate correctly when change event hasn't fired yet

validate() relied on this.value, which is only set by _inputChanged on
the change event. Submitting via Enter while focus is still in an input
field skips that event, leaving this.value undefined and causing valid
dates to fail validation.

Introduce _valueFromInputs() as the single parsing source: it reads the
three sub-inputs directly and returns a UTC-midnight DateTime.
validate()
now falls back to it when this.value is not set. _inputChanged is
refactored to call _valueFromInputs() as well, removing the duplicated
format-mapping logic.
This commit is contained in:
2026-06-10 22:42:27 +02:00
parent e189cb3b73
commit b32fd403ec
2 changed files with 44 additions and 16 deletions
+43 -15
View File
@@ -212,8 +212,20 @@ class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
const maxDate = this._getMaxDate(this.maxDate);
if ((this.inputs[0].invalid || this.inputs[1].invalid || this.inputs[2].invalid) ||
!this.dateValid(this.value, maxDate)) {
if (this.inputs[0].invalid || this.inputs[1].invalid || this.inputs[2].invalid) {
this.invalid = true;
return false;
}
// this.value is only set by _inputChanged which fires on the change event.
// When validate() is triggered before the change event fires (e.g. submitting
// via Enter while focus is still in the year field), this.value can be
// undefined/null even though the inputs contain a fully valid date.
// Fall back to computing the ISO value directly from the inputs in that case.
const utcDt = this._valueFromInputs();
const value = this.value || (utcDt ? utcDt.toISO() : null);
if (!this.dateValid(value, maxDate)) {
this.invalid = true;
return false;
}
@@ -222,6 +234,31 @@ class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
return true;
}
// Parses the three input fields into a UTC-midnight DateTime.
// Returns the DateTime on success, or null if any field is empty or the date is invalid.
_valueFromInputs() {
const i0 = this.inputs[0]?.value;
const i1 = this.inputs[1]?.value;
const i2 = this.inputs[2]?.value;
if (!i0 || !i1 || !i2) return null;
const format = this._inputAssign.map(part => {
switch (part) {
case 'MM': return 'LL';
case 'dd': return 'dd';
case 'y': return 'yyyy';
default: return part;
}
}).join('-');
const dt = DateTime.fromFormat(`${i0}-${i1}-${i2}`, format);
if (!dt.isValid) return null;
return DateTime.utc(dt.year, dt.month, dt.day);
}
focus() {
this.inputs[0].select();
}
@@ -291,26 +328,15 @@ class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
return;
}
// Convert luxon format to match input assignment
const format = this._inputAssign.map(part => {
switch (part) {
case 'MM': return 'LL';
case 'dd': return 'dd';
case 'y': return 'yyyy';
default: return part;
}
}).join('-');
// Parse the entered values as a plain calendar date, then store as UTC midnight.
// Date-only fields are timezone-agnostic — April 7 means April 7 for everyone.
const dt = DateTime.fromFormat(i0 + '-' + i1 + '-' + i2, format);
const utcMidnight = this._valueFromInputs();
if (dt.isValid) {
if (utcMidnight) {
this.inputs[0].invalid = false;
this.inputs[1].invalid = false;
this.inputs[2].invalid = false;
const utcMidnight = DateTime.utc(dt.year, dt.month, dt.day);
this.date = utcMidnight.toJSDate();
this.value = utcMidnight.toISO();
this.invalid = false;
@@ -450,6 +476,8 @@ class TpDateInput extends EventHelpers(ControlState(FormElement(LitElement))) {
// Reset invalid state if value was changed.
// This clears up old invalid states if the value was changed programmatically.
_onValueChanged() {
console.log(this.value);
// We skip if the user was just inputting values.
if (this._skipOnValueChanged) {
return;