diff --git a/addons/web/static/src/legacy/js/views/abstract_renderer.js b/addons/web/static/src/legacy/js/views/abstract_renderer.js index 27dd35aa7a7..f30541c2010 100644 --- a/addons/web/static/src/legacy/js/views/abstract_renderer.js +++ b/addons/web/static/src/legacy/js/views/abstract_renderer.js @@ -88,6 +88,15 @@ export default mvc.Renderer.extend({ */ giveFocus: function () { }, + /** + * Resets state that renderer keeps, state may contains scroll position, + * the currently active tab page, ... + * + * @see getLocalState + * @see setLocalState + */ + resetLocalState() { + }, /** * This is the reverse operation from getLocalState. With this method, we * expect the renderer to restore all DOM state, if it is relevant. diff --git a/addons/web/static/src/legacy/js/views/basic/basic_controller.js b/addons/web/static/src/legacy/js/views/basic/basic_controller.js index 22ca19992ac..24daed9bcf7 100644 --- a/addons/web/static/src/legacy/js/views/basic/basic_controller.js +++ b/addons/web/static/src/legacy/js/views/basic/basic_controller.js @@ -91,6 +91,7 @@ var BasicController = AbstractController.extend(FieldManagerMixin, { if (this._boundOnBeforeUnload) { window.removeEventListener("beforeunload", this._boundOnBeforeUnload); } + this.renderer.resetLocalState(); }, //-------------------------------------------------------------------------- diff --git a/addons/web/static/src/legacy/js/views/form/form_renderer.js b/addons/web/static/src/legacy/js/views/form/form_renderer.js index db6adb77d38..512b39dfe07 100644 --- a/addons/web/static/src/legacy/js/views/form/form_renderer.js +++ b/addons/web/static/src/legacy/js/views/form/form_renderer.js @@ -256,6 +256,21 @@ var FormRenderer = BasicRenderer.extend({ resetLastActivatedField: function () { this.lastActivatedFieldIndex = -1; }, + /** + * Resets state which stores information like scroll position, curently + * active page, ... + * + * @override + */ + resetLocalState() { + for (const notebook of this.el.querySelectorAll(':scope div.o_notebook')) { + [...notebook.querySelectorAll(':scope .o_notebook_headers .nav-item .nav-link')] + .map(nav => nav.classList.remove('active')); + [...notebook.querySelectorAll(':scope .tab-content > .tab-pane')] + .map(tab => tab.classList.remove('active')); + } + + }, /** * Restore active tab pages for each notebook. It relies on the implicit fact * that each nav header corresponds to a tab page. diff --git a/addons/web/static/tests/legacy/views/form_tests.js b/addons/web/static/tests/legacy/views/form_tests.js index e2ee4864c79..442065b76ce 100644 --- a/addons/web/static/tests/legacy/views/form_tests.js +++ b/addons/web/static/tests/legacy/views/form_tests.js @@ -1062,6 +1062,57 @@ QUnit.module('Views', { form.destroy(); }); + QUnit.test('reset local state when switching to another view', async function (assert) { + assert.expect(3); + + serverData.views = { + 'partner,false,form': `
+ + + + + + + + + + + +
`, + 'partner,false,list': '', + 'partner,false,search': '', + }; + + serverData.actions = { + 1: { + id: 1, + name: 'Partner', + res_model: 'partner', + type: 'ir.actions.act_window', + views: [[false, 'list'], [false, 'form']], + } + }; + + const webClient = await createWebClient({ serverData }); + await doAction(webClient, 1); + + await testUtils.dom.click(webClient.el.querySelector('.o_list_button_add')); + assert.containsOnce(webClient, '.o_form_view'); + + // click on second page tab + await testUtils.dom.click($(webClient.el).find('.o_notebook .nav-link:eq(1)')); + + await testUtils.dom.click('.o_control_panel .o_form_button_cancel'); + await legacyExtraNextTick(); + assert.containsNone(webClient, '.o_form_view'); + + await testUtils.dom.click(webClient.el.querySelector('.o_list_button_add')); + await legacyExtraNextTick(); + // check notebook active page is 0th page + assert.hasClass($(webClient.el).find('.o_notebook .nav-link:eq(0)'), 'active'); + + }); + QUnit.test('rendering stat buttons', async function (assert) { assert.expect(3);