FN-5856: preserve closed quick chat panel size
Prevent closed quick chat FAB drags from overwriting the saved desktop panel size. - gate desktop panel resize clamping and persistence behind the panel open state - pass the quick chat open state into the resize hook so closed FAB moves leave saved dimensions untouched - add a regression test covering closed-FAB dragging and reopened panel dimensions - add a patch changeset for the published CLI package Files changed: .changeset/fn-5856-quick-chat-resize.md | 5 +++++ packages/dashboard/app/components/QuickChatFAB.tsx | 12 +++++------ .../app/components/__tests__/QuickChatFAB.test.tsx | 23 ++++++++++++++++++++++ 3 files changed, 34 insertions(+), 6 deletions(-) Fusion-Task-Id: FN-5856 Fusion-Task-Lineage: 3358f2b0-ae8d-468c-8e9e-71a1b5365061
This commit is contained in:
@@ -572,7 +572,7 @@ function useDraggable(projectId?: string, externalDidDragRef?: React.MutableRefO
|
||||
};
|
||||
}
|
||||
|
||||
function usePanelResize(projectId: string | undefined, fabRight: number, fabBottom: number) {
|
||||
function usePanelResize(projectId: string | undefined, fabRight: number, fabBottom: number, isOpen: boolean) {
|
||||
const storageKey = `fusion:quick-chat-size-${projectId || "default"}`;
|
||||
|
||||
const isDesktopViewport = useCallback(
|
||||
@@ -631,19 +631,19 @@ function usePanelResize(projectId: string | undefined, fabRight: number, fabBott
|
||||
const [anchorOffset, setAnchorOffset] = useState<PanelAnchorOffset>({ right: 0, bottom: 0 });
|
||||
|
||||
useEffect(() => {
|
||||
if (!isDesktopViewport()) return;
|
||||
if (!isOpen || !isDesktopViewport()) return;
|
||||
const effective = { right: fabRight + anchorOffset.right, bottom: fabBottom + anchorOffset.bottom };
|
||||
setPanelSize((current) => clampPanelSize(current, effective.right, effective.bottom));
|
||||
}, [clampPanelSize, isDesktopViewport, fabRight, fabBottom, anchorOffset]);
|
||||
}, [anchorOffset, clampPanelSize, fabBottom, fabRight, isDesktopViewport, isOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isDesktopViewport()) return;
|
||||
if (!isOpen || !isDesktopViewport()) return;
|
||||
try {
|
||||
localStorage.setItem(storageKey, JSON.stringify(panelSize));
|
||||
} catch {
|
||||
// Ignore storage errors (private mode / quota)
|
||||
}
|
||||
}, [isDesktopViewport, panelSize, storageKey]);
|
||||
}, [isDesktopViewport, isOpen, panelSize, storageKey]);
|
||||
|
||||
const handleResizeStart = useCallback(
|
||||
(event: React.PointerEvent<HTMLDivElement>) => {
|
||||
@@ -1003,7 +1003,7 @@ export function QuickChatFAB({
|
||||
|
||||
// Panel stays 60px above FAB (FAB is 48px tall + 12px gap)
|
||||
const panelY = position.y + 60;
|
||||
const { panelSize, anchorOffset, handleResizeStart } = usePanelResize(projectId, position.x, panelY);
|
||||
const { panelSize, anchorOffset, handleResizeStart } = usePanelResize(projectId, position.x, panelY, isOpen);
|
||||
const shouldApplyDesktopPanelSize = typeof window !== "undefined" && window.innerWidth > QUICK_CHAT_DESKTOP_BREAKPOINT;
|
||||
|
||||
// Chat session hook
|
||||
|
||||
@@ -988,6 +988,29 @@ describe("QuickChatFAB session-first UX", () => {
|
||||
expect(saved).toContain("\"y\"");
|
||||
});
|
||||
|
||||
it("keeps persisted desktop panel size when dragging a closed FAB", async () => {
|
||||
const persistedSize = { width: 420, height: 360 };
|
||||
localStorage.setItem("fusion:quick-chat-size-proj-1", JSON.stringify(persistedSize));
|
||||
|
||||
render(<QuickChatFAB addToast={vi.fn()} projectId="proj-1" />);
|
||||
|
||||
const fab = screen.getByTestId("quick-chat-fab");
|
||||
fireEvent.pointerDown(fab, { pointerId: 44, pointerType: "mouse", button: 0, clientX: 960, clientY: 700 });
|
||||
fireEvent.pointerMove(document, { pointerId: 44, pointerType: "mouse", clientX: 900, clientY: 620 });
|
||||
fireEvent.pointerUp(document, { pointerId: 44, pointerType: "mouse", clientX: 900, clientY: 620 });
|
||||
|
||||
expect(screen.queryByTestId("quick-chat-panel")).toBeNull();
|
||||
expect(JSON.parse(localStorage.getItem("fusion:quick-chat-size-proj-1") || "null")).toEqual(persistedSize);
|
||||
|
||||
fireEvent.click(fab);
|
||||
expect(screen.queryByTestId("quick-chat-panel")).toBeNull();
|
||||
|
||||
fireEvent.click(fab);
|
||||
|
||||
const panel = await screen.findByTestId("quick-chat-panel");
|
||||
expect(panel).toHaveStyle({ width: "420px", height: "360px" });
|
||||
});
|
||||
|
||||
it("shows jump-to-latest only after leaving live tail and scrolls back on click", async () => {
|
||||
mockFetchChatMessages.mockResolvedValueOnce({
|
||||
messages: [
|
||||
|
||||
Reference in New Issue
Block a user