diff --git a/.changeset/popular-ways-fail.md b/.changeset/popular-ways-fail.md
new file mode 100644
index 000000000..12e4de2ab
--- /dev/null
+++ b/.changeset/popular-ways-fail.md
@@ -0,0 +1,5 @@
+---
+'slate': minor
+---
+
+Fixed regression in #4208 where normalization on empty block nodes could not be overridden
diff --git a/packages/slate/src/interfaces/editor.ts b/packages/slate/src/interfaces/editor.ts
index 1863e071e..f56626828 100644
--- a/packages/slate/src/interfaces/editor.ts
+++ b/packages/slate/src/interfaces/editor.ts
@@ -997,16 +997,18 @@ export const Editor: EditorInterface = {
*/
for (const dirtyPath of getDirtyPaths(editor)) {
if (Node.has(editor, dirtyPath)) {
- const [node, _] = Editor.node(editor, dirtyPath)
+ const entry = Editor.node(editor, dirtyPath)
+ const [node, _] = entry
- // Add a text child to elements with no children.
- // This is safe to do in any order, by definition it can't cause other paths to change.
+ /*
+ The default normalizer inserts an empty text node in this scenario, but it can be customised.
+ So there is some risk here.
+
+ As long as the normalizer only inserts child nodes for this case it is safe to do in any order;
+ by definition adding children to an empty node can't cause other paths to change.
+ */
if (Element.isElement(node) && node.children.length === 0) {
- const child = { text: '' }
- Transforms.insertNodes(editor, child, {
- at: dirtyPath.concat(0),
- voids: true,
- })
+ editor.normalizeNode(entry)
}
}
}
diff --git a/packages/slate/test/normalization/block/insert-custom-block.tsx b/packages/slate/test/normalization/block/insert-custom-block.tsx
new file mode 100644
index 000000000..55797b762
--- /dev/null
+++ b/packages/slate/test/normalization/block/insert-custom-block.tsx
@@ -0,0 +1,40 @@
+/** @jsx jsx */
+import { jsx } from '../..'
+import { Editor, Element, Transforms } from 'slate'
+
+export const input = (
+
+
+
+)
+
+// patch in a custom normalizer that inserts empty paragraphs in the body instead of text nodes
+// this test also verifies the new node itself is also normalized, because it's inserting a non-normalized node
+const editor = (input as unknown) as Editor
+const defaultNormalize = editor.normalizeNode
+editor.normalizeNode = entry => {
+ const [node, path] = entry
+ if (
+ Element.isElement(node) &&
+ node.children.length === 0 &&
+ (node as any).type === 'body'
+ ) {
+ const child = { type: 'paragraph', children: [] }
+ Transforms.insertNodes(editor, child, {
+ at: path.concat(0),
+ voids: true,
+ })
+ } else {
+ defaultNormalize(entry)
+ }
+}
+
+export const output = (
+
+
+
+
+
+
+
+)