diff --git a/addons/web/static/src/legacy/js/views/form/form_renderer.js b/addons/web/static/src/legacy/js/views/form/form_renderer.js index 9a9c3570c00..80950ec3fca 100644 --- a/addons/web/static/src/legacy/js/views/form/form_renderer.js +++ b/addons/web/static/src/legacy/js/views/form/form_renderer.js @@ -52,6 +52,9 @@ var FormRenderer = BasicRenderer.extend({ // display them (e.g. in Studio, in "show invisible" mode). This flag // allows to disable this optimization. this.renderInvisible = false; + // Keeps track of buttons that are disabled momentarily and need to be renabled. + // Needed to compare with buttons that have to stay disabled all the time. + this.manuallyDisabledButtons = new Set(); }, /** * @override @@ -182,16 +185,24 @@ var FormRenderer = BasicRenderer.extend({ * */ disableButtons: function () { - this.$('.o_statusbar_buttons button, .oe_button_box button') - .attr('disabled', true); + const allButtons = this.$el[0].querySelectorAll('.o_statusbar_buttons button, .oe_button_box button'); + for (const button of allButtons) { + if (!button.getAttribute("disabled")) { + this.manuallyDisabledButtons.add(button) + button.setAttribute("disabled", true) + } + } }, /** * Enable statusbar buttons and stat buttons so they can be clicked again * */ enableButtons: function () { - this.$('.o_statusbar_buttons button, .oe_button_box button') - .removeAttr('disabled'); + const allButtons = this.$el[0].querySelectorAll('.o_statusbar_buttons button, .oe_button_box button'); + this.manuallyDisabledButtons.forEach((button) => { + button.removeAttribute("disabled"); + }); + this.manuallyDisabledButtons.clear(); }, /** * Put the focus on the last activated widget. @@ -814,11 +825,20 @@ var FormRenderer = BasicRenderer.extend({ var $button = viewUtils.renderButtonFromNode(node, { extraClass: 'oe_stat_button', }); + + // If there is no type nor name, it will not bind a click listener and will set the button as disabled + const buttonDoesStartAnAction = node.attrs.type || node.attrs.name; + if (!buttonDoesStartAnAction) { + $button[0].setAttribute("disabled", true); + } + $button.append(_.map(node.children, this._renderNode.bind(this))); if (node.attrs.help) { this._addButtonTooltip(node, $button); } - this._addOnClickAction($button, node); + if (buttonDoesStartAnAction) { + this._addOnClickAction($button, node); + } this._handleAttributes($button, node); this._registerModifiers(node, this.state, $button); return $button; @@ -1109,6 +1129,7 @@ var FormRenderer = BasicRenderer.extend({ return Promise.all(defs).then(() => this.__renderView()).then(function () { self._postProcessLabels(); self._updateView($form.contents()); + self.manuallyDisabledButtons.clear(); if (self.state.res_id in self.alertFields) { self.displayTranslationAlert(); } diff --git a/addons/web/static/tests/legacy/views/form_tests.js b/addons/web/static/tests/legacy/views/form_tests.js index 4632e96d03e..0ff30a82261 100644 --- a/addons/web/static/tests/legacy/views/form_tests.js +++ b/addons/web/static/tests/legacy/views/form_tests.js @@ -1114,7 +1114,7 @@ QUnit.module('Views', { }); - QUnit.test('rendering stat buttons', async function (assert) { + QUnit.test('rendering stat buttons with action', async function (assert) { assert.expect(3); var form = await createView({ @@ -1123,11 +1123,11 @@ QUnit.module('Views', { data: this.data, arch:'
' + '' + - '
' + - '' + - '' + '
' + @@ -1147,7 +1147,86 @@ QUnit.module('Views', { count++; }); await testUtils.dom.click('.oe_stat_button'); - assert.strictEqual(count, 1, "should have triggered a execute action"); + assert.strictEqual(count, 0, "should have triggered an execute_action"); + form.destroy(); + }); + + QUnit.test('rendering stat buttons without action', async function (assert) { + assert.expect(4); + + var form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch:'' + + '' + + '
' + + '' + + '' + + '
' + + '' + + '' + + '' + + '
' + + '', + res_id: 2, + }); + + assert.containsN(form, 'button.oe_stat_button', 2); + assert.containsOnce(form, 'button.oe_stat_button.o_invisible_modifier'); + assert.containsN(form, 'button.oe_stat_button:disabled', 2); + + var count = 0; + await testUtils.mock.intercept(form, "execute_action", function () { + count++; + }); + await testUtils.dom.click('.oe_stat_button'); + assert.strictEqual(count, 0, "should have not triggered an execute_action"); + form.destroy(); + }); + + QUnit.test('readonly stat buttons stays disabled', async function (assert) { + assert.expect(4); + + var form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch:'
' + + '' + + '
' + + '' + + '' + + '
' + + '' + + '' + + '' + + '
' + + '
', + res_id: 2, + }); + + var count = 0; + await testUtils.mock.intercept(form, "execute_action", function (event) { + if (event.data.action_data.name == "action_to_perform") { + assert.containsN(form, 'button.oe_stat_button[disabled]', 2, "While performing the action, both buttons should be disabled."); + event.data.on_success(); + } + }); + + assert.containsN(form, 'button.oe_stat_button', 2); + assert.containsN(form, 'button.oe_stat_button[disabled]', 1); + await testUtils.dom.click('button[name=action_to_perform]'); + assert.containsN(form, 'button.oe_stat_button[disabled]', 1, "After performing the action, only one button should be disabled."); + form.destroy(); }); @@ -2741,7 +2820,7 @@ QUnit.module('Views', { arch:'
' + '' + '
' + - '' + '
' + @@ -7137,7 +7216,7 @@ QUnit.module('Views', { '' + '' + '
' + - '' + '
' + @@ -7201,7 +7280,7 @@ QUnit.module('Views', { '' + '' + '
' + - '' + '
' + @@ -7314,7 +7393,7 @@ QUnit.module('Views', { 'partner,false,form': '' + '' + '
' + - '' + '
' +