[FIX] board: display all data when force 1 column layout on small screen

Before this commit, on a dashboard having a multi-column layout, the
user couldn't see all those columns' actions when the layout fallback to
the "1 column" on a small screen.

This commit fixes it by, not only keeping the fallback to "1 column" to
optimize the screen's real-estate, but also properly bringing the hidden
columns' actions in the single column, so they become accessible by the
user.

Steps to reproduce:

- Create My dashboard on PC with two views set one next to the other
- Open My dashboard on mobile devices, and you see only the action
inside the column on the left and not the action inside the columns on
the right => bug

opw-3145706

closes odoo/odoo#124360

X-original-commit: fa1da53af1d327c4fafd944547dbf3c7312a93b4
Signed-off-by: Pierre Paridans (app) <app@odoo.com>
Signed-off-by: Romain Estievenart (res) <res@odoo.com>
This commit is contained in:
Romain Estievenart
2023-06-08 23:01:23 +02:00
parent 911e2a5df9
commit 46992fa90f
3 changed files with 80 additions and 33 deletions
+30 -26
View File
@@ -18,29 +18,29 @@ export class BoardController extends Component {
this.rpc = useService("rpc");
this.dialogService = useService("dialog");
if (this.env.isSmall) {
this.board.layout = "1";
this.board.colNumber = 1;
this.selectLayout("1", false);
} else {
const mainRef = useRef("main");
useSortable({
ref: mainRef,
elements: ".o-dashboard-action",
handle: ".o-dashboard-action-header",
cursor: "move",
groups: ".o-dashboard-column",
connectGroups: true,
onDrop: ({ element, previous, parent }) => {
const fromColIdx = parseInt(element.parentElement.dataset.idx, 10);
const fromActionIdx = parseInt(element.dataset.idx, 10);
const toColIdx = parseInt(parent.dataset.idx, 10);
const toActionIdx = previous ? parseInt(previous.dataset.idx, 10) + 1 : 0;
if (fromColIdx !== toColIdx) {
// to reduce visual flickering
element.classList.add("d-none");
}
this.moveAction(fromColIdx, fromActionIdx, toColIdx, toActionIdx);
},
});
}
const mainRef = useRef("main");
useSortable({
ref: mainRef,
elements: ".o-dashboard-action",
handle: ".o-dashboard-action-header",
cursor: "move",
groups: ".o-dashboard-column",
connectGroups: true,
onDrop: ({ element, previous, parent }) => {
const fromColIdx = parseInt(element.parentElement.dataset.idx, 10);
const fromActionIdx = parseInt(element.dataset.idx, 10);
const toColIdx = parseInt(parent.dataset.idx, 10);
const toActionIdx = previous ? parseInt(previous.dataset.idx, 10) + 1 : 0;
if (fromColIdx !== toColIdx) {
// to reduce visual flickering
element.classList.add("d-none");
}
this.moveAction(fromColIdx, fromActionIdx, toColIdx, toActionIdx);
},
});
}
moveAction(fromColIdx, fromActionIdx, toColIdx, toActionIdx) {
@@ -66,7 +66,7 @@ export class BoardController extends Component {
this.saveBoard();
}
selectLayout(layout) {
selectLayout(layout, save = true) {
const currentColNbr = this.board.colNumber;
const nextColNbr = layout.split("-").length;
if (nextColNbr < currentColNbr) {
@@ -80,7 +80,9 @@ export class BoardController extends Component {
}
this.board.layout = layout;
this.board.colNumber = nextColNbr;
this.saveBoard();
if (save) {
this.saveBoard();
}
if (document.querySelector("canvas")) {
// horrible hack to force charts to be recreated so they pick up the
// proper size. also, no idea why raf is needed :(
@@ -100,9 +102,11 @@ export class BoardController extends Component {
});
}
toggleAction(action) {
toggleAction(action, save = true) {
action.isFolded = !action.isFolded;
this.saveBoard();
if (save) {
this.saveBoard();
}
}
saveBoard() {
+3 -3
View File
@@ -34,9 +34,9 @@
<div class="o-dashboard-action mx-3 my-1 bg-view border" t-att-data-idx="action_index">
<h3 t-attf-class="o-dashboard-action-header {{action.title ? '' : 'oe_header_empty'}} p-2 m-2">
<span> <t t-esc="action.title"/> </span>
<span class="btn float-end p-1 text-muted" t-on-click="() => this.closeAction(column, action)"><i class="fa fa-close"/></span>
<span class="btn float-end p-1 text-muted" t-if="!action.isFolded" t-on-click="() => this.toggleAction(action)"><i class="fa fa-window-minimize"/></span>
<span class="btn float-end p-1 text-muted" t-if="action.isFolded" t-on-click="() => this.toggleAction(action)"><i class="fa fa-window-maximize"/></span>
<span t-if="!env.isSmall" class="btn float-end p-1 text-muted" t-on-click="() => this.closeAction(column, action)"><i class="fa fa-close"/></span>
<span class="btn float-end p-1 text-muted" t-if="!action.isFolded" t-on-click="() => this.toggleAction(action, !env.isSmall)"><i class="fa fa-window-minimize"/></span>
<span class="btn float-end p-1 text-muted" t-if="action.isFolded" t-on-click="() => this.toggleAction(action, !env.isSmall)"><i class="fa fa-window-maximize"/></span>
</h3>
<BoardAction t-if="!action.isFolded" action="action" />
</div>
@@ -62,10 +62,7 @@ QUnit.module("Board", (hooks) => {
if (route === "/web/action/load") {
return Promise.resolve({
res_model: "partner",
views: [
[4, "list"],
[5, "form"],
],
views: [[4, "list"]],
});
}
},
@@ -77,5 +74,51 @@ QUnit.module("Board", (hooks) => {
target.querySelector(".o-dashboard-action .o_control_panel"),
"views in the dashboard do not have a control panel"
);
assert.containsNone(
target,
".o-dashboard-action-header .fa-close",
"Should not have a close action button"
);
});
QUnit.test("Correctly soft switch to '1' layout on small screen", async function (assert) {
serverData.views["partner,4,list"] = '<tree string="Partner"><field name="foo"/></tree>';
await makeView({
serverData,
type: "form",
resModel: "board",
arch: `
<form string="My Dashboard" js_class="board">
<board style="2-1">
<column>
<action context="{&quot;orderedBy&quot;: [{&quot;name&quot;: &quot;foo&quot;, &quot;asc&quot;: True}]}" view_mode="list" string="ABC" name="51" domain="[['foo', '!=', 'False']]"></action>
</column>
<column>
<action context="{&quot;orderedBy&quot;: [{&quot;name&quot;: &quot;foo&quot;, &quot;asc&quot;: True}]}" view_mode="list" string="ABC" name="51" domain="[['foo', '!=', 'False']]"></action>
</column>
</board>
</form>`,
mockRPC(route, args) {
if (route === "/web/action/load") {
return Promise.resolve({
res_model: "partner",
views: [[4, "list"]],
});
}
},
});
assert.containsOnce(target, ".o-dashboard-layout-1", "The display layout is force to 1");
assert.containsOnce(
target,
".o-dashboard-column",
"The display layout is force to 1 column"
);
assert.containsN(
target,
".o-dashboard-action",
2,
"The display should contains the 2 actions"
);
});
});