diff --git a/CHANGELOG.md b/CHANGELOG.md index bd241ee..4d13169 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Unreleased +* Fix premeture closing of modals and slideovers (#284, #289) +* Update Tailwind v4 documentation + + # 6.1.3 * Support multiple classes for dropdown transitions diff --git a/src/slideover.js b/src/slideover.js index cf68339..bcdbc9c 100644 --- a/src/slideover.js +++ b/src/slideover.js @@ -33,7 +33,10 @@ export default class extends Controller { } backdropClose(event) { - if (event.target.nodeName == "DIALOG") this.close() + if (event.target.nodeName !== "DIALOG") return; + if (window.getSelection().toString().length > 0) return; + + this.close(); } show() { diff --git a/test/fixtures/slideover.html b/test/fixtures/slideover.html index a37dc09..8dd6fd1 100644 --- a/test/fixtures/slideover.html +++ b/test/fixtures/slideover.html @@ -1,6 +1,7 @@
- +

This slideover dialog has a groovy backdrop!

+
diff --git a/test/slideover_test.js b/test/slideover_test.js index a63e136..8be3aba 100644 --- a/test/slideover_test.js +++ b/test/slideover_test.js @@ -15,10 +15,62 @@ describe('SlideoverController', () => { }) it('opens dialog', () => { - const dialog = document.querySelector("dialog") - expect(dialog.hasAttribute("open")).to.equal(false) + const dialog = document.querySelector('dialog') + expect(dialog.hasAttribute('open')).to.equal(false) document.querySelector("[data-action='slideover#open']").click() - expect(dialog.hasAttribute("open")).to.equal(true) + expect(dialog.hasAttribute('open')).to.equal(true) + }) + + it('closes the slideover when clicking on the backdrop', async () => { + const dialog = document.querySelector('dialog') + const openSlideoverButton = document.querySelector("[data-action='slideover#open']") + openSlideoverButton.click() + expect(dialog.hasAttribute('open')).to.equal(true) + + // Simulate clicking on the dialog element itself (the backdrop) + const clickEvent = new MouseEvent('click', { bubbles: true }) + Object.defineProperty(clickEvent, 'target', { value: dialog }) + dialog.dispatchEvent(clickEvent) + + expect(dialog.hasAttribute('closing')).to.equal(true) + }) + + it('does not close the slideover when clicking inside slideover content', async () => { + const dialog = document.querySelector('dialog') + const openSlideoverButton = document.querySelector("[data-action='slideover#open']") + const input = document.querySelector("[data-testid='slideover-input']") + openSlideoverButton.click() + expect(dialog.hasAttribute('open')).to.equal(true) + + // Simulate clicking on the input inside the modal + input.click() + + expect(dialog.hasAttribute('closing')).to.equal(false) + expect(dialog.hasAttribute('open')).to.equal(true) + }) + + it('does not close the slideover when text is selected', async () => { + const dialog = document.querySelector('dialog') + const openSlideoverButton = document.querySelector("[data-action='slideover#open']") + openSlideoverButton.click() + expect(dialog.hasAttribute('open')).to.equal(true) + + // Simulate text selection + const selection = window.getSelection() + const input = document.querySelector("[data-testid='slideover-input']") + input.select() + + // Simulate clicking on the backdrop while text is selected + const clickEvent = new MouseEvent('click', { bubbles: true }) + Object.defineProperty(clickEvent, 'target', { value: dialog }) + dialog.dispatchEvent(clickEvent) + + // Modal should stay open because text is selected + expect(dialog.hasAttribute('closing')).to.equal(false) + expect(dialog.hasAttribute('open')).to.equal(true) + + // Clear selection + selection.removeAllRanges() }) }) })