From 04c214b149689606537b2ab7f29a72e5e351059c Mon Sep 17 00:00:00 2001 From: Albrecht Degering Date: Tue, 21 Jul 2026 03:17:11 +0200 Subject: [PATCH] Use the central Dialog for Campaign previews --- .../components/MessagePreviewOverlay.tsx | 45 ++++++++++--------- webui/src/styles/campaign-workspace.css | 4 ++ ...message-preview-overlay-structure.test.mjs | 6 ++- webui/tests/review-preview-ui.test.ts | 2 +- 4 files changed, 33 insertions(+), 24 deletions(-) diff --git a/webui/src/features/campaigns/components/MessagePreviewOverlay.tsx b/webui/src/features/campaigns/components/MessagePreviewOverlay.tsx index a056232..2fa62bf 100644 --- a/webui/src/features/campaigns/components/MessagePreviewOverlay.tsx +++ b/webui/src/features/campaigns/components/MessagePreviewOverlay.tsx @@ -1,6 +1,6 @@ -import { useEffect, type ReactNode } from "react"; +import { useEffect, useRef, type ReactNode } from "react"; import { ArrowBigLeft, ArrowBigLeftDash, ArrowBigRight, ArrowBigRightDash } from "lucide-react"; -import { Button, MessageDisplayPanel, type MessageDisplayAttachment } from "@govoplan/core-webui"; +import { Button, Dialog, MessageDisplayPanel, type MessageDisplayAttachment } from "@govoplan/core-webui"; // Campaign review/template/mock previews need recipient navigation, review notes, // raw MIME inspection and attachment grouping. Generic mailbox reading uses @@ -60,15 +60,13 @@ export default function CampaignMessagePreviewOverlay({ }: CampaignMessagePreviewOverlayProps) { const shownSubject = subject?.trim() || "i18n:govoplan-campaign.no_subject.7b4e8035"; const fields = metaItems.map((item) => ({ label: item.label, value: item.value })); + const contentRef = useRef(null); useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { + const dialogPanel = contentRef.current?.closest("[data-dialog-stack-state]"); + if (dialogPanel?.dataset.dialogStackState !== "topmost") return; if (isEditableTarget(event.target)) return; - if (event.key === "Escape") { - event.preventDefault(); - onClose(); - return; - } if (!navigation) return; if (event.key === "ArrowLeft") { event.preventDefault(); @@ -87,16 +85,26 @@ export default function CampaignMessagePreviewOverlay({ window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); - }, [navigation, onClose]); + }, [navigation]); return ( -
-
-
-

{title}

- -
-
+ + {actions &&
{actions}
} + + } + > +
{(recipientLabel || recipientNote || navigation) &&
@@ -132,13 +140,8 @@ export default function CampaignMessagePreviewOverlay({
{raw}
} -
-
-
); +
); } diff --git a/webui/src/styles/campaign-workspace.css b/webui/src/styles/campaign-workspace.css index 1824c7a..5cf6b22 100644 --- a/webui/src/styles/campaign-workspace.css +++ b/webui/src/styles/campaign-workspace.css @@ -1335,6 +1335,10 @@ scrollbar-gutter: stable; } +.message-preview-content { + display: contents; +} + .message-preview-modal .modal-footer { align-items: center; min-height: 68px; diff --git a/webui/tests/message-preview-overlay-structure.test.mjs b/webui/tests/message-preview-overlay-structure.test.mjs index cf8da30..4f3d487 100644 --- a/webui/tests/message-preview-overlay-structure.test.mjs +++ b/webui/tests/message-preview-overlay-structure.test.mjs @@ -15,8 +15,10 @@ assert( "message previews expose their responsive backdrop hook" ); assert( - overlaySource.includes('type="button" className="modal-close" aria-label={closeLabel} title={closeLabel}'), - "the close control has stable button semantics, an accessible name, and a tooltip" + overlaySource.includes("