[FIX] web: click on ViewButton will close parent Dropdown

# Issue
A Dropdown is not closed when a child ViewButton is clicked.

# Solution
Make the ViewButton close the Dropdown.

# Note
Usually, a dropdown is closed by a DropdownItem component.
This is not the case here, because the clicks on view buttons are stopped
(see the view button template). Letting them bubble would close their
eventual parent dropdown, but it brings other issues (e.g. for a view button
in a list renderer's group row, or view buttons that could get interpreted
as action links under some conditions <a type="action" .../>).

Instead of looking in the env to close the parent dropdown if any,
an alternative solution would be to extend the ViewButton component,
wrap it in a DropdownItem component, and reroute its `onClick` handler
to the DropdownItem's `onSelected` prop.
But this requires some preparation at the form compiler level
(when compiling the status bar buttons), which we thought was overengineering
at the time of writing.

closes odoo/odoo#98399

Signed-off-by: Samuel Degueldre <sad@odoo.com>
This commit is contained in:
Bruno Boi
2022-08-19 09:12:51 +02:00
parent cffedb2ee9
commit df52c0297f
2 changed files with 55 additions and 2 deletions
@@ -1,5 +1,6 @@
/** @odoo-module */
import { DROPDOWN } from "@web/core/dropdown/dropdown";
import { debounce as debounceFn } from "@web/core/utils/timing";
const { Component } = owl;
@@ -84,6 +85,11 @@ export class ViewButton extends Component {
this.env.onClickViewButton({
clickParams: this.clickParams,
record: this.props.record,
beforeExecute: () => {
if (this.env[DROPDOWN]) {
this.env[DROPDOWN].close();
}
},
});
}
@@ -1,11 +1,14 @@
/** @odoo-module */
import { ViewButton } from "@web/views/view_button/view_button";
import { useViewButtons } from "@web/views/view_button/view_button_hook";
import { setupViewRegistries } from "./helpers";
import { click, getFixture } from "../helpers/utils";
import { click, getFixture, mount } from "../helpers/utils";
import { makeTestEnv } from "../helpers/mock_env";
import { registry } from "@web/core/registry";
import { Dropdown } from "@web/core/dropdown/dropdown";
import { DropdownItem } from "@web/core/dropdown/dropdown_item";
const { useRef, mount, Component, xml } = owl;
const { useRef, Component, xml } = owl;
QUnit.module("UseViewButton tests", (hooks) => {
let target;
@@ -76,4 +79,48 @@ QUnit.module("UseViewButton tests", (hooks) => {
await click(target, ".myComponent");
assert.verifySteps(["beforeExecuteAction on handler"]);
});
QUnit.test("ViewButton clicked in Dropdown close the Dropdown", async (assert) => {
registry.category("services").add(
"action",
{
start() {
return {
doActionButton() {
assert.step("doActionButton");
},
};
},
},
{ force: true }
);
class MyComponent extends Component {
setup() {
const rootRef = useRef("root");
useViewButtons({}, rootRef);
}
}
MyComponent.components = { Dropdown, DropdownItem, ViewButton };
MyComponent.template = xml`
<div t-ref="root" class="myComponent">
<Dropdown>
<t t-set-slot="toggler">dropdown</t>
<DropdownItem>
<ViewButton tag="'a'" clickParams="{ type:'action' }" string="'coucou'" record="{ resId: 1 }" />
</DropdownItem>
</Dropdown>
</div>
`;
const env = await makeTestEnv();
await mount(MyComponent, target, { env });
await click(target, ".dropdown-toggle");
assert.containsOnce(target, ".dropdown-menu");
await click(target, "a[type=action]");
assert.verifySteps(["doActionButton"]);
assert.containsNone(target, ".dropdown-menu");
});
});