@portabletext/editor 1.49.7 → 1.49.8

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.
@@ -1,364 +0,0 @@
1
- import type {PortableTextBlock} from '@sanity/types'
2
- import {render, waitFor} from '@testing-library/react'
3
- import {createRef, type RefObject} from 'react'
4
- import {describe, expect, it, vi} from 'vitest'
5
- import {createTestKeyGenerator} from '../../internal-utils/test-key-generator'
6
- import {PortableTextEditor} from '../PortableTextEditor'
7
- import {PortableTextEditorTester, schemaType} from './PortableTextEditorTester'
8
-
9
- describe('when PTE would display warnings, instead it self solves', () => {
10
- it('when child at index is missing required _key in block with _key', async () => {
11
- const editorRef: RefObject<PortableTextEditor | null> = createRef()
12
- const initialValue = [
13
- {
14
- _key: 'abc',
15
- _type: 'myTestBlockType',
16
- children: [
17
- {
18
- _type: 'span',
19
- marks: [],
20
- text: 'Hello with a new key',
21
- },
22
- ],
23
- markDefs: [],
24
- style: 'normal',
25
- },
26
- ]
27
-
28
- const onChange = vi.fn()
29
- render(
30
- <PortableTextEditorTester
31
- keyGenerator={createTestKeyGenerator()}
32
- onChange={onChange}
33
- ref={editorRef}
34
- schemaType={schemaType}
35
- value={initialValue}
36
- />,
37
- )
38
- await waitFor(() => {
39
- expect(onChange).toHaveBeenCalledWith({
40
- type: 'value',
41
- value: initialValue,
42
- })
43
- expect(onChange).toHaveBeenCalledWith({type: 'ready'})
44
- })
45
- await waitFor(() => {
46
- if (editorRef.current) {
47
- expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
48
- {
49
- _key: 'abc',
50
- _type: 'myTestBlockType',
51
- children: [
52
- {
53
- _key: 'k3',
54
- _type: 'span',
55
- text: 'Hello with a new key',
56
- marks: [],
57
- },
58
- ],
59
- markDefs: [],
60
- style: 'normal',
61
- },
62
- ])
63
- }
64
- })
65
- })
66
-
67
- it('self-solves missing .markDefs', async () => {
68
- const editorRef: RefObject<PortableTextEditor | null> = createRef()
69
- const initialValue = [
70
- {
71
- _key: 'abc',
72
- _type: 'myTestBlockType',
73
- children: [
74
- {
75
- _key: 'def',
76
- _type: 'span',
77
- marks: [],
78
- text: 'No markDefs',
79
- },
80
- ],
81
- style: 'normal',
82
- },
83
- ]
84
-
85
- const onChange = vi.fn()
86
- render(
87
- <PortableTextEditorTester
88
- keyGenerator={createTestKeyGenerator()}
89
- onChange={onChange}
90
- ref={editorRef}
91
- schemaType={schemaType}
92
- value={initialValue}
93
- />,
94
- )
95
- await waitFor(() => {
96
- expect(onChange).toHaveBeenCalledWith({
97
- type: 'value',
98
- value: initialValue,
99
- })
100
- expect(onChange).toHaveBeenCalledWith({type: 'ready'})
101
- })
102
- await waitFor(() => {
103
- if (editorRef.current) {
104
- PortableTextEditor.focus(editorRef.current)
105
- expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
106
- {
107
- _key: 'abc',
108
- _type: 'myTestBlockType',
109
- children: [
110
- {
111
- _key: 'def',
112
- _type: 'span',
113
- text: 'No markDefs',
114
- marks: [],
115
- },
116
- ],
117
- markDefs: [],
118
- style: 'normal',
119
- },
120
- ])
121
- }
122
- })
123
- })
124
-
125
- it('adds missing .children', async () => {
126
- const editorRef: RefObject<PortableTextEditor | null> = createRef()
127
- const initialValue = [
128
- {
129
- _key: 'abc',
130
- _type: 'myTestBlockType',
131
- style: 'normal',
132
- markDefs: [],
133
- },
134
- {
135
- _key: 'def',
136
- _type: 'myTestBlockType',
137
- style: 'normal',
138
- children: [],
139
- markDefs: [],
140
- },
141
- ]
142
-
143
- const onChange = vi.fn()
144
- render(
145
- <PortableTextEditorTester
146
- keyGenerator={createTestKeyGenerator()}
147
- onChange={onChange}
148
- ref={editorRef}
149
- schemaType={schemaType}
150
- value={initialValue}
151
- />,
152
- )
153
- await waitFor(() => {
154
- expect(onChange).toHaveBeenCalledWith({
155
- type: 'value',
156
- value: initialValue,
157
- })
158
- expect(onChange).toHaveBeenCalledWith({type: 'ready'})
159
- })
160
- await waitFor(() => {
161
- if (editorRef.current) {
162
- PortableTextEditor.focus(editorRef.current)
163
- expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
164
- {
165
- _key: 'abc',
166
- _type: 'myTestBlockType',
167
- children: [
168
- {
169
- _key: 'k3',
170
- _type: 'span',
171
- text: '',
172
- marks: [],
173
- },
174
- ],
175
- markDefs: [],
176
- style: 'normal',
177
- },
178
- {
179
- _key: 'def',
180
- _type: 'myTestBlockType',
181
- children: [
182
- {
183
- _key: 'k5',
184
- _type: 'span',
185
- text: '',
186
- marks: [],
187
- },
188
- ],
189
- markDefs: [],
190
- style: 'normal',
191
- },
192
- ])
193
- }
194
- })
195
- })
196
-
197
- it('removes orphaned marks', async () => {
198
- const editorRef: RefObject<PortableTextEditor | null> = createRef()
199
- const initialValue = [
200
- {
201
- _key: 'abc',
202
- _type: 'myTestBlockType',
203
- style: 'normal',
204
- markDefs: [],
205
- children: [
206
- {
207
- _key: 'def',
208
- _type: 'span',
209
- marks: ['ghi'],
210
- text: 'Hello',
211
- },
212
- ],
213
- },
214
- ]
215
-
216
- const onChange = vi.fn()
217
- render(
218
- <PortableTextEditorTester
219
- keyGenerator={createTestKeyGenerator()}
220
- onChange={onChange}
221
- ref={editorRef}
222
- schemaType={schemaType}
223
- value={initialValue}
224
- />,
225
- )
226
- await waitFor(() => {
227
- expect(onChange).toHaveBeenCalledWith({
228
- type: 'value',
229
- value: initialValue,
230
- })
231
- expect(onChange).toHaveBeenCalledWith({type: 'ready'})
232
- })
233
- await waitFor(() => {
234
- if (editorRef.current) {
235
- PortableTextEditor.focus(editorRef.current)
236
- expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
237
- {
238
- _key: 'abc',
239
- _type: 'myTestBlockType',
240
- children: [
241
- {
242
- _key: 'def',
243
- _type: 'span',
244
- text: 'Hello',
245
- marks: [],
246
- },
247
- ],
248
- markDefs: [],
249
- style: 'normal',
250
- },
251
- ])
252
- }
253
- })
254
- })
255
-
256
- it('removes orphaned marksDefs', async () => {
257
- const editorRef: RefObject<PortableTextEditor | null> = createRef()
258
- const initialValue = [
259
- {
260
- _key: 'abc',
261
- _type: 'myTestBlockType',
262
- style: 'normal',
263
- markDefs: [
264
- {
265
- _key: 'ghi',
266
- _type: 'link',
267
- href: 'https://sanity.io',
268
- },
269
- ],
270
- children: [
271
- {
272
- _key: 'def',
273
- _type: 'span',
274
- marks: [],
275
- text: 'Hello',
276
- },
277
- ],
278
- },
279
- ]
280
-
281
- const onChange = vi.fn()
282
- render(
283
- <PortableTextEditorTester
284
- keyGenerator={createTestKeyGenerator()}
285
- onChange={onChange}
286
- ref={editorRef}
287
- schemaType={schemaType}
288
- value={initialValue}
289
- />,
290
- )
291
- await waitFor(() => {
292
- expect(onChange).toHaveBeenCalledWith({
293
- type: 'value',
294
- value: initialValue,
295
- })
296
- expect(onChange).toHaveBeenCalledWith({type: 'ready'})
297
- })
298
- await waitFor(() => {
299
- if (editorRef.current) {
300
- PortableTextEditor.focus(editorRef.current)
301
- expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
302
- {
303
- _key: 'abc',
304
- _type: 'myTestBlockType',
305
- children: [
306
- {
307
- _key: 'def',
308
- _type: 'span',
309
- text: 'Hello',
310
- marks: [],
311
- },
312
- ],
313
- markDefs: [],
314
- style: 'normal',
315
- },
316
- ])
317
- }
318
- })
319
- })
320
-
321
- it('allows empty array of blocks', async () => {
322
- const editorRef: RefObject<PortableTextEditor | null> = createRef()
323
- const initialValue = [] as PortableTextBlock[]
324
-
325
- const onChange = vi.fn()
326
- render(
327
- <PortableTextEditorTester
328
- keyGenerator={createTestKeyGenerator()}
329
- onChange={onChange}
330
- ref={editorRef}
331
- schemaType={schemaType}
332
- value={initialValue}
333
- />,
334
- )
335
- await waitFor(() => {
336
- expect(onChange).toHaveBeenCalledWith({
337
- type: 'value',
338
- value: initialValue,
339
- })
340
- expect(onChange).toHaveBeenCalledWith({type: 'ready'})
341
- })
342
- await waitFor(() => {
343
- if (editorRef.current) {
344
- PortableTextEditor.focus(editorRef.current)
345
- expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
346
- {
347
- _key: 'k2',
348
- _type: 'myTestBlockType',
349
- children: [{_key: 'k3', _type: 'span', marks: [], text: ''}],
350
- markDefs: [],
351
- style: 'normal',
352
- },
353
- ])
354
- }
355
- })
356
- await waitFor(() => {
357
- expect(onChange).toHaveBeenCalledWith({
358
- type: 'value',
359
- value: initialValue,
360
- })
361
- expect(onChange).toHaveBeenCalledWith({type: 'ready'})
362
- })
363
- })
364
- })
@@ -1,134 +0,0 @@
1
- import {useActorRef, useSelector} from '@xstate/react'
2
- import {useEffect} from 'react'
3
- import {debugWithName} from '../../internal-utils/debug'
4
- import {fromSlateValue} from '../../internal-utils/values'
5
- import {KEY_TO_VALUE_ELEMENT} from '../../internal-utils/weakMaps'
6
- import type {PortableTextSlateEditor} from '../../types/editor'
7
- import type {EditorActor} from '../editor-machine'
8
- import {mutationMachine} from '../mutation-machine'
9
- import {syncMachine} from '../sync-machine'
10
-
11
- const debug = debugWithName('component:PortableTextEditor:Synchronizer')
12
-
13
- /**
14
- * @internal
15
- */
16
- export interface SynchronizerProps {
17
- editorActor: EditorActor
18
- slateEditor: PortableTextSlateEditor
19
- }
20
-
21
- /**
22
- * Synchronizes the server value with the editor, and provides various contexts for the editor state.
23
- * @internal
24
- */
25
- export function Synchronizer(props: SynchronizerProps) {
26
- const {editorActor, slateEditor} = props
27
-
28
- const incomingValue = useSelector(
29
- props.editorActor,
30
- (s) => s.context.incomingValue,
31
- )
32
- const readOnly = useSelector(props.editorActor, (s) =>
33
- s.matches({'edit mode': 'read only'}),
34
- )
35
- const syncActorRef = useActorRef(syncMachine, {
36
- input: {
37
- keyGenerator: props.editorActor.getSnapshot().context.keyGenerator,
38
- readOnly: props.editorActor
39
- .getSnapshot()
40
- .matches({'edit mode': 'read only'}),
41
- schema: props.editorActor.getSnapshot().context.schema,
42
- slateEditor,
43
- },
44
- })
45
- const mutationActorRef = useActorRef(mutationMachine, {
46
- input: {
47
- schema: props.editorActor.getSnapshot().context.schema,
48
- slateEditor,
49
- },
50
- })
51
-
52
- useEffect(() => {
53
- const subscription = mutationActorRef.on('*', (event) => {
54
- if (event.type === 'has pending patches') {
55
- syncActorRef.send({type: 'has pending patches'})
56
- }
57
- if (event.type === 'mutation') {
58
- syncActorRef.send({type: 'mutation'})
59
- editorActor.send({
60
- type: 'mutation',
61
- patches: event.patches,
62
- snapshot: event.snapshot,
63
- value: event.snapshot,
64
- })
65
- }
66
- })
67
-
68
- return () => {
69
- subscription.unsubscribe()
70
- }
71
- }, [mutationActorRef, syncActorRef, editorActor])
72
-
73
- useEffect(() => {
74
- const subscription = syncActorRef.on('*', (event) => {
75
- switch (event.type) {
76
- case 'invalid value':
77
- props.editorActor.send({
78
- ...event,
79
- type: 'notify.invalid value',
80
- })
81
- break
82
- case 'value changed':
83
- props.editorActor.send({
84
- ...event,
85
- type: 'notify.value changed',
86
- })
87
- break
88
- case 'patch':
89
- props.editorActor.send({
90
- ...event,
91
- type: 'internal.patch',
92
- value: fromSlateValue(
93
- slateEditor.children,
94
- props.editorActor.getSnapshot().context.schema.block.name,
95
- KEY_TO_VALUE_ELEMENT.get(slateEditor),
96
- ),
97
- })
98
- break
99
-
100
- default:
101
- props.editorActor.send(event)
102
- }
103
- })
104
-
105
- return () => {
106
- subscription.unsubscribe()
107
- }
108
- }, [props.editorActor, slateEditor, syncActorRef])
109
-
110
- useEffect(() => {
111
- syncActorRef.send({type: 'update readOnly', readOnly})
112
- }, [syncActorRef, readOnly])
113
-
114
- useEffect(() => {
115
- debug('Value from props changed, syncing new value')
116
- syncActorRef.send({type: 'update value', value: incomingValue})
117
- }, [syncActorRef, incomingValue])
118
-
119
- // Subscribe to, and handle changes from the editor
120
- useEffect(() => {
121
- debug('Subscribing to patch events')
122
- const sub = editorActor.on('internal.patch', (event) => {
123
- mutationActorRef.send({...event, type: 'patch'})
124
- })
125
- return () => {
126
- debug('Unsubscribing to patch events')
127
- sub.unsubscribe()
128
- }
129
- }, [editorActor, mutationActorRef, slateEditor])
130
-
131
- return null
132
- }
133
-
134
- Synchronizer.displayName = 'Synchronizer'