diff options
| -rw-r--r-- | addons/account/static/src/js/section_and_note_fields_backend.js | 4 | ||||
| -rw-r--r-- | addons/web/static/src/js/core/dom.js | 2 | ||||
| -rw-r--r-- | addons/web/static/src/js/fields/basic_fields.js | 14 | ||||
| -rw-r--r-- | addons/web/static/src/js/fields/field_registry.js | 1 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/list/list_editable_renderer.js | 9 | ||||
| -rw-r--r-- | addons/web/static/src/scss/list_view.scss | 140 | ||||
| -rw-r--r-- | addons/web/static/tests/views/list_tests.js | 55 |
7 files changed, 131 insertions, 94 deletions
diff --git a/addons/account/static/src/js/section_and_note_fields_backend.js b/addons/account/static/src/js/section_and_note_fields_backend.js index 1f232ac0db8..8d9ab57cee4 100644 --- a/addons/account/static/src/js/section_and_note_fields_backend.js +++ b/addons/account/static/src/js/section_and_note_fields_backend.js @@ -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); }; diff --git a/addons/web/static/src/js/core/dom.js b/addons/web/static/src/js/core/dom.js index e618b644eaa..81211788246 100644 --- a/addons/web/static/src/js/core/dom.js +++ b/addons/web/static/src/js/core/dom.js @@ -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, diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index 1c5c19ebcf8..5919f100bcc 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -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, diff --git a/addons/web/static/src/js/fields/field_registry.js b/addons/web/static/src/js/fields/field_registry.js index cd4c92087eb..ccd5dabdff1 100644 --- a/addons/web/static/src/js/fields/field_registry.js +++ b/addons/web/static/src/js/fields/field_registry.js @@ -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) diff --git a/addons/web/static/src/js/views/list/list_editable_renderer.js b/addons/web/static/src/js/views/list/list_editable_renderer.js index 687f046b7e6..040a186d4ee 100644 --- a/addons/web/static/src/js/views/list/list_editable_renderer.js +++ b/addons/web/static/src/js/views/list/list_editable_renderer.js @@ -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 diff --git a/addons/web/static/src/scss/list_view.scss b/addons/web/static/src/scss/list_view.scss index fbd12c7e8a4..16f14f3717e 100644 --- a/addons/web/static/src/scss/list_view.scss +++ b/addons/web/static/src/scss/list_view.scss @@ -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; + // 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_many2manytags > .badge { - &, > .o_badge_text { - white-space: normal; - max-width: 100%; // Fix a chrome bug where badge would not break on multiple lines - } - } - } - > .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; diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index 2584c047508..d4efb3154a4 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -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); |
