Skip to content

onUpdate: split node-level { value } from whole-document { data } (#375)#380

Merged
CarlosNZ merged 3 commits into
v2.0-devfrom
375-return-value-from-onUpdate
Jun 23, 2026
Merged

onUpdate: split node-level { value } from whole-document { data } (#375)#380
CarlosNZ merged 3 commits into
v2.0-devfrom
375-return-value-from-onUpdate

Conversation

@CarlosNZ

Copy link
Copy Markdown
Owner

Closes #375.

Summary

An onUpdate that returns { value: X } previously replaced the whole document with X (applied at the root) — the name reads like "this node's value", so returning a node value silently clobbered the entire document. This splits the two intents:

  • { value: X } → the edited node's value, applied at its path. The common "tidy what the user just typed" case (lower-case, round, trim, sort this array). Honoured for edit / add; silently ignored for rename / move / delete (no node value to set).
  • { data: X } → the whole document (the previous behaviour). Cross-field changes — stamping a lastModified, sorting siblings, canonicalising structure. Works on every event.

Returning both is a mistake: data wins, value is ignored, and a console.warn is emitted.

How it works

applyValue(path, value) already existed — node-level is just applyValue(nodePath, value), whole-doc is applyValue([], data). The result-mapping in runUpdate resolves which path to use (it has the event + node in scope; for add, input.path is the new child's full path), the override outcome carries that path, and reconcile applies it.

  • src/types.tsUpdateResult object form is now { value?: unknown; data?: T; error? }.
  • src/JsonEditor.tsxrunUpdate maps data→root / value→node (gated to edit/add) and warns on both keys.
  • src/contexts/EditingProvider.tsxoverride outcome carries path; reconcile applies at it.

Breaking change

{ value } no longer replaces the whole document. Any onUpdate relying on that (whole-document timestamp/sort transforms) must switch to { data }. v2 is in beta, so now is the time. Migration-guide §9 updated; major changeset added.

Tests

Rewrote the old whole-doc test and added 6 to test/JsonEditor.test.tsx covering the new contract: node-level edit (proves sibling preservation, not a root replace), { data } whole-doc, value-on-add, value-ignored-on-delete, data-on-delete, and both-keys-warn. All 6 were confirmed to fail for the right reason before the fix, green after.

Docs & demo

  • README return-types list, migration-guide §9, dev-docs/EditingModel-new.md + MANUAL-TESTING-editing-model.md.
  • Migrated all 5 whole-document { value } sites to { data } (on-update example — which also gained a node-level { value } showcase + a new { data } branch; editing-model "Override" mode; three in the liveData guestbook). Removed the TODO(#375).

Verification

pnpm test (670 pass), pnpm compile, pnpm lint all green; demo typechecks against the updated core.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown

Bundle size impact

json-edit-react

Format Base raw PR raw Δ raw Base gzip PR gzip Δ gzip
esm 57.38 KB 57.66 KB 🔺 +288 B (+0.49%) 20.48 KB 20.60 KB 🔺 +124 B (+0.59%)
cjs 58.87 KB 59.16 KB 🔺 +288 B (+0.48%) 20.52 KB 20.64 KB 🔺 +124 B (+0.59%)

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR changes the onUpdate override contract to distinguish between node-level overrides ({ value }, applied at the edited node’s path) and whole-document overrides ({ data }, applied at the root), preventing accidental full-document clobbers when callers intended to “tidy” a single edited value.

Changes:

  • Update the public UpdateResult type to support { value?: unknown; data?: T; error? } and document the new semantics.
  • Map { data } → root override and { value } → node-path override (edit/add only) in runUpdate, and carry the override path through the commit outcome to reconciliation.
  • Expand Jest coverage to validate node-level vs whole-doc overrides across edit/add/delete, and update docs/demo/migration guidance plus a major changeset.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/JsonEditor.test.tsx Replaces the prior whole-doc { value } test with coverage for { value } (node-level), { data } (whole-doc), gated events, and both-keys warning.
src/types.ts Updates UpdateResult object form to include { data } and documents event-specific semantics and precedence.
src/JsonEditor.tsx Adjusts runUpdate to resolve override scope and return { status:'override', value, path }, warning when both keys are returned.
src/contexts/EditingProvider.tsx Extends UpdateOutcome to carry an override path and applies overrides via applyValue(outcome.path, ...).
README.md Updates onUpdate return-type docs to explain { value } vs { data } and precedence/warning behavior.
migration-guide.md Updates migration guidance to reflect { data } as the whole-document override key and clarifies the breaking change.
dev-docs/MANUAL-TESTING-editing-model.md Updates manual testing instructions to use { data } for whole-document overrides.
dev-docs/EditingModel-new.md Updates editing model documentation to reflect the new override split and semantics.
demo/src/examples/static/on-update/Example.tsx Updates the on-update demo to show node-level { value } and whole-doc { data } usage.
demo/src/examples/editing-model/Example.tsx Updates the “override” mode example to return { data } instead of { value }.
demo/src/demoData/dataDefinitions.tsx Migrates demo callback sites from { value: newData } whole-doc overrides to { data: newData }.
.changeset/onupdate-value-data-split.md Adds a major changeset describing the breaking change and migration path.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread test/JsonEditor.test.tsx
Addresses PR review: a `jest.spyOn(console, 'warn')` whose manual
`mockRestore()` runs only on success leaks the spy into later tests if an
assertion throws first. Enable `restoreMocks: true` globally so every spy
auto-restores after each test, and drop the now-redundant manual restores
(in the both-keys test and the pre-existing drag-and-drop key-collision
test).

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment thread src/JsonEditor.tsx
Addresses PR review: clarify that `{ value: undefined }` / `{ data: undefined }`
are deliberately treated as "no override for this key" (consistent with the
protocol's top-level `undefined`/`void` = proceed, and the adjacent `error`
check), rather than overriding a node to `undefined`. Comment only — no
behaviour change.

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.

@CarlosNZ CarlosNZ merged commit ceb8dd9 into v2.0-dev Jun 23, 2026
3 checks passed
@CarlosNZ CarlosNZ deleted the 375-return-value-from-onUpdate branch June 23, 2026 05:18
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