From b2c5f92e085c28eebeedd948a1386be9b4c1cdea Mon Sep 17 00:00:00 2001 From: Thijs Louisse Date: Mon, 9 Dec 2019 16:47:53 +0100 Subject: [PATCH 1/2] feat(overlays): close/hide events on dom (OverlayMixin) level --- packages/dialog/stories/index.stories.js | 6 +- .../src/LionCalendarOverlayFrame.js | 7 +- .../src/LionInputDatepicker.js | 6 +- packages/overlays/README.md | 2 +- packages/overlays/src/OverlayController.js | 47 ++++---- packages/overlays/src/OverlayMixin.js | 68 +++++++++--- packages/overlays/stories/index.stories.js | 104 ++++++++++++++++-- .../test-suites/OverlayMixin.suite.js | 83 ++++++++++++++ .../overlays/test/OverlayController.test.js | 27 ----- 9 files changed, 264 insertions(+), 86 deletions(-) diff --git a/packages/dialog/stories/index.stories.js b/packages/dialog/stories/index.stories.js index d880b5197..045e2fda5 100644 --- a/packages/dialog/stories/index.stories.js +++ b/packages/dialog/stories/index.stories.js @@ -80,7 +80,7 @@ storiesOf('Overlays Specific WC | Dialog', module) Hello! You can close this notification here: @@ -97,7 +97,7 @@ storiesOf('Overlays Specific WC | Dialog', module) Hello! You can close this notification here: @@ -123,7 +123,7 @@ storiesOf('Overlays Specific WC | Dialog', module) Hello! You can close this notification here: diff --git a/packages/input-datepicker/src/LionCalendarOverlayFrame.js b/packages/input-datepicker/src/LionCalendarOverlayFrame.js index 2978ef210..3325ccdc1 100644 --- a/packages/input-datepicker/src/LionCalendarOverlayFrame.js +++ b/packages/input-datepicker/src/LionCalendarOverlayFrame.js @@ -90,9 +90,8 @@ export class LionCalendarOverlayFrame extends LocalizeMixin(LitElement) { ]; } - __dispatchHideEvent() { - // Designed to work in conjunction with ModalDialogController - this.dispatchEvent(new CustomEvent('hide'), { bubbles: true }); + __dispatchCloseEvent() { + this.dispatchEvent(new Event('close-overlay'), { bubbles: true }); } render() { @@ -104,7 +103,7 @@ export class LionCalendarOverlayFrame extends LocalizeMixin(LitElement) {
@@ -168,7 +170,7 @@ storiesOf('Overlay System | Overlay as a WC', module) Hello! You can close this notification here: @@ -197,7 +199,9 @@ storiesOf('Overlay System | Overlay as a WC', module)
Hello! This is a notification. - e.target.dispatchEvent(new Event('hide', { bubbles: true }))} + @click=${e => + e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))} > ⨯ @@ -234,7 +239,7 @@ storiesOf('Overlay System | Overlay as a WC', module) Hello! You can close this notification here: @@ -299,7 +304,7 @@ storiesOf('Overlay System | Overlay as a WC', module) Hello! You can close this notification here: @@ -355,7 +360,7 @@ storiesOf('Overlay System | Overlay as a WC', module)

Close and open it again on a small screen (< 600px) and it will be a bottom sheet

