[FIX] web: avoid closing dropdown outside the active element

When a dropdown open a dialog and on the dialog there is a new dropdown,
before this commit, when clicking on the second dropdown, the first one
will close.

This is an issue with closing the first dropdown, is if the dialog is
opened using useOwnedDialogs, the dialog will also close.

Also, if the dialog is open to throw a "find more" of a x2many field,
even if the dialog is not close, as the dropdown will close, the
original field will also be destroyed and it will raise an error.

Now, we avoid closing dropdown's, when opening new dropdowns that there
are not on the same active element.

opw-3464528

closes odoo/odoo#144238

X-original-commit: 6fec387d258dfd945dbbd48a9aab91ce6113ccaa
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
Signed-off-by: Jorge Pinna Puissant (jpp) <jpp@odoo.com>
This commit is contained in:
Jorge Pinna Puissant
2023-12-04 15:10:30 +00:00
parent 167999c350
commit c7b07f4ec5
2 changed files with 69 additions and 2 deletions
@@ -261,7 +261,11 @@ export class Dropdown extends Component {
* @param {DropdownStateChangedPayload} args
*/
onDropdownStateChanged(args) {
if (!this.rootRef.el || this.rootRef.el.contains(args.emitter.rootRef.el)) {
if (
!this.rootRef.el ||
this.rootRef.el.contains(args.emitter.rootRef.el) ||
args.emitter.myActiveEl !== this.myActiveEl
) {
// Do not listen to events emitted by self or children
return;
}
+64 -1
View File
@@ -11,7 +11,7 @@ import { hotkeyService } from "@web/core/hotkeys/hotkey_service";
import { registry } from "@web/core/registry";
import { uiService } from "@web/core/ui/ui_service";
import { registerCleanup } from "../helpers/cleanup";
import { makeTestEnv } from "../helpers/mock_env";
import { clearRegistryWithCleanup, makeTestEnv } from "../helpers/mock_env";
import { makeFakeLocalizationService } from "../helpers/mock_services";
import {
click,
@@ -28,6 +28,8 @@ import {
import { makeParent } from "./tooltip/tooltip_service_tests";
import { getPickerCell } from "./datetime/datetime_test_helpers";
import { datetimePickerService } from "@web/core/datetime/datetimepicker_service";
import { Dialog } from "@web/core/dialog/dialog";
import { dialogService } from "@web/core/dialog/dialog_service";
const serviceRegistry = registry.category("services");
@@ -1385,4 +1387,65 @@ QUnit.module("Components", ({ beforeEach }) => {
`<span class="dropdown-item selected" role="menuitemcheckbox" tabindex="0" aria-checked="true"> My checkbox item </span>`
);
});
QUnit.test("don't close dropdown outside the active element", async (assert) => {
// This test checks that if a dropdown element opens a dialog with a dropdown inside,
// opening this dropdown will not close the first dropdown.
class CustomDialog extends Component {}
CustomDialog.template = xml`
<Dialog title="'Welcome'">
<Dropdown>
<DropdownItem>Item</DropdownItem>
</Dropdown>
<div class="outside_dialog">Outside Dialog</div>
</Dialog>`;
CustomDialog.components = { Dialog, Dropdown, DropdownItem };
const mainComponentRegistry = registry.category("main_components");
clearRegistryWithCleanup(mainComponentRegistry);
serviceRegistry.add("dialog", dialogService);
serviceRegistry.add("l10n", makeFakeLocalizationService());
class PseudoWebClient extends Component {
setup() {
this.Components = mainComponentRegistry.getEntries();
}
clicked() {
env.services.dialog.add(CustomDialog);
}
}
PseudoWebClient.template = xml`
<div>
<div>
<t t-foreach="Components" t-as="C" t-key="C[0]">
<t t-component="C[1].Component" t-props="C[1].props"/>
</t>
</div>
<div>
<Dropdown>
<button class="click-me" t-on-click="clicked">Click me</button>
</Dropdown>
<div class="outside_parent">Outside Parent</div>
</div>
</div>
`;
PseudoWebClient.components = { Dropdown };
env = await makeTestEnv();
await mount(PseudoWebClient, target, { env });
await click(target, "button.dropdown-toggle");
assert.containsOnce(target, ".dropdown-menu");
await click(target, "button.click-me");
assert.containsOnce(target, ".modal-dialog");
await click(target, ".modal-dialog button.dropdown-toggle");
assert.containsN(target, ".dropdown-menu", 2);
await click(target, ".outside_dialog");
assert.containsOnce(target, ".modal-dialog");
assert.containsN(target, ".dropdown-menu", 1);
await click(target, ".modal-dialog .btn-primary");
assert.containsNone(target, ".modal-dialog");
assert.containsN(target, ".dropdown-menu", 1);
await click(target, ".outside_parent");
assert.containsNone(target, ".dropdown-menu");
});
});