Files
kandev/apps/web/components/settings/sidebar-layout-editor-dnd.test.ts
T
2026-09-20 00:09:02 +01:00

140 lines
3.8 KiB
TypeScript

import type { DragEndEvent } from "@dnd-kit/core";
import { describe, expect, it } from "vitest";
import {
defaultSidebarLayout,
SIDEBAR_LAYOUT_LIMITS,
type SidebarLayout,
} from "@/lib/sidebar/layout-types";
import {
handleSidebarLayoutDragEnd,
type SidebarLayoutDragData,
} from "./sidebar-layout-editor-dnd";
function layoutWithGroups(): SidebarLayout {
return {
...defaultSidebarLayout(),
nodes: [
{
id: "group-a",
kind: "shortcuts",
visible: true,
name: "A",
shortcuts: [{ id: "chat", target: { kind: "host_action", id: "quick_chat" } }],
},
{ id: "group-b", kind: "shortcuts", visible: true, name: "B", shortcuts: [] },
],
};
}
function dragEvent(active: SidebarLayoutDragData, over: SidebarLayoutDragData): DragEndEvent {
return {
active: { data: { current: active } },
over: { data: { current: over } },
} as unknown as DragEndEvent;
}
function expectRejectedDrop(
layout: SidebarLayout,
active: SidebarLayoutDragData,
over: SidebarLayoutDragData,
) {
let failed = false;
handleSidebarLayoutDragEnd(dragEvent(active, over), layout, (operation) => {
try {
operation(layout);
} catch {
failed = true;
}
return false;
});
expect(failed).toBe(true);
}
describe("sidebar layout drag and drop", () => {
it("reorders layout groups through the drag handler", () => {
const layout = layoutWithGroups();
let next = layout;
handleSidebarLayoutDragEnd(
dragEvent({ type: "node", nodeId: "group-b" }, { type: "node", nodeId: "group-a" }),
layout,
(operation) => {
next = operation(next);
return true;
},
);
expect(next.nodes.map((node) => node.id)).toEqual(["group-b", "group-a"]);
});
it("moves a shortcut across groups through the drag handler", () => {
const layout = layoutWithGroups();
let next = layout;
handleSidebarLayoutDragEnd(
dragEvent(
{ type: "shortcut", nodeId: "group-a", shortcutId: "chat" },
{ type: "group", nodeId: "group-b" },
),
layout,
(operation) => {
next = operation(next);
return true;
},
);
expect(next.nodes.find((node) => node.id === "group-a")?.shortcuts).toEqual([]);
expect(next.nodes.find((node) => node.id === "group-b")?.shortcuts).toEqual([
{ id: "chat", target: { kind: "host_action", id: "quick_chat" } },
]);
});
it("lets the editor operation boundary reject a duplicate cross-group drop", () => {
const layout = layoutWithGroups();
const duplicate = {
...layout,
nodes: layout.nodes.map((node) =>
node.id === "group-b"
? {
...node,
shortcuts: [
{ id: "existing", target: { kind: "host_action" as const, id: "quick_chat" } },
],
}
: node,
),
};
expectRejectedDrop(
duplicate,
{ type: "shortcut", nodeId: "group-a", shortcutId: "chat" },
{ type: "group", nodeId: "group-b" },
);
});
it("lets the editor operation boundary reject a full destination", () => {
const layout = layoutWithGroups();
const fullDestination = {
...layout,
nodes: layout.nodes.map((node) =>
node.id === "group-b"
? {
...node,
shortcuts: Array.from(
{ length: SIDEBAR_LAYOUT_LIMITS.shortcutsPerGroup },
(_, index) => ({
id: `existing-${index}`,
target: { kind: "host_action" as const, id: `action-${index}` },
}),
),
}
: node,
),
};
expectRejectedDrop(
fullDestination,
{ type: "shortcut", nodeId: "group-a", shortcutId: "chat" },
{ type: "group", nodeId: "group-b" },
);
});
});