[IMP] web: add a badge based widget for selection fields

This commit introduces a new widget to be used for selection-based fields.
Valid choices are displayed using rectangular badges and not like the
standard list. This widget is valid for selection and many2one fields
like selection or radio widgets.

Co-authored-by: Siddarth Gajjar <sga@odoo.com>
Co-authored-by: Mohammed Shekha <msh@openerp.com>
This commit is contained in:
Siddarth Gajjar
2018-05-30 15:39:15 +02:00
committed by Thibault Delavallée
co-authored by Mohammed Shekha
parent cda8bdc8b3
commit c75c2fa9e3
6 changed files with 179 additions and 0 deletions
@@ -60,6 +60,7 @@ registry
registry
.add('selection', relational_fields.FieldSelection)
.add('radio', relational_fields.FieldRadio)
.add('selection_badge', relational_fields.FieldSelectionBadge)
.add('many2one', relational_fields.FieldMany2One)
.add('list.many2one', relational_fields.ListFieldMany2One)
.add('kanban.many2one', relational_fields.KanbanFieldMany2One)
@@ -2301,6 +2301,75 @@ var FieldRadio = FieldSelection.extend({
},
});
var FieldSelectionBadge = FieldSelection.extend({
template: null,
className: 'o_field_selection_badge',
tagName: 'span',
specialData: "_fetchSpecialMany2ones",
events: _.extend({}, AbstractField.prototype.events, {
'click span.o_selection_badge': '_onBadgeClicked',
}),
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* @private
* @override
*/
_renderEdit: function () {
this.currentValue = this.value;
if (this.field.type === 'many2one') {
this.currentValue = this.value && this.value.data.id;
}
this.$el.empty();
this.$el.html(qweb.render('FieldSelectionBadge', {'values': this.values, 'current_value': this.currentValue}));
},
/**
* Sets the possible field values. If the field is a many2one, those values
* may change during the life cycle of the widget if the domain change (an
* onchange may change the domain).
*
* @private
* @override
*/
_setValues: function () {
// Note: We can make abstract widget for common code in radio and selection badge
if (this.field.type === 'selection') {
this.values = this.field.selection || [];
} else if (this.field.type === 'many2one') {
this.values = _.map(this.record.specialData[this.name], function (val) {
return [val.id, val.display_name];
});
}
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* @private
* @param {MouseEvent} event
*/
_onBadgeClicked: function (event) {
var index = $(event.target).data('index');
var value = this.values[index];
if (value[0] !== this.currentValue) {
if (this.field.type === 'many2one') {
this._setValue({id: value[0], display_name: value[1]});
} else {
this._setValue(value[0]);
}
} else {
this._setValue(false);
}
},
});
/**
* The FieldReference is a combination of a select (for the model) and
* a FieldMany2one for its value.
@@ -2461,6 +2530,7 @@ return {
KanbanFieldMany2ManyTags: KanbanFieldMany2ManyTags,
FieldRadio: FieldRadio,
FieldSelectionBadge: FieldSelectionBadge,
FieldSelection: FieldSelection,
FieldStatus: FieldStatus,
+18
View File
@@ -229,6 +229,24 @@
cursor: ns-resize;
}
&.o_field_selection_badge {
.o_selection_badge {
display: inline-block;
margin: 0px 0px 4px 4px;
padding: 1px 6px;
color: $o-main-color-muted;
border: 1px solid $o-color-silver-dark;
cursor: pointer;
&.active {
color: $o-brand-optional;
border-color: $o-brand-optional;
}
&:hover:not(.active) {
color: darken($o-color-silver-dark, 30%);
border-color: darken($o-color-silver-dark, 30%);
}
}
}
// Radio buttons
&.o_field_radio {
.o_radio_input {
+7
View File
@@ -792,6 +792,13 @@
<label class="o_form_label" t-att-for="id"><t t-esc="value[1]"/></label>
</div>
</t>
<t t-name="FieldSelectionBadge">
<span t-foreach="values" t-as="value" t-attf-class="o_selection_badge #{value[0] == current_value ? 'active' : ''}" t-att-data-index="value_index">
<t t-esc="value[1]"/>
</span>
</t>
<t t-name="FieldMany2One">
<t t-if="widget.mode === 'readonly'">
<a t-if="!widget.nodeOptions.no_open" class="o_form_uri" href="#"/>
@@ -11382,6 +11382,79 @@ QUnit.module('relational_fields', {
form.destroy();
});
QUnit.module('FieldSelectionBadge');
QUnit.test('FieldSelectionBadge widget on a many2one in a new record', function (assert) {
assert.expect(6);
var form = createView({
View: FormView,
model: 'partner',
data: this.data,
arch: '<form>' +
'<field name="product_id" widget="selection_badge"/>' +
'</form>',
});
assert.ok(form.$('span.o_selection_badge').length, "should have rendered outer div");
assert.strictEqual(form.$('span.o_selection_badge').length, 2, "should have 2 possible choices");
assert.ok(form.$('span.o_selection_badge:contains(xphone)').length, "one of them should be xphone");
assert.strictEqual(form.$('span.active').length, 0, "none of the input should be checked");
$("span.o_selection_badge:first").click();
assert.strictEqual(form.$('span.active').length, 1, "one of the input should be checked");
form.$buttons.find('.o_form_button_save').click();
var newRecord = _.last(this.data.partner.records);
assert.strictEqual(newRecord.product_id, 37, "should have saved record with correct value");
form.destroy();
});
QUnit.test('FieldSelectionBadge widget on a selection in a new record', function (assert) {
assert.expect(4);
var form = createView({
View: FormView,
model: 'partner',
data: this.data,
arch: '<form>' +
'<field name="color" widget="selection_badge"/>' +
'</form>',
});
assert.ok(form.$('span.o_selection_badge').length, "should have rendered outer div");
assert.strictEqual(form.$('span.o_selection_badge').length, 2, "should have 2 possible choices");
assert.ok(form.$('span.o_selection_badge:contains(Red)').length, "one of them should be Red");
// click on 2nd option
form.$("span.o_selection_badge").eq(1).click();
form.$buttons.find('.o_form_button_save').click();
var newRecord = _.last(this.data.partner.records);
assert.strictEqual(newRecord.color, 'black', "should have saved record with correct value");
form.destroy();
});
QUnit.test('FieldSelectionBadge widget on a selection in a readonly mode', function (assert) {
assert.expect(1);
var form = createView({
View: FormView,
model: 'partner',
data: this.data,
arch: '<form>' +
'<field name="color" widget="selection_badge" readonly="1"/>' +
'</form>',
});
assert.strictEqual(form.$('span.o_readonly_modifier').length, 1, "should have 1 possible value in readonly mode");
form.destroy();
});
QUnit.module('FieldMany2ManyCheckBoxes');
QUnit.test('widget many2many_checkboxes', function (assert) {
+10
View File
@@ -1756,6 +1756,16 @@ Relational fields
<field name="recommended_activity_type_id" widget="radio"
options="{'horizontal':true}"/>
- selection_badge (FieldSelectionBadge)
This is a subfield of FieldSelection, but specialized to display all the
valid choices as rectangular badges.
- Supported field types: *selection, many2one*
.. code-block:: xml
<field name="recommended_activity_type_id" widget="selection_badge"/>
- many2one (FieldMany2One)
Default widget for many2one fields.