diff --git a/libs/ui/epg/src/lib/epg-timeline/epg-timeline.component.spec.ts b/libs/ui/epg/src/lib/epg-timeline/epg-timeline.component.spec.ts index 087def864..54d88a3b5 100644 --- a/libs/ui/epg/src/lib/epg-timeline/epg-timeline.component.spec.ts +++ b/libs/ui/epg/src/lib/epg-timeline/epg-timeline.component.spec.ts @@ -23,6 +23,13 @@ function programAt( }; } +/** Local yyyy-MM-dd day-key for a programme start (matches the component). */ +function localDateKey(iso: string): string { + const d = new Date(iso); + const pad = (n: number) => String(n).padStart(2, '0'); + return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`; +} + describe('EpgTimelineComponent', () => { let fixture: ComponentFixture; let component: EpgTimelineComponent; @@ -86,6 +93,26 @@ describe('EpgTimelineComponent', () => { expect(component.renderState()).toBe('empty-day'); }); + it('honours a controlled non-today selectedDate instead of resetting to today', () => { + // Data three days out (today empty). Without honouring the controlled + // input the panel would show empty-day for today; with it, the ribbon + // renders on the host-selected day. + const future = programAt(3 * 1440, 60); + setInputs({ + programs: [future], + selectedDate: localDateKey(future.start), + }); + expect(component.renderState()).toBe('ribbon'); + }); + + it('follows a programmatic selectedDate change from the host', () => { + const future = programAt(3 * 1440, 60); + setInputs({ programs: [future] }); + expect(component.renderState()).toBe('empty-day'); // viewing today + setInputs({ selectedDate: localDateKey(future.start) }); + expect(component.renderState()).toBe('ribbon'); // followed the host + }); + it('shows the ribbon controls (Now + zoom) only while a ribbon renders', () => { setInputs({ programs: [programAt(0, 120, 'Now')] }); expect(component.showRibbonControls()).toBe(true); diff --git a/libs/ui/epg/src/lib/epg-timeline/epg-timeline.component.ts b/libs/ui/epg/src/lib/epg-timeline/epg-timeline.component.ts index 9bfef738f..270a636b0 100644 --- a/libs/ui/epg/src/lib/epg-timeline/epg-timeline.component.ts +++ b/libs/ui/epg/src/lib/epg-timeline/epg-timeline.component.ts @@ -7,6 +7,7 @@ import { ElementRef, inject, input, + linkedSignal, output, signal, untracked, @@ -120,8 +121,13 @@ export class EpgTimelineComponent { private readonly nowMs = signal(Date.now()); readonly selectedKey = signal(null); - /** Day currently centred in the ribbon viewport. */ - private readonly viewDayKey = signal(getTodayEpgDateKey()); + /** Day centred in the ribbon. Seeded from the controlled `selectedDate` so a + * non-today date survives (re)mount and follows host changes; local + * navigation overrides via `commitDay`. Falls back to today when unset. */ + private readonly viewDayKey = linkedSignal(() => { + const key = this.selectedDate()?.trim(); + return key ? key : getTodayEpgDateKey(); + }); /** Ribbon scrolling + channel-select auto-focus, extracted from the view. */ private readonly scroll = new TimelineScrollController({