diff --git a/e2e/coach-layout.spec.ts b/e2e/coach-layout.spec.ts index 07b36d83e..af710b8a4 100644 --- a/e2e/coach-layout.spec.ts +++ b/e2e/coach-layout.spec.ts @@ -358,6 +358,12 @@ test.describe("Coach page frame", () => { const aside = (await panel(page).boundingBox())!; expect(aside.x).toBeGreaterThanOrEqual(open.threadX + open.thread); await expectPanelFlush(page, width, 900); + // Open, the toggle stands beside the panel's leading edge. + const openToggle = (await toggle(page).boundingBox())!; + expect(openToggle.x + openToggle.width).toBeLessThanOrEqual(aside.x); + expect(aside.x - (openToggle.x + openToggle.width)).toBeLessThanOrEqual( + 24, + ); await expectNoSidewaysScroll(page); await shot(page, testInfo, `coach-frame-${width}-open`); @@ -366,6 +372,13 @@ test.describe("Coach page frame", () => { await waitForWidth(panel(page), 0); await expect(toggle(page)).toHaveAttribute("aria-expanded", "false"); const closed = await columnWidths(page); + // Closed, it returns to the trailing edge of the top bar. + await expect + .poll(async () => { + const box = (await toggle(page).boundingBox())!; + return Math.round(width - (box.x + box.width)); + }) + .toBeLessThanOrEqual(24); expect(Math.abs(closed.thread - open.thread)).toBeLessThanOrEqual(1); expect(Math.abs(closed.composer - open.composer)).toBeLessThanOrEqual(1); // Closed, nothing inside the panel is reachable. diff --git a/src/components/insights/coach-panel/conversations-panel.tsx b/src/components/insights/coach-panel/conversations-panel.tsx index eda23e3df..c2c323764 100644 --- a/src/components/insights/coach-panel/conversations-panel.tsx +++ b/src/components/insights/coach-panel/conversations-panel.tsx @@ -37,7 +37,8 @@ import { useDeleteCoachConversationWithUndo } from "./use-coach"; * Below 1280 px it is a sheet from the right that opens when asked and * closes again when a conversation is picked or a new chat starts. * - * The toggle sits at the trailing edge of the top bar. The panel header + * The toggle sits at the trailing edge of the top bar and, while the docked + * panel is open, beside the panel's leading edge. The panel header * carries the title, a link to Plans, the settings gear and, in the sheet, * a close button. The round New chat button sits at the bottom right. * @@ -245,12 +246,27 @@ export function ConversationsPanel({ aria-label={toggleLabel} title={toggleLabel} data-slot="coach-panel-toggle" - className="text-muted-foreground hover:text-foreground size-11" + className={cn( + "text-muted-foreground hover:text-foreground size-11", + // Docked and open, the toggle keeps the panel's width as its + // right margin, so it stays beside the panel's leading edge and + // travels with it as the panel opens and shuts. + "transition-[margin] duration-200 ease-linear motion-reduce:transition-none", + docked && dockedOpen && "mr-72", + )} > + {/* Mirrored, so the chevron points the way the panel moves on a + click: right to shut it, left to open it. */} {expanded ? ( -