diff --git a/addons/web/static/src/core/dialog/dialog_service.js b/addons/web/static/src/core/dialog/dialog_service.js
index 0d6cbc23e71..34dc5f3bd57 100644
--- a/addons/web/static/src/core/dialog/dialog_service.js
+++ b/addons/web/static/src/core/dialog/dialog_service.js
@@ -22,7 +22,7 @@ const { markRaw, reactive } = owl;
export const dialogService = {
/** @returns {DialogServiceInterface} */
- start() {
+ start(env) {
const dialogs = reactive({});
let dialogId = 0;
@@ -52,8 +52,20 @@ export const dialogService = {
id,
class: dialogClass,
props: markRaw({ ...props, close }),
- dialogData: { isActive: true, close, id },
+ dialogData: {
+ isActive: true,
+ close,
+ id,
+ },
};
+ if (env.isSmall) {
+ const scrollOrigin = { top: window.scrollY, left: window.scrollX };
+ dialog.dialogData.scrollToOrigin = () => {
+ if (!Object.keys(dialogs).length) {
+ window.scrollTo(scrollOrigin);
+ }
+ };
+ }
dialogs[id] = dialog;
return close;
diff --git a/addons/web/static/tests/core/errors/error_service_tests.js b/addons/web/static/tests/core/errors/error_service_tests.js
index 9c2826c948f..f70bf75a310 100644
--- a/addons/web/static/tests/core/errors/error_service_tests.js
+++ b/addons/web/static/tests/core/errors/error_service_tests.js
@@ -11,6 +11,7 @@ import { errorService } from "@web/core/errors/error_service";
import { ConnectionLostError, RPCError } from "@web/core/network/rpc_service";
import { notificationService } from "@web/core/notifications/notification_service";
import { registry } from "@web/core/registry";
+import { uiService } from "@web/core/ui/ui_service";
import { registerCleanup } from "../../helpers/cleanup";
import { makeTestEnv } from "../../helpers/mock_env";
import {
@@ -36,6 +37,7 @@ QUnit.module("Error Service", {
serviceRegistry.add("notification", notificationService);
serviceRegistry.add("rpc", makeFakeRPCService());
serviceRegistry.add("localization", makeFakeLocalizationService());
+ serviceRegistry.add("ui", uiService);
const windowAddEventListener = browser.addEventListener;
browser.addEventListener = (type, cb) => {
if (type === "unhandledrejection") {
diff --git a/addons/web/static/tests/mobile/mobile_form_view_tests.js b/addons/web/static/tests/mobile/mobile_form_view_tests.js
index c6c4f994b4c..28931cf6aec 100644
--- a/addons/web/static/tests/mobile/mobile_form_view_tests.js
+++ b/addons/web/static/tests/mobile/mobile_form_view_tests.js
@@ -8,6 +8,7 @@ import {
getFixture,
makeDeferred,
nextTick,
+ patchWithCleanup,
} from "../helpers/utils";
import { makeView, setupViewRegistries } from "../views/helpers";
@@ -391,4 +392,79 @@ QUnit.module("Mobile Views", ({ beforeEach }) => {
);
}
);
+
+ QUnit.test(
+ `preserve current scroll position on form view while closing dialog`,
+ async function (assert) {
+ serverData.views = {
+ "partner,false,kanban": `
+
+
+
+
+
+
+
+
+
+ `,
+ "partner,false,search": `
+
+ `,
+ };
+
+ await makeView({
+ type: "form",
+ resModel: "partner",
+ resId: 2,
+ serverData,
+ arch: `
+
+ `,
+ });
+
+ let position = { top: 0, left: 0 };
+ patchWithCleanup(window, {
+ scrollTo(newPosition) {
+ position = newPosition;
+ },
+ get scrollX() {
+ return position.left;
+ },
+ get scrollY() {
+ return position.top;
+ },
+ });
+
+ await clickEdit(fixture);
+
+ window.scrollTo({ top: 265, left: 0 });
+ assert.strictEqual(window.scrollY, 265, "Should have scrolled 265 px vertically");
+ assert.strictEqual(window.screenLeft, 0, "Should be 0 px from left as it is");
+
+ // click on m2o field
+ await click(fixture, ".o_field_many2one input");
+ // assert.strictEqual(window.scrollY, 0, "Should have scrolled to top (0) px");
+ assert.containsOnce(
+ fixture,
+ ".modal.o_modal_full",
+ "there should be a many2one modal opened in full screen"
+ );
+
+ // click on back button
+ await click(fixture, ".modal .modal-header .fa-arrow-left");
+ assert.strictEqual(
+ window.scrollY,
+ 265,
+ "Should have scrolled back to 265 px vertically"
+ );
+ assert.strictEqual(window.screenLeft, 0, "Should be 0 px from left as it is");
+ }
+ );
});