Skip to content

OUT-3124 | Task dragging lacks responsiveness#1200

Merged
arpandhakal merged 4 commits intomainfrom
OUT-3124
May 6, 2026
Merged

OUT-3124 | Task dragging lacks responsiveness#1200
arpandhakal merged 4 commits intomainfrom
OUT-3124

Conversation

@arpandhakal
Copy link
Copy Markdown
Collaborator

@arpandhakal arpandhakal commented Apr 30, 2026

Summary

  • Migrates task drag-and-drop from react-dnd (HTML5 drag API + custom 1×1 GIF preview overlay) to @dnd-kit/core for smooth, pointer-tracked dragging that matches the App Library interaction.
  • Adds a thin wrapper layer in src/hoc/dndKit/ (TaskDndContext, DraggableTask, DroppableArea, TaskDragPreview) instead of using @dnd-kit/sortable, since column membership — not in-column ordering — is the only drop concern.
  • Preserves the existing drop business logic: same redux updateWorkflowStateIdByTaskId dispatch and same updateTask / clientUpdateTask PATCH, including the open-subtask cascade modal flow.
  • Removes react-dnd, react-dnd-html5-backend, react-dnd-touch-backend, and the custom ModifiedBackend / DndWrapper / DragDropHandler / Droppable / CustomDragLayer / CardDragLayer plumbing.

Notable details

  • MouseSensor activation distance is 6px and TouchSensor uses a 150ms delay so taps on task cards still navigate via the wrapping CustomLink/Next Link.
  • DragOverlay uses dropAnimation={null} + snapCenterToCursor to follow the pointer.
  • Auto-scroll is enabled only in list view (threshold.y: 0.18) and disabled in board view to avoid hijacking the column's horizontal scroll.
  • Virtualization (@tanstack/react-virtual) compatibility verified: useDraggable re-attaches refs cleanly as virtualized rows mount/unmount; the DragOverlay portal keeps the preview rendered after the source row scrolls out of view.

Test plan

  • Board view: drag a task across columns — preview tracks pointer continuously, drop updates the column.
  • List view: drag a task between sections — preview tracks pointer, auto-scroll triggers near top/bottom edges.
  • Click a task card without moving — navigation to detail page still works.
  • Drag a task with open subtasks into a "completed" column — cascade modal appears; "Update" cascades, "Skip" doesn't.
  • Touch device: tap-and-hold (~150ms) initiates drag; a quick tap navigates.
  • Task viewer (read-only access) — drag is disabled.
  • Both IU and Client portals.

Testing criteria

Upon testing, Found out we need to disabled drag for subtasks and shared task on CU view.
Also enable horizontal scrolling on drag.

Replaces react-dnd (HTML5 drag API + custom drag preview) with
@dnd-kit/core for smoother, pointer-tracked task dragging that aligns
with the App Library interaction. Drop business logic (redux update +
PATCH) is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@linear-code
Copy link
Copy Markdown

linear-code Bot commented Apr 30, 2026

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tasks-app Ready Ready Preview, Comment May 6, 2026 11:24am

Request Review

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 30, 2026

Greptile Summary

This PR migrates task drag-and-drop from react-dnd (HTML5/touch backends + custom 1×1 GIF preview) to @dnd-kit/core, introducing four thin wrapper components in src/hoc/dndKit/ (TaskDndContext, DraggableTask, DroppableArea, TaskDragPreview) and removing five legacy HOCs. The existing drop business logic — Redux updateWorkflowStateIdByTaskId, PATCH via updateTask/clientUpdateTask, and the open-subtask cascade modal flow — is fully preserved.

Confidence Score: 4/5

Safe to merge; all P2 findings are style/quality issues that do not affect the existing drop business logic or Redux state.

No P0 or P1 defects found. Three P2 issues: mid-file static imports mislabeled as lazy, touch-action: manipulation vs. the dnd-kit-recommended none, and KeyboardSensor wired without a coordinateGetter. Core drag/drop logic, same-column suppression, cascade modal flow, and Redux dispatch are all correct.

src/hoc/dndKit/TaskDndContext.tsx (import ordering, KeyboardSensor), src/hoc/dndKit/DraggableTask.tsx (touchAction)

Important Files Changed

Filename Overview
src/hoc/dndKit/TaskDndContext.tsx New DnD context with sensors, drag-start/end handlers, and DragOverlay portal; has mid-file static imports (mislabeled as lazy) and a KeyboardSensor with no coordinateGetter
src/hoc/dndKit/DraggableTask.tsx New draggable wrapper using useDraggable; touch-action set to 'manipulation' instead of the dnd-kit-recommended 'none', which may interfere with touch dragging on some devices
src/hoc/dndKit/DroppableArea.tsx Correct droppable wrapper with same-column drop suppression via isActiveDifferentColumn; looks clean
src/hoc/dndKit/TaskDragPreview.tsx Replacement for CardDragLayer; correctly uses getCardHref and passes workflowState to TaskCard in board view
src/app/ui/TaskBoard.tsx Wraps both board and list views in TaskDndContext with correct autoScroll conditional; removes react-dnd plumbing cleanly
src/app/ui/VirtualizedTasksLists.tsx Replaces DragDropHandler with DraggableTask/DroppableArea; removes onDropItem prop correctly as the context now owns drop logic
src/app/(home)/page.tsx Removes DndWrapper wrapper; clean change
src/app/client/page.tsx Removes DndWrapper wrapper for client portal; mirrors home page change correctly
package.json Adds @dnd-kit/core, @dnd-kit/modifiers, @dnd-kit/utilities; removes react-dnd, react-dnd-html5-backend, react-dnd-touch-backend cleanly

