diff --git a/frontend/src/components/common/BaseDialog.vue b/frontend/src/components/common/BaseDialog.vue
index e24c6fc226..ba0fe6a051 100644
--- a/frontend/src/components/common/BaseDialog.vue
+++ b/frontend/src/components/common/BaseDialog.vue
@@ -44,6 +44,7 @@
diff --git a/frontend/src/components/common/__tests__/BaseDialog.scrollLock.spec.ts b/frontend/src/components/common/__tests__/BaseDialog.scrollLock.spec.ts
new file mode 100644
index 0000000000..3e4182f3c2
--- /dev/null
+++ b/frontend/src/components/common/__tests__/BaseDialog.scrollLock.spec.ts
@@ -0,0 +1,51 @@
+import { afterEach, describe, expect, it } from 'vitest'
+import { enableAutoUnmount, mount } from '@vue/test-utils'
+import BaseDialog from '../BaseDialog.vue'
+
+enableAutoUnmount(afterEach)
+
+function dialog(show = true) {
+ return mount(BaseDialog, { props: { show, title: 'Details' }, global: { stubs: { Icon: true } } })
+}
+
+const locked = () => document.body.classList.contains('modal-open')
+
+describe('dialog body scroll lock', () => {
+ it('keeps the body locked when a hidden sibling mounts or unmounts', () => {
+ dialog()
+ const hidden = dialog(false)
+ expect(locked()).toBe(true)
+ hidden.unmount()
+ expect(locked()).toBe(true)
+ })
+
+ it('keeps the lock until the last visible dialog closes', async () => {
+ const first = dialog()
+ const second = dialog()
+ await second.setProps({ show: false })
+ expect(locked()).toBe(true)
+ await first.setProps({ show: false })
+ expect(locked()).toBe(false)
+ })
+
+ it('keeps the lock until the last visible dialog unmounts', () => {
+ const first = dialog()
+ const second = dialog()
+ second.unmount()
+ expect(locked()).toBe(true)
+ first.unmount()
+ expect(locked()).toBe(false)
+ })
+
+ it('releases the lock after repeated open and close cycles', async () => {
+ const wrapper = dialog(false)
+ expect(locked()).toBe(false)
+ await wrapper.setProps({ show: true })
+ expect(locked()).toBe(true)
+ await wrapper.setProps({ show: false })
+ expect(locked()).toBe(false)
+ await wrapper.setProps({ show: true })
+ wrapper.unmount()
+ expect(locked()).toBe(false)
+ })
+})