summaryrefslogtreecommitdiff
path: root/addons/web
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web')
-rw-r--r--addons/web/static/src/js/fields/basic_fields.js47
-rw-r--r--addons/web/static/src/js/fields/field_registry.js3
-rw-r--r--addons/web/static/src/js/fields/relational_fields.js12
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_record.js13
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_renderer.js16
-rw-r--r--addons/web/static/src/js/views/list/list_editable_renderer.js4
-rw-r--r--addons/web/static/src/js/views/pivot/pivot_model.js1
-rw-r--r--addons/web/static/src/scss/fields.scss5
-rw-r--r--addons/web/static/src/scss/form_view.scss10
-rw-r--r--addons/web/static/src/xml/base.xml4
-rw-r--r--addons/web/static/tests/fields/basic_fields_tests.js86
-rw-r--r--addons/web/static/tests/fields/relational_fields_tests.js24
-rw-r--r--addons/web/static/tests/views/kanban_tests.js110
-rw-r--r--addons/web/static/tests/views/list_tests.js46
-rw-r--r--addons/web/static/tests/views/pivot_tests.js42
15 files changed, 341 insertions, 82 deletions
diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js
index dc75ba8ea2a..ee911b141a0 100644
--- a/addons/web/static/src/js/fields/basic_fields.js
+++ b/addons/web/static/src/js/fields/basic_fields.js
@@ -1331,7 +1331,9 @@ var CopyClipboard = {
*/
destroy: function () {
this._super.apply(this, arguments);
- this.clipboard.destroy();
+ if (this.clipboard) {
+ this.clipboard.destroy();
+ }
},
//--------------------------------------------------------------------------
@@ -1346,8 +1348,8 @@ var CopyClipboard = {
var $clipboardBtn = this.$('.o_clipboard_button');
$clipboardBtn.tooltip({title: _t('Copied !'), trigger: 'manual', placement: 'right'});
this.clipboard = new ClipboardJS($clipboardBtn[0], {
- text: function (_) {
- return self.value.trim();
+ text: function () {
+ return self.value.trim();
},
// Container added because of Bootstrap modal that give the focus to another element.
// We need to give to correct focus to ClipboardJS (see in ClipboardJS doc)
@@ -1363,40 +1365,31 @@ var CopyClipboard = {
});
});
},
-};
-
-var TextCopyClipboard = FieldText.extend(CopyClipboard, {
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
/**
* @override
*/
- _render: function() {
+ _render: function () {
this._super.apply(this, arguments);
this.$el.addClass('o_field_copy');
- this.$el.append($(qweb.render('CopyClipboardText')));
- this._initClipboard();
- }
-});
-
-var CharCopyClipboard = FieldChar.extend(CopyClipboard, {
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
+ },
/**
* @override
*/
- _render: function() {
+ _renderReadonly: function () {
this._super.apply(this, arguments);
- this.$el.addClass('o_field_copy');
- this.$el.append($(qweb.render('CopyClipboardChar')));
- this._initClipboard();
+ if (this.value) {
+ this.$el.append($(qweb.render(this.clipboardTemplate)));
+ this._initClipboard();
+ }
}
+};
+
+var TextCopyClipboard = FieldText.extend(CopyClipboard, {
+ clipboardTemplate: 'CopyClipboardText',
+});
+
+var CharCopyClipboard = FieldChar.extend(CopyClipboard, {
+ clipboardTemplate: 'CopyClipboardChar',
});
var AbstractFieldBinary = AbstractField.extend({
diff --git a/addons/web/static/src/js/fields/field_registry.js b/addons/web/static/src/js/fields/field_registry.js
index c94c315cfa6..4406231868f 100644
--- a/addons/web/static/src/js/fields/field_registry.js
+++ b/addons/web/static/src/js/fields/field_registry.js
@@ -44,8 +44,9 @@ registry
.add('priority', basic_fields.PriorityWidget)
.add('attachment_image', basic_fields.AttachmentImage)
.add('label_selection', basic_fields.LabelSelection)
+ .add('kanban_label_selection', basic_fields.LabelSelection) // deprecated, use label_selection
.add('state_selection', basic_fields.StateSelectionWidget)
- .add('kanban_state_selection', basic_fields.StateSelectionWidget)
+ .add('kanban_state_selection', basic_fields.StateSelectionWidget) // deprecated, use state_selection
.add('boolean_favorite', basic_fields.FavoriteWidget)
.add('boolean_button', basic_fields.FieldBooleanButton)
.add('boolean_toggle', basic_fields.BooleanToggle)
diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js
index 6005c9f1a66..b78485a144d 100644
--- a/addons/web/static/src/js/fields/relational_fields.js
+++ b/addons/web/static/src/js/fields/relational_fields.js
@@ -242,7 +242,7 @@ var FieldMany2One = AbstractField.extend({
_bindAutoComplete: function () {
var self = this;
// avoid ignoring autocomplete="off" by obfuscating placeholder, see #30439
- if (this.$input.attr('placeholder')) {
+ if ($.browser.chrome && this.$input.attr('placeholder')) {
this.$input.attr('placeholder', function (index, val) {
return val.split('').join('\ufeff');
});
@@ -1886,6 +1886,7 @@ var FieldMany2ManyTags = AbstractField.extend({
}
this.colorField = this.nodeOptions.color_field;
+ this.hasDropdown = false;
},
//--------------------------------------------------------------------------
@@ -1952,6 +1953,7 @@ var FieldMany2ManyTags = AbstractField.extend({
return {
colorField: this.colorField,
elements: elements,
+ hasDropdown: this.hasDropdown,
readonly: this.mode === "readonly",
};
},
@@ -2063,6 +2065,14 @@ var FormFieldMany2ManyTags = FieldMany2ManyTags.extend({
'mousedown .o_colorpicker a': '_onUpdateColor',
'mousedown .o_colorpicker .o_hide_in_kanban': '_onUpdateColor',
}),
+ /**
+ * @override
+ */
+ init: function () {
+ this._super.apply(this, arguments);
+
+ this.hasDropdown = !!this.colorField;
+ },
//--------------------------------------------------------------------------
// Handlers
diff --git a/addons/web/static/src/js/views/kanban/kanban_record.js b/addons/web/static/src/js/views/kanban/kanban_record.js
index 46f6964dd1c..0f4fc1edd1c 100644
--- a/addons/web/static/src/js/views/kanban/kanban_record.js
+++ b/addons/web/static/src/js/views/kanban/kanban_record.js
@@ -300,6 +300,7 @@ var KanbanRecord = Widget.extend({
// field's widgets point of view
// that dict being shared between records, we don't modify it
// in place
+ var self = this;
var attrs = Object.create(null);
_.each(this.fieldsInfo[field_name], function (value, key) {
if (_.str.startsWith(key, 't-att-')) {
@@ -310,11 +311,12 @@ var KanbanRecord = Widget.extend({
});
var options = _.extend({}, this.options, {attrs: attrs});
var widget = new Widget(this, field_name, this.state, options);
- var def = widget.replace($field);
+ var def = widget.replace($field).then(function () {
+ self._setFieldDisplay(widget.$el, field_name);
+ });
if (def.state() === 'pending') {
this.defs.push(def);
}
- this._setFieldDisplay(widget.$el, field_name);
return widget;
},
_processWidgets: function () {
@@ -324,12 +326,13 @@ var KanbanRecord = Widget.extend({
var Widget = widgetRegistry.get($field.attr('name'));
var widget = new Widget(self, self.state);
- var def = widget._widgetRenderAndInsert(function () {});
+ var def = widget._widgetRenderAndInsert(function () {}).then(function () {
+ widget.$el.addClass('o_widget');
+ $field.replaceWith(widget.$el);
+ });
if (def.state() === 'pending') {
self.defs.push(def);
}
- widget.$el.addClass('o_widget');
- $field.replaceWith(widget.$el);
});
},
/**
diff --git a/addons/web/static/src/js/views/kanban/kanban_renderer.js b/addons/web/static/src/js/views/kanban/kanban_renderer.js
index cfb05792d4c..61101d9194f 100644
--- a/addons/web/static/src/js/views/kanban/kanban_renderer.js
+++ b/addons/web/static/src/js/views/kanban/kanban_renderer.js
@@ -247,7 +247,6 @@ var KanbanRenderer = BasicRenderer.extend({
*/
updateState: function (state) {
this._setState(state);
- this._toggleNoContentHelper();
return this._super.apply(this, arguments);
},
@@ -386,11 +385,8 @@ var KanbanRenderer = BasicRenderer.extend({
var self = this;
var oldWidgets = this.widgets;
this.widgets = [];
- this.$el.empty();
var isGrouped = !!this.state.groupedBy.length;
- this.$el.toggleClass('o_kanban_grouped', isGrouped);
- this.$el.toggleClass('o_kanban_ungrouped', !isGrouped);
var fragment = document.createDocumentFragment();
// render the kanban view
this.defs = [];
@@ -399,15 +395,19 @@ var KanbanRenderer = BasicRenderer.extend({
} else {
this._renderUngrouped(fragment);
}
- this.$el.append(fragment);
- this._toggleNoContentHelper();
var defs = this.defs;
- return this._super.apply(this, arguments).then(function () {
+ delete this.defs;
+ defs.push(this._super.apply(this, arguments));
+ return $.when.apply($, defs).then(function () {
_.invoke(oldWidgets, 'destroy');
+ self.$el.empty();
+ self.$el.toggleClass('o_kanban_grouped', isGrouped);
+ self.$el.toggleClass('o_kanban_ungrouped', !isGrouped);
+ self.$el.append(fragment);
+ self._toggleNoContentHelper();
if (self._isInDom) {
_.invoke(self.widgets, 'on_attach_callback');
}
- return $.when.apply(null, defs);
});
},
/**
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 ab5e5036dfa..185a385a812 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
@@ -278,6 +278,8 @@ ListRenderer.include({
this.currentRow = null;
}
+ // destroy widgets first
+ this._destroyFieldWidgets(recordID);
// remove the row
var $row = this.$('.o_data_row:nth(' + rowIndex + ')');
if (this.state.count >= 4) {
@@ -285,8 +287,6 @@ ListRenderer.include({
} else {
$row.replaceWith(this._renderEmptyRow());
}
-
- this._destroyFieldWidgets(recordID);
},
/**
* Updates the already rendered row associated to the given recordID so that
diff --git a/addons/web/static/src/js/views/pivot/pivot_model.js b/addons/web/static/src/js/views/pivot/pivot_model.js
index 3f195c1ea24..88d0aee83b8 100644
--- a/addons/web/static/src/js/views/pivot/pivot_model.js
+++ b/addons/web/static/src/js/views/pivot/pivot_model.js
@@ -993,7 +993,6 @@ var PivotModel = AbstractModel.extend({
var groupBys;
var m;
-
for (var i = 0; i < rowGroupBys.length + 1; i++) {
for (var j = 0; j < colGroupBys.length + 1; j++) {
for (var k = 0; k < data[index].length; k++) {
diff --git a/addons/web/static/src/scss/fields.scss b/addons/web/static/src/scss/fields.scss
index 81a59743788..2ba631575f7 100644
--- a/addons/web/static/src/scss/fields.scss
+++ b/addons/web/static/src/scss/fields.scss
@@ -178,7 +178,7 @@
}
}
}
- }
+ }
.o_tag_error {
box-shadow: inset 0 0 0 2px #FF0000;
@@ -443,7 +443,10 @@
}
}
}
+}
+span.o_field_copy:empty {
+ border: none;
}
button.o_field_float_toggle {
diff --git a/addons/web/static/src/scss/form_view.scss b/addons/web/static/src/scss/form_view.scss
index 0645e00544e..812e6cbd6cc 100644
--- a/addons/web/static/src/scss/form_view.scss
+++ b/addons/web/static/src/scss/form_view.scss
@@ -81,16 +81,10 @@
.o_form_uri {
display: inline-block;
+ color: $link-color;
- color: $o-main-text-color;
- &:first-line {
- color: $link-color;
- }
&:hover {
- color: darken($o-main-text-color, 1%); // hack required on chrome
- &:first-line {
- color: darken($link-color, 15%);
- }
+ color: darken($link-color, 15%);
}
}
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index e11184ebb1e..131c897bd8c 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -848,11 +848,11 @@
<t t-foreach="elements" t-as="el">
<t t-set="color" t-value="el[colorField] || 0"/>
<t t-set="colornames" t-value="['No color', 'Red', 'Orange', 'Yellow', 'Light blue', 'Dark purple', 'Salmon pink', 'Medium blue', 'Dark blue', 'Fushia', 'Green', 'Purple']"/>
- <div t-attf-class="badge badge-pill dropdown o_tag_color_#{color}" t-att-data-color="color" t-att-data-index="el_index" t-att-data-id="el.id" t-attf-title="Tag color: #{colornames[color]}">
+ <div t-attf-class="badge badge-pill #{hasDropdown ? 'dropdown' : ''} o_tag_color_#{color}" t-att-data-color="color" t-att-data-index="el_index" t-att-data-id="el.id" t-attf-title="Tag color: #{colornames[color]}">
<t t-set="_badge_text">
<span class="o_badge_text" t-att-title="el.display_name"><span role="img" t-attf-aria-label="Tag color: #{colornames[color]}"/><t t-esc="el.display_name"/></span>
</t>
- <t t-if="colorField">
+ <t t-if="hasDropdown">
<a role="button" href="#" class="dropdown-toggle o-no-caret" data-toggle="dropdown" aria-expanded="false">
<t t-raw="_badge_text"/>
</a>
diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js
index b721861c4b6..3e69a66b308 100644
--- a/addons/web/static/tests/fields/basic_fields_tests.js
+++ b/addons/web/static/tests/fields/basic_fields_tests.js
@@ -1408,45 +1408,79 @@ QUnit.module('basic_fields', {
list.destroy();
});
- QUnit.module('copy_clipboard', {
- beforeEach: function () {
- this.data = {
- partner: {
- fields: {
- script_external: {string: "Script External", type: "text"},
- web_page: {string: "Web page link", type: "char"}
- },
- records: [{
- id: 1,
- script_external:'Random Text',
- web_page: 'web page links'
- },],
- },
- };
- }
- });
+ QUnit.module('CopyClipboard');
QUnit.test('Char & Text Fields: Copy to clipboard button', function (assert) {
assert.expect(2);
- var done = assert.async();
- testUtils.createAsyncView({
+
+ var form = createView({
View: FormView,
model: 'partner',
data: this.data,
arch: '<form string="Partners">' +
'<sheet>' +
'<div>' +
- '<field name="script_external" widget="CopyClipboardText"/>' +
- '<field name="web_page" widget="CopyClipboardChar"/>' +
+ '<field name="txt" widget="CopyClipboardText"/>' +
+ '<field name="foo" widget="CopyClipboardChar"/>' +
'</div>' +
'</sheet>' +
'</form>',
- }).then(function (form) {
- assert.containsOnce(form, '.o_clipboard_button.o_btn_text_copy',"Should have copy button on text type field");
- assert.containsOnce(form, '.o_clipboard_button.o_btn_char_copy',"Should have copy button on char type field");
- form.destroy();
- done();
+ res_id: 1,
+ });
+
+ assert.containsOnce(form, '.o_clipboard_button.o_btn_text_copy',"Should have copy button on text type field");
+ assert.containsOnce(form, '.o_clipboard_button.o_btn_char_copy',"Should have copy button on char type field");
+
+ form.destroy();
+ });
+
+ QUnit.test('CopyClipboard widget on unset field', function (assert) {
+ assert.expect(1);
+
+ this.data.partner.records[0].foo = false;
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '<form>' +
+ '<sheet>' +
+ '<group>' +
+ '<field name="foo" widget="CopyClipboardChar" />' +
+ '</group>' +
+ '</sheet>' +
+ '</form>',
+ res_id: 1,
+ });
+
+ assert.containsNone(form, '.o_field_copy[name="foo"] .o_clipboard_button',
+ "foo (unset) should not contain a button");
+
+ form.destroy();
+ });
+
+ QUnit.test('CopyClipboard widget on readonly unset fields in create mode', function (assert) {
+ assert.expect(1);
+
+ this.data.partner.fields.display_name.readonly = true;
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '<form>' +
+ '<sheet>' +
+ '<group>' +
+ '<field name="display_name" widget="CopyClipboardChar" />' +
+ '</group>' +
+ '</sheet>' +
+ '</form>',
});
+
+ assert.containsNone(form, '.o_field_copy[name="display_name"] .o_clipboard_button',
+ "the readonly unset field should not contain a button");
+
+ form.destroy();
});
QUnit.module('FieldText');
diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js
index faf3df6f2e8..5b3ff08a1b6 100644
--- a/addons/web/static/tests/fields/relational_fields_tests.js
+++ b/addons/web/static/tests/fields/relational_fields_tests.js
@@ -1257,6 +1257,30 @@ QUnit.module('relational_fields', {
form.destroy();
});
+ QUnit.test('fieldmany2many tags in tree view', function (assert) {
+ assert.expect(3);
+
+ this.data.partner.records[0].timmy = [12, 14];
+ var list = createView({
+ View: ListView,
+ model: 'partner',
+ data: this.data,
+ arch: '<tree string="Partners">' +
+ '<field name="timmy" widget="many2many_tags" options="{\'color_field\': \'color\'}"/>' +
+ '</tree>',
+ });
+ assert.containsN(list, '.o_field_many2manytags .badge', 2, "there should be 2 tags");
+ assert.containsNone(list, '.dropdown-toggle', "the tags should not be dropdowns");
+
+ testUtils.intercept(list, 'switch_view', function (event) {
+ assert.strictEqual(event.data.view_type, "form", "should switch to form view");
+ });
+ // click on the tag: should do nothing and open the form view
+ testUtils.dom.click(list.$('.o_field_many2manytags .badge:first'));
+
+ list.destroy();
+ });
+
QUnit.test('fieldmany2many tags view a domain', function (assert) {
assert.expect(7);
diff --git a/addons/web/static/tests/views/kanban_tests.js b/addons/web/static/tests/views/kanban_tests.js
index 47af910083d..862c579c347 100644
--- a/addons/web/static/tests/views/kanban_tests.js
+++ b/addons/web/static/tests/views/kanban_tests.js
@@ -5209,6 +5209,116 @@ QUnit.module('Views', {
delete fieldRegistry.map.asyncWidget;
});
+ QUnit.test('asynchronous rendering of a field widget with display attr', function (assert) {
+ assert.expect(3);
+
+ var fooFieldDef = $.Deferred();
+ var FieldChar = fieldRegistry.get('char');
+ fieldRegistry.add('asyncwidget', FieldChar.extend({
+ willStart: function () {
+ return fooFieldDef;
+ },
+ start: function () {
+ this.$el.html('LOADED');
+ },
+ }));
+
+ var kanbanController;
+ testUtils.createAsyncView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ arch: '<kanban class="o_kanban_test"><templates><t t-name="kanban-box">' +
+ '<div><field name="foo" display="right" widget="asyncwidget"/></div>' +
+ '</t></templates></kanban>',
+ }).then(function (kanban) {
+ kanbanController = kanban;
+ });
+
+ assert.containsNone(document.body, '.o_kanban_record');
+
+ fooFieldDef.resolve();
+ assert.strictEqual(kanbanController.$('.o_kanban_record').text(),
+ "LOADEDLOADEDLOADEDLOADED");
+ assert.hasClass(kanbanController.$('.o_kanban_record:first .o_field_char'), 'float-right');
+
+ kanbanController.destroy();
+ delete fieldRegistry.map.asyncWidget;
+ });
+
+ QUnit.test('asynchronous rendering of a widget', function (assert) {
+ assert.expect(2);
+
+ var widgetDef = $.Deferred();
+ widgetRegistry.add('asyncwidget', Widget.extend({
+ willStart: function () {
+ return widgetDef;
+ },
+ start: function () {
+ this.$el.html('LOADED');
+ },
+ }));
+
+ var kanbanController;
+ testUtils.createAsyncView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ arch: '<kanban class="o_kanban_test"><templates><t t-name="kanban-box">' +
+ '<div><widget name="asyncwidget"/></div>' +
+ '</t></templates></kanban>',
+ }).then(function (kanban) {
+ kanbanController = kanban;
+ });
+
+ assert.containsNone(document.body, '.o_kanban_record');
+
+ widgetDef.resolve();
+ assert.strictEqual(kanbanController.$('.o_kanban_record .o_widget').text(),
+ "LOADEDLOADEDLOADEDLOADED");
+
+ kanbanController.destroy();
+ delete widgetRegistry.map.asyncWidget;
+ });
+
+ QUnit.test('update kanban with asynchronous field widget', function (assert) {
+ assert.expect(3);
+
+ var fooFieldDef = $.Deferred();
+ var FieldChar = fieldRegistry.get('char');
+ fieldRegistry.add('asyncwidget', FieldChar.extend({
+ willStart: function () {
+ return fooFieldDef;
+ },
+ start: function () {
+ this.$el.html('LOADED');
+ },
+ }));
+
+ var kanban = testUtils.createView({
+ View: KanbanView,
+ model: 'partner',
+ data: this.data,
+ arch: '<kanban class="o_kanban_test"><templates><t t-name="kanban-box">' +
+ '<div><field name="foo" widget="asyncwidget"/></div>' +
+ '</t></templates></kanban>',
+ domain: [['id', '=', '0']], // no record matches this domain
+ });
+
+ assert.containsNone(kanban, '.o_kanban_record:not(.o_kanban_ghost)');
+
+ kanban.update({domain: []}); // this rendering will be async
+
+ assert.containsNone(kanban, '.o_kanban_record:not(.o_kanban_ghost)');
+
+ fooFieldDef.resolve();
+
+ assert.strictEqual(kanban.$('.o_kanban_record').text(),
+ "LOADEDLOADEDLOADEDLOADED");
+
+ kanban.destroy();
+ delete widgetRegistry.map.asyncWidget;
+ });
});
});
diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js
index f5e0088173b..347d0c23a53 100644
--- a/addons/web/static/tests/views/list_tests.js
+++ b/addons/web/static/tests/views/list_tests.js
@@ -10,6 +10,7 @@ var ListView = require('web.ListView');
var mixins = require('web.mixins');
var NotificationService = require('web.NotificationService');
var testUtils = require('web.test_utils');
+var testUtilsDom = require('web.test_utils_dom');
var widgetRegistry = require('web.widget_registry');
var Widget = require('web.Widget');
@@ -277,6 +278,51 @@ QUnit.module('Views', {
list.destroy();
});
+ QUnit.test('editable list datetimepicker destroy widget', function (assert) {
+ assert.expect(7);
+ var done = assert.async();
+
+ var list = createView({
+ View: ListView,
+ model: 'foo',
+ data: this.data,
+ arch: '<tree editable="top">' +
+ '<field name="date"/>' +
+ '</tree>',
+ });
+ list.$el.on({
+ 'show.datetimepicker': function () {
+ assert.equal($('.bootstrap-datetimepicker-widget').length, 1,
+ 'The datetimepicker is open');
+
+ assert.equal(list.$('.o_data_row').length, 5,
+ 'There should be 5 rows');
+
+ assert.equal(list.$('.o_selected_row').length, 1,
+ 'One row in edit mode');
+
+ list.$('input.o_datepicker_input').trigger($.Event('keydown', {which: $.ui.keyCode.ESCAPE}));
+
+ assert.equal(list.$('.o_data_row').length, 4,
+ 'There should be 4 rows');
+
+ assert.equal(list.$('.o_selected_row').length, 0,
+ 'No row should be in edit mode');
+
+ done();
+ }
+ });
+ assert.equal(list.$('.o_data_row').length, 4,
+ 'There should be 4 rows');
+
+ assert.equal(list.$('.o_selected_row').length, 0,
+ 'No row should be in edit mode');
+
+ testUtilsDom.click(list.$buttons.find('.o_list_button_add'));
+
+ list.destroy();
+ });
+
QUnit.test('at least 4 rows are rendered, even if less data', function (assert) {
assert.expect(1);
diff --git a/addons/web/static/tests/views/pivot_tests.js b/addons/web/static/tests/views/pivot_tests.js
index 0e7e7ff0a2d..ccbf24895dd 100644
--- a/addons/web/static/tests/views/pivot_tests.js
+++ b/addons/web/static/tests/views/pivot_tests.js
@@ -1096,6 +1096,48 @@ QUnit.module('Views', {
pivot.destroy();
});
+ QUnit.skip('Reload, group by columns, reload', function (assert) {
+ assert.expect(2);
+
+ var pivot = createView({
+ View: PivotView,
+ model: "partner",
+ data: this.data,
+ arch: '<pivot/>',
+ });
+
+ // Set a column groupby
+ pivot.$('thead .o_pivot_header_cell_closed').click();
+ pivot.$('.o_field_selection .dropdown-item[data-field=customer]').click();
+
+ // Set a domain
+ pivot.update({domain: [['product_id', '=', 41]]});
+
+ var expectedContext = {pivot_column_groupby: ['customer'],
+ pivot_measures: ['__count'],
+ pivot_row_groupby: []};
+
+ // Check that column groupbys were not lost
+ assert.deepEqual(pivot.getContext(), expectedContext,
+ 'Column groupby not lost after first reload');
+
+ // Set a column groupby
+ pivot.$('thead .o_pivot_header_cell_closed').click();
+ pivot.$('.o_field_selection .dropdown-item[data-field=product_id]').click();
+
+ // Set a domain
+ pivot.update({domain: [['product_id', '=', 37]]});
+
+ var expectedContext = {pivot_column_groupby: ['customer', 'product_id'],
+ pivot_measures: ['__count'],
+ pivot_row_groupby: []};
+
+ assert.deepEqual(pivot.getContext(), expectedContext,
+ 'Column groupby not lost after second reload');
+
+ pivot.destroy();
+ });
+
QUnit.test('correctly uses pivot_ keys from the context', function (assert) {
assert.expect(7);