diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index 81ca4323871..e62eaf7b8e3 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -1167,9 +1167,21 @@ var StateSelectionWidget = AbstractField.extend({ 'click a': function (e) { e.preventDefault(); }, - 'click li': 'set_selection' + 'click li': '_setSelection' }, - prepare_dropdown_values: function () { + readonly: true, + supportedFieldTypes: ['selection'], + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * Prepares the state values to be rendered using the FormSelection.Items template. + * + * @private + */ + _prepareDropdownValues: function () { var self = this; var _data = []; var current_stage_id = self.recordData.stage_id && self.recordData.stage_id[0]; @@ -1197,26 +1209,44 @@ var StateSelectionWidget = AbstractField.extend({ }); return _data; }, + + /** + * This widget uses the FormSelection template but needs to customize it a bit. + * + * @private + * @override + */ _render: function () { var self = this; - var states = this.prepare_dropdown_values(); + var states = this._prepareDropdownValues(); // Adapt "FormSelection" - var current_state = _.find(states, function (state) { - return state.name === self.value; - }); + // Like priority, default on the first possible value if no value is given. + var currentState = _.findWhere(states, {name: self.value}) || states[0]; this.$('.o_status') .removeClass('o_status_red o_status_green') - .addClass(current_state.state_class); + .addClass(currentState.state_class) + .prop('special_click', true); // Render "FormSelection.Items" and move it into "FormSelection" var $items = $(qweb.render('FormSelection.items', { - states: _.without(states, current_state) + states: _.without(states, currentState) })); var $dropdown = this.$('.dropdown-menu'); $dropdown.children().remove(); // remove old items $items.appendTo($dropdown); }, - set_selection: function (ev) { + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * Intercepts the click on the FormSelection.Item to set the widget value. + * + * @private + * @param {MouseEvent} ev + */ + _setSelection: function (ev) { var li = $(ev.target).closest('li'); if (li.length) { var value = String(li.data('value')); 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 0c6a9a4c9ce..d8ab57e7a69 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 @@ -492,10 +492,13 @@ ListRenderer.include({ /** * When the user clicks on a cell, we simply select it. * + * @private * @param {MouseEvent} event */ _onCellClick: function (event) { - if (this.mode === 'readonly' || !this.editable) { + // The special_click property explicitely allow events to bubble all + // the way up to bootstrap's level rather than being stopped earlier. + if (this.mode === 'readonly' || !this.editable || $(event.target).prop('special_click')) { return; } var $td = $(event.currentTarget); @@ -503,7 +506,18 @@ ListRenderer.include({ var colIndex = $td.data('index'); if (colIndex !== undefined && rowIndex !== undefined) { this._selectCell(rowIndex, colIndex); - event.stopPropagation(); + } + }, + /** + * If the list view editable, just let the event bubble. We don't want to + * open the record in this case anyway. + * + * @override + * @private + */ + _onRowClicked: function () { + if (this.mode === 'readonly' || !this.editable) { + this._super.apply(this, arguments); } }, /** 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 fe80c9b17f9..3495e78d2f2 100644 --- a/addons/web/static/src/js/views/list/list_renderer.js +++ b/addons/web/static/src/js/views/list/list_renderer.js @@ -657,9 +657,13 @@ var ListRenderer = BasicRenderer.extend({ * @param {MouseEvent} event */ _onRowClicked: function (event) { - var id = $(event.currentTarget).data('id'); - if (id) { - this.trigger_up('open_record', {id:id, target: event.target}); + // The special_click property explicitely allow events to bubble all + // the way up to bootstrap's level rather than being stopped earlier. + if (!$(event.target).prop('special_click')) { + var id = $(event.currentTarget).data('id'); + if (id) { + this.trigger_up('open_record', {id:id, target: event.target}); + } } }, /** diff --git a/addons/web/static/test/fields/basic_fields_tests.js b/addons/web/static/test/fields/basic_fields_tests.js index 62d1fd6efe5..d489db97572 100644 --- a/addons/web/static/test/fields/basic_fields_tests.js +++ b/addons/web/static/test/fields/basic_fields_tests.js @@ -57,6 +57,7 @@ QUnit.module('basic_fields', { trululu: 1, sequence: 4, currency_id: 2, + selection: 'normal', }, { id: 4, display_name: "aaa", @@ -64,6 +65,7 @@ QUnit.module('basic_fields', { sequence: 9, int_field: false, qux: false, + selection: 'done', }, {id: 3, bar: true, foo: "gnap", int_field: 17, qux: -3.89859, m2o: 1, m2m: []}, {id: 5, bar: false, foo: "blop", int_field: -4, qux: 9.1, m2o: 1, m2m: [1], currency_id: 1}], @@ -1882,6 +1884,203 @@ QUnit.module('basic_fields', { }); + QUnit.module('StateSelection Widget'); + + QUnit.test('state_selection widget in form view', function (assert) { + assert.expect(21); + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '
', + res_id: 1, + }); + + assert.strictEqual(form.$('.o_form_field.o_selection > a span.o_status.o_status_red').length, 1, + "should have one red status since selection is the second, blocked state"); + assert.strictEqual(form.$('.o_form_field.o_selection > a span.o_status.o_status_green').length, 0, + "should not have one green status since selection is the second, blocked state"); + assert.strictEqual(form.$('ul.dropdown-menu.state:visible').length, 0, + "there should not be a dropdown"); + + // Click on the status button to make the dropdown appear + form.$('.o_form_field.o_selection .o_status').first().click(); + assert.strictEqual(form.$('ul.dropdown-menu.state:visible').length, 1, + "there should be a dropdown"); + assert.strictEqual(form.$('ul.dropdown-menu.state:visible li').length, 2, + "there should be two options in the dropdown"); + + // Click on the first option, "Normal" + form.$('ul.dropdown-menu.state:visible li').first().click(); + assert.strictEqual(form.$('ul.dropdown-menu.state:visible').length, 0, + "there should not be a dropdown anymore"); + assert.strictEqual(form.$('.o_form_field.o_selection > a span.o_status.o_status_red').length, 0, + "should not have one red status since selection is the first, normal state"); + assert.strictEqual(form.$('.o_form_field.o_selection > a span.o_status.o_status_green').length, 0, + "should not have one green status since selection is the first, normal state"); + assert.strictEqual(form.$('.o_form_field.o_selection > a span.o_status').length, 1, + "should have one grey status since selection is the first, normal state"); + + // switch to edit mode and check the result + form.$buttons.find('.o_form_button_edit').click(); + assert.strictEqual(form.$('ul.dropdown-menu.state:visible').length, 0, + "there should still not be a dropdown"); + assert.strictEqual(form.$('.o_form_field.o_selection > a span.o_status.o_status_red').length, 0, + "should still not have one red status since selection is the first, normal state"); + assert.strictEqual(form.$('.o_form_field.o_selection > a span.o_status.o_status_green').length, 0, + "should still not have one green status since selection is the first, normal state"); + assert.strictEqual(form.$('.o_form_field.o_selection > a span.o_status').length, 1, + "should still have one grey status since selection is the first, normal state"); + + // Click on the status button to make the dropdown appear + form.$('.o_form_field.o_selection .o_status').first().click(); + assert.strictEqual(form.$('ul.dropdown-menu.state:visible').length, 1, + "there should be a dropdown"); + assert.strictEqual(form.$('ul.dropdown-menu.state:visible li').length, 2, + "there should be two options in the dropdown"); + + // Click on the last option, "Done" + form.$('ul.dropdown-menu.state:visible li').last().click(); + assert.strictEqual(form.$('ul.dropdown-menu.state:visible').length, 0, + "there should not be a dropdown anymore"); + assert.strictEqual(form.$('.o_form_field.o_selection > a span.o_status.o_status_red').length, 0, + "should not have one red status since selection is the third, done state"); + assert.strictEqual(form.$('.o_form_field.o_selection > a span.o_status.o_status_green').length, 1, + "should have one green status since selection is the third, done state"); + + // save + form.$buttons.find('.o_form_button_save').click(); + assert.strictEqual(form.$('ul.dropdown-menu.state:visible').length, 0, + "there should still not be a dropdown anymore"); + assert.strictEqual(form.$('.o_form_field.o_selection > a span.o_status.o_status_red').length, 0, + "should still not have one red status since selection is the third, done state"); + assert.strictEqual(form.$('.o_form_field.o_selection > a span.o_status.o_status_green').length, 1, + "should still have one green status since selection is the third, done state"); + + form.destroy(); + }); + + QUnit.test('state_selection widget in editable list view', function(assert) { + assert.expect(34); + + var list = createView({ + View: ListView, + model: 'partner', + data: this.data, + arch: '' + + '' + + '' + + '', + }); + + assert.strictEqual(list.$('.o_state_selection_cell .o_selection > a span.o_status').length, 5, + "should have five status selection widgets"); + assert.strictEqual(list.$('.o_state_selection_cell .o_selection > a span.o_status.o_status_red').length, 1, + "should have one red status"); + assert.strictEqual(list.$('.o_state_selection_cell .o_selection > a span.o_status.o_status_green').length, 1, + "should have one green status"); + assert.strictEqual(list.$('ul.dropdown-menu.state:visible').length, 0, + "there should not be a dropdown"); + + // Click on the status button to make the dropdown appear + var $cell = list.$('tbody td.o_state_selection_cell').first(); + list.$('.o_state_selection_cell .o_selection > a span.o_status').first().click(); + assert.ok(!$cell.hasClass('o_edit_mode'), + 'should not be in edit mode since we clicked on the widget and not the cell itself'); + assert.ok(!$cell.siblings().hasClass('o_edit_mode'), + 'siblings should not be in edit mode since we clicked on the widget and not the cell itself'); + assert.strictEqual(list.$('ul.dropdown-menu.state:visible').length, 1, + "there should be a dropdown"); + assert.strictEqual(list.$('ul.dropdown-menu.state:visible li').length, 2, + "there should be two options in the dropdown"); + + // Click on the first option, "Normal" + list.$('ul.dropdown-menu.state:visible li').first().click(); + assert.strictEqual(list.$('.o_state_selection_cell .o_selection > a span.o_status').length, 5, + "should still have five status selection widgets"); + assert.strictEqual(list.$('.o_state_selection_cell .o_selection > a span.o_status.o_status_red').length, 0, + "should now have no red status"); + assert.strictEqual(list.$('.o_state_selection_cell .o_selection > a span.o_status.o_status_green').length, 1, + "should still have one green status"); + assert.strictEqual(list.$('ul.dropdown-menu.state:visible').length, 0, + "there should not be a dropdown"); + + // switch to edit mode and check the result + $cell = list.$('tbody td.o_state_selection_cell').first(); + $cell.click(); + assert.ok(!$cell.hasClass('o_edit_mode'), + 'should still not be in edit mode since widget is readonly'); + assert.ok($cell.siblings().hasClass('o_edit_mode'), + 'siblings should be in edit mode since we clicked on the cell and not the widget itself'); + assert.strictEqual(list.$('.o_state_selection_cell .o_selection > a span.o_status').length, 5, + "should still have five status selection widgets"); + assert.strictEqual(list.$('.o_state_selection_cell .o_selection > a span.o_status.o_status_red').length, 0, + "should now have no red status"); + assert.strictEqual(list.$('.o_state_selection_cell .o_selection > a span.o_status.o_status_green').length, 1, + "should still have one green status"); + assert.strictEqual(list.$('ul.dropdown-menu.state:visible').length, 0, + "there should not be a dropdown"); + + // Click on the status button to make the dropdown appear + list.$('.o_state_selection_cell .o_selection > a span.o_status').first().click(); + assert.strictEqual(list.$('ul.dropdown-menu.state:visible').length, 1, + "there should be a dropdown"); + assert.strictEqual(list.$('ul.dropdown-menu.state:visible li').length, 2, + "there should be two options in the dropdown"); + + // Click on another row + var $firstCell = list.$('tbody td.o_state_selection_cell').first(); + var $lastCell = list.$('tbody td.o_state_selection_cell').last(); + $lastCell.click(); + assert.strictEqual(list.$('ul.dropdown-menu.state:visible').length, 0, + "there should not be a dropdown anymore"); + assert.ok(!$firstCell.siblings().hasClass('o_edit_mode'), + 'first row should not be in edit mode anymore'); + assert.ok($lastCell.siblings().hasClass('o_edit_mode'), + 'last row should be in edit mode'); + + // Click on the last status button to make the dropdown appear + list.$('.o_state_selection_cell .o_selection > a span.o_status').last().click(); + assert.strictEqual(list.$('ul.dropdown-menu.state:visible').length, 1, + "there should be a dropdown"); + assert.strictEqual(list.$('ul.dropdown-menu.state:visible li').length, 2, + "there should be two options in the dropdown"); + + // Click on the last option, "Done" + list.$('ul.dropdown-menu.state:visible li').last().click(); + assert.strictEqual(list.$('ul.dropdown-menu.state:visible').length, 0, + "there should not be a dropdown anymore"); + assert.strictEqual(list.$('.o_state_selection_cell .o_selection > a span.o_status').length, 5, + "should still have five status selection widgets"); + assert.strictEqual(list.$('.o_state_selection_cell .o_selection > a span.o_status.o_status_red').length, 0, + "should still have no red status"); + assert.strictEqual(list.$('.o_state_selection_cell .o_selection > a span.o_status.o_status_green').length, 2, + "should now have two green status"); + assert.strictEqual(list.$('ul.dropdown-menu.state:visible').length, 0, + "there should not be a dropdown"); + + // save + list.$buttons.find('.o_list_button_save').click(); + assert.strictEqual(list.$('.o_state_selection_cell .o_selection > a span.o_status').length, 5, + "should have five status selection widgets"); + assert.strictEqual(list.$('.o_state_selection_cell .o_selection > a span.o_status.o_status_red').length, 0, + "should have no red status"); + assert.strictEqual(list.$('.o_state_selection_cell .o_selection > a span.o_status.o_status_green').length, 2, + "should have two green status"); + assert.strictEqual(list.$('ul.dropdown-menu.state:visible').length, 0, + "there should not be a dropdown"); + + list.destroy(); + }); + + QUnit.module('FieldDomain'); QUnit.test('basic domain field usage is ok', function (assert) {