@portabletext/editor 1.1.1 → 1.1.3

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 (53) hide show
  1. package/README.md +3 -0
  2. package/lib/index.d.mts +1668 -1
  3. package/lib/index.d.ts +1668 -1
  4. package/lib/index.esm.js +320 -172
  5. package/lib/index.esm.js.map +1 -1
  6. package/lib/index.js +320 -173
  7. package/lib/index.js.map +1 -1
  8. package/lib/index.mjs +320 -172
  9. package/lib/index.mjs.map +1 -1
  10. package/package.json +23 -23
  11. package/src/editor/Editable.tsx +32 -34
  12. package/src/editor/PortableTextEditor.tsx +23 -7
  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 +12 -14
  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/createWithEditableAPI.ts +5 -7
  36. package/src/editor/plugins/createWithInsertData.ts +4 -9
  37. package/src/editor/plugins/createWithObjectKeys.ts +7 -0
  38. package/src/editor/plugins/createWithPatches.ts +5 -6
  39. package/src/editor/plugins/createWithPortableTextBlockStyle.ts +10 -2
  40. package/src/editor/plugins/createWithPortableTextMarkModel.ts +40 -36
  41. package/src/editor/plugins/createWithPortableTextSelections.ts +4 -5
  42. package/src/editor/plugins/createWithSchemaTypes.ts +9 -0
  43. package/src/editor/plugins/index.ts +18 -8
  44. package/src/index.ts +9 -3
  45. package/src/utils/__tests__/dmpToOperations.test.ts +1 -1
  46. package/src/utils/__tests__/operationToPatches.test.ts +61 -61
  47. package/src/utils/__tests__/patchToOperations.test.ts +39 -39
  48. package/src/utils/__tests__/ranges.test.ts +1 -1
  49. package/src/utils/__tests__/valueNormalization.test.tsx +13 -2
  50. package/src/utils/__tests__/values.test.ts +17 -17
  51. package/src/utils/applyPatch.ts +4 -10
  52. package/src/utils/validateValue.ts +0 -22
  53. package/src/editor/__tests__/utils.ts +0 -44
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/editor",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -47,29 +47,29 @@
47
47
  "is-hotkey-esm": "^1.0.0",
48
48
  "lodash": "^4.17.21",
49
49
  "slate": "0.103.0",
50
- "slate-react": "0.108.0"
50
+ "slate-react": "0.110.1",
51
+ "xstate": "^5.18.2"
51
52
  },
52
53
  "devDependencies": {
53
- "@cucumber/cucumber-expressions": "^17.1.0",
54
- "@cucumber/gherkin": "^29.0.0",
55
- "@cucumber/messages": "^26.0.0",
54
+ "@babel/preset-env": "^7.25.4",
55
+ "@babel/preset-react": "^7.24.7",
56
56
  "@jest/globals": "^29.7.0",
57
57
  "@jest/types": "^29.6.3",
58
- "@playwright/test": "1.46.1",
58
+ "@playwright/test": "1.47.2",
59
59
  "@portabletext/toolkit": "^2.0.15",
60
60
  "@sanity/block-tools": "^3.55.0",
61
61
  "@sanity/diff-match-patch": "^3.1.1",
62
- "@sanity/pkg-utils": "^6.10.10",
62
+ "@sanity/pkg-utils": "^6.11.2",
63
63
  "@sanity/schema": "^3.55.0",
64
- "@sanity/test": "0.0.1-alpha.1",
65
64
  "@sanity/types": "^3.55.0",
66
- "@sanity/ui": "^2.8.8",
65
+ "@sanity/ui": "^2.8.9",
67
66
  "@sanity/util": "^3.55.0",
68
67
  "@testing-library/dom": "^10.4.0",
69
- "@testing-library/react": "^16.0.0",
68
+ "@testing-library/jest-dom": "^6.5.0",
69
+ "@testing-library/react": "^16.0.1",
70
70
  "@types/debug": "^4.1.5",
71
71
  "@types/express": "^4.17.21",
72
- "@types/express-ws": "^3.0.4",
72
+ "@types/express-ws": "^3.0.5",
73
73
  "@types/lodash": "^4.17.7",
74
74
  "@types/node": "^18.19.8",
75
75
  "@types/node-ipc": "^9.2.3",
@@ -81,18 +81,19 @@
81
81
  "express": "^4.19.2",
82
82
  "express-ws": "^5.0.2",
83
83
  "jest": "^29.7.0",
84
- "jest-dev-server": "^10.1.0",
85
- "jest-environment-jsdom": "^29.7.0",
84
+ "jest-dev-server": "^10.1.1",
86
85
  "jest-environment-node": "^29.7.0",
86
+ "jsdom": "^25.0.1",
87
87
  "node-ipc": "npm:@node-ipc/compat@9.2.5",
88
88
  "react": "^18.3.1",
89
89
  "react-dom": "^18.3.1",
90
90
  "rxjs": "^7.8.1",
91
- "styled-components": "^6.1.12",
91
+ "styled-components": "^6.1.13",
92
92
  "ts-node": "^10.9.2",
93
- "tsx": "^4.17.0",
94
- "typescript": "5.5.4",
95
- "vite": "^5.4.2"
93
+ "typescript": "5.6.2",
94
+ "vite": "^5.4.2",
95
+ "vitest": "^2.1.1",
96
+ "@sanity/gherkin-driver": "^0.0.1"
96
97
  },
