[FIX] web: ButtonBox: do not display More for one button

Before this commit, it could happen that the More dropdown was
displayed, but it only contained a single stat button. This isn't
what we want, as that single button could simply be displayed
instead of the More dropdown toggler.

Task 3778382

closes odoo/odoo#156241

Related: odoo/enterprise#58565
Signed-off-by: Lucas Perais (lpe) <lpe@odoo.com>
This commit is contained in:
Aaron Bohy
2024-03-13 12:28:18 +00:00
parent 24e88bec4f
commit 96e3782a14
4 changed files with 58 additions and 23 deletions
@@ -4,27 +4,30 @@ import { useService } from "@web/core/utils/hooks";
import { Dropdown } from "@web/core/dropdown/dropdown";
import { DropdownItem } from "@web/core/dropdown/dropdown_item";
import { Component } from "@odoo/owl";
import { Component, onWillRender } from "@odoo/owl";
export class ButtonBox extends Component {
setup() {
const ui = useService("ui");
this.getMaxButtons = () => [2, 3, 4, 6, 3, 4, 7][ui.size] || 7;
onWillRender(() => {
const maxVisibleButtons = [3, 4, 5, 7, 4, 5, 8][ui.size] || 8;
const allVisibleButtons = Object.entries(this.props.slots)
.filter(([_, slot]) => this.isSlotVisible(slot))
.map(([slotName]) => slotName);
if (allVisibleButtons.length <= maxVisibleButtons) {
this.visibleButtons = allVisibleButtons;
this.additionalButtons = [];
this.isFull = allVisibleButtons.length === maxVisibleButtons;
} else {
// -1 for "More" dropdown
this.visibleButtons = allVisibleButtons.slice(0, maxVisibleButtons - 1);
this.additionalButtons = allVisibleButtons.slice(maxVisibleButtons - 1);
this.isFull = true;
}
});
}
getButtons() {
const maxVisibleButtons = this.getMaxButtons();
const visible = [];
const additional = [];
for (const [slotName, slot] of Object.entries(this.props.slots)) {
if (!("isVisible" in slot) || slot.isVisible) {
if (visible.length >= maxVisibleButtons) {
additional.push(slotName);
} else {
visible.push(slotName);
}
}
}
return { visible, additional };
isSlotVisible(slot) {
return !("isVisible" in slot) || slot.isVisible;
}
}
ButtonBox.template = "web.Form.ButtonBox";
@@ -2,10 +2,7 @@
<templates xml:space="preserve">
<t t-name="web.Form.ButtonBox" >
<t t-set="allButtons" t-value="getButtons()" />
<t t-set="visibleButtons" t-value="allButtons['visible']" />
<t t-set="additionalButtons" t-value="allButtons['additional']" />
<div class="o-form-buttonbox position-relative d-flex w-md-auto" t-attf-class="{{ visibleButtons.length >= getMaxButtons() ? 'o_full w-100' : 'o_not_full'}} {{this.props.class}}">
<div class="o-form-buttonbox position-relative d-flex w-md-auto" t-attf-class="{{ isFull ? 'o_full w-100' : 'o_not_full'}} {{this.props.class}}">
<t t-slot="{{ button_value }}" t-foreach="visibleButtons" t-as="button" t-key="button_value"/>
<t t-if="additionalButtons.length" >
<Dropdown position="'bottom-end'" togglerClass="'o_button_more btn btn-outline-secondary d-flex justify-content-center align-items-center'" menuClass="'o_dropdown_more position-absolute top-100 p-0 border-0'" class="'oe_stat_button btn position-relative p-0 border-0'">
@@ -28,6 +28,7 @@ QUnit.module("Mobile Views", ({ beforeEach }) => {
fields: {
display_name: { type: "char", string: "Display Name" },
trululu: { type: "many2one", string: "Trululu", relation: "partner" },
boolean: { type: "boolean", string: "Bool" },
},
records: [
{ id: 1, display_name: "first record", trululu: 4 },
@@ -479,4 +480,38 @@ QUnit.module("Mobile Views", ({ beforeEach }) => {
await nextTick();
assert.verifySteps(["post"]);
});
QUnit.test("button box with 3/4 buttons (close to threshold)", async (assert) => {
await makeView({
type: "form",
resModel: "partner",
serverData,
arch: `
<form>
<sheet>
<div name="button_box">
<button>MyButton</button>
<button>MyButton2</button>
<button>MyButton3</button>
<button invisible="not boolean">MyButton4</button>
</div>
<field name="boolean"/>
</sheet>
</form>`,
resId: 2,
});
// 3 buttons to display -> no "More" dropdown
assert.containsNone(fixture, ".o_field_widget[name=boolean] input:checked");
assert.containsN(fixture, ".o-form-buttonbox > .oe_stat_button", 3);
assert.containsNone(fixture, ".o-form-buttonbox .o_button_more");
// 4 buttons to display -> 2 buttons visible + 2 inside the "More" dropdown
await click(fixture.querySelector(".o_field_widget[name=boolean] input"));
assert.containsN(fixture, ".o-form-buttonbox > .oe_stat_button", 3);
assert.containsOnce(fixture, ".o-form-buttonbox .oe_stat_button .o_button_more");
await click(fixture.querySelector(".o_button_more"));
assert.containsN(fixture, ".o_dropdown_more .oe_stat_button", 2);
});
});
@@ -430,7 +430,7 @@ QUnit.module("Views", (hooks) => {
});
return {
bus: new EventBus(),
size: 9,
size: 6,
isSmall: false,
};
},
@@ -8391,7 +8391,7 @@ QUnit.module("Views", (hooks) => {
});
QUnit.test("correct amount of buttons", async function (assert) {
let screenSize = 7;
let screenSize = 6;
const uiService = {
start(env) {
Object.defineProperty(env, "isSmall", {
@@ -8445,7 +8445,7 @@ QUnit.module("Views", (hooks) => {
await assertFormContainsNButtonsWithSizeClass(0, 2);
await assertFormContainsNButtonsWithSizeClass(1, 3);
await assertFormContainsNButtonsWithSizeClass(2, 4);
await assertFormContainsNButtonsWithSizeClass(3, 6);
await assertFormContainsNButtonsWithSizeClass(3, 7);
await assertFormContainsNButtonsWithSizeClass(4, 3);
await assertFormContainsNButtonsWithSizeClass(5, 4);
await assertFormContainsNButtonsWithSizeClass(6, 7);