@portabletext/editor 1.1.1 → 1.1.2

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 (51) hide show
  1. package/README.md +3 -0
  2. package/lib/index.d.mts +1667 -0
  3. package/lib/index.d.ts +1667 -0
  4. package/lib/index.esm.js +305 -153
  5. package/lib/index.esm.js.map +1 -1
  6. package/lib/index.js +305 -154
  7. package/lib/index.js.map +1 -1
  8. package/lib/index.mjs +305 -153
  9. package/lib/index.mjs.map +1 -1
  10. package/package.json +23 -22
  11. package/src/editor/Editable.tsx +30 -31
  12. package/src/editor/PortableTextEditor.tsx +23 -6
  13. package/src/editor/__tests__/PortableTextEditor.test.tsx +9 -9
  14. package/src/editor/__tests__/PortableTextEditorTester.tsx +2 -5
  15. package/src/editor/__tests__/RangeDecorations.test.tsx +2 -2
  16. package/src/editor/__tests__/handleClick.test.tsx +27 -7
  17. package/src/editor/__tests__/insert-block.test.tsx +4 -4
  18. package/src/editor/__tests__/pteWarningsSelfSolving.test.tsx +7 -7
  19. package/src/editor/__tests__/self-solving.test.tsx +176 -0
  20. package/src/editor/components/Leaf.tsx +28 -23
  21. package/src/editor/components/Synchronizer.tsx +60 -32
  22. package/src/editor/editor-machine.ts +195 -0
  23. package/src/editor/hooks/usePortableTextEditorSelection.tsx +11 -13
  24. package/src/editor/hooks/useSyncValue.test.tsx +9 -9
  25. package/src/editor/hooks/useSyncValue.ts +14 -13
  26. package/src/editor/plugins/__tests__/createWithInsertData.test.tsx +1 -1
  27. package/src/editor/plugins/__tests__/withEditableAPIDelete.test.tsx +28 -28
  28. package/src/editor/plugins/__tests__/withEditableAPIGetFragment.test.tsx +17 -17
  29. package/src/editor/plugins/__tests__/withEditableAPIInsert.test.tsx +8 -8
  30. package/src/editor/plugins/__tests__/withEditableAPISelectionsOverlapping.test.tsx +5 -5
  31. package/src/editor/plugins/__tests__/withPortableTextLists.test.tsx +2 -2
  32. package/src/editor/plugins/__tests__/withPortableTextMarkModel.test.tsx +46 -46
  33. package/src/editor/plugins/__tests__/withPortableTextSelections.test.tsx +22 -11
  34. package/src/editor/plugins/__tests__/withUndoRedo.test.tsx +9 -9
  35. package/src/editor/plugins/createWithInsertData.ts +4 -8
  36. package/src/editor/plugins/createWithObjectKeys.ts +7 -0
  37. package/src/editor/plugins/createWithPatches.ts +5 -6
  38. package/src/editor/plugins/createWithPortableTextBlockStyle.ts +10 -2
  39. package/src/editor/plugins/createWithPortableTextMarkModel.ts +20 -4
  40. package/src/editor/plugins/createWithPortableTextSelections.ts +4 -5
  41. package/src/editor/plugins/createWithSchemaTypes.ts +9 -0
  42. package/src/editor/plugins/index.ts +18 -8
  43. package/src/index.ts +9 -3
  44. package/src/utils/__tests__/dmpToOperations.test.ts +1 -1
  45. package/src/utils/__tests__/operationToPatches.test.ts +61 -61
  46. package/src/utils/__tests__/patchToOperations.test.ts +39 -39
  47. package/src/utils/__tests__/ranges.test.ts +1 -1
  48. package/src/utils/__tests__/valueNormalization.test.tsx +14 -2
  49. package/src/utils/__tests__/values.test.ts +17 -17
  50. package/src/utils/validateValue.ts +0 -22
  51. package/src/editor/__tests__/utils.ts +0 -44
@@ -1,6 +1,6 @@
1
- import {describe, expect, it, jest} from '@jest/globals'
2
1
  import {render, waitFor} from '@testing-library/react'
3
2
  import {createRef, type RefObject} from 'react'
