diff options
| author | Aaron Bohy <aab@odoo.com> | 2020-04-29 07:21:38 +0000 |
|---|---|---|
| committer | Aaron Bohy <aab@odoo.com> | 2020-05-05 10:05:12 +0000 |
| commit | df4693e7ffe8082335355b9a7c573d1038547f89 (patch) | |
| tree | d611e954c8b1961107f5e644545d6429348c106d | |
| parent | 4f8860e5038711c0da5b26584c4b72663908ddbc (diff) | |
[IMP] web: add Many2OneAvatar field widget
In readonly, this widget displays the image of the related record
next to its display_name. In edit, it behaves exactly like the
regular many2one.
Part of task 2195254
| -rw-r--r-- | addons/web/static/src/js/fields/field_registry.js | 1 | ||||
| -rw-r--r-- | addons/web/static/src/js/fields/relational_fields.js | 41 | ||||
| -rw-r--r-- | addons/web/static/src/scss/fields.scss | 11 | ||||
| -rw-r--r-- | addons/web/static/src/scss/kanban_view.scss | 6 | ||||
| -rw-r--r-- | addons/web/static/src/xml/base.xml | 6 | ||||
| -rw-r--r-- | addons/web/static/tests/fields/relational_fields/field_many2one_tests.js | 102 | ||||
| -rw-r--r-- | doc/reference/javascript_reference.rst | 9 |
7 files changed, 175 insertions, 1 deletions
diff --git a/addons/web/static/src/js/fields/field_registry.js b/addons/web/static/src/js/fields/field_registry.js index 3cadf6cf6fa..925b405051e 100644 --- a/addons/web/static/src/js/fields/field_registry.js +++ b/addons/web/static/src/js/fields/field_registry.js @@ -84,6 +84,7 @@ registry .add('many2one_barcode', relational_fields.Many2oneBarcode) .add('list.many2one', relational_fields.ListFieldMany2One) .add('kanban.many2one', relational_fields.KanbanFieldMany2One) + .add('many2one_avatar', relational_fields.Many2OneAvatar) .add('many2many', relational_fields.FieldMany2Many) .add('many2many_binary', relational_fields.FieldMany2ManyBinaryMultiFiles) .add('many2many_tags', relational_fields.FieldMany2ManyTags) diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js index 72476d74c92..b5b6d7252e5 100644 --- a/addons/web/static/src/js/fields/relational_fields.js +++ b/addons/web/static/src/js/fields/relational_fields.js @@ -959,6 +959,44 @@ var KanbanFieldMany2One = AbstractField.extend({ }, }); +/** + * Widget Many2OneAvatar is only supported on many2one fields pointing to a + * model which inherits from 'image.mixin'. In readonly, it displays the + * record's image next to the display_name. In edit, it behaves exactly like a + * regular many2one widget. + */ +const Many2OneAvatar = FieldMany2One.extend({ + _template: 'web.Many2OneAvatar', + + init() { + this._super.apply(this, arguments); + if (this.mode === 'readonly') { + this.template = null; + this.tagName = 'div'; + this.className = 'o_field_many2one_avatar'; + // disable the redirection to the related record on click, in readonly + this.noOpen = true; + } + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @override + */ + _renderReadonly() { + this.$el.empty(); + if (this.value) { + this.$el.html(qweb.render(this._template, { + url: `/web/image/${this.field.relation}/${this.value.res_id}/image_128`, + value: this.m2o_value, + })); + } + }, +}); + //------------------------------------------------------------------------------ // X2Many widgets //------------------------------------------------------------------------------ @@ -3308,8 +3346,9 @@ return { Many2oneBarcode: Many2oneBarcode, KanbanFieldMany2One: KanbanFieldMany2One, ListFieldMany2One: ListFieldMany2One, + Many2OneAvatar: Many2OneAvatar, - FieldX2Many : FieldX2Many, + FieldX2Many: FieldX2Many, FieldOne2Many: FieldOne2Many, FieldMany2Many: FieldMany2Many, diff --git a/addons/web/static/src/scss/fields.scss b/addons/web/static/src/scss/fields.scss index f0d15adbd52..8152389c32c 100644 --- a/addons/web/static/src/scss/fields.scss +++ b/addons/web/static/src/scss/fields.scss @@ -83,6 +83,17 @@ } } + // Many2OneAvatar + &.o_field_many2one_avatar { + > img.o_m2o_avatar { + border-radius: 50%; + width: 19px; + height: 19px; + object-fit: cover; + margin-right: 4px; + } + } + // Many2many tags &.o_field_many2manytags { flex-flow: row wrap; diff --git a/addons/web/static/src/scss/kanban_view.scss b/addons/web/static/src/scss/kanban_view.scss index 29cfade3857..d24a928536e 100644 --- a/addons/web/static/src/scss/kanban_view.scss +++ b/addons/web/static/src/scss/kanban_view.scss @@ -280,6 +280,12 @@ } } + .o_field_many2one_avatar { + img.o_m2o_avatar { + margin-right: 0; + } + } + // Commonly used to place an image beside the text // (e.g. Fleet, Employees, ...) .o_kanban_image { diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index b06b8402620..3abde74ac51 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1346,6 +1346,12 @@ <button type="button" t-if="!widget.noOpen" class="fa fa-external-link btn btn-secondary o_external_button" tabindex="-1" draggable="false" aria-label="External link" title="External link"/> </div> </t> + +<t t-name="web.Many2OneAvatar"> + <img t-att-src="url" t-att-alt="value" class="o_m2o_avatar"/> + <span t-esc="value"/> +</t> + <t t-name="FieldReference" t-extend="FieldMany2One"> <t t-jquery=".o_input_dropdown" t-operation="before"> <select t-att-class="'o_input o_field_widget' + (widget.nodeOptions.hide_model and ' d-none' or '')"> diff --git a/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js b/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js index 04c934290a1..c2c4942381a 100644 --- a/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js +++ b/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js @@ -3204,6 +3204,108 @@ QUnit.module('fields', {}, function () { "should be 1 column after the value change"); form.destroy(); }); + + QUnit.module('Many2OneAvatar'); + + QUnit.test('many2one_avatar widget in form view', async function (assert) { + assert.expect(10); + + const form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '<form><field name="user_id" widget="many2one_avatar"/></form>', + res_id: 1, + }); + + assert.hasClass(form.$('.o_form_view'), 'o_form_readonly'); + assert.strictEqual(form.$('.o_field_widget[name=user_id]').text().trim(), 'Aline'); + assert.containsOnce(form, 'img.o_m2o_avatar[data-src="/web/image/user/17/image_128"]'); + + await testUtils.form.clickEdit(form); + + assert.hasClass(form.$('.o_form_view'), 'o_form_editable'); + assert.containsOnce(form, '.o_input_dropdown'); + assert.strictEqual(form.$('.o_input_dropdown input').val(), 'Aline'); + assert.containsOnce(form, '.o_external_button'); + + await testUtils.fields.many2one.clickOpenDropdown("user_id"); + await testUtils.fields.many2one.clickItem("user_id", "Christine"); + await testUtils.form.clickSave(form); + + assert.hasClass(form.$('.o_form_view'), 'o_form_readonly'); + assert.strictEqual(form.$('.o_field_widget[name=user_id]').text().trim(), 'Christine'); + assert.containsOnce(form, 'img.o_m2o_avatar[data-src="/web/image/user/19/image_128"]'); + + form.destroy(); + }); + + QUnit.test('many2one_avatar widget in form view, with onchange', async function (assert) { + assert.expect(7); + + this.data.partner.onchanges = { + int_field: function (obj) { + if (obj.int_field === 1) { + obj.user_id = [19, 'Christine']; + } else if (obj.int_field === 2) { + obj.user_id = false; + } else { + obj.user_id = [17, 'Aline']; // default value + } + }, + }; + const form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: ` + <form> + <field name="int_field"/> + <field name="user_id" widget="many2one_avatar" readonly="1"/> + </form>`, + }); + + assert.hasClass(form.$('.o_form_view'), 'o_form_editable'); + assert.strictEqual(form.$('.o_field_widget[name=user_id]').text().trim(), 'Aline'); + assert.containsOnce(form, 'img.o_m2o_avatar[data-src="/web/image/user/17/image_128"]'); + + await testUtils.fields.editInput(form.$('.o_field_widget[name=int_field]'), 1); + + assert.strictEqual(form.$('.o_field_widget[name=user_id]').text().trim(), 'Christine'); + assert.containsOnce(form, 'img.o_m2o_avatar[data-src="/web/image/user/19/image_128"]'); + + await testUtils.fields.editInput(form.$('.o_field_widget[name=int_field]'), 2); + + assert.strictEqual(form.$('.o_field_widget[name=user_id]').text().trim(), ''); + assert.containsNone(form, 'img.o_m2o_avatar'); + + form.destroy(); + }); + + QUnit.test('many2one_avatar widget in list view', async function (assert) { + assert.expect(5); + + this.data.partner.records = [ + { id: 1, user_id: 17, }, + { id: 2, user_id: 19, }, + { id: 3, user_id: 17, }, + { id: 3, user_id: false, }, + ]; + const list = await createView({ + View: ListView, + model: 'partner', + data: this.data, + arch: '<tree><field name="user_id" widget="many2one_avatar"/></tree>', + }); + + assert.strictEqual(list.$('.o_data_cell span').text(), 'AlineChristineAline'); + assert.containsOnce(list.$('.o_data_cell:nth(0)'), 'img.o_m2o_avatar[data-src="/web/image/user/17/image_128"]'); + assert.containsOnce(list.$('.o_data_cell:nth(1)'), 'img.o_m2o_avatar[data-src="/web/image/user/19/image_128"]'); + assert.containsOnce(list.$('.o_data_cell:nth(2)'), 'img.o_m2o_avatar[data-src="/web/image/user/17/image_128"]'); + assert.containsNone(list.$('.o_data_cell:nth(3)'), 'img.o_m2o_avatar'); + + list.destroy(); + }); }); }); }); diff --git a/doc/reference/javascript_reference.rst b/doc/reference/javascript_reference.rst index 92c6a292ff9..433409acd5e 100644 --- a/doc/reference/javascript_reference.rst +++ b/doc/reference/javascript_reference.rst @@ -2139,6 +2139,15 @@ Relational fields - Supported field types: *many2one* +- many2one_avatar (Many2OneAvatar) + This widget is only supported on many2one fields pointing to a model which + inherits from 'image.mixin'. In readonly, it displays the image of the + related record next to its display_name. Note that the display_name isn't a + clickable link in this case. In edit, it behaves exactly like the regular + many2one. + + - Supported field types: *many2one* + - kanban.many2one (KanbanFieldMany2One) Default widget for many2one fields (in kanban view). We need to disable all editing in kanban views. |
