fix(ui): simplify datepicker structure

This commit is contained in:
Thijs Louisse 2022-12-06 10:26:54 +01:00 committed by Thijs Louisse
parent 64c0e26c20
commit 36910e5d70
8 changed files with 277 additions and 252 deletions

View file

@ -0,0 +1,5 @@
---
'@lion/ui': patch
---
datepicker uses no calender-overlay-frame element anymore

View file

@ -1,125 +0,0 @@
import { css, html, LitElement } from 'lit';
import { LocalizeMixin } from '@lion/ui/localize.js';
export class LionCalendarOverlayFrame extends LocalizeMixin(LitElement) {
static get styles() {
return [
css`
:host {
display: inline-block;
background: white;
position: relative;
}
:host([hidden]) {
display: none;
}
.calendar-overlay__header {
display: flex;
}
.calendar-overlay__heading {
padding: 16px 16px 8px;
flex: 1;
}
.calendar-overlay__heading > .calendar-overlay__close-button {
flex: none;
}
.calendar-overlay__close-button {
min-width: 40px;
min-height: 32px;
border-width: 0;
padding: 0;
font-size: 24px;
}
`,
];
}
static get localizeNamespaces() {
return [
{
'lion-calendar-overlay-frame': /** @param {string} locale */ locale => {
switch (locale) {
case 'bg-BG':
return import('@lion/ui/overlays-translations/bg-BG.js');
case 'cs-CZ':
return import('@lion/ui/overlays-translations/cs-CZ.js');
case 'de-DE':
return import('@lion/ui/overlays-translations/de-DE.js');
case 'en-AU':
return import('@lion/ui/overlays-translations/en-AU.js');
case 'en-GB':
return import('@lion/ui/overlays-translations/en-GB.js');
case 'en-US':
return import('@lion/ui/overlays-translations/en-US.js');
case 'en-PH':
return import('@lion/ui/overlays-translations/en.js');
case 'es-ES':
return import('@lion/ui/overlays-translations/es-ES.js');
case 'fr-FR':
return import('@lion/ui/overlays-translations/fr-FR.js');
case 'fr-BE':
return import('@lion/ui/overlays-translations/fr-BE.js');
case 'hu-HU':
return import('@lion/ui/overlays-translations/hu-HU.js');
case 'it-IT':
return import('@lion/ui/overlays-translations/it-IT.js');
case 'nl-BE':
return import('@lion/ui/overlays-translations/nl-BE.js');
case 'nl-NL':
return import('@lion/ui/overlays-translations/nl-NL.js');
case 'pl-PL':
return import('@lion/ui/overlays-translations/pl-PL.js');
case 'ro-RO':
return import('@lion/ui/overlays-translations/ro-RO.js');
case 'ru-RU':
return import('@lion/ui/overlays-translations/ru-RU.js');
case 'sk-SK':
return import('@lion/ui/overlays-translations/sk-SK.js');
case 'uk-UA':
return import('@lion/ui/overlays-translations/uk-UA.js');
case 'zh-CN':
return import('@lion/ui/overlays-translations/zh.js');
default:
return import('@lion/ui/overlays-translations/en.js');
}
},
},
...super.localizeNamespaces,
];
}
/** @private */
__dispatchCloseEvent() {
this.dispatchEvent(new Event('close-overlay'));
}
render() {
// eslint-disable-line class-methods-use-this
return html`
<div class="calendar-overlay">
<div class="calendar-overlay__header">
<h1 class="calendar-overlay__heading">
<slot name="heading"></slot>
</h1>
<button
@click="${this.__dispatchCloseEvent}"
id="close-button"
title="${this.msgLit('lion-calendar-overlay-frame:close')}"
aria-label="${this.msgLit('lion-calendar-overlay-frame:close')}"
class="calendar-overlay__close-button"
>
<slot name="close-icon">&times;</slot>
</button>
</div>
<div id="overlay-content-node-wrapper">
<slot name="content"></slot>
</div>
</div>
`;
}
}

View file

