fix(epg): scroll the programme guide to now on open and on Now/N

The guide opened at midnight and the Now button and N key never moved the
timeline horizontally:

- scrollToNow issued a horizontal smooth scroll followed by the CDK's
  vertical scrollToIndex; in Chromium the second smooth scroll cancels the
  first one's animation. Both axes now go in one scrollTo call.
- The initial jump ran in afterNextRender before the virtual viewport had
  rendered any rows, so the scroll width was the client width and
  scrollLeft was clamped to 0. It now waits for the first rendered range.

Closes #1733

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5.5 committed 2026-10-01 06:44:34 +02:00
1 parent 5d50995c30
commit 7a6b3d9dc8
7 files changed
+210 -39

No files matched your search

@@ -1,3 +1,4 @@
import { Page } from '@playwright/test';
import {
buildM3uContent,
channelItemByTitle,
@@ -40,6 +41,39 @@ function xmltvWithCurrentProgramme(
`;
}
/** True when the now-line is painted inside the visible programme lane. */
function nowLineInLane(page: Page): Promise<boolean> {
return page.evaluate(() => {
const lane = document
.querySelector('.epg-guide__now-clip')
?.getBoundingClientRect();
const line = document
.querySelector('.epg-guide__now-line')
?.getBoundingClientRect();
return (
!!lane &&
!!line &&
line.left >= lane.left &&
line.right <= lane.right
);
});
}
/** Scroll the lane to whichever end of the day is farther from now. */
async function scrollAwayFromNow(page: Page): Promise<void> {
await page.evaluate(() => {
const viewport = document.querySelector(
'.epg-guide__viewport'
) as HTMLElement;
const badge = document.querySelector(
'.epg-guide__now-badge'
) as HTMLElement;
const nowLeft = parseFloat(badge.style.left);
const end = viewport.scrollWidth - viewport.clientWidth;
viewport.scrollTo({ left: nowLeft > end / 2 ? 0 : end });
});
}
test('@epg @electron opens the programme guide with the playlist channels, switches channels and keeps the player mounted', async ({
dataDir,
}) => {
@@ -133,6 +167,23 @@ test('@epg @electron opens the programme guide with the playlist channels, switc
timeout: 20000,
});
// The guide opens on "now", and the Now button and N jump back to it
// on both axes at once (#1733: the lane stayed at midnight).
await expect.poll(() => nowLineInLane(app.mainWindow)).toBe(true);
await scrollAwayFromNow(app.mainWindow);
await expect.poll(() => nowLineInLane(app.mainWindow)).toBe(false);
await guide.locator('.guide-toolbar__now').click();
await expect.poll(() => nowLineInLane(app.mainWindow)).toBe(true);
await scrollAwayFromNow(app.mainWindow);
await expect.poll(() => nowLineInLane(app.mainWindow)).toBe(false);
// Keys are left alone while a toolbar button holds the focus.
await app.mainWindow.evaluate(() =>
(document.activeElement as HTMLElement | null)?.blur()
);
await app.mainWindow.keyboard.press('n');
await expect.poll(() => nowLineInLane(app.mainWindow)).toBe(true);
// "Only with EPG" hides the silent channel once coverage is known.
const toggle = guide.locator('.guide-toolbar__toggle input');
await expect(toggle).toBeEnabled({ timeout: 20000 });