diff --git a/packages/date-time-picker/src/vaadin-date-time-picker.js b/packages/date-time-picker/src/vaadin-date-time-picker.js
index e69617e627..83319d9ebf 100644
--- a/packages/date-time-picker/src/vaadin-date-time-picker.js
+++ b/packages/date-time-picker/src/vaadin-date-time-picker.js
@@ -5,6 +5,7 @@
*/
import './vaadin-date-time-picker-date-picker.js';
import './vaadin-date-time-picker-time-picker.js';
+import { FlattenedNodesObserver } from '@polymer/polymer/lib/utils/flattened-nodes-observer.js';
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
import { DisabledMixin } from '@vaadin/component-base/src/disabled-mixin.js';
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
@@ -327,6 +328,24 @@ class DateTimePicker extends FieldMixin(SlotMixin(DisabledMixin(ThemableMixin(El
i18n: {
type: Object,
value: () => Object.assign({}, datePickerI18nDefaults, timePickerI18nDefaults)
+ },
+
+ /**
+ * The current slotted date picker.
+ * @private
+ */
+ __datePicker: {
+ type: HTMLElement,
+ observer: '__datePickerChanged'
+ },
+
+ /**
+ * The current slotted time picker.
+ * @private
+ */
+ __timePicker: {
+ type: HTMLElement,
+ observer: '__timePickerChanged'
}
};
}
@@ -378,6 +397,10 @@ class DateTimePicker extends FieldMixin(SlotMixin(DisabledMixin(ThemableMixin(El
this.__changeEventHandler = this.__changeEventHandler.bind(this);
this.__valueChangedEventHandler = this.__valueChangedEventHandler.bind(this);
+
+ this._observer = new FlattenedNodesObserver(this, (info) => {
+ this.__onDomChange(info.addedNodes);
+ });
}
/** @protected */
@@ -390,11 +413,8 @@ class DateTimePicker extends FieldMixin(SlotMixin(DisabledMixin(ThemableMixin(El
}
});
- this.__datePickerChanged();
- this.__timePickerChanged();
-
- this.$.dateSlot.addEventListener('slotchange', this.__datePickerChanged.bind(this));
- this.$.timeSlot.addEventListener('slotchange', this.__timePickerChanged.bind(this));
+ this.__datePicker = this._getDirectSlotChild('date-picker');
+ this.__timePicker = this._getDirectSlotChild('time-picker');
if (this.autofocus && !this.disabled) {
window.requestAnimationFrame(() => this.focus());
@@ -449,87 +469,98 @@ class DateTimePicker extends FieldMixin(SlotMixin(DisabledMixin(ThemableMixin(El
}
/** @private */
- __datePickerChanged() {
- const datePicker = this._getDirectSlotChild('date-picker');
- if (this.__datePicker === datePicker || !datePicker) {
+ __onDomChange(addedNodes) {
+ addedNodes
+ .filter((node) => node.nodeType === Node.ELEMENT_NODE)
+ .forEach((node) => {
+ const slotAttributeValue = node.getAttribute('slot');
+
+ if (slotAttributeValue === 'date-picker') {
+ this.__datePicker = node;
+ } else if (slotAttributeValue === 'time-picker') {
+ this.__timePicker = node;
+ }
+ });
+ }
+
+ /** @private */
+ __datePickerChanged(newDatePicker, existingDatePicker) {
+ if (!newDatePicker) {
return;
}
- if (this.__datePicker) {
+ if (existingDatePicker) {
// Remove an existing date picker
- this.__removeInputListeners(this.__datePicker);
- this.__datePicker.remove();
+ this.__removeInputListeners(existingDatePicker);
+ existingDatePicker.remove();
}
- this.__addInputListeners(datePicker);
- this.__datePicker = datePicker;
+ this.__addInputListeners(newDatePicker);
- if (datePicker.__defaultPicker) {
+ if (newDatePicker.__defaultPicker) {
// Synchronize properties to default date picker
- datePicker.placeholder = this.datePlaceholder;
- datePicker.invalid = this.invalid;
- datePicker.initialPosition = this.initialPosition;
- datePicker.showWeekNumbers = this.showWeekNumbers;
- this.__syncI18n(datePicker, this, datePickerI18nProps);
+ newDatePicker.placeholder = this.datePlaceholder;
+ newDatePicker.invalid = this.invalid;
+ newDatePicker.initialPosition = this.initialPosition;
+ newDatePicker.showWeekNumbers = this.showWeekNumbers;
+ this.__syncI18n(newDatePicker, this, datePickerI18nProps);
} else {
// Synchronize properties from slotted date picker
- this.datePlaceholder = datePicker.placeholder;
- this.initialPosition = datePicker.initialPosition;
- this.showWeekNumbers = datePicker.showWeekNumbers;
- this.__syncI18n(this, datePicker, datePickerI18nProps);
+ this.datePlaceholder = newDatePicker.placeholder;
+ this.initialPosition = newDatePicker.initialPosition;
+ this.showWeekNumbers = newDatePicker.showWeekNumbers;
+ this.__syncI18n(this, newDatePicker, datePickerI18nProps);
}
// min and max are always synchronized from date time picker (host) to inner fields because time picker
// min and max need to be dynamically set depending on currently selected date instead of simple propagation
- datePicker.min = this.__formatDateISO(this.__minDateTime, this.__defaultDateMinMaxValue);
- datePicker.max = this.__formatDateISO(this.__maxDateTime, this.__defaultDateMinMaxValue);
- datePicker.required = this.required;
- datePicker.disabled = this.disabled;
- datePicker.readonly = this.readonly;
- datePicker.autoOpenDisabled = this.autoOpenDisabled;
+ newDatePicker.min = this.__formatDateISO(this.__minDateTime, this.__defaultDateMinMaxValue);
+ newDatePicker.max = this.__formatDateISO(this.__maxDateTime, this.__defaultDateMinMaxValue);
+ newDatePicker.required = this.required;
+ newDatePicker.disabled = this.disabled;
+ newDatePicker.readonly = this.readonly;
+ newDatePicker.autoOpenDisabled = this.autoOpenDisabled;
// Disable default internal validation for the component
- datePicker.validate = () => {};
- datePicker._validateInput = () => {};
+ newDatePicker.validate = () => {};
+ newDatePicker._validateInput = () => {};
}
/** @private */
- __timePickerChanged() {
- const timePicker = this._getDirectSlotChild('time-picker');
- if (this.__timePicker === timePicker || !timePicker) {
+ __timePickerChanged(newTimePicker, existingTimePicker) {
+ if (!newTimePicker) {
return;
}
- if (this.__timePicker) {
+ if (existingTimePicker) {
// Remove an existing time picker
- this.__removeInputListeners(this.__timePicker);
- this.__timePicker.remove();
+ this.__removeInputListeners(existingTimePicker);
+ existingTimePicker.remove();
}
- this.__addInputListeners(timePicker);
- this.__timePicker = timePicker;
+ this.__addInputListeners(newTimePicker);
- if (timePicker.__defaultPicker) {
+ if (newTimePicker.__defaultPicker) {
// Synchronize properties to default time picker
- timePicker.placeholder = this.timePlaceholder;
- timePicker.step = this.step;
- timePicker.invalid = this.invalid;
- this.__syncI18n(timePicker, this, timePickerI18nProps);
+ newTimePicker.placeholder = this.timePlaceholder;
+ newTimePicker.step = this.step;
+ newTimePicker.invalid = this.invalid;
+ this.__syncI18n(newTimePicker, this, timePickerI18nProps);
} else {
// Synchronize properties from slotted time picker
- this.timePlaceholder = timePicker.placeholder;
- this.step = timePicker.step;
- this.__syncI18n(this, timePicker, timePickerI18nProps);
+ this.timePlaceholder = newTimePicker.placeholder;
+ this.step = newTimePicker.step;
+ this.__syncI18n(this, newTimePicker, timePickerI18nProps);
}
// min and max are always synchronized from parent to slotted because time picker min and max
// need to be dynamically set depending on currently selected date instead of simple propagation
this.__updateTimePickerMinMax();
- timePicker.required = this.required;
- timePicker.disabled = this.disabled;
- timePicker.readonly = this.readonly;
- timePicker.autoOpenDisabled = this.autoOpenDisabled;
+ newTimePicker.required = this.required;
+ newTimePicker.disabled = this.disabled;
+ newTimePicker.readonly = this.readonly;
+ newTimePicker.autoOpenDisabled = this.autoOpenDisabled;
// Disable default internal validation for the component
- timePicker.validate = () => {};
+ newTimePicker.validate = () => {};
}
/** @private */
@@ -561,12 +592,10 @@ class DateTimePicker extends FieldMixin(SlotMixin(DisabledMixin(ThemableMixin(El
/** @private */
__i18nChanged(changeRecord) {
- this.__datePickerChanged();
if (this.__datePicker) {
this.__datePicker.set(changeRecord.path, changeRecord.value);
}
- this.__timePickerChanged();
if (this.__timePicker) {
this.__timePicker.set(changeRecord.path, changeRecord.value);
}
diff --git a/packages/date-time-picker/test/basic.test.js b/packages/date-time-picker/test/basic.test.js
index e7ff872359..4c41dca6c2 100644
--- a/packages/date-time-picker/test/basic.test.js
+++ b/packages/date-time-picker/test/basic.test.js
@@ -13,8 +13,6 @@ const fixtures = {
`,
'lazy-inputs': `
-
-
`,
'default-values': `
@@ -25,8 +23,6 @@ const fixtures = {
`,
'lazy-values': `
-
-
`
};
@@ -303,9 +299,14 @@ describe('Theme attribute', () => {
timePicker = dateTimePicker.querySelector('vaadin-time-picker');
if (set === 'lazy') {
- // Assign the slots lazily simulating the case if Flow adds the slotted elements after date time picker is ready
+ // Add slotted children lazily simulating the case where Flow adds the slotted elements after date time picker is ready
+ datePicker = document.createElement('vaadin-date-picker');
datePicker.slot = 'date-picker';
+ timePicker = document.createElement('vaadin-time-picker');
timePicker.slot = 'time-picker';
+ dateTimePicker.appendChild(datePicker);
+ dateTimePicker.appendChild(timePicker);
+ // Wait for FlattenedNodeObserver to trigger
await aTimeout(0);
}
});
@@ -358,6 +359,16 @@ describe('Theme attribute', () => {
});
if (set === 'lazy') {
+ it('should not contain original date picker', () => {
+ expect(originalDatePicker).not.to.be.undefined;
+ expect(dateTimePicker.contains(originalDatePicker)).to.be.false;
+ });
+
+ it('should not contain original time picker', () => {
+ expect(originalTimePicker).not.to.be.undefined;
+ expect(dateTimePicker.contains(originalTimePicker)).to.be.false;
+ });
+
it('should not react to changes on discarded pickers', () => {
sinon.spy(dateTimePicker, '__valueChangedEventHandler');
sinon.spy(dateTimePicker, '__changeEventHandler');
@@ -395,9 +406,16 @@ describe('Theme attribute', () => {
dateTimePicker = fixtureSync(fixtures[`${set}-values`]);
if (set === 'lazy') {
- // Assign the slots lazily simulating the case if Flow adds the slotted elements after date time picker is ready
- dateTimePicker.querySelector('vaadin-date-picker').slot = 'date-picker';
- dateTimePicker.querySelector('vaadin-time-picker').slot = 'time-picker';
+ // Add slotted children lazily simulating the case where Flow adds the slotted elements after date time picker is ready
+ const datePicker = document.createElement('vaadin-date-picker');
+ datePicker.value = '2019-09-16';
+ datePicker.slot = 'date-picker';
+ const timePicker = document.createElement('vaadin-time-picker');
+ timePicker.value = '15:00';
+ timePicker.slot = 'time-picker';
+ dateTimePicker.appendChild(datePicker);
+ dateTimePicker.appendChild(timePicker);
+ // Wait for FlattenedNodeObserver to trigger
await aTimeout(0);
}
});