[IMP] web,account: editable list: no more position absolute
Before this rev., widgets were absolutely positioned in table cells. This cause an undesired effect with text fields, as their textarea overflowed and overlapped the next lines. With this rev., wigdets are now statically positioned, and the cells height and width don't change when switching a row to edit mode. Part of task 1915702
This commit is contained in:
@@ -9,7 +9,7 @@ odoo.define('account.section_and_note_backend', function (require) {
|
||||
var FieldChar = require('web.basic_fields').FieldChar;
|
||||
var FieldOne2Many = require('web.relational_fields').FieldOne2Many;
|
||||
var fieldRegistry = require('web.field_registry');
|
||||
var FieldText = require('web.basic_fields').FieldText;
|
||||
var ListFieldText = require('web.basic_fields').ListFieldText;
|
||||
var ListRenderer = require('web.ListRenderer');
|
||||
|
||||
var SectionAndNoteListRenderer = ListRenderer.extend({
|
||||
@@ -94,7 +94,7 @@ var SectionAndNoteFieldOne2Many = FieldOne2Many.extend({
|
||||
// and a FieldText for the rest (product and note).
|
||||
var SectionAndNoteFieldText = function (parent, name, record, options) {
|
||||
var isSection = record.data.display_type === 'line_section';
|
||||
var Constructor = isSection ? FieldChar : FieldText;
|
||||
var Constructor = isSection ? FieldChar : ListFieldText;
|
||||
return new Constructor(parent, name, record, options);
|
||||
};
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ var dom = {
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
minHeight = (options && options.min_height) || 50;
|
||||
minHeight = 'min_height' in options ? options.min_height : 50;
|
||||
|
||||
$fixedTextarea = $('<textarea disabled>', {
|
||||
class: $textarea[0].className,
|
||||
|
||||
@@ -1112,6 +1112,7 @@ var FieldText = InputField.extend(TranslatableFieldMixin, {
|
||||
if (this.mode === 'edit') {
|
||||
this.tagName = 'textarea';
|
||||
}
|
||||
this.autoResizeOptions = {parent: this};
|
||||
},
|
||||
/**
|
||||
* As it it done in the start function, the autoresize is done only once.
|
||||
@@ -1120,7 +1121,7 @@ var FieldText = InputField.extend(TranslatableFieldMixin, {
|
||||
*/
|
||||
start: function () {
|
||||
if (this.mode === 'edit') {
|
||||
dom.autoresize(this.$el, {parent: this});
|
||||
dom.autoresize(this.$el, this.autoResizeOptions);
|
||||
|
||||
this.$el = this.$el.add(this._renderTranslateButton());
|
||||
}
|
||||
@@ -1157,6 +1158,16 @@ var FieldText = InputField.extend(TranslatableFieldMixin, {
|
||||
},
|
||||
});
|
||||
|
||||
var ListFieldText = FieldText.extend({
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this.autoResizeOptions.min_height = 0;
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Displays a handle to modify the sequence.
|
||||
*/
|
||||
@@ -2995,6 +3006,7 @@ return {
|
||||
FieldPhone: FieldPhone,
|
||||
FieldProgressBar: FieldProgressBar,
|
||||
FieldText: FieldText,
|
||||
ListFieldText: ListFieldText,
|
||||
FieldToggleBoolean: FieldToggleBoolean,
|
||||
HandleWidget: HandleWidget,
|
||||
InputField: InputField,
|
||||
|
||||
@@ -26,6 +26,7 @@ registry
|
||||
.add('datetime', basic_fields.FieldDateTime)
|
||||
.add('domain', basic_fields.FieldDomain)
|
||||
.add('text', basic_fields.FieldText)
|
||||
.add('list.text', basic_fields.ListFieldText)
|
||||
.add('html', basic_fields.FieldText)
|
||||
.add('float', basic_fields.FieldFloat)
|
||||
.add('char', basic_fields.FieldChar)
|
||||
|
||||
@@ -119,6 +119,10 @@ ListRenderer.include({
|
||||
* - There is no line in edition (all lines are saved so they are all valid)
|
||||
* - The line in edition can be saved
|
||||
*
|
||||
* If the given recordID is a record in the list, toggle a className on its
|
||||
* row's cells for invalid fields, so that we can style those cells
|
||||
* differently.
|
||||
*
|
||||
* @override
|
||||
* @param {string} [recordID]
|
||||
* @returns {string[]}
|
||||
@@ -130,7 +134,10 @@ ListRenderer.include({
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return this._super(recordID);
|
||||
var fieldNames = this._super(recordID);
|
||||
this.$('.o_selected_row .o_data_cell').removeClass('o_invalid_cell');
|
||||
this.$('.o_selected_row .o_data_cell:has(> .o_field_invalid)').addClass('o_invalid_cell');
|
||||
return fieldNames;
|
||||
},
|
||||
/**
|
||||
* We need to override the confirmChange method from BasicRenderer to
|
||||
|
||||
@@ -10,14 +10,6 @@
|
||||
border-collapse: initial;
|
||||
border-spacing: 0;
|
||||
|
||||
// Checkbox in editable list
|
||||
// should be clickable and activate the row
|
||||
&.o_editable_list .o_data_row:not(.o_selected_row) .o_data_cell {
|
||||
.custom-checkbox:not(.o_readonly_modifier) {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
thead {
|
||||
color: $o-main-text-color;
|
||||
border-bottom: 1px solid #cacaca;
|
||||
@@ -31,7 +23,13 @@
|
||||
}
|
||||
tbody > tr > td:not(.o_list_record_selector) {
|
||||
@include o-text-overflow(table-cell);
|
||||
white-space: normal;
|
||||
|
||||
&.o_list_number {
|
||||
text-align: right;
|
||||
}
|
||||
&.o_list_text {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
tfoot {
|
||||
@@ -144,15 +142,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Values styling
|
||||
.o_list_number {
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.o_list_text {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.o_data_cell.o_text_overflow,
|
||||
.o_data_cell.o_invisible_modifier {
|
||||
// Override display: none to keep table cells aligned. Note: cannot use
|
||||
@@ -173,89 +162,62 @@
|
||||
.o_list_record_remove_header, .o_handle_cell {
|
||||
width: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.o_data_row.o_selected_row > .o_data_cell:not(.o_readonly_modifier) {
|
||||
position: relative;
|
||||
|
||||
// The in-edition elements will overflow other elements (the rows below,
|
||||
// the content below the list view, ...). As those might be using
|
||||
// position: relative, the in-edition elements have to define a z-index.
|
||||
// Technically, creating a stacking context for a meaningful parent
|
||||
// (maybe the .o_content) might be required for safety but we should
|
||||
// consider this later if needed.
|
||||
z-index: 1;
|
||||
|
||||
// The in-edition elements will overflow other elements (the rows below,
|
||||
// the content below the list view, ...). As those might be using
|
||||
// position: relative, the in-edition elements have to define a z-index.
|
||||
// Technically, creating a stacking context for a meaningful parent
|
||||
// (maybe the .o_content) might be required for safety but we should
|
||||
// consider this later if needed.
|
||||
z-index: 1;
|
||||
|
||||
// Field widgets override
|
||||
> .o_field_widget {
|
||||
@include o-position-absolute(0, 0, auto, 0);
|
||||
min-height: 100%;
|
||||
|
||||
&:not(.o_field_text) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&.o_field_many2manytags > .badge {
|
||||
&, > .o_badge_text {
|
||||
white-space: normal;
|
||||
max-width: 100%; // Fix a chrome bug where badge would not break on multiple lines
|
||||
}
|
||||
// checkboxes in editable lists should be clickable and activate the row
|
||||
.o_data_row:not(.o_selected_row) .o_data_cell {
|
||||
.custom-checkbox:not(.o_readonly_modifier) {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
> .o_field_translate {
|
||||
@include o-position-absolute(0, 5px);
|
||||
width: 16px; // to have some space on the right
|
||||
}
|
||||
.o_input {
|
||||
padding: $table-cell-padding-sm;
|
||||
|
||||
.o_data_row.o_selected_row > .o_data_cell:not(.o_readonly_modifier) {
|
||||
position: relative; // for o_field_translate
|
||||
background-color: white;
|
||||
|
||||
.o_input {
|
||||
padding: 0!important;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
.o_input_dropdown {
|
||||
min-height: 100%;
|
||||
}
|
||||
.o_field_monetary input {
|
||||
width: 0;
|
||||
}
|
||||
.o_field_boolean {
|
||||
position: relative!important;
|
||||
}
|
||||
.o_field_widget {
|
||||
min-width: 0;
|
||||
}
|
||||
.o_input_dropdown, .o_datepicker {
|
||||
> .o_input {
|
||||
padding-right: 15px; // To prevent caret overlapping
|
||||
> .o_field_text {
|
||||
vertical-align: top;
|
||||
}
|
||||
> .o_dropdown_button, .o_datepicker_button {
|
||||
@include o-position-absolute(20%, 5px);
|
||||
> .o_field_widget {
|
||||
width: 100%;
|
||||
> .o_external_button {
|
||||
padding: 0;
|
||||
border: none;
|
||||
background-color: inherit;
|
||||
margin-left: 5px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
> .o_field_monetary input {
|
||||
width: 0; // override width: 100px and let flex makes this input grow
|
||||
margin: 0 4px;
|
||||
}
|
||||
> .o_field_many2manytags > .o_field_many2one {
|
||||
// reduce basis to prevent many2one input from going to the next line to often
|
||||
flex-basis: 40px;
|
||||
}
|
||||
.o_input_dropdown, .o_datepicker {
|
||||
> .o_input {
|
||||
padding-right: 15px; // to prevent caret overlapping
|
||||
}
|
||||
> .o_dropdown_button, .o_datepicker_button {
|
||||
@include o-position-absolute(0, 0);
|
||||
}
|
||||
}
|
||||
> .o_field_translate {
|
||||
@include o-position-absolute(0, 5px);
|
||||
width: 16px; // to have some space on the right
|
||||
}
|
||||
> .o_row_handle {
|
||||
visibility: hidden; // hide sequence when editing
|
||||
}
|
||||
}
|
||||
.o_field_widget > .o_external_button {
|
||||
padding: 0;
|
||||
background-color: inherit;
|
||||
margin-left: 3px;
|
||||
}
|
||||
.o_row_handle {
|
||||
visibility: hidden; // Hide sequence when editing
|
||||
}
|
||||
.o_field_boolean {
|
||||
width: auto!important; // Override the width computed in JS (needed for Chrome)
|
||||
}
|
||||
}
|
||||
|
||||
.o_field_x2many_list_row_add {
|
||||
.o_field_x2many_list_row_add, .o_group_field_row_add {
|
||||
a:focus, a:active {
|
||||
color: $link-hover-color;
|
||||
outline: none;
|
||||
|
||||
@@ -13,6 +13,7 @@ var testUtils = require('web.test_utils');
|
||||
var widgetRegistry = require('web.widget_registry');
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
var _t = core._t;
|
||||
var createView = testUtils.createView;
|
||||
|
||||
|
||||
@@ -25,6 +26,7 @@ QUnit.module('Views', {
|
||||
bar: {string: "Bar", type: "boolean"},
|
||||
date: {string: "Some Date", type: "date"},
|
||||
int_field: {string: "int_field", type: "integer", sortable: true, group_operator: "sum"},
|
||||
text: {string: "text field", type: "text"},
|
||||
qux: {string: "my float", type: "float"},
|
||||
m2o: {string: "M2O field", type: "many2one", relation: "bar"},
|
||||
o2m: {string: "O2M field", type: "one2many", relation: "bar"},
|
||||
@@ -1222,6 +1224,59 @@ QUnit.module('Views', {
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('row height should not change when switching mode', async function (assert) {
|
||||
// Warning: this test is css dependant
|
||||
assert.expect(3);
|
||||
|
||||
var multiLang = _t.database.multi_lang;
|
||||
_t.database.multi_lang = true;
|
||||
|
||||
this.data.foo.fields.foo.translate = true;
|
||||
this.data.foo.fields.boolean = {type: 'boolean', string: 'Bool'};
|
||||
var currencies = {};
|
||||
_.each(this.data.res_currency.records, function (currency) {
|
||||
currencies[currency.id] = currency;
|
||||
});
|
||||
|
||||
var list = await createView({
|
||||
View: ListView,
|
||||
model: 'foo',
|
||||
data: this.data,
|
||||
arch: '<tree editable="top">' +
|
||||
'<field name="foo" required="1"/>' +
|
||||
'<field name="int_field" readonly="1"/>' +
|
||||
'<field name="boolean"/>' +
|
||||
'<field name="date"/>' +
|
||||
'<field name="text" width_factor="2"/>' +
|
||||
'<field name="amount"/>' +
|
||||
'<field name="currency_id" invisible="1"/>' +
|
||||
'<field name="m2o"/>' +
|
||||
'<field name="m2m" widget="many2many_tags"/>' +
|
||||
'</tree>',
|
||||
session: {
|
||||
currencies: currencies,
|
||||
},
|
||||
});
|
||||
var startHeight = list.$('.o_data_row:first').height();
|
||||
|
||||
// start edition of first row
|
||||
await testUtils.dom.click(list.$('.o_data_row:first > td:not(.o_list_record_selector)').first());
|
||||
|
||||
assert.hasClass(list.$('.o_data_row:first'), 'o_selected_row');
|
||||
var editionHeight = list.$('.o_data_row:first').height();
|
||||
|
||||
// leave edition
|
||||
await testUtils.dom.click(list.$buttons.find('.o_list_button_save'));
|
||||
|
||||
var readonlyHeight = list.$('.o_data_row:first').height();
|
||||
|
||||
assert.strictEqual(startHeight, editionHeight);
|
||||
assert.strictEqual(startHeight, readonlyHeight);
|
||||
|
||||
_t.database.multi_lang = multiLang;
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('deleting one record', async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user