97
98
  "peerDependencies": {
98
99
  "@sanity/block-tools": "^3.47.1",
@@ -100,8 +101,8 @@
100
101
  "@sanity/types": "^3.47.1",
101
102
  "@sanity/util": "^3.47.1",
102
103
  "react": "^16.9 || ^17 || ^18",
103
- "rxjs": "^7",
104
- "styled-components": "^6.1"
104
+ "rxjs": "^7.8.1",
105
+ "styled-components": "^6.1.13"
105
106
  },
106
107
  "engines": {
107
108
  "node": ">=18"
@@ -115,11 +116,10 @@
115
116
  "check:types": "tsc",
116
117
  "clean": "del .turbo && del lib && del node_modules",
117
118
  "dev": "pkg-utils watch",
118
- "dev:e2e-server": "cd ./e2e-tests/ && tsx serve",
119
119
  "lint:fix": "biome lint --write .",
120
- "test": "jest",
120
+ "test": "vitest --run",
121
+ "test:watch": "vitest",
121
122
  "test:e2e": "jest --config=e2e-tests/e2e.config.ts",
122
- "test:e2e:watch": "jest --config=e2e-tests/e2e.config.ts --watch",
123
- "test:watch": "jest --watch"
123
+ "test:e2e:watch": "jest --config=e2e-tests/e2e.config.ts --watch"
124
124
  }
125
125
  }
@@ -36,7 +36,6 @@ import {
36
36
  type RenderLeafProps,
37
37
  } from 'slate-react'
38
38
  import type {
39
- EditorChange,
40
39
  EditorSelection,
41
40
  OnCopyFn,
42
41
  OnPasteFn,
@@ -161,15 +160,15 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
161
160
 
162
161
  const rangeDecorationsRef = useRef(rangeDecorations)
163
162
 
164
- const {change$, schemaTypes} = portableTextEditor
163
+ const {editorActor, schemaTypes} = portableTextEditor
165
164
  const slateEditor = useSlate()
166
165
 
167
166
  const blockTypeName = schemaTypes.block.name
168
167
 
169
168
  // React/UI-specific plugins
170
169
  const withInsertData = useMemo(
171
- () => createWithInsertData(change$, schemaTypes, keyGenerator),
172
- [change$, keyGenerator, schemaTypes],
170
+ () => createWithInsertData(editorActor, schemaTypes, keyGenerator),
171
+ [editorActor, keyGenerator, schemaTypes],
173
172
  )
174
173
  const withHotKeys = useMemo(
175
174
  () => createWithHotkeys(schemaTypes, portableTextEditor, hotkeys),
@@ -278,13 +277,16 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
278
277
  // Output selection here in those cases where the editor selection was the same, and there are no set_selection operations made.
279
278
  // The selection is usually automatically emitted to change$ by the withPortableTextSelections plugin whenever there is a set_selection operation applied.
280
279
  if (!slateEditor.operations.some((o) => o.type === 'set_selection')) {
281
- change$.next({type: 'selection', selection: normalizedSelection})
280
+ editorActor.send({
281
+ type: 'selection',
282
+ selection: normalizedSelection,
283
+ })
282
284
  }
283
285
  slateEditor.onChange()
284
286
  }
285
287
  }
286
288
  }
