diff --git a/playwright.foundation.config.ts b/playwright.foundation.config.ts index d7f4425..dc5b821 100644 --- a/playwright.foundation.config.ts +++ b/playwright.foundation.config.ts @@ -2,7 +2,7 @@ import { defineConfig, devices } from "@playwright/test"; export default defineConfig({ testDir: "./tests/browser", - testMatch: ["foundation.spec.ts", "table.spec.ts"], + testMatch: ["foundation.spec.ts", "table.spec.ts", "showcase.spec.ts"], fullyParallel: false, forbidOnly: true, retries: 0, diff --git a/preview/components.html b/preview/components.html index 3c7271d..3d67b78 100644 --- a/preview/components.html +++ b/preview/components.html @@ -28,7 +28,10 @@ header.site { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--dds-space-16); padding: var(--dds-space-40) 0 var(--dds-space-24); } header.site h1 { font-size: var(--dds-typo-title-1-font-size); line-height: var(--dds-typo-title-1-line-height); font-weight: var(--dds-typo-title-1-font-weight); margin: 0; } header.site .src { font-size: var(--dds-typo-label-font-size); color: var(--dds-color-text-muted); margin-left: auto; font-family: var(--dds-font-family-mono); } - header.site nav { display: flex; gap: var(--dds-space-16); flex-basis: 100%; margin-top: var(--dds-space-4); } + /* wrap: without it the row can't shrink past the sum of each link's + * narrowest word, and on a phone every command splits after its "--". + * Row gap 8 on 16px lines = 24px pitch, the web target-size floor. */ + header.site nav { display: flex; flex-wrap: wrap; gap: var(--dds-space-8) var(--dds-space-16); flex-basis: 100%; margin-top: var(--dds-space-4); } header.site nav a { font-family: var(--dds-font-family-mono); font-size: var(--dds-typo-label-font-size); diff --git a/preview/icons.html b/preview/icons.html index c6520de..5c37014 100644 --- a/preview/icons.html +++ b/preview/icons.html @@ -27,8 +27,11 @@ header.site { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--dds-space-16); padding: var(--dds-space-40) 0 var(--dds-space-24); } header.site h1 { font-size: var(--dds-typo-title-1-font-size); line-height: var(--dds-typo-title-1-line-height); font-weight: var(--dds-typo-title-1-font-weight); margin: 0; } header.site .src { font-size: var(--dds-typo-label-font-size); color: var(--dds-color-text-muted); margin-left: auto; font-family: var(--dds-font-family-mono); } - header.site nav { display: flex; gap: var(--dds-space-16); flex-basis: 100%; margin-top: var(--dds-space-4); } - header.site nav a { font-family: var(--dds-font-family-mono); font-size: var(--dds-typo-label-font-size); color: var(--dds-color-text-brand); text-decoration: none; } + /* wrap: without it the row can't shrink past the sum of each link's + * narrowest word, and on a phone every command splits after its "--". + * Row gap 8 on 16px lines = 24px pitch, the web target-size floor. */ + header.site nav { display: flex; flex-wrap: wrap; gap: var(--dds-space-8) var(--dds-space-16); flex-basis: 100%; margin-top: var(--dds-space-4); } + header.site nav a { font-family: var(--dds-font-family-mono); font-size: var(--dds-typo-label-font-size); line-height: var(--dds-typo-label-line-height); color: var(--dds-color-text-brand); text-decoration: none; } header.site nav a:hover { text-decoration: underline; } h2 { font-size: var(--dds-typo-title-2-font-size); line-height: var(--dds-typo-title-2-line-height); font-weight: var(--dds-typo-title-2-font-weight); margin: var(--dds-space-48) 0 var(--dds-space-4); } p.note { font-size: var(--dds-typo-body-2-font-size); line-height: var(--dds-typo-body-2-line-height); color: var(--dds-color-text-secondary); margin: 0 0 var(--dds-space-16); max-width: 60em; } diff --git a/preview/index.html b/preview/index.html index 03f0709..21a3960 100644 --- a/preview/index.html +++ b/preview/index.html @@ -98,7 +98,10 @@ padding: 2px 8px; border-radius: 9999px; } header.site .src { font-size: 13px; color: var(--muted); margin-left: auto; } - header.site nav { display: flex; gap: 16px; flex-basis: 100%; margin-top: 4px; } + /* wrap: without it the row can't shrink past the sum of each link's + narrowest word, and on a phone every command splits after its "--". + Row gap 8 on 16px lines = 24px pitch, the web target-size floor. */ + header.site nav { display: flex; flex-wrap: wrap; gap: 8px 16px; flex-basis: 100%; margin-top: 4px; } header.site nav a { font-family: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px; line-height: 16px; color: var(--brand-text); text-decoration: none; diff --git a/tests/browser/showcase.spec.ts b/tests/browser/showcase.spec.ts new file mode 100644 index 0000000..8fcb73b --- /dev/null +++ b/tests/browser/showcase.spec.ts @@ -0,0 +1,46 @@ +import { expect, test } from "@playwright/test"; +import { fileURLToPath } from "node:url"; + +// The showcase pages are shipped as-is to devslab.kr/dds/ (the site vendors +// them), so a phone-width overflow here is a phone-width overflow there. +// Served from disk under a stand-in origin: icons.html imports icons.js as an +// ES module, which Chromium refuses over file://. +const origin = "http://dds.showcase.test"; +const repo = new URL("../../", import.meta.url); +const pages = ["index", "components", "icons"] as const; +const widths = [320, 360, 375, 1440] as const; + +test.beforeEach(async ({ page }) => { + await page.route(`${origin}/**`, (route) => { + const path = new URL(route.request().url()).pathname.slice(1); + return route.fulfill({ path: fileURLToPath(new URL(path, repo)) }); + }); +}); + +for (const name of pages) { + for (const width of widths) { + test(`preview/${name}.html fits ${width}px and keeps each nav command on one line`, async ({ page }) => { + await page.setViewportSize({ width, height: 800 }); + await page.goto(`${origin}/preview/${name}.html`); + + const { scrollWidth, clientWidth } = await page.evaluate(() => ({ + scrollWidth: document.documentElement.scrollWidth, + clientWidth: document.documentElement.clientWidth, + })); + expect(scrollWidth, "the page must not scroll sideways").toBeLessThanOrEqual(clientWidth); + + // A nowrap nav squeezes each link to its narrowest word, so "> ./dds + // --components" stacks as "> ./dds --" / "components". Wrapping between + // links keeps every command whole. + const links = await page.locator("header.site nav a").evaluateAll((elements) => elements.map((element) => ({ + text: element.textContent, + height: element.getBoundingClientRect().height, + lineHeight: parseFloat(getComputedStyle(element).lineHeight), + }))); + expect(links.length).toBeGreaterThan(0); + for (const link of links) { + expect(link.height, `"${link.text}" must render on a single line`).toBeLessThanOrEqual(link.lineHeight + 0.5); + } + }); + } +}