@portabletext/editor 1.0.18 → 1.0.19

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/editor",
3
- "version": "1.0.18",
3
+ "version": "1.0.19",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -50,6 +50,9 @@
50
50
  "slate-react": "0.108.0"
51
51
  },
52
52
  "devDependencies": {
53
+ "@cucumber/cucumber-expressions": "^17.1.0",
54
+ "@cucumber/gherkin": "^29.0.0",
55
+ "@cucumber/messages": "^26.0.0",
53
56
  "@jest/globals": "^29.7.0",
54
57
  "@jest/types": "^29.6.3",
55
58
  "@playwright/test": "1.46.1",
@@ -100,6 +103,7 @@
100
103
  "react-dom": "^18.3.1",
101
104
  "rxjs": "^7.8.1",
102
105
  "styled-components": "^6.1.12",
106
+ "ts-node": "^10.9.2",
103
107
  "tsx": "^4.17.0",
104
108
  "typescript": "5.5.4",
105
109
  "vite": "^5.4.2"
@@ -128,8 +132,8 @@
128
132
  "dev:e2e-server": "cd ./e2e-tests/ && tsx serve",
129
133
  "lint:fix": "eslint . --fix",
130
134
  "test": "jest",
131
- "test:e2e": "jest --config=e2e-tests/e2e.config.cjs",
132
- "test:e2e:watch": "jest --config=e2e-tests/e2e.config.cjs --watch",
135
+ "test:e2e": "jest --config=e2e-tests/e2e.config.ts",
136
+ "test:e2e:watch": "jest --config=e2e-tests/e2e.config.ts --watch",
133
137
  "test:watch": "jest --watch"
134
138
  }
135
139
  }
@@ -13,249 +13,6 @@ import {PortableTextEditor} from '../../PortableTextEditor'
13
13
 
