diff --git a/e2e/mobile-shell.spec.ts b/e2e/mobile-shell.spec.ts index a3c280a45..e90cb59d1 100644 --- a/e2e/mobile-shell.spec.ts +++ b/e2e/mobile-shell.spec.ts @@ -174,6 +174,38 @@ test.describe('the shell at a phone viewport', () => { expect(styles.labelFits).toBe(true); }); + test('keeps every client-creation step reachable on a phone viewport', async ({ page }) => { + await page.route(/\/api\/v1\/offices/, (route) => + route.fulfill({ status: 200, contentType: 'application/json', body: '[]' }), + ); + await page.goto('/clients/create'); + + const stepper = page.locator('app-stepper .stepper'); + await expect(stepper).toBeVisible(); + await expect(stepper.locator('.step')).toHaveCount(3); + await expect(stepper.locator('.step-active')).toHaveAttribute('aria-current', 'step'); + await expect(stepper.locator('.step-label').nth(0)).toBeVisible(); + await expect(stepper.locator('.step-label').nth(1)).toBeHidden(); + await expect(stepper.locator('.step-label').nth(2)).toBeHidden(); + + const layout = await stepper.evaluate((element) => ({ + clientWidth: element.clientWidth, + scrollWidth: element.scrollWidth, + viewportWidth: document.documentElement.clientWidth, + markerEdges: Array.from(element.querySelectorAll('.step-marker'), (marker) => { + const box = marker.getBoundingClientRect(); + return { left: box.left, right: box.right }; + }), + })); + + expect(layout.scrollWidth).toBeLessThanOrEqual(layout.clientWidth); + expect(layout.markerEdges).toHaveLength(3); + for (const edge of layout.markerEdges) { + expect(edge.left).toBeGreaterThanOrEqual(0); + expect(edge.right).toBeLessThanOrEqual(layout.viewportWidth); + } + }); + describe_drawer(); test('renders tables as cards instead of a sideways scroll', async ({ page }) => { diff --git a/src/app/shared/components/stepper/stepper.component.ts b/src/app/shared/components/stepper/stepper.component.ts index 684441857..ae1d6187b 100644 --- a/src/app/shared/components/stepper/stepper.component.ts +++ b/src/app/shared/components/stepper/stepper.component.ts @@ -38,6 +38,8 @@ import { TranslatePipe } from '../../../core/adapters'; class="step" [class.step-done]="i < currentIndex()" [class.step-active]="i === currentIndex()" + [attr.aria-label]="label | appTranslate" + [attr.aria-current]="i === currentIndex() ? 'step' : null" > @if (i < currentIndex()) { @@ -108,6 +110,26 @@ import { TranslatePipe } from '../../../core/adapters'; margin: 0 12px; background: var(--border-color, #ccc); } + @media (max-width: 768px) { + .step { + min-width: 0; + } + .step:last-child { + flex: 1; + } + .step-label { + display: none; + } + .step-active .step-label { + display: block; + min-width: 0; + white-space: normal; + overflow-wrap: anywhere; + } + .step-connector { + margin: 0 6px; + } + } `, ], })