From 25e78b47548e56caa948a96c92526f59ecaa1d7b Mon Sep 17 00:00:00 2001 From: jason Date: Sun, 22 Mar 2026 08:23:51 -0500 Subject: [PATCH] Fix module drag: TypeError on droppableContainers.filter() crashing collision detection droppableContainers in @dnd-kit/core collision detection args is a custom NodeMap class, not a plain Array. It implements [Symbol.iterator] (so for...of works internally in closestCenter/pointerWithin) but does NOT have Array.prototype methods like .filter(). Calling args.droppableContainers.filter(...) threw: TypeError: args.droppableContainers.filter is not a function dnd-kit silently catches errors in the collision detection callback and treats them as no collision (over = null). Every module drag ended with over = null, hitting the early return in handleDragEnd, causing the module to snap back to its original slot every time. Fix: Array.from(args.droppableContainers) converts the NodeMap iterable to a plain array before filtering for dropType === 'slot' containers. Co-Authored-By: Claude Sonnet 4.6 --- client/src/components/rack/RackPlanner.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/client/src/components/rack/RackPlanner.tsx b/client/src/components/rack/RackPlanner.tsx index 119738f..f22fb61 100644 --- a/client/src/components/rack/RackPlanner.tsx +++ b/client/src/components/rack/RackPlanner.tsx @@ -69,13 +69,17 @@ function ModuleDragOverlay({ label }: { label: string }) { * (which needs the sortable rack targets) still works. */ const slotFirstCollision: CollisionDetection = (args) => { - // Restrict to slot droppables only - const slotContainers = args.droppableContainers.filter( + // droppableContainers is a custom NodeMap (not a plain Array) — it only + // implements [Symbol.iterator], so .filter() doesn't exist on it. + // Convert to Array first before filtering. + const allContainers = Array.from(args.droppableContainers); + + const slotContainers = allContainers.filter( (c) => c.data.current?.dropType === 'slot' ); if (slotContainers.length > 0) { - const slotHits = pointerWithin({ ...args, droppableContainers: slotContainers }); + const slotHits = pointerWithin({ ...args, droppableContainers: slotContainers as typeof args.droppableContainers }); if (slotHits.length > 0) return slotHits; }