[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:
@@ -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" />
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user