FIX: Propagate pointerdown events on DMenu trigger when the menu isn't expanded (#31104)

Stopping propagation when a `DMenu`'s trigger is clicked could prevent
another floating UI element, e.g. the search menu in the header, from
closing when clicking outside of it. We call `stopPropagation` on the
event to allow more clicks within a `DMenu`'s trigger without it getting
closed, so we shouldn't stop the event propagation if the `DMenu` hasn't
been expanded yet.
This commit is contained in:
Osama Sayegh
2025-02-05 18:01:37 +03:00
committed by GitHub
parent 0da3349513
commit bb91561b9e
3 changed files with 54 additions and 2 deletions

View File

@@ -386,4 +386,46 @@ module("Integration | Component | FloatKit | d-menu", function (hooks) {
assert.dom(document.body).isFocused();
});
test("traps pointerdown events only when expanded ", async function (assert) {
let propagated = false;
const listener = () => {
propagated = true;
};
this.didInsert = (element) => {
element.addEventListener("pointerdown", listener);
};
this.willDestroy = (element) => {
element.removeEventListener("pointerdown", listener);
};
await render(hbs`
<div {{didInsert this.didInsert}} {{will-destroy this.willDestroy}}>
<DMenu
@inline={{true}}
@label="label"
@identifier="d-menu-pointerdown-trap-test"
/>
</div>
`);
await triggerEvent(".d-menu-pointerdown-trap-test-trigger", "pointerdown");
assert.true(
propagated,
"the pointerdown event is propagated to the parent element when the menu isn't expanded"
);
propagated = false;
await open();
await triggerEvent(".d-menu-pointerdown-trap-test-trigger", "pointerdown");
assert.false(
propagated,
"the pointerdown event isn't propagated to the parent element when the menu is expanded"
);
});
});

View File

@@ -53,6 +53,10 @@ export default class DMenuInstance extends FloatKitInstance {
this.setupListeners();
}
get shouldTrapPointerDown() {
return this.expanded;
}
@action
async close(options = { focusTrigger: true }) {
if (getOwner(this).isDestroying) {

View File

@@ -53,7 +53,9 @@ export default class FloatKitInstance {
// this is done to avoid trigger on click outside when you click on your own trigger
// given trigger and content are not in the same div, we can't just check if target is
// inside the menu
event.stopPropagation();
if (this.shouldTrapPointerDown) {
event.stopPropagation();
}
}
@action
@@ -117,7 +119,7 @@ export default class FloatKitInstance {
.forEach((trigger) => {
switch (trigger) {
case "hold":
this.trigger.addEventListener("touchstart", this.onTouchStart);
this.trigger.removeEventListener("touchstart", this.onTouchStart);
break;
case "focus":
this.trigger.removeEventListener("focus", this.onFocus);
@@ -231,4 +233,8 @@ export default class FloatKitInstance {
return this.options.untriggers ?? ["click"];
}
get shouldTrapPointerDown() {
return true;
}
}