Skip to content

Commit

Permalink
experiment: add LitElement based version of date-time-picker (#8089)
Browse files Browse the repository at this point in the history
  • Loading branch information
web-padawan authored Nov 4, 2024
1 parent b19451d commit 194af49
Show file tree
Hide file tree
Showing 31 changed files with 248 additions and 74 deletions.
3 changes: 2 additions & 1 deletion packages/date-time-picker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
"@vaadin/time-picker": "24.6.0-alpha7",
"@vaadin/vaadin-lumo-styles": "24.6.0-alpha7",
"@vaadin/vaadin-material-styles": "24.6.0-alpha7",
"@vaadin/vaadin-themable-mixin": "24.6.0-alpha7"
"@vaadin/vaadin-themable-mixin": "24.6.0-alpha7",
"lit": "^3.0.0"
},
"devDependencies": {
"@vaadin/chai-plugins": "24.6.0-alpha7",
Expand Down
56 changes: 54 additions & 2 deletions packages/date-time-picker/src/vaadin-date-time-picker-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { DisabledMixin } from '@vaadin/a11y-base/src/disabled-mixin.js';
import { FocusMixin } from '@vaadin/a11y-base/src/focus-mixin.js';
import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
import { TooltipController } from '@vaadin/component-base/src/tooltip-controller.js';
import {
dateEquals,
formatUTCISODate,
Expand All @@ -25,10 +26,15 @@ const timePickerI18nProps = Object.keys(timePickerI18nDefaults);
*
* @private
*/
export class PickerSlotController extends SlotController {
class PickerSlotController extends SlotController {
constructor(host, type) {
super(host, `${type}-picker`, `vaadin-${type}-picker`, {
initializer: (picker, host) => {
// Ensure initial value-changed is fired on the slotted pickers
// synchronously in Lit version to avoid overriding host value.
if (picker.performUpdate) {
picker.performUpdate();
}
const prop = `__${type}Picker`;
host[prop] = picker;
},
Expand Down Expand Up @@ -69,6 +75,7 @@ export const DateTimePickerMixin = (superClass) =>
notify: true,
value: '',
observer: '__valueChanged',
sync: true,
},

/**
Expand All @@ -84,6 +91,7 @@ export const DateTimePickerMixin = (superClass) =>
min: {
type: String,
observer: '__minChanged',
sync: true,
},

/**
Expand All @@ -99,6 +107,7 @@ export const DateTimePickerMixin = (superClass) =>
max: {
type: String,
observer: '__maxChanged',
sync: true,
},

/**
Expand All @@ -108,6 +117,7 @@ export const DateTimePickerMixin = (superClass) =>
__minDateTime: {
type: Date,
value: '',
sync: true,
},

/**
Expand All @@ -117,6 +127,7 @@ export const DateTimePickerMixin = (superClass) =>
__maxDateTime: {
type: Date,
value: '',
sync: true,
},

/**
Expand All @@ -125,6 +136,7 @@ export const DateTimePickerMixin = (superClass) =>
*/
datePlaceholder: {
type: String,
sync: true,
},

/**
Expand All @@ -133,6 +145,7 @@ export const DateTimePickerMixin = (superClass) =>
*/
timePlaceholder: {
type: String,
sync: true,
},

/**
Expand All @@ -153,6 +166,7 @@ export const DateTimePickerMixin = (superClass) =>
*/
step: {
type: Number,
sync: true,
},

/**
Expand All @@ -163,6 +177,7 @@ export const DateTimePickerMixin = (superClass) =>
*/
initialPosition: {
type: String,
sync: true,
},

/**
Expand All @@ -174,6 +189,7 @@ export const DateTimePickerMixin = (superClass) =>
showWeekNumbers: {
type: Boolean,
value: false,
sync: true,
},

/**
Expand All @@ -182,6 +198,7 @@ export const DateTimePickerMixin = (superClass) =>
*/
autoOpenDisabled: {
type: Boolean,
sync: true,
},

/**
Expand All @@ -192,6 +209,7 @@ export const DateTimePickerMixin = (superClass) =>
type: Boolean,
value: false,
reflectToAttribute: true,
sync: true,
},

/**
Expand All @@ -208,6 +226,7 @@ export const DateTimePickerMixin = (superClass) =>
*/
__selectedDateTime: {
type: Date,
sync: true,
},

/**
Expand All @@ -222,6 +241,7 @@ export const DateTimePickerMixin = (superClass) =>
*/
i18n: {
type: Object,
sync: true,
value: () => ({ ...datePickerI18nDefaults, ...timePickerI18nDefaults }),
},

Expand All @@ -244,6 +264,7 @@ export const DateTimePickerMixin = (superClass) =>
*/
__datePicker: {
type: Object,
sync: true,
observer: '__datePickerChanged',
},

Expand All @@ -253,6 +274,7 @@ export const DateTimePickerMixin = (superClass) =>
*/
__timePicker: {
type: Object,
sync: true,
observer: '__timePickerChanged',
},
};
Expand Down Expand Up @@ -304,6 +326,32 @@ export const DateTimePickerMixin = (superClass) =>
return values.every(Boolean) ? values.join('T') : '';
}

/** @protected */
ready() {
super.ready();

this._datePickerController = new PickerSlotController(this, 'date');
this.addController(this._datePickerController);

this._timePickerController = new PickerSlotController(this, 'time');
this.addController(this._timePickerController);

if (this.autofocus && !this.disabled) {
window.requestAnimationFrame(() => this.focus());
}

this.setAttribute('role', 'group');

this._tooltipController = new TooltipController(this);
this.addController(this._tooltipController);
this._tooltipController.setPosition('top');
this._tooltipController.setShouldShow((target) => {
return target.__datePicker && !target.__datePicker.opened && target.__timePicker && !target.__timePicker.opened;
});

this.ariaTarget = this;
}

focus() {
if (this.__datePicker) {
this.__datePicker.focus();
Expand Down Expand Up @@ -878,7 +926,11 @@ export const DateTimePickerMixin = (superClass) =>
// The component has a value, update the new pickers values
this.__selectedDateTimeChanged(this.__selectedDateTime);

if (this.min || this.max) {
// When using Polymer version, mix and max observers are triggered initially
// before `ready()` and by that time pickers are not yet initialized, so we
// run initial validation here. Lit version runs observers differently and
// this observer is executed first - ignore it to prevent validating twice.
if ((this.min && this.__minDateTime) || (this.max && this.__maxDateTime)) {
this.validate();
}
}
Expand Down
29 changes: 1 addition & 28 deletions packages/date-time-picker/src/vaadin-date-time-picker.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ import '@vaadin/time-picker/src/vaadin-time-picker.js';
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
import { TooltipController } from '@vaadin/component-base/src/tooltip-controller.js';
import { inputFieldShared } from '@vaadin/field-base/src/styles/input-field-shared-styles.js';
import { registerStyles, ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { DateTimePickerMixin, PickerSlotController } from './vaadin-date-time-picker-mixin.js';
import { DateTimePickerMixin } from './vaadin-date-time-picker-mixin.js';

registerStyles('vaadin-date-time-picker', inputFieldShared, { moduleId: 'vaadin-date-time-picker' });

Expand Down Expand Up @@ -127,32 +126,6 @@ class DateTimePicker extends DateTimePickerMixin(ThemableMixin(ElementMixin(Poly
static get is() {
return 'vaadin-date-time-picker';
}

/** @protected */
ready() {
super.ready();

this._datePickerController = new PickerSlotController(this, 'date');
this.addController(this._datePickerController);

this._timePickerController = new PickerSlotController(this, 'time');
this.addController(this._timePickerController);

if (this.autofocus && !this.disabled) {
window.requestAnimationFrame(() => this.focus());
}

this.setAttribute('role', 'group');

this._tooltipController = new TooltipController(this);
this.addController(this._tooltipController);
this._tooltipController.setPosition('top');
this._tooltipController.setShouldShow((target) => {
return target.__datePicker && !target.__datePicker.opened && target.__timePicker && !target.__timePicker.opened;
});

this.ariaTarget = this;
}
}

defineCustomElement(DateTimePicker);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './vaadin-date-time-picker.js';
86 changes: 86 additions & 0 deletions packages/date-time-picker/src/vaadin-lit-date-time-picker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* @license
* Copyright (c) 2019 - 2024 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import '@vaadin/date-picker/src/vaadin-lit-date-picker.js';
import '@vaadin/time-picker/src/vaadin-lit-time-picker.js';
import { css, html, LitElement } from 'lit';
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
import { inputFieldShared } from '@vaadin/field-base/src/styles/input-field-shared-styles.js';
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { DateTimePickerMixin } from './vaadin-date-time-picker-mixin.js';

/**
* LitElement based version of `<vaadin-date-time-picker>` web component.
*
* ## Disclaimer
*
* This component is an experiment and not yet a part of Vaadin platform.
* There is no ETA regarding specific Vaadin version where it'll land.
* Feel free to try this code in your apps as per Apache 2.0 license.
*/
class DateTimePicker extends DateTimePickerMixin(ThemableMixin(ElementMixin(PolylitMixin(LitElement)))) {
static get is() {
return 'vaadin-date-time-picker';
}

static get styles() {
return [
inputFieldShared,
css`
.vaadin-date-time-picker-container {
--vaadin-field-default-width: auto;
}
.slots {
display: flex;
--vaadin-field-default-width: 12em;
}
.slots ::slotted([slot='date-picker']) {
min-width: 0;
flex: 1 1 auto;
}
.slots ::slotted([slot='time-picker']) {
min-width: 0;
flex: 1 1.65 auto;
}
`,
];
}

/** @protected */
render() {
return html`
<div class="vaadin-date-time-picker-container">
<div part="label" @click="${this.focus}">
<slot name="label"></slot>
<span part="required-indicator" aria-hidden="true"></span>
</div>
<div class="slots">
<slot name="date-picker" id="dateSlot"></slot>
<slot name="time-picker" id="timeSlot"></slot>
</div>
<div part="helper-text">
<slot name="helper"></slot>
</div>
<div part="error-message">
<slot name="error-message"></slot>
</div>
</div>
<slot name="tooltip"></slot>
`;
}
}

defineCustomElement(DateTimePicker);

export { DateTimePicker };
2 changes: 2 additions & 0 deletions packages/date-time-picker/test/aria-lit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import '../src/vaadin-lit-date-time-picker.js';
import './aria.common.js';
2 changes: 2 additions & 0 deletions packages/date-time-picker/test/aria-polymer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import '../src/vaadin-date-time-picker.js';
import './aria.common.js';
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { expect } from '@vaadin/chai-plugins';
import { aTimeout, fixtureSync } from '@vaadin/testing-helpers';
import '../vaadin-date-time-picker.js';
import { aTimeout, fixtureSync, nextFrame, nextRender } from '@vaadin/testing-helpers';

describe('ARIA', () => {
let dateTimePicker, error, label, helper;

beforeEach(() => {
beforeEach(async () => {
dateTimePicker = fixtureSync(
`<vaadin-date-time-picker helper-text="Helper text" label="Date and time"></vaadin-date-time-picker>`,
);
await nextRender();
label = dateTimePicker.querySelector(':scope > [slot=label]');
helper = dateTimePicker.querySelector(':scope > [slot=helper]');
error = dateTimePicker.querySelector(':scope > [slot=error-message]');
Expand All @@ -34,6 +34,7 @@ describe('ARIA', () => {

it('should add error message to aria-describedby when field is invalid', async () => {
dateTimePicker.invalid = true;
await nextFrame();
await aTimeout(0);
const aria = dateTimePicker.getAttribute('aria-describedby');
expect(aria).to.include(helper.id);
Expand Down
2 changes: 2 additions & 0 deletions packages/date-time-picker/test/basic-lit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import '../src/vaadin-lit-date-time-picker.js';
import './basic.common.js';
2 changes: 2 additions & 0 deletions packages/date-time-picker/test/basic-polymer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import '../src/vaadin-date-time-picker.js';
import './basic.common.js';
Loading

0 comments on commit 194af49

Please sign in to comment.