mirror of
https://github.com/block/buzz.git
synced 2026-10-29 13:31:44 +00:00
desktop: keep sidebar unread pill below top chrome strip (#1136)
Signed-off-by: Tyler Longwell <tlongwell@squareup.com> Co-authored-by: npub17jjz49l9jjmhhk7cac63j8yt9z555n9cw8vk7v5jz4vzw4ppld5qgj57cc <f4a42a97e594b77bdbd8ee35191c8b28a94a4cb871d96f32921558275421fb68@sprout-oss.stage.blox.sqprod.co>
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -483,7 +483,10 @@ export function AppSidebar({
|
||||
data-testid="app-sidebar"
|
||||
variant="sidebar"
|
||||
>
|
||||
<div className="relative flex min-h-0 flex-1 flex-col overflow-hidden">
|
||||
<div
|
||||
className="relative flex min-h-0 flex-1 flex-col overflow-hidden"
|
||||
data-testid="app-sidebar-scroll-anchor"
|
||||
>
|
||||
{unreadAboveCount > 0 ? (
|
||||
<MoreUnreadButton
|
||||
count={unreadAboveCount}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { topChromeInset } from "@/shared/layout/chromeLayout";
|
||||
import { UnreadPill, unreadCountLabel } from "@/shared/ui/UnreadPill";
|
||||
|
||||
export function MoreUnreadButton({
|
||||
@@ -15,7 +16,7 @@ export function MoreUnreadButton({
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={`pointer-events-none absolute inset-x-0 z-10 flex justify-center py-1 ${position === "top" ? "top-0" : bottomClassName}`}
|
||||
className={`pointer-events-none absolute inset-x-0 z-10 flex justify-center py-1 ${position === "top" ? topChromeInset.top : bottomClassName}`}
|
||||
>
|
||||
<UnreadPill
|
||||
direction={position === "top" ? "up" : "down"}
|
||||
|
||||
110
desktop/tests/e2e/sidebar-more-unread-overlap.spec.ts
Normal file
110
desktop/tests/e2e/sidebar-more-unread-overlap.spec.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Visual regression for the sidebar `MoreUnreadButton` (top variant)
|
||||
* overlapping the macOS traffic-light region in the global top chrome.
|
||||
*
|
||||
* The bug: `position="top"` anchored the pill at `top-0` inside a column
|
||||
* starting at window y=0, so it sat inside the 40px chrome strip — where
|
||||
* the macOS traffic lights live on the native window.
|
||||
*
|
||||
* The fix: anchor the top pill to `topChromeInset.top`
|
||||
* (`top-(--buzz-top-chrome-height,2.5rem)`) so it lines up with
|
||||
* `SidebarContent`'s existing top margin.
|
||||
*
|
||||
* This spec injects two synthetic pill elements into the live sidebar's
|
||||
* relative container — one with the legacy `top-0` className and one with
|
||||
* the fixed `top-(--buzz-top-chrome-height,2.5rem)` className — and
|
||||
* screenshots each next to the chrome strip so the difference is visible.
|
||||
*/
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
import { installMockBridge } from "../helpers/bridge";
|
||||
|
||||
const SHOTS = "test-results/sidebar-more-unread-overlap";
|
||||
|
||||
const LEGACY_TOP_CLASS = "top-0";
|
||||
const FIXED_TOP_CLASS = "top-(--buzz-top-chrome-height,2.5rem)";
|
||||
|
||||
const PILL_BASE =
|
||||
"pointer-events-none absolute inset-x-0 z-10 flex justify-center py-1";
|
||||
const PILL_INNER_HTML = `
|
||||
<button
|
||||
type="button"
|
||||
class="pointer-events-auto inline-flex items-center gap-1 rounded-full bg-destructive px-2.5 py-0.5 text-xs font-medium text-destructive-foreground shadow-sm"
|
||||
>
|
||||
<span>↑ 12 new</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
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 },
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user