14
14
  describe('plugin:withPortableTextMarksModel', () => {
15
15
  describe('normalization', () => {
16
- it('merges adjacent spans correctly when removing annotations', async () => {
17
- const editorRef: RefObject<PortableTextEditor> = createRef()
18
- const initialValue = [
19
- {
20
- _key: '5fc57af23597',
21
- _type: 'myTestBlockType',
22
- children: [
23
- {
24
- _key: 'be1c67c6971a',
25
- _type: 'span',
26
- marks: [],
27
- text: 'This is a ',
28
- },
29
- {
30
- _key: '11c8c9f783a8',
31
- _type: 'span',
32
- marks: ['fde1fd54b544'],
33
- text: 'link',
34
- },
35
- {
36
- _key: '576c748e0cd2',
37
- _type: 'span',
38
- marks: [],
39
- text: ', this is ',
40
- },
41
- {
42
- _key: 'f3d73d3833bf',
43
- _type: 'span',
44
- marks: ['7b6d3d5de30c'],
45
- text: 'another',
46
- },
47
- {
48
- _key: '73b01f13c2ec',
49
- _type: 'span',
50
- marks: [],
51
- text: ', and this is ',
52
- },
53
- {
54
- _key: '13eb0d467c82',
55
- _type: 'span',
56
- marks: ['93a1d24eade0'],
57
- text: 'a third',
58
- },
59
- ],
60
- markDefs: [
61
- {
62
- _key: 'fde1fd54b544',
63
- _type: 'link',
64
- url: '1',
65
- },
66
- {
67
- _key: '7b6d3d5de30c',
68
- _type: 'link',
69
- url: '2',
70
- },
71
- {
72
- _key: '93a1d24eade0',
73
- _type: 'link',
74
- url: '3',
75
- },
76
- ],
77
- style: 'normal',
78
- },
79
- ]
80
-
81
- const onChange = jest.fn()
82
-
83
- render(
84
- <PortableTextEditorTester
85
- onChange={onChange}
86
- ref={editorRef}
87
- schemaType={schemaType}
88
- value={initialValue}
89
- />,
90
- )
91
-
92
- await waitFor(() => {
93
- if (editorRef.current) {
94
- expect(onChange).toHaveBeenCalledWith({type: 'value', value: initialValue})
95
- expect(onChange).toHaveBeenCalledWith({type: 'ready'})
96
- }
97
- })
98
-
99
- await waitFor(() => {
100
- if (editorRef.current) {
101
- PortableTextEditor.focus(editorRef.current)
102
- PortableTextEditor.select(editorRef.current, {
103
- focus: {path: [{_key: '5fc57af23597'}, 'children', {_key: '11c8c9f783a8'}], offset: 4},
104
- anchor: {path: [{_key: '5fc57af23597'}, 'children', {_key: '11c8c9f783a8'}], offset: 0},
105
- })
106
- // eslint-disable-next-line max-nested-callbacks
107
- const linkType = editorRef.current.schemaTypes.annotations.find((a) => a.name === 'link')
108
- if (!linkType) {
109
- throw new Error('No link type found')
110
- }
111
- PortableTextEditor.removeAnnotation(editorRef.current, linkType)
112
- expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
113
- {
114
- _key: '5fc57af23597',
115
- _type: 'myTestBlockType',
116
- children: [
117
- {
118
- _key: 'be1c67c6971a',
119
- _type: 'span',
120
- marks: [],
121
- text: 'This is a link, this is ',
122
- },
123
- {
124
- _key: 'f3d73d3833bf',
125
- _type: 'span',
126
- marks: ['7b6d3d5de30c'],
127
- text: 'another',
128
- },
129
- {
130
- _key: '73b01f13c2ec',
131
- _type: 'span',
132
- marks: [],
133
- text: ', and this is ',
134
- },
135
- {
136
- _key: '13eb0d467c82',
137
- _type: 'span',
138
- marks: ['93a1d24eade0'],
139
- text: 'a third',
140
- },
141
- ],
142
- markDefs: [
143
- {
144
- _key: '7b6d3d5de30c',
145
- _type: 'link',
146
- url: '2',
147
- },
148
- {
149
- _key: '93a1d24eade0',
150
- _type: 'link',
151
- url: '3',
152
- },
153
- ],
154
- style: 'normal',
155
- },
156
- ])
157
- }
158
- })
159
- })
160
-
161
- it('splits correctly when adding marks', async () => {
162
- const editorRef: RefObject<PortableTextEditor> = createRef()
163
- const initialValue = [
164
- {
165
- _key: 'a',
166
- _type: 'myTestBlockType',
167
- children: [
168
- {
169
- _key: 'a1',
170
- _type: 'span',
171
- marks: [],
172
- text: '123',
173
- },
174
- ],
175
- markDefs: [],
176
- style: 'normal',
177
- },
178
- {
179
- _key: 'b',
180
- _type: 'myTestBlockType',
181
- children: [
182
- {
183
- _key: 'b1',
184
- _type: 'span',
185
- marks: [],
186
- text: '123',
187
- },
188
- ],
189
- markDefs: [],
190
- style: 'normal',
191
- },
192
- ]
193
- const onChange = jest.fn()
194
- await waitFor(() => {
195
- render(
196
- <PortableTextEditorTester
197
- onChange={onChange}
198
- ref={editorRef}
199
- schemaType={schemaType}
200
- value={initialValue}
201
- />,
202
- )
203
- })
204
- await waitFor(() => {
205
- if (editorRef.current) {
206
- const editor = editorRef.current
207
- PortableTextEditor.focus(editor)
208
- PortableTextEditor.select(editor, {
209
- focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 0},
210
- anchor: {path: [{_key: 'b'}, 'children', {_key: 'b1'}], offset: 1},
211
- })
212
- PortableTextEditor.toggleMark(editor, 'strong')
213
- const value = PortableTextEditor.getValue(editor)
214
- expect(value).toMatchInlineSnapshot(`
215
- Array [
216
- Object {
217
- "_key": "a",
218
- "_type": "myTestBlockType",
219
- "children": Array [
220
- Object {
221
- "_key": "a1",
222
- "_type": "span",
223
- "marks": Array [
224
- "strong",
225
- ],
226
- "text": "123",
227
- },
228
- ],
229
- "markDefs": Array [],
230
- "style": "normal",
231
- },
232
- Object {
233
- "_key": "b",
234
- "_type": "myTestBlockType",
235
- "children": Array [
236
- Object {
237
- "_key": "b1",
238
- "_type": "span",
239
- "marks": Array [
240
- "strong",
241
- ],
242
- "text": "1",
243
- },
244
- Object {
245
- "_key": "1",
246
- "_type": "span",
247
- "marks": Array [],
248
- "text": "23",
249
- },
250
- ],
251
- "markDefs": Array [],
252
- "style": "normal",
253
- },
254
- ]
255
- `)
256
- }
257
- })
258
- })
259
16
  it('merges children correctly when toggling marks in various ranges', async () => {
260
17
  const editorRef: RefObject<PortableTextEditor> = createRef()
261
18
  const initialValue = [
@@ -894,242 +651,6 @@ Array [
894
651
  })
895
652
 
896
653
  describe('removing annotations', () => {
897
- it('removes the markDefs if the annotation is no longer in use', async () => {
898
- const editorRef: RefObject<PortableTextEditor> = createRef()
899
- const initialValue = [
900
- {
901
- _key: '5fc57af23597',
902
- _type: 'myTestBlockType',
903
- children: [
904
- {
905
- _key: 'be1c67c6971a',
906
- _type: 'span',
907
- marks: ['fde1fd54b544'],
908
- text: 'This is a link',
909
- },
910
- ],
911
- markDefs: [
912
- {
913
- _key: 'fde1fd54b544',
914
- _type: 'link',
915
- url: '1',
916
- },
917
- ],
918
- style: 'normal',
919
- },
920
- ]
921
- const onChange = jest.fn()
922
- await waitFor(() => {
923
- render(
924
- <PortableTextEditorTester
925
- onChange={onChange}
926
- ref={editorRef}
927
- schemaType={schemaType}
928
- value={initialValue}
929
- />,
930
- )
931
- })
932
-
933
- await waitFor(() => {
934
- if (editorRef.current) {
935
- PortableTextEditor.focus(editorRef.current)
936
- PortableTextEditor.select(editorRef.current, {
937
- focus: {path: [{_key: '5fc57af23597'}, 'children', {_key: 'be1c67c6971a'}], offset: 14},
938
- anchor: {path: [{_key: '5fc57af23597'}, 'children', {_key: 'be1c67c6971a'}], offset: 0},
939
- })
940
- // // eslint-disable-next-line max-nested-callbacks
941
- const linkType = editorRef.current.schemaTypes.annotations.find((a) => a.name === 'link')
942
- if (!linkType) {
943
- throw new Error('No link type found')
944
- }
945
- PortableTextEditor.removeAnnotation(editorRef.current, linkType)
946
- expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
947
- {
948
- _key: '5fc57af23597',
949
- _type: 'myTestBlockType',
950
- children: [
951
- {
952
- _key: 'be1c67c6971a',
953
- _type: 'span',
954
- marks: [],
955
- text: 'This is a link',
956
- },
957
- ],
958
- markDefs: [],
959
- style: 'normal',
960
- },
961
- ])
962
- }
963
- })
964
- })
965
- it('preserves the markDefs if the annotation will continue in use', async () => {
966
- const editorRef: RefObject<PortableTextEditor> = createRef()
967
- const initialValue = [
968
- {
969
- _key: '5fc57af23597',
970
- _type: 'myTestBlockType',
971
- children: [
972
- {
973
- _key: 'be1c67c6971a',
974
- _type: 'span',
975
- marks: ['fde1fd54b544'],
976
- text: 'This is a link',
977
- },
978
- ],
979
- markDefs: [
980
- {
981
- _key: 'fde1fd54b544',
982
- _type: 'link',
983
- url: '1',
984
- },
985
- ],
986
- style: 'normal',
987
- },
988
- ]
989
- const onChange = jest.fn()
990
- await waitFor(() => {
991
- render(
992
- <PortableTextEditorTester
993
- onChange={onChange}
994
- ref={editorRef}
995
- schemaType={schemaType}
996
- value={initialValue}
997
- />,
998
- )
999
- })
1000
-
1001
- await waitFor(() => {
1002
- if (editorRef.current) {
1003
- PortableTextEditor.focus(editorRef.current)
1004
- PortableTextEditor.select(editorRef.current, {
1005
- focus: {path: [{_key: '5fc57af23597'}, 'children', {_key: 'be1c67c6971a'}], offset: 10},
1006
- anchor: {
1007
- path: [{_key: '5fc57af23597'}, 'children', {_key: 'be1c67c6971a'}],
1008
- offset: 0,
1009
- },
1010
- })
1011
- // // eslint-disable-next-line max-nested-callbacks
1012
- const linkType = editorRef.current.schemaTypes.annotations.find((a) => a.name === 'link')
1013
- if (!linkType) {
1014
- throw new Error('No link type found')
1015
- }
1016
- PortableTextEditor.removeAnnotation(editorRef.current, linkType)
1017
- expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
1018
- {
1019
- _key: '5fc57af23597',
1020
- _type: 'myTestBlockType',
1021
- children: [
1022
- {
1023
- _key: 'be1c67c6971a',
1024
- _type: 'span',
1025
- marks: [],
1026
- text: 'This is a ',
1027
- },
1028
- {
1029
- _key: '1',
1030
- marks: ['fde1fd54b544'],
1031
- _type: 'span',
1032
- text: 'link',
1033
- },
1034
- ],
1035
- markDefs: [
1036
- {
1037
- _key: 'fde1fd54b544',
1038
- _type: 'link',
1039
- url: '1',
1040
- },
1041
- ],
1042
- style: 'normal',
1043
- },
1044
- ])
1045
- }
1046
- })
1047
- })
1048
- it('removes the mark from the correct place', async () => {
1049
- const editorRef: RefObject<PortableTextEditor> = createRef()
1050
- const initialValue = [
1051
- {
1052
- _key: '5fc57af23597',
1053
- _type: 'myTestBlockType',
1054
- children: [
1055
- {
1056
- _key: 'be1c67c6971a',
1057
- _type: 'span',
1058
- marks: ['fde1fd54b544'],
1059
- text: 'This is a link',
1060
- },
1061
- ],
1062
- markDefs: [
1063
- {
1064
- _key: 'fde1fd54b544',
1065
- _type: 'link',
1066
- url: '1',
1067
- },
1068
- ],
1069
- style: 'normal',
1070
- },
1071
- ]
1072
- const onChange = jest.fn()
1073
- await waitFor(() => {
1074
- render(
1075
- <PortableTextEditorTester
1076
- onChange={onChange}
1077
- ref={editorRef}
1078
- schemaType={schemaType}
1079
- value={initialValue}
1080
- />,
1081
- )
1082
- })
1083
-
1084
- await waitFor(() => {
1085
- if (editorRef.current) {
1086
- PortableTextEditor.focus(editorRef.current)
1087
- // Selects `a link` from `This is a link`, so the mark should be kept in the first span.
1088
- PortableTextEditor.select(editorRef.current, {
1089
- focus: {path: [{_key: '5fc57af23597'}, 'children', {_key: 'be1c67c6971a'}], offset: 14},
1090
- anchor: {
1091
- path: [{_key: '5fc57af23597'}, 'children', {_key: 'be1c67c6971a'}],
1092
- offset: 8,
1093
- },
1094
- })
1095
-
1096
- // // eslint-disable-next-line max-nested-callbacks
1097
- const linkType = editorRef.current.schemaTypes.annotations.find((a) => a.name === 'link')
1098
- if (!linkType) {
1099
- throw new Error('No link type found')
1100
- }
1101
- PortableTextEditor.removeAnnotation(editorRef.current, linkType)
1102
- expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
1103
- {
1104
- _key: '5fc57af23597',
1105
- _type: 'myTestBlockType',
1106
- children: [
1107
- {
1108
- _key: 'be1c67c6971a',
1109
- _type: 'span',
1110
- marks: ['fde1fd54b544'],
1111
- text: 'This is ',
1112
- },
1113
- {
1114
- _key: '1',
1115
- _type: 'span',
1116
- marks: [],
1117
- text: 'a link',
1118
- },
1119
- ],
1120
- markDefs: [
1121
- {
1122
- _key: 'fde1fd54b544',
1123
- _type: 'link',
1124
- url: '1',
1125
- },
1126
- ],
1127
- style: 'normal',
1128
- },
1129
- ])
1130
- }
1131
- })
1132
- })
1133
654
  it('preserves other marks that apply to the spans', async () => {
1134
655
  const editorRef: RefObject<PortableTextEditor> = createRef()
1135
656
  const initialValue = [
@@ -1274,60 +795,6 @@ Array [
1274
795
  })
1275
796
 
1276
797
  describe('removing nodes', () => {
1277
- it("should insert an empty text block if it's removing the only block", async () => {
1278
- const editorRef: RefObject<PortableTextEditor> = createRef()
1279
- const initialValue = [
1280
- {
1281
- _key: '5fc57af23597',
1282
- _type: 'someObject',
1283
- },
1284
- ]
1285
- const onChange = jest.fn()
1286
- await waitFor(() => {
1287
- render(
1288
- <PortableTextEditorTester
1289
- onChange={onChange}
1290
- ref={editorRef}
1291
- schemaType={schemaType}
1292
- value={initialValue}
1293
- />,
1294
- )
1295
- })
1296
-
1297
- await waitFor(() => {
1298
- if (editorRef.current) {
1299
- PortableTextEditor.focus(editorRef.current)
1300
-
1301
- PortableTextEditor.delete(
1302
- editorRef.current,
1303
- {
1304
- focus: {path: [{_key: '5fc57af23597'}], offset: 0},
1305
- anchor: {path: [{_key: '5fc57af23597'}], offset: 0},
1306
- },
1307
- {mode: 'blocks'},
1308
- )
1309
-
1310
- const value = PortableTextEditor.getValue(editorRef.current)
1311
-
1312
- expect(value).toEqual([
1313
- {
1314
- _type: 'myTestBlockType',
1315
- _key: '3',
1316
- style: 'normal',
1317
- markDefs: [],
1318
- children: [
1319
- {
1320
- _type: 'span',
1321
- _key: '2',
1322
- text: '',
1323
- marks: [],
1324
- },
1325
- ],
1326
- },
1327
- ])
1328
- }
1329
- })
1330
- })
1331
798
  it('should not insert a new block if we have more blocks available', async () => {
1332
799
  const editorRef: RefObject<PortableTextEditor> = createRef()
1333
800
  const initialValue = [
@@ -378,11 +378,20 @@ export function createWithEditableAPI(
378
378
  if (!editor.isTextBlock(block)) {
379
379
  return undefined
380
380
  }
381
+ const [textNode] = Editor.node(editor, originalSelection.focus, {depth: 2})
382
+
383
+ if (!isPortableTextSpan(textNode)) {
384
+ return undefined
385
+ }
386
+
387
+ if (textNode.text === '') {
388
+ return undefined
389
+ }
390
+
381
391
  if (Range.isCollapsed(originalSelection)) {
382
392
  editor.pteExpandToWord()
383
393
  editor.onChange()
384
394
  }
385
- const [textNode] = Editor.node(editor, originalSelection.focus, {depth: 2})
386
395
 
387
396
  // If we still have a selection, add the annotation to the selected text
388
397
  if (editor.selection) {