fix(campaign): stabilize message preview overlays
This commit is contained in:
@@ -88,11 +88,11 @@ export default function CampaignMessagePreviewOverlay({
|
|||||||
}, [navigation, onClose]);
|
}, [navigation, onClose]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="overlay-backdrop" role="dialog" aria-modal="true" aria-labelledby="message-preview-title">
|
<div className="overlay-backdrop message-preview-backdrop" role="dialog" aria-modal="true" aria-labelledby="message-preview-title">
|
||||||
<div className="modal-panel template-preview-modal message-preview-modal">
|
<div className="modal-panel template-preview-modal message-preview-modal">
|
||||||
<header className="modal-header">
|
<header className="modal-header">
|
||||||
<h2 id="message-preview-title">{title}</h2>
|
<h2 id="message-preview-title">{title}</h2>
|
||||||
<button className="modal-close" onClick={onClose}>×</button>
|
<button type="button" className="modal-close" aria-label={closeLabel} title={closeLabel} onClick={onClose}>×</button>
|
||||||
</header>
|
</header>
|
||||||
<div className="modal-body">
|
<div className="modal-body">
|
||||||
{(recipientLabel || recipientNote || navigation) &&
|
{(recipientLabel || recipientNote || navigation) &&
|
||||||
|
|||||||
@@ -1096,9 +1096,36 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Shared message preview overlay --------------------------------------- */
|
/* Shared message preview overlay --------------------------------------- */
|
||||||
|
.message-preview-modal {
|
||||||
|
width: min(920px, 100%);
|
||||||
|
height: min(780px, calc(100dvh - 48px));
|
||||||
|
max-height: calc(100dvh - 48px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-preview-modal .modal-header {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-preview-modal .modal-header h2 {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.message-preview-modal .modal-body {
|
.message-preview-modal .modal-body {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-height: 0;
|
||||||
display: grid;
|
display: grid;
|
||||||
|
align-content: start;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
overflow: auto;
|
||||||
|
scrollbar-gutter: stable;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-preview-modal .modal-footer {
|
||||||
|
align-items: center;
|
||||||
|
min-height: 68px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-preview-modal .message-display-body,
|
.message-preview-modal .message-display-body,
|
||||||
@@ -1156,6 +1183,38 @@
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 720px), (max-height: 700px) {
|
||||||
|
.message-preview-backdrop {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-preview-modal {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100dvh - 16px);
|
||||||
|
max-height: calc(100dvh - 16px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-preview-modal .modal-header {
|
||||||
|
min-height: 52px;
|
||||||
|
padding-inline: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-preview-modal .modal-body {
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-preview-modal .modal-footer {
|
||||||
|
min-height: 58px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-preview-modal .message-display-body,
|
||||||
|
.message-preview-modal .message-display-html-frame {
|
||||||
|
height: clamp(210px, 38dvh, 360px);
|
||||||
|
max-height: clamp(210px, 38dvh, 360px);
|
||||||
|
}
|
||||||
|
}
|
||||||
.attachment-source-path-cell {
|
.attachment-source-path-cell {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
|
|||||||
43
webui/tests/message-preview-overlay-structure.test.mjs
Normal file
43
webui/tests/message-preview-overlay-structure.test.mjs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
|
||||||
|
function assert(condition, message) {
|
||||||
|
if (!condition) throw new Error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
const overlaySource = readFileSync(
|
||||||
|
"src/features/campaigns/components/MessagePreviewOverlay.tsx",
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
const styles = readFileSync("src/styles/campaign-workspace.css", "utf8");
|
||||||
|
|
||||||
|
assert(
|
||||||
|
overlaySource.includes("overlay-backdrop message-preview-backdrop"),
|
||||||
|
"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"
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
styles.includes("height: min(780px, calc(100dvh - 48px));"),
|
||||||
|
"desktop previews use a stable responsive height"
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
styles.includes("flex: 1 1 auto;") &&
|
||||||
|
styles.includes("min-height: 0;") &&
|
||||||
|
styles.includes("overflow: auto;") &&
|
||||||
|
styles.includes("scrollbar-gutter: stable;"),
|
||||||
|
"the preview body owns scrolling without moving the surrounding actions"
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
styles.includes(".message-preview-modal .modal-footer") &&
|
||||||
|
styles.includes("min-height: 68px;"),
|
||||||
|
"the preview footer keeps a stable desktop action area"
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
styles.includes("height: calc(100dvh - 16px);") &&
|
||||||
|
styles.includes("height: clamp(210px, 38dvh, 360px);"),
|
||||||
|
"small viewports use their available dynamic height"
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("Message preview overlay structure checks passed.");
|
||||||
Reference in New Issue
Block a user