Skip to content

Key-rename permission checks the parent's allowAdd, not the node's (#374) - #387

Merged
CarlosNZ merged 3 commits into
mainfrom
374-allowAdd-consistency-fix
Jun 27, 2026
Merged

Key-rename permission checks the parent's allowAdd, not the node's (#374)#387
CarlosNZ merged 3 commits into
mainfrom
374-allowAdd-consistency-fix

Conversation

@CarlosNZ

Copy link
Copy Markdown
Owner

Fixes #374.

What

A key-rename is a delete of the old key + an add of the new one to the parent collection, but the gate (canEditKey in useCommon.ts) evaluated allowEdit && allowAdd && allowDelete all on the node itself. That was wrong on two counts:

  • the add-half should consult the parent collection's allowAdd, not the node's — allowAdd(node) is meaningless on a leaf, and restricting allowAdd to a few collections silently locked every key in the tree;
  • allowEdit shouldn't gate a rename at all — a rename isn't a value edit.

Rule

A key is renamable when it isn't the root or an array index, the node is allowDelete, and its parent collection is allowAdd. allowEdit plays no part — a value-locked key can still be renamed.

This makes "can a key appear in collection X?" a single answer — allowAdd(X) — across the add button, rename, and drag relocate (the relocate rule landed in #386):

How a node enters a collection Gate
Add button allowAdd(collection)
Relocate (drag into a different collection) allowDelete(source) + allowAdd(destination)
Reorder (drag within the same collection) allowEdit(collection)
Rename (this PR) allowDelete(node) + allowAdd(parent)

How

One-liner, no new threading: #386 already threads the parent's allowAdd to every child as the canAddHere prop (CollectionNode passes canAddHere={canAdd}; root passes false). It's on BaseNodeProps, so useCommon already receives it — the gate just reads it:

const canEditKey = parentData !== null && !isArray && canDelete && canAddHere

canEdit / canAdd stay (still used for the value editor, add button, and child threading).

Compatibility (semver-significant)

Defaults are true, so most consumers see no change. Only those with restrictive allowEdit / allowAdd filters shift — toward the more correct behaviour:

  • allowEdit:false no longer blocks a key-rename (use allowDelete:false / a restrictive allowAdd on the parent to lock keys);
  • a key's rename now follows the parent's allowAdd, not the node's own.

Tests

test/JsonEditor.test.tsx: 3 new regression tests (confirmed red before the fix, green after) — add-half checks the parent not the node; parent forbidding adds blocks the rename even when the node allows them; allowEdit:false no longer blocks a rename — plus an array-index guard. Full suite 682 pass, lint + tsc clean, demo tsc + eslint clean.

Demo

Redesigned the edit-restrictions example as self-documenting matrix data (mirroring the drag-drop-rules page): three boxes — addable (allowAdd ✓), sealed (allowAdd ✗), editable (the whole box edits as raw JSON) — each holding the same four emoji-labelled leaves (🟢 edit+delete, 🟡 delete-only, 🟠 edit-only, 🔴 neither). It demonstrates every non-DnD permission combo, that the same 🟢 renames in addable but not sealed (the parent-side check), that 🟡 renames despite a frozen value (no allowEdit), and that editing the editable box as JSON overrides every per-leaf rule inside it. Registry blurb rewritten to match.

Docs

README_V2 key-rename NOTE rewritten; migration-guide §5 gets a short subsection; CHANGELOG entry under a new 2.0.0-beta.6 (beta.5 is already the published beta tag).

Not done in this PR

package.json left at beta.5 — the version bump is yours at publish time.

🤖 Generated with Claude Code

)

A key-rename is a delete of the old key + an add of the new one to the
PARENT collection, so gate it as exactly that: the node must be
deletable AND its parent collection must accept adds. The add-half now
reads the parent's allowAdd (via the `canAddHere` prop #386 already
threads down) instead of the node's own, and allowEdit no longer takes
part — a value-locked key can still be renamed.

This fixes the footgun where restricting allowAdd to a few collections
silently locked every key in the tree, and makes "can a key appear in
collection X?" a single answer (allowAdd(X)) across the add button,
rename, and drag relocate.

- src/hooks/useCommon.ts: rewrite the canEditKey gate.
- test/JsonEditor.test.tsx: 3 regression tests (red before, green
  after) + an array-index guard.
- demo edit-restrictions example: redesigned as self-documenting matrix
  data (addable / sealed / editable boxes), mirroring drag-drop-rules;
  the `editable` box shows raw-JSON editing overriding per-leaf rules.
- README_V2 NOTE, migration-guide §5 subsection, CHANGELOG beta.6.

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

Copy link
Copy Markdown

Bundle size impact

json-edit-react

Format Base raw PR raw Δ raw Base gzip PR gzip Δ gzip
esm 57.99 KB 58.00 KB 🔺 +10 B (+0.02%) 20.70 KB 20.70 KB 🔺 +6 B (+0.03%)
cjs 59.49 KB 59.50 KB 🔺 +10 B (+0.02%) 20.74 KB 20.74 KB 🔺 +2 B (+0.01%)

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 fixes key-rename permission logic so renaming a property key is gated by deleting the node (allowDelete(node)) and adding to the parent collection (allowAdd(parent) via the threaded canAddHere), and no longer depends on allowEdit (since a rename is not a value edit). This aligns key-renaming with the permission model used elsewhere (notably drag-and-drop relocate rules).

Changes:

  • Update canEditKey to require canDelete && canAddHere (parent add permission) and drop canEdit/node-canAdd from the rename gate.
  • Add regression tests covering parent-vs-node add permission, parent-sealed blocking, allowEdit={false} allowing rename, and array index non-renamability.
  • Update docs/changelog and redesign the demo “edit-restrictions” example to reflect the corrected permission semantics.

Reviewed changes

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

Show a summary per file
File Description
src/hooks/useCommon.ts Fixes canEditKey to use parent add permission (canAddHere) and remove allowEdit from rename gating.
test/JsonEditor.test.tsx Adds regression tests validating the corrected rename permission rules.
README_V2.md Updates documentation note to describe the new rename permission rule and its rationale.
migration-guide.md Adds a migration note describing the behavior change for restrictive permission filters.
demo/src/examples/static/edit-restrictions/Example.tsx Reworks the demo to clearly demonstrate edit/delete/add combinations and the parent-based rename rule.
demo/src/examples/registry.ts Updates the example blurb to match the redesigned demo and new semantics.
CHANGELOG.md Adds a 2.0.0-beta.6 entry documenting the rename permission change.

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

CarlosNZ and others added 2 commits June 27, 2026 19:29
Reads from the README "Controlling editing" / permissions context: lead
with the three editing permissions (not including drag-n-drop), and
introduce all three boxes up front (addable / sealed / editable) rather
than "two boxes" + a later third. Same wording fix in the example's
source comment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@CarlosNZ
CarlosNZ merged commit d39c2be into main Jun 27, 2026
2 checks passed
@CarlosNZ
CarlosNZ deleted the 374-allowAdd-consistency-fix branch June 27, 2026 07:34
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.

Key-rename permission checks the node's own allowAdd instead of the parent's

2 participants