Merge pull request #436 from ing-bank/feat/closeEventOnOverlay
feat(overlay): close/hide events on dom (OverlayMixin) level
This commit is contained in:
commit
eefd2e61b4
11 changed files with 268 additions and 92 deletions
|
|
@ -2,11 +2,6 @@ import { withModalDialogConfig, OverlayMixin } from '@lion/overlays';
|
||||||
import { LitElement, html } from '@lion/core';
|
import { LitElement, html } from '@lion/core';
|
||||||
|
|
||||||
export class LionDialog extends OverlayMixin(LitElement) {
|
export class LionDialog extends OverlayMixin(LitElement) {
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.closeEventName = 'dialog-close';
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line class-methods-use-this
|
// eslint-disable-next-line class-methods-use-this
|
||||||
_defineOverlayConfig() {
|
_defineOverlayConfig() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -15,6 +10,7 @@ export class LionDialog extends OverlayMixin(LitElement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
_setupOpenCloseListeners() {
|
_setupOpenCloseListeners() {
|
||||||
|
super._setupOpenCloseListeners();
|
||||||
this.__toggle = () => {
|
this.__toggle = () => {
|
||||||
this.opened = !this.opened;
|
this.opened = !this.opened;
|
||||||
};
|
};
|
||||||
|
|
@ -25,6 +21,7 @@ export class LionDialog extends OverlayMixin(LitElement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
_teardownOpenCloseListeners() {
|
_teardownOpenCloseListeners() {
|
||||||
|
super._teardownOpenCloseListeners();
|
||||||
if (this._overlayInvokerNode) {
|
if (this._overlayInvokerNode) {
|
||||||
this._overlayInvokerNode.removeEventListener('click', this.__toggle);
|
this._overlayInvokerNode.removeEventListener('click', this.__toggle);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ storiesOf('Overlays Specific WC | Dialog', module)
|
||||||
Hello! You can close this notification here:
|
Hello! You can close this notification here:
|
||||||
<button
|
<button
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
|
||||||
>
|
>
|
||||||
⨯
|
⨯
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -97,7 +97,7 @@ storiesOf('Overlays Specific WC | Dialog', module)
|
||||||
Hello! You can close this notification here:
|
Hello! You can close this notification here:
|
||||||
<button
|
<button
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
|
||||||
>
|
>
|
||||||
⨯
|
⨯
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -123,7 +123,7 @@ storiesOf('Overlays Specific WC | Dialog', module)
|
||||||
Hello! You can close this notification here:
|
Hello! You can close this notification here:
|
||||||
<button
|
<button
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
|
||||||
>
|
>
|
||||||
⨯
|
⨯
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -90,9 +90,8 @@ export class LionCalendarOverlayFrame extends LocalizeMixin(LitElement) {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
__dispatchHideEvent() {
|
__dispatchCloseEvent() {
|
||||||
// Designed to work in conjunction with ModalDialogController
|
this.dispatchEvent(new Event('close-overlay'), { bubbles: true });
|
||||||
this.dispatchEvent(new CustomEvent('hide'), { bubbles: true });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
@ -104,7 +103,7 @@ export class LionCalendarOverlayFrame extends LocalizeMixin(LitElement) {
|
||||||
<slot name="heading"></slot>
|
<slot name="heading"></slot>
|
||||||
</h1>
|
</h1>
|
||||||
<button
|
<button
|
||||||
@click="${this.__dispatchHideEvent}"
|
@click="${this.__dispatchCloseEvent}"
|
||||||
id="close-button"
|
id="close-button"
|
||||||
title="${this.msgLit('lion-calendar-overlay-frame:close')}"
|
title="${this.msgLit('lion-calendar-overlay-frame:close')}"
|
||||||
aria-label="${this.msgLit('lion-calendar-overlay-frame:close')}"
|
aria-label="${this.msgLit('lion-calendar-overlay-frame:close')}"
|
||||||
|
|
|
||||||
|
|
@ -343,6 +343,10 @@ export class LionInputDatepicker extends OverlayMixin(LionInputDate) {
|
||||||
* @override Configures OverlayMixin
|
* @override Configures OverlayMixin
|
||||||
*/
|
*/
|
||||||
get _overlayContentNode() {
|
get _overlayContentNode() {
|
||||||
return this.shadowRoot.querySelector('lion-calendar-overlay-frame');
|
if (this._cachedOverlayContentNode) {
|
||||||
|
return this._cachedOverlayContentNode;
|
||||||
|
}
|
||||||
|
this._cachedOverlayContentNode = this.shadowRoot.querySelector('lion-calendar-overlay-frame');
|
||||||
|
return this._cachedOverlayContentNode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ html`
|
||||||
<div slot="content">
|
<div slot="content">
|
||||||
This is an overlay
|
This is an overlay
|
||||||
<button
|
<button
|
||||||
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
@click=${e => e.target.dispatchEvent(new Event('overlay-close', { bubbles: true }))}
|
||||||
>x</button>
|
>x</button>
|
||||||
<div>
|
<div>
|
||||||
<button slot="invoker">
|
<button slot="invoker">
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ export class OverlayController {
|
||||||
trapsKeyboardFocus: false,
|
trapsKeyboardFocus: false,
|
||||||
hidesOnEsc: false,
|
hidesOnEsc: false,
|
||||||
hidesOnOutsideClick: false,
|
hidesOnOutsideClick: false,
|
||||||
hidesOnHideEventInContentNode: true,
|
|
||||||
isTooltip: false,
|
isTooltip: false,
|
||||||
handlesUserInteraction: false,
|
handlesUserInteraction: false,
|
||||||
handlesAccessibility: false,
|
handlesAccessibility: false,
|
||||||
|
|
@ -244,12 +243,16 @@ export class OverlayController {
|
||||||
if (this.isShown) {
|
if (this.isShown) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.dispatchEvent(new Event('before-show'));
|
|
||||||
this._contentNodeWrapper.style.display = this.placementMode === 'local' ? 'inline-block' : '';
|
const event = new CustomEvent('before-show', { cancelable: true });
|
||||||
await this._handleFeatures({ phase: 'show' });
|
this.dispatchEvent(event);
|
||||||
await this._handlePosition({ phase: 'show' });
|
if (!event.defaultPrevented) {
|
||||||
this.elementToFocusAfterHide = elementToFocusAfterHide;
|
this._contentNodeWrapper.style.display = this.placementMode === 'local' ? 'inline-block' : '';
|
||||||
this.dispatchEvent(new Event('show'));
|
await this._handleFeatures({ phase: 'show' });
|
||||||
|
await this._handlePosition({ phase: 'show' });
|
||||||
|
this.elementToFocusAfterHide = elementToFocusAfterHide;
|
||||||
|
this.dispatchEvent(new Event('show'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _handlePosition({ phase }) {
|
async _handlePosition({ phase }) {
|
||||||
|
|
@ -285,12 +288,15 @@ export class OverlayController {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dispatchEvent(new Event('before-hide'));
|
const event = new CustomEvent('before-hide', { cancelable: true });
|
||||||
// await this.transitionHide({ backdropNode: this.backdropNode, conentNode: this.contentNode });
|
this.dispatchEvent(event);
|
||||||
this._contentNodeWrapper.style.display = 'none';
|
if (!event.defaultPrevented) {
|
||||||
this._handleFeatures({ phase: 'hide' });
|
// await this.transitionHide({ backdropNode: this.backdropNode, conentNode: this.contentNode });
|
||||||
this.dispatchEvent(new Event('hide'));
|
this._contentNodeWrapper.style.display = 'none';
|
||||||
this._restoreFocus();
|
this._handleFeatures({ phase: 'hide' });
|
||||||
|
this.dispatchEvent(new Event('hide'));
|
||||||
|
this._restoreFocus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line class-methods-use-this, no-empty-function, no-unused-vars
|
// eslint-disable-next-line class-methods-use-this, no-empty-function, no-unused-vars
|
||||||
|
|
@ -337,9 +343,6 @@ export class OverlayController {
|
||||||
if (this.hidesOnOutsideClick) {
|
if (this.hidesOnOutsideClick) {
|
||||||
this._handleHidesOnOutsideClick({ phase });
|
this._handleHidesOnOutsideClick({ phase });
|
||||||
}
|
}
|
||||||
if (this.hidesOnHideEventInContentNode) {
|
|
||||||
this._handleHidesOnHideEventInContentNode({ phase });
|
|
||||||
}
|
|
||||||
if (this.handlesAccessibility) {
|
if (this.handlesAccessibility) {
|
||||||
this._handleAccessibility({ phase });
|
this._handleAccessibility({ phase });
|
||||||
}
|
}
|
||||||
|
|
@ -498,18 +501,6 @@ export class OverlayController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleHidesOnHideEventInContentNode({ phase }) {
|
|
||||||
if (phase === 'show') {
|
|
||||||
this.__hideEventInContentNodeHandler = ev => {
|
|
||||||
ev.stopPropagation();
|
|
||||||
this.hide();
|
|
||||||
};
|
|
||||||
this.contentNode.addEventListener('hide', this.__hideEventInContentNodeHandler);
|
|
||||||
} else if (phase === 'hide') {
|
|
||||||
this.contentNode.removeEventListener('keyup', this.__hideEventInContentNodeHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleInheritsReferenceWidth() {
|
_handleInheritsReferenceWidth() {
|
||||||
if (!this._referenceNode) {
|
if (!this._referenceNode) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,13 @@ export const OverlayMixin = dedupeMixin(
|
||||||
this.__config = value;
|
this.__config = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_requestUpdate(name, oldValue) {
|
||||||
|
super._requestUpdate(name, oldValue);
|
||||||
|
if (name === 'opened') {
|
||||||
|
this.dispatchEvent(new Event('opened-changed'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @overridable method `_defineOverlay`
|
* @overridable method `_defineOverlay`
|
||||||
* @desc returns an instance of a (dynamic) overlay controller
|
* @desc returns an instance of a (dynamic) overlay controller
|
||||||
|
|
@ -81,14 +88,32 @@ export const OverlayMixin = dedupeMixin(
|
||||||
* For example, set a click event listener on _overlayInvokerNode to set opened to true
|
* For example, set a click event listener on _overlayInvokerNode to set opened to true
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line class-methods-use-this
|
// eslint-disable-next-line class-methods-use-this
|
||||||
_setupOpenCloseListeners() {}
|
_setupOpenCloseListeners() {
|
||||||
|
this.__closeEventInContentNodeHandler = ev => {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this._overlayCtrl.hide();
|
||||||
|
};
|
||||||
|
if (this._overlayContentNode) {
|
||||||
|
this._overlayContentNode.addEventListener(
|
||||||
|
'close-overlay',
|
||||||
|
this.__closeEventInContentNodeHandler,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @overridable
|
* @overridable
|
||||||
* @desc use this method to tear down your event listeners
|
* @desc use this method to tear down your event listeners
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line class-methods-use-this
|
// eslint-disable-next-line class-methods-use-this
|
||||||
_teardownOpenCloseListeners() {}
|
_teardownOpenCloseListeners() {
|
||||||
|
if (this._overlayContentNode) {
|
||||||
|
this._overlayContentNode.removeEventListener(
|
||||||
|
'close-overlay',
|
||||||
|
this.__closeEventInContentNodeHandler,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
firstUpdated(changedProperties) {
|
firstUpdated(changedProperties) {
|
||||||
super.firstUpdated(changedProperties);
|
super.firstUpdated(changedProperties);
|
||||||
|
|
@ -110,7 +135,9 @@ export const OverlayMixin = dedupeMixin(
|
||||||
}
|
}
|
||||||
|
|
||||||
get _overlayContentNode() {
|
get _overlayContentNode() {
|
||||||
let contentNode;
|
if (this._cachedOverlayContentNode) {
|
||||||
|
return this._cachedOverlayContentNode;
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: This should shadow outlet in between the host and the content slot, is a problem
|
// FIXME: This should shadow outlet in between the host and the content slot, is a problem
|
||||||
// Should simply be Array.from(this.children).find(child => child.slot === 'content')
|
// Should simply be Array.from(this.children).find(child => child.slot === 'content')
|
||||||
|
|
@ -119,15 +146,15 @@ export const OverlayMixin = dedupeMixin(
|
||||||
child => child.slot === '_overlay-shadow-outlet',
|
child => child.slot === '_overlay-shadow-outlet',
|
||||||
);
|
);
|
||||||
if (shadowOutlet) {
|
if (shadowOutlet) {
|
||||||
contentNode = Array.from(shadowOutlet.children).find(child => child.slot === 'content');
|
this._cachedOverlayContentNode = Array.from(shadowOutlet.children).find(
|
||||||
|
child => child.slot === 'content',
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
contentNode = Array.from(this.children).find(child => child.slot === 'content');
|
this._cachedOverlayContentNode = Array.from(this.children).find(
|
||||||
|
child => child.slot === 'content',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
return this._cachedOverlayContentNode;
|
||||||
if (contentNode) {
|
|
||||||
this._cachedOverlayContentNode = contentNode;
|
|
||||||
}
|
|
||||||
return contentNode || this._cachedOverlayContentNode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_setupOverlayCtrl() {
|
_setupOverlayCtrl() {
|
||||||
|
|
@ -137,7 +164,6 @@ export const OverlayMixin = dedupeMixin(
|
||||||
});
|
});
|
||||||
this.__syncToOverlayController();
|
this.__syncToOverlayController();
|
||||||
this.__setupSyncFromOverlayController();
|
this.__setupSyncFromOverlayController();
|
||||||
|
|
||||||
this._setupOpenCloseListeners();
|
this._setupOpenCloseListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -151,22 +177,38 @@ export const OverlayMixin = dedupeMixin(
|
||||||
this.__onOverlayCtrlShow = () => {
|
this.__onOverlayCtrlShow = () => {
|
||||||
this.opened = true;
|
this.opened = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.__onOverlayCtrlHide = () => {
|
this.__onOverlayCtrlHide = () => {
|
||||||
this.opened = false;
|
this.opened = false;
|
||||||
};
|
};
|
||||||
this.__onBeforeShow = () => {
|
|
||||||
this.dispatchEvent(new Event('before-show'));
|
this.__onBeforeShow = beforeShowEvent => {
|
||||||
|
const event = new CustomEvent('before-opened', { cancelable: true });
|
||||||
|
this.dispatchEvent(event);
|
||||||
|
if (event.defaultPrevented) {
|
||||||
|
beforeShowEvent.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.__onBeforeHide = beforeHideEvent => {
|
||||||
|
const event = new CustomEvent('before-closed', { cancelable: true });
|
||||||
|
this.dispatchEvent(event);
|
||||||
|
if (event.defaultPrevented) {
|
||||||
|
beforeHideEvent.preventDefault();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this._overlayCtrl.addEventListener('show', this.__onOverlayCtrlShow);
|
this._overlayCtrl.addEventListener('show', this.__onOverlayCtrlShow);
|
||||||
this._overlayCtrl.addEventListener('hide', this.__onOverlayCtrlHide);
|
this._overlayCtrl.addEventListener('hide', this.__onOverlayCtrlHide);
|
||||||
this._overlayCtrl.addEventListener('before-show', this.__onBeforeShow);
|
this._overlayCtrl.addEventListener('before-show', this.__onBeforeShow);
|
||||||
|
this._overlayCtrl.addEventListener('before-hide', this.__onBeforeHide);
|
||||||
}
|
}
|
||||||
|
|
||||||
__teardownSyncFromOverlayController() {
|
__teardownSyncFromOverlayController() {
|
||||||
this._overlayCtrl.removeEventListener('show', this.__onOverlayCtrlShow);
|
this._overlayCtrl.removeEventListener('show', this.__onOverlayCtrlShow);
|
||||||
this._overlayCtrl.removeEventListener('hide', this.__onOverlayCtrlHide);
|
this._overlayCtrl.removeEventListener('hide', this.__onOverlayCtrlHide);
|
||||||
this._overlayCtrl.removeEventListener('before-show', this.__onBeforeShow);
|
this._overlayCtrl.removeEventListener('before-show', this.__onBeforeShow);
|
||||||
|
this._overlayCtrl.removeEventListener('before-hide', this.__onBeforeHide);
|
||||||
}
|
}
|
||||||
|
|
||||||
__syncToOverlayController() {
|
__syncToOverlayController() {
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,7 @@ customElements.define(
|
||||||
}
|
}
|
||||||
|
|
||||||
_setupOpenCloseListeners() {
|
_setupOpenCloseListeners() {
|
||||||
|
super._setupOpenCloseListeners();
|
||||||
this.__toggle = () => {
|
this.__toggle = () => {
|
||||||
this.opened = !this.opened;
|
this.opened = !this.opened;
|
||||||
};
|
};
|
||||||
|
|
@ -106,6 +107,7 @@ customElements.define(
|
||||||
}
|
}
|
||||||
|
|
||||||
_teardownOpenCloseListeners() {
|
_teardownOpenCloseListeners() {
|
||||||
|
super._teardownOpenCloseListeners();
|
||||||
this._overlayInvokerNode.removeEventListener('click', this.__toggle);
|
this._overlayInvokerNode.removeEventListener('click', this.__toggle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,7 +151,7 @@ storiesOf('Overlay System | Overlay as a WC', module)
|
||||||
Hello! You can close this notification here:
|
Hello! You can close this notification here:
|
||||||
<button
|
<button
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
|
||||||
>
|
>
|
||||||
⨯
|
⨯
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -168,7 +170,7 @@ storiesOf('Overlay System | Overlay as a WC', module)
|
||||||
Hello! You can close this notification here:
|
Hello! You can close this notification here:
|
||||||
<button
|
<button
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
|
||||||
>
|
>
|
||||||
⨯
|
⨯
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -197,7 +199,9 @@ storiesOf('Overlay System | Overlay as a WC', module)
|
||||||
<div slot="content" class="demo-overlay">
|
<div slot="content" class="demo-overlay">
|
||||||
<div>
|
<div>
|
||||||
Hello! This is a notification.
|
Hello! This is a notification.
|
||||||
<button @click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}>
|
<button
|
||||||
|
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
|
||||||
|
>
|
||||||
Close
|
Close
|
||||||
</button>
|
</button>
|
||||||
<lion-demo-overlay
|
<lion-demo-overlay
|
||||||
|
|
@ -208,7 +212,8 @@ storiesOf('Overlay System | Overlay as a WC', module)
|
||||||
Hello! You can close this notification here:
|
Hello! You can close this notification here:
|
||||||
<button
|
<button
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
@click=${e =>
|
||||||
|
e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
|
||||||
>
|
>
|
||||||
⨯
|
⨯
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -234,7 +239,7 @@ storiesOf('Overlay System | Overlay as a WC', module)
|
||||||
Hello! You can close this notification here:
|
Hello! You can close this notification here:
|
||||||
<button
|
<button
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
|
||||||
>
|
>
|
||||||
⨯
|
⨯
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -299,7 +304,7 @@ storiesOf('Overlay System | Overlay as a WC', module)
|
||||||
Hello! You can close this notification here:
|
Hello! You can close this notification here:
|
||||||
<button
|
<button
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
|
||||||
>
|
>
|
||||||
⨯
|
⨯
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -355,7 +360,7 @@ storiesOf('Overlay System | Overlay as a WC', module)
|
||||||
<p>Close and open it again on a small screen (< 600px) and it will be a bottom sheet</p>
|
<p>Close and open it again on a small screen (< 600px) and it will be a bottom sheet</p>
|
||||||
<lion-demo-overlay
|
<lion-demo-overlay
|
||||||
.config=${{ ...withBottomSheetConfig() }}
|
.config=${{ ...withBottomSheetConfig() }}
|
||||||
@before-show=${e => {
|
@before-opened=${e => {
|
||||||
if (window.innerWidth >= 600) {
|
if (window.innerWidth >= 600) {
|
||||||
e.target.config = { ...withModalDialogConfig() };
|
e.target.config = { ...withModalDialogConfig() };
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -368,7 +373,7 @@ storiesOf('Overlay System | Overlay as a WC', module)
|
||||||
Hello! You can close this notification here:
|
Hello! You can close this notification here:
|
||||||
<button
|
<button
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
|
||||||
>
|
>
|
||||||
⨯
|
⨯
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -450,6 +455,87 @@ storiesOf('Overlay System | Overlay as a WC', module)
|
||||||
${popup}
|
${popup}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
})
|
||||||
|
.add('Sync application state', () => {
|
||||||
|
const appState = {
|
||||||
|
opened: true,
|
||||||
|
};
|
||||||
|
const openedStateNode = renderOffline(
|
||||||
|
html`
|
||||||
|
<span>${appState.opened}</span>
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
function onOpenClosed(ev) {
|
||||||
|
appState.opened = ev.target.opened;
|
||||||
|
openedStateNode.innerText = appState.opened;
|
||||||
|
}
|
||||||
|
const popup = renderOffline(html`
|
||||||
|
<lion-demo-overlay .opened="${appState.opened}" @opened-changed=${onOpenClosed}>
|
||||||
|
<button slot="invoker">Overlay</button>
|
||||||
|
<div slot="content" class="demo-overlay">
|
||||||
|
Hello! You can close this notification here:
|
||||||
|
<button
|
||||||
|
class="close-button"
|
||||||
|
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
|
||||||
|
>
|
||||||
|
⨯
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</lion-demo-overlay>
|
||||||
|
`);
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<style>
|
||||||
|
${overlayDemoStyle}
|
||||||
|
</style>
|
||||||
|
appState.opened: ${openedStateNode}
|
||||||
|
<div class="demo-box_placements">
|
||||||
|
${popup}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
})
|
||||||
|
.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`
|
||||||
|
<button @click="${toggleIntercept}">${shouldIntercept}</button>
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const popup = renderOffline(html`
|
||||||
|
<lion-demo-overlay @before-closed=${intercept} @before-opened=${intercept}>
|
||||||
|
<button slot="invoker">Overlay</button>
|
||||||
|
<div slot="content" class="demo-overlay">
|
||||||
|
Hello! You can close this notification here:
|
||||||
|
<button
|
||||||
|
class="close-button"
|
||||||
|
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
|
||||||
|
>
|
||||||
|
⨯
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</lion-demo-overlay>
|
||||||
|
`);
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<style>
|
||||||
|
${overlayDemoStyle}
|
||||||
|
</style>
|
||||||
|
toggle shouldIntercept:${shouldInterceptButton}
|
||||||
|
<div class="demo-box_placements">
|
||||||
|
${popup}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
});
|
});
|
||||||
|
|
||||||
/* .add('Toggle placement with knobs', () => {
|
/* .add('Toggle placement with knobs', () => {
|
||||||
|
|
@ -467,7 +553,7 @@ storiesOf('Overlay System | Overlay as a WC', module)
|
||||||
Hello! You can close this notification here:
|
Hello! You can close this notification here:
|
||||||
<button
|
<button
|
||||||
class="close-button"
|
class="close-button"
|
||||||
@click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}
|
@click=${e => e.target.dispatchEvent(new Event('close-overlay', { bubbles: true }))}
|
||||||
>⨯</button
|
>⨯</button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { expect, fixture, html, aTimeout } from '@open-wc/testing';
|
import { expect, fixture, html, aTimeout } from '@open-wc/testing';
|
||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
export function runOverlayMixinSuite({ /* tagString, */ tag, suffix = '' }) {
|
export function runOverlayMixinSuite({ /* tagString, */ tag, suffix = '' }) {
|
||||||
describe(`OverlayMixin${suffix}`, () => {
|
describe(`OverlayMixin${suffix}`, () => {
|
||||||
|
|
@ -53,5 +54,87 @@ export function runOverlayMixinSuite({ /* tagString, */ tag, suffix = '' }) {
|
||||||
itEl.config = { viewportConfig: { placement: 'left' } };
|
itEl.config = { viewportConfig: { placement: 'left' } };
|
||||||
expect(itEl._overlayCtrl.viewportConfig.placement).to.equal('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}">
|
||||||
|
<div slot="content">content of the overlay</div>
|
||||||
|
<button slot="invoker">invoker button</button>
|
||||||
|
</${tag}>
|
||||||
|
`);
|
||||||
|
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}">
|
||||||
|
<div slot="content">content of the overlay</div>
|
||||||
|
<button slot="invoker">invoker button</button>
|
||||||
|
</${tag}>
|
||||||
|
`);
|
||||||
|
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}">
|
||||||
|
<div slot="content">content of the overlay</div>
|
||||||
|
<button slot="invoker">invoker button</button>
|
||||||
|
</${tag}>
|
||||||
|
`);
|
||||||
|
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}">
|
||||||
|
<div slot="content">content of the overlay</div>
|
||||||
|
<button slot="invoker">invoker button</button>
|
||||||
|
</${tag}>
|
||||||
|
`);
|
||||||
|
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`
|
||||||
|
<button @click=${sendCloseEvent}>
|
||||||
|
close
|
||||||
|
</button>
|
||||||
|
`);
|
||||||
|
|
||||||
|
el = await fixture(html`
|
||||||
|
<${tag} opened>
|
||||||
|
<div slot="content">
|
||||||
|
content of the overlay
|
||||||
|
${closeBtn}
|
||||||
|
</div>
|
||||||
|
<button slot="invoker">invoker button</button>
|
||||||
|
</${tag}>
|
||||||
|
`);
|
||||||
|
closeBtn.click();
|
||||||
|
expect(el.opened).to.be.false;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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`
|
|
||||||
<div>
|
|
||||||
my content
|
|
||||||
<button @click=${e => e.target.dispatchEvent(new Event('hide', { bubbles: true }))}>
|
|
||||||
x
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
`),
|
|
||||||
});
|
|
||||||
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', () => {
|
describe('hidesOnOutsideClick', () => {
|
||||||
it('hides on outside click', async () => {
|
it('hides on outside click', async () => {
|
||||||
const contentNode = await fixture('<div>Content</div>');
|
const contentNode = await fixture('<div>Content</div>');
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import { LitElement, html } from '@lion/core';
|
||||||
export class LionTooltip extends OverlayMixin(LitElement) {
|
export class LionTooltip extends OverlayMixin(LitElement) {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.closeEventName = 'tooltip-close';
|
|
||||||
this.mouseActive = false;
|
this.mouseActive = false;
|
||||||
this.keyActive = false;
|
this.keyActive = false;
|
||||||
}
|
}
|
||||||
|
|
@ -19,6 +18,7 @@ export class LionTooltip extends OverlayMixin(LitElement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
_setupOpenCloseListeners() {
|
_setupOpenCloseListeners() {
|
||||||
|
super._setupOpenCloseListeners();
|
||||||
this.__resetActive = () => {
|
this.__resetActive = () => {
|
||||||
this.mouseActive = false;
|
this.mouseActive = false;
|
||||||
this.keyActive = false;
|
this.keyActive = false;
|
||||||
|
|
@ -58,6 +58,7 @@ export class LionTooltip extends OverlayMixin(LitElement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
_teardownOpenCloseListeners() {
|
_teardownOpenCloseListeners() {
|
||||||
|
super._teardownOpenCloseListeners();
|
||||||
this._overlayCtrl.removeEventListener('hide', this.__resetActive);
|
this._overlayCtrl.removeEventListener('hide', this.__resetActive);
|
||||||
this.removeEventListener('mouseenter', this.__showMouse);
|
this.removeEventListener('mouseenter', this.__showMouse);
|
||||||
this.removeEventListener('mouseleave', this._hideMouse);
|
this.removeEventListener('mouseleave', this._hideMouse);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue