Skip to content

Commit 5f5e699

Browse files
committed
[FIX] web: Form stat button stays correctly disabled
A stat button set as disabled was used to just show some statistics without an action on click. However, on tasks executed by the Form, all the buttons would be temporarly set as disabled then set back to enabled. This would lose the original state of the button and make them all clickable. The way it worked before v14.5 was most likely because the action manager would not throw an error on an action with those "invalid" arguments. Also, a button without a type and name will now be set the disabled attribute automatically. closes odoo#78839 Signed-off-by: Aaron Bohy (aab) <[email protected]>
1 parent 88d0c49 commit 5f5e699

File tree

2 files changed

+112
-12
lines changed

2 files changed

+112
-12
lines changed

addons/web/static/src/legacy/js/views/form/form_renderer.js

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ var FormRenderer = BasicRenderer.extend({
5252
// display them (e.g. in Studio, in "show invisible" mode). This flag
5353
// allows to disable this optimization.
5454
this.renderInvisible = false;
55+
// Keeps track of buttons that are disabled momentarily and need to be renabled.
56+
// Needed to compare with buttons that have to stay disabled all the time.
57+
this.manuallyDisabledButtons = new Set();
5558
},
5659
/**
5760
* @override
@@ -182,16 +185,24 @@ var FormRenderer = BasicRenderer.extend({
182185
*
183186
*/
184187
disableButtons: function () {
185-
this.$('.o_statusbar_buttons button, .oe_button_box button')
186-
.attr('disabled', true);
188+
const allButtons = this.$el[0].querySelectorAll('.o_statusbar_buttons button, .oe_button_box button');
189+
for (const button of allButtons) {
190+
if (!button.getAttribute("disabled")) {
191+
this.manuallyDisabledButtons.add(button)
192+
button.setAttribute("disabled", true)
193+
}
194+
}
187195
},
188196
/**
189197
* Enable statusbar buttons and stat buttons so they can be clicked again
190198
*
191199
*/
192200
enableButtons: function () {
193-
this.$('.o_statusbar_buttons button, .oe_button_box button')
194-
.removeAttr('disabled');
201+
const allButtons = this.$el[0].querySelectorAll('.o_statusbar_buttons button, .oe_button_box button');
202+
this.manuallyDisabledButtons.forEach((button) => {
203+
button.removeAttribute("disabled");
204+
});
205+
this.manuallyDisabledButtons.clear();
195206
},
196207
/**
197208
* Put the focus on the last activated widget.
@@ -814,11 +825,20 @@ var FormRenderer = BasicRenderer.extend({
814825
var $button = viewUtils.renderButtonFromNode(node, {
815826
extraClass: 'oe_stat_button',
816827
});
828+
829+
// If there is no type nor name, it will not bind a click listener and will set the button as disabled
830+
const buttonDoesStartAnAction = node.attrs.type || node.attrs.name;
831+
if (!buttonDoesStartAnAction) {
832+
$button[0].setAttribute("disabled", true);
833+
}
834+
817835
$button.append(_.map(node.children, this._renderNode.bind(this)));
818836
if (node.attrs.help) {
819837
this._addButtonTooltip(node, $button);
820838
}
821-
this._addOnClickAction($button, node);
839+
if (buttonDoesStartAnAction) {
840+
this._addOnClickAction($button, node);
841+
}
822842
this._handleAttributes($button, node);
823843
this._registerModifiers(node, this.state, $button);
824844
return $button;
@@ -1109,6 +1129,7 @@ var FormRenderer = BasicRenderer.extend({
11091129
return Promise.all(defs).then(() => this.__renderView()).then(function () {
11101130
self._postProcessLabels();
11111131
self._updateView($form.contents());
1132+
self.manuallyDisabledButtons.clear();
11121133
if (self.state.res_id in self.alertFields) {
11131134
self.displayTranslationAlert();
11141135
}

addons/web/static/tests/legacy/views/form_tests.js

Lines changed: 86 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,7 +1114,7 @@ QUnit.module('Views', {
11141114

11151115
});
11161116

1117-
QUnit.test('rendering stat buttons', async function (assert) {
1117+
QUnit.test('rendering stat buttons with action', async function (assert) {
11181118
assert.expect(3);
11191119

11201120
var form = await createView({
@@ -1123,7 +1123,44 @@ QUnit.module('Views', {
11231123
data: this.data,
11241124
arch:'<form string="Partners">' +
11251125
'<sheet>' +
1126-
'<div name="button_box">' +
1126+
'<div name="button_box" class="oe_button_box">' +
1127+
'<button class="oe_stat_button" >' +
1128+
'<field name="int_field"/>' +
1129+
'</button>' +
1130+
'<button class="oe_stat_button" name="some_action" type="action" attrs=\'{"invisible": [["bar", "=", true]]}\'>' +
1131+
'<field name="bar"/>' +
1132+
'</button>' +
1133+
'</div>' +
1134+
'<group>' +
1135+
'<field name="foo"/>' +
1136+
'</group>' +
1137+
'</sheet>' +
1138+
'</form>',
1139+
res_id: 2,
1140+
});
1141+
1142+
assert.containsN(form, 'button.oe_stat_button', 2);
1143+
assert.containsOnce(form, 'button.oe_stat_button.o_invisible_modifier');
1144+
1145+
var count = 0;
1146+
await testUtils.mock.intercept(form, "execute_action", function () {
1147+
count++;
1148+
});
1149+
await testUtils.dom.click('.oe_stat_button');
1150+
assert.strictEqual(count, 0, "should have triggered an execute_action");
1151+
form.destroy();
1152+
});
1153+
1154+
QUnit.test('rendering stat buttons without action', async function (assert) {
1155+
assert.expect(4);
1156+
1157+
var form = await createView({
1158+
View: FormView,
1159+
model: 'partner',
1160+
data: this.data,
1161+
arch:'<form string="Partners">' +
1162+
'<sheet>' +
1163+
'<div name="button_box" class="oe_button_box">' +
11271164
'<button class="oe_stat_button">' +
11281165
'<field name="int_field"/>' +
11291166
'</button>' +
@@ -1141,13 +1178,55 @@ QUnit.module('Views', {
11411178

11421179
assert.containsN(form, 'button.oe_stat_button', 2);
11431180
assert.containsOnce(form, 'button.oe_stat_button.o_invisible_modifier');
1181+
assert.containsN(form, 'button.oe_stat_button:disabled', 2);
11441182

11451183
var count = 0;
11461184
await testUtils.mock.intercept(form, "execute_action", function () {
11471185
count++;
11481186
});
11491187
await testUtils.dom.click('.oe_stat_button');
1150-
assert.strictEqual(count, 1, "should have triggered a execute action");
1188+
assert.strictEqual(count, 0, "should have not triggered an execute_action");
1189+
form.destroy();
1190+
});
1191+
1192+
QUnit.test('readonly stat buttons stays disabled', async function (assert) {
1193+
assert.expect(4);
1194+
1195+
var form = await createView({
1196+
View: FormView,
1197+
model: 'partner',
1198+
data: this.data,
1199+
arch:'<form string="Partners">' +
1200+
'<sheet>' +
1201+
'<div name="button_box" class="oe_button_box">' +
1202+
'<button class="oe_stat_button">' +
1203+
'<field name="int_field"/>' +
1204+
'</button>' +
1205+
'<button class="oe_stat_button" type="action" name="some_action">' +
1206+
'<field name="bar"/>' +
1207+
'</button>' +
1208+
'</div>' +
1209+
'<group>' +
1210+
'<button type="action" name="action_to_perform">Run an action</button>' +
1211+
'</group>' +
1212+
'</sheet>' +
1213+
'</form>',
1214+
res_id: 2,
1215+
});
1216+
1217+
var count = 0;
1218+
await testUtils.mock.intercept(form, "execute_action", function (event) {
1219+
if (event.data.action_data.name == "action_to_perform") {
1220+
assert.containsN(form, 'button.oe_stat_button[disabled]', 2, "While performing the action, both buttons should be disabled.");
1221+
event.data.on_success();
1222+
}
1223+
});
1224+
1225+
assert.containsN(form, 'button.oe_stat_button', 2);
1226+
assert.containsN(form, 'button.oe_stat_button[disabled]', 1);
1227+
await testUtils.dom.click('button[name=action_to_perform]');
1228+
assert.containsN(form, 'button.oe_stat_button[disabled]', 1, "After performing the action, only one button should be disabled.");
1229+
11511230
form.destroy();
11521231
});
11531232

@@ -2741,7 +2820,7 @@ QUnit.module('Views', {
27412820
arch:'<form string="Partners">' +
27422821
'<sheet>' +
27432822
'<div name="button_box">' +
2744-
'<button class="oe_stat_button">' +
2823+
'<button class="oe_stat_button" name="some_action" type="action">' +
27452824
'<field name="bar"/>' +
27462825
'</button>' +
27472826
'</div>' +
@@ -7137,7 +7216,7 @@ QUnit.module('Views', {
71377216
'</header>' +
71387217
'<sheet>' +
71397218
'<div name="button_box" class="oe_button_box">' +
7140-
'<button class="oe_stat_button">' +
7219+
'<button class="oe_stat_button" name="some_action" type="action">' +
71417220
'<field name="bar"/>' +
71427221
'</button>' +
71437222
'</div>' +
@@ -7201,7 +7280,7 @@ QUnit.module('Views', {
72017280
'</header>' +
72027281
'<sheet>' +
72037282
'<div name="button_box" class="oe_button_box">' +
7204-
'<button class="oe_stat_button">' +
7283+
'<button class="oe_stat_button" name="some_action" type="action">' +
72057284
'<field name="bar"/>' +
72067285
'</button>' +
72077286
'</div>' +
@@ -7314,7 +7393,7 @@ QUnit.module('Views', {
73147393
'partner,false,form': '<form>' +
73157394
'<sheet>' +
73167395
'<div name="button_box" class="oe_button_box">' +
7317-
'<button class="oe_stat_button">' +
7396+
'<button class="oe_stat_button" name="some_action" type="action">' +
73187397
'<field name="bar"/>' +
73197398
'</button>' +
73207399
'</div>' +

0 commit comments

Comments
 (0)