diff --git a/addons/web/static/src/js/views/abstract_controller.js b/addons/web/static/src/js/views/abstract_controller.js index 58a011599d1..53bcc0d5bad 100644 --- a/addons/web/static/src/js/views/abstract_controller.js +++ b/addons/web/static/src/js/views/abstract_controller.js @@ -113,6 +113,9 @@ var AbstractController = mvc.Controller.extend(ActionMixin, { if (this._controlPanel) { this._controlPanel.on_attach_callback(); } + if (this._searchPanel) { + this._searchPanel.on_attach_callback(); + } this.renderer.on_attach_callback(); }, /** diff --git a/addons/web/static/src/js/views/search_panel.js b/addons/web/static/src/js/views/search_panel.js index 38872e8d381..f09ef071f79 100644 --- a/addons/web/static/src/js/views/search_panel.js +++ b/addons/web/static/src/js/views/search_panel.js @@ -95,6 +95,7 @@ var SearchPanel = Widget.extend({ }); this.initialState = params.state; + this.scrollTop = this.initialState && this.initialState.scrollTop || null; this.defaultValues = params.defaultValues || {}; this.fields = params.fields; this.model = params.model; @@ -123,6 +124,14 @@ var SearchPanel = Widget.extend({ this._render(); return this._super.apply(this, arguments); }, + /** + * Called each time the searchPanel is attached into the DOM. + */ + on_attach_callback: function () { + if (this.scrollTop !== null) { + this.el.scrollTop = this.scrollTop; + } + }, //-------------------------------------------------------------------------- // Public @@ -175,6 +184,7 @@ var SearchPanel = Widget.extend({ return { categories: this.categories, filters: this.filters, + scrollTop: this.el ? this.el.scrollTop : null, }; }, /** @@ -192,8 +202,9 @@ var SearchPanel = Widget.extend({ * @param {Object} state.categories */ importState: function (state) { - this.filters = state.filters || this.filters; this.categories = state.categories || this.categories; + this.filters = state.filters || this.filters; + this.scrollTop = state.scrollTop; this._render(); }, /** diff --git a/addons/web/static/tests/views/search_panel_tests.js b/addons/web/static/tests/views/search_panel_tests.js index a285c1df81b..0c4ec6fb7bb 100644 --- a/addons/web/static/tests/views/search_panel_tests.js +++ b/addons/web/static/tests/views/search_panel_tests.js @@ -2042,6 +2042,40 @@ QUnit.module('Views', { actionManager.destroy(); }); + QUnit.test('scroll position is kept when switching between controllers', async function (assert) { + assert.expect(6); + + for (var i = 10; i < 20; i++) { + this.data.category.records.push({id: i, name: "Cat " + i}); + } + + var actionManager = await createActionManager({ + actions: this.actions, + archs: this.archs, + data: this.data, + services: this.services, + }); + actionManager.$el.css('max-height', 300); + + await actionManager.doAction(1); + assert.containsOnce(actionManager, '.o_content .o_kanban_view'); + assert.strictEqual(actionManager.$('.o_search_panel').scrollTop(), 0); + + // simulate a scroll in the search panel and switch into list + actionManager.$('.o_search_panel').scrollTop(50); + await testUtils.dom.click(actionManager.$('.o_cp_switch_list')); + assert.containsOnce(actionManager, '.o_content .o_list_view'); + assert.strictEqual(actionManager.$('.o_search_panel').scrollTop(), 50); + + // simulate another scroll and switch back to kanban + actionManager.$('.o_search_panel').scrollTop(30); + await testUtils.dom.click(actionManager.$('.o_cp_switch_kanban')); + assert.containsOnce(actionManager, '.o_content .o_kanban_view'); + assert.strictEqual(actionManager.$('.o_search_panel').scrollTop(), 30); + + actionManager.destroy(); + }); + QUnit.test('search panel is not instanciated in dialogs', async function (assert) { assert.expect(2);