@ -1,7 +1,7 @@
/* eslint-disable import/no-extraneous-dependencies */
import { LionCalendar } from '@lion/ui/calendar.js';
import { ScopedElementsMixin } from '@open-wc/scoped-elements';
import { html, render } from 'lit';
import { html, css } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
import { LionInputDate } from '@lion/ui/input-date.js';
import {
@ -10,7 +10,8 @@ import {
withModalDialogConfig,
ArrowMixin,
} from '@lion/ui/overlays.js';
import { LionCalendarOverlayFrame } from './LionCalendarOverlayFrame.js';
import { LocalizeMixin } from '@lion/ui/localize.js';
import { localizeNamespaceLoader } from './localizeNamespaceLoader.js';
/**
* @typedef {import('../../form-core/src/validate/Validator.js').Validator} Validator
@ -21,16 +22,49 @@ import { LionCalendarOverlayFrame } from './LionCalendarOverlayFrame.js';
* @customElement lion-input-datepicker
*/
export class LionInputDatepicker extends ScopedElementsMixin(
ArrowMixin(OverlayMixin(LionInputDate)),
ArrowMixin(OverlayMixin(LocalizeMixin(LionInputDate))),
) {
static get scopedElements() {
return {
...super.scopedElements,
'lion-calendar': LionCalendar,
'lion-calendar-overlay-frame': LionCalendarOverlayFrame,
};
}
static get styles() {
return [
...super.styles,
css`
.calendar__overlay-frame {
display: inline-block;
background: white;
position: relative;
}
.calendar-overlay__header {
display: flex;
}
.calendar-overlay__heading {
padding: 16px 16px 8px;
flex: 1;
}
.calendar-overlay__heading > .calendar-overlay__close-button {
flex: none;
}
.calendar-overlay__close-button {
min-width: 40px;
min-height: 32px;
border-width: 0;
padding: 0;
font-size: 24px;
}
`,
];
}
/** @type {any} */
static get properties() {
return {
@ -68,101 +102,12 @@ export class LionInputDatepicker extends ScopedElementsMixin(
get slots() {
return {
...super.slots,
[this._calendarInvokerSlot]: () => {
const renderParent = document.createElement('div');
render(
this._invokerTemplate(),
renderParent,
/** @type {RenderOptions} */ ({
scopeName: this.localName,
eventContext: this,
}),
);
return /** @type {HTMLElement} */ (renderParent.firstElementChild);
},
[this._calendarInvokerSlot]: () => this._invokerTemplate(),
};
}
static get localizeNamespaces() {
return [
{
'lion-input-datepicker': /** @param {string} locale */ locale => {
switch (locale) {
case 'bg-BG':
return import('@lion/ui/input-datepicker-translations/bg-BG.js');
case 'bg':
return import('@lion/ui/input-datepicker-translations/bg.js');
case 'cs-CZ':
return import('@lion/ui/input-datepicker-translations/cs-CZ.js');
case 'cs':
return import('@lion/ui/input-datepicker-translations/cs.js');
case 'de-DE':
return import('@lion/ui/input-datepicker-translations/de-DE.js');
case 'de':
return import('@lion/ui/input-datepicker-translations/de.js');
case 'en-AU':
return import('@lion/ui/input-datepicker-translations/en-AU.js');
case 'en-GB':
return import('@lion/ui/input-datepicker-translations/en-GB.js');
case 'en-US':
return import('@lion/ui/input-datepicker-translations/en-US.js');
case 'en-PH':
case 'en':
return import('@lion/ui/input-datepicker-translations/en.js');
case 'es-ES':
return import('@lion/ui/input-datepicker-translations/es-ES.js');
case 'es':
return import('@lion/ui/input-datepicker-translations/es.js');
case 'fr-FR':
return import('@lion/ui/input-datepicker-translations/fr-FR.js');
case 'fr-BE':
return import('@lion/ui/input-datepicker-translations/fr-BE.js');
case 'fr':
return import('@lion/ui/input-datepicker-translations/fr.js');
case 'hu-HU':
return import('@lion/ui/input-datepicker-translations/hu-HU.js');
case 'hu':
return import('@lion/ui/input-datepicker-translations/hu.js');
case 'it-IT':
return import('@lion/ui/input-datepicker-translations/it-IT.js');
case 'it':
return import('@lion/ui/input-datepicker-translations/it.js');
case 'nl-BE':
return import('@lion/ui/input-datepicker-translations/nl-BE.js');
case 'nl-NL':
return import('@lion/ui/input-datepicker-translations/nl-NL.js');
case 'nl':
return import('@lion/ui/input-datepicker-translations/nl.js');
case 'pl-PL':
return import('@lion/ui/input-datepicker-translations/pl-PL.js');
case 'pl':
return import('@lion/ui/input-datepicker-translations/pl.js');
case 'ro-RO':
return import('@lion/ui/input-datepicker-translations/ro-RO.js');
case 'ro':
return import('@lion/ui/input-datepicker-translations/ro.js');
case 'ru-RU':
return import('@lion/ui/input-datepicker-translations/ru-RU.js');
case 'ru':
return import('@lion/ui/input-datepicker-translations/ru.js');
case 'sk-SK':
return import('@lion/ui/input-datepicker-translations/sk-SK.js');
case 'sk':
return import('@lion/ui/input-datepicker-translations/sk.js');
case 'uk-UA':
return import('@lion/ui/input-datepicker-translations/uk-UA.js');
case 'uk':
return import('@lion/ui/input-datepicker-translations/uk.js');
case 'zh-CN':
case 'zh':
return import('@lion/ui/input-datepicker-translations/zh.js');
default:
return import('@lion/ui/input-datepicker-translations/en.js');
}
},
},
...super.localizeNamespaces,
];
return [{ 'lion-input-datepicker': localizeNamespaceLoader }, ...super.localizeNamespaces];
}
/**
@ -262,11 +207,29 @@ export class LionInputDatepicker extends ScopedElementsMixin(
// This would make first paint quicker
return html`
<div id="overlay-content-node-wrapper">
<lion-calendar-overlay-frame class="calendar__overlay-frame">
<span slot="heading">${this.calendarHeading}</span>
${this._calendarTemplate()}
</lion-calendar-overlay-frame>
${this._arrowNodeTemplate()}
${this._overlayFrameTemplate()} ${this._arrowNodeTemplate()}
</div>
`;
}
_overlayFrameTemplate() {
return html`
<div class="calendar__overlay-frame">
<div class="calendar-overlay">
<div class="calendar-overlay__header">
<h1 class="calendar-overlay__heading">${this.calendarHeading}</h1>
<button
@click="${() => this._overlayCtrl.hide()}"
id="close-button"
title="${this.msgLit('lion-input-datepicker:close')}"
aria-label="${this.msgLit('lion-input-datepicker:close')}"
class="calendar-overlay__close-button"
>
<slot name="close-icon">&times;</slot>
</button>
</div>
<div>${this._calendarTemplate()}</div>
</div>
</div>
`;
}
@ -466,6 +429,7 @@ export class LionInputDatepicker extends ScopedElementsMixin(
this._cachedOverlayContentNode = /** @type {HTMLElement} */ (
/** @type {ShadowRoot} */ (this.shadowRoot).querySelector('.calendar__overlay-frame')
);
return this._cachedOverlayContentNode;
}
}

View file

@ -0,0 +1,188 @@
/**
* @param {Promise<{default:object}>[]} importPromises
*/
async function combineLocalizeImports(importPromises) {
const localizeObjects = await Promise.all(importPromises);
const combinedResult = localizeObjects[0];
for (const localizeObject of localizeObjects.slice(1)) {
Object.entries(localizeObject.default).forEach(([key, val]) => {
combinedResult.default[key] = val;
});
}
return combinedResult;
}
/* eslint-disable import/no-extraneous-dependencies */
export const localizeNamespaceLoader = /** @param {string} locale */ locale => {
switch (locale) {
case 'bg-BG':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/bg-BG.js'),
import('@lion/ui/input-datepicker-translations/bg-BG.js'),
]);
case 'bg':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/bg.js'),
import('@lion/ui/input-datepicker-translations/bg.js'),
]);
case 'cs-CZ':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/cs-CZ.js'),
import('@lion/ui/input-datepicker-translations/cs-CZ.js'),
]);
case 'cs':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/cs.js'),
import('@lion/ui/input-datepicker-translations/cs.js'),
]);
case 'de-DE':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/de-DE.js'),
import('@lion/ui/input-datepicker-translations/de-DE.js'),
]);
case 'de':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/de.js'),
import('@lion/ui/input-datepicker-translations/de.js'),
]);
case 'en-AU':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/en-AU.js'),
import('@lion/ui/input-datepicker-translations/en-AU.js'),
]);
case 'en-GB':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/en-GB.js'),
import('@lion/ui/input-datepicker-translations/en-GB.js'),
]);
case 'en-US':
return import('@lion/ui/input-datepicker-translations/en-US.js');
case 'en-PH':
case 'en':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/en.js'),
import('@lion/ui/input-datepicker-translations/en.js'),
]);
case 'es-ES':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/es-ES.js'),
import('@lion/ui/input-datepicker-translations/es-ES.js'),
]);
case 'es':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/es.js'),
import('@lion/ui/input-datepicker-translations/es.js'),
]);
case 'fr-FR':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/fr-FR.js'),
import('@lion/ui/input-datepicker-translations/fr-FR.js'),
]);
case 'fr-BE':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/fr-BE.js'),
import('@lion/ui/input-datepicker-translations/fr-BE.js'),
]);
case 'fr':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/fr.js'),
import('@lion/ui/input-datepicker-translations/fr.js'),
]);
case 'hu-HU':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/hu-HU.js'),
import('@lion/ui/input-datepicker-translations/hu-HU.js'),
]);
case 'hu':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/hu.js'),
import('@lion/ui/input-datepicker-translations/hu.js'),
]);
case 'it-IT':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/it-IT.js'),
import('@lion/ui/input-datepicker-translations/it-IT.js'),
]);
case 'it':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/it.js'),
import('@lion/ui/input-datepicker-translations/it.js'),
]);
case 'nl-BE':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/nl-BE.js'),
import('@lion/ui/input-datepicker-translations/nl-BE.js'),
]);
case 'nl-NL':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/nl-NL.js'),
import('@lion/ui/input-datepicker-translations/nl-NL.js'),
]);
case 'nl':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/nl.js'),
import('@lion/ui/input-datepicker-translations/nl.js'),
]);
case 'pl-PL':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/pl-PL.js'),
import('@lion/ui/input-datepicker-translations/pl-PL.js'),
]);
case 'pl':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/pl.js'),
import('@lion/ui/input-datepicker-translations/pl.js'),
]);
case 'ro-RO':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/ro-RO.js'),
import('@lion/ui/input-datepicker-translations/ro-RO.js'),
]);
case 'ro':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/ro.js'),
import('@lion/ui/input-datepicker-translations/ro.js'),
]);
case 'ru-RU':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/ru-RU.js'),
import('@lion/ui/input-datepicker-translations/ru-RU.js'),
]);
case 'ru':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/ru.js'),
import('@lion/ui/input-datepicker-translations/ru.js'),
]);
case 'sk-SK':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/sk-SK.js'),
import('@lion/ui/input-datepicker-translations/sk-SK.js'),
]);
case 'sk':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/sk.js'),
import('@lion/ui/input-datepicker-translations/sk.js'),
]);
case 'uk-UA':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/uk-UA.js'),
import('@lion/ui/input-datepicker-translations/uk-UA.js'),
]);
case 'uk':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/uk.js'),
import('@lion/ui/input-datepicker-translations/uk.js'),
]);
case 'zh-CN':
case 'zh':
return combineLocalizeImports([
import('@lion/ui/overlays-translations/zh.js'),
import('@lion/ui/input-datepicker-translations/zh.js'),
]);
default:
return combineLocalizeImports([
import('@lion/ui/overlays-translations/en.js'),
import('@lion/ui/input-datepicker-translations/en.js'),
]);
}
};

