[FIX] web: kanban: attributes of card dropdowns

Some attributes of the elements determining the "ellipsis" dropdown menu
of a kanban card were invalidly reported on the main Dropdown node
created during the compilation. This could cause crashes (during the
props validation) or broke the CSS.

In this commit:
    - we fix the above mentioned problem
    - we put the class o_drowdown_kanban on all "ellipsis" dropdowns
      generated by the kanban compiler in order to get the appropriate CSS
    - we make sure that those dropdowns are visible in kanban dashboards
      by default

Part-of: odoo/odoo#92475
This commit is contained in:
Mathieu Duckerts-Antoine
2022-06-30 09:29:49 +02:00
committed by Aaron Bohy
parent 5d0a28d0f5
commit a5bfa26692
4 changed files with 112 additions and 8 deletions
@@ -30,12 +30,21 @@ export class KanbanCompiler extends ViewCompiler {
this.compilers.push(
{ selector: `[t-name='${KANBAN_BOX_ATTRIBUTE}']`, fn: this.compileCard },
{ selector: ".oe_kanban_colorpicker", fn: this.compileColorPicker },
{ selector: ".dropdown,.o_kanban_manage_button_section", fn: this.compileDropdown },
{
selector: ".dropdown,.o_kanban_manage_button_section",
fn: this.compileDropdown,
doNotCopyAttributes: true,
},
{
selector: ".dropdown-toggle,.o_kanban_manage_toggle_button",
fn: this.compileDropdownButton,
doNotCopyAttributes: true,
},
{
selector: ".dropdown-menu",
fn: this.compileDropdownMenu,
doNotCopyAttributes: true,
},
{ selector: ".dropdown-menu", fn: this.compileDropdownMenu },
{ selector: "t[t-call]", fn: this.compileTCall }
);
this.dropdown = null;
@@ -55,6 +64,7 @@ export class KanbanCompiler extends ViewCompiler {
this.dropdown = createElement("Dropdown", {
position: toStringExpression("bottom-end"),
});
this.dropdown.setAttribute("class", "'o_dropdown_kanban'");
return this.dropdown;
}
@@ -23,6 +23,10 @@
// ------- Dropdown toggle & menu -------
$o-kanban-manage-toggle-height: 35px;
.o_dropdown_kanban {
visibility: visible;
}
.o_kanban_manage_toggle_button {
@include o-kanban-dropdown($o-kanban-dashboard-hpadding);
@include o-position-absolute(0, 0);
+3 -6
View File
@@ -97,10 +97,6 @@ export function assignOwlDirectives(target, ...sources) {
* @param {Element} compiled
*/
export function copyAttributes(el, compiled) {
if (getTag(el, true) === "button") {
return;
}
const isComponent = isComponentNode(compiled);
const classes = el.className;
if (classes) {
@@ -215,7 +211,8 @@ export class ViewCompiler {
this.id = 1;
/** @type {Compiler[]} */
this.compilers = [
{ selector: "a[type],a[data-type],button", fn: this.compileButton },
{ selector: "a[type],a[data-type]", fn: this.compileButton },
{ selector: "button", fn: this.compileButton, doNotCopyAttributes: true },
{ selector: "field", fn: this.compileField },
{ selector: "widget", fn: this.compileWidget },
];
@@ -287,7 +284,7 @@ export class ViewCompiler {
let compiledNode;
if (compiler) {
compiledNode = compiler.fn.call(this, node, params);
if (compiledNode) {
if (!compiler.doNotCopyAttributes && compiledNode) {
copyAttributes(node, compiledNode);
}
} else {
@@ -9874,4 +9874,97 @@ QUnit.module("Views", (hooks) => {
assert.containsNone(target, ".o_kanban_quick_create");
assert.containsN(target, ".o_kanban_group:nth-child(2) .o_kanban_record", 3);
});
QUnit.test("classes on dropdown menu do not end on dropdown main div", async (assert) => {
serverData.models.partner.records.splice(1, 3); // keep one record only
await makeView({
type: "kanban",
resModel: "partner",
serverData,
arch: `
<kanban>
<templates>
<t t-name="kanban-box">
<div>
<field name="foo"/>
<ul class="o_kanban_card_manage_pane dropdown-menu" role="menu">
<li>Hello</li>
</ul>
</div>
</t>
</templates>
</kanban>
`,
});
const dropdown = target.querySelector(".o_kanban_record .o-dropdown");
assert.isVisible(dropdown);
assert.hasClass(dropdown, "o_dropdown_kanban");
assert.doesNotHaveClass(dropdown, "dropdown-menu");
assert.doesNotHaveClass(dropdown, "o_kanban_card_manage_pane");
await click(dropdown, "button.dropdown-toggle");
assert.containsOnce(dropdown, ".o_kanban_card_manage_pane.dropdown-menu");
});
QUnit.test("classes on toggler do not end on dropdown main div", async (assert) => {
serverData.models.partner.records.splice(1, 3); // keep one record only
await makeView({
type: "kanban",
resModel: "partner",
serverData,
arch: `
<kanban>
<templates>
<t t-name="kanban-box">
<div>
<field name="foo"/>
<a class="o_kanban_manage_toggle_button o_left" href="#">
<i class="fa fa-ellipsis-v" role="img" aria-label="Manage" title="Manage"/>
</a>
</div>
</t>
</templates>
</kanban>
`,
});
const dropdown = target.querySelector(".o_kanban_record .o-dropdown");
assert.isVisible(dropdown);
assert.hasClass(dropdown, "o_dropdown_kanban");
assert.doesNotHaveClass(dropdown, "o_kanban_manage_toggle_button");
assert.doesNotHaveClass(dropdown, "o_left");
assert.containsOnce(dropdown, ".o_kanban_manage_toggle_button.o_left");
});
QUnit.test(
"classes on dropdown are on dropdown main div but not the other attributes",
async (assert) => {
serverData.models.partner.records.splice(1, 3); // keep one record only
await makeView({
type: "kanban",
resModel: "partner",
serverData,
arch: `
<kanban>
<templates>
<t t-name="kanban-box">
<div>
<field name="foo"/>
<div class="o_kanban_manage_button_section my_class" placeholder="Bouh"/>
</div>
</t>
</templates>
</kanban>
`,
});
const dropdown = target.querySelector(".o_kanban_record .o-dropdown");
assert.isVisible(dropdown);
assert.strictEqual(
dropdown.className,
"o-dropdown dropdown o_dropdown_kanban o_kanban_manage_button_section my_class o-dropdown--no-caret"
);
assert.notOk(dropdown.hasAttribute("placeholder"));
}
);
});