feat(epg): add a Guide button to the timeline toolbar

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Fable 5.1 committed 2026-09-06 12:58:23 +02:00
1 parent 44e421103e
commit 688fd4584d
5 files changed
+60 -6

No files matched your search

@@ -28,6 +28,7 @@ export class StubEpgTimelineComponent {
readonly collapsed = input(false);
readonly summary = input<LiveEpgPanelSummary | null>(null);
readonly summaryLabelKey = input('');
readonly guideAvailable = input(false);
readonly programActivated = output<{
program?: EpgProgram;
type: 'timeshift' | 'live';
@@ -37,4 +38,5 @@ export class StubEpgTimelineComponent {
readonly openEpgSettings = output<void>();
readonly retry = output<void>();
readonly collapsedChange = output<boolean>();
readonly openGuide = output<void>();
}
@@ -55,7 +55,10 @@
summary()?.title
}}</span>
@if (progress() !== null) {
<span class="epg-timeline__summary-bar" aria-hidden="true">
<span
class="epg-timeline__summary-bar"
aria-hidden="true"
>
<i [style.width.%]="progress()"></i>
</span>
}
@@ -97,6 +100,17 @@
</button>
}
@if (showRibbonControls()) {
@if (guideAvailable()) {
<button
type="button"
class="epg-timeline__guide"
(click)="openGuide.emit()"
[matTooltip]="'EPG.TIMELINE.OPEN_GUIDE' | translate"
>
<mat-icon>grid_view</mat-icon>
{{ 'EPG.TIMELINE.GUIDE' | translate }}
</button>
}
<button
type="button"
class="epg-timeline__jump"
@@ -145,7 +159,8 @@
}
</span>
<small>{{
viewDate() | date: 'EEE, d MMM' : '' : currentLocale()
viewDate()
| date: 'EEE, d MMM' : '' : currentLocale()
}}</small>
</div>
<button
@@ -166,13 +181,17 @@
@if (state === 'ribbon' && !archivePlaybackAvailable()) {
<div class="epg-timeline__notice" role="note">
<mat-icon>info</mat-icon>
<span>{{ 'EPG.TIMELINE.SCHEDULE_ONLY_NOTICE' | translate }}</span>
<span>{{
'EPG.TIMELINE.SCHEDULE_ONLY_NOTICE' | translate
}}</span>
</div>
}
@switch (state) {
@case ('loading') {
<div class="epg-timeline__ribbon epg-timeline__ribbon--skeleton">
<div
class="epg-timeline__ribbon epg-timeline__ribbon--skeleton"
>
@for (width of skeletonWidths; track $index) {
<div
class="epg-timeline__sk"
@@ -161,7 +161,8 @@
}
}
.epg-timeline__jump {
.epg-timeline__jump,
.epg-timeline__guide {
display: inline-flex;
align-items: center;
gap: 7px;
@@ -189,6 +190,11 @@
}
}
.epg-timeline__guide {
color: $accent-text;
border-color: $accent-blue;
}
.epg-timeline__stepper {
display: inline-flex;
align-items: center;
@@ -40,7 +40,9 @@ describe('EpgTimelineComponent', () => {
providers: [
{
provide: MatDialog,
useValue: { open: () => ({ afterClosed: () => of(undefined) }) },
useValue: {
open: () => ({ afterClosed: () => of(undefined) }),
},
},
{
provide: TranslateService,
@@ -48,6 +50,9 @@ describe('EpgTimelineComponent', () => {
currentLang: 'en',
defaultLang: 'en',
onLangChange: new BehaviorSubject(null),
onTranslationChange: new BehaviorSubject(null),
onDefaultLangChange: new BehaviorSubject(null),
get: (key: string) => of(key),
},
},
],
@@ -238,4 +243,23 @@ describe('EpgTimelineComponent', () => {
});
expect(component.scale()).toBeGreaterThan(3);
});
it('shows a Guide button only when the host offers a guide and emits openGuide', () => {
setInputs({ programs: [programAt(0, 120, 'Now')] });
fixture.detectChanges();
expect(
fixture.nativeElement.querySelector('.epg-timeline__guide')
).toBeNull();
const openGuide = jest.fn();
component.openGuide.subscribe(openGuide);
setInputs({ guideAvailable: true });
fixture.detectChanges();
const button = fixture.nativeElement.querySelector(
'.epg-timeline__guide'
) as HTMLButtonElement;
expect(button).not.toBeNull();
button.click();
expect(openGuide).toHaveBeenCalledTimes(1);
});
});
@@ -99,6 +99,8 @@ export class EpgTimelineComponent {
readonly summary = input<EpgTimelineSummary | null>(null);
readonly summaryLabelKey = input('EPG.CURRENT_PROGRAM');
readonly offsetMinutes = input(0);
/** The host can open a multi-channel guide; renders the Guide button. */
readonly guideAvailable = input(false);
readonly programActivated = output<EpgProgramActivationEvent>();
readonly returnToLive = output<void>();
@@ -106,6 +108,7 @@ export class EpgTimelineComponent {
readonly openEpgSettings = output<void>();
readonly retry = output<void>();
readonly collapsedChange = output<boolean>();
readonly openGuide = output<void>();
private readonly programmeDialog = inject(EpgProgrammeDialogService);
private readonly translate = inject(TranslateService);