diff --git a/integration/tests/dialog-popover.test.js b/integration/tests/dialog-popover.test.js
index 7ef20b4305..f1647386d7 100644
--- a/integration/tests/dialog-popover.test.js
+++ b/integration/tests/dialog-popover.test.js
@@ -71,3 +71,66 @@ describe('popover in dialog', () => {
});
});
});
+
+describe('dialog in popover', () => {
+ let popover, target, button, dialog;
+
+ beforeEach(async () => {
+ [target, popover] = fixtureSync(`
+
+
+
+
+ `).children;
+
+ popover.renderer = (root) => {
+ root.innerHTML = `
+
+
+ `;
+ [button, dialog] = root.children;
+
+ button.addEventListener('click', () => {
+ dialog.opened = true;
+ });
+
+ dialog.renderer = (dialogRoot) => {
+ dialogRoot.textContent = 'Dialog content';
+ };
+ };
+
+ await nextRender();
+ target.click();
+ await nextRender();
+ });
+
+ ['modal', 'modeless'].forEach((type) => {
+ describe(`${type} popover`, () => {
+ beforeEach(async () => {
+ if (type === 'modal') {
+ popover.modal = true;
+ await nextUpdate(popover);
+ }
+
+ button.focus();
+ button.click();
+ await nextRender();
+ });
+
+ it(`should not close the ${type} popover when closing a child dialog on Escape`, async () => {
+ await sendKeys({ press: 'Escape' });
+
+ expect(dialog.opened).to.be.false;
+ expect(popover.opened).to.be.true;
+ });
+
+ it(`should close the ${type} popover on subsequent Escape after the child dialog is closed`, async () => {
+ await sendKeys({ press: 'Escape' });
+
+ await sendKeys({ press: 'Escape' });
+
+ expect(popover.opened).to.be.false;
+ });
+ });
+ });
+});
diff --git a/packages/popover/src/vaadin-popover.js b/packages/popover/src/vaadin-popover.js
index 301c852b36..ed29e57483 100644
--- a/packages/popover/src/vaadin-popover.js
+++ b/packages/popover/src/vaadin-popover.js
@@ -12,6 +12,7 @@ import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
import { OverlayClassMixin } from '@vaadin/component-base/src/overlay-class-mixin.js';
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
import { generateUniqueId } from '@vaadin/component-base/src/unique-id-utils.js';
+import { isLastOverlay } from '@vaadin/overlay/src/vaadin-overlay-stack-mixin.js';
import { ThemePropertyMixin } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
import { PopoverPositionMixin } from './vaadin-popover-position-mixin.js';
import { PopoverTargetMixin } from './vaadin-popover-target-mixin.js';
@@ -500,7 +501,8 @@ class Popover extends PopoverPositionMixin(
!this.__isManual &&
!this.modal &&
!event.composedPath().some((el) => el === this._overlayElement || el === this.target) &&
- !this.noCloseOnOutsideClick
+ !this.noCloseOnOutsideClick &&
+ isLastOverlay(this._overlayElement)
) {
this._openedStateController.close(true);
}
@@ -526,7 +528,14 @@ class Popover extends PopoverPositionMixin(
* @private
*/
__onGlobalKeyDown(event) {
- if (event.key === 'Escape' && !this.modal && !this.noCloseOnEsc && this.opened && !this.__isManual) {
+ if (
+ event.key === 'Escape' &&
+ !this.modal &&
+ !this.noCloseOnEsc &&
+ this.opened &&
+ !this.__isManual &&
+ isLastOverlay(this._overlayElement)
+ ) {
// Prevent closing parent overlay (e.g. dialog)
event.stopPropagation();
this._openedStateController.close(true);
diff --git a/packages/popover/test/basic.test.js b/packages/popover/test/basic.test.js
index f69063517c..7740101a29 100644
--- a/packages/popover/test/basic.test.js
+++ b/packages/popover/test/basic.test.js
@@ -269,6 +269,66 @@ describe('popover', () => {
});
});
+ describe('nested popovers', () => {
+ let secondPopover, secondTarget;
+
+ function nestedRenderer(root) {
+ root.innerHTML = `
+
+
+ `;
+ [secondTarget, secondPopover] = root.children;
+ }
+
+ beforeEach(async () => {
+ popover.renderer = nestedRenderer;
+
+ // Open the first popover
+ target.click();
+ await nextRender();
+
+ // Open the second popover
+ secondTarget.click();
+ await nextRender();
+
+ // Expect both popovers to be opened
+ expect(popover.opened).to.be.true;
+ expect(secondPopover.opened).to.be.true;
+ });
+
+ it('should close the topmost overlay on global Escape press', async () => {
+ esc(document.body);
+ await nextRender();
+
+ // Expect only the second popover to be closed
+ expect(popover.opened).to.be.true;
+ expect(secondPopover.opened).to.be.false;
+
+ esc(document.body);
+ await nextRender();
+
+ // Expect both popovers to be closed
+ expect(popover.opened).to.be.false;
+ expect(secondPopover.opened).to.be.false;
+ });
+
+ it('should close the topmost overlay on outside click', async () => {
+ outsideClick();
+ await nextRender();
+
+ // Expect only the second popover to be closed
+ expect(popover.opened).to.be.true;
+ expect(secondPopover.opened).to.be.false;
+
+ outsideClick();
+ await nextRender();
+
+ // Expect both popovers to be closed
+ expect(popover.opened).to.be.false;
+ expect(secondPopover.opened).to.be.false;
+ });
+ });
+
describe('backdrop', () => {
beforeEach(async () => {
popover.withBackdrop = true;