@portabletext/editor 1.0.18 → 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 +1164 -410
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +1164 -410
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +1164 -410
- package/lib/index.mjs.map +1 -1
- package/package.json +8 -4
- 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 -550
- 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 -115
- 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 +301 -155
- 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 -10
- package/src/utils/weakMaps.ts +18 -8
- package/src/utils/withChanges.ts +4 -2
- package/src/editor/plugins/__tests__/withHotkeys.test.tsx +0 -212
- package/src/editor/plugins/__tests__/withInsertBreak.test.tsx +0 -220
- package/src/editor/plugins/__tests__/withPlaceholderBlock.test.tsx +0 -133
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import {jest} from '@jest/globals'
|
|
2
2
|
import {Schema} from '@sanity/schema'
|
|
3
3
|
import {defineArrayMember, defineField} from '@sanity/types'
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import {
|
|
5
|
+
forwardRef,
|
|
6
|
+
useCallback,
|
|
7
|
+
useEffect,
|
|
8
|
+
useMemo,
|
|
9
|
+
type ForwardedRef,
|
|
10
|
+
} from 'react'
|
|
6
11
|
import {
|
|
7
12
|
PortableTextEditable,
|
|
8
|
-
type PortableTextEditableProps,
|
|
9
13
|
PortableTextEditor,
|
|
14
|
+
type PortableTextEditableProps,
|
|
10
15
|
type PortableTextEditorProps,
|
|
11
16
|
} from '../../index'
|
|
12
17
|
|
|
@@ -74,42 +79,49 @@ const schema = Schema.compile({
|
|
|
74
79
|
|
|
75
80
|
let key = 0
|
|
76
81
|
|
|
77
|
-
export const PortableTextEditorTester = forwardRef(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
export const PortableTextEditorTester = forwardRef(
|
|
83
|
+
function PortableTextEditorTester(
|
|
84
|
+
props: Partial<
|
|
85
|
+
Omit<PortableTextEditorProps, 'type' | 'onChange' | 'value'>
|
|
86
|
+
> & {
|
|
87
|
+
onChange?: PortableTextEditorProps['onChange']
|
|
88
|
+
rangeDecorations?: PortableTextEditableProps['rangeDecorations']
|
|
89
|
+
renderPlaceholder?: PortableTextEditableProps['renderPlaceholder']
|
|
90
|
+
schemaType: PortableTextEditorProps['schemaType']
|
|
91
|
+
selection?: PortableTextEditableProps['selection']
|
|
92
|
+
value?: PortableTextEditorProps['value']
|
|
93
|
+
},
|
|
94
|
+
ref: ForwardedRef<PortableTextEditor>,
|
|
95
|
+
) {
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
key = 0
|
|
98
|
+
}, [])
|
|
99
|
+
const _keyGenerator = useCallback(() => {
|
|
100
|
+
key++
|
|
101
|
+
return `${key}`
|
|
102
|
+
}, [])
|
|
103
|
+
const onChange = useMemo(
|
|
104
|
+
() => props.onChange || jest.fn(),
|
|
105
|
+
[props.onChange],
|
|
106
|
+
)
|
|
107
|
+
return (
|
|
108
|
+
<PortableTextEditor
|
|
109
|
+
schemaType={props.schemaType}
|
|
110
|
+
onChange={onChange}
|
|
111
|
+
value={props.value || undefined}
|
|
112
|
+
keyGenerator={_keyGenerator}
|
|
113
|
+
ref={ref}
|
|
114
|
+
>
|
|
115
|
+
<PortableTextEditable
|
|
116
|
+
selection={props.selection || undefined}
|
|
117
|
+
rangeDecorations={props.rangeDecorations}
|
|
118
|
+
renderPlaceholder={props.renderPlaceholder}
|
|
119
|
+
aria-describedby="desc_foo"
|
|
120
|
+
/>
|
|
121
|
+
</PortableTextEditor>
|
|
122
|
+
)
|
|
85
123
|
},
|
|
86
|
-
|
|
87
|
-
) {
|
|
88
|
-
useEffect(() => {
|
|
89
|
-
key = 0
|
|
90
|
-
}, [])
|
|
91
|
-
const _keyGenerator = useCallback(() => {
|
|
92
|
-
key++
|
|
93
|
-
return `${key}`
|
|
94
|
-
}, [])
|
|
95
|
-
const onChange = useMemo(() => props.onChange || jest.fn(), [props.onChange])
|
|
96
|
-
return (
|
|
97
|
-
<PortableTextEditor
|
|
98
|
-
schemaType={props.schemaType}
|
|
99
|
-
onChange={onChange}
|
|
100
|
-
value={props.value || undefined}
|
|
101
|
-
keyGenerator={_keyGenerator}
|
|
102
|
-
ref={ref}
|
|
103
|
-
>
|
|
104
|
-
<PortableTextEditable
|
|
105
|
-
selection={props.selection || undefined}
|
|
106
|
-
rangeDecorations={props.rangeDecorations}
|
|
107
|
-
renderPlaceholder={props.renderPlaceholder}
|
|
108
|
-
aria-describedby="desc_foo"
|
|
109
|
-
/>
|
|
110
|
-
</PortableTextEditor>
|
|
111
|
-
)
|
|
112
|
-
})
|
|
124
|
+
)
|
|
113
125
|
|
|
114
126
|
export const schemaType = schema.get('body')
|
|
115
127
|
|
|
@@ -3,7 +3,6 @@ import {describe, expect, it, jest} from '@jest/globals'
|
|
|
3
3
|
import {type PortableTextBlock} from '@sanity/types'
|
|
4
4
|
import {render, waitFor} from '@testing-library/react'
|
|
5
5
|
import {createRef, type ReactNode, type RefObject} from 'react'
|
|
6
|
-
|
|
7
6
|
import {type RangeDecoration} from '../..'
|
|
8
7
|
import {type PortableTextEditor} from '../PortableTextEditor'
|
|
9
8
|
import {PortableTextEditorTester, schemaType} from './PortableTextEditorTester'
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {describe, expect, it, jest} from '@jest/globals'
|
|
2
2
|
import {fireEvent, render, waitFor} from '@testing-library/react'
|
|
3
3
|
import {createRef, type RefObject} from 'react'
|
|
4
|
-
|
|
5
4
|
import {PortableTextEditor} from '../PortableTextEditor'
|
|
6
5
|
import {PortableTextEditorTester, schemaType} from './PortableTextEditorTester'
|
|
7
6
|
import {getEditableElement} from './utils'
|
|
@@ -47,7 +46,10 @@ describe('adds empty text block if its needed', () => {
|
|
|
47
46
|
|
|
48
47
|
await waitFor(() => {
|
|
49
48
|
if (editorRef.current) {
|
|
50
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
49
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
50
|
+
type: 'value',
|
|
51
|
+
value: initialValue,
|
|
52
|
+
})
|
|
51
53
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
52
54
|
}
|
|
53
55
|
})
|
|
@@ -59,7 +61,10 @@ describe('adds empty text block if its needed', () => {
|
|
|
59
61
|
PortableTextEditor.focus(editorRef.current)
|
|
60
62
|
PortableTextEditor.select(editorRef.current, initialSelection)
|
|
61
63
|
fireEvent.click(element)
|
|
62
|
-
expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
|
|
64
|
+
expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
|
|
65
|
+
initialValue[0],
|
|
66
|
+
newBlock,
|
|
67
|
+
])
|
|
63
68
|
}
|
|
64
69
|
})
|
|
65
70
|
})
|
|
@@ -107,7 +112,9 @@ describe('adds empty text block if its needed', () => {
|
|
|
107
112
|
PortableTextEditor.focus(editorRef.current)
|
|
108
113
|
PortableTextEditor.select(editorRef.current, initialSelection)
|
|
109
114
|
fireEvent.click(element)
|
|
110
|
-
expect(PortableTextEditor.getValue(editorRef.current)).toEqual(
|
|
115
|
+
expect(PortableTextEditor.getValue(editorRef.current)).toEqual(
|
|
116
|
+
initialValue,
|
|
117
|
+
)
|
|
111
118
|
}
|
|
112
119
|
})
|
|
113
120
|
})
|
|
@@ -156,7 +163,10 @@ describe('adds empty text block if its needed', () => {
|
|
|
156
163
|
|
|
157
164
|
await waitFor(() => {
|
|
158
165
|
if (editorRef.current) {
|
|
159
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
166
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
167
|
+
type: 'value',
|
|
168
|
+
value: initialValue,
|
|
169
|
+
})
|
|
160
170
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
161
171
|
}
|
|
162
172
|
})
|
|
@@ -168,7 +178,9 @@ describe('adds empty text block if its needed', () => {
|
|
|
168
178
|
PortableTextEditor.focus(editorRef.current)
|
|
169
179
|
PortableTextEditor.select(editorRef.current, initialSelection)
|
|
170
180
|
fireEvent.click(element)
|
|
171
|
-
expect(PortableTextEditor.getValue(editorRef.current)).toEqual(
|
|
181
|
+
expect(PortableTextEditor.getValue(editorRef.current)).toEqual(
|
|
182
|
+
initialValue,
|
|
183
|
+
)
|
|
172
184
|
}
|
|
173
185
|
})
|
|
174
186
|
})
|
|
@@ -217,7 +229,10 @@ describe('adds empty text block if its needed', () => {
|
|
|
217
229
|
|
|
218
230
|
await waitFor(() => {
|
|
219
231
|
if (editorRef.current) {
|
|
220
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
232
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
233
|
+
type: 'value',
|
|
234
|
+
value: initialValue,
|
|
235
|
+
})
|
|
221
236
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
222
237
|
}
|
|
223
238
|
})
|
|
@@ -225,13 +240,17 @@ describe('adds empty text block if its needed', () => {
|
|
|
225
240
|
const element = await getEditableElement(component)
|
|
226
241
|
|
|
227
242
|
const editor = editorRef.current
|
|
228
|
-
const inlineType = editor?.schemaTypes.inlineObjects.find(
|
|
243
|
+
const inlineType = editor?.schemaTypes.inlineObjects.find(
|
|
244
|
+
(t) => t.name === 'someObject',
|
|
245
|
+
)
|
|
229
246
|
await waitFor(async () => {
|
|
230
247
|
if (editor && inlineType && element) {
|
|
231
248
|
PortableTextEditor.focus(editor)
|
|
232
249
|
PortableTextEditor.select(editor, initialSelection)
|
|
233
250
|
fireEvent.click(element)
|
|
234
|
-
expect(PortableTextEditor.getValue(editor)).toEqual(
|
|
251
|
+
expect(PortableTextEditor.getValue(editor)).toEqual(
|
|
252
|
+
initialValue.concat(newBlock),
|
|
253
|
+
)
|
|
235
254
|
}
|
|
236
255
|
})
|
|
237
256
|
})
|
|
@@ -3,14 +3,17 @@ import {Schema} from '@sanity/schema'
|
|
|
3
3
|
import {type PortableTextBlock} from '@sanity/types'
|
|
4
4
|
import {render, waitFor} from '@testing-library/react'
|
|
5
5
|
import {createRef, type RefObject} from 'react'
|
|
6
|
-
|
|
7
6
|
import {type EditorChange, type EditorSelection} from '../../types/editor'
|
|
8
7
|
import {PortableTextEditable} from '../Editable'
|
|
9
8
|
import {PortableTextEditor} from '../PortableTextEditor'
|
|
10
9
|
|
|
11
10
|
const schema = Schema.compile({
|
|
12
11
|
types: [
|
|
13
|
-
{
|
|
12
|
+
{
|
|
13
|
+
name: 'portable-text',
|
|
14
|
+
type: 'array',
|
|
15
|
+
of: [{type: 'block'}, {type: 'image'}],
|
|
16
|
+
},
|
|
14
17
|
{name: 'image', type: 'object'},
|
|
15
18
|
],
|
|
16
19
|
}).get('portable-text')
|
|
@@ -49,7 +52,10 @@ describe(PortableTextEditor.insertBlock.name, () => {
|
|
|
49
52
|
// Given an empty text block
|
|
50
53
|
await waitFor(() => {
|
|
51
54
|
if (editorRef.current) {
|
|
52
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
55
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
56
|
+
type: 'value',
|
|
57
|
+
value: initialValue,
|
|
58
|
+
})
|
|
53
59
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
54
60
|
}
|
|
55
61
|
})
|
|
@@ -94,6 +100,7 @@ describe(PortableTextEditor.insertBlock.name, () => {
|
|
|
94
100
|
marks: [],
|
|
95
101
|
},
|
|
96
102
|
],
|
|
103
|
+
markDefs: [],
|
|
97
104
|
style: 'normal',
|
|
98
105
|
}
|
|
99
106
|
const initialValue: Array<PortableTextBlock> = [nonEmptyTextBlock]
|
|
@@ -114,7 +121,10 @@ describe(PortableTextEditor.insertBlock.name, () => {
|
|
|
114
121
|
// Given an non-empty text block
|
|
115
122
|
await waitFor(() => {
|
|
116
123
|
if (editorRef.current) {
|
|
117
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
124
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
125
|
+
type: 'value',
|
|
126
|
+
value: initialValue,
|
|
127
|
+
})
|
|
118
128
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
119
129
|
}
|
|
120
130
|
})
|
|
@@ -184,7 +194,10 @@ describe(PortableTextEditor.insertBlock.name, () => {
|
|
|
184
194
|
// Given an empty text block followed by an image
|
|
185
195
|
await waitFor(() => {
|
|
186
196
|
if (editorRef.current) {
|
|
187
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
197
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
198
|
+
type: 'value',
|
|
199
|
+
value: initialValue,
|
|
200
|
+
})
|
|
188
201
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
189
202
|
}
|
|
190
203
|
})
|
|
@@ -202,7 +215,10 @@ describe(PortableTextEditor.insertBlock.name, () => {
|
|
|
202
215
|
})
|
|
203
216
|
await waitFor(() => {
|
|
204
217
|
if (editorRef.current) {
|
|
205
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
218
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
219
|
+
type: 'selection',
|
|
220
|
+
selection: initialSelection,
|
|
221
|
+
})
|
|
206
222
|
}
|
|
207
223
|
})
|
|
208
224
|
|
|
@@ -2,7 +2,6 @@ import {describe, expect, it, jest} from '@jest/globals'
|
|
|
2
2
|
import {type PortableTextBlock} from '@sanity/types'
|
|
3
3
|
import {render, waitFor} from '@testing-library/react'
|
|
4
4
|
import {createRef, type RefObject} from 'react'
|
|
5
|
-
|
|
6
5
|
import {PortableTextEditor} from '../PortableTextEditor'
|
|
7
6
|
import {PortableTextEditorTester, schemaType} from './PortableTextEditorTester'
|
|
8
7
|
|
|
@@ -35,7 +34,10 @@ describe('when PTE would display warnings, instead it self solves', () => {
|
|
|
35
34
|
/>,
|
|
36
35
|
)
|
|
37
36
|
await waitFor(() => {
|
|
38
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
37
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
38
|
+
type: 'value',
|
|
39
|
+
value: initialValue,
|
|
40
|
+
})
|
|
39
41
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
40
42
|
})
|
|
41
43
|
await waitFor(() => {
|
|
@@ -60,7 +62,7 @@ describe('when PTE would display warnings, instead it self solves', () => {
|
|
|
60
62
|
})
|
|
61
63
|
})
|
|
62
64
|
|
|
63
|
-
it('
|
|
65
|
+
it('self-solves missing .markDefs', async () => {
|
|
64
66
|
const editorRef: RefObject<PortableTextEditor> = createRef()
|
|
65
67
|
const initialValue = [
|
|
66
68
|
{
|
|
@@ -88,7 +90,10 @@ describe('when PTE would display warnings, instead it self solves', () => {
|
|
|
88
90
|
/>,
|
|
89
91
|
)
|
|
90
92
|
await waitFor(() => {
|
|
91
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
93
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
94
|
+
type: 'value',
|
|
95
|
+
value: initialValue,
|
|
96
|
+
})
|
|
92
97
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
93
98
|
})
|
|
94
99
|
await waitFor(() => {
|
|
@@ -106,6 +111,7 @@ describe('when PTE would display warnings, instead it self solves', () => {
|
|
|
106
111
|
marks: [],
|
|
107
112
|
},
|
|
108
113
|
],
|
|
114
|
+
markDefs: [],
|
|
109
115
|
style: 'normal',
|
|
110
116
|
},
|
|
111
117
|
])
|
|
@@ -141,7 +147,10 @@ describe('when PTE would display warnings, instead it self solves', () => {
|
|
|
141
147
|
/>,
|
|
142
148
|
)
|
|
143
149
|
await waitFor(() => {
|
|
144
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
150
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
151
|
+
type: 'value',
|
|
152
|
+
value: initialValue,
|
|
153
|
+
})
|
|
145
154
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
146
155
|
})
|
|
147
156
|
await waitFor(() => {
|
|
@@ -210,7 +219,10 @@ describe('when PTE would display warnings, instead it self solves', () => {
|
|
|
210
219
|
/>,
|
|
211
220
|
)
|
|
212
221
|
await waitFor(() => {
|
|
213
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
222
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
223
|
+
type: 'value',
|
|
224
|
+
value: initialValue,
|
|
225
|
+
})
|
|
214
226
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
215
227
|
})
|
|
216
228
|
await waitFor(() => {
|
|
@@ -271,7 +283,10 @@ describe('when PTE would display warnings, instead it self solves', () => {
|
|
|
271
283
|
/>,
|
|
272
284
|
)
|
|
273
285
|
await waitFor(() => {
|
|
274
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
286
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
287
|
+
type: 'value',
|
|
288
|
+
value: initialValue,
|
|
289
|
+
})
|
|
275
290
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
276
291
|
})
|
|
277
292
|
await waitFor(() => {
|
|
@@ -297,59 +312,6 @@ describe('when PTE would display warnings, instead it self solves', () => {
|
|
|
297
312
|
})
|
|
298
313
|
})
|
|
299
314
|
|
|
300
|
-
it('allows missing .markDefs', async () => {
|
|
301
|
-
const editorRef: RefObject<PortableTextEditor> = createRef()
|
|
302
|
-
const initialValue = [
|
|
303
|
-
{
|
|
304
|
-
_key: 'abc',
|
|
305
|
-
_type: 'myTestBlockType',
|
|
306
|
-
children: [
|
|
307
|
-
{
|
|
308
|
-
_key: 'def',
|
|
309
|
-
_type: 'span',
|
|
310
|
-
marks: [],
|
|
311
|
-
text: 'No markDefs',
|
|
312
|
-
},
|
|
313
|
-
],
|
|
314
|
-
style: 'normal',
|
|
315
|
-
},
|
|
316
|
-
]
|
|
317
|
-
|
|
318
|
-
const onChange = jest.fn()
|
|
319
|
-
render(
|
|
320
|
-
<PortableTextEditorTester
|
|
321
|
-
onChange={onChange}
|
|
322
|
-
ref={editorRef}
|
|
323
|
-
schemaType={schemaType}
|
|
324
|
-
value={initialValue}
|
|
325
|
-
/>,
|
|
326
|
-
)
|
|
327
|
-
await waitFor(() => {
|
|
328
|
-
expect(onChange).toHaveBeenCalledWith({type: 'value', value: initialValue})
|
|
329
|
-
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
330
|
-
})
|
|
331
|
-
await waitFor(() => {
|
|
332
|
-
if (editorRef.current) {
|
|
333
|
-
PortableTextEditor.focus(editorRef.current)
|
|
334
|
-
expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
|
|
335
|
-
{
|
|
336
|
-
_key: 'abc',
|
|
337
|
-
_type: 'myTestBlockType',
|
|
338
|
-
children: [
|
|
339
|
-
{
|
|
340
|
-
_key: 'def',
|
|
341
|
-
_type: 'span',
|
|
342
|
-
text: 'No markDefs',
|
|
343
|
-
marks: [],
|
|
344
|
-
},
|
|
345
|
-
],
|
|
346
|
-
style: 'normal',
|
|
347
|
-
},
|
|
348
|
-
])
|
|
349
|
-
}
|
|
350
|
-
})
|
|
351
|
-
})
|
|
352
|
-
|
|
353
315
|
it('allows empty array of blocks', async () => {
|
|
354
316
|
const editorRef: RefObject<PortableTextEditor> = createRef()
|
|
355
317
|
const initialValue = [] as PortableTextBlock[]
|
|
@@ -364,7 +326,10 @@ describe('when PTE would display warnings, instead it self solves', () => {
|
|
|
364
326
|
/>,
|
|
365
327
|
)
|
|
366
328
|
await waitFor(() => {
|
|
367
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
329
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
330
|
+
type: 'value',
|
|
331
|
+
value: initialValue,
|
|
332
|
+
})
|
|
368
333
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
369
334
|
})
|
|
370
335
|
await waitFor(() => {
|
|
@@ -382,7 +347,10 @@ describe('when PTE would display warnings, instead it self solves', () => {
|
|
|
382
347
|
}
|
|
383
348
|
})
|
|
384
349
|
await waitFor(() => {
|
|
385
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
350
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
351
|
+
type: 'value',
|
|
352
|
+
value: initialValue,
|
|
353
|
+
})
|
|
386
354
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
387
355
|
})
|
|
388
356
|
})
|
|
@@ -3,7 +3,10 @@ import {fireEvent, type render} from '@testing-library/react'
|
|
|
3
3
|
import {parseHotkey} from 'is-hotkey-esm'
|
|
4
4
|
import {act} from 'react'
|
|
5
5
|
|
|
6
|
-
export async function triggerKeyboardEvent(
|
|
6
|
+
export async function triggerKeyboardEvent(
|
|
7
|
+
hotkey: string,
|
|
8
|
+
element: Element,
|
|
9
|
+
): Promise<void> {
|
|
7
10
|
const eventProps = parseHotkey(hotkey)
|
|
8
11
|
const values = hotkey.split('+')
|
|
9
12
|
|
|
@@ -19,9 +22,13 @@ export async function triggerKeyboardEvent(hotkey: string, element: Element): Pr
|
|
|
19
22
|
)
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
export async function getEditableElement(
|
|
25
|
+
export async function getEditableElement(
|
|
26
|
+
component: ReturnType<typeof render>,
|
|
27
|
+
): Promise<Element> {
|
|
23
28
|
await act(async () => component)
|
|
24
|
-
const element = component.container.querySelector(
|
|
29
|
+
const element = component.container.querySelector(
|
|
30
|
+
'[data-slate-editor="true"]',
|
|
31
|
+
)
|
|
25
32
|
if (!element) {
|
|
26
33
|
throw new Error('Could not find element')
|
|
27
34
|
}
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type DragEvent,
|
|
3
|
-
type MutableRefObject,
|
|
4
|
-
type ReactNode,
|
|
5
2
|
useCallback,
|
|
6
3
|
useEffect,
|
|
7
4
|
useMemo,
|
|
8
5
|
useRef,
|
|
9
6
|
useState,
|
|
7
|
+
type DragEvent,
|
|
8
|
+
type MutableRefObject,
|
|
9
|
+
type ReactNode,
|
|
10
10
|
} from 'react'
|
|
11
|
-
import {Editor, type Element as SlateElement
|
|
11
|
+
import {Editor, Path, Transforms, type Element as SlateElement} from 'slate'
|
|
12
12
|
import {ReactEditor, useSlateStatic} from 'slate-react'
|
|
13
|
-
|
|
14
13
|
import {debugWithName} from '../../utils/debug'
|
|
15
14
|
import {
|
|
16
15
|
IS_DRAGGING,
|
|
@@ -36,17 +35,31 @@ export interface DraggableBlockProps {
|
|
|
36
35
|
* Implements drag and drop functionality on editor block nodes
|
|
37
36
|
* @internal
|
|
38
37
|
*/
|
|
39
|
-
export const DraggableBlock = ({
|
|
38
|
+
export const DraggableBlock = ({
|
|
39
|
+
children,
|
|
40
|
+
element,
|
|
41
|
+
readOnly,
|
|
42
|
+
blockRef,
|
|
43
|
+
}: DraggableBlockProps) => {
|
|
40
44
|
const editor = useSlateStatic()
|
|
41
45
|
const dragGhostRef: MutableRefObject<undefined | HTMLElement> = useRef()
|
|
42
46
|
const [isDragOver, setIsDragOver] = useState(false)
|
|
43
|
-
const isVoid = useMemo(
|
|
44
|
-
|
|
47
|
+
const isVoid = useMemo(
|
|
48
|
+
() => Editor.isVoid(editor, element),
|
|
49
|
+
[editor, element],
|
|
50
|
+
)
|
|
51
|
+
const isInline = useMemo(
|
|
52
|
+
() => Editor.isInline(editor, element),
|
|
53
|
+
[editor, element],
|
|
54
|
+
)
|
|
45
55
|
|
|
46
56
|
const [blockElement, setBlockElement] = useState<HTMLElement | null>(null)
|
|
47
57
|
|
|
48
58
|
useEffect(
|
|
49
|
-
() =>
|
|
59
|
+
() =>
|
|
60
|
+
setBlockElement(
|
|
61
|
+
blockRef ? blockRef.current : ReactEditor.toDOMNode(editor, element),
|
|
62
|
+
),
|
|
50
63
|
[editor, element, blockRef],
|
|
51
64
|
)
|
|
52
65
|
|
|
@@ -122,7 +135,11 @@ export const DraggableBlock = ({children, element, readOnly, blockRef}: Draggabl
|
|
|
122
135
|
)}`,
|
|
123
136
|
)
|
|
124
137
|
}
|
|
125
|
-
if (
|
|
138
|
+
if (
|
|
139
|
+
dragPosition === 'top' &&
|
|
140
|
+
isBefore &&
|
|
141
|
+
targetPath[0] !== editor.children.length - 1
|
|
142
|
+
) {
|
|
126
143
|
const originalPath = targetPath
|
|
127
144
|
targetPath = Path.previous(targetPath)
|
|
128
145
|
debug(
|
|
@@ -201,7 +218,9 @@ export const DraggableBlock = ({children, element, readOnly, blockRef}: Draggabl
|
|
|
201
218
|
// drag ghost by adding a truthy data attribute 'data-pt-drag-ghost-element' to a HTML element.
|
|
202
219
|
if (blockElement && blockElement instanceof HTMLElement) {
|
|
203
220
|
let dragGhost = blockElement.cloneNode(true) as HTMLElement
|
|
204
|
-
const customGhost = dragGhost.querySelector(
|
|
221
|
+
const customGhost = dragGhost.querySelector(
|
|
222
|
+
'[data-pt-drag-ghost-element]',
|
|
223
|
+
)
|
|
205
224
|
if (customGhost) {
|
|
206
225
|
dragGhost = customGhost as HTMLElement
|
|
207
226
|
}
|
|
@@ -232,12 +251,16 @@ export const DraggableBlock = ({children, element, readOnly, blockRef}: Draggabl
|
|
|
232
251
|
isDragOver && editor.children[0] === IS_DRAGGING_ELEMENT_TARGET.get(editor)
|
|
233
252
|
const isDraggingOverLastBlock =
|
|
234
253
|
isDragOver &&
|
|
235
|
-
editor.children[editor.children.length - 1] ===
|
|
254
|
+
editor.children[editor.children.length - 1] ===
|
|
255
|
+
IS_DRAGGING_ELEMENT_TARGET.get(editor)
|
|
236
256
|
const dragPosition = IS_DRAGGING_BLOCK_TARGET_POSITION.get(editor)
|
|
237
257
|
|
|
238
258
|
const isDraggingOverTop =
|
|
239
259
|
isDraggingOverFirstBlock ||
|
|
240
|
-
(isDragOver &&
|
|
260
|
+
(isDragOver &&
|
|
261
|
+
!isDraggingOverFirstBlock &&
|
|
262
|
+
!isDraggingOverLastBlock &&
|
|
263
|
+
dragPosition === 'top')
|
|
241
264
|
const isDraggingOverBottom =
|
|
242
265
|
isDraggingOverLastBlock ||
|
|
243
266
|
(isDragOver &&
|