@portabletext/editor 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/lib/index.d.mts +911 -0
  4. package/lib/index.d.ts +911 -0
  5. package/lib/index.esm.js +4896 -0
  6. package/lib/index.esm.js.map +1 -0
  7. package/lib/index.js +4874 -0
  8. package/lib/index.js.map +1 -0
  9. package/lib/index.mjs +4896 -0
  10. package/lib/index.mjs.map +1 -0
  11. package/package.json +119 -0
  12. package/src/editor/Editable.tsx +683 -0
  13. package/src/editor/PortableTextEditor.tsx +308 -0
  14. package/src/editor/__tests__/PortableTextEditor.test.tsx +386 -0
  15. package/src/editor/__tests__/PortableTextEditorTester.tsx +116 -0
  16. package/src/editor/__tests__/RangeDecorations.test.tsx +115 -0
  17. package/src/editor/__tests__/handleClick.test.tsx +218 -0
  18. package/src/editor/__tests__/pteWarningsSelfSolving.test.tsx +389 -0
  19. package/src/editor/__tests__/utils.ts +39 -0
  20. package/src/editor/components/DraggableBlock.tsx +287 -0
  21. package/src/editor/components/Element.tsx +279 -0
  22. package/src/editor/components/Leaf.tsx +288 -0
  23. package/src/editor/components/SlateContainer.tsx +81 -0
  24. package/src/editor/components/Synchronizer.tsx +190 -0
  25. package/src/editor/hooks/usePortableTextEditor.ts +23 -0
  26. package/src/editor/hooks/usePortableTextEditorKeyGenerator.ts +24 -0
  27. package/src/editor/hooks/usePortableTextEditorSelection.ts +22 -0
  28. package/src/editor/hooks/usePortableTextEditorValue.ts +16 -0
  29. package/src/editor/hooks/usePortableTextReadOnly.ts +20 -0
  30. package/src/editor/hooks/useSyncValue.test.tsx +125 -0
  31. package/src/editor/hooks/useSyncValue.ts +372 -0
  32. package/src/editor/nodes/DefaultAnnotation.tsx +16 -0
  33. package/src/editor/nodes/DefaultObject.tsx +15 -0
  34. package/src/editor/nodes/index.ts +189 -0
  35. package/src/editor/plugins/__tests__/withEditableAPIDelete.test.tsx +244 -0
  36. package/src/editor/plugins/__tests__/withEditableAPIGetFragment.test.tsx +142 -0
  37. package/src/editor/plugins/__tests__/withEditableAPIInsert.test.tsx +346 -0
  38. package/src/editor/plugins/__tests__/withEditableAPISelectionsOverlapping.test.tsx +162 -0
  39. package/src/editor/plugins/__tests__/withHotkeys.test.tsx +212 -0
  40. package/src/editor/plugins/__tests__/withInsertBreak.test.tsx +204 -0
  41. package/src/editor/plugins/__tests__/withPlaceholderBlock.test.tsx +133 -0
  42. package/src/editor/plugins/__tests__/withPortableTextLists.test.tsx +65 -0
  43. package/src/editor/plugins/__tests__/withPortableTextMarkModel.test.tsx +1377 -0
  44. package/src/editor/plugins/__tests__/withPortableTextSelections.test.tsx +91 -0
  45. package/src/editor/plugins/__tests__/withUndoRedo.test.tsx +115 -0
  46. package/src/editor/plugins/createWithEditableAPI.ts +573 -0
  47. package/src/editor/plugins/createWithHotKeys.ts +304 -0
  48. package/src/editor/plugins/createWithInsertBreak.ts +45 -0
  49. package/src/editor/plugins/createWithInsertData.ts +359 -0
  50. package/src/editor/plugins/createWithMaxBlocks.ts +24 -0
  51. package/src/editor/plugins/createWithObjectKeys.ts +63 -0
  52. package/src/editor/plugins/createWithPatches.ts +274 -0
  53. package/src/editor/plugins/createWithPlaceholderBlock.ts +36 -0
  54. package/src/editor/plugins/createWithPortableTextBlockStyle.ts +91 -0
  55. package/src/editor/plugins/createWithPortableTextLists.ts +160 -0
  56. package/src/editor/plugins/createWithPortableTextMarkModel.ts +441 -0
  57. package/src/editor/plugins/createWithPortableTextSelections.ts +65 -0
  58. package/src/editor/plugins/createWithSchemaTypes.ts +76 -0
  59. package/src/editor/plugins/createWithUndoRedo.ts +494 -0
  60. package/src/editor/plugins/createWithUtils.ts +81 -0
  61. package/src/editor/plugins/index.ts +155 -0
  62. package/src/index.ts +11 -0
  63. package/src/patch/PatchEvent.ts +33 -0
  64. package/src/patch/applyPatch.ts +29 -0
  65. package/src/patch/array.ts +89 -0
  66. package/src/patch/arrayInsert.ts +27 -0
  67. package/src/patch/object.ts +39 -0
  68. package/src/patch/patches.ts +53 -0
  69. package/src/patch/primitive.ts +43 -0
  70. package/src/patch/string.ts +51 -0
  71. package/src/types/editor.ts +576 -0
  72. package/src/types/options.ts +17 -0
  73. package/src/types/patch.ts +65 -0
  74. package/src/types/slate.ts +25 -0
  75. package/src/utils/__tests__/dmpToOperations.test.ts +181 -0
  76. package/src/utils/__tests__/operationToPatches.test.ts +421 -0
  77. package/src/utils/__tests__/patchToOperations.test.ts +293 -0
  78. package/src/utils/__tests__/ranges.test.ts +18 -0
  79. package/src/utils/__tests__/valueNormalization.test.tsx +62 -0
  80. package/src/utils/__tests__/values.test.ts +253 -0
  81. package/src/utils/applyPatch.ts +407 -0
  82. package/src/utils/bufferUntil.ts +15 -0
  83. package/src/utils/debug.ts +12 -0
  84. package/src/utils/getPortableTextMemberSchemaTypes.ts +100 -0
  85. package/src/utils/operationToPatches.ts +357 -0
  86. package/src/utils/patches.ts +36 -0
  87. package/src/utils/paths.ts +60 -0
  88. package/src/utils/ranges.ts +77 -0
  89. package/src/utils/schema.ts +8 -0
  90. package/src/utils/selection.ts +65 -0
  91. package/src/utils/ucs2Indices.ts +67 -0
  92. package/src/utils/validateValue.ts +394 -0
  93. package/src/utils/values.ts +208 -0
  94. package/src/utils/weakMaps.ts +24 -0
  95. package/src/utils/withChanges.ts +25 -0
  96. package/src/utils/withPreserveKeys.ts +14 -0
  97. package/src/utils/withoutPatching.ts +14 -0
