diff --git a/packages/core/src/modal_dialog/view/ModalView.ts b/packages/core/src/modal_dialog/view/ModalView.ts index 225a9720a..c30181f6e 100644 --- a/packages/core/src/modal_dialog/view/ModalView.ts +++ b/packages/core/src/modal_dialog/view/ModalView.ts @@ -19,10 +19,14 @@ export default class ModalView extends ModuleView { events() { return { click: 'onClick', + mousedown: 'onMouseDown', + mouseup: 'onMouseUp', 'click [data-close-modal]': 'hide', }; } + private ignoreBackdropClick = false; + $title?: JQuery; $content?: JQuery; $collector?: JQuery; @@ -35,9 +39,18 @@ export default class ModalView extends ModuleView { this.listenTo(model, 'change:content', this.updateContent); } + onMouseDown(e: Event) { + this.ignoreBackdropClick = e.target !== this.el; + } + + onMouseUp(e: Event) { + this.ignoreBackdropClick = this.ignoreBackdropClick || e.target !== this.el; + } + onClick(e: Event) { - const bkd = this.config.backdrop; - bkd && e.target === this.el && this.hide(); + const isBackdropClick = e.target === this.el && !this.ignoreBackdropClick; + this.ignoreBackdropClick = false; + this.config.backdrop && isBackdropClick && this.hide(); } /** diff --git a/packages/core/test/specs/modal/view/ModalView.js b/packages/core/test/specs/modal/view/ModalView.js index e7e42d61f..e60f34e9e 100644 --- a/packages/core/test/specs/modal/view/ModalView.js +++ b/packages/core/test/specs/modal/view/ModalView.js @@ -9,7 +9,7 @@ describe('ModalView', () => { beforeEach(() => { em = new Editor({}); - model = new Modal(em); + model = new Modal(em.Modal); view = new ModalView({ model, }); @@ -53,4 +53,46 @@ describe('ModalView', () => { model.set('open', 1); expect(view.el.style.display).toEqual(''); }); + + const clickBetween = (start, end) => { + start.dispatchEvent(new MouseEvent('mousedown', { bubbles: true })); + end.dispatchEvent(new MouseEvent('mouseup', { bubbles: true })); + view.el.dispatchEvent(new MouseEvent('click', { bubbles: true })); + }; + + test('Closes on a complete backdrop click', () => { + model.open(); + clickBetween(view.el, view.el); + expect(model.get('open')).toBeFalsy(); + }); + + test('Does not close when dragging from the dialog to the backdrop', () => { + model.open(); + clickBetween(view.getContent().get(0), view.el); + expect(model.get('open')).toBeTruthy(); + clickBetween(view.el, view.el); + expect(model.get('open')).toBeFalsy(); + }); + + test('Does not close when dragging from the backdrop into the dialog', () => { + model.open(); + clickBetween(view.el, view.getContent().get(0)); + expect(model.get('open')).toBeTruthy(); + }); + + test('Respects the disabled backdrop setting', () => { + view.config.backdrop = false; + model.open(); + clickBetween(view.el, view.el); + expect(model.get('open')).toBeTruthy(); + }); + + test('The close button still closes after interacting with the dialog', () => { + model.open(); + const close = view.el.querySelector('[data-close-modal]'); + close.dispatchEvent(new MouseEvent('mousedown', { bubbles: true })); + close.dispatchEvent(new MouseEvent('mouseup', { bubbles: true })); + close.click(); + expect(model.get('open')).toBeFalsy(); + }); });