fix(ux): confirm modal's Cancel/Confirm were dead on the promise-API path
Release / build-and-push (push) Successful in 3m29s
Release / build-and-push (push) Successful in 3m29s
User report: the Purge Archived dialog ('Permanently delete all
archived items?...') opened, and Cancel did nothing — and Confirm
would have been equally dead, leaving the page stuck behind the
overlay until a reload.
Root cause: confirm.ts wired the modal's buttons ONLY inside the
htmx:confirm interceptor. confirmDialog() — the promise API used by
purgeArchivedItems, remove-account, and other TS call sites — never
wired them. So whenever a page's first confirm interaction came from
the promise path (no hx-confirm element clicked first), both buttons
had no listeners. The 2026-10-30 confirm_probe spec only exercised the
hx-confirm path, which is why it survived.
Fix: wireButtons() now runs at module init (the header renders before
deferred modules) and inside confirmDialog() — both idempotent via the
existing data-wired guard.
Pinned by tests/ui/promise_confirm_probe.spec.ts (no env gating): the
purge POST is route-intercepted, so the probe asserts Cancel closes
the modal and Confirm issues the guarded request without deleting any
dev data. hx-confirm path re-pinned green (confirm_probe with BH_FEED).
Verified manually at 390x844: open -> cancelWired=true -> Cancel
closes -> Confirm fires the intercepted request.
This commit is contained in:
1 parent
04e855e691
commit
2165829d0e
3 files changed
+74
-1
No files matched your search
+12
-1
@@ -25,6 +25,7 @@ function hide(ok: boolean): void {
|
||||
* Safe to call from any module (import this instead of window.confirm).
|
||||
*/
|
||||
export function confirmDialog(message: string): Promise<boolean> {
|
||||
wireButtons();
|
||||
const { overlay, message: messageEl } = modalElements();
|
||||
if (!overlay || !messageEl) {
|
||||
// Header missing (bare layouts): degrade to the native dialog
|
||||
@@ -38,7 +39,13 @@ export function confirmDialog(message: string): Promise<boolean> {
|
||||
});
|
||||
}
|
||||
|
||||
// Wire the static buttons once.
|
||||
// Wire the static buttons once. Called at module init AND from both
|
||||
// entry paths below (idempotent via data-wired): the htmx:confirm
|
||||
// interceptor and the confirmDialog promise API. Wiring only on the
|
||||
// htmx path left the promise path (purge archived, remove account, ...)
|
||||
// with dead Cancel/Confirm buttons whenever no hx-confirm element had
|
||||
// been clicked first — the modal opened and nothing could close it
|
||||
// (user report: Purge Archived, 2026-11-02).
|
||||
function wireButtons(): void {
|
||||
const { overlay } = modalElements();
|
||||
const accept = document.getElementById("confirm-modal-accept");
|
||||
@@ -60,6 +67,10 @@ function wireButtons(): void {
|
||||
}
|
||||
}
|
||||
|
||||
// The header (and its confirm modal) render before deferred modules run,
|
||||
// so the elements are present at import time.
|
||||
wireButtons();
|
||||
|
||||
// htmx:confirm interceptor: every element with hx-confirm gets the
|
||||
// modal instead of the browser dialog; confirming issues the request.
|
||||
// (Canonical htmx pattern — see htmx docs, "confirming with a modal".)
|
||||
|
||||
Reference in new issue
Block a user