[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:
committed by
Thibault Delavallée
co-authored by
Mohammed Shekha
parent
cda8bdc8b3
commit
c75c2fa9e3
@@ -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,
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user