Unify toolbox drag and drop reorder behavior
All checks were successful
Deploy Sokko G / deploy (push) Successful in 7s
All checks were successful
Deploy Sokko G / deploy (push) Successful in 7s
This commit is contained in:
parent
a6d35c6e6b
commit
1879b245fb
17 changed files with 1258 additions and 929 deletions
|
|
@ -13,6 +13,7 @@ test("toolbox storage, cards and pages are wired", async () => {
|
|||
const storageQuota = await readFile("website/src/components/StorageQuota.jsx", "utf8");
|
||||
const importButton = await readFile("website/src/components/ImportButton.jsx", "utf8");
|
||||
const reorderHook = await readFile("website/src/hooks/usePointerReorder.js", "utf8");
|
||||
const groupedReorderHook = await readFile("website/src/hooks/useGroupedReorder.js", "utf8");
|
||||
|
||||
assert.match(`${indexedToolboxesHook}\n${toolboxPages}`, /indexedDbStorage\.js/);
|
||||
assert.match(source, /features\/toolboxes\/storage\/useIndexedToolboxes\.js/);
|
||||
|
|
@ -48,11 +49,13 @@ test("toolbox storage, cards and pages are wired", async () => {
|
|||
assert.match(toolboxCard, /export function ToolboxIconPicker/);
|
||||
assert.match(toolboxCard, /export function ToolboxGameIcon/);
|
||||
assert.match(toolboxCard, /toolbox-card-drag-handle/);
|
||||
assert.match(toolboxCard, /reorder\.getItemProps/);
|
||||
assert.match(toolboxCard, /toolbox-card-cover-link/);
|
||||
assert.match(toolboxCard, /toolbox-icon-cover/);
|
||||
assert.match(toolboxPages, /\.\/ToolboxCard\.jsx/);
|
||||
assert.match(toolboxPages, /updateToolboxOrder/);
|
||||
assert.match(toolboxPages, /draggingToolboxId/);
|
||||
assert.match(toolboxPages, /useGroupedReorder/);
|
||||
assert.match(toolboxPages, /orientation: "horizontal"/);
|
||||
assert.match(toolboxPages, /components\/StorageQuota\.jsx/);
|
||||
assert.match(toolboxPages, /components\/ImportButton\.jsx/);
|
||||
assert.match(toolboxPages, /import-icon-button/);
|
||||
|
|
@ -65,7 +68,7 @@ test("toolbox storage, cards and pages are wired", async () => {
|
|||
assert.match(toolboxPages, /moduleColumns/);
|
||||
assert.match(toolboxPages, /normalizeCombosData/);
|
||||
assert.match(toolboxPages, /function ToolboxView/);
|
||||
assert.match(toolboxPages, /usePointerReorder/);
|
||||
assert.doesNotMatch(toolboxPages, /usePointerReorder/);
|
||||
assert.match(storageQuota, /export function StorageQuota/);
|
||||
assert.match(storageQuota, /role="progressbar"/);
|
||||
assert.match(importButton, /export function ImportButton/);
|
||||
|
|
@ -73,6 +76,12 @@ test("toolbox storage, cards and pages are wired", async () => {
|
|||
assert.match(reorderHook, /export function usePointerReorder/);
|
||||
assert.match(reorderHook, /setPointerCapture/);
|
||||
assert.match(reorderHook, /elementFromPoint/);
|
||||
assert.match(groupedReorderHook, /export function useGroupedReorder/);
|
||||
assert.match(groupedReorderHook, /usePointerReorder/);
|
||||
assert.match(groupedReorderHook, /export function getGroupedEntries/);
|
||||
assert.match(groupedReorderHook, /export function applyGroupedReorderOperation/);
|
||||
assert.match(groupedReorderHook, /data-reorder-orientation/);
|
||||
assert.match(groupedReorderHook, /operation\.sourceParentId === operation\.targetParentId/);
|
||||
});
|
||||
|
||||
test("toolbox module registry and modules expose expected behavior", async () => {
|
||||
|
|
@ -114,9 +123,9 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
|||
assert.match(moduleRegistry, /Annotation d'images/);
|
||||
assert.match(moduleRegistry, /editable: false/);
|
||||
assert.match(moduleRegistry, /module-edit-button/);
|
||||
assert.match(moduleRegistry, /onDragStart/);
|
||||
assert.match(moduleRegistry, /itemReorder\.startDrag/);
|
||||
assert.match(moduleRegistry, /onPointerDown/);
|
||||
assert.match(moduleRegistry, /usePointerReorder/);
|
||||
assert.match(moduleRegistry, /useGroupedReorder/);
|
||||
assert.match(moduleRegistry, /tool-add-card/);
|
||||
assert.match(moduleRegistry, /tool-quick-add-button/);
|
||||
assert.match(moduleRegistry, /tool-add-quick-toggle/);
|
||||
|
|
@ -134,10 +143,16 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
|||
assert.match(combosModule, /ComboSequence/);
|
||||
assert.match(combosModule, /keyboardLayout/);
|
||||
assert.match(combosModule, /simultaneousMode/);
|
||||
assert.match(combosModule, /useGroupedReorder/);
|
||||
assert.match(combosModule, /reorderFeatures/);
|
||||
assert.doesNotMatch(combosModule, /canMoveItem:/);
|
||||
assert.doesNotMatch(combosModule, /canMoveGroup:/);
|
||||
assert.match(calculatorModule, /export function CalculatorModule/);
|
||||
assert.match(calculatorModule, /calculateExpression/);
|
||||
assert.match(calculatorModule, /activeParentId/);
|
||||
assert.match(calculatorModule, /parentId/);
|
||||
assert.match(calculatorModule, /useGroupedReorder/);
|
||||
assert.match(calculatorModule, /calculator-drag-handle/);
|
||||
assert.match(calculatorModule, /scrollResults/);
|
||||
assert.match(calculatorModule, /tool-split-scroll-toggle/);
|
||||
assert.match(calculatorModule, /EditableCalculatorLabel/);
|
||||
|
|
@ -188,7 +203,10 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
|||
assert.match(taskPlannerModule, /categoryOrder/);
|
||||
assert.match(taskPlannerModule, /categoryLabel/);
|
||||
assert.match(taskPlannerModule, /task-planner-warning/);
|
||||
assert.match(taskPlannerModule, /usePointerReorder/);
|
||||
assert.match(taskPlannerModule, /useGroupedReorder/);
|
||||
assert.match(taskPlannerModule, /reorderFeatures/);
|
||||
assert.doesNotMatch(taskPlannerModule, /canMoveItem:/);
|
||||
assert.doesNotMatch(taskPlannerModule, /canMoveGroup:/);
|
||||
assert.match(checklistModule, /export function ChecklistModule/);
|
||||
assert.match(checklistModule, /TextImportModal/);
|
||||
assert.match(checklistModule, /Icon name="import"/);
|
||||
|
|
@ -203,9 +221,12 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
|||
assert.match(checklistModule, /Number\.parseInt/);
|
||||
assert.match(checklistModule, /checklist-qty-current/);
|
||||
assert.match(checklistModule, /checklist-delete-button danger/);
|
||||
assert.doesNotMatch(checklistModule, /useGroupedReorder/);
|
||||
assert.match(textImportModal, /createPortal/);
|
||||
assert.match(textImportModal, /lockBodyScroll/);
|
||||
assert.match(imagesModule, /export function ImagesModule/);
|
||||
assert.match(imagesModule, /useGroupedReorder/);
|
||||
assert.match(imagesModule, /orientation: "horizontal"/);
|
||||
assert.match(imagesModule, /editing &&/);
|
||||
assert.match(imagesModule, /clipboardData/);
|
||||
assert.match(imagesModule, /createImageAnnotationModule/);
|
||||
|
|
@ -226,9 +247,18 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
|||
assert.match(linksModule, /copyText/);
|
||||
assert.match(linksModule, /parseColonImportLines/);
|
||||
assert.match(linksModule, /context\.normalizeUrl/);
|
||||
assert.match(linksModule, /useGroupedReorder/);
|
||||
assert.match(linksModule, /link-drag-handle/);
|
||||
assert.match(countersModule, /export function CountersModule/);
|
||||
assert.match(countersModule, /editing &&/);
|
||||
assert.match(countersModule, /counter-actions/);
|
||||
assert.match(countersModule, /useGroupedReorder/);
|
||||
assert.match(countersModule, /orientation: "horizontal"/);
|
||||
assert.match(countersModule, /counter-drag-handle/);
|
||||
assert.match(timerModule, /useGroupedReorder/);
|
||||
assert.match(timerModule, /reorderEnabled=\{!data\.sortResults\}/);
|
||||
assert.match(timerModule, /parentId=\{moduleId\}/);
|
||||
assert.match(timerModule, /timer-drag-handle/);
|
||||
assert.match(textImport, /export function parseColonImportLines/);
|
||||
assert.match(textImport, /line\.indexOf\(":\"\)/);
|
||||
assert.match(imageViewer, /image-viewer-media/);
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue