Files
kandev/apps/web/components/task/preview-tab.test.tsx
T
José Almeida 3e676e3bb2 feat(web): double-click tab to toggle maximize (#823)
* feat(web): double-click tab to toggle maximize

Adds a dblclick gesture on dockview tabs that toggles maximize/restore on
the containing group, mirroring the existing maximize button. Preview tabs
keep their promote-to-pin shortcut: first dblclick pins, then dblclick
maximizes. Sidebar group is excluded.

* fix(web): add cursor-pointer to tab wrappers for consistency

* fix(web): add cursor-pointer to plan-tab wrapper

* fix(web): make tab dblclick maximize resilient to layout rebuild

Refactor the dblclick-to-maximize handler so it reads the panel's group ID
and store state at call time instead of from a render-time closure, and
guards against firing while the dockview store is mid-layout-restore.

This addresses oscillation when double-clicking a session tab on a task
that has multiple sessions: the maximize call now stops event propagation,
respects isRestoringLayout, and always uses the live api.group.id, so it
remains correct even after dockview rebuilds groups via fromJSON.
2026-05-05 13:55:46 +01:00

95 lines
3.2 KiB
TypeScript

import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { render, fireEvent, screen, cleanup } from "@testing-library/react";
const mockPromote = vi.fn();
const mockMaximizeGroup = vi.fn();
const mockExitMaximizedLayout = vi.fn();
const storeState = {
promotePreviewToPinned: mockPromote,
preMaximizeLayout: null as object | null,
sidebarGroupId: "sidebar-group",
isRestoringLayout: false,
maximizeGroup: mockMaximizeGroup,
exitMaximizedLayout: mockExitMaximizedLayout,
};
vi.mock("@/lib/state/dockview-store", () => ({
useDockviewStore: Object.assign(
(selector: (state: typeof storeState) => unknown) => selector(storeState),
{ getState: () => storeState },
),
}));
vi.mock("dockview-react", () => ({
DockviewDefaultTab: () => null,
}));
vi.mock("@kandev/ui/context-menu", () => ({
ContextMenu: ({ children }: { children: React.ReactNode }) => <>{children}</>,
ContextMenuTrigger: ({ children }: { children: React.ReactNode }) => <>{children}</>,
ContextMenuContent: () => null,
ContextMenuItem: () => null,
ContextMenuSeparator: () => null,
}));
import { PreviewFileTab } from "./preview-tab";
const PREVIEW_TAB_TID = "preview-tab-file-editor";
type TabApi = {
id: string;
group: { id: string };
};
function makeProps(promoted: boolean) {
const api: TabApi = { id: "panel-1", group: { id: "group-a" } };
const containerApi = { getPanel: () => undefined, removePanel: () => {} };
return {
api,
containerApi,
params: { promoted },
} as unknown as React.ComponentProps<typeof PreviewFileTab>;
}
describe("PreviewTab dblclick sequencing", () => {
beforeEach(() => {
mockPromote.mockClear();
mockMaximizeGroup.mockClear();
mockExitMaximizedLayout.mockClear();
storeState.preMaximizeLayout = null;
storeState.isRestoringLayout = false;
});
afterEach(() => cleanup());
it("first dblclick on unpinned preview promotes to pinned (no maximize)", () => {
render(<PreviewFileTab {...makeProps(false)} />);
fireEvent.doubleClick(screen.getByTestId(PREVIEW_TAB_TID));
expect(mockPromote).toHaveBeenCalledWith("file-editor");
expect(mockMaximizeGroup).not.toHaveBeenCalled();
});
it("dblclick on pinned preview maximizes (no further promote)", () => {
render(<PreviewFileTab {...makeProps(true)} />);
fireEvent.doubleClick(screen.getByTestId(PREVIEW_TAB_TID));
expect(mockPromote).not.toHaveBeenCalled();
expect(mockMaximizeGroup).toHaveBeenCalledWith("group-a");
});
it("dblclick on pinned preview while maximized restores layout", () => {
storeState.preMaximizeLayout = { columns: [] };
render(<PreviewFileTab {...makeProps(true)} />);
fireEvent.doubleClick(screen.getByTestId(PREVIEW_TAB_TID));
expect(mockExitMaximizedLayout).toHaveBeenCalledTimes(1);
expect(mockMaximizeGroup).not.toHaveBeenCalled();
});
it("ignores dblclick while a layout restore is in progress", () => {
storeState.isRestoringLayout = true;
render(<PreviewFileTab {...makeProps(true)} />);
fireEvent.doubleClick(screen.getByTestId(PREVIEW_TAB_TID));
expect(mockMaximizeGroup).not.toHaveBeenCalled();
expect(mockExitMaximizedLayout).not.toHaveBeenCalled();
});
});