[IMP] web: o_form_label_empty on standalone label

Before this commit: when standalone label is rendered in form view class
o_form_label_empty is not added due to which label text is not muted.

After this commit: when standalone label is rendered, label is sometime
rendered before field widget is rendered due to which o_form_label_empty class
is not added to label, with this commit we postprocess labels so that
o_form_label_empty is added on all field labels.

Also with commit, signature widget's isSet method will return true if value is
set in signature widget, this will make label of signature widget muted if it
is empty and form view mode is readonly.

task-2523197

closes odoo/odoo#71322

Signed-off-by: Simon Genin (ges@odoo) <ges@odoo.com>
This commit is contained in:
Mohammed Shekha
2021-07-07 08:29:28 +00:00
parent 1e430dc614
commit 4e40979099
3 changed files with 80 additions and 8 deletions
@@ -45,10 +45,7 @@ var FieldBinarySignature = AbstractFieldBinary.extend({
* @override
*/
isSet: function () {
if (this.mode === 'edit') {
return this.value;
}
return true;
return this.value;
},
//--------------------------------------------------------------------------
@@ -46,6 +46,7 @@ var FormRenderer = BasicRenderer.extend({
this.idsForLabels = {};
this.lastActivatedFieldIndex = -1;
this.alertFields = {};
this.labelsToPostProcess = [];
// The form renderer doesn't render invsible fields (invisible="1") by
// default, to speed up the rendering. However, we sometimes have to
// display them (e.g. in Studio, in "show invisible" mode). This flag
@@ -170,6 +171,7 @@ var FormRenderer = BasicRenderer.extend({
if (self.$('.o_field_invalid').length) {
self.canBeSaved(self.state.id);
}
self._postProcessLabels();
return resetWidgets;
});
},
@@ -461,6 +463,18 @@ var FormRenderer = BasicRenderer.extend({
this.defaultFocusField = widget;
}
},
/**
* This function is called once form view is rendered or modifiers are
* changed to process labels to add o_form_label_empty.
*
* @private
*/
_postProcessLabels() {
this.labelsToPostProcess.forEach((label) => {
label.call();
});
this.labelsToPostProcess = [];
},
/**
* @private
* @param {Object} node
@@ -959,11 +973,12 @@ var FormRenderer = BasicRenderer.extend({
callback: function (element, modifiers, record) {
var widgets = self.allFieldWidgets[record.id];
var widget = _.findWhere(widgets, {name: fieldName});
const fieldsInfo = record.fieldsInfo[self.viewType];
if (!widget) {
return; // FIXME this occurs if the widget is created
// after the label (explicit <label/> tag in the
// arch), so this won't work on first rendering
// only on reevaluation
if (fieldsInfo[fieldName]) {
self.labelsToPostProcess.push(element.callback.bind(self, element, modifiers, record));
}
return;
}
element.$el.toggleClass('o_form_label_empty', !!( // FIXME condition is evaluated twice (label AND widget...)
record.data.id
@@ -1090,6 +1105,7 @@ var FormRenderer = BasicRenderer.extend({
delete this.defs;
return Promise.all(defs).then(() => this.__renderView()).then(function () {
self._postProcessLabels();
self._updateView($form.contents());
if (self.state.res_id in self.alertFields) {
self.displayTranslationAlert();
@@ -1543,6 +1543,65 @@ QUnit.module('Views', {
form.destroy();
});
QUnit.test('label tag added for fields have o_form_empty class in readonly mode if field is empty', async function (assert) {
assert.expect(8);
this.data.partner.fields.foo.default = false; // no default value for this test
this.data.partner.records[1].foo = false; // 1 is record with id=2
this.data.partner.records[1].trululu = false; // 1 is record with id=2
this.data.partner.fields.int_field.readonly = true;
this.data.partner.onchanges.foo = function (obj) {
if (obj.foo === "hello") {
obj.int_field = false;
}
};
const form = await createView({
View: FormView,
model: 'partner',
data: this.data,
arch: `<form string="Partners">
<sheet>
<label for="foo" string="Foo"/>
<field name="foo"/>
<label for="trululu" string="Trululu" attrs="{'readonly': [['foo', '=', False]]}"/>
<field name="trululu" attrs="{'readonly': [['foo', '=', False]]}"/>
<label for="int_field" string="IntField" attrs="{'readonly': [['int_field', '=', False]]}"/>
<field name="int_field"/>
</sheet>
</form>`,
res_id: 2,
});
assert.containsN(form, '.o_field_widget.o_field_empty', 2,
"should have 2 empty fields with correct class");
assert.containsN(form, '.o_form_label_empty', 2,
"should have 2 muted labels (for the empty fieds) in readonly");
await testUtils.form.clickEdit(form);
assert.containsOnce(form, '.o_field_empty',
"in edit mode, only empty readonly fields should have the o_field_empty class");
assert.containsOnce(form, '.o_form_label_empty',
"in edit mode, only labels associated to empty readonly fields should have the o_form_label_empty class");
await testUtils.fields.editInput(form.$('input[name=foo]'), 'test');
assert.containsNone(form, '.o_field_empty',
"after readonly modifier change, the o_field_empty class should have been removed");
assert.containsNone(form, '.o_form_label_empty',
"after readonly modifier change, the o_form_label_empty class should have been removed");
await testUtils.fields.editInput(form.$('input[name=foo]'), 'hello');
assert.containsOnce(form, '.o_field_empty',
"after value changed to false for a readonly field, the o_field_empty class should have been added");
assert.containsOnce(form, '.o_form_label_empty',
"after value changed to false for a readonly field, the o_form_label_empty class should have been added");
form.destroy();
});
QUnit.test('form view can switch to edit mode', async function (assert) {
assert.expect(9);