-
Notifications
You must be signed in to change notification settings - Fork 83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(date-picker): add unparsable-change event #6594
Changes from all commits
a4d588e
3062576
3731fa1
4606b0c
8be6ae4
4bfa618
a2f522f
4defa9d
375df5a
67a74e5
3b84d9e
5eade8c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -427,6 +427,20 @@ export const DatePickerMixin = (subclass) => | |
return null; | ||
} | ||
|
||
/** | ||
* The input element's value when it cannot be parsed as a date, and an empty string otherwise. | ||
* | ||
* @return {string} | ||
* @private | ||
*/ | ||
get __unparsableValue() { | ||
if (!this._inputElementValue || this.__parseDate(this._inputElementValue)) { | ||
return ''; | ||
} | ||
|
||
return this._inputElementValue; | ||
} | ||
|
||
/** | ||
* Override an event listener from `DelegateFocusMixin` | ||
* @protected | ||
|
@@ -645,27 +659,52 @@ export const DatePickerMixin = (subclass) => | |
this._shouldKeepFocusRing = focused && this._keyboardActive; | ||
} | ||
|
||
/** @private */ | ||
__dispatchChange() { | ||
this.validate(); | ||
this.dispatchEvent(new CustomEvent('change', { bubbles: true })); | ||
/** | ||
* Depending on the nature of the value change that has occurred since | ||
* the last commit attempt, triggers validation and fires an event: | ||
* | ||
* Value change | Event | ||
* :------------------------|:------------------ | ||
* empty => parsable | change | ||
* empty => unparsable | unparsable-change | ||
* parsable => empty | change | ||
* parsable => parsable | change | ||
* parsable => unparsable | change | ||
* unparsable => empty | unparsable-change | ||
* unparsable => parsable | change | ||
* unparsable => unparsable | unparsable-change | ||
* | ||
* @private | ||
*/ | ||
__commitValueChange() { | ||
const unparsableValue = this.__unparsableValue; | ||
|
||
if (this.__committedValue !== this.value) { | ||
this.validate(); | ||
this.dispatchEvent(new CustomEvent('change', { bubbles: true })); | ||
} else if (this.__committedUnparsableValue !== unparsableValue) { | ||
this.validate(); | ||
this.dispatchEvent(new CustomEvent('unparsable-change')); | ||
} | ||
|
||
this.__committedValue = this.value; | ||
this.__committedUnparsableValue = unparsableValue; | ||
} | ||
|
||
/** | ||
* Sets the given date as the value and fires a change event | ||
* if the value has changed. | ||
* Sets the given date as the value and commits it. | ||
* | ||
* @param {Date} date | ||
* @private | ||
*/ | ||
__commitDate(date) { | ||
const prevValue = this.value; | ||
|
||
// Prevent the value observer from treating the following value change | ||
// as initiated programmatically by the developer, and therefore | ||
// from automatically committing it without a change event. | ||
this.__keepCommittedValue = true; | ||
this._selectedDate = date; | ||
|
||
if (prevValue !== this.value) { | ||
this.__dispatchChange(); | ||
} | ||
this.__keepCommittedValue = false; | ||
DiegoCardoso marked this conversation as resolved.
Show resolved
Hide resolved
|
||
this.__commitValueChange(); | ||
} | ||
|
||
/** @private */ | ||
|
@@ -801,6 +840,11 @@ export const DatePickerMixin = (subclass) => | |
this._selectedDate = null; | ||
} | ||
|
||
if (!this.__keepCommittedValue) { | ||
this.__committedValue = this.value; | ||
this.__committedUnparsableValue = ''; | ||
} | ||
|
||
this._toggleHasValue(this._hasValue); | ||
} | ||
|
||
|
@@ -892,9 +936,9 @@ export const DatePickerMixin = (subclass) => | |
} | ||
|
||
/** | ||
* Tries to parse the input element's value as a date. When succeeds, | ||
* sets the resulting date as the value and fires a change event | ||
* (if the value has changed). If no i18n parser is provided, sets | ||
* Tries to parse the input element's value as a date. If the input value | ||
* is parsable, commits the resulting date as the value. Otherwise, commits | ||
* an empty string as the value. If no i18n parser is provided, commits | ||
* the focused date as the value. | ||
* | ||
* @private | ||
|
@@ -911,7 +955,6 @@ export const DatePickerMixin = (subclass) => | |
} else { | ||
this.__keepInputValue = true; | ||
this.__commitDate(null); | ||
this._selectedDate = null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note: This line is unnecessary since the selected date is already updated by |
||
this.__keepInputValue = false; | ||
} | ||
} else if (this._focusedDate) { | ||
|
@@ -927,7 +970,6 @@ export const DatePickerMixin = (subclass) => | |
this.__showOthers(); | ||
this.__showOthers = null; | ||
} | ||
|
||
window.removeEventListener('scroll', this._boundOnScroll, true); | ||
|
||
this.__commitParsedOrFocusedDate(); | ||
|
@@ -1080,16 +1122,12 @@ export const DatePickerMixin = (subclass) => | |
* @override | ||
*/ | ||
_onEnter(_event) { | ||
const oldValue = this.value; | ||
if (this.opened) { | ||
// Closing will implicitly select parsed or focused date | ||
this.close(); | ||
} else { | ||
this.__commitParsedOrFocusedDate(); | ||
} | ||
if (oldValue === this.value) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note: Now that the validation is the responsibility of the commit logic, we no longer need to trigger validation here, which fixes #6591. |
||
this.validate(); | ||
} | ||
} | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to pass the unparseable value to event to allow handling with flow? This would reduce the need for additionals calls to __committedUnparsableValue or syncing it with the server for better (?) performance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we've considered this option too, but in that case we will need to pass it also with the
change
event. Note, the component fireschange
rather thanunparsable-change
when the user changes parsable input to unparsable and vice versa. So, ultimately, we come to the same outcome as with property synchronization in Flow. Both approaches seem to offer relatively similar performance: all the data is sent within a single round-trip – which is triggered by change or unparsable-change. Property synchronization appears to be more preferable, as it doesn't require changing the API of thechange
event.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understandable, but wouldn't that be a good change anyway? This would align the change event also to the change event from the normal inputs where you can access the value https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to make sure we understand each other, did you mean passing the input value via
event.detail
or some other way? I'm not sure if the native change event does it this way. The native event provides access to the target element through which you can access the value, yes.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, dismiss my comment 😬 it's done by accessing the event.target.value instead of details