Close a persistent rich tooltip on a second press of its trigger
The bubble is a popover="auto" and the trigger is outside it, so the press on the trigger light-dismissed the open bubble and the click that followed opened it again: a second press never closed it, in any engine. Found by the new exit test. A close the browser made within 250ms is now taken for that press, as menu.js's REOPEN_GUARD_MS does, timed from beforetoggle because toggle is queued past the click; the morph test waits past the guard before pressing again, as the menu tests do. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
6a133c2c67
commit
684d60efc8
@@ -18,6 +18,12 @@
|
|||||||
const HOVER_DELAY_MS = 500
|
const HOVER_DELAY_MS = 500
|
||||||
const LEAVE_GRACE_MS = 1500
|
const LEAVE_GRACE_MS = 1500
|
||||||
|
|
||||||
|
// A persistent bubble is a `popover="auto"`, and its trigger is outside it: the press on the
|
||||||
|
// trigger light-dismisses the open bubble, and the click that follows would open it again, so a
|
||||||
|
// second press never closed it. A close the browser made this recently is taken as that press, as
|
||||||
|
// menu.js's REOPEN_GUARD_MS does; `beforetoggle` times it, because `toggle` is queued past the click.
|
||||||
|
const REOPEN_GUARD_MS = 250
|
||||||
|
|
||||||
document.addEventListener('alpine:init', () => {
|
document.addEventListener('alpine:init', () => {
|
||||||
window.Alpine.data('materialRichTooltip', (persistent = false) => ({
|
window.Alpine.data('materialRichTooltip', (persistent = false) => ({
|
||||||
timer: null,
|
timer: null,
|
||||||
@@ -50,12 +56,28 @@ document.addEventListener('alpine:init', () => {
|
|||||||
wrapper.addEventListener('pointerenter', describe)
|
wrapper.addEventListener('pointerenter', describe)
|
||||||
|
|
||||||
if (persistent) {
|
if (persistent) {
|
||||||
|
let dismissedAt = -Infinity
|
||||||
|
let closingExplicitly = false
|
||||||
|
|
||||||
|
bubble.addEventListener('beforetoggle', (event) => {
|
||||||
|
if (event.newState === 'closed' && !closingExplicitly) {
|
||||||
|
dismissedAt = performance.now()
|
||||||
|
}
|
||||||
|
|
||||||
|
closingExplicitly = false
|
||||||
|
})
|
||||||
|
|
||||||
wrapper.addEventListener('click', (event) => {
|
wrapper.addEventListener('click', (event) => {
|
||||||
if (bubble.contains(event.target)) {
|
if (bubble.contains(event.target)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
open() ? bubble.hidePopover() : bubble.showPopover()
|
if (open()) {
|
||||||
|
closingExplicitly = true
|
||||||
|
bubble.hidePopover()
|
||||||
|
} else if (performance.now() - dismissedAt > REOPEN_GUARD_MS) {
|
||||||
|
bubble.showPopover()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -282,8 +282,11 @@ it('keeps a rich tooltip open while its action renders the component, and opens
|
|||||||
->assertSeeIn('#renders', '1')
|
->assertSeeIn('#renders', '1')
|
||||||
->assertScript("{$persistent}.matches(':popover-open')");
|
->assertScript("{$persistent}.matches(':popover-open')");
|
||||||
|
|
||||||
|
// Past the reopen guard (rich-tooltip.js's REOPEN_GUARD_MS), which takes a press this soon after
|
||||||
|
// a light dismiss for the press that dismissed it, as menu.js's does.
|
||||||
$page->click('#outside')
|
$page->click('#outside')
|
||||||
->assertScript("! {$persistent}.matches(':popover-open')");
|
->assertScript("! {$persistent}.matches(':popover-open')")
|
||||||
|
->wait(0.3);
|
||||||
|
|
||||||
$page->click('@details')
|
$page->click('@details')
|
||||||
->assertScript("{$persistent}.matches(':popover-open')");
|
->assertScript("{$persistent}.matches(':popover-open')");
|
||||||
|
|||||||
Reference in New Issue
Block a user