@@ -0,0 +1,181 @@
1
+ import {describe, expect, test} from '@jest/globals'
2
+ import {makeDiff, makePatches, stringifyPatches} from '@sanity/diff-match-patch'
3
+ import {
4
+ isPortableTextSpan,
5
+ isPortableTextTextBlock,
6
+ type Path,
7
+ type PortableTextBlock,
8
+ type PortableTextSpan,
9
+ type PortableTextTextBlock,
10
+ } from '@sanity/types'
11
+ import {type Descendant, type Operation} from 'slate'
12
+
13
+ import {type PortableTextSlateEditor} from '../../types/editor'
14
+ import {type DiffMatchPatch} from '../../types/patch'
15
+ import {diffMatchPatch} from '../applyPatch'
16
+
17
+ describe('operationToPatches: diffMatchPatch', () => {
18
+ test.todo('skips patches for blocks that cannot be found locally')
19
+ test.todo('skips patches for non-PT-blocks')
20
+ test.todo('skips patches for non-spans')
21
+ test.todo('throws if cannot find span')
22
+
23
+ test('should apply the most basic additive operation correctly', () => {
24
+ const source = 'Hello'
25
+ const target = 'Hello there'
26
+ const patch = getPteDmpPatch(stringifyPatches(makePatches(makeDiff(source, target))))
27
+ const editor = getMockEditor({text: source})
28
+ expect(diffMatchPatch(editor, patch)).toBe(true)
29
+ expect(editor.getText()).toBe(target)
30
+ })
31
+
32
+ test('should apply the most basic removal operation correctly', () => {
33
+ const source = 'Hello there'
34
+ const target = 'Hello'
35
+ const patch = getPteDmpPatch(stringifyPatches(makePatches(makeDiff(source, target))))
36
+ const editor = getMockEditor({text: source})
37
+ expect(diffMatchPatch(editor, patch)).toBe(true)
38
+ expect(editor.getText()).toBe(target)
39
+ })
40
+
41
+ test('should treat equality as noops', () => {
42
+ const source = 'Hello'
43
+ const target = 'Hello'
44
+ const patch = getPteDmpPatch(stringifyPatches(makePatches(makeDiff(source, target))))
45
+ const editor = getMockEditor({text: source})
46
+ expect(diffMatchPatch(editor, patch)).toBe(true)
47
+ expect(editor.getText()).toBe(target)
48
+ })
49
+
50
+ test('should apply combined add + remove operations', () => {
51
+ const source = 'A quick brown fox jumps over the very lazy dog'
52
+ const target = 'The quick brown fox jumps over the lazy dog'
53
+ const patch = getPteDmpPatch(stringifyPatches(makePatches(makeDiff(source, target))))
54
+ const editor = getMockEditor({text: source})
55
+ expect(diffMatchPatch(editor, patch)).toBe(true)
56
+ expect(editor.getText()).toBe(target)
57
+ })
58
+
59
+ test('should apply combined add + remove operations', () => {
60
+ const source = 'Many quick brown fox jumps over the very lazy dog'
61
+ const target = 'The many, quick, brown, foxes jumps over all of the lazy dogs'
62
+ const patch = getPteDmpPatch(stringifyPatches(makePatches(makeDiff(source, target))))
63
+ const editor = getMockEditor({text: source})
64
+ expect(diffMatchPatch(editor, patch)).toBe(true)
65
+ expect(editor.getText()).toBe(target)
66
+ })
67
+
68
+ test('should apply reverse line edits correctly', () => {
69
+ const line1 = 'The quick brown fox jumps over the lazy dog'
70
+ const line2 = 'But the slow green frog jumps over the wild cat'
71
+ const source = [line1, line2, line1, line2].join('\n')
72
+ const target = [line2, line1, line2, line1].join('\n')
73
+ const patch = getPteDmpPatch(stringifyPatches(makePatches(makeDiff(source, target))))
74
+ const editor = getMockEditor({text: source})
75
+ expect(diffMatchPatch(editor, patch)).toBe(true)
76
+ expect(editor.getText()).toBe(target)
77
+ })
78
+
79
+ test('should apply larger text differences correctly', () => {
80
+ const source = `Portable Text is a agnostic abstraction of "rich text" that can be stringified into any markup language, for instance HTML, Markdown, SSML, XML, etc. It's designed to be efficient for collaboration, and makes it possible to enrich rich text with data structures in depth.\n\nPortable Text is built on the idea of rich text as an array of blocks, themselves arrays of children spans. Each block can have a style and a set of mark dfinitions, which describe data structures distributed on the children spans. Portable Text also allows for inserting arbitrary data objects in the array, only requiring _type-key. Portable Text also allows for custom objects in the root array, enabling rendering environments to mix rich text with custom content types.\n\nPortable Text is a combination of arrays and objects. In its simplest form it's an array of objects with an array of children. Some definitions: \n- Block: Typically recognized as a section of a text, e.g. a paragraph or a heading.\n- Span: Piece of text with a set of marks, e.g. bold or italic.\n- Mark: A mark is a data structure that can be appliad to a span, e.g. a link or a comment.\n- Mark definition: A mark definition is a structure that describes a mark, a link or a comment.`
81
+ const target = `Portable Text is an agnostic abstraction of rich text that can be serialized into pretty much any markup language, be it HTML, Markdown, SSML, XML, etc. It is designed to be efficient for real-time collaborative interfaces, and makes it possible to annotate rich text with additional data structures recursively.\n\nPortable Text is built on the idea of rich text as an array of blocks, themselves arrays of child spans. Each block can have a style and a set of mark definitions, which describe data structures that can be applied on the children spans. Portable Text also allows for inserting arbitrary data objects in the array, only requiring _type-key. Portable Text also allows for custom content objects in the root array, enabling editing- and rendering environments to mix rich text with custom content types.\n\nPortable Text is a recursive composition of arrays and objects. In its simplest form it's an array of objects of a type with an array of children. Some definitions: \n- Block: A block is what's typically recognized as a section of a text, e.g. a paragraph or a heading.\n- Span: A span is a piece of text with a set of marks, e.g. bold or italic.\n- Mark: A mark is a data structure that can be applied to a span, e.g. a link or a comment.\n- Mark definition: A mark definition is a data structure that describes a mark, e.g. a link or a comment.`
82
+ const patch = getPteDmpPatch(stringifyPatches(makePatches(makeDiff(source, target))))
83
+ const editor = getMockEditor({text: source})
84
+ expect(diffMatchPatch(editor, patch)).toBe(true)
85
+ expect(editor.getText()).toBe(target)
86
+ })
87
+
88
+ test('should apply offset text differences correctly', () => {
89
+ const source = `This string has changes, but they occur somewhere near the end. That means we need to use an offset to get at the change, we cannot just rely on equality segaments in the generated diff.`
90
+ const target = `This string has changes, but they occur somewhere near the end. That means we need to use an offset to get at the change, we cannot just rely on equality segments in the generated diff.`
91
+ const patch = getPteDmpPatch(stringifyPatches(makePatches(makeDiff(source, target))))
92
+ const editor = getMockEditor({text: source})
93
+ expect(diffMatchPatch(editor, patch)).toBe(true)
94
+ expect(editor.getText()).toBe(target)
95
+ })
96
+ })
97
+
98
+ function getPteDmpPatch(
99
+ value: string,
100
+ path: Path = [{_key: 'bA'}, 'children', {_key: 's1'}, 'text'],
101
+ ): DiffMatchPatch {
102
+ return {
103
+ type: 'diffMatchPatch',
104
+ path,
105
+ origin: 'remote',
106
+ value,
107
+ }
108
+ }
109
+
110
+ type MockEditorOptions = {children: PortableTextTextBlock[]} | {text: string}
111
+
112
+ function getMockEditor(options: MockEditorOptions): Pick<
113
+ PortableTextSlateEditor,
114
+ 'children' | 'isTextBlock' | 'apply' | 'selection' | 'onChange'
115
+ > & {
116
+ getText: () => string
117
+ } {
118
+ let children: PortableTextBlock[] = 'children' in options ? options.children : []
119
+ if (!('children' in options)) {
120
+ children = [
121
+ {
122
+ _type: 'block',
123
+ _key: 'bA',
124
+ children: [{_type: 'span', _key: 's1', text: 'text' in options ? options.text : ''}],
125
+ markDefs: [],
126
+ },
127
+ ]
128
+ }
129
+
130
+ function getText(blockKey?: string) {
131
+ return children
132
+ .filter((child): child is PortableTextTextBlock => isPortableTextTextBlock(child))
133
+ .filter((child) => (blockKey ? child._key === blockKey : true))
134
+ .flatMap((block) =>
135
+ block.children
136
+ .filter((span) => isPortableTextSpan(span))
137
+ .map((span) => span.text)
138
+ .join(''),
139
+ )
140
+ .join('\n\n')
141
+ }
142
+
143
+ function isTextBlock(value: unknown): value is PortableTextTextBlock {
144
+ return isPortableTextTextBlock(value)
145
+ }
146
+
147
+ function apply(operation: Operation): void {
148
+ if (operation.type !== 'insert_text' && operation.type !== 'remove_text') {
149
+ throw new Error(`Unexpected operation type ${operation.type}`)
150
+ }
151
+
152
+ // Forcing for tests, theoretically can target non-PT blocks
153
+ const ptBlocks = children as PortableTextTextBlock<PortableTextSpan>[]
154
+
155
+ const {type, path, offset, text} = operation
156
+ const [blockIndex, spanIndex] = path
157
+ const span = ptBlocks[blockIndex].children[spanIndex]
158
+ const current = span.text
159
+
160
+ if (type === 'insert_text') {
161
+ const before = current.slice(0, offset)
162
+ const after = current.slice(offset)
163
+ span.text = `${before}${text}${after}`
164
+ } else if (type === 'remove_text') {
165
+ const before = current.slice(0, offset)
166
+ const after = current.slice(offset + text.length)
167
+ span.text = `${before}${after}`
168
+ }
169
+ }
170
+
171
+ return {
172
+ selection: null,
173
+ getText,
174
+ children: children as Descendant[],
175
+ apply,
176
+ onChange: () => {
177
+ // NOOP
178
+ },
179
+ isTextBlock,
180
+ }
181
+ }
@@ -0,0 +1,421 @@
1
+ import {beforeEach, describe, expect, it} from '@jest/globals'
2
+ import {type PortableTextTextBlock} from '@sanity/types'
3
+ import {createEditor, type Descendant} from 'slate'
4
+
5
+ import {PortableTextEditor, type PortableTextEditorProps} from '../..'
6
+ import {schemaType} from '../../editor/__tests__/PortableTextEditorTester'
7
+ import {defaultKeyGenerator} from '../../editor/hooks/usePortableTextEditorKeyGenerator'
8
+ import {withPlugins} from '../../editor/plugins'
9
+ import {getPortableTextMemberSchemaTypes} from '../getPortableTextMemberSchemaTypes'
10
+ import {createOperationToPatches} from '../operationToPatches'
11
+
12
+ const portableTextFeatures = getPortableTextMemberSchemaTypes(schemaType)
13
+
14
+ const operationToPatches = createOperationToPatches(portableTextFeatures)
15
+
16
+ const {editor} = withPlugins(createEditor(), {
17
+ portableTextEditor: new PortableTextEditor({schemaType} as PortableTextEditorProps),
18
+ keyGenerator: defaultKeyGenerator,
19
+ readOnly: false,
20
+ })
21
+
22
+ const createDefaultValue = () =>
23
+ [
24
+ {
25
+ _type: 'myTestBlockType',
26
+ _key: '1f2e64b47787',
27
+ style: 'normal',
28
+ markDefs: [],
29
+ children: [
30
+ {_type: 'span', _key: 'c130395c640c', text: '', marks: []},
31
+ {
32
+ _key: '773866318fa8',
33
+ _type: 'someObject',
34
+ value: {title: 'The Object'},
35
+ __inline: true,
36
+ children: [{_type: 'span', _key: 'bogus', text: '', marks: []}],
37
+ },
38
+ {_type: 'span', _key: 'fd9b4a4e6c0b', text: '', marks: []},
39
+ ],
40
+ },
41
+ ] as Descendant[]
42
+
43
+ describe('operationToPatches', () => {
44
+ beforeEach(() => {
45
+ editor.children = createDefaultValue()
46
+ editor.onChange()
47
+ })
48
+
49
+ it('translates void items correctly when splitting spans', () => {
50
+ expect(
51
+ operationToPatches.splitNodePatch(
52
+ editor,
53
+ {
54
+ type: 'split_node',
55
+ path: [0, 0],
56
+ position: 0,
57
+ properties: {_type: 'span', _key: 'c130395c640c', marks: []},
58
+ },
59
+
60
+ createDefaultValue(),
61
+ ),
62
+ ).toMatchInlineSnapshot(`
63
+ Array [
64
+ Object {
65
+ "items": Array [
66
+ Object {
67
+ "_key": "773866318fa8",
68
+ "_type": "someObject",
69
+ "title": "The Object",
70
+ },
71
+ ],
72
+ "path": Array [
73
+ Object {
74
+ "_key": "1f2e64b47787",
75
+ },
76
+ "children",
77
+ Object {
78
+ "_key": "c130395c640c",
79
+ },
80
+ ],
81
+ "position": "after",
82
+ "type": "insert",
83
+ },
84
+ Object {
85
+ "path": Array [
86
+ Object {
87
+ "_key": "1f2e64b47787",
88
+ },
89
+ "children",
90
+ Object {
91
+ "_key": "c130395c640c",
92
+ },
93
+ "text",
94
+ ],
95
+ "type": "set",
96
+ "value": "",
97
+ },
98
+ ]
99
+ `)
100
+ })
101
+
102
+ it('produce correct insert block patch', () => {
103
+ expect(
104
+ operationToPatches.insertNodePatch(
105
+ editor,
106
+ {
107
+ type: 'insert_node',
108
+ path: [0],
109
+ node: {
110
+ _type: 'someObject',
111
+ _key: 'c130395c640c',
112
+ value: {title: 'The Object'},
113
+ __inline: false,
114
+ children: [{_key: '1', _type: 'span', text: '', marks: []}],
115
+ },
116
+ },
117
+ createDefaultValue(),
118
+ ),
119
+ ).toMatchInlineSnapshot(`
120
+ Array [
121
+ Object {
122
+ "items": Array [
123
+ Object {
124
+ "_key": "c130395c640c",
125
+ "_type": "someObject",
126
+ "title": "The Object",
127
+ },
128
+ ],
129
+ "path": Array [
130
+ Object {
131
+ "_key": "1f2e64b47787",
132
+ },
133
+ ],
134
+ "position": "before",
135
+ "type": "insert",
136
+ },
137
+ ]
138
+ `)
139
+ })
140
+
141
+ it('produce correct insert block patch with an empty editor', () => {
142
+ editor.children = []
143
+ editor.onChange()
144
+ expect(
145
+ operationToPatches.insertNodePatch(
146
+ editor,
147
+ {
148
+ type: 'insert_node',
149
+ path: [0],
150
+ node: {
151
+ _type: 'someObject',
152
+ _key: 'c130395c640c',
153
+ value: {},
154
+ __inline: false,
155
+ children: [{_key: '1', _type: 'span', text: '', marks: []}],
156
+ },
157
+ },
158
+
159
+ [],
160
+ ),
161
+ ).toMatchInlineSnapshot(`
162
+ Array [
163
+ Object {
164
+ "path": Array [],
165
+ "type": "setIfMissing",
166
+ "value": Array [],
167
+ },
168
+ Object {
169
+ "items": Array [
170
+ Object {
171
+ "_key": "c130395c640c",
172
+ "_type": "someObject",
173
+ },
174
+ ],
175
+ "path": Array [
176
+ 0,
177
+ ],
178
+ "position": "before",
179
+ "type": "insert",
180
+ },
181
+ ]
182
+ `)
183
+ })
184
+
185
+ it('produce correct insert child patch', () => {
186
+ expect(
187
+ operationToPatches.insertNodePatch(
188
+ editor,
189
+ {
190
+ type: 'insert_node',
191
+ path: [0, 3],
192
+ node: {
193
+ _type: 'someObject',
194
+ _key: 'c130395c640c',
195
+ value: {title: 'The Object'},
196
+ __inline: true,
197
+ children: [{_key: '1', _type: 'span', text: '', marks: []}],
198
+ },
199
+ },
200
+
201
+ createDefaultValue(),
202
+ ),
203
+ ).toMatchInlineSnapshot(`
204
+ Array [
205
+ Object {
206
+ "items": Array [
207
+ Object {
208
+ "_key": "c130395c640c",
209
+ "_type": "someObject",
210
+ "title": "The Object",
211
+ },
212
+ ],
213
+ "path": Array [
214
+ Object {
215
+ "_key": "1f2e64b47787",
216
+ },
217
+ "children",
218
+ Object {
219
+ "_key": "fd9b4a4e6c0b",
220
+ },
221
+ ],
222
+ "position": "after",
223
+ "type": "insert",
224
+ },
225
+ ]
226
+ `)
227
+ })
228
+
229
+ it('produce correct insert text patch', () => {
230
+ ;(editor.children[0] as PortableTextTextBlock).children[2].text = '1'
231
+ editor.onChange()
232
+ expect(
233
+ operationToPatches.insertTextPatch(
234
+ editor,
235
+ {
236
+ type: 'insert_text',
237
+ path: [0, 2],
238
+ text: '1',
239
+ offset: 0,
240
+ },
241
+
242
+ createDefaultValue(),
243
+ ),
244
+ ).toMatchInlineSnapshot(`
245
+ Array [
246
+ Object {
247
+ "path": Array [
248
+ Object {
249
+ "_key": "1f2e64b47787",
250
+ },
251
+ "children",
252
+ Object {
253
+ "_key": "fd9b4a4e6c0b",
254
+ },
255
+ "text",
256
+ ],
257
+ "type": "diffMatchPatch",
258
+ "value": "@@ -0,0 +1 @@
259
+ +1
260
+ ",
261
+ },
262
+ ]
263
+ `)
264
+ })
265
+
266
+ it('produces correct remove text patch', () => {
267
+ const before = createDefaultValue()
268
+ ;(before[0] as PortableTextTextBlock).children[2].text = '1'
269
+ expect(
270
+ operationToPatches.removeTextPatch(
271
+ editor,
272
+ {
273
+ type: 'remove_text',
274
+ path: [0, 2],
275
+ text: '1',
276
+ offset: 1,
277
+ },
278
+
279
+ before,
280
+ ),
281
+ ).toMatchInlineSnapshot(`
282
+ Array [
283
+ Object {
284
+ "path": Array [
285
+ Object {
286
+ "_key": "1f2e64b47787",
287
+ },
288
+ "children",
289
+ Object {
290
+ "_key": "fd9b4a4e6c0b",
291
+ },
292
+ "text",
293
+ ],
294
+ "type": "diffMatchPatch",
295
+ "value": "@@ -1 +0,0 @@
296
+ -1
297
+ ",
298
+ },
299
+ ]
300
+ `)
301
+ })
302
+
303
+ it('produces correct remove child patch', () => {
304
+ expect(
305
+ operationToPatches.removeNodePatch(
306
+ editor,
307
+ {
308
+ type: 'remove_node',
309
+ path: [0, 1],
310
+ node: {
311
+ _key: '773866318fa8',
312
+ _type: 'someObject',
313
+ value: {title: 'The object'},
314
+ __inline: true,
315
+ children: [{_type: 'span', _key: 'bogus', text: '', marks: []}],
316
+ },
317
+ },
318
+
319
+ createDefaultValue(),
320
+ ),
321
+ ).toMatchInlineSnapshot(`
322
+ Array [
323
+ Object {
324
+ "path": Array [
325
+ Object {
326
+ "_key": "1f2e64b47787",
327
+ },
328
+ "children",
329
+ Object {
330
+ "_key": "773866318fa8",
331
+ },
332
+ ],
333
+ "type": "unset",
334
+ },
335
+ ]
336
+ `)
337
+ })
338
+
339
+ it('produce correct remove block patch', () => {
340
+ const val = createDefaultValue()
341
+ expect(
342
+ operationToPatches.removeNodePatch(
343
+ editor,
344
+ {
345
+ type: 'remove_node',
346
+ path: [0],
347
+ node: val[0],
348
+ },
349
+
350
+ val,
351
+ ),
352
+ ).toMatchInlineSnapshot(`
353
+ Array [
354
+ Object {
355
+ "path": Array [
356
+ Object {
357
+ "_key": "1f2e64b47787",
358
+ },
359
+ ],
360
+ "type": "unset",
361
+ },
362
+ ]
363
+ `)
364
+ })
365
+
366
+ it('produce correct merge node patch', () => {
367
+ const val = createDefaultValue()
368
+ ;(val[0] as PortableTextTextBlock).children.push({
369
+ _type: 'span',
370
+ _key: 'r4wr323432',
371
+ text: '1234',
372
+ marks: [],
373
+ })
374
+ const block = editor.children[0] as PortableTextTextBlock
375
+ block.children = block.children.splice(0, 3)
376
+ block.children[2].text = '1234'
377
+ editor.onChange()
378
+ expect(
379
+ operationToPatches.mergeNodePatch(
380
+ editor,
381
+ {
382
+ type: 'merge_node',
383
+ path: [0, 3],
384
+ position: 2,
385
+ properties: {text: '1234'},
386
+ },
387
+
388
+ val,
389
+ ),
390
+ ).toMatchInlineSnapshot(`
391
+ Array [
392
+ Object {
393
+ "path": Array [
394
+ Object {
395
+ "_key": "1f2e64b47787",
396
+ },
397
+ "children",
398
+ Object {
399
+ "_key": "fd9b4a4e6c0b",
400
+ },
401
+ "text",
402
+ ],
403
+ "type": "set",
404
+ "value": "1234",
405
+ },
406
+ Object {
407
+ "path": Array [
408
+ Object {
409
+ "_key": "1f2e64b47787",
410
+ },
411
+ "children",
412
+ Object {
413
+ "_key": "r4wr323432",
414
+ },
415
+ ],
416
+ "type": "unset",
417
+ },
418
+ ]
419
+ `)
420
+ })
421
+ })