{ + @before-opened=${e => { if (window.innerWidth >= 600) { e.target.config = { ...withModalDialogConfig() }; } else { @@ -368,7 +373,7 @@ storiesOf('Overlay System | Overlay as a WC', module) Hello! You can close this notification here: @@ -450,6 +455,87 @@ storiesOf('Overlay System | Overlay as a WC', module) ${popup}
`; + }) + .add('Sync application state', () => { + const appState = { + opened: true, + }; + const openedStateNode = renderOffline( + html` + ${appState.opened} + `, + ); + function onOpenClosed(ev) { + appState.opened = ev.target.opened; + openedStateNode.innerText = appState.opened; + } + const popup = renderOffline(html` + + +
+ Hello! You can close this notification here: + +
+
+ `); + + return html` + + appState.opened: ${openedStateNode} +
+ ${popup} +
+ `; + }) + .add('Intercept open/close', () => { + let shouldIntercept = true; + let shouldInterceptButton; + function toggleIntercept() { + shouldIntercept = !shouldIntercept; + shouldInterceptButton.textContent = shouldIntercept; + } + function intercept(ev) { + if (shouldIntercept) { + ev.preventDefault(); + } + } + shouldInterceptButton = renderOffline( + html` + + `, + ); + + const popup = renderOffline(html` + + +
+ Hello! You can close this notification here: + +
+
+ `); + + return html` + + toggle shouldIntercept:${shouldInterceptButton} +
+ ${popup} +
+ `; }); /* .add('Toggle placement with knobs', () => { @@ -467,7 +553,7 @@ storiesOf('Overlay System | Overlay as a WC', module) Hello! You can close this notification here:
diff --git a/packages/overlays/test-suites/OverlayMixin.suite.js b/packages/overlays/test-suites/OverlayMixin.suite.js index cf95dff83..15b75ca4c 100644 --- a/packages/overlays/test-suites/OverlayMixin.suite.js +++ b/packages/overlays/test-suites/OverlayMixin.suite.js @@ -1,4 +1,5 @@ import { expect, fixture, html, aTimeout } from '@open-wc/testing'; +import sinon from 'sinon'; export function runOverlayMixinSuite({ /* tagString, */ tag, suffix = '' }) { describe(`OverlayMixin${suffix}`, () => { @@ -53,5 +54,87 @@ export function runOverlayMixinSuite({ /* tagString, */ tag, suffix = '' }) { itEl.config = { viewportConfig: { placement: 'left' } }; expect(itEl._overlayCtrl.viewportConfig.placement).to.equal('left'); }); + + it('fires "opened-changed" event on hide', async () => { + const spy = sinon.spy(); + el = await fixture(html` + <${tag} @opened-changed="${spy}"> +
content of the overlay
+ + + `); + expect(spy).not.to.have.been.called; + await el._overlayCtrl.show(); + expect(spy.callCount).to.equal(1); + expect(el.opened).to.be.true; + await el._overlayCtrl.hide(); + expect(spy.callCount).to.equal(2); + expect(el.opened).to.be.false; + }); + + it('fires "before-closed" event on hide', async () => { + const beforeSpy = sinon.spy(); + el = await fixture(html` + <${tag} @before-closed="${beforeSpy}" .opened="${true}"> +
content of the overlay
+ + + `); + expect(beforeSpy).not.to.have.been.called; + await el._overlayCtrl.hide(); + expect(beforeSpy).to.have.been.called; + expect(el.opened).to.be.false; + }); + + it('fires before-opened" event on show', async () => { + const beforeSpy = sinon.spy(); + el = await fixture(html` + <${tag} @before-opened="${beforeSpy}"> +
content of the overlay
+ + + `); + expect(beforeSpy).not.to.have.been.called; + await el._overlayCtrl.show(); + expect(beforeSpy).to.have.been.called; + expect(el.opened).to.be.true; + }); + + it('allows to call `preventDefault()` on "before-opened"/"before-closed" events', async () => { + function preventer(ev) { + ev.preventDefault(); + } + el = await fixture(html` + <${tag} @before-opened="${preventer}" @before-closed="${preventer}"> +
content of the overlay
+ + + `); + await el._overlayCtrl.show(); + expect(el.opened).to.be.false; + }); + + it('hides content on "close-overlay" event within the content ', async () => { + function sendCloseEvent(e) { + e.target.dispatchEvent(new Event('close-overlay', { bubbles: true })); + } + const closeBtn = await fixture(html` + + `); + + el = await fixture(html` + <${tag} opened> +
+ content of the overlay + ${closeBtn} +
+ + + `); + closeBtn.click(); + expect(el.opened).to.be.false; + }); }); } diff --git a/packages/overlays/test/OverlayController.test.js b/packages/overlays/test/OverlayController.test.js index 883507e11..e31b59a57 100644 --- a/packages/overlays/test/OverlayController.test.js +++ b/packages/overlays/test/OverlayController.test.js @@ -314,33 +314,6 @@ describe('OverlayController', () => { }); }); - describe('hidesOnHideEventInContentNode', () => { - it('hides content on hide event within the content ', async () => { - const ctrl = new OverlayController({ - ...withGlobalTestConfig(), - hidesOnHideEventInContentNode: true, - contentNode: fixtureSync(html` -
- my content - -
- `), - }); - await ctrl.show(); - - const closeBtn = ctrl.contentNode.querySelector('button'); - closeBtn.click(); - - expect(ctrl.isShown).to.be.false; - }); - - it('does stop propagation of the "hide" event to not pollute the event stack and to prevent side effects', () => { - // TODO: how to test this? - }); - }); - describe('hidesOnOutsideClick', () => { it('hides on outside click', async () => { const contentNode = await fixture('
Content
'); From 1290d9be9556311dd91c539e57d1b0652e5a419c Mon Sep 17 00:00:00 2001 From: Thijs Louisse Date: Wed, 11 Dec 2019 15:51:37 +0100 Subject: [PATCH 2/2] fix: deleted obsolete overlay event names --- packages/dialog/src/LionDialog.js | 7 ++----- packages/tooltip/src/LionTooltip.js | 3 ++- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/dialog/src/LionDialog.js b/packages/dialog/src/LionDialog.js index 68e1b0666..905576f01 100644 --- a/packages/dialog/src/LionDialog.js +++ b/packages/dialog/src/LionDialog.js @@ -2,11 +2,6 @@ import { withModalDialogConfig, OverlayMixin } from '@lion/overlays'; import { LitElement, html } from '@lion/core'; export class LionDialog extends OverlayMixin(LitElement) { - constructor() { - super(); - this.closeEventName = 'dialog-close'; - } - // eslint-disable-next-line class-methods-use-this _defineOverlayConfig() { return { @@ -15,6 +10,7 @@ export class LionDialog extends OverlayMixin(LitElement) { } _setupOpenCloseListeners() { + super._setupOpenCloseListeners(); this.__toggle = () => { this.opened = !this.opened; }; @@ -25,6 +21,7 @@ export class LionDialog extends OverlayMixin(LitElement) { } _teardownOpenCloseListeners() { + super._teardownOpenCloseListeners(); if (this._overlayInvokerNode) { this._overlayInvokerNode.removeEventListener('click', this.__toggle); } diff --git a/packages/tooltip/src/LionTooltip.js b/packages/tooltip/src/LionTooltip.js index ef1c09b5e..535bdbdff 100644 --- a/packages/tooltip/src/LionTooltip.js +++ b/packages/tooltip/src/LionTooltip.js @@ -4,7 +4,6 @@ import { LitElement, html } from '@lion/core'; export class LionTooltip extends OverlayMixin(LitElement) { constructor() { super(); - this.closeEventName = 'tooltip-close'; this.mouseActive = false; this.keyActive = false; } @@ -19,6 +18,7 @@ export class LionTooltip extends OverlayMixin(LitElement) { } _setupOpenCloseListeners() { + super._setupOpenCloseListeners(); this.__resetActive = () => { this.mouseActive = false; this.keyActive = false; @@ -58,6 +58,7 @@ export class LionTooltip extends OverlayMixin(LitElement) { } _teardownOpenCloseListeners() { + super._teardownOpenCloseListeners(); this._overlayCtrl.removeEventListener('hide', this.__resetActive); this.removeEventListener('mouseenter', this.__showMouse); this.removeEventListener('mouseleave', this._hideMouse);