summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorAaron Bohy <aab@odoo.com>2020-04-29 07:21:31 +0000
committerAaron Bohy <aab@odoo.com>2020-05-05 08:48:34 +0000
commita1d3e224d874dcee966b2df97f24605e816d40d4 (patch)
tree3eb4c49238d3fe48c6b1c40ffcab2ae429cc9f95 /addons
parent47144b9ee4afd9f1dec9e6e66f7b38f75bdf31e9 (diff)
[IMP] web: list: allow to add decorations on field nodes
In list views, one can specify decoration-XXX attributes on the arch root node. Those decorations are evaluated for each record, and the corresponding style is applied on rows for which the condition is true. This commit allows to specify those decoration-XXX attributes on field nodes as well. In this case, when the condition is met by a record, only the field on which the attribute is set will be impacted. Part of task 2195254
Diffstat (limited to 'addons')
-rw-r--r--addons/web/static/src/js/views/list/list_editable_renderer.js2
-rw-r--r--addons/web/static/src/js/views/list/list_renderer.js62
-rw-r--r--addons/web/static/tests/views/list_tests.js21
3 files changed, 64 insertions, 21 deletions
diff --git a/addons/web/static/src/js/views/list/list_editable_renderer.js b/addons/web/static/src/js/views/list/list_editable_renderer.js
index 697430e24b7..38a96f04bca 100644
--- a/addons/web/static/src/js/views/list/list_editable_renderer.js
+++ b/addons/web/static/src/js/views/list/list_editable_renderer.js
@@ -186,7 +186,7 @@ ListRenderer.include({
if (widgets.length) {
var $row = self._getRow(recordID);
var record = self._getRecord(recordID);
- self._setDecorationClasses(record, $row);
+ self._setDecorationClasses($row, self.rowDecorations, record);
self._updateFooter();
}
return widgets;
diff --git a/addons/web/static/src/js/views/list/list_renderer.js b/addons/web/static/src/js/views/list/list_renderer.js
index 13deb446c1c..93576be9348 100644
--- a/addons/web/static/src/js/views/list/list_renderer.js
+++ b/addons/web/static/src/js/views/list/list_renderer.js
@@ -58,12 +58,12 @@ var ListRenderer = BasicRenderer.extend({
init: function (parent, state, params) {
this._super.apply(this, arguments);
this.columnInvisibleFields = params.columnInvisibleFields;
- this.rowDecorations = _.chain(this.arch.attrs)
- .pick(function (value, key) {
- return DECORATIONS.indexOf(key) >= 0;
- }).mapObject(function (value) {
- return py.parse(py.tokenize(value));
- }).value();
+ this.rowDecorations = this._extractDecorationAttrs(this.arch);
+ this.fieldDecorations = {};
+ for (const field of this.arch.children.filter(c => c.tag === 'field')) {
+ const decorations = this._extractDecorationAttrs(field);
+ this.fieldDecorations[field.attrs.name] = decorations;
+ }
this.hasSelectors = params.hasSelectors;
this.selection = params.selectedRecords || [];
this.pagers = []; // instantiated pagers (only for grouped lists)
@@ -187,6 +187,24 @@ var ListRenderer = BasicRenderer.extend({
}
},
/**
+ * Extract the decoration attributes (e.g. decoration-danger) of a node. The
+ * condition is processed such that it is ready to be evaluated.
+ *
+ * @private
+ * @param {Object} node the <tree> or a <field> node
+ * @returns {Object}
+ */
+ _extractDecorationAttrs: function (node) {
+ const decorations = {};
+ for (const [key, expr] of Object.entries(node.attrs)) {
+ if (DECORATIONS.includes(key)) {
+ const cssClass = key.replace('decoration', 'text');
+ decorations[cssClass] = py.parse(py.tokenize(expr));
+ }
+ }
+ return decorations;
+ },
+ /**
*
* @private
* @param {jQuery} $cell
@@ -440,6 +458,8 @@ var ListRenderer = BasicRenderer.extend({
return $td.append($el);
}
this._handleAttributes($td, node);
+ this._setDecorationClasses($td, this.fieldDecorations[node.attrs.name], record);
+
var name = node.attrs.name;
var field = this.state.fields[name];
var value = record.data[name];
@@ -474,7 +494,7 @@ var ListRenderer = BasicRenderer.extend({
if (node.attrs.icon) {
// if there is an icon, we force the btn-link style, unless a btn-xxx
// style class is explicitely provided
- const btnStyleRegex = /\bbtn-(primary|secondary|link|success|info|warning|danger)\b/;
+ const btnStyleRegex = /\bbtn-[a-z]+\b/;
if (!btnStyleRegex.test(nodeWithoutWidth.attrs.class)) {
extraClass = 'btn-link o_icon_button';
}
@@ -843,7 +863,7 @@ var ListRenderer = BasicRenderer.extend({
if (this.hasSelectors) {
$tr.prepend(this._renderSelector('td', !record.res_id));
}
- this._setDecorationClasses(record, $tr);
+ this._setDecorationClasses($tr, this.rowDecorations, record);
return $tr;
},
/**
@@ -1006,22 +1026,24 @@ var ListRenderer = BasicRenderer.extend({
return Promise.all([this._super.apply(this, arguments), prom]);
},
/**
- * Each line can be decorated according to a few simple rules. The arch
- * description of the list may have one of the decoration-X attribute with
- * a domain as value. Then, for each record, we check if the domain matches
- * the record, and add the text-X css class to the element. This method is
- * concerned with the computation of the list of css classes for a given
- * record.
+ * Each line or cell can be decorated according to a few simple rules. The
+ * arch description of the list or the field nodes may have one of the
+ * decoration-X attributes with a python expression as value. Then, for each
+ * record, we evaluate the python expression, and conditionnaly add the
+ * text-X css class to the element. This method is concerned with the
+ * computation of the list of css classes for a given record.
*
* @private
+ * @param {jQueryElement} $el the element to which to add the classes (a tr
+ * or td)
+ * @param {Object} decorations keys are the decoration classes (e.g.
+ * 'text-bf') and values are the python expressions to evaluate
* @param {Object} record a basic model record
- * @param {jQueryElement} $tr a jquery <tr> element (the row to add decoration)
*/
- _setDecorationClasses: function (record, $tr) {
- _.each(this.rowDecorations, function (expr, decoration) {
- var cssClass = decoration.replace('decoration', 'text');
- $tr.toggleClass(cssClass, py.PY_isTrue(py.evaluate(expr, record.evalContext)));
- });
+ _setDecorationClasses: function ($el, decorations, record) {
+ for (const [cssClass, expr] of Object.entries(decorations)) {
+ $el.toggleClass(cssClass, py.PY_isTrue(py.evaluate(expr, record.evalContext)));
+ }
},
/**
* @private
diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js
index 913d75ed8bf..03e62488ba4 100644
--- a/addons/web/static/tests/views/list_tests.js
+++ b/addons/web/static/tests/views/list_tests.js
@@ -3356,6 +3356,27 @@ QUnit.module('Views', {
list.destroy();
});
+ QUnit.test('support field decoration', async function (assert) {
+ assert.expect(3);
+
+ var list = await createView({
+ View: ListView,
+ model: 'foo',
+ data: this.data,
+ arch: `
+ <tree>
+ <field name="foo" decoration-danger="int_field > 5"/>
+ <field name="int_field"/>
+ </tree>`,
+ });
+
+ assert.containsN(list, 'tbody tr', 4, "should have 4 rows");
+ assert.containsN(list, 'tbody td.o_list_char.text-danger', 3);
+ assert.containsNone(list, 'tbody td.o_list_number.text-danger');
+
+ list.destroy();
+ });
+
QUnit.test('no content helper when no data', async function (assert) {
assert.expect(5);