@portabletext/editor 1.0.19 → 1.1.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.
- package/lib/index.d.mts +140 -66
- package/lib/index.d.ts +140 -66
- package/lib/index.esm.js +1125 -362
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +1125 -362
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +1125 -362
- package/lib/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/editor/Editable.tsx +107 -36
- package/src/editor/PortableTextEditor.tsx +47 -12
- package/src/editor/__tests__/PortableTextEditor.test.tsx +42 -15
- package/src/editor/__tests__/PortableTextEditorTester.tsx +50 -38
- package/src/editor/__tests__/RangeDecorations.test.tsx +0 -1
- package/src/editor/__tests__/handleClick.test.tsx +28 -9
- package/src/editor/__tests__/insert-block.test.tsx +22 -6
- package/src/editor/__tests__/pteWarningsSelfSolving.test.tsx +30 -62
- package/src/editor/__tests__/utils.ts +10 -3
- package/src/editor/components/DraggableBlock.tsx +36 -13
- package/src/editor/components/Element.tsx +59 -17
- package/src/editor/components/Leaf.tsx +106 -68
- package/src/editor/components/SlateContainer.tsx +12 -5
- package/src/editor/components/Synchronizer.tsx +5 -2
- package/src/editor/hooks/usePortableTextEditor.ts +2 -2
- package/src/editor/hooks/usePortableTextEditorSelection.tsx +9 -3
- package/src/editor/hooks/useSyncValue.test.tsx +9 -4
- package/src/editor/hooks/useSyncValue.ts +199 -130
- package/src/editor/nodes/DefaultAnnotation.tsx +6 -3
- package/src/editor/plugins/__tests__/createWithInsertData.test.tsx +25 -7
- package/src/editor/plugins/__tests__/withEditableAPIDelete.test.tsx +26 -9
- package/src/editor/plugins/__tests__/withEditableAPIGetFragment.test.tsx +15 -5
- package/src/editor/plugins/__tests__/withEditableAPIInsert.test.tsx +60 -19
- package/src/editor/plugins/__tests__/withEditableAPISelectionsOverlapping.test.tsx +4 -2
- package/src/editor/plugins/__tests__/withPortableTextLists.test.tsx +4 -2
- package/src/editor/plugins/__tests__/withPortableTextMarkModel.test.tsx +61 -17
- package/src/editor/plugins/__tests__/withPortableTextSelections.test.tsx +6 -3
- package/src/editor/plugins/__tests__/withUndoRedo.test.tsx +30 -13
- package/src/editor/plugins/createWithEditableAPI.ts +354 -124
- package/src/editor/plugins/createWithHotKeys.ts +41 -121
- package/src/editor/plugins/createWithInsertBreak.ts +166 -27
- package/src/editor/plugins/createWithInsertData.ts +60 -23
- package/src/editor/plugins/createWithMaxBlocks.ts +5 -2
- package/src/editor/plugins/createWithObjectKeys.ts +7 -3
- package/src/editor/plugins/createWithPatches.ts +60 -16
- package/src/editor/plugins/createWithPlaceholderBlock.ts +7 -3
- package/src/editor/plugins/createWithPortableTextBlockStyle.ts +17 -7
- package/src/editor/plugins/createWithPortableTextLists.ts +21 -8
- package/src/editor/plugins/createWithPortableTextMarkModel.ts +213 -46
- package/src/editor/plugins/createWithPortableTextSelections.ts +4 -2
- package/src/editor/plugins/createWithSchemaTypes.ts +25 -9
- package/src/editor/plugins/createWithUndoRedo.ts +107 -24
- package/src/editor/plugins/createWithUtils.ts +32 -10
- package/src/editor/plugins/index.ts +31 -10
- package/src/types/editor.ts +44 -15
- package/src/types/options.ts +4 -2
- package/src/types/slate.ts +2 -2
- package/src/utils/__tests__/dmpToOperations.test.ts +38 -13
- package/src/utils/__tests__/operationToPatches.test.ts +3 -2
- package/src/utils/__tests__/patchToOperations.test.ts +15 -4
- package/src/utils/__tests__/ranges.test.ts +8 -3
- package/src/utils/__tests__/valueNormalization.test.tsx +12 -4
- package/src/utils/__tests__/values.test.ts +0 -1
- package/src/utils/applyPatch.ts +71 -20
- package/src/utils/getPortableTextMemberSchemaTypes.ts +30 -15
- package/src/utils/operationToPatches.ts +126 -43
- package/src/utils/paths.ts +24 -7
- package/src/utils/ranges.ts +12 -5
- package/src/utils/selection.ts +19 -7
- package/src/utils/validateValue.ts +118 -45
- package/src/utils/values.ts +31 -9
- package/src/utils/weakMaps.ts +18 -8
- package/src/utils/withChanges.ts +4 -2
|
@@ -2,14 +2,20 @@
|
|
|
2
2
|
import {type PortableTextBlock} from '@sanity/types'
|
|
3
3
|
import {debounce, isEqual} from 'lodash'
|
|
4
4
|
import {useCallback, useMemo, useRef} from 'react'
|
|
5
|
-
import {
|
|
5
|
+
import {Editor, Text, Transforms, type Descendant, type Node} from 'slate'
|
|
6
6
|
import {useSlate} from 'slate-react'
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import {
|
|
8
|
+
type EditorChange,
|
|
9
|
+
type PortableTextSlateEditor,
|
|
10
|
+
} from '../../types/editor'
|
|
9
11
|
import {debugWithName} from '../../utils/debug'
|
|
10
12
|
import {validateValue} from '../../utils/validateValue'
|
|
11
13
|
import {toSlateValue, VOID_CHILD_KEY} from '../../utils/values'
|
|
12
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
isChangingLocally,
|
|
16
|
+
isChangingRemotely,
|
|
17
|
+
withRemoteChanges,
|
|
18
|
+
} from '../../utils/withChanges'
|
|
13
19
|
import {withoutPatching} from '../../utils/withoutPatching'
|
|
14
20
|
import {withoutSaving} from '../plugins/createWithUndoRedo'
|
|
15
21
|
import {type PortableTextEditor} from '../PortableTextEditor'
|
|
@@ -26,7 +32,10 @@ export interface UseSyncValueProps {
|
|
|
26
32
|
readOnly: boolean
|
|
27
33
|
}
|
|
28
34
|
|
|
29
|
-
const CURRENT_VALUE = new WeakMap<
|
|
35
|
+
const CURRENT_VALUE = new WeakMap<
|
|
36
|
+
PortableTextEditor,
|
|
37
|
+
PortableTextBlock[] | undefined
|
|
38
|
+
>()
|
|
30
39
|
|
|
31
40
|
/**
|
|
32
41
|
* Sync value with the editor state
|
|
@@ -42,12 +51,16 @@ const CURRENT_VALUE = new WeakMap<PortableTextEditor, PortableTextBlock[] | unde
|
|
|
42
51
|
*/
|
|
43
52
|
export function useSyncValue(
|
|
44
53
|
props: UseSyncValueProps,
|
|
45
|
-
): (
|
|
54
|
+
): (
|
|
55
|
+
value: PortableTextBlock[] | undefined,
|
|
56
|
+
userCallbackFn?: () => void,
|
|
57
|
+
) => void {
|
|
46
58
|
const {portableTextEditor, readOnly, keyGenerator} = props
|
|
47
59
|
const {change$, schemaTypes} = portableTextEditor
|
|
48
60
|
const previousValue = useRef<PortableTextBlock[] | undefined>()
|
|
49
61
|
const slateEditor = useSlate()
|
|
50
|
-
const updateValueFunctionRef =
|
|
62
|
+
const updateValueFunctionRef =
|
|
63
|
+
useRef<(value: PortableTextBlock[] | undefined) => void>()
|
|
51
64
|
|
|
52
65
|
const updateFromCurrentValue = useCallback(() => {
|
|
53
66
|
const currentValue = CURRENT_VALUE.get(portableTextEditor)
|
|
@@ -61,7 +74,8 @@ export function useSyncValue(
|
|
|
61
74
|
}
|
|
62
75
|
}, [portableTextEditor])
|
|
63
76
|
const updateValueDebounced = useMemo(
|
|
64
|
-
() =>
|
|
77
|
+
() =>
|
|
78
|
+
debounce(updateFromCurrentValue, 1000, {trailing: true, leading: false}),
|
|
65
79
|
[updateFromCurrentValue],
|
|
66
80
|
)
|
|
67
81
|
|
|
@@ -103,7 +117,11 @@ export function useSyncValue(
|
|
|
103
117
|
at: [childrenLength - 1 - index],
|
|
104
118
|
})
|
|
105
119
|
})
|
|
106
|
-
Transforms.insertNodes(
|
|
120
|
+
Transforms.insertNodes(
|
|
121
|
+
slateEditor,
|
|
122
|
+
slateEditor.pteCreateTextBlock({decorators: []}),
|
|
123
|
+
{at: [0]},
|
|
124
|
+
)
|
|
107
125
|
// Add a new selection in the top of the document
|
|
108
126
|
if (hadSelection) {
|
|
109
127
|
Transforms.select(slateEditor, [0, 0])
|
|
@@ -125,7 +143,11 @@ export function useSyncValue(
|
|
|
125
143
|
const childrenLength = slateEditor.children.length
|
|
126
144
|
// Remove blocks that have become superfluous
|
|
127
145
|
if (slateValueFromProps.length < childrenLength) {
|
|
128
|
-
for (
|
|
146
|
+
for (
|
|
147
|
+
let i = childrenLength - 1;
|
|
148
|
+
i > slateValueFromProps.length - 1;
|
|
149
|
+
i--
|
|
150
|
+
) {
|
|
129
151
|
Transforms.removeNodes(slateEditor, {
|
|
130
152
|
at: [i],
|
|
131
153
|
})
|
|
@@ -133,70 +155,102 @@ export function useSyncValue(
|
|
|
133
155
|
isChanged = true
|
|
134
156
|
}
|
|
135
157
|
// Go through all of the blocks and see if they need to be updated
|
|
136
|
-
slateValueFromProps.forEach(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
//
|
|
149
|
-
if (
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
158
|
+
slateValueFromProps.forEach(
|
|
159
|
+
(currentBlock, currentBlockIndex) => {
|
|
160
|
+
const oldBlock = slateEditor.children[currentBlockIndex]
|
|
161
|
+
const hasChanges =
|
|
162
|
+
oldBlock && !isEqual(currentBlock, oldBlock)
|
|
163
|
+
if (hasChanges && isValid) {
|
|
164
|
+
const validationValue = [value[currentBlockIndex]]
|
|
165
|
+
const validation = validateValue(
|
|
166
|
+
validationValue,
|
|
167
|
+
schemaTypes,
|
|
168
|
+
keyGenerator,
|
|
169
|
+
)
|
|
170
|
+
// Resolve validations that can be resolved automatically, without involving the user (but only if the value was changed)
|
|
171
|
+
if (
|
|
172
|
+
!validation.valid &&
|
|
173
|
+
validation.resolution?.autoResolve &&
|
|
174
|
+
validation.resolution?.patches.length > 0
|
|
175
|
+
) {
|
|
176
|
+
// Only apply auto resolution if the value has been populated before and is different from the last one.
|
|
177
|
+
if (
|
|
178
|
+
!readOnly &&
|
|
179
|
+
previousValue.current &&
|
|
180
|
+
previousValue.current !== value
|
|
181
|
+
) {
|
|
182
|
+
// Give a console warning about the fact that it did an auto resolution
|
|
183
|
+
console.warn(
|
|
184
|
+
`${validation.resolution.action} for block with _key '${validationValue[0]._key}'. ${validation.resolution?.description}`,
|
|
185
|
+
)
|
|
186
|
+
validation.resolution.patches.forEach((patch) => {
|
|
187
|
+
change$.next({type: 'patch', patch})
|
|
188
|
+
})
|
|
189
|
+
}
|
|
157
190
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
191
|
+
if (
|
|
192
|
+
validation.valid ||
|
|
193
|
+
validation.resolution?.autoResolve
|
|
194
|
+
) {
|
|
195
|
+
if (oldBlock._key === currentBlock._key) {
|
|
196
|
+
if (debug.enabled)
|
|
197
|
+
debug('Updating block', oldBlock, currentBlock)
|
|
198
|
+
_updateBlock(
|
|
199
|
+
slateEditor,
|
|
200
|
+
currentBlock,
|
|
201
|
+
oldBlock,
|
|
202
|
+
currentBlockIndex,
|
|
203
|
+
)
|
|
204
|
+
} else {
|
|
205
|
+
if (debug.enabled)
|
|
206
|
+
debug('Replacing block', oldBlock, currentBlock)
|
|
207
|
+
_replaceBlock(
|
|
208
|
+
slateEditor,
|
|
209
|
+
currentBlock,
|
|
210
|
+
currentBlockIndex,
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
isChanged = true
|
|
163
214
|
} else {
|
|
164
|
-
|
|
165
|
-
|
|
215
|
+
change$.next({
|
|
216
|
+
type: 'invalidValue',
|
|
217
|
+
resolution: validation.resolution,
|
|
218
|
+
value,
|
|
219
|
+
})
|
|
220
|
+
isValid = false
|
|
166
221
|
}
|
|
167
|
-
isChanged = true
|
|
168
|
-
} else {
|
|
169
|
-
change$.next({
|
|
170
|
-
type: 'invalidValue',
|
|
171
|
-
resolution: validation.resolution,
|
|
172
|
-
value,
|
|
173
|
-
})
|
|
174
|
-
isValid = false
|
|
175
222
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
'Validating and inserting new block in the end of the value',
|
|
183
|
-
currentBlock,
|
|
223
|
+
if (!oldBlock && isValid) {
|
|
224
|
+
const validationValue = [value[currentBlockIndex]]
|
|
225
|
+
const validation = validateValue(
|
|
226
|
+
validationValue,
|
|
227
|
+
schemaTypes,
|
|
228
|
+
keyGenerator,
|
|
184
229
|
)
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
230
|
+
if (debug.enabled)
|
|
231
|
+
debug(
|
|
232
|
+
'Validating and inserting new block in the end of the value',
|
|
233
|
+
currentBlock,
|
|
234
|
+
)
|
|
235
|
+
if (
|
|
236
|
+
validation.valid ||
|
|
237
|
+
validation.resolution?.autoResolve
|
|
238
|
+
) {
|
|
239
|
+
Transforms.insertNodes(slateEditor, currentBlock, {
|
|
240
|
+
at: [currentBlockIndex],
|
|
241
|
+
})
|
|
242
|
+
} else {
|
|
243
|
+
debug('Invalid', validation)
|
|
244
|
+
change$.next({
|
|
245
|
+
type: 'invalidValue',
|
|
246
|
+
resolution: validation.resolution,
|
|
247
|
+
value,
|
|
248
|
+
})
|
|
249
|
+
isValid = false
|
|
250
|
+
}
|
|
197
251
|
}
|
|
198
|
-
}
|
|
199
|
-
|
|
252
|
+
},
|
|
253
|
+
)
|
|
200
254
|
})
|
|
201
255
|
})
|
|
202
256
|
})
|
|
@@ -286,78 +340,93 @@ function _updateBlock(
|
|
|
286
340
|
at: [currentBlockIndex],
|
|
287
341
|
})
|
|
288
342
|
// Text block's need to have their children updated as well (setNode does not target a node's children)
|
|
289
|
-
if (
|
|
343
|
+
if (
|
|
344
|
+
slateEditor.isTextBlock(currentBlock) &&
|
|
345
|
+
slateEditor.isTextBlock(oldBlock)
|
|
346
|
+
) {
|
|
290
347
|
const oldBlockChildrenLength = oldBlock.children.length
|
|
291
348
|
if (currentBlock.children.length < oldBlockChildrenLength) {
|
|
292
349
|
// Remove any children that have become superfluous
|
|
293
|
-
Array.from(
|
|
294
|
-
(
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
}
|
|
302
|
-
},
|
|
303
|
-
)
|
|
304
|
-
}
|
|
305
|
-
currentBlock.children.forEach((currentBlockChild, currentBlockChildIndex) => {
|
|
306
|
-
const oldBlockChild = oldBlock.children[currentBlockChildIndex]
|
|
307
|
-
const isChildChanged = !isEqual(currentBlockChild, oldBlockChild)
|
|
308
|
-
const isTextChanged = !isEqual(currentBlockChild.text, oldBlockChild?.text)
|
|
309
|
-
const path = [currentBlockIndex, currentBlockChildIndex]
|
|
310
|
-
if (isChildChanged) {
|
|
311
|
-
// Update if this is the same child
|
|
312
|
-
if (currentBlockChild._key === oldBlockChild?._key) {
|
|
313
|
-
debug('Updating changed child', currentBlockChild, oldBlockChild)
|
|
314
|
-
Transforms.setNodes(slateEditor, currentBlockChild as Partial<Node>, {
|
|
315
|
-
at: path,
|
|
350
|
+
Array.from(
|
|
351
|
+
Array(oldBlockChildrenLength - currentBlock.children.length),
|
|
352
|
+
).forEach((_, index) => {
|
|
353
|
+
const childIndex = oldBlockChildrenLength - 1 - index
|
|
354
|
+
if (childIndex > 0) {
|
|
355
|
+
debug('Removing child')
|
|
356
|
+
Transforms.removeNodes(slateEditor, {
|
|
357
|
+
at: [currentBlockIndex, childIndex],
|
|
316
358
|
})
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
debug('Updating changed
|
|
359
|
+
}
|
|
360
|
+
})
|
|
361
|
+
}
|
|
362
|
+
currentBlock.children.forEach(
|
|
363
|
+
(currentBlockChild, currentBlockChildIndex) => {
|
|
364
|
+
const oldBlockChild = oldBlock.children[currentBlockChildIndex]
|
|
365
|
+
const isChildChanged = !isEqual(currentBlockChild, oldBlockChild)
|
|
366
|
+
const isTextChanged = !isEqual(
|
|
367
|
+
currentBlockChild.text,
|
|
368
|
+
oldBlockChild?.text,
|
|
369
|
+
)
|
|
370
|
+
const path = [currentBlockIndex, currentBlockChildIndex]
|
|
371
|
+
if (isChildChanged) {
|
|
372
|
+
// Update if this is the same child
|
|
373
|
+
if (currentBlockChild._key === oldBlockChild?._key) {
|
|
374
|
+
debug('Updating changed child', currentBlockChild, oldBlockChild)
|
|
333
375
|
Transforms.setNodes(
|
|
334
376
|
slateEditor,
|
|
335
|
-
|
|
377
|
+
currentBlockChild as Partial<Node>,
|
|
336
378
|
{
|
|
337
|
-
at:
|
|
338
|
-
voids: true,
|
|
379
|
+
at: path,
|
|
339
380
|
},
|
|
340
381
|
)
|
|
382
|
+
const isSpanNode =
|
|
383
|
+
Text.isText(currentBlockChild) &&
|
|
384
|
+
currentBlockChild._type === 'span' &&
|
|
385
|
+
Text.isText(oldBlockChild) &&
|
|
386
|
+
oldBlockChild._type === 'span'
|
|
387
|
+
if (isSpanNode && isTextChanged) {
|
|
388
|
+
Transforms.delete(slateEditor, {
|
|
389
|
+
at: {
|
|
390
|
+
focus: {path, offset: 0},
|
|
391
|
+
anchor: {path, offset: oldBlockChild.text.length},
|
|
392
|
+
},
|
|
393
|
+
})
|
|
394
|
+
Transforms.insertText(slateEditor, currentBlockChild.text, {
|
|
395
|
+
at: path,
|
|
396
|
+
})
|
|
397
|
+
slateEditor.onChange()
|
|
398
|
+
} else if (!isSpanNode) {
|
|
399
|
+
// If it's a inline block, also update the void text node key
|
|
400
|
+
debug('Updating changed inline object child', currentBlockChild)
|
|
401
|
+
Transforms.setNodes(
|
|
402
|
+
slateEditor,
|
|
403
|
+
{_key: VOID_CHILD_KEY},
|
|
404
|
+
{
|
|
405
|
+
at: [...path, 0],
|
|
406
|
+
voids: true,
|
|
407
|
+
},
|
|
408
|
+
)
|
|
409
|
+
}
|
|
410
|
+
// Replace the child if _key's are different
|
|
411
|
+
} else if (oldBlockChild) {
|
|
412
|
+
debug('Replacing child', currentBlockChild)
|
|
413
|
+
Transforms.removeNodes(slateEditor, {
|
|
414
|
+
at: [currentBlockIndex, currentBlockChildIndex],
|
|
415
|
+
})
|
|
416
|
+
Transforms.insertNodes(slateEditor, currentBlockChild as Node, {
|
|
417
|
+
at: [currentBlockIndex, currentBlockChildIndex],
|
|
418
|
+
})
|
|
419
|
+
slateEditor.onChange()
|
|
420
|
+
// Insert it if it didn't exist before
|
|
421
|
+
} else if (!oldBlockChild) {
|
|
422
|
+
debug('Inserting new child', currentBlockChild)
|
|
423
|
+
Transforms.insertNodes(slateEditor, currentBlockChild as Node, {
|
|
424
|
+
at: [currentBlockIndex, currentBlockChildIndex],
|
|
425
|
+
})
|
|
426
|
+
slateEditor.onChange()
|
|
341
427
|
}
|
|
342
|
-
// Replace the child if _key's are different
|
|
343
|
-
} else if (oldBlockChild) {
|
|
344
|
-
debug('Replacing child', currentBlockChild)
|
|
345
|
-
Transforms.removeNodes(slateEditor, {
|
|
346
|
-
at: [currentBlockIndex, currentBlockChildIndex],
|
|
347
|
-
})
|
|
348
|
-
Transforms.insertNodes(slateEditor, currentBlockChild as Node, {
|
|
349
|
-
at: [currentBlockIndex, currentBlockChildIndex],
|
|
350
|
-
})
|
|
351
|
-
slateEditor.onChange()
|
|
352
|
-
// Insert it if it didn't exist before
|
|
353
|
-
} else if (!oldBlockChild) {
|
|
354
|
-
debug('Inserting new child', currentBlockChild)
|
|
355
|
-
Transforms.insertNodes(slateEditor, currentBlockChild as Node, {
|
|
356
|
-
at: [currentBlockIndex, currentBlockChildIndex],
|
|
357
|
-
})
|
|
358
|
-
slateEditor.onChange()
|
|
359
428
|
}
|
|
360
|
-
}
|
|
361
|
-
|
|
429
|
+
},
|
|
430
|
+
)
|
|
362
431
|
}
|
|
363
432
|
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import {type PortableTextObject} from '@sanity/types'
|
|
2
|
-
import {type ReactNode
|
|
2
|
+
import {useCallback, type ReactNode} from 'react'
|
|
3
3
|
|
|
4
4
|
type Props = {
|
|
5
5
|
annotation: PortableTextObject
|
|
6
6
|
children: ReactNode
|
|
7
7
|
}
|
|
8
8
|
export function DefaultAnnotation(props: Props) {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
const handleClick = useCallback(
|
|
10
|
+
// eslint-disable-next-line no-alert
|
|
11
|
+
() => alert(JSON.stringify(props.annotation)),
|
|
12
|
+
[props.annotation],
|
|
13
|
+
)
|
|
11
14
|
return (
|
|
12
15
|
<span style={{color: 'blue'}} onClick={handleClick}>
|
|
13
16
|
{props.children}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {describe, expect, it} from '@jest/globals'
|
|
2
2
|
import {isPortableTextSpan, isPortableTextTextBlock} from '@sanity/types'
|
|
3
3
|
import {type Descendant} from 'slate'
|
|
4
|
-
|
|
5
4
|
import {exportedForTesting} from '../createWithInsertData'
|
|
6
5
|
|
|
7
6
|
const initialValue = [
|
|
@@ -57,10 +56,20 @@ describe('plugin: createWithInsertData _regenerateKeys', () => {
|
|
|
57
56
|
'span',
|
|
58
57
|
{
|
|
59
58
|
annotations: [
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
{
|
|
60
|
+
name: 'color',
|
|
61
|
+
jsonType: 'object',
|
|
62
|
+
fields: [],
|
|
63
|
+
// eslint-disable-next-line camelcase
|
|
64
|
+
__experimental_search: [],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'link',
|
|
68
|
+
jsonType: 'object',
|
|
69
|
+
fields: [],
|
|
70
|
+
// eslint-disable-next-line camelcase
|
|
71
|
+
__experimental_search: [],
|
|
72
|
+
},
|
|
64
73
|
],
|
|
65
74
|
},
|
|
66
75
|
)
|
|
@@ -140,8 +149,17 @@ describe('plugin: createWithInsertData _regenerateKeys', () => {
|
|
|
140
149
|
return `k${keyCursor}`
|
|
141
150
|
},
|
|
142
151
|
'span',
|
|
143
|
-
|
|
144
|
-
|
|
152
|
+
{
|
|
153
|
+
annotations: [
|
|
154
|
+
{
|
|
155
|
+
name: 'color',
|
|
156
|
+
jsonType: 'object',
|
|
157
|
+
fields: [],
|
|
158
|
+
// eslint-disable-next-line camelcase
|
|
159
|
+
__experimental_search: [],
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
},
|
|
145
163
|
)
|
|
146
164
|
|
|
147
165
|
// orphaned children marks are removed later in the normalize function
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {describe, expect, it, jest} from '@jest/globals'
|
|
2
2
|
import {render, waitFor} from '@testing-library/react'
|
|
3
3
|
import {createRef, type RefObject} from 'react'
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import {
|
|
5
|
+
PortableTextEditorTester,
|
|
6
|
+
schemaType,
|
|
7
|
+
} from '../../__tests__/PortableTextEditorTester'
|
|
6
8
|
import {PortableTextEditor} from '../../PortableTextEditor'
|
|
7
9
|
|
|
8
10
|
const initialValue = [
|
|
@@ -56,7 +58,10 @@ describe('plugin:withEditableAPI: .delete()', () => {
|
|
|
56
58
|
|
|
57
59
|
await waitFor(() => {
|
|
58
60
|
if (editorRef.current) {
|
|
59
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
61
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
62
|
+
type: 'value',
|
|
63
|
+
value: initialValue,
|
|
64
|
+
})
|
|
60
65
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
61
66
|
}
|
|
62
67
|
})
|
|
@@ -70,7 +75,8 @@ describe('plugin:withEditableAPI: .delete()', () => {
|
|
|
70
75
|
PortableTextEditor.getSelection(editorRef.current),
|
|
71
76
|
{mode: 'blocks'},
|
|
72
77
|
)
|
|
73
|
-
expect(PortableTextEditor.getValue(editorRef.current))
|
|
78
|
+
expect(PortableTextEditor.getValue(editorRef.current))
|
|
79
|
+
.toMatchInlineSnapshot(`
|
|
74
80
|
Array [
|
|
75
81
|
Object {
|
|
76
82
|
"_key": "a",
|
|
@@ -105,7 +111,10 @@ describe('plugin:withEditableAPI: .delete()', () => {
|
|
|
105
111
|
)
|
|
106
112
|
|
|
107
113
|
await waitFor(() => {
|
|
108
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
114
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
115
|
+
type: 'value',
|
|
116
|
+
value: initialValue,
|
|
117
|
+
})
|
|
109
118
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
110
119
|
})
|
|
111
120
|
|
|
@@ -124,7 +133,8 @@ describe('plugin:withEditableAPI: .delete()', () => {
|
|
|
124
133
|
await waitFor(() => {
|
|
125
134
|
if (editorRef.current) {
|
|
126
135
|
// New keys here confirms that a placeholder block has been created
|
|
127
|
-
expect(PortableTextEditor.getValue(editorRef.current))
|
|
136
|
+
expect(PortableTextEditor.getValue(editorRef.current))
|
|
137
|
+
.toMatchInlineSnapshot(`
|
|
128
138
|
Array [
|
|
129
139
|
Object {
|
|
130
140
|
"_key": "1",
|
|
@@ -161,7 +171,10 @@ describe('plugin:withEditableAPI: .delete()', () => {
|
|
|
161
171
|
|
|
162
172
|
await waitFor(() => {
|
|
163
173
|
if (editorRef.current) {
|
|
164
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
174
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
175
|
+
type: 'value',
|
|
176
|
+
value: initialValue,
|
|
177
|
+
})
|
|
165
178
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
166
179
|
}
|
|
167
180
|
})
|
|
@@ -187,7 +200,8 @@ describe('plugin:withEditableAPI: .delete()', () => {
|
|
|
187
200
|
|
|
188
201
|
await waitFor(() => {
|
|
189
202
|
if (editorRef.current) {
|
|
190
|
-
expect(PortableTextEditor.getValue(editorRef.current))
|
|
203
|
+
expect(PortableTextEditor.getValue(editorRef.current))
|
|
204
|
+
.toMatchInlineSnapshot(`
|
|
191
205
|
Array [
|
|
192
206
|
Object {
|
|
193
207
|
"_key": "a",
|
|
@@ -238,7 +252,10 @@ describe('plugin:withEditableAPI: .delete()', () => {
|
|
|
238
252
|
|
|
239
253
|
await waitFor(() => {
|
|
240
254
|
if (editorRef.current) {
|
|
241
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
255
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
256
|
+
type: 'value',
|
|
257
|
+
value: initialValue,
|
|
258
|
+
})
|
|
242
259
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
243
260
|
}
|
|
244
261
|
})
|
|
@@ -2,8 +2,10 @@ import {describe, expect, it, jest} from '@jest/globals'
|
|
|
2
2
|
import {isPortableTextTextBlock} from '@sanity/types'
|
|
3
3
|
import {render, waitFor} from '@testing-library/react'
|
|
4
4
|
import {createRef, type RefObject} from 'react'
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
import {
|
|
6
|
+
PortableTextEditorTester,
|
|
7
|
+
schemaType,
|
|
8
|
+
} from '../../__tests__/PortableTextEditorTester'
|
|
7
9
|
import {PortableTextEditor} from '../../PortableTextEditor'
|
|
8
10
|
|
|
9
11
|
const initialValue = [
|
|
@@ -67,7 +69,10 @@ describe('plugin:withEditableAPI: .getFragment()', () => {
|
|
|
67
69
|
|
|
68
70
|
await waitFor(() => {
|
|
69
71
|
if (editorRef.current) {
|
|
70
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
72
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
73
|
+
type: 'value',
|
|
74
|
+
value: initialValue,
|
|
75
|
+
})
|
|
71
76
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
72
77
|
}
|
|
73
78
|
})
|
|
@@ -78,7 +83,9 @@ describe('plugin:withEditableAPI: .getFragment()', () => {
|
|
|
78
83
|
PortableTextEditor.select(editorRef.current, initialSelection)
|
|
79
84
|
const fragment = PortableTextEditor.getFragment(editorRef.current)
|
|
80
85
|
expect(
|
|
81
|
-
fragment &&
|
|
86
|
+
fragment &&
|
|
87
|
+
isPortableTextTextBlock(fragment[0]) &&
|
|
88
|
+
fragment[0]?.children[0]?.text,
|
|
82
89
|
).toBe('A')
|
|
83
90
|
}
|
|
84
91
|
})
|
|
@@ -103,7 +110,10 @@ describe('plugin:withEditableAPI: .getFragment()', () => {
|
|
|
103
110
|
|
|
104
111
|
await waitFor(() => {
|
|
105
112
|
if (editorRef.current) {
|
|
106
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
113
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
114
|
+
type: 'value',
|
|
115
|
+
value: initialValue,
|
|
116
|
+
})
|
|
107
117
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
108
118
|
}
|
|
109
119
|
})
|