@portabletext/editor 2.7.2 → 2.8.1

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 (72) hide show
  1. package/lib/_chunks-cjs/selector.is-selecting-entire-blocks.cjs +3 -1
  2. package/lib/_chunks-cjs/selector.is-selecting-entire-blocks.cjs.map +1 -1
  3. package/lib/_chunks-cjs/selector.is-selection-expanded.cjs +10 -4
  4. package/lib/_chunks-cjs/selector.is-selection-expanded.cjs.map +1 -1
  5. package/lib/_chunks-cjs/util.merge-text-blocks.cjs +0 -1
  6. package/lib/_chunks-cjs/util.merge-text-blocks.cjs.map +1 -1
  7. package/lib/_chunks-cjs/util.slice-blocks.cjs +60 -9
  8. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
  9. package/lib/_chunks-dts/behavior.types.action.d.cts +149 -149
  10. package/lib/_chunks-dts/behavior.types.action.d.ts +86 -86
  11. package/lib/_chunks-es/selector.is-selecting-entire-blocks.js +4 -2
  12. package/lib/_chunks-es/selector.is-selecting-entire-blocks.js.map +1 -1
  13. package/lib/_chunks-es/selector.is-selection-expanded.js +10 -4
  14. package/lib/_chunks-es/selector.is-selection-expanded.js.map +1 -1
  15. package/lib/_chunks-es/util.merge-text-blocks.js +0 -1
  16. package/lib/_chunks-es/util.merge-text-blocks.js.map +1 -1
  17. package/lib/_chunks-es/util.slice-blocks.js +56 -8
  18. package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
  19. package/lib/index.cjs +94 -131
  20. package/lib/index.cjs.map +1 -1
  21. package/lib/index.js +90 -128
  22. package/lib/index.js.map +1 -1
  23. package/lib/plugins/index.d.cts +3 -3
  24. package/lib/selectors/index.d.cts +13 -3
  25. package/lib/selectors/index.d.ts +13 -3
  26. package/package.json +13 -14
  27. package/src/behaviors/behavior.abstract.insert.ts +58 -1
  28. package/src/behaviors/behavior.abstract.split.ts +0 -1
  29. package/src/behaviors/behavior.core.annotations.ts +24 -2
  30. package/src/behaviors/behavior.core.ts +1 -1
  31. package/src/behaviors/behavior.types.event.ts +18 -18
  32. package/src/converters/converter.portable-text.ts +0 -1
  33. package/src/converters/converter.text-html.serialize.test.ts +27 -17
  34. package/src/converters/converter.text-html.ts +0 -1
  35. package/src/converters/converter.text-plain.test.ts +1 -1
  36. package/src/converters/converter.text-plain.ts +0 -1
  37. package/src/editor/Editable.tsx +0 -1
  38. package/src/editor/plugins/createWithEditableAPI.ts +16 -0
  39. package/src/internal-utils/parse-blocks.test.ts +23 -23
  40. package/src/internal-utils/parse-blocks.ts +13 -24
  41. package/src/internal-utils/test-editor.tsx +15 -21
  42. package/src/operations/behavior.operation.annotation.add.ts +2 -13
  43. package/src/operations/behavior.operation.block.set.ts +1 -1
  44. package/src/operations/behavior.operation.block.unset.ts +2 -2
  45. package/src/operations/behavior.operation.insert.block.ts +1 -1
  46. package/src/operations/behavior.operations.ts +0 -18
  47. package/src/plugins/plugin.internal.auto-close-brackets.test.tsx +25 -71
  48. package/src/plugins/plugin.markdown.test.tsx +12 -30
  49. package/src/selectors/selector.get-selected-value.test.ts +748 -0
  50. package/src/selectors/selector.get-selected-value.ts +28 -7
  51. package/src/selectors/selector.get-trimmed-selection.test.ts +0 -1
  52. package/src/selectors/selector.is-active-annotation.test.ts +320 -0
  53. package/src/selectors/selector.is-active-annotation.ts +24 -0
  54. package/src/utils/util.merge-text-blocks.ts +1 -1
  55. package/src/utils/util.slice-blocks.ts +36 -3
  56. package/src/editor/__tests__/PortableTextEditor.test.tsx +0 -430
  57. package/src/editor/__tests__/PortableTextEditorTester.tsx +0 -58
  58. package/src/editor/__tests__/RangeDecorations.test.tsx +0 -213
  59. package/src/editor/__tests__/insert-block.test.tsx +0 -224
  60. package/src/editor/__tests__/self-solving.test.tsx +0 -183
  61. package/src/editor/plugins/__tests__/withEditableAPIDelete.test.tsx +0 -298
  62. package/src/editor/plugins/__tests__/withEditableAPIGetFragment.test.tsx +0 -177
  63. package/src/editor/plugins/__tests__/withEditableAPIInsert.test.tsx +0 -538
  64. package/src/editor/plugins/__tests__/withEditableAPISelectionsOverlapping.test.tsx +0 -162
  65. package/src/editor/plugins/__tests__/withPortableTextLists.test.tsx +0 -65
  66. package/src/editor/plugins/__tests__/withPortableTextMarkModel.test.tsx +0 -612
  67. package/src/editor/plugins/__tests__/withPortableTextSelections.test.tsx +0 -103
  68. package/src/editor/plugins/__tests__/withUndoRedo.test.tsx +0 -147
  69. package/src/internal-utils/__tests__/valueNormalization.test.tsx +0 -79
  70. package/src/operations/behavior.operation.insert-inline-object.ts +0 -59
  71. package/src/operations/behavior.operation.insert-span.ts +0 -48
  72. package/src/utils/util.slice-blocks.test.ts +0 -465
