[FIX] web: editable list: widths when toggling fields

Let's assume the following situation:
 - an empty editable x2many list with date field (or any other
   fixed width field) and optional fields
 - add a record to the list, but discard it directly
 - toggle an optional field

After those steps, the date field doesn't have its hardcoded,
absolute width anymore.

This rev. fixes this issue by clarifying the way the stored widths
are erased when the columns change, preventing to reach a corner
case in which we have stored widths, but we can't apply them, and
thus let the browser uniformly divide the space amongst columns.
This commit is contained in:
Aaron Bohy
2019-08-23 06:28:35 +00:00
parent e35c5e044e
commit 85bfef9ced
2 changed files with 73 additions and 22 deletions
@@ -492,7 +492,6 @@ ListRenderer.include({
* @override
*/
updateState: function (state, params) {
this.oldColumns = this.columns;
this.columnWidths = false;
if (params.keepWidths) {
this.columnWidths = this.$('thead th').toArray().map(function (th) {
@@ -542,24 +541,14 @@ ListRenderer.include({
*/
_freezeColumnWidths: function () {
if (!this._hasVisibleRecords(this.state) && !this.columnWidths) {
// there is no record -> don't force column's widths w.r.t. their label
// there is no record nor widths to restore -> don't force column's
// widths w.r.t. their label
return;
}
let restoreWidths = false;
if (this.columnWidths) {
if (this.oldColumns && this.oldColumns.length === this.columns.length) {
for (let i = 0; i < this.oldColumns.length; i++) {
if (this.oldColumns[i] !== this.columns[i]) {
break;
}
restoreWidths = (i === this.oldColumns.length - 1); // columns are as before
}
}
}
var $thead = this.$('thead');
$thead.find('th').each((index, th) => {
var $th = $(th);
$th.css('width', restoreWidths ? this.columnWidths[index] : $th.outerWidth() + 'px');
$th.css('width', this.columnWidths ? this.columnWidths[index] : $th.outerWidth() + 'px');
});
this.$('table').css('table-layout', 'fixed');
},
@@ -841,22 +830,37 @@ ListRenderer.include({
});
},
/**
* Overridden to set weights or explicit width on columns for the fixed layout.
* Override to compute the (relative or absolute) width of each column.
*
* @override
* @private
*/
_processColumns: function () {
const oldColumns = this.columns;
this._super.apply(this, arguments);
if (this.editable) {
this.columns.forEach((column) => {
const width = this._getColumnWidth(column);
if (width.match(/[a-zA-Z]/)) { // absolute width with measure unit (e.g. 100px)
column.attrs.absoluteWidth = width;
} else { // relative width expressed as a weight (e.g. 1.5)
column.attrs.relativeWidth = parseFloat(width, 10);
// check if stored widths still apply
if (this.columnWidths && oldColumns && oldColumns.length === this.columns.length) {
for (let i = 0; i < oldColumns.length; i++) {
if (oldColumns[i] !== this.columns[i]) {
this.columnWidths = false; // columns changed, so forget stored widths
break;
}
}
});
} else {
this.columnWidths = false; // columns changed, so forget stored widths
}
// if we don't have widths yet, computed them
if (!this.columnWidths) {
this.columns.forEach((column) => {
const width = this._getColumnWidth(column);
if (width.match(/[a-zA-Z]/)) { // absolute width with measure unit (e.g. 100px)
column.attrs.absoluteWidth = width;
} else { // relative width expressed as a weight (e.g. 1.5)
column.attrs.relativeWidth = parseFloat(width, 10);
}
});
}
}
},
/**
@@ -2,10 +2,12 @@ odoo.define('web.field_one_to_many_tests', function (require) {
"use strict";
var AbstractField = require('web.AbstractField');
var AbstractStorageService = require('web.AbstractStorageService');
var FormView = require('web.FormView');
var KanbanRecord = require('web.KanbanRecord');
var ListRenderer = require('web.ListRenderer');
var NotificationService = require('web.NotificationService');
var RamStorage = require('web.RamStorage');
var relationalFields = require('web.relational_fields');
var testUtils = require('web.test_utils');
var fieldUtils = require('web.field_utils');
@@ -8638,6 +8640,51 @@ QUnit.module('fields', {}, function () {
form.destroy();
});
QUnit.test('column widths are correct after toggling optional fields', async function (assert) {
assert.expect(2);
var RamStorageService = AbstractStorageService.extend({
storage: new RamStorage(),
});
this.data.partner.records[0].p = [2];
var form = await createView({
View: FormView,
model: 'partner',
data: this.data,
arch: '<form>' +
'<field name="p">' +
'<tree editable="top">' +
'<field name="date" required="1"/>' + // we want the list to remain empty
'<field name="foo"/>' +
'<field name="int_field" optional="1"/>' +
'</tree>' +
'</field>' +
'</form>',
services: {
local_storage: RamStorageService,
},
});
// date fields have an hardcoded width, which apply when there is no
// record, and should be kept afterwards
let width = form.$('th[data-name="date"]')[0].offsetWidth;
// create a record to store the current widths, but discard it directly to keep
// the list empty (otherwise, the browser automatically computes the optimal widths)
await testUtils.dom.click(form.$('.o_field_x2many_list_row_add a'));
assert.strictEqual(form.$('th[data-name="date"]')[0].offsetWidth, width);
await testUtils.dom.click(form.$('.o_optional_columns_dropdown_toggle'));
await testUtils.dom.click(form.$('div.o_optional_columns div.dropdown-item input'));
assert.strictEqual(form.$('th[data-name="date"]')[0].offsetWidth, width);
form.destroy();
});
});
});
});