fix: allow the popup dialog to close when it is setup by lit cache (#2563)
This commit is contained in:
parent
9ac3fa403a
commit
da22fdb7d0
5 changed files with 137 additions and 7 deletions
5
.changeset/six-ghosts-crash.md
Normal file
5
.changeset/six-ghosts-crash.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@lion/ui': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
[overlays] allow the popup dialog to close when it is setup by `lit` `cache`
|
||||||
|
|
@ -1,9 +1,20 @@
|
||||||
/* eslint-disable lit-a11y/no-autofocus */
|
/* eslint-disable lit-a11y/no-autofocus */
|
||||||
import { expect, fixture as _fixture, html, unsafeStatic, aTimeout } from '@open-wc/testing';
|
import {
|
||||||
|
expect,
|
||||||
|
fixture as _fixture,
|
||||||
|
html,
|
||||||
|
unsafeStatic,
|
||||||
|
aTimeout,
|
||||||
|
defineCE,
|
||||||
|
waitUntil,
|
||||||
|
} from '@open-wc/testing';
|
||||||
|
import { cache } from 'lit/directives/cache.js';
|
||||||
|
import { LitElement, nothing } from 'lit';
|
||||||
import { runOverlayMixinSuite } from '../../overlays/test-suites/OverlayMixin.suite.js';
|
import { runOverlayMixinSuite } from '../../overlays/test-suites/OverlayMixin.suite.js';
|
||||||
import { isActiveElement } from '../../core/test-helpers/isActiveElement.js';
|
import { isActiveElement } from '../../core/test-helpers/isActiveElement.js';
|
||||||
import '../test-helpers/test-router.js';
|
import '../test-helpers/test-router.js';
|
||||||
import '@lion/ui/define/lion-dialog.js';
|
import '@lion/ui/define/lion-dialog.js';
|
||||||
|
import '@lion/ui/define/lion-tabs.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import('../src/LionDialog.js').LionDialog} LionDialog
|
* @typedef {import('../src/LionDialog.js').LionDialog} LionDialog
|
||||||
|
|
@ -296,5 +307,107 @@ describe('lion-dialog', () => {
|
||||||
await dialogAfterRouteChange._overlayCtrl._showComplete;
|
await dialogAfterRouteChange._overlayCtrl._showComplete;
|
||||||
expect(dialogAfterRouteChange.opened).to.be.true;
|
expect(dialogAfterRouteChange.opened).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should close the popup dialog after rendered from cache', async () => {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Event} e
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const closeButtonHandler = e =>
|
||||||
|
e.target?.dispatchEvent(new Event('close-overlay', { bubbles: true }));
|
||||||
|
const dialog = html` <lion-dialog>
|
||||||
|
<button slot="invoker" class="invoker-button">Click me to open dialog</button>
|
||||||
|
<div slot="content" class="demo-dialog-content">
|
||||||
|
Hello! You can close this dialog here:
|
||||||
|
<button class="close-button" @click="${closeButtonHandler}">⨯</button>
|
||||||
|
</div>
|
||||||
|
</lion-dialog>`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note, inactive tab content is **destroyed** on every tab switch.
|
||||||
|
*/
|
||||||
|
class Wrapper extends LitElement {
|
||||||
|
static properties = {
|
||||||
|
...super.properties,
|
||||||
|
activeTabIndex: { type: Number },
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.activeTabIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} index
|
||||||
|
*/
|
||||||
|
changeActiveTabIndex(index) {
|
||||||
|
this.activeTabIndex = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const changeActiveTabIndexRef = this.changeActiveTabIndex.bind(this);
|
||||||
|
return html`
|
||||||
|
<lion-tabs>
|
||||||
|
<button slot="tab" class="first-button" @click=${() => changeActiveTabIndexRef(0)}>
|
||||||
|
First
|
||||||
|
</button>
|
||||||
|
<p slot="panel">${cache(this.activeTabIndex === 0 ? dialog : nothing)}</p>
|
||||||
|
<button slot="tab" class="second-button" @click=${() => changeActiveTabIndexRef(1)}>
|
||||||
|
Second
|
||||||
|
</button>
|
||||||
|
<p slot="panel">Info page with lots of information about us.</p>
|
||||||
|
</lion-tabs>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapperFixture = /** @type {(arg: TemplateResult) => Promise<Wrapper>} */ (_fixture);
|
||||||
|
const tagString = defineCE(Wrapper);
|
||||||
|
const wrapperTag = unsafeStatic(tagString);
|
||||||
|
const wrapperElement = /** @type {Wrapper} */ (
|
||||||
|
await wrapperFixture(html`<${wrapperTag}></${wrapperTag}>`)
|
||||||
|
);
|
||||||
|
await wrapperElement.updateComplete;
|
||||||
|
const wrapperElementShadowRoot = wrapperElement.shadowRoot;
|
||||||
|
/**
|
||||||
|
* @returns { HTMLElement | null | undefined }
|
||||||
|
*/
|
||||||
|
const getFirstButton = () => wrapperElementShadowRoot?.querySelector('.first-button');
|
||||||
|
/**
|
||||||
|
* @returns { HTMLElement | null | undefined }
|
||||||
|
*/
|
||||||
|
const getSecondButton = () => wrapperElementShadowRoot?.querySelector('.second-button');
|
||||||
|
/**
|
||||||
|
* @returns { HTMLElement | null | undefined }
|
||||||
|
*/
|
||||||
|
const getInvokerButton = () => wrapperElementShadowRoot?.querySelector('.invoker-button');
|
||||||
|
/**
|
||||||
|
* @returns { HTMLElement | null | undefined }
|
||||||
|
*/
|
||||||
|
const getCloseButton = () => wrapperElementShadowRoot?.querySelector('.close-button');
|
||||||
|
/**
|
||||||
|
* @returns { Element | null | undefined }
|
||||||
|
*/
|
||||||
|
const getDialog = () =>
|
||||||
|
wrapperElementShadowRoot?.querySelector('lion-dialog')?.shadowRoot?.querySelector('dialog');
|
||||||
|
// @ts-ignore
|
||||||
|
const isDialogVisible = () => getDialog()?.checkVisibility() === true;
|
||||||
|
const isDialogRendered = () =>
|
||||||
|
!!wrapperElement.shadowRoot?.querySelector('lion-dialog')?.shadowRoot?.childNodes.length;
|
||||||
|
getInvokerButton()?.click();
|
||||||
|
await waitUntil(isDialogVisible);
|
||||||
|
getCloseButton()?.click();
|
||||||
|
await waitUntil(() => !isDialogVisible());
|
||||||
|
getSecondButton()?.click();
|
||||||
|
await waitUntil(() => !isDialogRendered());
|
||||||
|
getFirstButton()?.click();
|
||||||
|
await waitUntil(isDialogRendered);
|
||||||
|
getInvokerButton()?.click();
|
||||||
|
await waitUntil(isDialogVisible);
|
||||||
|
getCloseButton()?.click();
|
||||||
|
await waitUntil(() => !isDialogVisible());
|
||||||
|
expect(isDialogVisible()).to.equal(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -246,16 +246,23 @@ export const OverlayMixinImplementation = superclass =>
|
||||||
|
|
||||||
/** @protected */
|
/** @protected */
|
||||||
_setupOverlayCtrl() {
|
_setupOverlayCtrl() {
|
||||||
if (this._overlayCtrl) return;
|
if (this.#hasSetup) return;
|
||||||
|
const config = {
|
||||||
/** @type {OverlayController} */
|
|
||||||
this._overlayCtrl = this._defineOverlay({
|
|
||||||
contentNode: this._overlayContentNode,
|
contentNode: this._overlayContentNode,
|
||||||
contentWrapperNode: this._overlayContentWrapperNode,
|
contentWrapperNode: this._overlayContentWrapperNode,
|
||||||
invokerNode: this._overlayInvokerNode,
|
invokerNode: this._overlayInvokerNode,
|
||||||
referenceNode: this._overlayReferenceNode,
|
referenceNode: this._overlayReferenceNode,
|
||||||
backdropNode: this._overlayBackdropNode,
|
backdropNode: this._overlayBackdropNode,
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (this._overlayCtrl) {
|
||||||
|
// when `lit` `cache` attaches node to the DOM, register the controller back in the OverlaysManager
|
||||||
|
this._overlayCtrl.updateConfig(config);
|
||||||
|
} else {
|
||||||
|
/** @type {OverlayController} */
|
||||||
|
this._overlayCtrl = this._defineOverlay(config);
|
||||||
|
}
|
||||||
|
|
||||||
this.__syncToOverlayController();
|
this.__syncToOverlayController();
|
||||||
this.__setupSyncFromOverlayController();
|
this.__setupSyncFromOverlayController();
|
||||||
this._setupOpenCloseListeners();
|
this._setupOpenCloseListeners();
|
||||||
|
|
|
||||||
|
|
@ -533,8 +533,11 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) {
|
||||||
|
|
||||||
el.switched = false;
|
el.switched = false;
|
||||||
await el.updateComplete;
|
await el.updateComplete;
|
||||||
|
|
||||||
expect(setupSpy.callCount).to.equal(1);
|
expect(setupSpy.callCount).to.equal(1);
|
||||||
|
|
||||||
|
const elCtrl = /** @type {OverlayEl} */ (el.overlayEl)?._overlayCtrl;
|
||||||
|
const isCtrlRegisteredAtOverlaysManager = elCtrl.manager.list.some(ctrl => elCtrl === ctrl);
|
||||||
|
expect(isCtrlRegisteredAtOverlaysManager).to.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('correctly removes event listeners when disconnected from dom', async () => {
|
it('correctly removes event listeners when disconnected from dom', async () => {
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,8 @@ async function createNestedEscControllers(parentContent) {
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
overlays.teardown();
|
overlays.teardown();
|
||||||
|
// clean document.body from the DOM nodes left by previous tests
|
||||||
|
document.body.innerHTML = '';
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('OverlayController', () => {
|
describe('OverlayController', () => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue