From 9c3224b4ff87939fb1f703c061b4da9b084f82ed Mon Sep 17 00:00:00 2001 From: Thomas Allmer Date: Mon, 2 Nov 2020 08:55:49 +0100 Subject: [PATCH] chore: add ArrowMixin improvements, add Datepicker local/global switch --- .changeset/hip-pigs-type.md | 8 ++ package-lock.json | 45 ++++++----- package.json | 4 +- .../src/LionInputDatepicker.js | 49 ++++++++--- .../test/lion-input-datepicker.test.js | 51 +++++++++++- packages/overlays/src/ArrowMixin.js | 81 ++++++++++--------- packages/overlays/types/ArrowMixinTypes.d.ts | 1 + 7 files changed, 167 insertions(+), 72 deletions(-) create mode 100644 .changeset/hip-pigs-type.md diff --git a/.changeset/hip-pigs-type.md b/.changeset/hip-pigs-type.md new file mode 100644 index 000000000..78877e60a --- /dev/null +++ b/.changeset/hip-pigs-type.md @@ -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) diff --git a/package-lock.json b/package-lock.json index a6e5909dd..5af9ac365 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,9 +23,9 @@ "@types/chai-dom": "^0.0.8", "@web/dev-server": "^0.0.13", "@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-playwright": "^0.6.3", + "@web/test-runner-playwright": "^0.6.4", "@webcomponents/webcomponentsjs": "^2.4.4", "babel-eslint": "^8.2.6", "babel-polyfill": "^6.26.0", @@ -4799,9 +4799,9 @@ "dev": true }, "node_modules/@types/puppeteer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-3.0.2.tgz", - "integrity": "sha512-JRuHPSbHZBadOxxFwpyZPeRlpPTTeMbQneMdpFd8LXdyNfFSiX950CGewdm69g/ipzEAXAmMyFF1WOWJOL/nKw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-3.0.4.tgz", + "integrity": "sha512-wmkBg1Wn/wwgGDl9IrxwY9nJ/Hfv/YHFAqy6Dch9KYDSjA0lif8PA7j3Ls1iOfKWEMRIbHkpWIPoiw4r9Vq+3A==", "dev": true, "dependencies": { "@types/node": "*" @@ -5302,16 +5302,17 @@ } }, "node_modules/@web/test-runner": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/@web/test-runner/-/test-runner-0.9.6.tgz", - "integrity": "sha512-ThtPMj2MPe6k6xyg5CZKmtzH0G1CN8NlBrS6+YpHtg7jKneXsFeg8RcinY0Maq4sKRdeYCfwUKqSdGNeqrChlg==", + "version": "0.9.7", + "resolved": "https://registry.npmjs.org/@web/test-runner/-/test-runner-0.9.7.tgz", + "integrity": "sha512-A894e2sgI3aZ7voDMo0o/0ms7oS7NPOb9zui7tMGZt4RUWc4uWduolHd7znAVCTXSJwmlGw8Aa1gWwf4IzAvaQ==", "dev": true, + "license": "MIT", "dependencies": { "@rollup/plugin-node-resolve": "^8.1.0", "@web/config-loader": "^0.1.2", "@web/dev-server-rollup": "^0.2.9", "@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-core": "^0.8.7", "@web/test-runner-mocha": "^0.5.1", @@ -5361,9 +5362,9 @@ } }, "node_modules/@web/test-runner-cli": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@web/test-runner-cli/-/test-runner-cli-0.6.8.tgz", - "integrity": "sha512-lZhViVaB3/wDfoUPsw9meucUsSonpBnVBy/b3Sqtmfu6bsvOxVEIl5VVRBKIhN94JSQPNUDRWPUVDeV1tljddg==", + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/@web/test-runner-cli/-/test-runner-cli-0.6.9.tgz", + "integrity": "sha512-OgjGb0KEs8ZwlUymsAonMiwpZ1qOSQh4dwqfEqbvbu6LyHbjM6Sm9DLThHCibBIYGzxzdHMwgsgWJJCZFAdaKA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.10.4", @@ -29623,9 +29624,9 @@ "dev": true }, "@types/puppeteer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-3.0.2.tgz", - "integrity": "sha512-JRuHPSbHZBadOxxFwpyZPeRlpPTTeMbQneMdpFd8LXdyNfFSiX950CGewdm69g/ipzEAXAmMyFF1WOWJOL/nKw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-3.0.4.tgz", + "integrity": "sha512-wmkBg1Wn/wwgGDl9IrxwY9nJ/Hfv/YHFAqy6Dch9KYDSjA0lif8PA7j3Ls1iOfKWEMRIbHkpWIPoiw4r9Vq+3A==", "dev": true, "requires": { "@types/node": "*" @@ -30072,16 +30073,16 @@ } }, "@web/test-runner": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/@web/test-runner/-/test-runner-0.9.6.tgz", - "integrity": "sha512-ThtPMj2MPe6k6xyg5CZKmtzH0G1CN8NlBrS6+YpHtg7jKneXsFeg8RcinY0Maq4sKRdeYCfwUKqSdGNeqrChlg==", + "version": "0.9.7", + "resolved": "https://registry.npmjs.org/@web/test-runner/-/test-runner-0.9.7.tgz", + "integrity": "sha512-A894e2sgI3aZ7voDMo0o/0ms7oS7NPOb9zui7tMGZt4RUWc4uWduolHd7znAVCTXSJwmlGw8Aa1gWwf4IzAvaQ==", "dev": true, "requires": { "@rollup/plugin-node-resolve": "^8.1.0", "@web/config-loader": "^0.1.2", "@web/dev-server-rollup": "^0.2.9", "@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-core": "^0.8.7", "@web/test-runner-mocha": "^0.5.1", @@ -30144,9 +30145,9 @@ } }, "@web/test-runner-cli": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@web/test-runner-cli/-/test-runner-cli-0.6.8.tgz", - "integrity": "sha512-lZhViVaB3/wDfoUPsw9meucUsSonpBnVBy/b3Sqtmfu6bsvOxVEIl5VVRBKIhN94JSQPNUDRWPUVDeV1tljddg==", + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/@web/test-runner-cli/-/test-runner-cli-0.6.9.tgz", + "integrity": "sha512-OgjGb0KEs8ZwlUymsAonMiwpZ1qOSQh4dwqfEqbvbu6LyHbjM6Sm9DLThHCibBIYGzxzdHMwgsgWJJCZFAdaKA==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", diff --git a/package.json b/package.json index 18610b245..804f98214 100644 --- a/package.json +++ b/package.json @@ -48,9 +48,9 @@ "@types/chai-dom": "^0.0.8", "@web/dev-server": "^0.0.13", "@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-playwright": "^0.6.3", + "@web/test-runner-playwright": "^0.6.4", "@webcomponents/webcomponentsjs": "^2.4.4", "babel-eslint": "^8.2.6", "babel-polyfill": "^6.26.0", diff --git a/packages/input-datepicker/src/LionInputDatepicker.js b/packages/input-datepicker/src/LionInputDatepicker.js index 9cfd9817a..40d19104c 100644 --- a/packages/input-datepicker/src/LionInputDatepicker.js +++ b/packages/input-datepicker/src/LionInputDatepicker.js @@ -1,14 +1,21 @@ import { LionCalendar } from '@lion/calendar/src/LionCalendar'; import { html, ifDefined, ScopedElementsMixin } from '@lion/core'; 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'; /** * @customElement lion-input-datepicker */ // @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() { return { ...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. // This would make first paint quicker return html` - - ${this.calendarHeading} - ${this._calendarTemplate()} - +
+ + ${this.calendarHeading} + ${this._calendarTemplate()} + + ${this._arrowNodeTemplate()} +
`; } @@ -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 * @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 _defineOverlayConfig() { - return { - ...withModalDialogConfig(), - hidesOnOutsideClick: true, - }; + if (window.innerWidth >= 600) { + this.hasArrow = true; + return { + ...withModalDialogConfig(), + hidesOnOutsideClick: true, + ...super._defineOverlayConfig(), + popperConfig: { + ...super._defineOverlayConfig().popperConfig, + placement: 'bottom', + }, + }; + } + this.hasArrow = false; + return withBottomSheetConfig(); } async __openCalendarOverlay() { diff --git a/packages/input-datepicker/test/lion-input-datepicker.test.js b/packages/input-datepicker/test/lion-input-datepicker.test.js index 9b8489ee9..3019d7183 100644 --- a/packages/input-datepicker/test/lion-input-datepicker.test.js +++ b/packages/input-datepicker/test/lion-input-datepicker.test.js @@ -4,6 +4,7 @@ import { html, LitElement } from '@lion/core'; import { IsDateDisabled, MaxDate, MinDate, MinMaxDate } from '@lion/form-core'; import { aTimeout, defineCE, expect, fixture as _fixture, nextFrame } from '@open-wc/testing'; import sinon from 'sinon'; +import { setViewport } from '@web/test-runner-commands'; import '../lion-input-datepicker.js'; import { LionInputDatepicker } from '../src/LionInputDatepicker.js'; import { DatepickerInputObject } from '../test-helpers.js'; @@ -515,7 +516,55 @@ describe('', () => { await myElObj.openCalendar(); myElObj.overlayEl.hidden = true; 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``); + 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``); + 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``); + 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; }); }); }); diff --git a/packages/overlays/src/ArrowMixin.js b/packages/overlays/src/ArrowMixin.js index 14e9239ce..7a159784f 100644 --- a/packages/overlays/src/ArrowMixin.js +++ b/packages/overlays/src/ArrowMixin.js @@ -22,48 +22,51 @@ export const ArrowMixinImplementation = superclass => } static get styles() { - return css` - :host { - --tooltip-arrow-width: 12px; - --tooltip-arrow-height: 8px; - } + return [ + super.styles ? super.styles : [], + css` + :host { + --tooltip-arrow-width: 12px; + --tooltip-arrow-height: 8px; + } - .arrow { - display: none; - position: absolute; - width: var(--tooltip-arrow-width); - height: var(--tooltip-arrow-height); - } + .arrow { + display: none; + position: absolute; + width: var(--tooltip-arrow-width); + height: var(--tooltip-arrow-height); + } - :host([has-arrow]) .arrow { - display: block; - } + :host([has-arrow]) .arrow { + display: block; + } - .arrow svg { - display: block; - } + .arrow svg { + display: block; + } - [x-placement^='bottom'] .arrow { - top: calc(-1 * var(--tooltip-arrow-height)); - transform: rotate(180deg); - } + [x-placement^='bottom'] .arrow { + top: calc(-1 * var(--tooltip-arrow-height)); + transform: rotate(180deg); + } - [x-placement^='left'] .arrow { - right: calc( - -1 * (var(--tooltip-arrow-height) + - (var(--tooltip-arrow-width) - var(--tooltip-arrow-height)) / 2) - ); - transform: rotate(270deg); - } + [x-placement^='left'] .arrow { + right: calc( + -1 * (var(--tooltip-arrow-height) + + (var(--tooltip-arrow-width) - var(--tooltip-arrow-height)) / 2) + ); + transform: rotate(270deg); + } - [x-placement^='right'] .arrow { - left: calc( - -1 * (var(--tooltip-arrow-height) + - (var(--tooltip-arrow-width) - var(--tooltip-arrow-height)) / 2) - ); - transform: rotate(90deg); - } - `; + [x-placement^='right'] .arrow { + left: calc( + -1 * (var(--tooltip-arrow-height) + + (var(--tooltip-arrow-width) - var(--tooltip-arrow-height)) / 2) + ); + transform: rotate(90deg); + } + `, + ]; } constructor() { @@ -77,11 +80,15 @@ export const ArrowMixinImplementation = superclass =>
-
${this._arrowTemplate()}
+ ${this._arrowNodeTemplate()}
`; } + _arrowNodeTemplate() { + return html`
${this._arrowTemplate()}
`; + } + // eslint-disable-next-line class-methods-use-this _arrowTemplate() { return html` diff --git a/packages/overlays/types/ArrowMixinTypes.d.ts b/packages/overlays/types/ArrowMixinTypes.d.ts index c13f54c6c..0451176a6 100644 --- a/packages/overlays/types/ArrowMixinTypes.d.ts +++ b/packages/overlays/types/ArrowMixinTypes.d.ts @@ -19,6 +19,7 @@ export declare class ArrowHost { render(): TemplateResult; _arrowTemplate(): TemplateResult; + _arrowNodeTemplate(): TemplateResult; _defineOverlayConfig(): OverlayConfig; __setupRepositionCompletePromise(): void; get _arrowNode(): Element | null;