1

I would appreciate any help with this case, so if you see any minor issue - please, write me. There will be rather a lot of code.

I was trying to implement 'dnd-kit/sortable' into my bug tracker app. I have Kanban board consisting of four repeating column components. I needed to implement dnd-kit to be able to move task cards not only inside of each column, but between columns as well. Current code with sorting task cards in column, but if you try to move a card to any other column - most of the time nothing happens, but sometimes you get the Uncaught TypeError: Cannot read properties of undefined (reading 'id') I red through documentation many times and looked through similar projects in open source, but couldn't find what could be the reason for this bug.

The tasks from TasksContext is object with keys backlog, todo, inProgress, inReview, done and contains array of object. Each object inside of array represents task card.

Dashboard.js

const Dashboard = () => {
    const { tasks, setTasks } = useContext(TasksContext)
    const [activeId, setActiveId] = useState(null);
    const mouseSensor = useSensor(MouseSensor);
    const touchSensor = useSensor(TouchSensor);
    const sensors = useSensors(mouseSensor, touchSensor)
    const fullArray = Array.from(Object.values(tasks).flat())
    console.log(fullArray)


    const handleDragStart = ({ active }) => setActiveId(active.id);

    const handleDragCancel = () => setActiveId(null);

    const handleDragEnd = ({active, over}) => {
        const { containerId: activeContainer } = active.data.current.sortable
        const { containerId: overContainer } = over.data.current.sortable
        const oldIndex = tasks[activeContainer].findIndex(obj => obj.id === active.id);
        const newIndex = tasks[overContainer].findIndex(obj => obj.id === over.id);

        if (active.id !== over.id) {
            setTasks((prevTasks) => ({
                ...prevTasks,
                [overContainer]: arrayMove(prevTasks[overContainer], oldIndex, newIndex)
            }));
        }

        setActiveId(null);
    }

    return (
        <div className='relative grid grid-cols-4 gap-6 px-6 grow-0 shrink-0 basis-5/6 overflow-y-scroll'>
            <DndContext sensors={sensors} collisionDetection={rectIntersection} onDragStart={handleDragStart} onDragCancel={handleDragCancel} onDragEnd={handleDragEnd}>
                    <TasksColumn key='to do' title='to do' id='todo' tasks={tasks.todo} />
                    <TasksColumn key='in progress' title='in progress' id='inProgress' tasks={tasks.inProgress} />
                    <TasksColumn key='in review' title='in review' id='inReview' tasks={tasks.inReview} />
                    <TasksColumn key='done' title='done' id='done' tasks={tasks.done} />
                    <DragOverlay>{activeId ? <TaskCard id={activeId} task={fullArray.filter(task => task?.id === activeId)[0]} /> : null}</DragOverlay>
            </DndContext>
        </div>
    )
}
TasksColumn.js

const TasksColumn = ({ title, id, tasks }) => {
    const { setNodeRef } = useDroppable({id});

  return (
    <div className=''>
        <ColumnHeader title={title} id={id} />

        <div className="h-3 w-full border-b-2 border-grayDark" />
        <SortableContext items={tasks} id={id} strategy={verticalListSortingStrategy}>
            <div ref={setNodeRef} className=''>  
               {tasks.map(task => (
                    <Draggable key={task.name} id={task.id} task={task} />
                ))} 
            </div>            
        </SortableContext>  
    </div>
  )
}
Draggable.js

const Draggable = ({ id, task }) => {
    const { setNodeRef, transform, transition, isDragging, } = useSortable({id});

    const style = {
        transform: CSS.Translate.toString(transform),
        transition,
        opacity: isDragging ? 0.5 : 1,
    };

  return (
    <div ref={setNodeRef} style={style}>
        <TaskCard id={id} task={task} />
    </div>
  )
}
TaskCard.js

const TaskCard = ({ id, task }) => {
    const { attributes, listeners, setActivatorNodeRef } = useSortable({id});

    
    return (
        <div className="py-4 border-b-2 border-grayLight">
            <div className="">
                <p className="">{task.deadline}</p>
                <p className="">{task.priority}</p>
            </div>
            <ArrowsPointingOutIcon className='rotate-45 w-5 h-5 outline-none' ref={setActivatorNodeRef} {...listeners} {...attributes} />
            <p className="">{task.name}</p>
            <div className="">
                <p className="">{task.author}</p>
                <p className="">{task.time}</p>
            </div>
        </div>  
    )
}
rey2197
  • 71
  • 8

2 Answers2

0

DND-kit/sortable doesn't support across containers drag. Update the items of the required columns in the onDragOver event. Make sure to handle the edge cases :).

  • Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center. – DSDmark Jun 23 '23 at 03:25
  • I don't believe this is correct. See the following examples/docs for cross container dnd with dnd-kit https://5fc05e08a4a65d0021ae0bf2-xkdjvdfnuz.chromatic.com/?path=/story/presets-sortable-multiple-containers--vertical-grid, https://codesandbox.io/s/dnd-kit-multi-containers-lknfe?file=/src/app.js, https://docs.dndkit.com/presets/sortable/sortable-context#usage – nvk Aug 14 '23 at 15:21
0

Its seems like you are very close. Have you seen this codesandbox? https://codesandbox.io/s/dnd-kit-multi-containers-lknfe?file=/src/app.js

(it looks really close to your implementation, so maybe you solved it and this is yours?)

I do see one potential issue, it doesn't look like you are spreading the draggable properties on the draggable div. Should look something like:

<div ref={setNodeRef} style={style} {...attributes} {...listeners}>
  <TaskCard ... />
</div>

It may also be helpful to checkout dnd-kits multi container example for some learnings. The collision detection strategy might be one such place where you can improve.

dnd kit multi container storybook example dnd kit multi container source code

Hope that helps!

nvk
  • 221
  • 3
  • 1