Reuse the existing workspace drag interaction for pinned rows and persist their local order across grouping modes. Keep pressed and dragged row scrims aligned with the active backdrop, and preserve memoized row rendering on web.
114 lines
3.4 KiB
TypeScript
114 lines
3.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import type {
|
|
SidebarProjectEntry,
|
|
SidebarWorkspacePlacement,
|
|
} from "@/hooks/sidebar-workspaces-view-model";
|
|
import { splitPinnedSidebarGroups } from "@/hooks/use-sidebar-pins";
|
|
|
|
function placement(workspaceKey: string): SidebarWorkspacePlacement {
|
|
return {
|
|
workspaceKey,
|
|
serverId: "s1",
|
|
workspaceId: workspaceKey,
|
|
projectViewKey: "p1",
|
|
projectName: "Project 1",
|
|
projectKind: "git",
|
|
workspaceKind: "worktree",
|
|
name: workspaceKey,
|
|
};
|
|
}
|
|
|
|
function project(projectKey: string, workspaces: SidebarWorkspacePlacement[]): SidebarProjectEntry {
|
|
return {
|
|
viewKey: projectKey,
|
|
projectName: projectKey,
|
|
projectKind: "git",
|
|
iconWorkingDir: "",
|
|
hosts: [],
|
|
workspaces,
|
|
};
|
|
}
|
|
|
|
describe("splitPinnedSidebarGroups", () => {
|
|
it("drops the empty shell when every chat of a project is pinned", () => {
|
|
const only = placement("w1");
|
|
const projects = [project("p1", [only])];
|
|
const result = splitPinnedSidebarGroups({
|
|
projects,
|
|
keys: {
|
|
pinnedWorkspaceKeys: ["w1"],
|
|
pinnedAtByKey: { w1: "2026-01-01T00:00:00Z" },
|
|
},
|
|
pinnedWorkspaceOrder: [],
|
|
});
|
|
expect(result.pinnedChats).toHaveLength(1);
|
|
expect(result.unpinnedProjects).toHaveLength(0);
|
|
});
|
|
|
|
it("keeps a genuinely empty project so its new-workspace row stays reachable", () => {
|
|
const projects = [project("p1", [])];
|
|
const result = splitPinnedSidebarGroups({
|
|
projects,
|
|
keys: { pinnedWorkspaceKeys: [], pinnedAtByKey: {} },
|
|
pinnedWorkspaceOrder: [],
|
|
});
|
|
expect(result.unpinnedProjects).toHaveLength(1);
|
|
});
|
|
|
|
it("keeps remaining chats when only some are pinned", () => {
|
|
const projects = [project("p1", [placement("w1"), placement("w2")])];
|
|
const result = splitPinnedSidebarGroups({
|
|
projects,
|
|
keys: {
|
|
pinnedWorkspaceKeys: ["w1"],
|
|
pinnedAtByKey: { w1: "2026-01-01T00:00:00Z" },
|
|
},
|
|
pinnedWorkspaceOrder: [],
|
|
});
|
|
expect(result.pinnedChats.map((w) => w.workspaceKey)).toEqual(["w1"]);
|
|
expect(result.unpinnedProjects[0]?.workspaces.map((w) => w.workspaceKey)).toEqual(["w2"]);
|
|
});
|
|
|
|
it("orders pinned chats by most-recently-pinned first", () => {
|
|
const projects = [project("p1", [placement("older"), placement("newer")])];
|
|
const result = splitPinnedSidebarGroups({
|
|
projects,
|
|
keys: {
|
|
pinnedWorkspaceKeys: ["older", "newer"],
|
|
pinnedAtByKey: {
|
|
older: "2026-01-01T00:00:00Z",
|
|
newer: "2026-02-01T00:00:00Z",
|
|
},
|
|
},
|
|
pinnedWorkspaceOrder: [],
|
|
});
|
|
|
|
expect(result.pinnedChats.map((workspace) => workspace.workspaceKey)).toEqual([
|
|
"newer",
|
|
"older",
|
|
]);
|
|
});
|
|
|
|
it("applies the saved order while keeping a newly pinned chat first", () => {
|
|
const projects = [project("p1", [placement("older"), placement("newer"), placement("new")])];
|
|
const result = splitPinnedSidebarGroups({
|
|
projects,
|
|
keys: {
|
|
pinnedWorkspaceKeys: ["older", "newer", "new"],
|
|
pinnedAtByKey: {
|
|
older: "2026-01-01T00:00:00Z",
|
|
newer: "2026-02-01T00:00:00Z",
|
|
new: "2026-03-01T00:00:00Z",
|
|
},
|
|
},
|
|
pinnedWorkspaceOrder: ["older", "newer"],
|
|
});
|
|
|
|
expect(result.pinnedChats.map((workspace) => workspace.workspaceKey)).toEqual([
|
|
"new",
|
|
"older",
|
|
"newer",
|
|
]);
|
|
});
|
|
});
|