FN-8558: enlarge Quick Chat tablet drag target
Make the floating Quick Chat header easier to drag on tablet viewports. - Increase only the floating Chat header touch target in the tablet viewport band. - Cover floating header controls for empty and populated chats and scope the CSS contract. - Add a patch changeset for the Quick Chat usability fix. Files changed: .changeset/quick-chat-tablet-drag-header.md | 7 ++++ packages/dashboard/app/components/ChatView.css | 13 +++++++ packages/dashboard/app/components/__tests__/ChatView.mobile.test.tsx | 41 ++++++++++++++++++++++ packages/dashboard/app/components/__tests__/FloatingWindow.test.tsx | 20 +++++++++++ 4 files changed, 81 insertions(+) Fusion-Task-Id: FN-8558 Fusion-Task-Lineage: 2d2f788b-b417-4fa3-b066-8790858b393a Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/quick-chat-tablet-drag-header.md
Normal file
7
.changeset/quick-chat-tablet-drag-header.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Make the Quick Chat header easier to drag on tablets.
|
||||
category: fix
|
||||
dev: Enlarges only the floating Chat tablet drag target while preserving phone and desktop layouts.
|
||||
@@ -910,6 +910,19 @@ The old Quick Chat panel is replaced by the full ChatView inside a movable Float
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:QuickChat 2026-07-24-00:00:
|
||||
Tablet Quick Chat keeps its delegated ViewHeader drag handle but needs a modestly taller touch target
|
||||
than canonical desktop chrome. Scope the token-based height to the movable tablet band so phone sheets,
|
||||
desktop geometry, and every other ViewHeader consumer remain unchanged.
|
||||
*/
|
||||
@media (min-width: 769px) and (max-width: 1024px) and (min-height: 481px) {
|
||||
.chat-view--floating .view-header {
|
||||
min-height: calc(var(--view-header-min-height) + var(--space-sm));
|
||||
height: calc(var(--view-header-min-height) + var(--space-sm));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:ChatModal 2026-06-22-14:38:
|
||||
Floating Chat can become narrow while the browser viewport remains desktop-sized. Mirror the mobile one-pane layout with a class driven by the modal ResizeObserver so a narrow pop-out hides the sidebar and shows the mobile thread/list surfaces.
|
||||
|
||||
@@ -803,6 +803,47 @@ describe("ChatView mobile behavior", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps floating Quick Chat header controls for empty and populated content", async () => {
|
||||
const restoreMatchMedia = mockDesktopViewport();
|
||||
const renderFloatingChat = async () => {
|
||||
const onMaximize = vi.fn();
|
||||
const onMinimize = vi.fn();
|
||||
const onClose = vi.fn();
|
||||
const rendered = await renderWithAct(
|
||||
<ChatView
|
||||
projectId="proj-123"
|
||||
addToast={vi.fn()}
|
||||
floating
|
||||
onMaximize={onMaximize}
|
||||
onMinimize={onMinimize}
|
||||
onClose={onClose}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(document.querySelector(".chat-view--floating .view-header")).toBeInTheDocument();
|
||||
for (const testId of ["chat-modal-maximize", "chat-modal-minimize", "chat-modal-close"]) {
|
||||
expect(screen.getByTestId(testId)).toBeInTheDocument();
|
||||
}
|
||||
return rendered;
|
||||
};
|
||||
|
||||
try {
|
||||
setupMockChat({ sessions: [], filteredSessions: [], activeSession: null });
|
||||
const emptyRender = await renderFloatingChat();
|
||||
emptyRender.unmount();
|
||||
|
||||
setupMockChat({
|
||||
sessions: [activeSessionFixture],
|
||||
filteredSessions: [activeSessionFixture],
|
||||
activeSession: activeSessionFixture,
|
||||
messages: [{ id: "message-001", sessionId: "session-001", role: "assistant", content: "Populated response", createdAt: "2026-04-08T00:00:00.000Z" }],
|
||||
});
|
||||
await renderFloatingChat();
|
||||
} finally {
|
||||
restoreMatchMedia.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it("floating narrow mode: quick session switcher includes New Chat", async () => {
|
||||
const restoreMatchMedia = mockDesktopViewport();
|
||||
const originalResizeObserver = globalThis.ResizeObserver;
|
||||
|
||||
@@ -5,6 +5,7 @@ import { loadAllAppCss, loadStylesCss } from "../../test/cssFixture";
|
||||
import { FLOATING_WINDOW_GEOMETRY_CHANGE_EVENT, FloatingWindow } from "../FloatingWindow";
|
||||
|
||||
const floatingWindowCss = readFileSync("app/components/FloatingWindow.css", "utf8");
|
||||
const chatViewCss = readFileSync("app/components/ChatView.css", "utf8");
|
||||
const allAppCss = loadAllAppCss();
|
||||
const stylesCss = loadStylesCss();
|
||||
|
||||
@@ -313,6 +314,25 @@ describe("FloatingWindow", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("gives only delegated Quick Chat headers a larger tablet touch target", () => {
|
||||
const tabletRule = mediaBlockFor(
|
||||
chatViewCss,
|
||||
"(min-width: 769px) and (max-width: 1024px) and (min-height: 481px)",
|
||||
);
|
||||
const floatingHeaderRule = cssRuleFor(chatViewCss, ".chat-view--floating .view-header");
|
||||
|
||||
expect(tabletRule).toContain(".chat-view--floating .view-header");
|
||||
expect(tabletRule).toContain("min-height: calc(var(--view-header-min-height) + var(--space-sm));");
|
||||
expect(tabletRule).toContain("height: calc(var(--view-header-min-height) + var(--space-sm));");
|
||||
expect(floatingHeaderRule).toContain("cursor: grab;");
|
||||
expect(floatingHeaderRule).toContain("user-select: none;");
|
||||
expect(floatingHeaderRule).toContain("touch-action: none;");
|
||||
|
||||
// The explicit tablet query leaves the ≤768px sheet and >1024px desktop header geometry canonical.
|
||||
expect(chatViewCss).not.toMatch(/@media \(max-width: 768px\)\s*\{\s*\.chat-view--floating \.view-header\s*\{/);
|
||||
expect(chatViewCss).not.toMatch(/@media \(min-width: 1025px\)[\s\S]*\.chat-view--floating \.view-header/);
|
||||
});
|
||||
|
||||
it("keeps every tablet movable-modal drag handle on the explicit touch-action none contract", () => {
|
||||
const tabletStylesStart = stylesCss.indexOf("@media (min-width: 769px) and (max-width: 1024px)");
|
||||
const mobileStylesStart = stylesCss.indexOf("@media (max-width: 768px)", tabletStylesStart);
|
||||
|
||||
Reference in New Issue
Block a user