Skip to content

fix: prevent duplication of work package chips during drag and drop#129

Open
ihordubas99 wants to merge 2 commits intodevfrom
bug/74540-dragging-text-containing-chip-creates-copy-instead-of-moving-it
Open

fix: prevent duplication of work package chips during drag and drop#129
ihordubas99 wants to merge 2 commits intodevfrom
bug/74540-dragging-text-containing-chip-creates-copy-instead-of-moving-it

Conversation

@ihordubas99
Copy link
Copy Markdown
Collaborator

@ihordubas99 ihordubas99 commented Apr 29, 2026

Ticket

https://community.openproject.org/projects/communicator-stream/work_packages/74540

What are you trying to accomplish?

Fix a duplication bug that occurs when dragging and dropping Work Package chips (both Inline and Block) inside the BlockNote editor.

Previously, if a user selected text along with a chip and initiated the drag specifically by grabbing the chip (contentEditable={false}), ProseMirror would treat the drop as a new HTML insertion (copy) rather than a structural move, leaving the original content behind.

What approach did you choose and why?

The root cause of the duplication bug lies in how the browser handles native dragstart events on non-editable elements. When a user drags a chip, the browser takes over the drag operation, causing ProseMirror to lose the context of the internal text selection. Because of this, ProseMirror defaults to a copy operation upon drop, inserting the HTML without removing the original source.

To resolve this, I created a custom useDragSelection hook to encapsulate the internal ProseMirror logic. This hook intercepts the dragstart event, checks if there is an active selection range, and explicitly instructs ProseMirror to treat the action as a move operation by configuring the internal dragging state. This ensures that the original content is properly deleted after the drop.

I implemented this hook in both the inline and block work package components. For the inline chip specifically, I extracted the BlockNote-specific logic (useBlockNoteEditor + useDragSelection) into a thin InlineWorkPackageChipInEditor wrapper component. This keeps InlineWorkPackageChip free of any BlockNote context dependency, making it independently testable and renderable outside of the editor. The drag handler is passed down as an optional onDragStart prop.

Initially, I considered overriding the global handleDrop or transformPastedHTML properties directly on the ProseMirror view. I ultimately discarded this approach because it creates a leaky abstraction and risks breaking standard drag-and-drop behaviors across the entire editor. Handling the fix locally at the component level via a reusable hook is a much safer approach that keeps the UI components clean and limits the potential blast radius.

Merge checklist

  • Added/updated tests
  • Added/updated documentation in Lookbook (patterns, previews, etc)
  • Tested major browsers (Chrome, Firefox, Edge, ...)

@ihordubas99 ihordubas99 self-assigned this Apr 29, 2026
@ihordubas99 ihordubas99 marked this pull request as ready for review April 30, 2026 10:42
@ihordubas99 ihordubas99 requested a review from judithroth April 30, 2026 10:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant