fix(ui): keep detail back navigation available while scrolling (#1576)

This commit is contained in:
4gray authored and GitHub committed 2026-09-10 21:34:10 +02:00
1 parent e76447975b
commit fff022afe4
28 files changed
+679 -138

No files matched your search

+11
View File
@@ -250,6 +250,17 @@ test('@web @m3u @tmdb browse and watch keep the adjusted volume', async ({
)
)
.toBe(0.25);
// M3U has no browse Back target, but the sticky watch control can close it.
const shell = detail(page).locator('app-portal-detail-shell');
await shell
.getByRole('button', { name: 'Close player', exact: true })
.first()
.click();
await expect(inlineVideo(page)).toHaveCount(0);
await expect(shell.locator('.shell__back-button')).toHaveCount(0);
await shell.focus();
await page.keyboard.press('Escape');
await expect(playButton).toBeVisible();
});
for (const theme of ['light', 'dark']) {
+9
View File
@@ -1218,6 +1218,15 @@ test('@stalker series watched toggle — embedded series marks and clears from t
await expect(seriesToggle).toContainText(
`Mark series as watched (${episodeCount})`
);
// The menu owns the first Escape; inline Stalker detail owns the next.
const detailUrl = page.url();
await page.keyboard.press('Escape');
await expect(seriesToggle).toBeHidden();
await menuTrigger.focus();
await page.keyboard.press('Escape');
await expect(page.locator('app-portal-detail-shell')).toHaveCount(0);
await expect(page).toHaveURL(detailUrl);
await expect(card).toBeVisible();
});
test('@stalker series — seasons load for a series item', async ({
+61
View File
@@ -1400,5 +1400,66 @@ for (const theme of ['light', 'dark']) {
.poll(() => shell.evaluate((el) => el.scrollTop))
.toBeGreaterThan(0);
});
test(`@xtream sticky detail Back ${section} (${theme})`, async ({
page,
}, testInfo) => {
await page.setViewportSize({ width: 1200, height: 540 });
await addXtreamPortal(page);
await page
.getByRole('link', { name: section, exact: true })
.click();
await page.locator('app-grid-list mat-card').first().click();
const shell = page.locator('app-portal-detail-shell');
await expect(
shell.getByRole('heading', { level: 1 })
).toBeVisible();
await shell.focus();
await page.evaluate(
(dark) => document.body.classList.toggle('dark-theme', dark),
theme === 'dark'
);
const back = shell.getByRole('button', {
name: 'Back',
exact: true,
});
await expect(back).toBeVisible();
const backOffset = () =>
back.evaluate((el) => {
const owner = el.closest('app-portal-detail-shell');
return owner
? el.getBoundingClientRect().top -
owner.getBoundingClientRect().top
: NaN;
});
await expect.poll(backOffset).toBeCloseTo(16, 0);
await page.keyboard.press('End');
await expect
.poll(() => shell.evaluate((el) => el.scrollTop))
.toBeGreaterThan(0);
await waitForScrollIdle(shell);
await expect.poll(backOffset).toBeCloseTo(16, 0);
await expect(back).toBeInViewport();
await shell.screenshot({
path: testInfo.outputPath(
`sticky-back-${section}-${theme}.png`
),
});
await back.click();
await expect(shell).toHaveCount(0);
await page.locator('app-grid-list mat-card').first().click();
await expect(
shell.getByRole('heading', { level: 1 })
).toBeVisible();
await shell.focus();
await page.keyboard.press('End');
// Hover/focus must not make a tooltip consume the advertised Esc.
await page.clock.install();
await back.focus();
await back.hover();
await page.clock.runFor(500);
await page.keyboard.press('Escape');
await expect(shell).toHaveCount(0);
});
}
}