diff --git a/frontend/e2e/responsive-i18n.spec.ts b/frontend/e2e/responsive-i18n.spec.ts new file mode 100644 index 0000000..65b9c44 --- /dev/null +++ b/frontend/e2e/responsive-i18n.spec.ts @@ -0,0 +1,57 @@ +import { expect, test, type APIRequestContext } from "@playwright/test"; + +async function resetDemoData(request: APIRequestContext) { + await request.post("/api/v1/demo/login", { data: { role: "operations_manager" } }); + await request.post("/api/v1/demo/reset"); +} + +// Cross-checks the responsive shell across the exact breakpoint matrix from the polish +// brief (1440x1000, 1280x800, 1024x768, 768x1024, 430x932, 390x844, 360x800) in all three +// supported languages -- both that nothing overflows horizontally and that the localized +// heading text still renders (rather than being silently truncated away). +const BREAKPOINTS: { width: number; height: number }[] = [ + { width: 1440, height: 1000 }, + { width: 1280, height: 800 }, + { width: 1024, height: 768 }, + { width: 768, height: 1024 }, + { width: 430, height: 932 }, + { width: 390, height: 844 }, + { width: 360, height: 800 }, +]; + +const LANGUAGES: { code: string; attentionTitle: string }[] = [ + { code: "nl-BE", attentionTitle: "Aandachtspunten" }, + { code: "en-GB", attentionTitle: "Attention queue" }, + { code: "fr-BE", attentionTitle: "File d'attention" }, +]; + +test.beforeEach(async ({ request }) => { + await resetDemoData(request); +}); + +for (const language of LANGUAGES) { + test.describe(`${language.code}`, () => { + for (const bp of BREAKPOINTS) { + test(`no horizontal overflow at ${bp.width}x${bp.height}`, async ({ page }) => { + await page.addInitScript( + (lang) => localStorage.setItem("fleetops.language", lang), + language.code, + ); + await page.setViewportSize({ width: bp.width, height: bp.height }); + await page.goto("/login"); + await page.getByRole("button", { name: /begeleide demo|guided demo|démo guidée/i }).click(); + await expect(page).toHaveURL(/\/dashboard/); + + await expect(page.getByRole("heading", { name: language.attentionTitle })).toBeVisible(); + + const dimensions = await page.evaluate(() => ({ + scroll: document.documentElement.scrollWidth, + client: document.documentElement.clientWidth, + })); + expect(dimensions.scroll, `${language.code} @ ${bp.width}x${bp.height}`).toBeLessThanOrEqual( + dimensions.client + 1, + ); + }); + } + }); +} diff --git a/frontend/src/styles.css b/frontend/src/styles.css index e2556dd..ee64c2f 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -427,6 +427,6 @@ details summary { cursor: pointer; color: var(--teal-dark); }.data-table details .tabs { margin-inline: -2px; }.record-surface { padding: 10px; }.detail-grid { grid-template-columns: 1fr 1fr; }.detail-grid div { min-height: 70px; padding: 11px; }.return-progress { padding-inline: 12px; gap: 6px; }.return-progress b { width: 20px; }.return-progress span { font-size: .58rem; }.return-form > .section-heading { padding: 16px; }.return-capture, .return-review { padding: 16px; }.form-grid, .condition-fieldset, .review-facts { grid-template-columns: 1fr; }.condition-fieldset { display: grid; }.review-facts div { display: flex; justify-content: space-between; align-items: center; }.review-facts dd { margin: 0; }.form-actions { padding: 12px 16px; }.form-actions .button { flex: 1; }.duplicate-compare { padding: 14px; }.duplicate-compare fieldset label { display: flex !important; margin-bottom: 8px; }.integration-cards article { min-height: 140px; }.knowledge-input-row { align-items: stretch; }.knowledge-input-row .button { min-width: 72px; padding-inline: 10px; }.knowledge-empty { min-height: 250px; padding: 22px 16px; }.retrieval-flow { width: 100%; gap: 4px; }.retrieval-flow span { padding: 5px; font-size: .52rem; }.retrieval-flow i { flex: 1; min-width: 5px; }.login-story { min-height: 38vh; }.login-message > p:last-child { font-size: .8rem; }.control-illustration { display: none; }.login-access { min-height: 62vh; padding: 34px 20px 50px; }.login-options button { min-height: 72px; }.app-footer { display: none; } } -@media (max-width: 420px) { +@media (max-width: 440px) { .global-search input::placeholder { color: transparent; }.global-search { max-width: 118px; }.topbar-meta { margin-left: auto; }.demo-guide-trigger { display: none; }.avatar { width: 29px; height: 29px; }.mobile-nav a span, .mobile-nav button span { max-width: 56px; overflow: hidden; text-overflow: ellipsis; }.filters { grid-template-columns: 1fr; }.filters label:first-child { grid-column: auto; }.detail-grid { grid-template-columns: 1fr; }.readiness-metrics { margin-right: -1px; }.badge { font-size: .55rem; }.page-header h1 { font-size: 1.5rem; }.login-message h1 { font-size: 2.6rem; } }