Skip to content

Commit open edit when displaced by clicking another node (#376) - #379

Merged
CarlosNZ merged 3 commits into
v2.0-devfrom
376-correct-behaviour-when-clicking-edit-on-another-node-when-session-is-open
Jun 22, 2026
Merged

Commit open edit when displaced by clicking another node (#376)#379
CarlosNZ merged 3 commits into
v2.0-devfrom
376-correct-behaviour-when-clicking-edit-on-another-node-when-session-is-open

Conversation

@CarlosNZ

Copy link
Copy Markdown
Owner

Closes #376.

What & why

Opening an edit on another node — clicking its ✎ pencil, double-clicking another value, or clicking another key to rename — currently cancels the in-progress edit. But Tab commits then moves. The two "leave this field and go edit elsewhere" gestures behaved oppositely, so typing a value and clicking the next field silently discarded the typed text.

This makes a click-to-another-node switch behave like Tab.

Behaviour (the contract the tests pin)

Outgoing session Changed Unchanged Invalid
value edit commit + commitEdit + onUpdate, open new submitEdit+commitEdit (no onUpdate/setData), open new block: stay open w/ error
collection JSON edit commit, open new commits (ref-eq misses structural no-op) malformed JSON → block
key rename commitRename + onUpdate, open new commitRename (no onUpdate), open new duplicate key → block
object add cancelAdd, open new cancelAdd, open new n/a
  • No-op → commit* (consistent with Tab), incl. no-op rename → commitRename (now flagged via buildCommit isNoOp: oldKey === newKey; also aligns Tab-off-an-unchanged-key).
  • Object add is the exception — a displace still cancels it (you can't Tab out of a new-key edit). Implemented simply by not registering a commit callback for add.
  • Invalid blocks the switch — the editor stays open with its inline error; Esc / ✗ remain the discard path.

How

  • The commit logic lives on the outgoing node but open() is called by the incoming one, so the outgoing node registers a commit-on-displace callback in the editing store, alongside today's cancelOp. open(), on a switch, calls it and defers opening the new node into the commit's onCommit (synchronous for editor ops, so it still feels instant) — no re-entrancy into open().
  • The callback forwards to the node's live handleEdit via a ref — a closure frozen at open() time would commit the stale initial buffer (the ref hooks sit above the !isVisible early returns to keep hook order stable).
  • Invalid-blocks and no-op fall out of the existing engine: an invalid edit returns before submit (so onCommit never runs → switch blocked); a no-op routes through submit's isNoOp branch.
  • submit() now also abandons gracefully if a displace fires for a session whose node already unmounted (buildCommit would otherwise throw on the vanished path).

Touches: EditingProvider (store), ValueNodeWrapper, CollectionNode, KeyDisplay + useCommon (rename), JsonEditor (buildCommit).

Tests & docs

  • Rewrote the two cancel-on-displace tests to the commit contract (confirmed failing first) and added 7 new tests covering changed / unchanged / invalid across value, collection, custom-node, rename, plus the add-cancels regression guard.
  • Updated the A8 manual-test matrix, added a changeset, and noted the onEditEvent contract shift in the migration guide (§10) + an end-user README bullet.
  • pnpm test (653 pass), pnpm compile, pnpm lint all green.

Note for review

One behaviour change slightly beyond the strict displace scope, for consistency: a duplicate-key rename on Enter now keeps the editor open with the error instead of closing (matching value/collection invalid handling). And the automated suite can't reproduce the Firefox DnD phantom-dragstart path — a quick Firefox walk of the A8 matrix (incl. a drag right after a commit-on-displace) is worth doing before merge, though the draggable flip logic is untouched.

🤖 Generated with Claude Code

Opening an edit on another node (its pencil, double-clicking another
value, or clicking another key to rename) now COMMITS the in-progress
edit instead of cancelling it — matching Tab. Previously the two
"leave this field and go edit elsewhere" gestures behaved oppositely
(Tab committed, click-away discarded), silently dropping typed text.

Behaviour now mirrors Tab:
- changed edit -> commits (onUpdate runs), then opens the new node
- unchanged edit -> closes via commit* (no onUpdate / setData), opens new
- can't commit (malformed JSON, duplicate key, throwing fromStandardType)
  -> switch is BLOCKED; editor stays open with its inline error
- object-add session -> still cancels (you can't Tab out of a new-key edit)

The outgoing node registers a commit-on-displace callback in the editing
store (alongside cancelOp); open() invokes it on a switch, deferring the
new node's opening into the commit's onCommit. The callback forwards to
the node's LIVE handleEdit via a ref so it commits the current buffer,
not a stale closure. submit() now also abandons gracefully if a
commit-on-displace fires for a session whose node already unmounted.

Includes rewritten + new regression tests, the A8 manual-test matrix,
a changeset, and migration-guide / README notes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown

Bundle size impact

json-edit-react

Format Base raw PR raw Δ raw Base gzip PR gzip Δ gzip
esm 56.71 KB 57.27 KB 🔺 +574 B (+0.99%) 20.30 KB 20.45 KB 🔺 +149 B (+0.72%)
cjs 58.19 KB 58.76 KB 🔺 +587 B (+0.99%) 20.33 KB 20.49 KB 🔺 +162 B (+0.78%)

Measured from build/index.{cjs,esm}.js. Gzip at level 9.

@CarlosNZ
CarlosNZ merged commit 94e5598 into v2.0-dev Jun 22, 2026
@CarlosNZ
CarlosNZ deleted the 376-correct-behaviour-when-clicking-edit-on-another-node-when-session-is-open branch June 22, 2026 23:04
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