@@ -19,7 +19,6 @@ export function parseBlocks({
19
19
  context: Pick<EditorContext, 'keyGenerator' | 'schema'>
20
20
  blocks: unknown
21
21
  options: {
22
- refreshKeys: boolean
23
22
  validateFields: boolean
24
23
  }
25
24
  }): Array<PortableTextBlock> {
@@ -42,7 +41,6 @@ export function parseBlock({
42
41
  context: Pick<EditorContext, 'keyGenerator' | 'schema'>
43
42
  block: unknown
44
43
  options: {
45
- refreshKeys: boolean
46
44
  validateFields: boolean
47
45
  }
48
46
  }): PortableTextBlock | undefined {
@@ -59,7 +57,7 @@ export function parseBlockObject({
59
57
  }: {
60
58
  blockObject: unknown
61
59
  context: Pick<EditorContext, 'keyGenerator' | 'schema'>
62
- options: {refreshKeys: boolean; validateFields: boolean}
60
+ options: {validateFields: boolean}
63
61
  }): PortableTextObject | undefined {
64
62
  if (!isTypedObject(blockObject)) {
65
63
  return undefined
@@ -101,7 +99,7 @@ export function parseTextBlock({
101
99
  }: {
102
100
  block: unknown
103
101
  context: Pick<EditorContext, 'keyGenerator' | 'schema'>
104
- options: {refreshKeys: boolean; validateFields: boolean}
102
+ options: {validateFields: boolean}
105
103
  }): PortableTextTextBlock | undefined {
106
104
  if (!isTypedObject(block)) {
107
105
  return undefined
@@ -135,11 +133,8 @@ export function parseTextBlock({
135
133
  return undefined
136
134
  }
137
135
 
138
- const _key = options.refreshKeys
139
- ? context.keyGenerator()
140
- : typeof block._key === 'string'
141
- ? block._key
142
- : context.keyGenerator()
136
+ const _key =
137
+ typeof block._key === 'string' ? block._key : context.keyGenerator()
143
138
 
144
139
  const unparsedMarkDefs: Array<unknown> = Array.isArray(block.markDefs)
145
140
  ? block.markDefs
@@ -193,6 +188,7 @@ export function parseTextBlock({
193
188
  parseInlineObject({inlineObject: child, context, options}),
194
189
  )
195
190
  .filter((child) => child !== undefined)
191
+ const marks = children.flatMap((child) => child.marks ?? [])
196
192
 
197
193
  const parsedBlock: PortableTextTextBlock = {
198
194
  _type: context.schema.block.name,
@@ -208,7 +204,7 @@ export function parseTextBlock({
208
204
  marks: [],
209
205
  },
210
206
  ],
211
- markDefs,
207
+ markDefs: markDefs.filter((markDef) => marks.includes(markDef._key)),
212
208
  ...customFields,
213
209
  }
214
210
 
@@ -250,7 +246,7 @@ export function parseSpan({
250
246
  span: unknown
251
247
  context: Pick<EditorContext, 'keyGenerator' | 'schema'>
252
248
  markDefKeyMap: Map<string, string>
253
- options: {refreshKeys: boolean; validateFields: boolean}
249
+ options: {validateFields: boolean}
254
250
  }): PortableTextSpan | undefined {
255
251
  if (!isTypedObject(span)) {
256
252
  return undefined
@@ -299,11 +295,7 @@ export function parseSpan({
299
295
 
300
296
  return {
301
297
  _type: 'span',
302
- _key: options.refreshKeys
303
- ? context.keyGenerator()
304
- : typeof span._key === 'string'
305
- ? span._key
306
- : context.keyGenerator(),
298
+ _key: typeof span._key === 'string' ? span._key : context.keyGenerator(),
307
299
  text: typeof span.text === 'string' ? span.text : '',
308
300
  marks,
309
301
  ...(options.validateFields ? {} : customFields),
@@ -317,7 +309,7 @@ export function parseInlineObject({
317
309
  }: {
318
310
  inlineObject: unknown
319
311
  context: Pick<EditorContext, 'keyGenerator' | 'schema'>
320
- options: {refreshKeys: boolean; validateFields: boolean}
312
+ options: {validateFields: boolean}
321
313
  }): PortableTextObject | undefined {
322
314
  if (!isTypedObject(inlineObject)) {
323
315
  return undefined
@@ -348,7 +340,7 @@ export function parseAnnotation({
348
340
  }: {
349
341
  annotation: TypedObject
350
342
  context: Pick<EditorContext, 'keyGenerator' | 'schema'>
351
- options: {refreshKeys: boolean; validateFields: boolean}
343
+ options: {validateFields: boolean}
352
344
  }): PortableTextObject | undefined {
353
345
  if (!isTypedObject(annotation)) {
354
346
  return undefined
@@ -381,7 +373,7 @@ function parseObject({
381
373
  context: Pick<EditorContext, 'keyGenerator'> & {
382
374
  schemaType: EditorSchema['blockObjects'][0]
383
375
  }
384
- options: {refreshKeys: boolean; validateFields: boolean}
376
+ options: {validateFields: boolean}
385
377
  }): PortableTextObject {
386
378
  const {_type, _key, ...customFields} = object
387
379
 
@@ -404,11 +396,8 @@ function parseObject({
404
396
 
405
397
  return {
406
398
  _type: context.schemaType.name,
407
- _key: options.refreshKeys
408
- ? context.keyGenerator()
409
- : typeof object._key === 'string'
410
- ? object._key
411
- : context.keyGenerator(),
399
+ _key:
400
+ typeof object._key === 'string' ? object._key : context.keyGenerator(),
412
401
  ...values,
413
402
  }
414
403
  }
@@ -5,13 +5,16 @@ import {page} from '@vitest/browser/context'
5
5
  import React from 'react'
6
6
  import {expect, vi} from 'vitest'
7
7
  import {render} from 'vitest-browser-react'
8
+ import type {Context} from '../../gherkin-tests-v2/step-context'
9
+ import type {NativeBehaviorEvent} from '../behaviors'
8
10
  import type {Editor} from '../editor'
9
- import {PortableTextEditable} from '../editor/Editable'
11
+ import {
12
+ PortableTextEditable,
13
+ type PortableTextEditableProps,
14
+ } from '../editor/Editable'
10
15
  import type {EditorActor} from '../editor/editor-machine'
11
16
  import {EditorProvider} from '../editor/editor-provider'
12
- import type {EditorEmittedEvent} from '../editor/relay-machine'
13
17
  import {EditorRefPlugin} from '../plugins/plugin.editor-ref'
14
- import {EventListenerPlugin} from '../plugins/plugin.event-listener'
15
18
  import {InternalEditorAfterRefPlugin} from '../plugins/plugin.internal.editor-actor-ref'
16
19
  import {InternalSlateEditorRefPlugin} from '../plugins/plugin.internal.slate-editor-ref'
17
20
  import type {PortableTextSlateEditor} from '../types/editor'
@@ -22,12 +25,12 @@ export async function createTestEditor(
22
25
  keyGenerator?: () => string
23
26
  schemaDefinition?: SchemaDefinition
24
27
  children?: React.ReactNode
28
+ editableProps?: PortableTextEditableProps
25
29
  } = {},
26
- ) {
30
+ ): Promise<Pick<Context, 'editor' | 'locator'>> {
27
31
  const editorRef = React.createRef<Editor>()
28
32
  const editorActorRef = React.createRef<EditorActor>()
29
33
  const slateRef = React.createRef<PortableTextSlateEditor>()
30
- const onEvent = vi.fn<() => EditorEmittedEvent>()
31
34
  const keyGenerator = options.keyGenerator ?? createTestKeyGenerator()
32
35
 
33
36
  render(
@@ -41,8 +44,7 @@ export async function createTestEditor(
41
44
  <EditorRefPlugin ref={editorRef} />
42
45
  <InternalEditorAfterRefPlugin ref={editorActorRef} />
43
46
  <InternalSlateEditorRefPlugin ref={slateRef} />
44
- <EventListenerPlugin on={onEvent} />
45
- <PortableTextEditable />
47
+ <PortableTextEditable {...options.editableProps} />
46
48
  {options.children}
47
49
  </EditorProvider>,
48
50
  )
@@ -51,27 +53,19 @@ export async function createTestEditor(
51
53
 
52
54
  await vi.waitFor(() => expect.element(locator).toBeInTheDocument())
53
55
 
54
- function paste(dataTransfer: DataTransfer) {
56
+ function sendNativeEvent(event: NativeBehaviorEvent) {
55
57
  editorActorRef.current?.send({
56
58
  type: 'behavior event',
57
- behaviorEvent: {
58
- type: 'clipboard.paste',
59
- originEvent: {dataTransfer},
60
- position: {
61
- selection: editorRef.current?.getSnapshot().context.selection!,
62
- },
63
- },
59
+ behaviorEvent: event,
64
60
  editor: slateRef.current!,
65
61
  })
66
62
  }
67
63
 
68
64
  return {
69
- editorActorRef,
70
- editorRef,
71
- keyGenerator,
65
+ editor: {
66
+ ...editorRef.current!,
67
+ sendNativeEvent,
68
+ },
72
69
  locator,
73
- onEvent,
74
- slateRef,
75
- paste,
76
70
  }
77
71
  }
@@ -31,7 +31,7 @@ export const addAnnotationOperationImplementation: BehaviorOperationImplementati
31
31
  ...operation.annotation.value,
32
32
  },
33
33
  context,
34
- options: {refreshKeys: false, validateFields: true},
34
+ options: {validateFields: true},
35
35
  })
36
36
 
37
37
  if (!parsedAnnotation) {
@@ -116,22 +116,11 @@ export const addAnnotationOperationImplementation: BehaviorOperationImplementati
116
116
  }
117
117
 
118
118
  const marks = span.marks ?? []
119
- const existingSameTypeAnnotations = marks.filter((mark) =>
120
- markDefs.some(
121
- (markDef) =>
122
- markDef._key === mark && markDef._type === parsedAnnotation._type,
123
- ),
124
- )
125
119
 
126
120
  Transforms.setNodes(
127
121
  editor,
128
122
  {
129
- marks: [
130
- ...marks.filter(
131
- (mark) => !existingSameTypeAnnotations.includes(mark),
132
- ),
133
- annotationKey,
134
- ],
123
+ marks: [...marks, annotationKey],
135
124
  },
136
125
  {at: path},
137
126
  )
@@ -51,7 +51,7 @@ export const blockSetOperationImplementation: BehaviorOperationImplementation<
51
51
  ...parsedBlock,
52
52
  ...filteredProps,
53
53
  },
54
- options: {refreshKeys: false, validateFields: true},
54
+ options: {validateFields: true},
55
55
  })
56
56
 
57
57
  if (!updatedBlock) {
@@ -51,7 +51,7 @@ export const blockUnsetOperationImplementation: BehaviorOperationImplementation<
51
51
  const updatedTextBlock = parseBlock({
52
52
  context,
53
53
  block: omit(parsedBlock, propsToRemove),
54
- options: {refreshKeys: false, validateFields: true},
54
+ options: {validateFields: true},
55
55
  })
56
56
 
57
57
  if (!updatedTextBlock) {
@@ -81,7 +81,7 @@ export const blockUnsetOperationImplementation: BehaviorOperationImplementation<
81
81
  parsedBlock,
82
82
  operation.props.filter((prop) => prop !== '_type'),
83
83
  ),
84
- options: {refreshKeys: false, validateFields: true},
84
+ options: {validateFields: true},
85
85
  })
86
86
 
87
87
  if (!updatedBlockObject) {
@@ -24,7 +24,7 @@ export const insertBlockOperationImplementation: BehaviorOperationImplementation
24
24
  const parsedBlock = parseBlock({
25
25
  block: operation.block,
26
26
  context,
27
- options: {refreshKeys: false, validateFields: true},
27
+ options: {validateFields: true},
28
28
  })
29
29
 
30
30
  if (!parsedBlock) {
@@ -18,8 +18,6 @@ import {childSetOperationImplementation} from './behavior.operation.child.set'
18
18
  import {childUnsetOperationImplementation} from './behavior.operation.child.unset'
19
19
  import {decoratorAddOperationImplementation} from './behavior.operation.decorator.add'
20
20
  import {deleteOperationImplementation} from './behavior.operation.delete'
21
- import {insertInlineObjectOperationImplementation} from './behavior.operation.insert-inline-object'
22
- import {insertSpanOperationImplementation} from './behavior.operation.insert-span'
23
21
  import {insertBlockOperationImplementation} from './behavior.operation.insert.block'
24
22
  import {insertTextOperationImplementation} from './behavior.operation.insert.text'
25
23
  import {moveBackwardOperationImplementation} from './behavior.operation.move.backward'
@@ -68,8 +66,6 @@ const behaviorOperationImplementations: BehaviorOperationImplementations = {
68
66
  'history.redo': historyRedoOperationImplementation,
69
67
  'history.undo': historyUndoOperationImplementation,
70
68
  'insert.block': insertBlockOperationImplementation,
71
- 'insert.inline object': insertInlineObjectOperationImplementation,
72
- 'insert.span': insertSpanOperationImplementation,
73
69
  'insert.text': insertTextOperationImplementation,
74
70
  'move.backward': moveBackwardOperationImplementation,
75
71
  'move.block': moveBlockOperationImplementation,
@@ -170,20 +166,6 @@ export function performOperation({
170
166
  })
171
167
  break
172
168
  }
173
- case 'insert.inline object': {
174
- behaviorOperationImplementations['insert.inline object']({
175
- context,
176
- operation: operation,
177
- })
178
- break
179
- }
180
- case 'insert.span': {
181
- behaviorOperationImplementations['insert.span']({
182
- context,
183
- operation: operation,
184
- })
185
- break
186
- }
187
169
  case 'insert.text': {
188
170
  behaviorOperationImplementations['insert.text']({
189
171
  context,
@@ -1,117 +1,71 @@
1
- import {defineSchema} from '@portabletext/schema'
2
- import {createTestKeyGenerator, getTersePt} from '@portabletext/test'
3
- import {page, userEvent} from '@vitest/browser/context'
4
- import React from 'react'
1
+ import {getTersePt} from '@portabletext/test'
2
+ import {userEvent} from '@vitest/browser/context'
5
3
  import {describe, expect, test, vi} from 'vitest'
6
- import {render} from 'vitest-browser-react'
7
- import {EditorProvider, PortableTextEditable, type Editor} from '..'
8
- import {EditorRefPlugin} from './plugin.editor-ref'
4
+ import {createTestEditor} from '../internal-utils/test-editor'
9
5
  import {AutoCloseBracketsPlugin} from './plugin.internal.auto-close-brackets'
10
6
 
11
7
  describe(AutoCloseBracketsPlugin.name, () => {
12
8
  test('One-character text insertion', async () => {
13
- const editorRef = React.createRef<Editor>()
14
-
15
- render(
16
- <EditorProvider
17
- initialConfig={{
18
- keyGenerator: createTestKeyGenerator(),
19
- schemaDefinition: defineSchema({}),
20
- }}
21
- >
22
- <EditorRefPlugin ref={editorRef} />
23
- <PortableTextEditable />
24
- <AutoCloseBracketsPlugin />
25
- </EditorProvider>,
26
- )
27
-
28
- const locator = page.getByRole('textbox')
29
- await vi.waitFor(() => expect.element(locator).toBeInTheDocument())
9
+ const {editor, locator} = await createTestEditor({
10
+ children: <AutoCloseBracketsPlugin />,
11
+ })
12
+
30
13
  await userEvent.click(locator)
31
14
 
32
- editorRef.current?.send({type: 'insert.text', text: '('})
15
+ editor.send({type: 'insert.text', text: '('})
33
16
 
34
17
  await vi.waitFor(() => {
35
- expect(getTersePt(editorRef.current!.getSnapshot().context)).toEqual([
36
- '()',
37
- ])
18
+ expect(getTersePt(editor.getSnapshot().context)).toEqual(['()'])
38
19
  })
39
20
 
40
21
  await userEvent.type(locator, 'foo')
41
22
 
42
23
  await vi.waitFor(() => {
43
- expect(getTersePt(editorRef.current!.getSnapshot().context)).toEqual([
44
- '(foo)',
45
- ])
24
+ expect(getTersePt(editor.getSnapshot().context)).toEqual(['(foo)'])
46
25
  })
47
26
 
48
- editorRef.current?.send({type: 'history.undo'})
27
+ editor.send({type: 'history.undo'})
49
28
 
50
29
  await vi.waitFor(() => {
51
- expect(getTersePt(editorRef.current!.getSnapshot().context)).toEqual([
52
- '()',
53
- ])
30
+ expect(getTersePt(editor.getSnapshot().context)).toEqual(['()'])
54
31
  })
55
32
 
56
- editorRef.current?.send({type: 'history.undo'})
33
+ editor.send({type: 'history.undo'})
57
34
 
58
35
  await vi.waitFor(() => {
59
- expect(getTersePt(editorRef.current!.getSnapshot().context)).toEqual([
60
- '(',
61
- ])
36
+ expect(getTersePt(editor.getSnapshot().context)).toEqual(['('])
62
37
  })
63
38
  })
64
39
 
65
40
  test('Two-character text insertion', async () => {
66
- const editorRef = React.createRef<Editor>()
67
-
68
- render(
69
- <EditorProvider
70
- initialConfig={{
71
- keyGenerator: createTestKeyGenerator(),
72
- schemaDefinition: defineSchema({}),
73
- }}
74
- >
75
- <EditorRefPlugin ref={editorRef} />
76
- <PortableTextEditable />
77
- <AutoCloseBracketsPlugin />
78
- </EditorProvider>,
79
- )
80
-
81
- const locator = page.getByRole('textbox')
82
- await vi.waitFor(() => expect.element(locator).toBeInTheDocument())
41
+ const {editor, locator} = await createTestEditor({
42
+ children: <AutoCloseBracketsPlugin />,
43
+ })
44
+
83
45
  await userEvent.click(locator)
84
46
 
85
- editorRef.current?.send({type: 'insert.text', text: '(f'})
47
+ editor.send({type: 'insert.text', text: '(f'})
86
48
 
87
49
  await vi.waitFor(() => {
88
- expect(getTersePt(editorRef.current!.getSnapshot().context)).toEqual([
89
- '(f)',
90
- ])
50
+ expect(getTersePt(editor.getSnapshot().context)).toEqual(['(f)'])
91
51
  })
92
52
 
93
53
  await userEvent.type(locator, 'oo')
94
54
 
95
55
  await vi.waitFor(() => {
96
- expect(getTersePt(editorRef.current!.getSnapshot().context)).toEqual([
97
- '(foo)',
98
- ])
56
+ expect(getTersePt(editor.getSnapshot().context)).toEqual(['(foo)'])
99
57
  })
100
58
 
101
- editorRef.current?.send({type: 'history.undo'})
59
+ editor.send({type: 'history.undo'})
102
60
 
103
61
  await vi.waitFor(() => {
104
- expect(getTersePt(editorRef.current!.getSnapshot().context)).toEqual([
105
- '(f)',
106
- ])
62
+ expect(getTersePt(editor.getSnapshot().context)).toEqual(['(f)'])
107
63
  })
108
64
 
109
- editorRef.current?.send({type: 'history.undo'})
65
+ editor.send({type: 'history.undo'})
110
66
 
111
67
  await vi.waitFor(() => {
112
- expect(getTersePt(editorRef.current!.getSnapshot().context)).toEqual([
113
- '(f',
114
- ])
68
+ expect(getTersePt(editor.getSnapshot().context)).toEqual(['(f'])
115
69
  })
116
70
  })
117
71
  })
@@ -1,58 +1,40 @@
1
1
  import {defineSchema} from '@portabletext/schema'
2
- import {createTestKeyGenerator, getTersePt} from '@portabletext/test'
3
- import {page, userEvent} from '@vitest/browser/context'
4
- import React from 'react'
2
+ import {getTersePt} from '@portabletext/test'
3
+ import {userEvent} from '@vitest/browser/context'
5
4
  import {describe, expect, test, vi} from 'vitest'
6
- import {render} from 'vitest-browser-react'
7
- import {PortableTextEditable, type Editor} from '..'
8
- import {EditorProvider} from '../editor/editor-provider'
5
+ import {createTestEditor} from '../internal-utils/test-editor'
9
6
  import {getTextMarks} from '../internal-utils/text-marks'
10
- import {EditorRefPlugin} from './plugin.editor-ref'
11
7
  import {MarkdownPlugin} from './plugin.markdown'
12
8
 
13
9
  describe(MarkdownPlugin.name, () => {
14
10
  test('Scenario: Undoing bold shortcut', async () => {
15
- const editorRef = React.createRef<Editor>()
16
-
17
- render(
18
- <EditorProvider
19
- initialConfig={{
20
- keyGenerator: createTestKeyGenerator(),
21
- schemaDefinition: defineSchema({decorators: [{name: 'strong'}]}),
22
- }}
23
- >
24
- <EditorRefPlugin ref={editorRef} />
25
- <PortableTextEditable />
11
+ const {editor, locator} = await createTestEditor({
12
+ children: (
26
13
  <MarkdownPlugin
27
14
  config={{
28
15
  boldDecorator: () => 'strong',
29
16
  }}
30
17
  />
31
- </EditorProvider>,
32
- )
33
-
34
- const locator = page.getByRole('textbox')
35
-
36
- await vi.waitFor(() => expect.element(locator).toBeInTheDocument())
18
+ ),
19
+ schemaDefinition: defineSchema({decorators: [{name: 'strong'}]}),
20
+ })
37
21
 
38
22
  await userEvent.type(locator, '**Hello world!**')
39
23
 
40
24
  await vi.waitFor(() => {
41
- expect(getTersePt(editorRef.current!.getSnapshot().context)).toEqual([
42
- 'Hello world!',
43
- ])
25
+ expect(getTersePt(editor.getSnapshot().context)).toEqual(['Hello world!'])
44
26
  })
45
27
 
46
28
  await vi.waitFor(() => {
47
29
  expect(
48
- getTextMarks(editorRef.current!.getSnapshot().context, 'Hello world!'),
30
+ getTextMarks(editor.getSnapshot().context, 'Hello world!'),
49
31
  ).toEqual(['strong'])
50
32
  })
51
33
 
52
- editorRef.current?.send({type: 'history.undo'})
34
+ editor.send({type: 'history.undo'})
53
35
 
54
36
  await vi.waitFor(() => {
55
- expect(getTersePt(editorRef.current!.getSnapshot().context)).toEqual([
37
+ expect(getTersePt(editor.getSnapshot().context)).toEqual([
56
38
  '**Hello world!**',
57
39
  ])
58
40
  })