View file

@ -29,7 +29,7 @@ export class DatepickerInputObject {
async closeCalendar() {
this.overlayCloseButtonEl.click();
await this.overlayEl.updateComplete;
await this.el.updateComplete;
}
/**
@ -58,13 +58,13 @@ export class DatepickerInputObject {
get overlayHeadingEl() {
return /** @type {HTMLElement} */ (
this.overlayEl && this.overlayEl.shadowRoot?.querySelector('.calendar-overlay__heading')
this.overlayEl && this.el.shadowRoot?.querySelector('.calendar-overlay__heading')
);
}
get overlayCloseButtonEl() {
return /** @type {HTMLElement} */ (
this.calendarEl && this.overlayEl.shadowRoot?.querySelector('#close-button')
this.calendarEl && this.el.shadowRoot?.querySelector('#close-button')
);
}

View file

@ -44,15 +44,17 @@ describe('<lion-input-datepicker>', () => {
const elObj = new DatepickerInputObject(el);
await elObj.openCalendar();
expect(elObj.overlayEl.shadowRoot.querySelector('.calendar-overlay')).not.to.equal(null);
expect(elObj.overlayEl.shadowRoot.querySelector('.calendar-overlay__header')).not.to.equal(
null,
);
expect(elObj.overlayEl.shadowRoot.querySelector('.calendar-overlay__heading')).not.to.equal(
null,
);
expect(
elObj.overlayEl.shadowRoot.querySelector('.calendar-overlay__close-button'),
/** @type {ShadowRoot} */ (el.shadowRoot).querySelector('.calendar-overlay'),
).not.to.equal(null);
expect(
/** @type {ShadowRoot} */ (el.shadowRoot).querySelector('.calendar-overlay__header'),
).not.to.equal(null);
expect(
/** @type {ShadowRoot} */ (el.shadowRoot).querySelector('.calendar-overlay__heading'),
).not.to.equal(null);
expect(
/** @type {ShadowRoot} */ (el.shadowRoot).querySelector('.calendar-overlay__close-button'),
).not.to.equal(null);
});
@ -74,11 +76,7 @@ describe('<lion-input-datepicker>', () => {
`);
const elObj = new DatepickerInputObject(el);
await elObj.openCalendar();
expect(
/** @type {HTMLSlotElement} */ (
elObj.overlayHeadingEl.querySelector('slot[name="heading"]')
).assignedNodes()[0],
).lightDom.to.equal('Pick your date');
expect(elObj.overlayHeadingEl.textContent).to.equal('Pick your date');
});
it('can have a custom heading', async () => {
@ -90,11 +88,7 @@ describe('<lion-input-datepicker>', () => {
`);
const elObj = new DatepickerInputObject(el);
await elObj.openCalendar();
expect(
/** @type {HTMLSlotElement} */ (
elObj.overlayHeadingEl.querySelector('slot[name="heading"]')
).assignedNodes()[0],
).lightDom.to.equal('foo');
expect(elObj.overlayHeadingEl.innerText).to.equal('foo');
});
it('closes the calendar on [esc] key', async () => {
@ -670,7 +664,8 @@ describe('<lion-input-datepicker>', () => {
expect(submitSpy.callCount).to.equal(0);
});
it('is hidden when attribute hidden is true', async () => {
// TODO: remove: became irrelevant after we don't need dialog-frame
it.skip('is hidden when attribute hidden is true', async () => {
const el = await fixture(html`<lion-input-datepicker></lion-input-datepicker>`);
await el.updateComplete;
@ -690,7 +685,9 @@ describe('<lion-input-datepicker>', () => {
await myElObj.openCalendar();
expect(el.hasArrow).to.be.false;
expect(
el?.shadowRoot?.contains(myElObj.overlayController.contentNode),
myElObj.overlayController.contentNode.classList.contains(
'global-overlays__overlay--bottom-sheet',
),
'Datepicker does not get rendered as bottom sheet',
).to.be.false;
});

View file

@ -1,3 +0,0 @@
import { LionCalendarOverlayFrame } from '../input-datepicker.js';
customElements.define('lion-calendar-overlay-frame', LionCalendarOverlayFrame);

View file

@ -1,2 +1 @@
export { LionInputDatepicker } from '../components/input-datepicker/src/LionInputDatepicker.js';
export { LionCalendarOverlayFrame } from '../components/input-datepicker/src/LionCalendarOverlayFrame.js';