[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:
@@ -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>
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user