chore: add ArrowMixin improvements, add Datepicker local/global switch

This commit is contained in:
Thomas Allmer 2020-11-02 08:55:49 +01:00
parent c242204709
commit 9c3224b4ff
7 changed files with 167 additions and 72 deletions

View file

@ -0,0 +1,8 @@
---
'@lion/input-datepicker': minor
'@lion/overlays': minor
---
- ArrowMixin needs to extend styles and not overwrite them
- ArrowMixin add an `_arrowNodeTemplate` which can be used to only override the arrow content
- InputDatepicker switch between bottomsheet style (on mobile) popover (on desktop/table with screensize bigger then 600px)

45
package-lock.json generated
View file

@ -23,9 +23,9 @@
"@types/chai-dom": "^0.0.8", "@types/chai-dom": "^0.0.8",
"@web/dev-server": "^0.0.13", "@web/dev-server": "^0.0.13",
"@web/dev-server-legacy": "^0.1.4", "@web/dev-server-legacy": "^0.1.4",
"@web/test-runner": "^0.9.0", "@web/test-runner": "^0.9.7",
"@web/test-runner-browserstack": "^0.2.0", "@web/test-runner-browserstack": "^0.2.0",
"@web/test-runner-playwright": "^0.6.3", "@web/test-runner-playwright": "^0.6.4",
"@webcomponents/webcomponentsjs": "^2.4.4", "@webcomponents/webcomponentsjs": "^2.4.4",
"babel-eslint": "^8.2.6", "babel-eslint": "^8.2.6",
"babel-polyfill": "^6.26.0", "babel-polyfill": "^6.26.0",
@ -4799,9 +4799,9 @@
"dev": true "dev": true
}, },
"node_modules/@types/puppeteer": { "node_modules/@types/puppeteer": {
"version": "3.0.2", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-3.0.2.tgz", "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-3.0.4.tgz",
"integrity": "sha512-JRuHPSbHZBadOxxFwpyZPeRlpPTTeMbQneMdpFd8LXdyNfFSiX950CGewdm69g/ipzEAXAmMyFF1WOWJOL/nKw==", "integrity": "sha512-wmkBg1Wn/wwgGDl9IrxwY9nJ/Hfv/YHFAqy6Dch9KYDSjA0lif8PA7j3Ls1iOfKWEMRIbHkpWIPoiw4r9Vq+3A==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@types/node": "*" "@types/node": "*"
@ -5302,16 +5302,17 @@
} }
}, },
"node_modules/@web/test-runner": { "node_modules/@web/test-runner": {
"version": "0.9.6", "version": "0.9.7",
"resolved": "https://registry.npmjs.org/@web/test-runner/-/test-runner-0.9.6.tgz", "resolved": "https://registry.npmjs.org/@web/test-runner/-/test-runner-0.9.7.tgz",
"integrity": "sha512-ThtPMj2MPe6k6xyg5CZKmtzH0G1CN8NlBrS6+YpHtg7jKneXsFeg8RcinY0Maq4sKRdeYCfwUKqSdGNeqrChlg==", "integrity": "sha512-A894e2sgI3aZ7voDMo0o/0ms7oS7NPOb9zui7tMGZt4RUWc4uWduolHd7znAVCTXSJwmlGw8Aa1gWwf4IzAvaQ==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"@rollup/plugin-node-resolve": "^8.1.0", "@rollup/plugin-node-resolve": "^8.1.0",
"@web/config-loader": "^0.1.2", "@web/config-loader": "^0.1.2",
"@web/dev-server-rollup": "^0.2.9", "@web/dev-server-rollup": "^0.2.9",
"@web/test-runner-chrome": "^0.7.2", "@web/test-runner-chrome": "^0.7.2",
"@web/test-runner-cli": "^0.6.8", "@web/test-runner-cli": "^0.6.9",
"@web/test-runner-commands": "^0.2.1", "@web/test-runner-commands": "^0.2.1",
"@web/test-runner-core": "^0.8.7", "@web/test-runner-core": "^0.8.7",
"@web/test-runner-mocha": "^0.5.1", "@web/test-runner-mocha": "^0.5.1",
@ -5361,9 +5362,9 @@
} }
}, },
"node_modules/@web/test-runner-cli": { "node_modules/@web/test-runner-cli": {
"version": "0.6.8", "version": "0.6.9",
"resolved": "https://registry.npmjs.org/@web/test-runner-cli/-/test-runner-cli-0.6.8.tgz", "resolved": "https://registry.npmjs.org/@web/test-runner-cli/-/test-runner-cli-0.6.9.tgz",
"integrity": "sha512-lZhViVaB3/wDfoUPsw9meucUsSonpBnVBy/b3Sqtmfu6bsvOxVEIl5VVRBKIhN94JSQPNUDRWPUVDeV1tljddg==", "integrity": "sha512-OgjGb0KEs8ZwlUymsAonMiwpZ1qOSQh4dwqfEqbvbu6LyHbjM6Sm9DLThHCibBIYGzxzdHMwgsgWJJCZFAdaKA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.10.4", "@babel/code-frame": "^7.10.4",
@ -29623,9 +29624,9 @@
"dev": true "dev": true
}, },
"@types/puppeteer": { "@types/puppeteer": {
"version": "3.0.2", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-3.0.2.tgz", "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-3.0.4.tgz",
"integrity": "sha512-JRuHPSbHZBadOxxFwpyZPeRlpPTTeMbQneMdpFd8LXdyNfFSiX950CGewdm69g/ipzEAXAmMyFF1WOWJOL/nKw==", "integrity": "sha512-wmkBg1Wn/wwgGDl9IrxwY9nJ/Hfv/YHFAqy6Dch9KYDSjA0lif8PA7j3Ls1iOfKWEMRIbHkpWIPoiw4r9Vq+3A==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/node": "*" "@types/node": "*"
@ -30072,16 +30073,16 @@
} }
}, },
"@web/test-runner": { "@web/test-runner": {
"version": "0.9.6", "version": "0.9.7",
"resolved": "https://registry.npmjs.org/@web/test-runner/-/test-runner-0.9.6.tgz", "resolved": "https://registry.npmjs.org/@web/test-runner/-/test-runner-0.9.7.tgz",
"integrity": "sha512-ThtPMj2MPe6k6xyg5CZKmtzH0G1CN8NlBrS6+YpHtg7jKneXsFeg8RcinY0Maq4sKRdeYCfwUKqSdGNeqrChlg==", "integrity": "sha512-A894e2sgI3aZ7voDMo0o/0ms7oS7NPOb9zui7tMGZt4RUWc4uWduolHd7znAVCTXSJwmlGw8Aa1gWwf4IzAvaQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@rollup/plugin-node-resolve": "^8.1.0", "@rollup/plugin-node-resolve": "^8.1.0",
"@web/config-loader": "^0.1.2", "@web/config-loader": "^0.1.2",
"@web/dev-server-rollup": "^0.2.9", "@web/dev-server-rollup": "^0.2.9",
"@web/test-runner-chrome": "^0.7.2", "@web/test-runner-chrome": "^0.7.2",
"@web/test-runner-cli": "^0.6.8", "@web/test-runner-cli": "^0.6.9",
"@web/test-runner-commands": "^0.2.1", "@web/test-runner-commands": "^0.2.1",
"@web/test-runner-core": "^0.8.7", "@web/test-runner-core": "^0.8.7",
"@web/test-runner-mocha": "^0.5.1", "@web/test-runner-mocha": "^0.5.1",
@ -30144,9 +30145,9 @@
} }
}, },
"@web/test-runner-cli": { "@web/test-runner-cli": {
"version": "0.6.8", "version": "0.6.9",
"resolved": "https://registry.npmjs.org/@web/test-runner-cli/-/test-runner-cli-0.6.8.tgz", "resolved": "https://registry.npmjs.org/@web/test-runner-cli/-/test-runner-cli-0.6.9.tgz",
"integrity": "sha512-lZhViVaB3/wDfoUPsw9meucUsSonpBnVBy/b3Sqtmfu6bsvOxVEIl5VVRBKIhN94JSQPNUDRWPUVDeV1tljddg==", "integrity": "sha512-OgjGb0KEs8ZwlUymsAonMiwpZ1qOSQh4dwqfEqbvbu6LyHbjM6Sm9DLThHCibBIYGzxzdHMwgsgWJJCZFAdaKA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/code-frame": "^7.10.4", "@babel/code-frame": "^7.10.4",

View file

@ -48,9 +48,9 @@
"@types/chai-dom": "^0.0.8", "@types/chai-dom": "^0.0.8",
"@web/dev-server": "^0.0.13", "@web/dev-server": "^0.0.13",
"@web/dev-server-legacy": "^0.1.4", "@web/dev-server-legacy": "^0.1.4",
"@web/test-runner": "^0.9.0", "@web/test-runner": "^0.9.7",
"@web/test-runner-browserstack": "^0.2.0", "@web/test-runner-browserstack": "^0.2.0",
"@web/test-runner-playwright": "^0.6.3", "@web/test-runner-playwright": "^0.6.4",
"@webcomponents/webcomponentsjs": "^2.4.4", "@webcomponents/webcomponentsjs": "^2.4.4",
"babel-eslint": "^8.2.6", "babel-eslint": "^8.2.6",
"babel-polyfill": "^6.26.0", "babel-polyfill": "^6.26.0",

View file

@ -1,14 +1,21 @@
import { LionCalendar } from '@lion/calendar/src/LionCalendar'; import { LionCalendar } from '@lion/calendar/src/LionCalendar';
import { html, ifDefined, ScopedElementsMixin } from '@lion/core'; import { html, ifDefined, ScopedElementsMixin } from '@lion/core';
import { LionInputDate } from '@lion/input-date'; import { LionInputDate } from '@lion/input-date';
import { OverlayMixin, withModalDialogConfig } from '@lion/overlays'; import {
OverlayMixin,
withBottomSheetConfig,
withModalDialogConfig,
ArrowMixin,
} from '@lion/overlays';
import { LionCalendarOverlayFrame } from './LionCalendarOverlayFrame.js'; import { LionCalendarOverlayFrame } from './LionCalendarOverlayFrame.js';
/** /**
* @customElement lion-input-datepicker * @customElement lion-input-datepicker
*/ */
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110 // @ts-expect-error https://github.com/microsoft/TypeScript/issues/40110
export class LionInputDatepicker extends ScopedElementsMixin(OverlayMixin(LionInputDate)) { export class LionInputDatepicker extends ScopedElementsMixin(
ArrowMixin(OverlayMixin(LionInputDate)),
) {
static get scopedElements() { static get scopedElements() {
return { return {
...super.scopedElements, ...super.scopedElements,
@ -226,10 +233,13 @@ export class LionInputDatepicker extends ScopedElementsMixin(OverlayMixin(LionIn
// When not opened (usually on init), it does not need to be rendered. // When not opened (usually on init), it does not need to be rendered.
// This would make first paint quicker // This would make first paint quicker
return html` return html`
<lion-calendar-overlay-frame class="calendar__overlay-frame"> <div id="overlay-content-node-wrapper">
<span slot="heading">${this.calendarHeading}</span> <lion-calendar-overlay-frame class="calendar__overlay-frame">
${this._calendarTemplate()} <span slot="heading">${this.calendarHeading}</span>
</lion-calendar-overlay-frame> ${this._calendarTemplate()}
</lion-calendar-overlay-frame>
${this._arrowNodeTemplate()}
</div>
`; `;
} }
@ -280,6 +290,15 @@ export class LionInputDatepicker extends ScopedElementsMixin(OverlayMixin(LionIn
`; `;
} }
_setupOverlayCtrl() {
super._setupOverlayCtrl();
this.__datepickerBeforeShow = () => {
this._overlayCtrl.updateConfig(this._defineOverlayConfig());
};
this._overlayCtrl.addEventListener('before-show', this.__datepickerBeforeShow);
}
/** /**
* @override Configures OverlayMixin * @override Configures OverlayMixin
* @desc overrides default configuration options for this component * @desc overrides default configuration options for this component
@ -287,10 +306,20 @@ export class LionInputDatepicker extends ScopedElementsMixin(OverlayMixin(LionIn
*/ */
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
_defineOverlayConfig() { _defineOverlayConfig() {
return { if (window.innerWidth >= 600) {
...withModalDialogConfig(), this.hasArrow = true;
hidesOnOutsideClick: true, return {
}; ...withModalDialogConfig(),
hidesOnOutsideClick: true,
...super._defineOverlayConfig(),
popperConfig: {
...super._defineOverlayConfig().popperConfig,
placement: 'bottom',
},
};
}
this.hasArrow = false;
return withBottomSheetConfig();
} }
async __openCalendarOverlay() { async __openCalendarOverlay() {

View file

@ -4,6 +4,7 @@ import { html, LitElement } from '@lion/core';
import { IsDateDisabled, MaxDate, MinDate, MinMaxDate } from '@lion/form-core'; import { IsDateDisabled, MaxDate, MinDate, MinMaxDate } from '@lion/form-core';
import { aTimeout, defineCE, expect, fixture as _fixture, nextFrame } from '@open-wc/testing'; import { aTimeout, defineCE, expect, fixture as _fixture, nextFrame } from '@open-wc/testing';
import sinon from 'sinon'; import sinon from 'sinon';
import { setViewport } from '@web/test-runner-commands';
import '../lion-input-datepicker.js'; import '../lion-input-datepicker.js';
import { LionInputDatepicker } from '../src/LionInputDatepicker.js'; import { LionInputDatepicker } from '../src/LionInputDatepicker.js';
import { DatepickerInputObject } from '../test-helpers.js'; import { DatepickerInputObject } from '../test-helpers.js';
@ -515,7 +516,55 @@ describe('<lion-input-datepicker>', () => {
await myElObj.openCalendar(); await myElObj.openCalendar();
myElObj.overlayEl.hidden = true; myElObj.overlayEl.hidden = true;
await el.updateComplete; await el.updateComplete;
expect(myElObj.overlayEl).not.to.be.displayed; expect(getComputedStyle(myElObj.overlayEl).display).to.equal('none', 'Display is not none');
});
});
describe('responsive', async () => {
it('opens as bottom sheet on mobile', async () => {
await setViewport({ width: 360, height: 640 });
const el = await fixture(html`<lion-input-datepicker></lion-input-datepicker>`);
const myElObj = new DatepickerInputObject(el);
await myElObj.openCalendar();
expect(el.hasArrow).to.be.false;
expect(
el?.shadowRoot?.contains(myElObj.overlayController.contentNode),
'Datepicker does not get rendered as bottom sheet',
).to.be.false;
});
it('opens as popover on desktop', async () => {
await setViewport({ width: 1200, height: 640 });
const el = await fixture(html`<lion-input-datepicker></lion-input-datepicker>`);
const myElObj = new DatepickerInputObject(el);
await myElObj.openCalendar();
expect(el.hasArrow).to.be.true;
expect(
el?.shadowRoot?.contains(myElObj.overlayController.contentNode),
'Datepicker does not get rendered as a popover',
).to.be.true;
});
// TODO: fix a bug in overlays which does not move global nodes back to the local dom
it.skip('can switch between bottom sheet and popover', async () => {
const el = await fixture(html`<lion-input-datepicker></lion-input-datepicker>`);
await el.updateComplete;
const myElObj = new DatepickerInputObject(el);
await setViewport({ width: 360, height: 640 });
await myElObj.openCalendar();
expect(
el?.shadowRoot?.contains(myElObj.overlayController.contentNode),
'Datepicker does not get rendered as bottom sheet',
).to.be.false;
await myElObj.closeCalendar();
await setViewport({ width: 1200, height: 640 });
await myElObj.openCalendar();
expect(
el?.shadowRoot?.contains(myElObj.overlayController.contentNode),
'Datepicker does not get rendered as a popover',
).to.be.true;
}); });
}); });
}); });

