diff options
| author | Kishan Gajjar <kig@odoo.com> | 2019-01-24 10:29:28 +0000 |
|---|---|---|
| committer | Xavier Morel <xmo@odoo.com> | 2019-08-12 13:38:31 +0000 |
| commit | 22b598bc1b8331907ba0ce77739b64c48d6fcbbd (patch) | |
| tree | 527b726ebfccb01fc7677107d3081523faeee031 /addons/web/static | |
| parent | b6193a6b8d569d9d3a9fb4abe1590012782d03c2 (diff) | |
[IMP] web: export improvements
* remove center buttons, corresponding features either moved to fields
list (handle / bin on exported, etc...) or removed
entirely (e.g. clear)
* replace XLS to XLSX format, increases rows# to 1048579
* add FAYT filtering of available fields
* sort available fields alphabetically (CI) always
* change export templates UI: remove buttons
TaskID: 1910953
closes odoo/odoo#32339
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
Diffstat (limited to 'addons/web/static')
| -rw-r--r-- | addons/web/static/src/js/widgets/data_export.js | 257 | ||||
| -rw-r--r-- | addons/web/static/src/scss/data_export.scss | 118 | ||||
| -rw-r--r-- | addons/web/static/src/xml/base.xml | 113 | ||||
| -rw-r--r-- | addons/web/static/tests/widgets/data_export_tests.js | 112 |
4 files changed, 328 insertions, 272 deletions
diff --git a/addons/web/static/src/js/widgets/data_export.js b/addons/web/static/src/js/widgets/data_export.js index 9031dd43232..975cc99e35c 100644 --- a/addons/web/static/src/js/widgets/data_export.js +++ b/addons/web/static/src/js/widgets/data_export.js @@ -19,15 +19,14 @@ var DataExport = Dialog.extend({ 'click .o_add_field': '_onClickAddField', 'click .o_delete_exported_list': '_onClickDeleteExportListBtn', 'click .o_expand': '_onClickExpand', - 'click .o_move_down': '_onClickMovedown', - 'click .o_move_up': '_onClickMoveup', - 'click .o_remove_all_field': '_onClickRemoveAllFields', 'click .o_remove_field': '_onClickRemoveField', - 'click .o_toggle_save_list': '_onClickSaveList', - 'click .o_save_list > button': '_onClickSaveListBtn', + 'click .o_save_list .o_save_list_btn': '_onClickSaveListBtn', + 'click .o_save_list .o_cancel_list_btn': '_resetTemplateField', 'click .o_export_tree_item': '_onClickTreeItem', 'dblclick .o_export_tree_item:not(.haschild)': '_onDblclickTreeItem', - 'keydown .o_export_tree_item': '_onKeydownTreeItem' + 'keydown .o_export_tree_item': '_onKeydownTreeItem', + 'keydown .o_save_list_name': '_onKeydownSaveList', + 'input .o_export_search_input': '_onSearchInput', }, /** * @constructor @@ -39,7 +38,7 @@ var DataExport = Dialog.extend({ var options = { title: _t("Export Data"), buttons: [ - {text: _t("Export To File"), click: this._exportData, classes: 'btn-primary'}, + {text: _t("Export"), click: this._exportData, classes: 'btn-primary'}, {text: _t("Close"), close: true}, ], }; @@ -65,7 +64,6 @@ var DataExport = Dialog.extend({ this.$modal.find('.modal-content').css('height', '100%'); this.$fieldsList = this.$('.o_fields_list'); - this.$importCompatRadios = this.$('.o_import_compat input'); proms.push(this._rpc({route: '/web/export/formats'}).then(doSetupExportFormats)); proms.push(this._onChangeCompatibleInput()); @@ -81,29 +79,37 @@ var DataExport = Dialog.extend({ })); proms.push(this._showExportsList().then(function () { - _.each(self.records, function (record, key) { - if (_.contains(self.defaultExportFields, key)) { - self._addField(key, record.string); - } + _.each(self.defaultExportFields, function (field) { + var record = self.records[field]; + self._addField(record.id, record.string); }); })); + // Bind sortable events after Dialog is open + this.opened().then(function () { + self.$('.o_fields_list').sortable({ + axis: 'y', + handle: '.o_short_field', + forcePlaceholderSize: true, + placeholder: 'o-field-placeholder', + update: self.proxy('_resetTemplateField'), + }); + }); return Promise.all(proms); function doSetupExportFormats(formats) { var $fmts = self.$('.o_export_format'); _.each(formats, function (format) { - var $radio = $('<input/>', {type: 'radio', value: format.tag, name: 'o_export_format_name'}); - var $label = $('<span/>', {html: format.label}); + var $radio = $('<input/>', {type: 'radio', value: format.tag, name: 'o_export_format_name', class: 'form-check-input', id: 'o_radio' + format.label}); + var $label = $('<label/>', {html: format.label, class: 'form-check-label', for: 'o_radio' + format.label}); if (format.error) { $radio.prop('disabled', true); $label.html(_.str.sprintf("%s — %s", format.label, format.error)); } - var $radioButton = $('<label/>').append($radio, $label); - $fmts.append($("<div class='radio'></div>").append($radioButton)); + $fmts.append($("<div class='radio form-check form-check-inline pl-4'></div>").append($radio, $label)); }); self.$exportFormatInputs = $fmts.find('input'); @@ -124,22 +130,27 @@ var DataExport = Dialog.extend({ */ _addField: function (fieldID, label) { var $fieldList = this.$('.o_fields_list'); - fieldID = this.records[fieldID].value || fieldID; - if ($fieldList.find('option[value="' + fieldID + '"]').length === 0) { - $fieldList.append(new Option(label, fieldID)); + if (!$fieldList.find(".o_export_field[data-field_id='" + fieldID + "']").length) { + $fieldList.append( + $('<li>', {'class': 'o_export_field', 'data-field_id': fieldID}).append( + $('<span>', {'class': "fa fa-arrows o_short_field mx-1"}), + label, + $('<span>', {'class': 'fa fa-trash m-1 pull-right o_remove_field', 'title': _t("Remove field")}) + ) + ); } }, + /** * Submit the user data and export the file * * @private */ _exportData: function () { - var self = this; - var exportedFields = this.$('.o_fields_list option').map(function () { + var exportedFields = this.$('.o_export_field').map(function () { return { - name: (self.records[this.value] || this).value, - label: this.textContent || this.innerText, // DOM property is textContent, but IE8 only knows innerText + name: $(this).data('field_id'), + label: this.textContent }; }).get(); @@ -147,7 +158,7 @@ var DataExport = Dialog.extend({ Dialog.alert(this, _t("Please select fields to export...")); return; } - if (!this.isCompatibleMode) { + if (this.isCompatibleMode) { exportedFields.unshift({ name: 'id', label: _t('External ID') }); } @@ -163,7 +174,7 @@ var DataExport = Dialog.extend({ ids: this.idsToExport, domain: this.domain, context: pyUtils.eval('contexts', [this.record.getContext()]), - import_compat: !!this.$importCompatRadios.filter(':checked').val(), + import_compat: this.isCompatibleMode, }) }, complete: framework.unblockUI, @@ -175,8 +186,8 @@ var DataExport = Dialog.extend({ * @returns {string[]} exportFields */ _getFields: function () { - var exportFields = this.$('.o_fields_list option').map(function () { - return $(this).val(); + var exportFields = this.$('.o_export_field').map(function () { + return $(this).data('field_id'); }).get(); if (exportFields.length === 0) { Dialog.alert(this, _t("Please select fields to save export list...")); @@ -211,7 +222,7 @@ var DataExport = Dialog.extend({ model: model, prefix: prefix, parent_name: name, - import_compat: !!this.$importCompatRadios.filter(':checked').val(), + import_compat: this.isCompatibleMode, parent_field_type: record.field_type, parent_field: record.params.parent_field, exclude: excludeFields, @@ -254,6 +265,31 @@ var DataExport = Dialog.extend({ var $el = $(el); $el.find('.o_tree_column').first().toggleClass('o_required', !!self.records[$el.data('id')].required); }); + this.$('#o-export-search-filter').val(''); + }, + /** + * @private + */ + _addNewTemplate: function () { + this.$('.o_exported_lists').addClass('d-none'); + + this.$(".o_save_list") + .show() + .find(".o_save_list_name") + .val("") + .focus(); + }, + /** + * @private + */ + _resetTemplateField: function () { + this.$('.o_exported_lists_select').val(""); + this.$('.o_delete_exported_list').addClass('d-none'); + this.$('.o_exported_lists').removeClass('d-none'); + + this.$(".o_save_list") + .hide() + .find(".o_save_list_name").val(""); }, /** * If relational fields info is already fetched then this method is @@ -283,6 +319,7 @@ var DataExport = Dialog.extend({ $child.show(); } } + this.$('#o-export-search-filter').val(''); }, /** * Fetches the saved export list for the current model @@ -303,9 +340,6 @@ var DataExport = Dialog.extend({ fields: ['name'], domain: [['resource', '=', this.record.model]] }).then(function (exportList) { - if (!exportList.length) { - return; - } self.$('.o_exported_lists').append(QWeb.render('Export.SavedList', { existing_exports: exportList, })); @@ -323,9 +357,10 @@ var DataExport = Dialog.extend({ */ _onChangeExportList: function () { var self = this; - this.$fieldsList.empty(); var exportID = this.$('.o_exported_lists_select option:selected').val(); - if (exportID) { + this.$('.o_delete_exported_list').toggleClass('d-none', !exportID); + if (exportID && exportID !== 'new_template') { + this.$('.o_fields_list').empty(); this._rpc({ route: '/web/export/namelist', params: { @@ -334,9 +369,11 @@ var DataExport = Dialog.extend({ }, }).then(function (fieldList) { _.each(fieldList, function (field) { - self.$fieldsList.append(new Option(field.label, field.name)); + self._addField(field.name, field.label); }); }); + } else if (exportID === 'new_template') { + self._addNewTemplate(); } }, /** @@ -345,10 +382,10 @@ var DataExport = Dialog.extend({ */ _onChangeCompatibleInput: function () { var self = this; - this.isCompatibleMode = this.$('.o_import_compat input[value="yes"]').is(':checked'); + this.isCompatibleMode = this.$('.o_import_compat input').is(':checked'); this.$('.o_field_tree_structure').remove(); - + this._resetTemplateField(); return this._rpc({ route: '/web/export/get_fields', params: { @@ -357,40 +394,33 @@ var DataExport = Dialog.extend({ }, }).then(function (records) { var compatibleFields = _.map(records, function (record) { return record.id; }); - self.$fieldsList - .find('option') - .filter(function () { - var optionField = $(this).attr('value'); - if (compatibleFields.indexOf(optionField) === -1) { - return true; - } - }) - .remove(); self._onShowData(records); - // In compatible mode add ID field as first field to export - if (self.isCompatibleMode) { - self.$('.o_fields_list').prepend(new Option(_t("External ID"), 'id')); - } - _.each(records, function (record) { - if (_.contains(self.defaultExportFields, record.id)) { - self._addField(record.id, record.string); - } + self.$('.o_fields_list').empty(); + + _.chain(self.$fieldsList.find('.o_export_field')) + .map(function (field) { return $(field).data('field_id'); }) + .union(self.defaultExportFields) + .intersection(compatibleFields) + .each(function (field) { + var record = _.find(records, function (rec) { + return rec.id === field; + }); + self._addField(record.id, record.string); }); + self.$('#o-export-search-filter').val(''); }); }, /** * Add a field to export list * * @private + * @param {Event} ev */ - _onClickAddField: function () { - var self = this; - this.$('.o_field_tree_structure .o_selected') - .removeClass('o_selected') - .each(function () { - var $this = $(this); - self._addField($this.data('id'), $this.children('.o_tree_column').text()); - }); + _onClickAddField: function(ev) { + ev.stopPropagation(); + var $field = $(ev.currentTarget); + this._resetTemplateField(); + this._addField($field.closest('.o_export_tree_item').data('id'), $field.closest('.o_tree_column').text()); }, /** * Delete selected export list item from the saved export list @@ -405,7 +435,6 @@ var DataExport = Dialog.extend({ if (selectExp.val()) { self.exports.unlink([parseInt(selectExp.val(), 10)]); selectExp.remove(); - self.$fieldsList.empty(); if (self.$('.o_exported_lists_select option').length <= 1) { self.$('.o_exported_lists').hide(); } @@ -422,59 +451,14 @@ var DataExport = Dialog.extend({ this._onExpandAction(this.records[$(ev.target).closest('.o_export_tree_item').data('id')]); }, /** - * @private - */ - _onClickMovedown: function () { - var $selectedRows = this.$fieldsList.find('option:selected'); - var $nextRow = $selectedRows.last().next(); - if ($nextRow.length) { - $nextRow.after($selectedRows.detach()); - } - }, - /** - * @private - */ - _onClickMoveup: function () { - var $selectedRows = this.$fieldsList.find('option:selected'); - var $prevRow = $selectedRows.first().prev(); - if ($prevRow.length) { - $prevRow.before($selectedRows.detach()); - } - }, - /** - * Remove all fields from export field list - * - * @private - */ - _onClickRemoveAllFields: function () { - this.$fieldsList.empty(); - }, - /** * Remove selected field from export field list * * @private - */ - _onClickRemoveField: function () { - this.$fieldsList.find('option:selected').remove(); - }, - /** - * @private * @param {Event} ev */ - _onClickSaveList: function (ev) { - ev.preventDefault(); - - var $saveList = this.$('.o_save_list'); - if ($saveList.is(':empty')) { - $saveList.append(QWeb.render('Export.SaveList')); - } else { - if ($saveList.is(':hidden')) { - $saveList.show(); - $saveList.find('.o_export_list_input').val(''); - } else { - $saveList.hide(); - } - } + _onClickRemoveField: function (ev) { + $(ev.currentTarget).closest('.o_export_field').remove(); + this._resetTemplateField(); }, /** * This method will create a record in 'ir.exports' model with list of @@ -514,11 +498,13 @@ var DataExport = Dialog.extend({ self._showExportsList(); } $select.append(new Option(value, exportListID)); + self.$('.o_exported_lists').removeClass('d-none'); + $select.val(exportListID); }); }, /** * @private - * @param {Event} ev + * @param ev */ _onClickTreeItem: function (ev) { ev.stopPropagation(); @@ -546,7 +532,7 @@ var DataExport = Dialog.extend({ } else if (ev.shiftKey) { $elem.addClass('o_selected').focus(); } else { - this.$('.o_selected').removeClass('o_selected') + this.$('.o_selected').removeClass('o_selected'); $elem.addClass('o_selected').focus(); } @@ -567,6 +553,7 @@ var DataExport = Dialog.extend({ */ _onDblclickTreeItem: function (ev) { var self = this; + this._resetTemplateField(); function addElement(el) { self._addField(el.getAttribute('data-id'), el.querySelector('.o_tree_column').textContent); } @@ -580,10 +567,19 @@ var DataExport = Dialog.extend({ addElement(target); }, /** + * @private + * @param ev + */ + _onKeydownSaveList: function (ev) { + if (ev.keyCode === $.ui.keyCode.ENTER) { + this._onClickSaveListBtn(); + } + }, + /** * Handles the keyboard navigation for the fields * * @private - * @param {Event} ev + * @param ev */ _onKeydownTreeItem: function (ev) { ev.stopPropagation(); @@ -636,6 +632,41 @@ var DataExport = Dialog.extend({ break; } }, + /** + * Search fields from a field list. + * + * @private + */ + _onSearchInput: function (ev) { + var searchText = $(ev.currentTarget).val().trim().toUpperCase(); + if (!searchText) { + this.$('.o_no_match').remove(); + this.$(".o_export_tree_item").show(); + this.$(".o_export_tree_item.haschild:not(.show) .o_export_tree_item").hide(); + return; + } + + var matchItems = this.$(".o_tree_column").filter(function () { + return this.innerText.toUpperCase().indexOf(searchText) >= 0; + }).parent(); + this.$(".o_export_tree_item").hide(); + if (matchItems.length) { + this.$('.o_no_match').remove(); + _.each(matchItems, function (col) { + var $col = $(col); + $col.show(); + $col.parents('.haschild.show').show(); + if (!$col.parent().hasClass('show') && !$col.parent().hasClass('o_field_tree_structure')) { + $col.hide(); + } + }); + } else if (!this.$('.o_no_match').length) { + this.$(".o_field_tree_structure").append($("<h3/>", { + class: 'text-center text-muted mt-5 o_no_match', + text: _t("No match found.") + })); + } + }, }); return DataExport; diff --git a/addons/web/static/src/scss/data_export.scss b/addons/web/static/src/scss/data_export.scss index c03d13b355b..f52bcf10383 100644 --- a/addons/web/static/src/scss/data_export.scss +++ b/addons/web/static/src/scss/data_export.scss @@ -1,90 +1,52 @@ .o_web_client .o_export { - flex: 1 1 auto; - display: flex; - flex-flow: column nowrap; - min-height: 0; - - > p, > .row { - flex: 0 0 auto; + padding: 20px; + .o-export-panel { + height: 100%; } - - .o_export_panel { - flex: 1 1 auto; - position: relative; - min-height: 0; - - > div { - height: 100%; - display: flex; - flex-flow: column nowrap; - justify-content: center; - - &.o_left_panel { // Needed because of chrome - @include o-position-absolute($top: 0, $bottom: 0, $left: 0, $right: 57%); + .o_left_panel { + padding-right: 10px; + flex-flow: column nowrap; + height: 100%; + .o_left_field_panel { + flex: 1; + overflow: auto; + } + .o_export_tree_item { + cursor: pointer; + position: relative; + padding-left: 20px; + &.o_selected > .o_tree_column { + background-color: $o-brand-odoo; + color: white; } - &.o_center_panel { // Needed because of chrome - @include o-position-absolute($top: 0, $bottom: 0, $left: 43%, $right: 43%); + .o_expand_parent { + @include o-position-absolute($top: 4px, $left: 5px); + font-size: 10px; } - &.o_right_panel { // Needed because of chrome - @include o-position-absolute($top: 0, $bottom: 0, $left: 57%, $right: 0); + .o_required { + border-bottom: 2px solid $o-main-text-color; } + } + } - > h4, > div { - flex: 0 0 auto; + .o_right_panel { + padding-left: 10px; + flex-flow: column nowrap; + height: 100%; + .o_save_list { + display: none; + } + .o_right_field_panel { + flex: 1; + overflow: auto; + .o_short_field { + cursor: ns-resize; } - - > button { - flex: 0 0 auto; - margin-top: 10px; + .o-field-placeholder { + border: 1px dashed $o-brand-primary; } - - .o_left_field_panel, .o_fields_list { - border: 1px solid $o-brand-lightsecondary; - overflow: auto; - flex: 1 1 auto; - min-height: 0; - } - - .o_export_tree_item { + .o_remove_field { cursor: pointer; - position: relative; - padding-left: 20px; - user-select: none; - - &.o_selected > .o_tree_column { - background-color: $o-brand-odoo; - color: white; - } - - .o_expand_parent { - @include o-position-absolute($top: 4px, $left: 5px); - font-size: 10px; - } - .o_tree_column { - display: block; - - &.o_required { - border-bottom: 2px solid $o-main-text-color; - } - } - - &:focus { - outline: none; - } - } - - .o_save_list, .o_exported_lists { - display: flex; - align-items: baseline; - - > label, > button { - margin: 0 0 5px; - flex: 0 0 auto; - } - > input, > select { - width: 0; - flex: 1 0 auto; - } } } } diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index c342c24441f..1b319f6b3e5 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1279,79 +1279,76 @@ </div> <div t-name="ExportDialog" class="o_export"> - <div class="row"> - <div class="col-lg-6"> - <strong>What do you want to do?</strong> - <div class="o_import_compat"> - <div class="radio"> - <label> - <input type="radio" name="o_import_compat_name" value="" checked="checked"/> - Use data in a spreadsheet (export all data) - </label> - </div> - <div class="radio"> - <label> - <input type="radio" name="o_import_compat_name" value="yes"/> - Update data (import-compatible export) - </label> - </div> + <div class="row o-export-panel no-gutters"> + <div class="col-6 d-flex o_left_panel"> + <div class="form-check o_import_compat"> + <input class="form-check-input" id="o-update-data" type="checkbox"/> + <label class="form-check-label" for="o-update-data"> + I want to update data (import-compatible export) + </label> </div> + <div class="mt-3"> + <h4>Available fields</h4> + </div> + <div class="input-group mb-3"> + <input type="search" class="form-control o_export_search_input" id="o-export-search-filter" placeholder="Search" /> + </div> + <div class="border o_left_field_panel"/> </div> - <div class="col-lg-6"> - <strong>Export Format:</strong> - <div class="o_export_format"/> - </div> - </div> - - <div class="o_export_panel"> - <div class="o_left_panel"> - <h4>Available fields</h4> - <div class="o_left_field_panel"/> - </div> - <div class="o_center_panel"> - <button type="button" class="btn btn-secondary o_add_field">Add</button> - <button type="button" class="btn btn-secondary o_remove_field">Remove</button> - <button type="button" class="btn btn-secondary o_remove_all_field">Remove All</button> - <button type="button" class="btn btn-secondary o_move_up">Move Up</button> - <button type="button" class="btn btn-secondary o_move_down">Move Down</button> - </div> - <div class="o_right_panel"> - <h4> - <a href="#" class="float-right o_toggle_save_list">Save fields list</a> - Fields to export - </h4> - <div class="o_save_list"/> + <div class="col-6 d-flex o_right_panel"> + <div class="o_export_format"> + <strong>Export Format:</strong> + </div> + <div class="mt-3"> + <h4>Fields to export</h4> + </div> + <div class="o_save_list"> + <t t-call="Export.SaveList"/> + </div> <div class="o_exported_lists"/> - <select class="o_fields_list" multiple="multiple"></select> + <div class="border o_right_field_panel"> + <ul class="o_fields_list list-unstyled"/> + </div> </div> </div> </div> <t t-name="Export.TreeItems"> <t t-foreach="fields" t-as="field"> <t t-set="has_child" t-value="field.children && (field.id).split('/').length != 3" /> - <div t-att-data-id="field.id" t-attf-class="o_export_tree_item #{has_child ? 'haschild' : ''}" tabindex="-1" role="treeitem"> <!-- tabindex make the div focusable --> - <t t-if="has_child"> - <span class="o_expand o_expand_parent fa fa-chevron-right" role="img" aria-label="Expand parents" title="Expand parents"/> - <span class="o_tree_column o_expand" t-att-title="debug and field.id or None"><t t-esc="field.string"/></span> - </t> - <t t-else=""> - <span class="o_tree_column" t-att-title="debug and field.id or None"><t t-esc="field.string"/></span> - </t> + <div t-att-data-id="field.id" t-attf-class="o_export_tree_item #{has_child ? 'haschild o_expand' : ''}" tabindex="-1" role="treeitem"> <!-- tabindex make the div focusable --> + <span t-if="has_child" class="o_expand_parent fa fa-chevron-right" role="img" aria-label="Show sub-fields" title="Show sub-fields"/> + <div t-attf-class="o_tree_column" t-att-title="debug and field.id or None"> + <t t-esc="field.string"/> + <span title="Select field" class="fa fa-plus pull-right m-1 o_add_field"/> + </div> </div> </t> </t> <t t-name="Export.SaveList"> - <label>Save as:</label> <input type="text"/><button type="button" class="btn btn-secondary">Ok</button> + <div class="input-group mb-3"> + <label class="pt-2 font-weight-bold">Save as : </label> + <input class="form-control ml-4 o_save_list_name" + placeholder="New template"/> + <button type="button" class="btn btn-secondary ml-1 o_save_list_btn"><i class="fa fa-floppy-o"/></button> + <button type="button" class="btn btn-secondary ml-1 o_cancel_list_btn"><i class="fa fa-times"/></button> + </div> </t> <t t-name="Export.SavedList"> - <label>Saved exports: </label> - <select class="o_exported_lists_select"> - <option></option> - <t t-foreach="existing_exports" t-as="export"> - <option t-att-value="export.id"><t t-esc="export.name"/></option> - </t> - </select> - <button type="button" class="btn btn-secondary o_delete_exported_list">Delete</button> + <div class="input-group mb-3"> + <label class="pt-2 mb-0 font-weight-bold">Template: </label> + <select class="form-control ml-4 o_exported_lists_select"> + <option></option> + <option class="font-italic" value="new_template">New template</option> + <t t-foreach="existing_exports" t-as="export"> + <option t-att-value="export.id"> + <t t-esc="export.name"/> + </option> + </t> + </select> + <button type="button" class="btn btn-secondary d-none ml-1 o_delete_exported_list"> + <i class="fa fa-trash"/> + </button> + </div> </t> <t t-name="Throbber"> diff --git a/addons/web/static/tests/widgets/data_export_tests.js b/addons/web/static/tests/widgets/data_export_tests.js index d9453d70405..6d29e8e888d 100644 --- a/addons/web/static/tests/widgets/data_export_tests.js +++ b/addons/web/static/tests/widgets/data_export_tests.js @@ -36,7 +36,7 @@ QUnit.module('widgets', { QUnit.test('exporting all data in list view', async function (assert) { - assert.expect(6); + assert.expect(8); var blockUI = framework.blockUI; var unblockUI = framework.unblockUI; @@ -75,12 +75,12 @@ QUnit.module('widgets', { children: true, }, { children: false, - field_type: 'text', - id: "note", + field_type: 'char', + id: "foo", relation_field: null, required: false, - string: 'Description', - value: "note", + string: 'Foo', + value: "foo", } ]); } @@ -99,15 +99,15 @@ QUnit.module('widgets', { await testUtils.dom.click(list.sidebar.$('a:contains(Export)')); assert.strictEqual($('.modal').length, 1, "a modal dialog should be open"); - assert.strictEqual($('span.o_tree_column:contains(Activities)').length, 1, + assert.strictEqual($('div.o_tree_column:contains(Activities)').length, 1, "the Activities field should be in the list of exportable fields"); + assert.strictEqual($('.modal .o_export_field').length, 1, "There should be only one export field"); + assert.strictEqual($('.modal .o_export_field').data('field_id'), 'foo', "There should be only one export field"); // select the field Description, click on add, then export and close - await testUtils.dom.click($('.modal span:contains(Description)')); - await testUtils.dom.click($('.modal .o_add_field')); - await testUtils.dom.click($('.modal span:contains(Export To File)')); + await testUtils.dom.click($('.modal .o_tree_column:contains(Foo) .o_add_field')); + await testUtils.dom.click($('.modal span:contains(Export)')); await testUtils.dom.click($('.modal span:contains(Close)')); - list.destroy(); framework.blockUI = blockUI; framework.unblockUI = unblockUI; @@ -119,7 +119,7 @@ QUnit.module('widgets', { }); QUnit.test('saving fields list when exporting data', async function (assert) { - assert.expect(5); + assert.expect(4); var create = data.DataSet.prototype.create; @@ -154,7 +154,15 @@ QUnit.module('widgets', { params: {"model": "mail.activity", "prefix": "activity_ids", "name": "Activities"}, relation_field: "res_id", children: true, - }, + }, { + children: false, + field_type: 'char', + id: "foo", + relation_field: null, + required: false, + string: 'Foo', + value: "foo", + } ]); } return this._super.apply(this, arguments); @@ -169,17 +177,14 @@ QUnit.module('widgets', { "a modal dialog should be open"); // Select 'Activities' in fields to export - assert.strictEqual($('.modal select.o_fields_list option').length, 0, - "the fields list should be empty"); - await testUtils.dom.click($('.modal .o_export_tree_item:contains(Activities)')); - await testUtils.dom.click($('.modal button:contains(Add)')); - assert.strictEqual($('.modal select.o_fields_list option').length, 1, - "there should be one item in the fields list"); - - // Save fields list - await testUtils.dom.click($('.modal a:contains(Save fields list)')); - await testUtils.fields.editInput($('.modal .o_save_list > input'), 'fields list'); - await testUtils.dom.click($('.modal .o_save_list > button')); + await testUtils.dom.click($('.modal .o_export_tree_item:contains(Activities) .o_add_field')); + assert.strictEqual($('.modal .o_fields_list .o_export_field').length, 2, + "there should be two items in the fields list"); + // Save as template + await testUtils.fields.editAndTrigger($('.modal .o_exported_lists_select'), 'new_template', ['change']); + await testUtils.fields.editInput($('.modal .o_save_list .o_save_list_name'), 'fields list'); + await testUtils.dom.click($('.modal .o_save_list .o_save_list_btn')); + assert.verifySteps(['create'], "create should have been called"); @@ -191,6 +196,67 @@ QUnit.module('widgets', { data.DataSet.prototype.create = create; }); + QUnit.test('Export dialog UI test', async function (assert) { + assert.expect(4); + var list = await createView({ + View: ListView, + model: 'partner', + data: this.data, + arch: '<tree><field name="foo"/></tree>', + viewOptions: { + hasSidebar: true, + }, + mockRPC: function (route) { + if (route === '/web/export/formats') { + return Promise.resolve([ + {tag: 'csv', label: 'CSV' }, + {tag: 'xls', label: 'Excel' }, + ]); + } + if (route === '/web/export/get_fields') { + return Promise.resolve([ + { + field_type: "one2many", + string: "Activities", + required: false, + value: "activity_ids/id", + id: "activity_ids", + params: {"model": "mail.activity", "prefix": "activity_ids", "name": "Activities" }, + relation_field: "res_id", + children: true, + }, { + children: false, + field_type: 'char', + id: "foo", + relation_field: null, + required: false, + string: 'Foo', + value: "foo", + } + ]); + } + return this._super.apply(this, arguments); + }, + }); + // Open the export modal + await testUtils.dom.click(list.$('.o_data_row .o_list_record_selector input')); + await testUtils.dom.click(list.sidebar.$('.o_dropdown_toggler_btn:contains(Action)')); + await testUtils.dom.click(list.sidebar.$('a:contains(Export)')); + + assert.strictEqual($('.modal .o_export_tree_item:visible').length, 2, "There should be only two items visible"); + await testUtils.dom.click($('.modal .o_export_search_input')); + $('.modal .o_export_search_input').val('Activities').trigger($.Event('input', { + keyCode: 65, + })); + assert.strictEqual($('.modal .o_export_tree_item:visible').length, 1, "Only match item visible"); + // Add field + await testUtils.dom.click($('.modal div:contains(Activities) .o_add_field')); + assert.strictEqual($('.modal .o_fields_list li').length, 2, "There should be two fields in export field list."); + // Remove field + await testUtils.dom.click($('.modal .o_fields_list li:first .o_remove_field')); + assert.strictEqual($('.modal .o_fields_list li').length, 1, "There should be only one field in list"); + list.destroy(); + }); }); }); |
