Skip to content

Commit 7a0c715

Browse files
Overlay: fix outside click handling for stacked inner overlays (T1327307) (#33389)
1 parent 65ceb37 commit 7a0c715

2 files changed

Lines changed: 63 additions & 2 deletions

File tree

packages/devextreme/js/__internal/ui/overlay/overlay.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ class Overlay<
456456

457457
const isTargetDocument = domUtils.contains(window.document, target);
458458
const isAttachedTarget = $(window.document).is($target) || isTargetDocument;
459-
const isInnerOverlay = $($target).closest(`.${INNER_OVERLAY_CLASS}`).length;
459+
const isInnerOverlay = this._isTargetInLowerInnerOverlay($target);
460460
const isTargetContent = this._$content?.is($target);
461461
const content = this._$content?.get(0);
462462
const isTargetInContent = content ? domUtils.contains(content, target) : false;
@@ -496,6 +496,27 @@ class Overlay<
496496
this.hide();
497497
}
498498

499+
_isTargetInLowerInnerOverlay($target: dxElementWrapper): boolean {
500+
const $closestInnerOverlay = $target.closest(`.${INNER_OVERLAY_CLASS}`);
501+
502+
if (!$closestInnerOverlay.length) {
503+
return false;
504+
}
505+
506+
const overlayStack = this._overlayStack();
507+
const innerOverlayElement = $closestInnerOverlay.get(0);
508+
// @ts-expect-error this and Overlay have no overlap
509+
const thisIndex = overlayStack.indexOf(this);
510+
511+
for (let i = 0; i < overlayStack.length; i += 1) {
512+
if (overlayStack[i]._$content?.get(0) === innerOverlayElement) {
513+
return thisIndex <= i;
514+
}
515+
}
516+
517+
return true;
518+
}
519+
499520
_getAnonymousTemplateName(): string {
500521
return ANONYMOUS_TEMPLATE_NAME;
501522
}

packages/devextreme/testing/tests/DevExpress.ui.widgets/overlay.tests.js

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2063,6 +2063,46 @@ testModule('hide on outside click', moduleConfig, () => {
20632063
assert.equal(overlay1.option('visible'), true, 'Bottom overlay should not get outside click when inner overlay clicked');
20642064
});
20652065

2066+
test('overlay opened above inner overlay should close on click inside inner overlay', function(assert) {
2067+
const overlay1 = $('#overlay').dxOverlay({
2068+
hideOnOutsideClick: true,
2069+
innerOverlay: true,
2070+
visible: true,
2071+
propagateOutsideClick: true
2072+
}).dxOverlay('instance');
2073+
2074+
const overlay2 = $('#overlay2').dxOverlay({
2075+
hideOnOutsideClick: true,
2076+
visible: true,
2077+
propagateOutsideClick: true
2078+
}).dxOverlay('instance');
2079+
2080+
$(overlay1.$content()).trigger('dxpointerdown');
2081+
2082+
assert.strictEqual(overlay2.option('visible'), false, 'Overlay opened above inner overlay should close');
2083+
assert.strictEqual(overlay1.option('visible'), true, 'Inner overlay itself should stay visible');
2084+
});
2085+
2086+
test('click inside inner overlay should not close overlays below it in stack', function(assert) {
2087+
const overlay1 = $('#overlay').dxOverlay({
2088+
hideOnOutsideClick: true,
2089+
visible: true,
2090+
propagateOutsideClick: true
2091+
}).dxOverlay('instance');
2092+
2093+
const overlay2 = $('#overlay2').dxOverlay({
2094+
hideOnOutsideClick: true,
2095+
innerOverlay: true,
2096+
visible: true,
2097+
propagateOutsideClick: true
2098+
}).dxOverlay('instance');
2099+
2100+
$(overlay2.$content()).trigger('dxpointerdown');
2101+
2102+
assert.strictEqual(overlay1.option('visible'), true, 'Overlay below inner overlay should not close');
2103+
assert.strictEqual(overlay2.option('visible'), true, 'Inner overlay should stay visible');
2104+
});
2105+
20662106
// T494814
20672107
test('overlay should not be hidden after click in detached element', function(assert) {
20682108
const overlay = $('#overlayWithAnonymousTmpl').dxOverlay({
@@ -4125,7 +4165,7 @@ testModule('renderGeometry', {
41254165
};
41264166
for(const optionName in newOptions) {
41274167
QUnit.testInActiveWindow(optionName, function(assert) {
4128-
// eslint-disable-next-line qunit/no-async-in-loops
4168+
41294169
const showingResizeHandled = assert.async();
41304170
setTimeout(() => {
41314171
this.overlayInstance.option(optionName, newOptions[optionName]);

0 commit comments

Comments
 (0)