diff --git a/desktop/playwright.config.ts b/desktop/playwright.config.ts index d7e39aeb9d..7f7ccce4fd 100644 --- a/desktop/playwright.config.ts +++ b/desktop/playwright.config.ts @@ -46,6 +46,7 @@ export default defineConfig({ "**/identity-archive-hide.spec.ts", "**/relay-connectivity-screenshots.spec.ts", "**/unread-pill-screenshots.spec.ts", + "**/sidebar-more-unread-overlap.spec.ts", "**/thread-unread-screenshots.spec.ts", "**/animated-avatar-screenshots.spec.ts", "**/reminders-screenshots.spec.ts", diff --git a/desktop/src/features/sidebar/ui/AppSidebar.tsx b/desktop/src/features/sidebar/ui/AppSidebar.tsx index 98b8ee6052..8c2a8a8b9d 100644 --- a/desktop/src/features/sidebar/ui/AppSidebar.tsx +++ b/desktop/src/features/sidebar/ui/AppSidebar.tsx @@ -483,7 +483,10 @@ export function AppSidebar({ data-testid="app-sidebar" variant="sidebar" > -
+
{unreadAboveCount > 0 ? ( + ↑ 12 new + +`; + +async function injectSyntheticPill( + page: import("@playwright/test").Page, + topClass: string, + testId: string, +) { + await page.evaluate( + ({ topClass, base, html, testId }) => { + const container = document.querySelector( + '[data-testid="app-sidebar-scroll-anchor"]', + ) as HTMLElement | null; + if (!container) throw new Error("sidebar scroll anchor not found"); + + // Remove any prior injection so we can screenshot variants in sequence. + container + .querySelectorAll("[data-synthetic-more-unread]") + .forEach((el) => { + el.remove(); + }); + + const pill = document.createElement("div"); + pill.dataset.syntheticMoreUnread = "true"; + pill.dataset.testid = testId; + pill.className = `${base} ${topClass}`; + pill.innerHTML = html; + container.appendChild(pill); + }, + { topClass, base: PILL_BASE, html: PILL_INNER_HTML, testId }, + ); +} + +test.describe("sidebar MoreUnreadButton top chrome overlap", () => { + test.beforeEach(async ({ page }) => { + await installMockBridge(page); + await page.goto("/"); + await expect(page.getByTestId("app-sidebar")).toBeVisible(); + }); + + test("before fix: top-0 overlaps traffic-light strip", async ({ page }) => { + await injectSyntheticPill(page, LEGACY_TOP_CLASS, "synthetic-before"); + const pill = page.getByTestId("synthetic-before"); + await expect(pill).toBeVisible(); + + const box = await pill.boundingBox(); + expect(box).not.toBeNull(); + // Pre-fix: pill is anchored at y=0, inside the 40px chrome strip. + expect(box?.y ?? Number.NaN).toBeLessThan(20); + + // Screenshot the top-left corner of the sidebar with the chrome strip + // visible above it. + await page.screenshot({ + path: `${SHOTS}/before-top-0-overlap.png`, + clip: { x: 0, y: 0, width: 320, height: 120 }, + }); + }); + + test("after fix: top-(--buzz-top-chrome-height) clears chrome strip", async ({ + page, + }) => { + await injectSyntheticPill(page, FIXED_TOP_CLASS, "synthetic-after"); + const pill = page.getByTestId("synthetic-after"); + await expect(pill).toBeVisible(); + + const box = await pill.boundingBox(); + expect(box).not.toBeNull(); + // Post-fix: pill sits below the 40px chrome strip. + expect(box?.y ?? Number.NaN).toBeGreaterThanOrEqual(40); + + await page.screenshot({ + path: `${SHOTS}/after-top-chrome-inset.png`, + clip: { x: 0, y: 0, width: 320, height: 120 }, + }); + }); +});