mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-07 04:12:04 +00:00
The top bar's bottom border and the sidebar logo band's bottom border draw one horizontal line across the top of the app, and they sat a pixel apart. Both declared h-16, so the number was never the problem. Every box in the app is border-box: the top bar carried `h-16 border-b` on one element, which puts the line inside the 4rem at 63 to 64 px, while the sidebar put `border-b` on the wrapper and `h-16` on the link inside it, which puts the line after the 4rem at 64 to 65 px. Same number, different line. The height and the border now travel together as SHELL_HEADER_BAND in shell-metrics.ts, next to the sub-shell scroll floor that exists for the same reason. The element carrying that class is the band; neither call site restates a height, and the sidebar link stretches with h-full. Border colour stays per-surface, because the two chrome surfaces are tinted apart by design and only the geometry has to match. Two guards. chrome-header-seam.test.tsx asserts the structural property, that each band renders every token of the shared constant and declares no height of its own; a second height class on either element replaces the shared one through twMerge, so both halves fire on the same edit. The e2e spec measures the painted result at 768, 900, 1024 and 1280 px in both the expanded sidebar and the collapsed rail, and checks each band actually paints a border at the same weight first, so two missing lines cannot pass as two aligned ones.
136 lines
5.1 KiB
TypeScript
136 lines
5.1 KiB
TypeScript
import { expect, test } from "./setup/test";
|
|
|
|
import { STORAGE_STATE_PATH } from "./setup/global-setup";
|
|
|
|
/**
|
|
* The bottom border of the top bar and the bottom border of the sidebar logo
|
|
* band form one horizontal line across the top of the app. The two elements
|
|
* sit in different columns of the shell flex row, so nothing in the layout
|
|
* makes them agree; both read `SHELL_HEADER_BAND`
|
|
* (`src/components/layout/shell-metrics.ts`) for the height AND the border,
|
|
* which is what keeps the line continuous.
|
|
*
|
|
* The structural half of that contract is asserted in
|
|
* `src/components/layout/__tests__/chrome-header-seam.test.tsx`. This spec
|
|
* asserts the painted result: same border bottom edge, same border width, at
|
|
* four widths spanning the sidebar's visible range, in both the expanded
|
|
* sidebar and the collapsed icon rail.
|
|
*
|
|
* The waits below settle on the two bands being VISIBLE and on the viewport
|
|
* having actually resized. Nothing here waits on a height or an offset, so a
|
|
* regression cannot hide behind the setup.
|
|
*/
|
|
|
|
// 768 px is the `md` breakpoint where the sidebar first appears; 1024 px is
|
|
// `lg`, where an unset collapse preference flips the rail to icons. Both
|
|
// boundaries and one width either side of them.
|
|
const WIDTHS = [768, 900, 1024, 1280] as const;
|
|
|
|
const SIDEBAR_COLLAPSED_KEY = "healthlog-sidebar-collapsed";
|
|
|
|
type BandGeometry = {
|
|
bottom: number;
|
|
height: number;
|
|
borderBottomWidth: number;
|
|
};
|
|
|
|
test.describe("app-chrome header seam", () => {
|
|
test.use({ storageState: STORAGE_STATE_PATH });
|
|
|
|
test.beforeEach(({}, testInfo) => {
|
|
test.skip(
|
|
testInfo.project.name === "chromium-mobile",
|
|
"the global sidebar is desktop-only (md+), so below md there is no seam",
|
|
);
|
|
});
|
|
|
|
for (const collapsed of [false, true] as const) {
|
|
test(`top bar and sidebar borders share one line (${
|
|
collapsed ? "collapsed rail" : "expanded sidebar"
|
|
})`, async ({ page }) => {
|
|
// Pin the rail state instead of letting the viewport default decide, so
|
|
// each run covers both shapes at every width.
|
|
await page.addInitScript(
|
|
([key, value]) => {
|
|
window.localStorage.setItem(key, value);
|
|
},
|
|
[SIDEBAR_COLLAPSED_KEY, String(collapsed)] as const,
|
|
);
|
|
|
|
await page.goto("/", { waitUntil: "domcontentloaded" });
|
|
|
|
const topBar = page.locator('[data-slot="top-bar"]');
|
|
const sidebarHeader = page.locator('[data-slot="sidebar-header"]');
|
|
|
|
for (const width of WIDTHS) {
|
|
await page.setViewportSize({ width, height: 900 });
|
|
|
|
// Settle on paint, not on geometry: both bands present and visible,
|
|
// the logo link inside the sidebar band rendered, and the viewport
|
|
// actually at the requested width.
|
|
await expect(topBar).toBeVisible();
|
|
await expect(sidebarHeader).toBeVisible();
|
|
await expect(sidebarHeader.locator("a[href='/']")).toBeVisible();
|
|
await expect
|
|
.poll(() => page.evaluate(() => window.innerWidth))
|
|
.toBe(width);
|
|
|
|
const seam = await page.evaluate(() => {
|
|
const read = (slot: string) => {
|
|
const el = document.querySelector(`[data-slot="${slot}"]`);
|
|
if (!el) return null;
|
|
const rect = el.getBoundingClientRect();
|
|
const style = getComputedStyle(el);
|
|
return {
|
|
bottom: rect.bottom,
|
|
height: rect.height,
|
|
borderBottomWidth: parseFloat(style.borderBottomWidth),
|
|
};
|
|
};
|
|
return {
|
|
topBar: read("top-bar"),
|
|
sidebar: read("sidebar-header"),
|
|
} as { topBar: BandGeometry | null; sidebar: BandGeometry | null };
|
|
});
|
|
|
|
const at = `at ${width}px (${collapsed ? "collapsed" : "expanded"})`;
|
|
expect(seam.topBar, `top bar missing ${at}`).not.toBeNull();
|
|
expect(seam.sidebar, `sidebar band missing ${at}`).not.toBeNull();
|
|
const top = seam.topBar!;
|
|
const side = seam.sidebar!;
|
|
|
|
// Two borders that do not exist also "line up". Prove each band
|
|
// actually paints a line, and paints it at the same weight, before
|
|
// comparing where the lines sit.
|
|
expect(
|
|
top.borderBottomWidth,
|
|
`top bar has no bottom border ${at}`,
|
|
).toBeGreaterThan(0);
|
|
expect(
|
|
side.borderBottomWidth,
|
|
`sidebar band has no bottom border ${at}`,
|
|
).toBeGreaterThan(0);
|
|
expect(
|
|
side.borderBottomWidth,
|
|
`the two borders are drawn at different weights ${at}`,
|
|
).toBeCloseTo(top.borderBottomWidth, 1);
|
|
|
|
// The bands are collapsed neither to zero nor to a sliver.
|
|
expect(top.height, `top bar has no height ${at}`).toBeGreaterThan(32);
|
|
expect(side.height, `sidebar band has no height ${at}`).toBeGreaterThan(
|
|
32,
|
|
);
|
|
|
|
// The seam itself: the bottom border sits on the band's border-box
|
|
// bottom edge, so equal bottoms means one continuous line.
|
|
expect(
|
|
Math.abs(side.bottom - top.bottom),
|
|
`header seam steps by ${(side.bottom - top.bottom).toFixed(
|
|
2,
|
|
)}px ${at}`,
|
|
).toBeLessThan(1);
|
|
}
|
|
});
|
|
}
|
|
});
|