Files
kandev/apps/web/hooks/use-quick-chat-width.test.ts
T
2026-07-15 09:13:52 +01:00

124 lines
4.5 KiB
TypeScript

import { act, renderHook } from "@testing-library/react";
import { beforeEach, describe, expect, it } from "vitest";
import {
clampQuickChatWidth,
QUICK_CHAT_WIDTH_LIMITS,
useQuickChatWidth,
} from "./use-quick-chat-width";
const WIDTH_PROPERTY = "--quick-chat-width";
function mouseDown(clientX: number, dialog: HTMLElement) {
const handle = document.createElement("button");
dialog.appendChild(handle);
return {
clientX,
currentTarget: handle,
preventDefault: () => {},
} as unknown as React.MouseEvent;
}
function mouseMove(clientX: number) {
document.dispatchEvent(new MouseEvent("mousemove", { clientX, bubbles: true }));
}
function mouseUp() {
document.dispatchEvent(new MouseEvent("mouseup", { bubbles: true }));
}
describe("quick chat dialog width", () => {
beforeEach(() => {
window.localStorage.clear();
Object.defineProperty(window, "innerWidth", { configurable: true, value: 1200 });
document.body.style.cursor = "";
document.body.style.userSelect = "";
});
it("defaults to 80 percent of the viewport", () => {
const { result } = renderHook(() => useQuickChatWidth());
expect(result.current.width).toBe(960);
});
it("restores and clamps a persisted width", () => {
window.localStorage.setItem(QUICK_CHAT_WIDTH_LIMITS.storageKey, JSON.stringify(5000));
const { result } = renderHook(() => useQuickChatWidth());
expect(result.current.width).toBe(1200 - QUICK_CHAT_WIDTH_LIMITS.viewportMargin);
});
it("grows symmetrically when the right edge moves right", () => {
const { result } = renderHook(() => useQuickChatWidth());
const dialog = document.createElement("div");
act(() => result.current.rightResizeHandleProps.onMouseDown(mouseDown(900, dialog)));
act(() => mouseMove(950));
expect(dialog.style.getPropertyValue(WIDTH_PROPERTY)).toBe("1060px");
expect(result.current.width).toBe(960);
act(() => mouseUp());
expect(result.current.width).toBe(1060);
});
it("grows symmetrically when the left edge moves left", () => {
const { result } = renderHook(() => useQuickChatWidth());
const dialog = document.createElement("div");
act(() => result.current.leftResizeHandleProps.onMouseDown(mouseDown(300, dialog)));
act(() => mouseMove(250));
expect(dialog.style.getPropertyValue(WIDTH_PROPERTY)).toBe("1060px");
expect(result.current.width).toBe(960);
act(() => mouseUp());
expect(result.current.width).toBe(1060);
});
it("persists the final width on mouseup and restores it after remount", () => {
const first = renderHook(() => useQuickChatWidth());
const dialog = document.createElement("div");
act(() => first.result.current.rightResizeHandleProps.onMouseDown(mouseDown(900, dialog)));
act(() => mouseMove(950));
act(() => mouseUp());
first.unmount();
const second = renderHook(() => useQuickChatWidth());
expect(second.result.current.width).toBe(1060);
});
it("clamps a drag on viewport resize without rendering until mouseup", () => {
const { result } = renderHook(() => useQuickChatWidth());
const dialog = document.createElement("div");
act(() => result.current.rightResizeHandleProps.onMouseDown(mouseDown(900, dialog)));
act(() => mouseMove(950));
Object.defineProperty(window, "innerWidth", { configurable: true, value: 1000 });
act(() => window.dispatchEvent(new Event("resize")));
expect(dialog.style.getPropertyValue(WIDTH_PROPERTY)).toBe("968px");
expect(result.current.width).toBe(960);
act(() => mouseUp());
expect(result.current.width).toBe(968);
});
it("rebases an active drag after the viewport changes", () => {
const { result } = renderHook(() => useQuickChatWidth());
const dialog = document.createElement("div");
act(() => result.current.rightResizeHandleProps.onMouseDown(mouseDown(900, dialog)));
act(() => mouseMove(950));
Object.defineProperty(window, "innerWidth", { configurable: true, value: 1000 });
act(() => window.dispatchEvent(new Event("resize")));
Object.defineProperty(window, "innerWidth", { configurable: true, value: 1200 });
act(() => window.dispatchEvent(new Event("resize")));
act(() => mouseMove(951));
expect(dialog.style.getPropertyValue(WIDTH_PROPERTY)).toBe("970px");
act(() => mouseUp());
});
it("clamps to the minimum and current viewport", () => {
expect(clampQuickChatWidth(100, 1200)).toBe(QUICK_CHAT_WIDTH_LIMITS.minWidth);
expect(clampQuickChatWidth(5000, 900)).toBe(900 - QUICK_CHAT_WIDTH_LIMITS.viewportMargin);
});
});