Skip to content

Commit 1d903fc

Browse files
authored
fix: prevent overlay opening when setting opened while disconnected (#9758)
1 parent 45001eb commit 1d903fc

File tree

2 files changed

+89
-1
lines changed

2 files changed

+89
-1
lines changed

packages/overlay/src/vaadin-overlay-mixin.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,12 @@ export const OverlayMixin = (superClass) =>
292292
/** @private */
293293
_openedChanged(opened, wasOpened) {
294294
if (opened) {
295+
// Prevent possible errors on setting `opened` to `true` while disconnected
296+
if (!this.isConnected) {
297+
this.opened = false;
298+
return;
299+
}
300+
295301
this._saveFocus();
296302

297303
this._animatedOpening();

packages/overlay/test/basic.test.js

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { expect } from '@vaadin/chai-plugins';
22
import { aTimeout, fixtureSync, isIOS, nextFrame, oneEvent } from '@vaadin/testing-helpers';
33
import sinon from 'sinon';
4-
import '../src/vaadin-overlay.js';
4+
import { Overlay } from '../src/vaadin-overlay.js';
55
import { createOverlay } from './helpers.js';
66

77
describe('vaadin-overlay', () => {
@@ -274,4 +274,86 @@ describe('vaadin-overlay', () => {
274274
overlay._detectIosNavbar.restore();
275275
});
276276
});
277+
278+
describe('disconnected', () => {
279+
customElements.define(
280+
'overlay-wrapper',
281+
class extends HTMLElement {
282+
constructor() {
283+
super();
284+
285+
this.attachShadow({ mode: 'open' });
286+
287+
const overlay = document.createElement('wrapped-overlay');
288+
this.overlay = overlay;
289+
290+
// Forward the slotted content from wrapper to overlay
291+
const slot = document.createElement('slot');
292+
overlay.appendChild(slot);
293+
294+
overlay.renderer = (root) => {
295+
root.innerHTML = '<input placeholder="Input">';
296+
};
297+
298+
this.shadowRoot.append(overlay);
299+
}
300+
301+
get opened() {
302+
return this.overlay.opened;
303+
}
304+
305+
set opened(opened) {
306+
this.overlay.opened = opened;
307+
}
308+
309+
connectedCallback() {
310+
const root = document.createElement('div');
311+
this.overlay.__customRoot = root;
312+
this.append(root);
313+
}
314+
315+
disconnectedCallback() {
316+
this.opened = false;
317+
}
318+
},
319+
);
320+
321+
customElements.define(
322+
'wrapped-overlay',
323+
class extends Overlay {
324+
get _contentRoot() {
325+
return this.__customRoot;
326+
}
327+
328+
_attachOverlay() {
329+
this.setAttribute('popover', 'manual');
330+
this.showPopover();
331+
}
332+
333+
_detachOverlay() {
334+
this.hidePopover();
335+
}
336+
},
337+
);
338+
339+
let owner, overlay;
340+
341+
beforeEach(() => {
342+
owner = document.createElement('overlay-wrapper');
343+
document.body.appendChild(owner);
344+
overlay = owner.shadowRoot.querySelector('wrapped-overlay');
345+
});
346+
347+
it('should not call overlay opening when overlay is disconnected', async () => {
348+
overlay.opened = true;
349+
await oneEvent(overlay, 'vaadin-overlay-open');
350+
351+
const spy = sinon.spy(overlay, '_attachOverlay');
352+
353+
owner.remove();
354+
owner.opened = true;
355+
356+
expect(spy.called).to.be.false;
357+
});
358+
});
277359
});

0 commit comments

Comments
 (0)