287
- }, [propsSelection, slateEditor, blockTypeName, change$])
289
+ }, [editorActor, propsSelection, slateEditor])
288
290
 
289
291
  const syncRangeDecorations = useCallback(
290
292
  (operation?: Operation) => {
@@ -348,28 +350,24 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
348
350
  [portableTextEditor, rangeDecorations, schemaTypes, slateEditor],
349
351
  )
350
352
 
351
- // Subscribe to change$ and restore selection from props when the editor has been initialized properly with it's value
353
+ // Restore selection from props when the editor has been initialized properly with it's value
352
354
  useEffect(() => {
353
- // debug('Subscribing to editor changes$')
354
- const sub = change$.subscribe((next: EditorChange): void => {
355
- switch (next.type) {
356
- case 'ready':
357
- restoreSelectionFromProps()
358
- break
359
- case 'invalidValue':
360
- setHasInvalidValue(true)
361
- break
362
- case 'value':
363
- setHasInvalidValue(false)
364
- break
365
- default:
366
- }
355
+ const onReady = editorActor.on('ready', () => {
356
+ restoreSelectionFromProps()
367
357
  })
358
+ const onInvalidValue = editorActor.on('invalid value', () => {
359
+ setHasInvalidValue(true)
360
+ })
361
+ const onValueChanged = editorActor.on('value changed', () => {
362
+ setHasInvalidValue(false)
363
+ })
364
+
368
365
  return () => {
369
- // debug('Unsubscribing to changes$')
370
- sub.unsubscribe()
366
+ onReady.unsubscribe()
367
+ onInvalidValue.unsubscribe()
368
+ onValueChanged.unsubscribe()
371
369
  }
372
- }, [change$, restoreSelectionFromProps])
370
+ }, [editorActor, restoreSelectionFromProps])
373
371
 
374
372
  // Restore selection from props when it changes
375
373
  useEffect(() => {
@@ -451,7 +449,7 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
451
449
  slateEditor.insertData(event.clipboardData)
452
450
  } else {
453
451
  // Resolve it as promise (can be either async promise or sync return value)
454
- change$.next({type: 'loading', isLoading: true})
452
+ editorActor.send({type: 'loading'})
455
453
  Promise.resolve(onPasteResult)
456
454
  .then((result) => {
457
455
  debug('Custom paste function from client resolved', result)
@@ -476,11 +474,11 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
476
474
  return error
477
475
  })
478
476
  .finally(() => {
479
- change$.next({type: 'loading', isLoading: false})
477
+ editorActor.send({type: 'done loading'})
480
478
  })
481
479
  }
482
480
  },
483
- [change$, onPaste, portableTextEditor, schemaTypes, slateEditor],
481
+ [onPaste, portableTextEditor, schemaTypes, slateEditor],
484
482
  )
485
483
 
486
484
  const handleOnFocus: FocusEventHandler<HTMLDivElement> = useCallback(
@@ -495,18 +493,18 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
495
493
  Transforms.select(slateEditor, Editor.start(slateEditor, []))
496
494
  slateEditor.onChange()
497
495
  }
498
- change$.next({type: 'focus', event})
496
+ editorActor.send({type: 'focus', event})
499
497
  const newSelection = PortableTextEditor.getSelection(portableTextEditor)
500
498
  // If the selection is the same, emit it explicitly here as there is no actual onChange event triggered.
501
499
  if (selection === newSelection) {
502
- change$.next({
500
+ editorActor.send({
503
501
  type: 'selection',
504
502
  selection,
505
503
  })
506
504
  }
507
505
  }
508
506
  },
509
- [onFocus, portableTextEditor, change$, slateEditor],
507
+ [editorActor, onFocus, portableTextEditor, slateEditor],
510
508
  )
511
509
 
512
510
  const handleClick = useCallback(
@@ -542,10 +540,10 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
542
540
  onBlur(event)
543
541
  }
544
542
  if (!event.isPropagationStopped()) {
545
- change$.next({type: 'blur', event})
543
+ editorActor.send({type: 'blur', event})
546
544
  }
547
545
  },
548
- [change$, onBlur],
546
+ [editorActor, onBlur],
549
547
  )
550
548
 
551
549
  const handleOnBeforeInput = useCallback(
@@ -604,7 +602,7 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
604
602
  // Set the correct range
605
603
  domSelection.addRange(newDOMRange)
606
604
  }
