Unify toolbox drag and drop reorder behavior
All checks were successful
Deploy Sokko G / deploy (push) Successful in 7s

This commit is contained in:
Shinuwa 2026-08-01 20:00:47 +02:00
parent a6d35c6e6b
commit 1879b245fb
17 changed files with 1258 additions and 929 deletions

View file

@ -5,6 +5,7 @@ import { evaluateTableCell } from "../website/src/features/toolboxes/modules/tab
import { parseColonImportLines } from "../website/src/features/toolboxes/modules/textImport.js";
import { getTimePatternRecurrenceMs, getTimePatternTargetMs } from "../website/src/features/toolboxes/modules/timerUtils.js";
import { compactModuleDataForStorage, createToolboxExportPayload, normalizeCombosData, normalizeNotepadData, normalizeTableData, normalizeTaskPlannerData } from "../website/src/features/toolboxes/storage/toolboxStorage.js";
import { applyGroupedReorderOperation, completeGroupOrder, getBoundaryItemId, getGroupedEntries, moveGroupOrder, moveGroupOrderToEnd, moveGroupOrderToStart, moveItem, moveItemGroup } from "../website/src/hooks/useGroupedReorder.js";
test("colon text import keeps urls intact after the first separator", () => {
assert.deepEqual(parseColonImportLines("Build:https://example.com/build?class=rogue\nPotion:10"), [
@ -95,6 +96,136 @@ test("table storage clamps dimensions and compacts non-empty cells", () => {
assert.equal(compactModuleDataForStorage("table", { rows: 10, columns: 6, cells: {} }), null);
});
test("grouped reorder helpers group flat items and complete group order", () => {
const items = [
{ id: "a", category: "" },
{ id: "b", category: "Boss" },
{ id: "c", category: "Farm" },
{ id: "d", category: "Boss" }
];
assert.deepEqual(completeGroupOrder(["Farm"], ["Boss", "Farm"]), ["Farm", "Boss"]);
assert.deepEqual(getGroupedEntries(items, {
groupOrder: ["Farm"],
getItemGroup: (item) => item.category,
groupIdKey: "category",
groupItemKey: "items"
}), [
{ type: "item", item: items[0] },
{ type: "group", group: { category: "Boss", items: [items[1], items[3]] } },
{ type: "group", group: { category: "Farm", items: [items[2]] } }
]);
});
test("grouped reorder helpers move items and grouped item blocks", () => {
const items = [{ id: "a" }, { id: "b" }, { id: "c" }, { id: "d" }];
assert.deepEqual(moveItem(items, "a", "c", "after").map((item) => item.id), ["b", "c", "a", "d"]);
assert.deepEqual(moveItemGroup(items, ["b", "c"], "a", "before").map((item) => item.id), ["b", "c", "a", "d"]);
assert.equal(getBoundaryItemId(items, ["b", "c"], "before"), "b");
assert.equal(getBoundaryItemId(items, ["b", "c"], "after"), "c");
});
test("grouped reorder helpers reorder categories at targets and edges", () => {
const groups = ["Neutral", "Advanced", "Punish"];
assert.deepEqual(moveGroupOrder(["Neutral", "Advanced", "Punish"], groups, "Punish", "Neutral", "before"), ["Punish", "Neutral", "Advanced"]);
assert.deepEqual(moveGroupOrderToStart(["Neutral", "Advanced", "Punish"], groups, "Punish"), ["Punish", "Neutral", "Advanced"]);
assert.deepEqual(moveGroupOrderToEnd(["Neutral", "Advanced", "Punish"], groups, "Neutral"), ["Advanced", "Punish", "Neutral"]);
});
test("grouped reorder operation moves items in and out of categories", () => {
const data = {
categoryOrder: ["Boss"],
collapsedCategories: ["Farm"],
items: [
{ id: "a" },
{ id: "b", category: "Boss" },
{ id: "c", category: "Boss" },
{ id: "d", category: "Farm" }
]
};
const config = {
itemsKey: "items",
groupOrderKey: "categoryOrder",
collapsedGroupsKey: "collapsedCategories",
getItemGroup: (item) => item.category || "",
setItemGroup: (item, category) => {
const nextItem = { ...item };
if (category) nextItem.category = category;
else delete nextItem.category;
return nextItem;
}
};
const movedIn = applyGroupedReorderOperation(data, {
...config,
operation: {
type: "item",
sourceId: "a",
targetId: "Farm",
targetType: "group",
placement: "before",
sourceGroup: "",
targetGroup: "Farm",
sourceParentId: "",
targetParentId: ""
}
});
assert.deepEqual(movedIn.items.map((item) => `${item.id}:${item.category || ""}`), ["b:Boss", "c:Boss", "a:Farm", "d:Farm"]);
assert.deepEqual(movedIn.collapsedCategories, []);
const movedOut = applyGroupedReorderOperation(movedIn, {
...config,
operation: {
type: "item",
sourceId: "a",
targetId: "Boss",
targetType: "boundary",
placement: "after",
sourceGroup: "Farm",
targetGroup: "Boss",
sourceParentId: "",
targetParentId: ""
}
});
assert.deepEqual(movedOut.items.map((item) => `${item.id}:${item.category || ""}`), ["b:Boss", "c:Boss", "a:", "d:Farm"]);
});
test("grouped reorder operation moves whole categories", () => {
const data = {
categoryOrder: ["Boss", "Farm"],
items: [
{ id: "a", category: "Boss" },
{ id: "b", category: "Boss" },
{ id: "c", category: "Farm" },
{ id: "d", category: "Farm" },
{ id: "e" }
]
};
const moved = applyGroupedReorderOperation(data, {
operation: {
type: "group",
sourceId: "Boss",
targetId: "Farm",
targetType: "group",
placement: "after",
sourceGroup: "Boss",
targetGroup: "Farm",
sourceParentId: "",
targetParentId: ""
},
groupOrderKey: "categoryOrder",
getItemGroup: (item) => item.category || "",
setItemGroup: (item) => item
});
assert.deepEqual(moved.items.map((item) => item.id), ["c", "d", "a", "b", "e"]);
assert.deepEqual(moved.categoryOrder, ["Farm", "Boss"]);
});
test("combos storage normalizes devices, steps and compact export ids", () => {
const longText = "x".repeat(120);
const normalized = normalizeCombosData({