diff --git a/packages/design-system/src/components/OcModal/OcModal.spec.ts b/packages/design-system/src/components/OcModal/OcModal.spec.ts index ad10550048b..c447448f70e 100644 --- a/packages/design-system/src/components/OcModal/OcModal.spec.ts +++ b/packages/design-system/src/components/OcModal/OcModal.spec.ts @@ -111,6 +111,21 @@ describe('OcModal', () => { expect(wrapper.emitted('cancel')).toBeTruthy() }) + it('emits cancel when pressing escape after focus left the dialog', async () => { + const wrapper = mountModal() + await wrapper.find('.oc-modal-background').trigger('click') + pressEscapeFromBody() + + expect(wrapper.emitted('cancel')).toBeTruthy() + }) + + it('does not emit cancel from the document while inactive', () => { + const wrapper = mountModal({ active: false }) + pressEscapeFromBody() + + expect(wrapper.emitted('cancel')).toBeFalsy() + }) + // the cancel button is disabled while loading, so escape has to be blocked as // well. Otherwise an in-flight handler gets discarded without its result ever // reaching the user. @@ -119,6 +134,13 @@ describe('OcModal', () => { await wrapper.find('[role="dialog"]').trigger('keydown.esc') expect(wrapper.emitted('cancel')).toBeFalsy() }) + + it('does not emit cancel from the document while loading', () => { + const wrapper = mountModal({ isLoading: true }) + pressEscapeFromBody() + + expect(wrapper.emitted('cancel')).toBeFalsy() + }) }) it('displays loading state', async () => { @@ -164,3 +186,7 @@ describe('OcModal', () => { expect(wrapper.html()).toMatchSnapshot() }) }) + +const pressEscapeFromBody = () => { + document.body.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true })) +} diff --git a/packages/design-system/src/components/OcModal/OcModal.vue b/packages/design-system/src/components/OcModal/OcModal.vue index 1b36219b406..f347b4d8712 100644 --- a/packages/design-system/src/components/OcModal/OcModal.vue +++ b/packages/design-system/src/components/OcModal/OcModal.vue @@ -3,7 +3,11 @@ class="oc-modal-background fixed left-0 top-0 z-[var(--z-index-modal)] flex items-center justify-center flex-row flex-wrap size-full" :class="{ 'bg-black/40': active }" > - +
{ emit('cancel') } +const onDocumentKeydown = (event: KeyboardEvent) => { + if (!active || event.key !== 'Escape') { + return + } + event.stopPropagation() + cancelModalAction() +} + +useEventListener(document, 'keydown', onDocumentKeydown) + const confirm = () => { if (buttonConfirmDisabled || inputError) { return