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 5cf4b022c5d..68a449a9246 100644 --- a/addons/web/static/src/js/views/list/list_renderer.js +++ b/addons/web/static/src/js/views/list/list_renderer.js @@ -1109,6 +1109,11 @@ var ListRenderer = BasicRenderer.extend({ _onToggleOptionalColumn: function (ev) { var self = this; ev.stopPropagation(); + // when the input's label is clicked, the click event is also raised on the + // input itself (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label), + // so this handler is executed twice (except if the rendering is quick enough, + // as when we render, we empty the HTML) + ev.preventDefault(); var input = ev.currentTarget.querySelector('input'); var fieldIndex = this.optionalColumnsEnabled.indexOf(input.name); if (fieldIndex >= 0) { diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index 50e5e6cb9c9..c22e164bdf2 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -5,6 +5,7 @@ var AbstractStorageService = require('web.AbstractStorageService'); var BasicModel = require('web.BasicModel'); var core = require('web.core'); var basicFields = require('web.basic_fields'); +var fieldRegistry = require('web.field_registry'); var FormView = require('web.FormView'); var ListRenderer = require('web.ListRenderer'); var ListView = require('web.ListView'); @@ -8961,6 +8962,62 @@ QUnit.module('Views', { list.destroy(); }); + QUnit.test('list view with optional fields and async rendering', async function (assert) { + assert.expect(14); + + const prom = testUtils.makeTestPromise(); + const FieldChar = fieldRegistry.get('char'); + fieldRegistry.add('asyncwidget', FieldChar.extend({ + async _render() { + assert.ok(true, 'the rendering must be async'); + this._super(...arguments); + await prom; + }, + })); + + const RamStorageService = AbstractStorageService.extend({ + storage: new RamStorage(), + }); + + const list = await createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: ` + + + + `, + services: { + local_storage: RamStorageService, + }, + }); + + assert.containsN(list, 'th', 2); + assert.isNotVisible(list.$('.o_optional_columns_dropdown')); + + // add an optional field (we click on the label on purpose, as it will trigger + // a second event on the input) + await testUtils.dom.click(list.$('table .o_optional_columns_dropdown_toggle')); + assert.isVisible(list.$('.o_optional_columns_dropdown')); + assert.containsNone(list.$('.o_optional_columns_dropdown'), 'input:checked'); + await testUtils.dom.click(list.$('div.o_optional_columns div.dropdown-item:first label')); + + assert.containsN(list, 'th', 2); + assert.isVisible(list.$('.o_optional_columns_dropdown')); + assert.containsNone(list.$('.o_optional_columns_dropdown'), 'input:checked'); + + prom.resolve(); + await testUtils.nextTick(); + + assert.containsN(list, 'th', 3); + assert.isVisible(list.$('.o_optional_columns_dropdown')); + assert.containsOnce(list.$('.o_optional_columns_dropdown'), 'input:checked'); + + list.destroy(); + delete fieldRegistry.map.asyncwidget; + }); + QUnit.test('change the viewType of the current action', async function (assert) { assert.expect(25);