diff --git a/src/components/EmailListPanel.tsx b/src/components/EmailListPanel.tsx index 4c4ac00..9f5ae0e 100644 --- a/src/components/EmailListPanel.tsx +++ b/src/components/EmailListPanel.tsx @@ -1815,129 +1815,150 @@ export default function EmailListPanel({ )} - {/* Quiet quick actions, visible on hover/focus. */} + {/* Quick actions overlay the row so hidden controls do not + reserve space that the message text could use. */} {!selectionMode && ( -
-
- {view === "inbox" && archiveMailboxId && ( + <> + + {view === "inbox" && pinnableEmails.length > 0 && ( + <> +
- {view === "inbox" && pinnableEmails.length > 0 && ( - + )} -
+ )} diff --git a/tests/smoke/mail.spec.ts b/tests/smoke/mail.spec.ts index 8243b9b..e3d640d 100644 --- a/tests/smoke/mail.spec.ts +++ b/tests/smoke/mail.spec.ts @@ -113,6 +113,46 @@ test("opens a conversation from a desktop click", async ({ page }) => { ).toBeVisible(); }); +test("mail quick actions overlay text instead of reserving row space", async ({ + page, +}) => { + await page.goto("/smoke-tests"); + + const conversation = page.locator( + 'a[href="/smoke-tests/thread/thread-maya"]', + ); + const row = conversation.locator("xpath=.."); + const quickActions = row.locator( + '[data-thread-quick-actions="thread-maya"]', + ); + + const defaultLayout = await conversation.evaluate((element) => { + const rowElement = element.parentElement; + const linkRect = element.getBoundingClientRect(); + const rowRect = rowElement?.getBoundingClientRect(); + return { + linkRight: linkRect.right, + rowRight: rowRect?.right ?? 0, + }; + }); + + expect(defaultLayout.rowRight - defaultLayout.linkRight).toBeLessThan(20); + + await row.hover(); + await expect( + quickActions.getByRole("button", { name: "Archive thread" }), + ).toBeVisible(); + + const overlayColors = await quickActions.evaluate((element) => { + const rowElement = element.parentElement; + return { + actions: getComputedStyle(element).backgroundColor, + row: rowElement ? getComputedStyle(rowElement).backgroundColor : "", + }; + }); + expect(overlayColors.actions).toBe(overlayColors.row); +}); + test("replaces search with selection actions without shifting conversations", async ({ page, }) => {