diff --git a/CHANGELOG.md b/CHANGELOG.md index beda77a..5937985 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ unreachable model within minutes, and the pipeline no longer imports the chat. N ### Changed +- **On a phone the toasts stack one high.** Below 48rem only the newest toast shows; an older one comes back + when the newest goes, unless its own time ran out first. - **The warm-up waits for the chat to be used, and for the changes to pause.** Before the first open since start, a run's end or a status change prepares nothing; after it, the warm-up starts once `leadgen.chat.suggestion-quiet-window` (default 30 s) has passed without a further change, so triaging applications one by one costs one call from the diff --git a/docs/decisions/frontend-design-system.md b/docs/decisions/frontend-design-system.md index 73cc30e..7ea71be 100644 --- a/docs/decisions/frontend-design-system.md +++ b/docs/decisions/frontend-design-system.md @@ -630,6 +630,10 @@ One line at the edge of the screen after a write or a run, and the decisions tha advert prose, which scrolls. The reveal's duration and easing are the first tokens in `src/styles/motion.css`, off under `prefers-reduced-motion`; the lifetime and the cap are constants beside the model, because the timer that reads them is TypeScript. +- **Below 48rem the pile is one toast high.** Full width above the bar, three in a row took a + third of a phone's height. The stylesheet shows the newest only; the older ones stay in the store + with their timers running and show when the newest goes. CSS rather than a smaller cap for + phones, because the cap is the store's and the store does not know the width. - **The DOM-render screenshot never paints the fixed stack.** The toast stood in the DOM with a measured rectangle while the capture showed nothing. The pictures in the spec come from a pixel capture over CDP, taken past the reveal; a frame inside it reads as a diff --git a/frontend/src/app/layout/toast-stack/toast-stack.browser.spec.ts b/frontend/src/app/layout/toast-stack/toast-stack.browser.spec.ts index b19a4e7..dc05012 100644 --- a/frontend/src/app/layout/toast-stack/toast-stack.browser.spec.ts +++ b/frontend/src/app/layout/toast-stack/toast-stack.browser.spec.ts @@ -1,7 +1,7 @@ import {TestBed} from '@angular/core/testing'; import {provideRouter} from '@angular/router'; import {injectDispatch} from '@ngrx/signals/events'; -import {cdp} from 'vitest/browser'; +import {cdp, page} from 'vitest/browser'; import {shortlistEvents} from '@core/store/shortlist.events'; import {toastEvents} from '@core/toast/toast.events'; import {actionToast, toast} from '@core/toast/toast.model'; @@ -102,3 +102,48 @@ describe('ToastStack sizes (ISC-500)', () => { } }); }); + +/** + * Below 48rem the stack runs the full width above the bottom bar, and three toasts in a row took a + * third of a phone's height. There it shows the newest one only; the older ones stay in the store and + * come back as the newest goes. + */ +describe('ToastStack on a phone', () => { + let dispatch: ReturnType>; + + beforeEach(() => { + TestBed.configureTestingModule({providers: [provideRouter([])]}); + dispatch = TestBed.runInInjectionContext(() => injectDispatch(toastEvents)); + }); + + afterEach(async () => { + await page.viewport(1280, 800); + }); + + function raiseThree(): HTMLElement[] { + const fixture = TestBed.createComponent(ToastStack); + fixture.detectChanges(); + for (const title of ['First', 'Second', 'Third']) { + dispatch.raised(toast('info', 'toast.archived', {title}, '/shortlist/8')); + } + fixture.detectChanges(); + return Array.from(fixture.nativeElement.querySelectorAll('.alert')); + } + + const shown = (alerts: HTMLElement[]) => alerts.filter((alert) => alert.getClientRects().length > 0); + + it('shows only the newest toast at 390px', async () => { + await page.viewport(390, 844); + const alerts = raiseThree(); + + expect(alerts).toHaveLength(3); + expect(shown(alerts)).toEqual([alerts[2]]); + expect(alerts[2]!.textContent).toContain('Third'); + }); + + it('keeps the whole stack from 48rem up', async () => { + await page.viewport(1280, 800); + + expect(shown(raiseThree())).toHaveLength(3); + }); +}); diff --git a/frontend/src/app/layout/toast-stack/toast-stack.css b/frontend/src/app/layout/toast-stack/toast-stack.css index 6f992d8..cb4b435 100644 --- a/frontend/src/app/layout/toast-stack/toast-stack.css +++ b/frontend/src/app/layout/toast-stack/toast-stack.css @@ -41,6 +41,13 @@ .alert { width: 100%; } + + /* The newest toast only. Full width above the bar, three in a row took a third of a phone's + height. The older ones stay in the store, their timers running, and the next one shows + when the newest goes; each was announced when it arrived, so hiding it says nothing twice. */ + .lg-toasts > :not(:last-child) { + display: none; + } } .alert {