Skip to content

Commit

Permalink
refactor: handle user-selected date properly (#3530)
Browse files Browse the repository at this point in the history
  • Loading branch information
web-padawan committed Mar 9, 2022
1 parent 4be73e8 commit 247ba84
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 25 deletions.
2 changes: 1 addition & 1 deletion packages/date-picker/src/vaadin-date-picker-light.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class DatePickerLight extends ThemableMixin(DatePickerMixin(PolymerElement)) {
i18n="[[i18n]]"
fullscreen$="[[_fullscreen]]"
label="[[label]]"
selected-date="{{_selectedDate}}"
selected-date="[[_selectedDate]]"
slot="dropdown-content"
focused-date="{{_focusedDate}}"
show-week-numbers="[[showWeekNumbers]]"
Expand Down
56 changes: 44 additions & 12 deletions packages/date-picker/src/vaadin-date-picker-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ export const DatePickerMixin = (subclass) =>
if (this.autoOpenDisabled) {
const parsedDate = this._getParsedDate();
if (this._isValidDate(parsedDate)) {
this._selectedDate = parsedDate;
this._selectDate(parsedDate);
}
}

Expand Down Expand Up @@ -462,6 +462,20 @@ export const DatePickerMixin = (subclass) =>
this._overlayContent.addEventListener('close', this._close.bind(this));
this._overlayContent.addEventListener('focus-input', this._focusAndSelect.bind(this));

// User confirmed selected date by clicking the calendar.
this._overlayContent.addEventListener('date-tap', (e) => {
this.__userConfirmedDate = true;

this._selectDate(e.detail.date);
});

// User confirmed selected date by pressing Enter or Today.
this._overlayContent.addEventListener('date-selected', (e) => {
this.__userConfirmedDate = true;

this._selectDate(e.detail.date);
});

// Keep focus attribute in focusElement for styling
this._overlayContent.addEventListener('focus', () => {
this._setFocused(true);
Expand Down Expand Up @@ -508,6 +522,24 @@ export const DatePickerMixin = (subclass) =>
return inputValid && minMaxValid && inputValidity;
}

/**
* Select date on user interaction and set the flag
* to fire change event if necessary.
*
* @param {Date} dateToSelect
* @protected
*/
_selectDate(dateToSelect) {
const value = this._formatISO(dateToSelect);

// Only set flag if the value will change.
if (this.value !== value) {
this.__dispatchChange = true;
}

this._selectedDate = dateToSelect;
}

/** @private */
_close(e) {
if (e) {
Expand Down Expand Up @@ -609,9 +641,6 @@ export const DatePickerMixin = (subclass) =>
if (selectedDate === undefined || formatDate === undefined) {
return;
}
if (this.__userInputOccurred) {
this.__dispatchChange = true;
}
const value = this._formatISO(selectedDate);

this.__keepInputValue || this._applyInputValue(selectedDate);
Expand All @@ -620,8 +649,6 @@ export const DatePickerMixin = (subclass) =>
this.validate();
this.value = value;
}
this.__userInputOccurred = false;
this.__dispatchChange = false;
this._ignoreFocusedDateChange = true;
this._focusedDate = selectedDate;
this._ignoreFocusedDateChange = false;
Expand All @@ -632,7 +659,6 @@ export const DatePickerMixin = (subclass) =>
if (focusedDate === undefined || formatDate === undefined) {
return;
}
this.__userInputOccurred = true;
if (!this._ignoreFocusedDateChange && !this._noInput) {
this._applyInputValue(focusedDate);
}
Expand Down Expand Up @@ -751,14 +777,15 @@ export const DatePickerMixin = (subclass) =>
const parsedDate = this._getParsedDate(inputValue);

if (this._isValidDate(parsedDate)) {
this._selectedDate = parsedDate;
this._selectDate(parsedDate);
} else {
this.__keepInputValue = true;
this._selectDate(null);
this._selectedDate = null;
this.__keepInputValue = false;
}
} else if (this._focusedDate) {
this._selectedDate = this._focusedDate;
this._selectDate(this._focusedDate);
}
this._ignoreFocusedDateChange = false;
}
Expand All @@ -774,7 +801,12 @@ export const DatePickerMixin = (subclass) =>
this._touchPrevented = [];
}

this._selectParsedOrFocusedDate();
// No need to select date on close if it was confirmed by the user.
if (this.__userConfirmedDate) {
this.__userConfirmedDate = false;
} else {
this._selectParsedOrFocusedDate();
}

if (this._nativeInput && this._nativeInput.selectionStart) {
this._nativeInput.selectionStart = this._nativeInput.selectionEnd;
Expand Down Expand Up @@ -903,7 +935,7 @@ export const DatePickerMixin = (subclass) =>
const isValidDate = this._isValidDate(parsedDate);
if (this.opened) {
if (this._overlayInitialized && this._overlayContent.focusedDate && isValidDate) {
this._selectedDate = this._overlayContent.focusedDate;
this._selectDate(this._overlayContent.focusedDate);
}
this.close();
} else if (!isValidDate && this.inputElement.value !== '') {
Expand All @@ -926,7 +958,7 @@ export const DatePickerMixin = (subclass) =>
} else if (this.autoOpenDisabled) {
// Do not restore selected date if Esc was pressed after clearing input field
if (this.inputElement.value === '') {
this._selectedDate = null;
this._selectDate(null);
}
this._applyInputValue(this._selectedDate);
} else {
Expand Down
24 changes: 17 additions & 7 deletions packages/date-picker/src/vaadin-date-picker-overlay-content.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,7 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
* The value for this element.
*/
selectedDate: {
type: Date,
notify: true
type: Date
},

/**
Expand Down Expand Up @@ -370,6 +369,17 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
this._scrollToPosition(this._differenceInMonths(date, this._originDate), animate);
}

/**
* Select a date and fire event indicating user interaction.
* @protected
*/
_selectDate(dateToSelect) {
this.selectedDate = dateToSelect;
this.dispatchEvent(
new CustomEvent('date-selected', { detail: { date: dateToSelect }, bubbles: true, composed: true })
);
}

_focusedDateChanged(focusedDate) {
this.revealDate(focusedDate);
}
Expand Down Expand Up @@ -460,7 +470,7 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
if (Math.abs(this.$.monthScroller.position - this._differenceInMonths(today, this._originDate)) < 0.001) {
// Select today only if the month scroller is positioned approximately
// at the beginning of the current month
this.selectedDate = today;
this._selectDate(today);
this._close();
} else {
this._scrollToCurrentMonth();
Expand Down Expand Up @@ -659,7 +669,7 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
}

_clear() {
this.selectedDate = '';
this._selectDate('');
}

_close() {
Expand All @@ -683,10 +693,10 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po

__toggleDate(date) {
if (dateEquals(date, this.selectedDate)) {
this.selectedDate = '';
this._clear();
this.focusedDate = date;
} else {
this.selectedDate = date;
this._selectDate(date);
}
}

Expand All @@ -711,7 +721,7 @@ class DatePickerOverlayContent extends ControllerMixin(ThemableMixin(DirMixin(Po
handled = true;
break;
case 'Enter':
this.selectedDate = this.focusedDate;
this._selectDate(this.focusedDate);
this._close();
handled = true;
break;
Expand Down
2 changes: 1 addition & 1 deletion packages/date-picker/src/vaadin-date-picker.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ class DatePicker extends DatePickerMixin(InputControlMixin(ThemableMixin(Element
i18n="[[i18n]]"
fullscreen$="[[_fullscreen]]"
label="[[label]]"
selected-date="{{_selectedDate}}"
selected-date="[[_selectedDate]]"
slot="dropdown-content"
focused-date="{{_focusedDate}}"
show-week-numbers="[[showWeekNumbers]]"
Expand Down
4 changes: 3 additions & 1 deletion packages/date-picker/src/vaadin-month-calendar.js
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,9 @@ class MonthCalendar extends FocusMixin(ThemableMixin(PolymerElement)) {
_handleTap(e) {
if (!this.ignoreTaps && !this._notTapping && e.target.date && !e.target.hasAttribute('disabled')) {
this.selectedDate = e.target.date;
this.dispatchEvent(new CustomEvent('date-tap', { bubbles: true, composed: true }));
this.dispatchEvent(
new CustomEvent('date-tap', { detail: { date: e.target.date }, bubbles: true, composed: true })
);
}
}

Expand Down
7 changes: 4 additions & 3 deletions packages/date-picker/test/basic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ describe('basic features', () => {
it('should close on overlay date tap', async () => {
await open(datepicker);
const spy = sinon.spy(datepicker, 'close');
getOverlayContent(datepicker).dispatchEvent(new CustomEvent('date-tap', { bubbles: true, composed: true }));
const evt = new CustomEvent('date-tap', { detail: { date: new Date() }, bubbles: true, composed: true });
getOverlayContent(datepicker).dispatchEvent(evt);
expect(spy.called).to.be.true;
});

Expand Down Expand Up @@ -424,7 +425,7 @@ describe('basic features', () => {
});

datepicker.open();
getOverlayContent(datepicker).selectedDate = new Date('2017-01-01'); // invalid
getOverlayContent(datepicker)._selectDate(new Date('2017-01-01')); // invalid
});

it('should change invalid state only once', (done) => {
Expand All @@ -436,7 +437,7 @@ describe('basic features', () => {
const invalidChangedSpy = sinon.spy();
datepicker.addEventListener('invalid-changed', invalidChangedSpy);
datepicker.open();
getOverlayContent(datepicker).selectedDate = new Date('2017-01-01');
getOverlayContent(datepicker)._selectDate(new Date('2017-01-01'));
});

it('should scroll to min date when today is not allowed', (done) => {
Expand Down

0 comments on commit 247ba84

Please sign in to comment.