From 9c59d9a4a0249292e12ed9aa76df369481cae1ac Mon Sep 17 00:00:00 2001 From: Samuel Degueldre Date: Tue, 4 Oct 2022 10:26:42 +0000 Subject: [PATCH] [FIX] web: support in list new view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Samuel Degueldre --- .../static/src/views/list/list_arch_parser.js | 23 +++++++++++++++++++ .../static/src/views/list/list_renderer.js | 3 ++- .../static/src/views/list/list_renderer.xml | 3 +++ addons/web/static/src/views/widgets/widget.js | 12 +++++++++- .../web/static/tests/helpers/mock_server.js | 2 +- .../web/static/tests/views/list_view_tests.js | 23 +++++++++++++++++++ 6 files changed, 63 insertions(+), 3 deletions(-) diff --git a/addons/web/static/src/views/list/list_arch_parser.js b/addons/web/static/src/views/list/list_arch_parser.js index f945dcdb423..38f26d9a331 100644 --- a/addons/web/static/src/views/list/list_arch_parser.js +++ b/addons/web/static/src/views/list/list_arch_parser.js @@ -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(); diff --git a/addons/web/static/src/views/list/list_renderer.js b/addons/web/static/src/views/list/list_renderer.js index abd7ba100b2..aa50bd27eab 100644 --- a/addons/web/static/src/views/list/list_renderer.js +++ b/addons/web/static/src/views/list/list_renderer.js @@ -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", diff --git a/addons/web/static/src/views/list/list_renderer.xml b/addons/web/static/src/views/list/list_renderer.xml index 69f3ac37425..f4689a43034 100644 --- a/addons/web/static/src/views/list/list_renderer.xml +++ b/addons/web/static/src/views/list/list_renderer.xml @@ -247,6 +247,9 @@ + + + diff --git a/addons/web/static/src/views/widgets/widget.js b/addons/web/static/src/views/widgets/widget.js index 5f853fce6d6..1efd5e23af7 100644 --- a/addons/web/static/src/views/widgets/widget.js +++ b/addons/web/static/src/views/widgets/widget.js @@ -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, + }; }; diff --git a/addons/web/static/tests/helpers/mock_server.js b/addons/web/static/tests/helpers/mock_server.js index 25d20794f33..30c6a0a5c73 100644 --- a/addons/web/static/tests/helpers/mock_server.js +++ b/addons/web/static/tests/helpers/mock_server.js @@ -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; diff --git a/addons/web/static/tests/views/list_view_tests.js b/addons/web/static/tests/views/list_view_tests.js index 88b274f571f..e41f9c8f306 100644 --- a/addons/web/static/tests/views/list_view_tests.js +++ b/addons/web/static/tests/views/list_view_tests.js @@ -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`
`; + registry.category("view_widgets").add("test_widget", TestWidget); + await makeView({ + type: "list", + resModel: "foo", + serverData, + arch: ` + + + + + `, + }); + 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" + ); + }); });