View file

@ -22,48 +22,51 @@ export const ArrowMixinImplementation = superclass =>
} }
static get styles() { static get styles() {
return css` return [
:host { super.styles ? super.styles : [],
--tooltip-arrow-width: 12px; css`
--tooltip-arrow-height: 8px; :host {
} --tooltip-arrow-width: 12px;
--tooltip-arrow-height: 8px;
}
.arrow { .arrow {
display: none; display: none;
position: absolute; position: absolute;
width: var(--tooltip-arrow-width); width: var(--tooltip-arrow-width);
height: var(--tooltip-arrow-height); height: var(--tooltip-arrow-height);
} }
:host([has-arrow]) .arrow { :host([has-arrow]) .arrow {
display: block; display: block;
} }
.arrow svg { .arrow svg {
display: block; display: block;
} }
[x-placement^='bottom'] .arrow { [x-placement^='bottom'] .arrow {
top: calc(-1 * var(--tooltip-arrow-height)); top: calc(-1 * var(--tooltip-arrow-height));
transform: rotate(180deg); transform: rotate(180deg);
} }
[x-placement^='left'] .arrow { [x-placement^='left'] .arrow {
right: calc( right: calc(
-1 * (var(--tooltip-arrow-height) + -1 * (var(--tooltip-arrow-height) +
(var(--tooltip-arrow-width) - var(--tooltip-arrow-height)) / 2) (var(--tooltip-arrow-width) - var(--tooltip-arrow-height)) / 2)
); );
transform: rotate(270deg); transform: rotate(270deg);
} }
[x-placement^='right'] .arrow { [x-placement^='right'] .arrow {
left: calc( left: calc(
-1 * (var(--tooltip-arrow-height) + -1 * (var(--tooltip-arrow-height) +
(var(--tooltip-arrow-width) - var(--tooltip-arrow-height)) / 2) (var(--tooltip-arrow-width) - var(--tooltip-arrow-height)) / 2)
); );
transform: rotate(90deg); transform: rotate(90deg);
} }
`; `,
];
} }
constructor() { constructor() {
@ -77,11 +80,15 @@ export const ArrowMixinImplementation = superclass =>
<slot name="invoker"></slot> <slot name="invoker"></slot>
<div id="overlay-content-node-wrapper"> <div id="overlay-content-node-wrapper">
<slot name="content"></slot> <slot name="content"></slot>
<div class="arrow" x-arrow>${this._arrowTemplate()}</div> ${this._arrowNodeTemplate()}
</div> </div>
`; `;
} }
_arrowNodeTemplate() {
return html`<div class="arrow" x-arrow>${this._arrowTemplate()}</div>`;
}
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
_arrowTemplate() { _arrowTemplate() {
return html` return html`

View file

@ -19,6 +19,7 @@ export declare class ArrowHost {
render(): TemplateResult; render(): TemplateResult;
_arrowTemplate(): TemplateResult; _arrowTemplate(): TemplateResult;
_arrowNodeTemplate(): TemplateResult;
_defineOverlayConfig(): OverlayConfig; _defineOverlayConfig(): OverlayConfig;
__setupRepositionCompletePromise(): void; __setupRepositionCompletePromise(): void;
get _arrowNode(): Element | null; get _arrowNode(): Element | null;