[FIX] web: support <widget> in list new view

When converting the list view to owl, the support for view widgets was
overlooked as this feature is not used very extensively and was not
tested.

This commit adds support for them and adds a test for it.

closes odoo/odoo#102193

X-original-commit: 9098c1d3cd4af8f324b5b09668e92822575f932d
Signed-off-by: Géry Debongnie <ged@odoo.com>
Signed-off-by: Samuel Degueldre <sad@odoo.com>
This commit is contained in:
Samuel Degueldre
2022-10-06 10:42:40 +02:00
parent 547c3da36e
commit 9c59d9a4a0
6 changed files with 63 additions and 3 deletions
@@ -10,6 +10,8 @@ import {
} from "@web/views/utils";
import { Field } from "@web/views/fields/field";
import { XMLParser } from "@web/core/utils/xml";
import { Widget } from "@web/views/widgets/widget";
import { encodeObjectForTemplate } from "@web/views/view_compiler";
export class GroupListArchParser extends XMLParser {
parse(arch, models, modelName, jsClass) {
@@ -104,6 +106,27 @@ export class ListArchParser extends XMLParser {
});
}
return false;
} else if (node.tagName === "widget") {
const widgetInfo = Widget.parseWidgetNode(node);
addFieldDependencies(
activeFields,
models[modelName],
widgetInfo.WidgetComponent.fieldDependencies
);
const widgetProps = {
...widgetInfo,
// FIXME: this is dumb, we encode it into a weird object so that the widget
// can decode it later...
node: encodeObjectForTemplate({ attrs: widgetInfo.rawAttrs }).slice(1, -1),
className: node.getAttribute("class"),
};
columns.push({
...widgetInfo,
props: widgetProps,
id: `column_${nextId++}`,
type: "widget",
});
} else if (node.tagName === "groupby" && node.getAttribute("name")) {
const fieldName = node.getAttribute("name");
const xmlSerializer = new XMLSerializer();
@@ -17,6 +17,7 @@ import { getTooltipInfo } from "@web/views/fields/field_tooltip";
import { getClassNameFromDecoration } from "@web/views/utils";
import { ViewButton } from "@web/views/view_button/view_button";
import { useBounceButton } from "@web/views/view_hook";
import { Widget } from "@web/views/widgets/widget";
const {
Component,
@@ -1747,7 +1748,7 @@ ListRenderer.rowsTemplate = "web.ListRenderer.Rows";
ListRenderer.recordRowTemplate = "web.ListRenderer.RecordRow";
ListRenderer.groupRowTemplate = "web.ListRenderer.GroupRow";
ListRenderer.components = { DropdownItem, Field, ViewButton, CheckBox, Dropdown, Pager };
ListRenderer.components = { DropdownItem, Field, ViewButton, CheckBox, Dropdown, Pager, Widget };
ListRenderer.props = [
"activeActions?",
"list",
@@ -247,6 +247,9 @@
</t>
</td>
</t>
<t t-if="column.type === 'widget'">
<Widget t-props="column.props" record="record"/>
</t>
</t>
<t t-set="activeActions" t-value="props.activeActions" />
+11 -1
View File
@@ -60,5 +60,15 @@ Widget.template = xml/*xml*/ `
Widget.parseWidgetNode = function (node) {
const name = node.getAttribute("name");
const WidgetComponent = findWidgetComponent(name);
return { WidgetComponent };
const attrs = Object.fromEntries(
[...node.attributes].map(({ name, value }) => {
return [name, name === "modifiers" ? JSON.parse(value || "{}") : value];
})
);
return {
options: evaluateExpr(node.getAttribute("options") || "{}"),
name,
rawAttrs: attrs,
WidgetComponent,
};
};
@@ -251,7 +251,7 @@ export class MockServer {
doc = arch;
}
const editableView = editable && this._editableNode(doc, modelName);
const inTreeView = doc.tagName === "tree";
const inTreeView = ["tree", "list"].includes(doc.tagName);
const inFormView = doc.tagName === "form";
// mock _postprocess_access_rights
const isBaseModel = !context.base_model_name || modelName === context.base_model_name;
@@ -15514,4 +15514,27 @@ QUnit.module("Views", (hooks) => {
assert.containsOnce(target, "div[name=text] textarea:focus");
assert.containsOnce(target, ".o_selected_row div[name=text]");
});
QUnit.test("view widgets are rendered in list view", async function (assert) {
class TestWidget extends Component {}
TestWidget.template = xml`<div class="test_widget" t-esc="props.record.data.bar"/>`;
registry.category("view_widgets").add("test_widget", TestWidget);
await makeView({
type: "list",
resModel: "foo",
serverData,
arch: `
<list>
<field name="bar" invisible="1"/>
<widget name="test_widget"/>
</list>
`,
});
assert.containsN(target, ".test_widget", 4, "there should be one widget per record");
assert.deepEqual(
[...target.querySelectorAll(".test_widget")].map((w) => w.textContent),
["true", "true", "true", "false"],
"the widget has access to the record's data"
);
});
});