3
+ import {describe, expect, it, vi} from 'vitest'
4
4
  import {
5
5
  PortableTextEditorTester,
6
6
  schemaType,
@@ -27,7 +27,7 @@ const initialValue = [
27
27
  describe('useSyncValue', () => {
28
28
  it('updates span text', async () => {
29
29
  const editorRef: RefObject<PortableTextEditor> = createRef()
30
- const onChange = jest.fn()
30
+ const onChange = vi.fn()
31
31
  const syncedValue = [
32
32
  {
33
33
  _key: '77071c3af231',
@@ -70,7 +70,7 @@ describe('useSyncValue', () => {
70
70
  })
71
71
  it('replaces span nodes with different keys inside the same children array', async () => {
72
72
  const editorRef: RefObject<PortableTextEditor> = createRef()
73
- const onChange = jest.fn()
73
+ const onChange = vi.fn()
74
74
  const syncedValue = [
75
75
  {
76
76
  _key: '77071c3af231',
@@ -107,19 +107,19 @@ describe('useSyncValue', () => {
107
107
  if (editorRef.current) {
108
108
  expect(PortableTextEditor.getValue(editorRef.current))
109
109
  .toMatchInlineSnapshot(`
110
- Array [
111
- Object {
110
+ [
111
+ {
112
112
  "_key": "77071c3af231",
113
113
  "_type": "myTestBlockType",
114
- "children": Array [
115
- Object {
114
+ "children": [
115
+ {
116
116
  "_key": "c001f0e92c1f0__NEW_KEY_YA!",
117
117
  "_type": "span",
118
- "marks": Array [],
118
+ "marks": [],
119
119
  "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. ",
120
120
  },
121
121
  ],
122
- "markDefs": Array [],
122
+ "markDefs": [],
123
123
  "style": "normal",
124
124
  },
125
125
  ]
@@ -3,7 +3,7 @@ import {debounce, isEqual} from 'lodash'
3
3
  import {useCallback, useMemo, useRef} from 'react'
4
4
  import {Editor, Text, Transforms, type Descendant, type Node} from 'slate'
5
5
  import {useSlate} from 'slate-react'
6
- import type {EditorChange, PortableTextSlateEditor} from '../../types/editor'
6
+ import type {PortableTextSlateEditor} from '../../types/editor'
7
7
  import {debugWithName} from '../../utils/debug'
8
8
  import {validateValue} from '../../utils/validateValue'
9
9
  import {toSlateValue, VOID_CHILD_KEY} from '../../utils/values'
@@ -13,6 +13,7 @@ import {
13
13
  withRemoteChanges,
14
14
  } from '../../utils/withChanges'
15
15
  import {withoutPatching} from '../../utils/withoutPatching'
16
+ import type {EditorActor} from '../editor-machine'
16
17
  import {withoutSaving} from '../plugins/createWithUndoRedo'
17
18
  import type {PortableTextEditor} from '../PortableTextEditor'
18
19
 
@@ -22,8 +23,8 @@ const debug = debugWithName('hook:useSyncValue')
22
23
  * @internal
23
24
  */
24
25
  export interface UseSyncValueProps {
26
+ editorActor: EditorActor
25
27
  keyGenerator: () => string
26
- onChange: (change: EditorChange) => void
27
28
  portableTextEditor: PortableTextEditor
28
29
  readOnly: boolean
29
30
  }
@@ -51,8 +52,8 @@ export function useSyncValue(
51
52
  value: PortableTextBlock[] | undefined,
52
53
  userCallbackFn?: () => void,
53
54
  ) => void {
54
- const {portableTextEditor, readOnly, keyGenerator} = props
55
- const {change$, schemaTypes} = portableTextEditor
55
+ const {editorActor, portableTextEditor, readOnly, keyGenerator} = props
56
+ const {schemaTypes} = portableTextEditor
56
57
  const previousValue = useRef<PortableTextBlock[] | undefined>()
57
58
  const slateEditor = useSlate()
58
59
  const updateValueFunctionRef =
@@ -180,7 +181,7 @@ export function useSyncValue(
180
181
  `${validation.resolution.action} for block with _key '${validationValue[0]._key}'. ${validation.resolution?.description}`,
181
182
  )
182
183
  validation.resolution.patches.forEach((patch) => {
183
- change$.next({type: 'patch', patch})
184
+ editorActor.send({type: 'patch', patch})
184
185
  })
185
186
  }
186
187
  }
@@ -208,8 +209,8 @@ export function useSyncValue(
208
209
  }
209
210
  isChanged = true
210
211
  } else {
211
- change$.next({
212
- type: 'invalidValue',
212
+ editorActor.send({
213
+ type: 'invalid value',
213
214
  resolution: validation.resolution,
214
215
  value,
215
216
  })
@@ -237,8 +238,8 @@ export function useSyncValue(
237
238
  })
238
239
  } else {
239
240
  debug('Invalid', validation)
240
- change$.next({
241
- type: 'invalidValue',
241
+ editorActor.send({
242
+ type: 'invalid value',
242
243
  resolution: validation.resolution,
243
244
  value,
244
245
  })
@@ -263,8 +264,8 @@ export function useSyncValue(
263
264
  slateEditor.onChange()
264
265
  } catch (err) {
265
266
  console.error(err)
266
- change$.next({
267
- type: 'invalidValue',
267
+ editorActor.send({
268
+ type: 'invalid value',
268
269
  resolution: null,
269
270
  value,
270
271
  })
@@ -277,7 +278,7 @@ export function useSyncValue(
277
278
  })
278
279
  slateEditor.onChange()
279
280
  }
280
- change$.next({type: 'value', value})
281
+ editorActor.send({type: 'value changed', value})
281
282
  } else {
282
283
  debug('Server value and editor value is equal, no need to sync.')
283
284
  }
@@ -286,7 +287,7 @@ export function useSyncValue(
286
287
  updateValueFunctionRef.current = updateFunction
287
288
  return updateFunction
288
289
  }, [
289
- change$,
290
+ editorActor,
290
291
  keyGenerator,
291
292
  portableTextEditor,
292
293
  readOnly,
@@ -1,6 +1,6 @@
1
- import {describe, expect, it} from '@jest/globals'
2
1
  import {isPortableTextSpan, isPortableTextTextBlock} from '@sanity/types'
3
2
  import type {Descendant} from 'slate'
3
+ import {describe, expect, it} from 'vitest'
4
4
  import {exportedForTesting} from '../createWithInsertData'
5
5
 
6
6
  const initialValue = [
@@ -1,6 +1,6 @@
1
- import {describe, expect, it, jest} from '@jest/globals'
2
1
  import {render, waitFor} from '@testing-library/react'
3
2
  import {createRef, type RefObject} from 'react'
3
+ import {describe, expect, it, vi} from 'vitest'
4
4
  import {
5
5
  PortableTextEditorTester,
6
6
  schemaType,
@@ -46,7 +46,7 @@ const initialSelection = {
46
46
  describe('plugin:withEditableAPI: .delete()', () => {
47
47
  it('deletes block', async () => {
48
48
  const editorRef: RefObject<PortableTextEditor> = createRef()
49
- const onChange = jest.fn()
49
+ const onChange = vi.fn()
50
50
  render(
51
51
  <PortableTextEditorTester
52
52
  onChange={onChange}
@@ -77,19 +77,19 @@ describe('plugin:withEditableAPI: .delete()', () => {
77
77
  )
78
78
  expect(PortableTextEditor.getValue(editorRef.current))
79
79
  .toMatchInlineSnapshot(`
80
- Array [
81
- Object {
80
+ [
81
+ {
82
82
  "_key": "a",
83
83
  "_type": "myTestBlockType",
84
- "children": Array [
85
- Object {
84
+ "children": [
85
+ {
86
86
  "_key": "a1",
87
87
  "_type": "span",
88
- "marks": Array [],
88
+ "marks": [],
89
89
  "text": "Block A",
90
90
  },
91
91
  ],
92
- "markDefs": Array [],
92
+ "markDefs": [],
93
93
  "style": "normal",
94
94
  },
95
95
  ]
@@ -100,7 +100,7 @@ describe('plugin:withEditableAPI: .delete()', () => {
100
100
 
101
101
  it('deletes all the blocks, but leaves a placeholder block', async () => {
102
102
  const editorRef: RefObject<PortableTextEditor> = createRef()
103
- const onChange = jest.fn()
103
+ const onChange = vi.fn()
104
104
  render(
105
105
  <PortableTextEditorTester
106
106
  onChange={onChange}
@@ -135,19 +135,19 @@ describe('plugin:withEditableAPI: .delete()', () => {
135
135
  // New keys here confirms that a placeholder block has been created
136
136
  expect(PortableTextEditor.getValue(editorRef.current))
137
137
  .toMatchInlineSnapshot(`
138
- Array [
139
- Object {
138
+ [
139
+ {
140
140
  "_key": "1",
141
141
  "_type": "myTestBlockType",
142
- "children": Array [
143
- Object {
142
+ "children": [
143
+ {
144
144
  "_key": "2",
145
145
  "_type": "span",
146
- "marks": Array [],
146
+ "marks": [],
147
147
  "text": "",
148
148
  },
149
149
  ],
150
- "markDefs": Array [],
150
+ "markDefs": [],
151
151
  "style": "normal",
152
152
  },
153
153
  ]
@@ -158,7 +158,7 @@ describe('plugin:withEditableAPI: .delete()', () => {
158
158
 
159
159
  it('deletes children', async () => {
160
160
  const editorRef: RefObject<PortableTextEditor> = createRef()
161
- const onChange = jest.fn()
161
+ const onChange = vi.fn()
162
162
 
163
163
  render(
164
164
  <PortableTextEditorTester
@@ -202,33 +202,33 @@ describe('plugin:withEditableAPI: .delete()', () => {
202
202
  if (editorRef.current) {
203
203
  expect(PortableTextEditor.getValue(editorRef.current))
204
204
  .toMatchInlineSnapshot(`
205
- Array [
206
- Object {
205
+ [
206
+ {
207
207
  "_key": "a",
208
208
  "_type": "myTestBlockType",
209
- "children": Array [
210
- Object {
209
+ "children": [
210
+ {
211
211
  "_key": "a1",
212
212
  "_type": "span",
213
- "marks": Array [],
213
+ "marks": [],
214
214
  "text": "Block A",
215
215
  },
216
216
  ],
217
- "markDefs": Array [],
217
+ "markDefs": [],
218
218
  "style": "normal",
219
219
  },
220
- Object {
220
+ {
221
221
  "_key": "b",
222
222
  "_type": "myTestBlockType",
223
- "children": Array [
224
- Object {
223
+ "children": [
224
+ {
225
225
  "_key": "1",
226
226
  "_type": "span",
227
- "marks": Array [],
227
+ "marks": [],
228
228
  "text": "",
229
229
  },
230
230
  ],
231
- "markDefs": Array [],
231
+ "markDefs": [],
232
232
  "style": "normal",
233
233
  },
234
234
  ]
@@ -239,7 +239,7 @@ describe('plugin:withEditableAPI: .delete()', () => {
239
239
 
240
240
  it('deletes selected', async () => {
241
241
  const editorRef: RefObject<PortableTextEditor> = createRef()
242
- const onChange = jest.fn()
242
+ const onChange = vi.fn()
243
243
 
244
244
  render(
245
245
  <PortableTextEditorTester
@@ -1,7 +1,7 @@
1
- import {describe, expect, it, jest} from '@jest/globals'
2
1
  import {isPortableTextTextBlock} from '@sanity/types'
3
2
  import {render, waitFor} from '@testing-library/react'
4
3
  import {createRef, type RefObject} from 'react'
4
+ import {describe, expect, it, vi} from 'vitest'
5
5
  import {
6
6
  PortableTextEditorTester,
7
7
  schemaType,
@@ -52,7 +52,7 @@ const initialValue = [
52
52
  describe('plugin:withEditableAPI: .getFragment()', () => {
53
53
  it('can get a Portable Text fragment of the current selection in a single block', async () => {
54
54
  const editorRef: RefObject<PortableTextEditor> = createRef()
55
- const onChange = jest.fn()
55
+ const onChange = vi.fn()
56
56
 
57
57
  render(
58
58
  <PortableTextEditorTester
@@ -93,7 +93,7 @@ describe('plugin:withEditableAPI: .getFragment()', () => {
93
93
 
94
94
  it('can get a Portable Text fragment of the current selection in multiple blocks', async () => {
95
95
  const editorRef: RefObject<PortableTextEditor> = createRef()
96
- const onChange = jest.fn()
96
+ const onChange = vi.fn()
97
97
 
98
98
  render(
99
99
  <PortableTextEditorTester
@@ -124,43 +124,43 @@ describe('plugin:withEditableAPI: .getFragment()', () => {
124
124
  PortableTextEditor.select(editorRef.current, initialSelection)
125
125
  const fragment = PortableTextEditor.getFragment(editorRef.current)
126
126
  expect(fragment).toMatchInlineSnapshot(`
127
- Array [
128
- Object {
127
+ [
128
+ {
129
129
  "_key": "a",
130
130
  "_type": "myTestBlockType",
131
- "children": Array [
132
- Object {
131
+ "children": [
132
+ {
133
133
  "_key": "a1",
134
134
  "_type": "span",
135
- "marks": Array [],
135
+ "marks": [],
136
136
  "text": "A",
137
137
  },
138
138
  ],
139
- "markDefs": Array [],
139
+ "markDefs": [],
140
140
  "style": "normal",
141
141
  },
142
- Object {
142
+ {
143
143
  "_key": "b",
144
144
  "_type": "myTestBlockType",
145
- "children": Array [
146
- Object {
145
+ "children": [
146
+ {
147
147
  "_key": "b1",
148
148
  "_type": "span",
149
- "marks": Array [],
149
+ "marks": [],
150
150
  "text": "Block B ",
151
151
  },
152
- Object {
152
+ {
153
153
  "_key": "b2",
154
154
  "_type": "someObject",
155
155
  },
156
- Object {
156
+ {
157
157
  "_key": "b3",
158
158
  "_type": "span",
159
- "marks": Array [],
159
+ "marks": [],
160
160
  "text": " contains",
161
161
  },
162
162
  ],
163
- "markDefs": Array [],
163
+ "markDefs": [],
164
164
  "style": "normal",
165
165
  },
166
166
  ]
@@ -1,6 +1,6 @@
1
- import {describe, expect, it, jest} from '@jest/globals'
2
1
  import {render, waitFor} from '@testing-library/react'
3
2
  import {createRef, type RefObject} from 'react'
3
+ import {describe, expect, it, vi} from 'vitest'
4
4
  import {
5
5
  PortableTextEditorTester,
6
6
  schemaType,
@@ -58,7 +58,7 @@ const emptyBlockSelection = {
58
58
  describe('plugin:withEditableAPI: .insertChild()', () => {
59
59
  it('inserts child nodes correctly', async () => {
60
60
  const editorRef: RefObject<PortableTextEditor> = createRef()
61
- const onChange = jest.fn()
61
+ const onChange = vi.fn()
62
62
 
63
63
  render(
64
64
  <PortableTextEditorTester
@@ -200,7 +200,7 @@ describe('plugin:withEditableAPI: .insertBlock()', () => {
200
200
  style: 'normal',
201
201
  },
202
202
  ]
203
- const onChange = jest.fn()
203
+ const onChange = vi.fn()
204
204
 
205
205
  render(
206
206
  <PortableTextEditorTester
@@ -248,7 +248,7 @@ describe('plugin:withEditableAPI: .insertBlock()', () => {
248
248
 
249
249
  it('should not add empty blank blocks: non-empty block', async () => {
250
250
  const editorRef: RefObject<PortableTextEditor> = createRef()
251
- const onChange = jest.fn()
251
+ const onChange = vi.fn()
252
252
 
253
253
  render(
254
254
  <PortableTextEditorTester
@@ -300,7 +300,7 @@ describe('plugin:withEditableAPI: .insertBlock()', () => {
300
300
 
301
301
  it('should be inserted before if focus is on start of block', async () => {
302
302
  const editorRef: RefObject<PortableTextEditor> = createRef()
303
- const onChange = jest.fn()
303
+ const onChange = vi.fn()
304
304
 
305
305
  render(
306
306
  <PortableTextEditorTester
@@ -358,7 +358,7 @@ describe('plugin:withEditableAPI: .insertBlock()', () => {
358
358
  ...initialValue,
359
359
  {_key: 'b', _type: 'someObject', color: 'red'},
360
360
  ]
361
- const onChange = jest.fn()
361
+ const onChange = vi.fn()
362
362
 
363
363
  render(
364
364
  <PortableTextEditorTester
@@ -414,7 +414,7 @@ describe('plugin:withEditableAPI: .insertBlock()', () => {
414
414
  ...initialValue,
415
415
  {_key: 'b', _type: 'someObject', color: 'red'},
416
416
  ]
417
- const onChange = jest.fn()
417
+ const onChange = vi.fn()
418
418
 
419
419
  render(
420
420
  <PortableTextEditorTester
@@ -465,7 +465,7 @@ describe('plugin:withEditableAPI: .insertBlock()', () => {
465
465
  it('should not add empty blank blocks: in new empty text block', async () => {
466
466
  const editorRef: RefObject<PortableTextEditor> = createRef()
467
467
  const value = [...initialValue, ...emptyTextBlock]
468
- const onChange = jest.fn()
468
+ const onChange = vi.fn()
469
469
 
470
470
  render(
471
471
  <PortableTextEditorTester
@@ -1,7 +1,7 @@
1
- import {describe, expect, it, jest} from '@jest/globals'
2
1
  import type {PortableTextBlock} from '@sanity/types'
3
2
  import {render, waitFor} from '@testing-library/react'
4
3
  import {createRef, type RefObject} from 'react'
4
+ import {describe, expect, it, vi} from 'vitest'
5
5
  import {
6
6
  PortableTextEditorTester,
7
7
  schemaType,
@@ -28,7 +28,7 @@ const INITIAL_VALUE: PortableTextBlock[] = [
28
28
  describe('plugin:withEditableAPI: .isSelectionsOverlapping', () => {
29
29
  it('returns true if the selections are partially overlapping', async () => {
30
30
  const editorRef: RefObject<PortableTextEditor> = createRef()
31
- const onChange = jest.fn()
31
+ const onChange = vi.fn()
32
32
  render(
33
33
  <PortableTextEditorTester
34
34
  onChange={onChange}
@@ -62,7 +62,7 @@ describe('plugin:withEditableAPI: .isSelectionsOverlapping', () => {
62
62
 
63
63
  it('returns true if the selections are fully overlapping', async () => {
64
64
  const editorRef: RefObject<PortableTextEditor> = createRef()
65
- const onChange = jest.fn()
65
+ const onChange = vi.fn()
66
66
  render(
67
67
  <PortableTextEditorTester
68
68
  onChange={onChange}
@@ -96,7 +96,7 @@ describe('plugin:withEditableAPI: .isSelectionsOverlapping', () => {
96
96
 
97
97
  it('return true if selection is fully inside another selection', async () => {
98
98
  const editorRef: RefObject<PortableTextEditor> = createRef()
99
- const onChange = jest.fn()
99
+ const onChange = vi.fn()
100
100
  render(
101
101
  <PortableTextEditorTester
102
102
  onChange={onChange}
@@ -130,7 +130,7 @@ describe('plugin:withEditableAPI: .isSelectionsOverlapping', () => {
130
130
 
131
131
  it('returns false if the selections are not overlapping', async () => {
132
132
  const editorRef: RefObject<PortableTextEditor> = createRef()
133
- const onChange = jest.fn()
133
+ const onChange = vi.fn()
134
134
  render(
135
135
  <PortableTextEditorTester
136
136
  onChange={onChange}
@@ -1,6 +1,6 @@
1
- import {describe, expect, it, jest} from '@jest/globals'
2
1
  import {render, waitFor} from '@testing-library/react'
3
2
  import {createRef, type RefObject} from 'react'
3
+ import {describe, expect, it, vi} from 'vitest'
4
4
  import {
5
5
  PortableTextEditorTester,
6
6
  schemaType,
@@ -42,7 +42,7 @@ describe('plugin:withPortableTextLists', () => {
42
42
  style: 'normal',
43
43
  },
44
44
  ]
45
- const onChange = jest.fn()
45
+ const onChange = vi.fn()
46
46
  await waitFor(() => {
47
47
  render(
48
48
  <PortableTextEditorTester