diff --git a/tools/web-ux/browser-tests/console_history_shell_test.ts b/tools/web-ux/browser-tests/console_history_shell_test.ts index 36e4c3cbc..29a9b522e 100644 --- a/tools/web-ux/browser-tests/console_history_shell_test.ts +++ b/tools/web-ux/browser-tests/console_history_shell_test.ts @@ -41,6 +41,39 @@ async function historyRequestCount(baseUrl: string): Promise { return state.history_requests; } +async function checkSidebarModeSwitch(page: Page, name: string): Promise { + const button = page.getByRole("button", { name, exact: true }); + await button.hover(); + const samples = await button.evaluate(async (button) => { + const frame = button.closest(".sidebar-frame")!; + await Promise.all(frame.getAnimations({ subtree: true }).map((animation) => animation.finished)); + const panel = frame.querySelector(".sidebar-frame__bevel")!; + const content = frame.querySelector(".sidebar-frame-content")!; + const sample = () => ({ + panel: panel.getBoundingClientRect().toJSON(), + content: content.getBoundingClientRect().toJSON(), + button: button.getBoundingClientRect().toJSON(), + folded: frame.classList.contains("folded"), + motion: frame.getAnimations({ subtree: true }).filter((animation) => + animation instanceof CSSTransition && ["width", "transform"].includes(animation.transitionProperty) + ).length, + }); + const samples = [sample()]; + (button as HTMLButtonElement).click(); + const start = performance.now(); + do { + await new Promise((resolve) => requestAnimationFrame(() => resolve())); + samples.push(sample()); + } while (performance.now() - start < 280); + return samples; + }); + for (const sample of samples) { + assertEquals(sample, samples[0], `${name} must keep the open panel stationary without slide animation`); + assertEquals(sample.folded, false); + assertEquals(sample.motion, 0); + } +} + async function checkSidebarSlide(page: Page, mobile: boolean, opening: boolean): Promise { const toggle = page.locator(mobile ? ".app-shell__mobile-sidebar-toggle" : ".sidebar-frame"); const result = await toggle.evaluate(async (button, { mobile, opening }) => { @@ -394,7 +427,7 @@ async function checkConsoleHistory(viewportHeight: number): Promise { const settleSidebar = () => sidebar.evaluate(async (element) => { await Promise.all(element.getAnimations({ subtree: true }).map((animation) => animation.finished)); }); - await page.getByRole("button", { name: "Unpin sidebar", exact: true }).click(); + await checkSidebarModeSwitch(page, "Unpin sidebar"); await page.mouse.move(1000, 30); await page.locator(".sidebar-frame.folded").waitFor(); await settleSidebar(); @@ -415,12 +448,12 @@ async function checkConsoleHistory(viewportHeight: number): Promise { await page.locator(".sidebar-frame.folded").waitFor(); await settleSidebar(); await sidebar.hover({ position: { x: 10, y: 40 } }); - await page.getByRole("button", { name: "Pin sidebar", exact: true }).click(); + await checkSidebarModeSwitch(page, "Pin sidebar"); await page.mouse.move(1000, 30); await page.waitForTimeout(250); assertEquals(await page.locator(".sidebar-frame.folded").count(), 0); assertEquals(await page.evaluate(() => localStorage.getItem("yoi.sidebar.mode.v1")), "pinned"); - await page.getByRole("button", { name: "Unpin sidebar", exact: true }).click(); + await checkSidebarModeSwitch(page, "Unpin sidebar"); // Hold a preview open across reload; only the mode should be restored. await sidebarLink.focus(); await page.mouse.move(1000, 30); diff --git a/web/workspace/src/lib/workspace/sidebar/sidebar.css b/web/workspace/src/lib/workspace/sidebar/sidebar.css index f641cfd28..27ff7d434 100644 --- a/web/workspace/src/lib/workspace/sidebar/sidebar.css +++ b/web/workspace/src/lib/workspace/sidebar/sidebar.css @@ -13,13 +13,14 @@ grid-column: 1; grid-row: 1 / 3; width: var(--sidebar-width); - transition: width var(--sidebar-motion-duration) var(--sidebar-motion-easing); + /* Reserving layout space is a mode switch, not a panel open/close. */ min-width: 0; min-height: 0; overflow: hidden; } .sidebar-frame .bevel.sidebar-frame__bevel { - width: 100%; + /* Keep the open panel's width identical in pinned and overlay modes. */ + width: var(--sidebar-width); min-width: 0; min-height: 0; }