Skip to content

fix: refresh internalDrag.origin on every dragstart#2557

Merged
christianhg merged 1 commit intoeditor-v6.xfrom
fix/dnd-stale-internalDrag-origin
Apr 29, 2026
Merged

fix: refresh internalDrag.origin on every dragstart#2557
christianhg merged 1 commit intoeditor-v6.xfrom
fix/dnd-stale-internalDrag-origin

Conversation

@christianhg
Copy link
Copy Markdown
Member

@christianhg christianhg commented Apr 29, 2026

The bug

In Sanity Studio, dragging block objects between each other sometimes resulted in two copies of the second block appearing in the document, with the first block deleted. The captured event log showed the failing drop carried dragOrigin._key = imageA while dataTransfer contained image B's content. The drop handler deleted the block referenced by dragOrigin and inserted the block carried by dataTransfer, so image B ended up duplicated and image A was lost.

How dragOrigin and dataTransfer got out of sync

dragOrigin is read from internalDrag.origin in the editor machine. Before this fix, the machine only assigned internalDrag.origin while in the idle state, and only returned to idle when the document received a drop or dragend event.

dataTransfer is overwritten by the LATEST dragstart in the behavior pipeline, which always re-runs the dragstart serializer regardless of machine state.

In some browsers (and consistently in Studio under specific layouts), dragend does not fire on the document after a successful drop. The machine stayed in dragging internally, the next dragstart was ignored, and internalDrag.origin kept pointing at the previous gesture's source while dataTransfer was freshly serialized from the new gesture. On drop, the two inputs disagreed and the drop handler corrupted the document.

The fix

dragstart now re-assigns internalDrag.origin while in dragging internally, in addition to the existing assignment on the idle → dragging internally transition. The behavior layer continues to trust its inputs unchanged.

Regression test

packages/editor/tests/event.drag.drop.duplication.test.tsx drives the actor directly: it dispatches two consecutive dragstart events without an intervening dragend and asserts that internalDrag.origin reflects the LATEST dragstart's origin, not the first.

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 29, 2026

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

Project Deployment Actions Updated (UTC)
portable-text-editor-documentation Ready Ready Preview, Comment Apr 29, 2026 7:21am
portable-text-example-basic Ready Ready Preview, Comment Apr 29, 2026 7:21am
portable-text-playground Ready Ready Preview, Comment Apr 29, 2026 7:21am

Request Review

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 29, 2026

🦋 Changeset detected

Latest commit: f04ee37

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 11 packages
Name Type
@portabletext/editor Patch
@portabletext/plugin-character-pair-decorator Patch
@portabletext/plugin-emoji-picker Patch
@portabletext/plugin-input-rule Patch
@portabletext/plugin-markdown-shortcuts Patch
@portabletext/plugin-one-line Patch
@portabletext/plugin-paste-link Patch
@portabletext/plugin-sdk-value Patch
@portabletext/plugin-typeahead-picker Patch
@portabletext/plugin-typography Patch
@portabletext/toolbar Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 29, 2026

📦 Bundle Stats — @portabletext/editor

Compared against editor-v6.x (5454335c)

@portabletext/editor

Metric Value vs editor-v6.x (5454335)
Internal (raw) 747.4 KB +267 B, +0.0%
Internal (gzip) 140.8 KB +5 B, +0.0%
Bundled (raw) 1.34 MB +269 B, +0.0%
Bundled (gzip) 301.2 KB +4 B, +0.0%
Import time 95ms -0ms, -0.0%

@portabletext/editor/behaviors

Metric Value vs editor-v6.x (5454335)
Internal (raw) 467 B -
Internal (gzip) 207 B -
Bundled (raw) 424 B -
Bundled (gzip) 171 B -
Import time 2ms +0ms, +2.4%

@portabletext/editor/plugins

Metric Value vs editor-v6.x (5454335)
Internal (raw) 2.5 KB -
Internal (gzip) 910 B -
Bundled (raw) 2.3 KB -
Bundled (gzip) 839 B -
Import time 8ms +0ms, +1.6%

@portabletext/editor/selectors

Metric Value vs editor-v6.x (5454335)
Internal (raw) 60.5 KB -
Internal (gzip) 9.5 KB -
Bundled (raw) 56.9 KB -
Bundled (gzip) 8.7 KB -
Import time 6ms -0ms, -0.9%

@portabletext/editor/utils

Metric Value vs editor-v6.x (5454335)
Internal (raw) 24.2 KB -
Internal (gzip) 4.7 KB -
Bundled (raw) 22.2 KB -
Bundled (gzip) 4.4 KB -
Import time 6ms -0ms, -0.2%

🗺️ . · ./behaviors · ./plugins · ./selectors · ./utils · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

When 'dragend' was missed after a successful drop, the editor machine stayed in 'dragging internally' and ignored subsequent 'dragstart' events. Any later drop then read the previous gesture's drag origin while 'dataTransfer' carried the new gesture's content, so the drop handler deleted the wrong block and duplicated the new one.

'dragstart' now re-assigns 'internalDrag.origin' even when the machine has not returned to 'idle'. Includes a regression test that drives the actor directly to capture the stale-origin state.
@christianhg christianhg force-pushed the fix/dnd-stale-internalDrag-origin branch from 1f25e40 to f04ee37 Compare April 29, 2026 07:19
@christianhg christianhg changed the title test: reproduce drag-and-drop duplication on stale internalDrag.origin fix: refresh internalDrag.origin on every dragstart Apr 29, 2026
@christianhg christianhg marked this pull request as ready for review April 29, 2026 07:19
@christianhg christianhg merged commit fff1313 into editor-v6.x Apr 29, 2026
17 checks passed
@christianhg christianhg deleted the fix/dnd-stale-internalDrag-origin branch April 29, 2026 07:48
@ecoscript ecoscript Bot mentioned this pull request Apr 29, 2026
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.

1 participant