fix(images): scroll to the rescan's discovery alert only when the toast band covers it

D42 scrolled the alert into view after every rescan, so a user scrolled down
the grid was sent back to the top. It now scrolls only when the alert
intersects the toast's band (its scroll-margin-block-end).
This commit is contained in:
Vito Sansevero
2026-10-20 22:57:35 -07:00
parent b3d732fdf1
commit 9ba9f33019
2 changed files with 30 additions and 3 deletions
+24
View File
@@ -1592,6 +1592,30 @@ for (const width of [1600, 390]) {
});
}
// Final review m3: D42 scrolls only when the alert meets the toast's band, never back up to one above the window.
test('Rescan does not scroll back to a discovery alert above the window', async ({ page }) => {
await page.setViewportSize({ width: 1600, height: 1100 });
await page.route('**/prompt_manager/library/options*', route => route.abort('failed'));
await page.goto(workspace);
await expect(cards(page)).toHaveCount(2);
const alert = page.getByRole('alert');
await expect(alert).toHaveText('Categories could not be loaded. Rescan from Import to retry.');
await page.setViewportSize({ width: 1600, height: 260 });
// Import is at the top; the page is scrolled down to the last card while its dialog is open.
await page.getByRole('button', { name: 'Import', exact: true }).click();
await cards(page).last().evaluate(node => node.scrollIntoView({ block: 'end' }));
expect((await boxOf(alert)).bottom).toBeLessThan(0);
// The reload itself may move the page; what D42 must not do is scroll the alert back into view.
await alert.evaluate(node => {
window.alertScrolls = 0;
node.scrollIntoView = (...args) => { window.alertScrolls += 1; Element.prototype.scrollIntoView.apply(node, args); };
});
await importDialog(page).getByRole('button', { name: 'Rescan', exact: true }).click();
await expect(toast(page)).toHaveText(/^Rescan finished\. /);
await expect(alert).toBeAttached();
expect(await page.evaluate(() => window.alertScrolls)).toBe(0);
});
// #249 final review m8: D36's alphabetical categories hold whatever order the server sends.
test('toolbar categories are alphabetical regardless of server order', async ({ page }) => {
await page.route('**/prompt_manager/library/options*', route => route.fulfill({
+6 -3
View File
@@ -648,12 +648,15 @@ export function mountImages(main, api, parentScope, {embedded = false, fetchPage
}
}
/**
* D42: the concept's toast stays fixed at the bottom, so a discovery alert the rescan leaves is scrolled, only when
* it must be, clear of the toast's band (its `scroll-margin-block-end`).
* D42: the concept's toast stays fixed at the bottom, so a discovery alert the rescan leaves is scrolled clear of
* the toast's band (its `scroll-margin-block-end`) only when it is in that band; elsewhere the page stays put.
*/
function announceRescan(message) {
showWorkspaceToast(message);
if (!optionsStatus.hidden) optionsStatus.scrollIntoView({block:'nearest', inline:'nearest'});
if (optionsStatus.hidden) return;
const {top, bottom} = optionsStatus.getBoundingClientRect();
const band = parseFloat(getComputedStyle(optionsStatus).scrollMarginBlockEnd) || 0;
if (bottom > innerHeight - band && top < innerHeight) optionsStatus.scrollIntoView({block:'nearest', inline:'nearest'});
}
async function refreshInspectionCollection() {
if (scope.signal.aborted) return;