diff --git a/.changeset/silent-bees-sing.md b/.changeset/silent-bees-sing.md
new file mode 100644
index 000000000..10d5602e7
--- /dev/null
+++ b/.changeset/silent-bees-sing.md
@@ -0,0 +1,5 @@
+---
+'slate': patch
+---
+
+set_node operations did not invert correctly after serialization
diff --git a/packages/slate/src/transforms/general.ts b/packages/slate/src/transforms/general.ts
index a3d17a4ec..49fd0757a 100644
--- a/packages/slate/src/transforms/general.ts
+++ b/packages/slate/src/transforms/general.ts
@@ -182,7 +182,7 @@ export const GeneralTransforms: GeneralTransforms = {
}
case 'set_node': {
- const { path, newProperties } = op
+ const { path, properties, newProperties } = op
if (path.length === 0) {
throw new Error(`Cannot set properties on the root node!`)
@@ -204,6 +204,13 @@ export const GeneralTransforms: GeneralTransforms = {
}
}
+ // properties that were previously defined, but are now missing, must be deleted
+ for (const key in properties) {
+ if (!newProperties.hasOwnProperty(key)) {
+ delete node[key]
+ }
+ }
+
break
}
diff --git a/packages/slate/src/transforms/node.ts b/packages/slate/src/transforms/node.ts
index 4fac582a8..2a4d2d061 100644
--- a/packages/slate/src/transforms/node.ts
+++ b/packages/slate/src/transforms/node.ts
@@ -631,7 +631,8 @@ export const NodeTransforms: NodeTransforms = {
}
if (props[k] !== node[k]) {
- properties[k] = node[k]
+ // Omit new properties from the old property list rather than set them to undefined
+ if (node.hasOwnProperty(k)) properties[k] = node[k]
newProperties[k] = props[k]
}
}
diff --git a/packages/slate/test/operations/set_node/remove-null.tsx b/packages/slate/test/operations/set_node/remove-null.tsx
new file mode 100644
index 000000000..918d65809
--- /dev/null
+++ b/packages/slate/test/operations/set_node/remove-null.tsx
@@ -0,0 +1,28 @@
+/** @jsx jsx */
+import { jsx } from 'slate-hyperscript'
+import { Transforms, Editor } from 'slate'
+
+export const input = (
+
+
+
+
+
+)
+
+export const operations = [
+ {
+ type: 'set_node',
+ path: [0, 0],
+ properties: { key: true },
+ newProperties: { key: null },
+ },
+]
+
+export const output = (
+
+
+
+
+
+)
diff --git a/packages/slate/test/operations/set_node/remove-omit.tsx b/packages/slate/test/operations/set_node/remove-omit.tsx
new file mode 100644
index 000000000..615c5c89a
--- /dev/null
+++ b/packages/slate/test/operations/set_node/remove-omit.tsx
@@ -0,0 +1,28 @@
+/** @jsx jsx */
+import { jsx } from 'slate-hyperscript'
+import { Transforms, Editor } from 'slate'
+
+export const input = (
+
+
+
+
+
+)
+
+export const operations = [
+ {
+ type: 'set_node',
+ path: [0, 0],
+ properties: { key: true },
+ newProperties: {},
+ },
+]
+
+export const output = (
+
+
+
+
+
+)
diff --git a/packages/slate/test/operations/set_node/remove-undefined.tsx b/packages/slate/test/operations/set_node/remove-undefined.tsx
new file mode 100644
index 000000000..0c8cc7d96
--- /dev/null
+++ b/packages/slate/test/operations/set_node/remove-undefined.tsx
@@ -0,0 +1,28 @@
+/** @jsx jsx */
+import { jsx } from 'slate-hyperscript'
+import { Transforms, Editor } from 'slate'
+
+export const input = (
+
+
+
+
+
+)
+
+export const operations = [
+ {
+ type: 'set_node',
+ path: [0, 0],
+ properties: { key: true },
+ newProperties: { key: undefined },
+ },
+]
+
+export const output = (
+
+
+
+
+
+)
diff --git a/packages/slate/test/transforms/setNodes/basic-structure/can-be-serialized.tsx b/packages/slate/test/transforms/setNodes/basic-structure/can-be-serialized.tsx
new file mode 100644
index 000000000..7e9cc31bd
--- /dev/null
+++ b/packages/slate/test/transforms/setNodes/basic-structure/can-be-serialized.tsx
@@ -0,0 +1,25 @@
+/** @jsx jsx */
+import assert from 'assert'
+import { Editor, Transforms, Operation } from 'slate'
+import { jsx } from '../../..'
+
+export const run = (editor: Editor) => {
+ Transforms.setNodes(editor, { key: true }, { at: [0] })
+ const [op] = editor.operations
+ const roundTrip: Operation = JSON.parse(JSON.stringify(op))
+ assert.deepStrictEqual(op, roundTrip)
+}
+export const input = (
+
+
+
+
+
+)
+export const output = (
+
+
+
+
+
+)
diff --git a/packages/slate/test/transforms/setNodes/basic-structure/invert-after-serialization.tsx b/packages/slate/test/transforms/setNodes/basic-structure/invert-after-serialization.tsx
new file mode 100644
index 000000000..4e7218194
--- /dev/null
+++ b/packages/slate/test/transforms/setNodes/basic-structure/invert-after-serialization.tsx
@@ -0,0 +1,25 @@
+/** @jsx jsx */
+import { Editor, Transforms, Operation } from 'slate'
+import { jsx } from '../../..'
+
+export const run = (editor: Editor) => {
+ Transforms.setNodes(editor, { key: true }, { at: [0] })
+ const [op] = editor.operations
+ const roundTrip: Operation = JSON.parse(JSON.stringify(op))
+ const inverted = Operation.inverse(roundTrip)
+ editor.apply(inverted)
+}
+export const input = (
+
+
+
+
+
+)
+export const output = (
+
+
+
+
+
+)