[IMP] web: allow custom row click action

This commit adds a generic `action="action_some_method"` attribute to
the list view to allow for a custom action when clicking on a (record)
row. This feature mirrors the existing Kanban "action" attribute.

Note that in cases where the action method does not return a valid
action then the default action `act_window_close` will be called
instead (same behavior as the kanban view and buttons in general).

Supports "magic link" part of Task: 2882539

Part-of: odoo/odoo#97109
This commit is contained in:
Tiffany Chang (tic)
2022-08-29 23:46:36 +02:00
parent f05e3eabbe
commit a066638cd5
4 changed files with 70 additions and 2 deletions
@@ -163,6 +163,11 @@ export class ListArchParser extends XMLParser {
treeAttr.noOpen = archParseBoolean(node.getAttribute("no_open") || "");
treeAttr.expand = archParseBoolean(xmlDoc.getAttribute("expand") || "");
treeAttr.decorations = getDecoration(xmlDoc);
// custom open action when clicking on record row
const action = xmlDoc.getAttribute("action");
const type = xmlDoc.getAttribute("type");
treeAttr.openAction = action && type ? { action, type } : null;
}
});
@@ -167,8 +167,23 @@ export class ListController extends Component {
}
async openRecord(record) {
const activeIds = this.model.root.records.map((datapoint) => datapoint.resId);
this.props.selectRecord(record.resId, { activeIds });
if (this.archInfo.openAction) {
this.actionService.doActionButton({
name: this.archInfo.openAction.action,
type: this.archInfo.openAction.type,
resModel: record.resModel,
resId: record.resId,
resIds: record.resIds,
context: record.context,
onClose: async () => {
await record.model.root.load();
record.model.notify();
},
});
} else {
const activeIds = this.model.root.records.map((datapoint) => datapoint.resId);
this.props.selectRecord(record.resId, { activeIds });
}
}
onClickCreate() {
@@ -1799,6 +1799,52 @@ QUnit.module("Views", (hooks) => {
assert.verifySteps(["openRecord", "openRecord"]);
});
QUnit.test("action/type attributes on tree arch, type='object'", async (assert) => {
const list = await makeView({
type: "list",
resModel: "foo",
serverData,
arch: '<tree action="a1" type="object"><field name="foo"/></tree>',
mockRPC(route, args) {
assert.step(args.method);
},
});
patchWithCleanup(list.env.services.action, {
doActionButton(params) {
assert.step(`doActionButton type ${params.type} name ${params.name}`);
params.onClose();
},
});
assert.verifySteps(["get_views", "web_search_read"]);
await click(target.querySelector(".o_data_cell"));
assert.verifySteps(["doActionButton type object name a1", "web_search_read"]);
});
QUnit.test("action/type attributes on tree arch, type='action'", async (assert) => {
const list = await makeView({
type: "list",
resModel: "foo",
serverData,
arch: '<tree action="a1" type="action"><field name="foo"/></tree>',
mockRPC(route, args) {
assert.step(args.method);
},
});
patchWithCleanup(list.env.services.action, {
doActionButton(params) {
assert.step(`doActionButton type ${params.type} name ${params.name}`);
params.onClose();
},
});
assert.verifySteps(["get_views", "web_search_read"]);
await click(target.querySelector(".o_data_cell"));
assert.verifySteps(["doActionButton type action name a1", "web_search_read"]);
});
QUnit.test("editable list view: readonly fields cannot be edited", async function (assert) {
serverData.models.foo.fields.foo.readonly = true;
+2
View File
@@ -46,6 +46,8 @@
<rng:optional><rng:attribute name="decoration-warning"/></rng:optional>
<rng:optional><rng:attribute name="banner_route"/></rng:optional>
<rng:optional><rng:attribute name="sample"/></rng:optional>
<rng:optional><rng:attribute name="action"/></rng:optional>
<rng:optional><rng:attribute name="type"/></rng:optional>
<rng:optional>
<rng:attribute name="limit">
<rng:data type="int"/>