@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,346 @@
1
+ import {describe, expect, it, jest} from '@jest/globals'
2
+ import {render, waitFor} from '@testing-library/react'
3
+ import {createRef, type RefObject} from 'react'
4
+
5
+ import {PortableTextEditorTester, schemaType} from '../../__tests__/PortableTextEditorTester'
6
+ import {PortableTextEditor} from '../../PortableTextEditor'
7
+
8
+ const initialValue = [
9
+ {
10
+ _key: 'a',
11
+ _type: 'myTestBlockType',
12
+ children: [
13
+ {
14
+ _key: 'a1',
15
+ _type: 'span',
16
+ marks: [],
17
+ text: 'Block A',
18
+ },
19
+ ],
20
+ markDefs: [],
21
+ style: 'normal',
22
+ },
23
+ ]
24
+ const initialSelection = {
25
+ focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 7},
26
+ anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 7},
27
+ }
28
+
29
+ const emptyTextBlock = [
30
+ {
31
+ _key: 'emptyBlock',
32
+ _type: 'myTestBlockType',
33
+ children: [
34
+ {
35
+ _key: 'emptySpan',
36
+ _type: 'span',
37
+ marks: [],
38
+ text: '',
39
+ },
40
+ ],
41
+ markDefs: [],
42
+ style: 'normal',
43
+ },
44
+ ]
45
+ const emptyBlockSelection = {
46
+ focus: {path: [{_key: 'emptyBlock'}, 'children', {_key: 'emptySpan'}], offset: 0},
47
+ anchor: {path: [{_key: 'emptyBlock'}, 'children', {_key: 'emptySpan'}], offset: 0},
48
+ }
49
+
50
+ describe('plugin:withEditableAPI: .insertChild()', () => {
51
+ it('inserts child nodes correctly', async () => {
52
+ const editorRef: RefObject<PortableTextEditor> = createRef()
53
+ const onChange = jest.fn()
54
+ render(
55
+ <PortableTextEditorTester
56
+ onChange={onChange}
57
+ ref={editorRef}
58
+ schemaType={schemaType}
59
+ value={initialValue}
60
+ />,
61
+ )
62
+ const editor = editorRef.current
63
+ const inlineType = editor?.schemaTypes.inlineObjects.find((t) => t.name === 'someObject')
64
+ await waitFor(() => {
65
+ if (editor && inlineType) {
66
+ PortableTextEditor.focus(editor)
67
+ PortableTextEditor.select(editor, initialSelection)
68
+ PortableTextEditor.insertChild(editorRef.current, inlineType, {color: 'red'})
69
+ expect(PortableTextEditor.getValue(editorRef.current)).toMatchInlineSnapshot(`
70
+ Array [
71
+ Object {
72
+ "_key": "a",
73
+ "_type": "myTestBlockType",
74
+ "children": Array [
75
+ Object {
76
+ "_key": "a1",
77
+ "_type": "span",
78
+ "marks": Array [],
79
+ "text": "Block A",
80
+ },
81
+ Object {
82
+ "_key": "3",
83
+ "_type": "someObject",
84
+ "color": "red",
85
+ },
86
+ Object {
87
+ "_key": "4",
88
+ "_type": "span",
89
+ "marks": Array [],
90
+ "text": "",
91
+ },
92
+ ],
93
+ "markDefs": Array [],
94
+ "style": "normal",
95
+ },
96
+ ]
97
+ `)
98
+ PortableTextEditor.insertChild(editor, editor.schemaTypes.span, {text: ' '})
99
+ expect(PortableTextEditor.getValue(editor)).toMatchInlineSnapshot(`
100
+ Array [
101
+ Object {
102
+ "_key": "a",
103
+ "_type": "myTestBlockType",
104
+ "children": Array [
105
+ Object {
106
+ "_key": "a1",
107
+ "_type": "span",
108
+ "marks": Array [],
109
+ "text": "Block A",
110
+ },
111
+ Object {
112
+ "_key": "3",
113
+ "_type": "someObject",
114
+ "color": "red",
115
+ },
116
+ Object {
117
+ "_key": "7",
118
+ "_type": "span",
119
+ "marks": Array [],
120
+ "text": " ",
121
+ },
122
+ ],
123
+ "markDefs": Array [],
124
+ "style": "normal",
125
+ },
126
+ ]
127
+ `)
128
+ const sel = PortableTextEditor.getSelection(editor)
129
+ expect(sel).toMatchInlineSnapshot(`
130
+ Object {
131
+ "anchor": Object {
132
+ "offset": 1,
133
+ "path": Array [
134
+ Object {
135
+ "_key": "a",
136
+ },
137
+ "children",
138
+ Object {
139
+ "_key": "7",
140
+ },
141
+ ],
142
+ },
143
+ "backward": false,
144
+ "focus": Object {
145
+ "offset": 1,
146
+ "path": Array [
147
+ Object {
148
+ "_key": "a",
149
+ },
150
+ "children",
151
+ Object {
152
+ "_key": "7",
153
+ },
154
+ ],
155
+ },
156
+ }
157
+ `)
158
+ }
159
+ })
160
+ })
161
+ })
162
+
163
+ describe('plugin:withEditableAPI: .insertBlock()', () => {
164
+ it('should not add empty blank blocks: empty block', async () => {
165
+ const editorRef: RefObject<PortableTextEditor> = createRef()
166
+ const onChange = jest.fn()
167
+ render(
168
+ <PortableTextEditorTester
169
+ onChange={onChange}
170
+ ref={editorRef}
171
+ schemaType={schemaType}
172
+ value={emptyTextBlock}
173
+ />,
174
+ )
175
+ const editor = editorRef.current
176
+ const someObject = editor?.schemaTypes.inlineObjects.find((t) => t.name === 'someObject')
177
+
178
+ await waitFor(() => {
179
+ if (editorRef.current && someObject) {
180
+ PortableTextEditor.focus(editorRef.current)
181
+ PortableTextEditor.select(editorRef.current, emptyBlockSelection)
182
+ PortableTextEditor.insertBlock(editorRef.current, someObject, {color: 'red'})
183
+
184
+ expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
185
+ {_key: '2', _type: 'someObject', color: 'red'},
186
+ ])
187
+ } else {
188
+ throw new Error('No editor or someObject')
189
+ }
190
+ })
191
+ })
192
+
193
+ it('should not add empty blank blocks: non-empty block', async () => {
194
+ const editorRef: RefObject<PortableTextEditor> = createRef()
195
+ const onChange = jest.fn()
196
+ render(
197
+ <PortableTextEditorTester
198
+ onChange={onChange}
199
+ ref={editorRef}
200
+ schemaType={schemaType}
201
+ value={initialValue}
202
+ />,
203
+ )
204
+ const editor = editorRef.current
205
+ const someObject = editor?.schemaTypes.inlineObjects.find((t) => t.name === 'someObject')
206
+
207
+ await waitFor(() => {
208
+ if (editorRef.current && someObject) {
209
+ PortableTextEditor.focus(editorRef.current)
210
+ PortableTextEditor.select(editorRef.current, initialSelection)
211
+ PortableTextEditor.insertBlock(editorRef.current, someObject, {color: 'red'})
212
+ expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
213
+ ...initialValue,
214
+ {_key: '2', _type: 'someObject', color: 'red'},
215
+ ])
216
+ } else {
217
+ throw new Error('No editor or someObject')
218
+ }
219
+ })
220
+ })
221
+ it('should be inserted before if focus is on start of block', async () => {
222
+ const editorRef: RefObject<PortableTextEditor> = createRef()
223
+ const onChange = jest.fn()
224
+ render(
225
+ <PortableTextEditorTester
226
+ onChange={onChange}
227
+ ref={editorRef}
228
+ schemaType={schemaType}
229
+ value={initialValue}
230
+ />,
231
+ )
232
+ const editor = editorRef.current
233
+ const someObject = editor?.schemaTypes.inlineObjects.find((t) => t.name === 'someObject')
234
+
235
+ await waitFor(() => {
236
+ if (editorRef.current && someObject) {
237
+ PortableTextEditor.focus(editorRef.current)
238
+ PortableTextEditor.select(editorRef.current, {
239
+ focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 0},
240
+ anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 0},
241
+ })
242
+ PortableTextEditor.insertBlock(editorRef.current, someObject, {color: 'red'})
243
+ expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
244
+ {_key: '2', _type: 'someObject', color: 'red'},
245
+ ...initialValue,
246
+ ])
247
+ } else {
248
+ throw new Error('No editor or someObject')
249
+ }
250
+ })
251
+ })
252
+ it('should not add empty blank blocks: non text block', async () => {
253
+ const editorRef: RefObject<PortableTextEditor> = createRef()
254
+ const onChange = jest.fn()
255
+ const value = [...initialValue, {_key: 'b', _type: 'someObject', color: 'red'}]
256
+ render(
257
+ <PortableTextEditorTester
258
+ onChange={onChange}
259
+ ref={editorRef}
260
+ schemaType={schemaType}
261
+ value={value}
262
+ />,
263
+ )
264
+ const editor = editorRef.current
265
+ const someObject = editor?.schemaTypes.inlineObjects.find((t) => t.name === 'someObject')
266
+
267
+ await waitFor(() => {
268
+ if (editorRef.current && someObject) {
269
+ PortableTextEditor.focus(editorRef.current)
270
+ // Focus the `someObject` block
271
+ PortableTextEditor.select(editorRef.current, {
272
+ focus: {path: [{_key: 'b'}], offset: 0},
273
+ anchor: {path: [{_key: 'b'}], offset: 0},
274
+ })
275
+ PortableTextEditor.insertBlock(editorRef.current, someObject, {color: 'yellow'})
276
+ expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
277
+ ...value,
278
+ {_key: '2', _type: 'someObject', color: 'yellow'},
279
+ ])
280
+ } else {
281
+ throw new Error('No editor or someObject')
282
+ }
283
+ })
284
+ })
285
+ it('should not add empty blank blocks: in between blocks', async () => {
286
+ const editorRef: RefObject<PortableTextEditor> = createRef()
287
+ const onChange = jest.fn()
288
+ const value = [...initialValue, {_key: 'b', _type: 'someObject', color: 'red'}]
289
+ render(
290
+ <PortableTextEditorTester
291
+ onChange={onChange}
292
+ ref={editorRef}
293
+ schemaType={schemaType}
294
+ value={value}
295
+ />,
296
+ )
297
+ const editor = editorRef.current
298
+ const someObject = editor?.schemaTypes.inlineObjects.find((t) => t.name === 'someObject')
299
+
300
+ await waitFor(() => {
301
+ if (editorRef.current && someObject) {
302
+ PortableTextEditor.focus(editorRef.current)
303
+ // Focus the `text` block
304
+ PortableTextEditor.select(editorRef.current, initialSelection)
305
+ PortableTextEditor.insertBlock(editorRef.current, someObject, {color: 'yellow'})
306
+ expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
307
+ value[0],
308
+ {_key: '2', _type: 'someObject', color: 'yellow'},
309
+ value[1],
310
+ ])
311
+ } else {
312
+ throw new Error('No editor or someObject')
313
+ }
314
+ })
315
+ })
316
+ it('should not add empty blank blocks: in new empty text block', async () => {
317
+ const editorRef: RefObject<PortableTextEditor> = createRef()
318
+ const onChange = jest.fn()
319
+ const value = [...initialValue, ...emptyTextBlock]
320
+ render(
321
+ <PortableTextEditorTester
322
+ onChange={onChange}
323
+ ref={editorRef}
324
+ schemaType={schemaType}
325
+ value={value}
326
+ />,
327
+ )
328
+ const editor = editorRef.current
329
+ const someObject = editor?.schemaTypes.inlineObjects.find((t) => t.name === 'someObject')
330
+
331
+ await waitFor(() => {
332
+ if (editorRef.current && someObject) {
333
+ PortableTextEditor.focus(editorRef.current)
334
+ // Focus the empty `text` block
335
+ PortableTextEditor.select(editorRef.current, emptyBlockSelection)
336
+ PortableTextEditor.insertBlock(editorRef.current, someObject, {color: 'yellow'})
337
+ expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
338
+ value[0],
339
+ {_key: '2', _type: 'someObject', color: 'yellow'},
340
+ ])
341
+ } else {
342
+ throw new Error('No editor or someObject')
343
+ }
344
+ })
345
+ })
346
+ })
@@ -0,0 +1,162 @@
1
+ import {describe, expect, it, jest} from '@jest/globals'
2
+ import {type PortableTextBlock} from '@sanity/types'
3
+ import {render, waitFor} from '@testing-library/react'
4
+ import {createRef, type RefObject} from 'react'
5
+
6
+ import {PortableTextEditorTester, schemaType} from '../../__tests__/PortableTextEditorTester'
7
+ import {PortableTextEditor} from '../../PortableTextEditor'
8
+
9
+ const INITIAL_VALUE: PortableTextBlock[] = [
10
+ {
11
+ _key: 'a',
12
+ _type: 'myTestBlockType',
13
+ children: [
14
+ {
15
+ _key: 'a1',
16
+ _type: 'span',
17
+ marks: [],
18
+ text: 'This is some text in the block',
19
+ },
20
+ ],
21
+ markDefs: [],
22
+ style: 'normal',
23
+ },
24
+ ]
25
+
26
+ describe('plugin:withEditableAPI: .isSelectionsOverlapping', () => {
27
+ it('returns true if the selections are partially overlapping', async () => {
28
+ const editorRef: RefObject<PortableTextEditor> = createRef()
29
+ const onChange = jest.fn()
30
+ render(
31
+ <PortableTextEditorTester
32
+ onChange={onChange}
33
+ ref={editorRef}
34
+ schemaType={schemaType}
35
+ value={INITIAL_VALUE}
36
+ />,
37
+ )
38
+ const selectionA = {
39
+ focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 4},
40
+ anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 8},
41
+ }
42
+
43
+ const selectionB = {
44
+ focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 2},
45
+ anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 6},
46
+ }
47
+
48
+ await waitFor(() => {
49
+ if (editorRef.current) {
50
+ const isOverlapping = PortableTextEditor.isSelectionsOverlapping(
51
+ editorRef.current,
52
+ selectionA,
53
+ selectionB,
54
+ )
55
+
56
+ expect(isOverlapping).toBe(true)
57
+ }
58
+ })
59
+ })
60
+
61
+ it('returns true if the selections are fully overlapping', async () => {
62
+ const editorRef: RefObject<PortableTextEditor> = createRef()
63
+ const onChange = jest.fn()
64
+ render(
65
+ <PortableTextEditorTester
66
+ onChange={onChange}
67
+ ref={editorRef}
68
+ schemaType={schemaType}
69
+ value={INITIAL_VALUE}
70
+ />,
71
+ )
72
+ const selectionA = {
73
+ focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 4},
74
+ anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 8},
75
+ }
76
+
77
+ const selectionB = {
78
+ focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 4},
79
+ anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 8},
80
+ }
81
+
82
+ await waitFor(() => {
83
+ if (editorRef.current) {
84
+ const isOverlapping = PortableTextEditor.isSelectionsOverlapping(
85
+ editorRef.current,
86
+ selectionA,
87
+ selectionB,
88
+ )
89
+
90
+ expect(isOverlapping).toBe(true)
91
+ }
92
+ })
93
+ })
94
+
95
+ it('return true if selection is fully inside another selection', async () => {
96
+ const editorRef: RefObject<PortableTextEditor> = createRef()
97
+ const onChange = jest.fn()
98
+ render(
99
+ <PortableTextEditorTester
100
+ onChange={onChange}
101
+ ref={editorRef}
102
+ schemaType={schemaType}
103
+ value={INITIAL_VALUE}
104
+ />,
105
+ )
106
+ const selectionA = {
107
+ focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 2},
108
+ anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 10},
109
+ }
110
+
111
+ const selectionB = {
112
+ focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 4},
113
+ anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 6},
114
+ }
115
+
116
+ await waitFor(() => {
117
+ if (editorRef.current) {
118
+ const isOverlapping = PortableTextEditor.isSelectionsOverlapping(
119
+ editorRef.current,
120
+ selectionA,
121
+ selectionB,
122
+ )
123
+
124
+ expect(isOverlapping).toBe(true)
125
+ }
126
+ })
127
+ })
128
+
129
+ it('returns false if the selections are not overlapping', async () => {
130
+ const editorRef: RefObject<PortableTextEditor> = createRef()
131
+ const onChange = jest.fn()
132
+ render(
133
+ <PortableTextEditorTester
134
+ onChange={onChange}
135
+ ref={editorRef}
136
+ schemaType={schemaType}
137
+ value={INITIAL_VALUE}
138
+ />,
139
+ )
140
+ const selectionA = {
141
+ focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 4},
142
+ anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 8},
143
+ }
144
+
145
+ const selectionB = {
146
+ focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 10},
147
+ anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 12},
148
+ }
149
+
150
+ await waitFor(() => {
151
+ if (editorRef.current) {
152
+ const isOverlapping = PortableTextEditor.isSelectionsOverlapping(
153
+ editorRef.current,
154
+ selectionA,
155
+ selectionB,
156
+ )
157
+
158
+ expect(isOverlapping).toBe(false)
159
+ }
160
+ })
161
+ })
162
+ })