Sequence Diagram

sequenceDiagram
    participant User
    participant DraggableTask
    participant TaskDndContext
    participant DroppableArea
    participant TaskBoard

    User->>DraggableTask: pointerdown / touchstart
    Note over DraggableTask: MouseSensor: 6px distance<br/>TouchSensor: 150ms delay
    DraggableTask->>TaskDndContext: onDragStart(event)
    TaskDndContext->>TaskDndContext: setActiveTask(task)
    TaskDndContext-->>User: DragOverlay renders preview (snapCenterToCursor)

    User->>DroppableArea: pointer moves over column/section
    DroppableArea->>DroppableArea: isOver=true, highlight if different column

    User->>TaskDndContext: pointerup (onDragEnd)
    TaskDndContext->>TaskDndContext: setActiveTask(null)
    alt over is null OR same workflowStateId
        TaskDndContext-->>User: no-op, overlay dismissed
    else different column
        TaskDndContext->>TaskBoard: onDropItem({taskId, targetWorkflowStateId})
        TaskBoard->>TaskBoard: updateWorkflowStateIdByTaskId (Redux)
        TaskBoard->>TaskBoard: updateTask / clientUpdateTask (PATCH)
        alt task has open subtasks AND target is completed
            TaskBoard-->>User: CompletionCascadeModal
        end
    end
Loading

Reviews (1): Last reviewed commit: "feat(OUT-3124): migrate task drag-and-dr..." | Re-trigger Greptile

Comment thread src/hoc/dndKit/TaskDndContext.tsx Outdated
Comment on lines +72 to +74
// Split out so we can lazy-import DragOverlay without breaking SSR.
import { DragOverlay } from '@dnd-kit/core'
import { snapCenterToCursor } from '@dnd-kit/modifiers'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Mid-file imports are not actually lazy

DragOverlay and snapCenterToCursor are static import statements, which the ES module system hoists to the top of the module regardless of their position in source. The comment "lazy-import DragOverlay without breaking SSR" is inaccurate — this achieves nothing different from placing them at the top of the file, and most linters (import/first) will flag it. A true lazy-load would require dynamic() from Next.js or React.lazy. Please move these imports to the top of the file to avoid confusion.

...style,
opacity: isDragging ? 0.35 : 1,
cursor: disabled ? undefined : 'grab',
touchAction: 'manipulation',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 touch-action: manipulation may prevent touch dragging

@dnd-kit's TouchSensor documentation recommends touch-action: none on draggable elements so the browser does not intercept touch events for scrolling before the sensor can claim them. manipulation still permits panning, so on certain browsers/devices the browser may begin a scroll pass during the 150ms delay window, preventing the sensor from ever reaching its activation threshold. If you specifically need page scrolling to work while a finger rests on a card (before the drag activates), this is a deliberate trade-off worth calling out in a comment; otherwise none is the safer default for drag reliability.

Suggested change
touchAction: 'manipulation',
touchAction: 'none',

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread src/hoc/dndKit/TaskDndContext.tsx Outdated
const sensors = useSensors(
useSensor(MouseSensor, { activationConstraint: { distance: 6 } }),
useSensor(TouchSensor, { activationConstraint: { delay: 150, tolerance: 8 } }),
useSensor(KeyboardSensor),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 KeyboardSensor lacks a coordinateGetter for column/section layout

The default KeyboardSensor moves the drag overlay in 2-D pixel space using arrow keys, which doesn't correspond to the column (board) or section (list) model used here. Keyboard users who attempt to drag-and-drop a task will see the overlay shift but won't be able to land it on a target column, silently failing to trigger onDropItem. Consider either wiring up a custom coordinateGetter that snaps to column/section droppable bounds, or removing KeyboardSensor until that work is done so keyboard users get no false affordance.

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 1, 2026

Deployment failed with the following error:

Deploying Serverless Functions to multiple regions is restricted to the Pro and Enterprise plans.

Learn More: https://vercel.link/multiple-function-regions

- Hoist DragOverlay/snapCenterToCursor imports to module top (ES modules
  hoist them anyway; the mid-file placement was misleading).
- Drop KeyboardSensor: without a column/section coordinateGetter it
  produced a non-functional drag affordance for keyboard users.
- Document the deliberate touchAction: 'manipulation' choice so the
  trade-off vs. dnd-kit's recommended 'none' is explicit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@priosshrsth priosshrsth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@arpandhakal arpandhakal merged commit 43e8351 into main May 6, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants