Right panel's thread stop list shows a GripVertical drag handle with cursor: grab but no onDragStart/onDrop — visual lie; thread stop reorder in the right panel is non-functional #177

Closed
opened 2026-10-03 17:37:52 +00:00 by Mark · 1 comment
Owner

Finding

src/components/workspace/RightPanel.tsx:486 renders a GripVertical icon as the first child of each thread stop row:

<GripVertical size={12} style={{ color: '#A8A29E', cursor: 'grab' }} />

The cursor: 'grab' style suggests the user can drag the row to reorder it. Verified: there is no onDragStart, no draggable attribute, no onDragOver, no onDrop on the parent <div> (line 481-505). The cursor change is a visual lie.

The thread stop list in the right panel renders the same items the BottomThreadBar shows. The reorder capability was filed in #165 (BottomThreadBar). The right panel has the same gap with a drag handle that does nothing.

Where

  • src/components/workspace/RightPanel.tsx:486 — GripVertical icon with cursor: 'grab'
  • No drag handler: src/components/workspace/RightPanel.tsx:481-505 (the parent <div> has no draggable / onDragStart)
  • No reorder reducer action: grep -rn 'REORDER_THREAD_STOPS' src/ returns 0 hits (the reducer has no REORDER_THREAD_STOPS action)

Suggested fix shape

Two options:

  1. Remove the GripVertical icon (and its cursor: grab) until reorder is implemented. Replace with a non-interactive indicator (or just no leading icon). The row is clickable for the color picker; the user gets to see the order, but can't change it.

  2. Implement thread stop reorder. Add a REORDER_THREAD_STOPS reducer action ({ from: number, to: number }). Use HTML5 drag-and-drop on the row (draggable={true}, onDragStart={...}, onDragOver={preventDefault}, onDrop={...}). On drop, dispatch the reorder. The GripVertical becomes a real drag handle.

Option 2 is the right product move; the right panel and the BottomThreadBar both benefit.

Severity

Medium. The cursor change is a UI lie. Users who try to drag will see the cursor change but the row won't move.

Discovered by

Gap analysis (2026-10-02) of right panel affordances.

  • #165 (open) — BottomThreadBar's thread stop reorder (the same gap, in a different component). One fix covers both.
## Finding `src/components/workspace/RightPanel.tsx:486` renders a `GripVertical` icon as the first child of each thread stop row: ```tsx <GripVertical size={12} style={{ color: '#A8A29E', cursor: 'grab' }} /> ``` The `cursor: 'grab'` style suggests the user can drag the row to reorder it. Verified: there is no `onDragStart`, no `draggable` attribute, no `onDragOver`, no `onDrop` on the parent `<div>` (line 481-505). The cursor change is a visual lie. The thread stop list in the right panel renders the same items the BottomThreadBar shows. The reorder capability was filed in #165 (BottomThreadBar). The right panel has the same gap with a drag handle that does nothing. ## Where - `src/components/workspace/RightPanel.tsx:486` — `GripVertical` icon with `cursor: 'grab'` - No drag handler: `src/components/workspace/RightPanel.tsx:481-505` (the parent `<div>` has no `draggable` / `onDragStart`) - No reorder reducer action: `grep -rn 'REORDER_THREAD_STOPS' src/` returns 0 hits (the reducer has no `REORDER_THREAD_STOPS` action) ## Suggested fix shape Two options: 1. **Remove the GripVertical icon (and its `cursor: grab`) until reorder is implemented.** Replace with a non-interactive indicator (or just no leading icon). The row is clickable for the color picker; the user gets to see the order, but can't change it. 2. **Implement thread stop reorder.** Add a `REORDER_THREAD_STOPS` reducer action (`{ from: number, to: number }`). Use HTML5 drag-and-drop on the row (`draggable={true}`, `onDragStart={...}`, `onDragOver={preventDefault}`, `onDrop={...}`). On drop, dispatch the reorder. The GripVertical becomes a real drag handle. Option 2 is the right product move; the right panel and the BottomThreadBar both benefit. ## Severity Medium. The cursor change is a UI lie. Users who try to drag will see the cursor change but the row won't move. ## Discovered by Gap analysis (2026-10-02) of right panel affordances. ## Related - #165 (open) — BottomThreadBar's thread stop reorder (the same gap, in a different component). One fix covers both.
Author
Owner

Fixed in commit b5bc1fe on origin/main.

The right panel now mirrors the BottomThreadBar pattern from #165:

  • Each thread stop row renders a <GripVertical> drag handle wrapped in a <span draggable> that initiates the drag.
  • The row is a drop target. onDragEnter computes a left/right drop indicator from the cursor X relative to the row's midpoint; onDragOver allows the drop; onDragLeave clears the indicator when the cursor leaves the row (not when it enters a child element); onDrop dispatches REORDER_THREAD_STOPS { from, to } to the workspace reducer.
  • The source row dims to opacity 0.4 while a drag is in flight, so the user can see where the stop will land. Opacity flips back to 1 on dragend / drop once the reducer commits.
  • The reducer splice-moves the stop and pushes history, so undo/redo round-trip the new order (unchanged from #165).

6 new tests in issue-177.test.tsx cover the handle rendering, the forward-drop case (from < to, indicator adjusts target), the backward-drop case (from > to, no adjustment), same-row no-op, drop indicator visibility, and source-wrapper opacity dim. All 788 tests pass; the 3 EditorCanvas failures are pre-existing on main (verified via git stash) and unrelated to this change.

Out of scope for this card: the second GripVertical icon in the Object List (line 587 in the pre-fix file) renders the same visual lie but for a different surface (colorRegions / stitchObjects) with no reorder reducer action. Filing that as a separate gap so the next triage sweep can pick it up with the correct shape.

Fixed in commit b5bc1fe on origin/main. The right panel now mirrors the BottomThreadBar pattern from #165: - Each thread stop row renders a `<GripVertical>` drag handle wrapped in a `<span draggable>` that initiates the drag. - The row is a drop target. `onDragEnter` computes a left/right drop indicator from the cursor X relative to the row's midpoint; `onDragOver` allows the drop; `onDragLeave` clears the indicator when the cursor leaves the row (not when it enters a child element); `onDrop` dispatches `REORDER_THREAD_STOPS { from, to }` to the workspace reducer. - The source row dims to opacity 0.4 while a drag is in flight, so the user can see where the stop will land. Opacity flips back to 1 on `dragend` / `drop` once the reducer commits. - The reducer splice-moves the stop and pushes history, so undo/redo round-trip the new order (unchanged from #165). 6 new tests in `issue-177.test.tsx` cover the handle rendering, the forward-drop case (from < to, indicator adjusts target), the backward-drop case (from > to, no adjustment), same-row no-op, drop indicator visibility, and source-wrapper opacity dim. All 788 tests pass; the 3 EditorCanvas failures are pre-existing on main (verified via `git stash`) and unrelated to this change. Out of scope for this card: the second `GripVertical` icon in the Object List (line 587 in the pre-fix file) renders the same visual lie but for a different surface (colorRegions / stitchObjects) with no reorder reducer action. Filing that as a separate gap so the next triage sweep can pick it up with the correct shape.
Mark closed this issue 2026-10-03 19:38:26 +00:00
Sign in to join this conversation.
No description provided.