mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-05 08:16:22 +00:00
fix: keep toasts above the quick reply box
This commit is contained in:
@@ -85,3 +85,18 @@ describe('quick reply wording', () => {
|
||||
expect(onReply).toHaveBeenCalledWith('Thanks, will do');
|
||||
});
|
||||
});
|
||||
|
||||
// compose-p2-17: the toasts lift over the box by its height (toast.tsx).
|
||||
describe('quick reply box and the toasts', () => {
|
||||
it('marks the box and gives the page its height, and takes both back when it goes', async () => {
|
||||
const height = vi.spyOn(HTMLElement.prototype, 'offsetHeight', 'get').mockReturnValue(96);
|
||||
const { unmount } = render(<EmailViewer email={message} isLoading={false} mailboxes={mailboxes} selectedMailbox="inbox" currentMailboxRole="inbox" onQuickReply={vi.fn()} onReply={vi.fn()} />);
|
||||
await act(async () => { await new Promise((r) => setTimeout(r, 10)); });
|
||||
|
||||
expect(document.querySelector('[data-quick-reply-box]')).not.toBeNull();
|
||||
expect(document.documentElement.style.getPropertyValue('--quick-reply-height')).toBe('96px');
|
||||
unmount();
|
||||
expect(document.documentElement.style.getPropertyValue('--quick-reply-height')).toBe('');
|
||||
height.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3516,6 +3516,21 @@ export function EmailViewer({
|
||||
// Not on spam: a reply confirms the address to the sender.
|
||||
const showQuickReply = !!email && quickReplyEnabled && !mvReplyRow && !replyComposerOpen && !isDraft && !isScheduled && !isBodyLoading && !isInJunkFolder
|
||||
&& (!effectiveEmailContent.isHtml || iframeReady);
|
||||
// Toasts sit above the quick reply box, as above a composer's footer
|
||||
// (compose-p2-17): its height for toast.tsx, as it grows when focused.
|
||||
useEffect(() => {
|
||||
const box = quickReplyBoxRef.current;
|
||||
if (!showQuickReply || !box || typeof ResizeObserver === 'undefined') return;
|
||||
const root = document.documentElement;
|
||||
const update = () => root.style.setProperty('--quick-reply-height', `${box.offsetHeight}px`);
|
||||
update();
|
||||
const observer = new ResizeObserver(update);
|
||||
observer.observe(box);
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
root.style.removeProperty('--quick-reply-height');
|
||||
};
|
||||
}, [showQuickReply]);
|
||||
// The plain-text body, where reader-above-quote mounts between text and quote.
|
||||
const [plainBodyEl, setPlainBodyEl] = useState<HTMLDivElement | null>(null);
|
||||
// Read aloud (browser speech): the body as shown, HTML frame or plain text.
|
||||
@@ -6191,7 +6206,7 @@ ${printWatermark ? printWatermark.html : ''}
|
||||
)}
|
||||
|
||||
{/* Quick Reply Section - hidden for drafts and while loading a new email */}
|
||||
{showQuickReply && (<div ref={quickReplyBoxRef} className="bg-background border-t border-border px-6 mt-auto max-sm:scroll-mb-16" style={{ paddingBlock: 'var(--density-header-py)' }}>
|
||||
{showQuickReply && (<div ref={quickReplyBoxRef} data-quick-reply-box="" className="bg-background border-t border-border px-6 mt-auto max-sm:scroll-mb-16" style={{ paddingBlock: 'var(--density-header-py)' }}>
|
||||
<div className="flex items-start" style={{ gap: 'var(--density-item-gap)' }}>
|
||||
<div className="flex-shrink-0">
|
||||
<Avatar
|
||||
|
||||
@@ -43,6 +43,14 @@ describe('toast layout', () => {
|
||||
expect(container).toHaveClass('[html[data-composer-open]_&]:bottom-20');
|
||||
});
|
||||
|
||||
// compose-p2-17: "Draft saved" covered the quick reply box.
|
||||
it('sits over the quick reply box, by its height', () => {
|
||||
render(<Toasts />);
|
||||
const container = toastContainer();
|
||||
expect(container).toHaveClass('[html:has([data-quick-reply-box])_&]:bottom-[calc(var(--quick-reply-height,5rem)+0.75rem)]');
|
||||
expect(container).toHaveClass('max-sm:[html:has([data-quick-reply-box])_&]:bottom-[calc(var(--quick-reply-height,5rem)+0.75rem)]');
|
||||
});
|
||||
|
||||
it('sits above a registered bottom bar on the desktop too, such as a form footer', () => {
|
||||
render(<Toasts />);
|
||||
const container = toastContainer();
|
||||
|
||||
@@ -475,10 +475,13 @@ export function ToastContainer({ toasts, onClose }: { toasts: Toast[]; onClose:
|
||||
// lifted over a registered bottom bar (a form's Save/Cancel footer)
|
||||
// and over an open composer's footer.
|
||||
"fixed bottom-[max(1.25rem,calc(var(--bottom-controls-height,0px)+0.75rem))] end-5 z-[99999] flex flex-col gap-2.5 [html[data-composer-open]_&]:bottom-20",
|
||||
// And over the reader's quick reply box (compose-p2-17).
|
||||
"[html:has([data-quick-reply-box])_&]:bottom-[calc(var(--quick-reply-height,5rem)+0.75rem)]",
|
||||
// Phone: full width with 12px margins, above the bottom controls
|
||||
// (the tab bar, or whichever bar registered with useBottomControls).
|
||||
"max-sm:inset-x-3 max-sm:bottom-[calc(var(--bottom-controls-height,3.75rem)+0.75rem)]",
|
||||
"max-sm:[html[data-composer-open]_&]:bottom-[max(5rem,calc(var(--bottom-controls-height,3.75rem)+0.75rem))]",
|
||||
"max-sm:[html:has([data-quick-reply-box])_&]:bottom-[calc(var(--quick-reply-height,5rem)+0.75rem)]",
|
||||
// Out of the way of the focused element: the top end corner.
|
||||
dodge && "top-5 bottom-auto!",
|
||||
held && "invisible"
|
||||
|
||||
Reference in New Issue
Block a user