607
- } catch (error) {
605
+ } catch {
608
606
  debug(`Could not resolve selection, selecting top document`)
609
607
  // Deselect the editor
610
608
  Transforms.deselect(slateEditor)
@@ -657,7 +655,7 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
657
655
  return noop
658
656
  }
659
657
  // Translate PortableTextEditor prop fn to Slate plugin fn
660
- return (editor: ReactEditor, domRange: Range) => {
658
+ return (_editor: ReactEditor, domRange: Range) => {
661
659
  scrollSelectionIntoView(portableTextEditor, domRange)
662
660
  }
663
661
  }, [portableTextEditor, scrollSelectionIntoView])
@@ -11,6 +11,7 @@ import type {
11
11
  } from '@sanity/types'
12
12
  import {Component, type MutableRefObject, type PropsWithChildren} from 'react'
13
13
  import {Subject} from 'rxjs'
14
+ import {createActor} from 'xstate'
14
15
  import type {
15
16
  EditableAPI,
16
17
  EditableAPIDeleteOptions,
@@ -25,6 +26,7 @@ import {getPortableTextMemberSchemaTypes} from '../utils/getPortableTextMemberSc
25
26
  import {compileType} from '../utils/schema'
26
27
  import {SlateContainer} from './components/SlateContainer'
27
28
  import {Synchronizer} from './components/Synchronizer'
29
+ import {editorMachine, type EditorActor} from './editor-machine'
28
30
  import {PortableTextEditorContext} from './hooks/usePortableTextEditor'
29
31
  import {
30
32
  defaultKeyGenerator,
@@ -92,6 +94,11 @@ export type PortableTextEditorProps = PropsWithChildren<{
92
94
  * @public
93
95
  */
94
96
  export class PortableTextEditor extends Component<PortableTextEditorProps> {
97
+ /**
98
+ * @internal
99
+ * Don't use this API directly. It's subject to change.
100
+ */
101
+ public editorActor: EditorActor
95
102
  /**
96
103
  * An observable of all the editor changes.
97
104
  */
@@ -118,7 +125,8 @@ export class PortableTextEditor extends Component<PortableTextEditorProps> {
118
125
  )
119
126
  }
120
127
 
121
- this.change$.next({type: 'loading', isLoading: true})
128
+ this.editorActor = createActor(editorMachine)
129
+ this.editorActor.start()
122
130
 
123
131
  this.schemaTypes = getPortableTextMemberSchemaTypes(
124
132
  props.schemaType.hasOwnProperty('jsonType')
@@ -154,8 +162,7 @@ export class PortableTextEditor extends Component<PortableTextEditorProps> {
154
162
  }
155
163
 
156
164
  render() {
157
- const {onChange, value, children, patches$, incomingPatches$} = this.props
158
- const {change$} = this
165
+ const {value, children, patches$, incomingPatches$} = this.props
159
166
  const _patches$ = incomingPatches$ || patches$ // Backward compatibility
160
167
 
161
168
  const maxBlocks =
@@ -176,11 +183,20 @@ export class PortableTextEditor extends Component<PortableTextEditorProps> {
176
183
  <PortableTextEditorKeyGeneratorContext.Provider value={keyGenerator}>
177
184
  <PortableTextEditorContext.Provider value={this}>
178
185
  <PortableTextEditorReadOnlyContext.Provider value={readOnly}>
179
- <PortableTextEditorSelectionProvider change$={change$}>
186
+ <PortableTextEditorSelectionProvider
187
+ editorActor={this.editorActor}
188
+ >
180
189
  <Synchronizer
181
- change$={change$}
190
+ editorActor={this.editorActor}
182
191
  getValue={this.getValue}
183
- onChange={onChange}
192
+ onChange={(change) => {
193
+ this.props.onChange(change)
194
+ /**
195
+ * For backwards compatibility, we relay all changes to the
196
+ * `change$` Subject as well.
197
+ */
198
+ this.change$.next(change)
199
+ }}
184
200
  value={value}
185
201
  />
186
202
  {children}
@@ -298,7 +314,7 @@ export class PortableTextEditor extends Component<PortableTextEditorProps> {
298
314
  ) => {
299
315
  return editor.editable?.isVoid(element)
300
316
  }
301
- static isObjectPath = (editor: PortableTextEditor, path: Path): boolean => {
317
+ static isObjectPath = (_editor: PortableTextEditor, path: Path): boolean => {
302
318
  if (!path || !Array.isArray(path)) return false
303
319
  const isChildObjectEditPath = path.length > 3 && path[1] === 'children'
304
320
  const isBlockObjectEditPath = path.length > 1 && path[1] !== 'children'
@@ -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 type {EditorSelection} from '../..'
6
6
  import {PortableTextEditor} from '../PortableTextEditor'
7
7
  import {PortableTextEditorTester, schemaType} from './PortableTextEditorTester'
@@ -18,7 +18,7 @@ const renderPlaceholder = () => 'Jot something down here'
18
18
  describe('initialization', () => {
19
19
  it('receives initial onChange events and has custom placeholder', async () => {
20
20
  const editorRef: RefObject<PortableTextEditor> = createRef()
21
- const onChange = jest.fn()
21
+ const onChange = vi.fn()
22
22
  const {container} = render(
23
23
  <PortableTextEditorTester
24
24
  onChange={onChange}
@@ -88,7 +88,7 @@ describe('initialization', () => {
88
88
  })
89
89
  it('takes value from props and confirms it by emitting value change event', async () => {
90
90
  const initialValue = [helloBlock]
91
- const onChange = jest.fn()
91
+ const onChange = vi.fn()
92
92
  const editorRef = createRef<PortableTextEditor>()
93
93
  render(
94
94
  <PortableTextEditorTester
@@ -120,7 +120,7 @@ describe('initialization', () => {
120
120
  focus: {path: [{_key: '123'}, 'children', {_key: '567'}], offset: 2},
121
121
  backward: false,
122
122
  }
123
- const onChange = jest.fn()
123
+ const onChange = vi.fn()
124
124
  render(
125
125
  <PortableTextEditorTester
126
126
  onChange={onChange}
@@ -164,7 +164,7 @@ describe('initialization', () => {
164
164
  focus: {path: [{_key: '123'}, 'children', {_key: '567'}], offset: 3},
165
165
  backward: false,
166
166
  }
167
- const onChange = jest.fn()
167
+ const onChange = vi.fn()
168
168
  const {rerender} = render(
169
169
  <PortableTextEditorTester
170
170
  onChange={onChange}
@@ -223,7 +223,7 @@ describe('initialization', () => {
223
223
  anchor: {path: [{_key: '123'}, 'children', {_key: '567'}], offset: 2},
224
224
  focus: {path: [{_key: '123'}, 'children', {_key: '567'}], offset: 2},
225
225
  }
226
- const onChange = jest.fn()
226
+ const onChange = vi.fn()
227
227
  render(
228
228
  <PortableTextEditorTester
229
229
  onChange={onChange}
@@ -266,7 +266,7 @@ describe('initialization', () => {
266
266
  anchor: {path: [{_key: '123'}, 'children', {_key: '567'}], offset: 2},
267
267
  focus: {path: [{_key: '123'}, 'children', {_key: '567'}], offset: 2},
268
268
  }
269
- const onChange = jest.fn()
269
+ const onChange = vi.fn()
270
270
  let _rerender: any
271
271
  await waitFor(() => {
272
272
  render(
@@ -300,7 +300,7 @@ describe('initialization', () => {
300
300
  expect(onChange).toHaveBeenCalledWith({type: 'value', value})
301
301
  })
302
302
  value = [{_type: 'banana', _key: '123'}]
303
- const newOnChange = jest.fn()
303
+ const newOnChange = vi.fn()
304
304
  _rerender(
305
305
  <PortableTextEditorTester
306
306
  onChange={newOnChange}
@@ -352,7 +352,7 @@ describe('initialization', () => {
352
352
  anchor: {path: [{_key: '123'}, 'children', {_key: '567'}], offset: 2},
353
353
  focus: {path: [{_key: '123'}, 'children', {_key: '567'}], offset: 2},
354
354
  }
355
- const onChange = jest.fn()
355
+ const onChange = vi.fn()
356
356
  render(
357
357
  <PortableTextEditorTester
358
358
  onChange={onChange}
@@ -1,4 +1,3 @@
1
- import {jest} from '@jest/globals'
2
1
  import {Schema} from '@sanity/schema'
3
2
  import {defineArrayMember, defineField} from '@sanity/types'
4
3
  import {
@@ -8,6 +7,7 @@ import {
8
7
  useMemo,
9
8
  type ForwardedRef,
10
9
  } from 'react'
10
+ import {vi} from 'vitest'
11
11
  import {
12
12
  PortableTextEditable,
13
13
  PortableTextEditor,
@@ -100,10 +100,7 @@ export const PortableTextEditorTester = forwardRef(
100
100
  key++
101
101
  return `${key}`
102
102
  }, [])
103
- const onChange = useMemo(
104
- () => props.onChange || jest.fn(),
105
- [props.onChange],
106
- )
103
+ const onChange = useMemo(() => props.onChange || vi.fn(), [props.onChange])
107
104
  return (
108
105
  <PortableTextEditor
109
106
  schemaType={props.schemaType}
@@ -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 ReactNode, type RefObject} from 'react'
4
+ import {describe, expect, it, vi} from 'vitest'
5
5
  import type {RangeDecoration} from '../..'
6
6
  import type {PortableTextEditor} from '../PortableTextEditor'
7
7
  import {PortableTextEditorTester, schemaType} from './PortableTextEditorTester'
@@ -23,7 +23,7 @@ const RangeDecorationTestComponent = ({children}: {children?: ReactNode}) => {
23
23
  describe('RangeDecorations', () => {
24
24
  it('only render range decorations as necessary', async () => {
25
25
  const editorRef: RefObject<PortableTextEditor> = createRef()
26
- const onChange = jest.fn()
26
+ const onChange = vi.fn()
27
27
  const value = [helloBlock]
28
28
  let rangeDecorations: RangeDecoration[] = [
29
29
  {
@@ -1,9 +1,29 @@
1
- import {describe, expect, it, jest} from '@jest/globals'
2
1
  import {fireEvent, render, waitFor} from '@testing-library/react'
3
- import {createRef, type RefObject} from 'react'
2
+ import {act, createRef, type RefObject} from 'react'
3
+ import {describe, expect, it, vi} from 'vitest'
4
4
  import {PortableTextEditor} from '../PortableTextEditor'
5
5
  import {PortableTextEditorTester, schemaType} from './PortableTextEditorTester'
6
- import {getEditableElement} from './utils'
6
+
7
+ async function getEditableElement(
8
+ component: ReturnType<typeof render>,
9
+ ): Promise<Element> {
10
+ await act(async () => component)
11
+ const element = component.container.querySelector(
12
+ '[data-slate-editor="true"]',
13
+ )
14
+ if (!element) {
15
+ throw new Error('Could not find element')
16
+ }
17
+ /**
18
+ * Manually add this because JSDom doesn't implement this and Slate checks for it
19
+ * internally before doing stuff.
20
+ *
21
+ * https://github.com/jsdom/jsdom/issues/1670
22
+ */
23
+ // @ts-ignore
24
+ element.isContentEditable = true
25
+ return element
26
+ }
7
27
 
8
28
  describe('adds empty text block if its needed', () => {
9
29
  const newBlock = {
@@ -34,7 +54,7 @@ describe('adds empty text block if its needed', () => {
34
54
  }
35
55
 
36
56
  const editorRef: RefObject<PortableTextEditor> = createRef()
37
- const onChange = jest.fn()
57
+ const onChange = vi.fn()
38
58
  const component = render(
39
59
  <PortableTextEditorTester
40
60
  onChange={onChange}
@@ -96,7 +116,7 @@ describe('adds empty text block if its needed', () => {
96
116
  }
97
117
 
98
118
  const editorRef: RefObject<PortableTextEditor> = createRef()
99
- const onChange = jest.fn()
119
+ const onChange = vi.fn()
100
120
  const component = render(
101
121
  <PortableTextEditorTester
102
122
  onChange={onChange}
@@ -151,7 +171,7 @@ describe('adds empty text block if its needed', () => {
151
171
  }
152
172
 
153
173
  const editorRef: RefObject<PortableTextEditor> = createRef()
154
- const onChange = jest.fn()
174
+ const onChange = vi.fn()
155
175
  const component = render(
156
176
  <PortableTextEditorTester
157
177
  onChange={onChange}
@@ -217,7 +237,7 @@ describe('adds empty text block if its needed', () => {
217
237
  }
218
238
 
219
239
  const editorRef: RefObject<PortableTextEditor> = createRef()
220
- const onChange = jest.fn()
240
+ const onChange = vi.fn()
221
241
  const component = render(
222
242
  <PortableTextEditorTester
223
243
  onChange={onChange}
@@ -1,8 +1,8 @@
1
- import {describe, expect, jest, test} from '@jest/globals'
2
1
  import {Schema} from '@sanity/schema'
3
2
  import type {PortableTextBlock} from '@sanity/types'
4
3
  import {render, waitFor} from '@testing-library/react'
5
4
  import {createRef, type RefObject} from 'react'
5
+ import {describe, expect, test, vi} from 'vitest'
6
6
  import type {EditorChange, EditorSelection} from '../../types/editor'
7
7
  import {PortableTextEditable} from '../Editable'
8
8
  import {PortableTextEditor} from '../PortableTextEditor'
@@ -35,7 +35,7 @@ describe(PortableTextEditor.insertBlock.name, () => {
35
35
  style: 'normal',
36
36
  }
37
37
  const initialValue: Array<PortableTextBlock> = [emptyTextBlock]
38
- const onChange: (change: EditorChange) => void = jest.fn()
38
+ const onChange: (change: EditorChange) => void = vi.fn()
39
39
 
40
40
  render(
41
41
  <PortableTextEditor
@@ -104,7 +104,7 @@ describe(PortableTextEditor.insertBlock.name, () => {
104
104
  style: 'normal',
105
105
  }
106
106
  const initialValue: Array<PortableTextBlock> = [nonEmptyTextBlock]
107
- const onChange: (change: EditorChange) => void = jest.fn()
107
+ const onChange: (change: EditorChange) => void = vi.fn()
108
108
 
109
109
  render(
110
110
  <PortableTextEditor
@@ -177,7 +177,7 @@ describe(PortableTextEditor.insertBlock.name, () => {
177
177
  _type: 'image',
178
178
  }
179
179
  const initialValue: Array<PortableTextBlock> = [emptyTextBlock, imageBlock]
180
- const onChange: (change: EditorChange) => void = jest.fn()
180
+ const onChange: (change: EditorChange) => void = vi.fn()
181
181
 
182
182
  render(
183
183
  <PortableTextEditor
@@ -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 {PortableTextEditor} from '../PortableTextEditor'
6
6
  import {PortableTextEditorTester, schemaType} from './PortableTextEditorTester'
7
7
 
@@ -24,7 +24,7 @@ describe('when PTE would display warnings, instead it self solves', () => {
24
24
  },
25
25
  ]
26
26
 
27
- const onChange = jest.fn()
27
+ const onChange = vi.fn()
28
28
  render(
29
29
  <PortableTextEditorTester
30
30
  onChange={onChange}
@@ -80,7 +80,7 @@ describe('when PTE would display warnings, instead it self solves', () => {
80
80
  },
81
81
  ]
82
82
 
83
- const onChange = jest.fn()
83
+ const onChange = vi.fn()
84
84
  render(
85
85
  <PortableTextEditorTester
86
86
  onChange={onChange}
@@ -137,7 +137,7 @@ describe('when PTE would display warnings, instead it self solves', () => {
137
137
  },
138
138
  ]
139
139
 
140
- const onChange = jest.fn()
140
+ const onChange = vi.fn()
141
141
  render(
142
142
  <PortableTextEditorTester
143
143
  onChange={onChange}
@@ -209,7 +209,7 @@ describe('when PTE would display warnings, instead it self solves', () => {
209
209
  },
210
210
  ]
211
211
 
212
- const onChange = jest.fn()
212
+ const onChange = vi.fn()
213
213
  render(
214
214
  <PortableTextEditorTester
215
215
  onChange={onChange}
@@ -273,7 +273,7 @@ describe('when PTE would display warnings, instead it self solves', () => {
273
273
  },
274
274
  ]
275
275
 
276
- const onChange = jest.fn()
276
+ const onChange = vi.fn()
277
277
  render(
278
278
  <PortableTextEditorTester
279
279
  onChange={onChange}
@@ -316,7 +316,7 @@ describe('when PTE would display warnings, instead it self solves', () => {
316
316
  const editorRef: RefObject<PortableTextEditor> = createRef()
317
317
  const initialValue = [] as PortableTextBlock[]
318
318
 
319
- const onChange = jest.fn()
319
+ const onChange = vi.fn()
320
320
  render(
321
321
  <PortableTextEditorTester
322
322
  onChange={onChange}