[IMP] web: add buttons in control panel without selection in list

This commit adds the possibility to define buttons that are always
present in the arch of a list view.

Purpose:
Several customisations of the list view consist in adding an always
present button in the control panel that performs an action/calls a
model's method.
We want to support this feature directly in the framework to remove
these customisations.

Solution:
Add a "display" option that lets you know when to display a button.
The different values that can be assigned to "display":
- "selection": the button is only displayed if at least one record is selected.
- "always": the button is always displayed.
The default value of "display" is "selection".

Exemple:
<tree>
    <header>
        <button name="to_do" type="object" string="Do" display="always"/>
    </header>
</tree>

Taskid: 3082303
Part-of: odoo/odoo#106519
This commit is contained in:
FrancoisGe
2022-12-16 18:39:56 +01:00
parent 875be3bc0c
commit ce30c50e8d
4 changed files with 59 additions and 9 deletions
@@ -47,26 +47,26 @@
Create
</button>
</t>
<t t-if="nbTotal and !nbSelected and activeActions.exportXlsx and isExportEnable and !env.isSmall">
<button type="button" class="btn btn-secondary fa fa-download o_list_export_xlsx" data-tooltip="Export All" aria-label="Export All" t-on-click="onDirectExportData"/>
</t>
</t>
<t t-if="nbSelected">
<t t-foreach="archInfo.headerButtons" t-as="button" t-key="button.id">
<ListViewHeaderButton
t-if="nbSelected || (button.display === 'always')"
list="model.root"
className="button.className"
clickParams="button.clickParams"
defaultRank="button.defaultRank"
defaultRank="'btn-secondary'"
domain="props.domain"
icon="button.icon"
string="button.string"
title="button.title"
/>
</t>
<t t-if="!env.isSmall">
<t t-call="web.ListView.Selection"/>
<t t-if="nbTotal and !nbSelected and activeActions.exportXlsx and isExportEnable and !env.isSmall">
<button type="button" class="btn btn-secondary fa fa-download o_list_export_xlsx" data-tooltip="Export All" aria-label="Export All" t-on-click="onDirectExportData"/>
</t>
</t>
<t t-if="!env.isSmall and nbSelected">
<t t-call="web.ListView.Selection"/>
</t>
</div>
</t>
+1
View File
@@ -208,6 +208,7 @@ export function processButton(node) {
string: node.getAttribute("string") || undefined,
options: JSON.parse(node.getAttribute("options") || "{}"),
modifiers: JSON.parse(node.getAttribute("modifiers") || "{}"),
display: node.getAttribute("display") || "selection",
clickParams: {
close: archParseBoolean(node.getAttribute("close"), false),
context: node.getAttribute("context") || "{}",
@@ -810,7 +810,7 @@ QUnit.module("Views", (hooks) => {
);
cpButtons = getButtons(target);
assert.containsOnce(cpButtons[0], 'button[name="x"]');
assert.hasClass(cpButtons[0].querySelector('button[name="x"]'), "btn btn-secondary");
assert.hasClass(cpButtons[0].querySelector('button[name="x"]'), "btn btn-secondary plaf");
assert.containsOnce(cpButtons[0], ".o_list_selection_box");
assert.strictEqual(
cpButtons[0].querySelector('button[name="x"]').nextElementSibling,
@@ -827,6 +827,54 @@ QUnit.module("Views", (hooks) => {
assert.containsNone(cpButtons[0], 'button[name="y"]');
});
QUnit.test(
"list view: action button in controlPanel with display='always'",
async function (assert) {
await makeView({
type: "list",
resModel: "foo",
serverData,
arch: `
<tree>
<header>
<button name="display" type="object" class="display" string="display" display="always"/>
<button name="default-selection" type="object" class="default-selection" string="default-selection"/>
</header>
<field name="foo" />
</tree>`,
});
let cpButtons = getButtons(target)[0];
assert.deepEqual(
[...cpButtons.querySelectorAll("button")].map((button) =>
button.textContent.trim()
),
["Create", "display", ""]
);
await click(
target.querySelector('.o_data_row .o_list_record_selector input[type="checkbox"]')
);
cpButtons = getButtons(target)[0];
assert.deepEqual(
[...cpButtons.querySelectorAll("button")].map((button) =>
button.textContent.trim()
),
["Create", "display", "default-selection"]
);
await click(
target.querySelector('.o_data_row .o_list_record_selector input[type="checkbox"]')
);
cpButtons = getButtons(target)[0];
assert.deepEqual(
[...cpButtons.querySelectorAll("button")].map((button) =>
button.textContent.trim()
),
["Create", "display", ""]
);
}
);
QUnit.test(
"list view: action button executes action on click: buttons are disabled and re-enabled",
async function (assert) {
+1
View File
@@ -360,6 +360,7 @@
<rng:optional><rng:attribute name="title"/></rng:optional>
<rng:optional><rng:attribute name="aria-label"/></rng:optional>
<rng:optional><rng:attribute name="aria-pressed"/></rng:optional>
<rng:optional><rng:attribute name="display"/></rng:optional>
<rng:zeroOrMore>
<rng:choice>
<rng:ref name="field" />