@contentful/field-editor-rich-text 4.18.0 → 4.19.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.
@@ -0,0 +1,432 @@
1
+ import { BLOCKS, INLINES } from '@contentful/rich-text-types';
2
+ import { block, document, inline, text } from '../nodeFactory';
3
+ import { toSlateDoc } from '../toSlateDoc';
4
+ describe('toSlateDoc', ()=>{
5
+ const cases = [
6
+ {
7
+ title: 'undefined documents',
8
+ input: undefined,
9
+ expected: [
10
+ {
11
+ type: 'paragraph',
12
+ children: [
13
+ {
14
+ text: ''
15
+ }
16
+ ],
17
+ data: {}
18
+ }
19
+ ]
20
+ },
21
+ {
22
+ title: 'empty documents',
23
+ input: document(),
24
+ expected: [
25
+ {
26
+ type: 'paragraph',
27
+ children: [
28
+ {
29
+ text: ''
30
+ }
31
+ ],
32
+ data: {}
33
+ }
34
+ ]
35
+ },
36
+ {
37
+ title: 'empty hyperlinks',
38
+ input: document(block(BLOCKS.PARAGRAPH, {}, inline(INLINES.HYPERLINK), inline(INLINES.HYPERLINK, {}, text('')))),
39
+ expected: [
40
+ {
41
+ type: 'paragraph',
42
+ children: [
43
+ {
44
+ text: ''
45
+ }
46
+ ],
47
+ data: {}
48
+ }
49
+ ]
50
+ },
51
+ {
52
+ title: 'adjacent text nodes with identical marks',
53
+ input: document(block(BLOCKS.PARAGRAPH, {}, text('This '), text(''), text('should be one '), text('text node.'), text(' but this is unique.', [
54
+ {
55
+ type: 'bold'
56
+ }
57
+ ]), text(' and this should be merged ', [
58
+ {
59
+ type: 'italic'
60
+ }
61
+ ]), text('with this.', [
62
+ {
63
+ type: 'italic'
64
+ }
65
+ ]))),
66
+ expected: [
67
+ {
68
+ type: 'paragraph',
69
+ children: [
70
+ {
71
+ text: 'This should be one text node.',
72
+ data: {}
73
+ },
74
+ {
75
+ text: ' but this is unique.',
76
+ bold: true,
77
+ data: {}
78
+ },
79
+ {
80
+ text: ' and this should be merged with this.',
81
+ italic: true,
82
+ data: {}
83
+ }
84
+ ],
85
+ data: {}
86
+ }
87
+ ]
88
+ },
89
+ {
90
+ title: 'inlines without surrounding text',
91
+ input: document(block(BLOCKS.PARAGRAPH, {}, inline(INLINES.EMBEDDED_ENTRY), inline(INLINES.EMBEDDED_RESOURCE))),
92
+ expected: [
93
+ {
94
+ type: 'paragraph',
95
+ children: [
96
+ {
97
+ text: ''
98
+ },
99
+ {
100
+ type: INLINES.EMBEDDED_ENTRY,
101
+ data: {},
102
+ children: [
103
+ {
104
+ text: ''
105
+ }
106
+ ]
107
+ },
108
+ {
109
+ text: ''
110
+ },
111
+ {
112
+ type: INLINES.EMBEDDED_RESOURCE,
113
+ data: {},
114
+ children: [
115
+ {
116
+ text: ''
117
+ }
118
+ ]
119
+ },
120
+ {
121
+ text: ''
122
+ }
123
+ ],
124
+ data: {}
125
+ }
126
+ ]
127
+ },
128
+ {
129
+ title: 'uneven tables',
130
+ input: document(block('table', {}, block('table-row', {}, block('table-header-cell', {}, block('paragraph', {}, text('cell #1')))), block('table-row', {}, block('table-cell', {}, block('paragraph', {}, text('cell #1'))), block('table-cell', {}, block('paragraph', {}, text('cell #2'))), block('table-cell', {}, block('paragraph', {}, text('cell #3')))), block('table-row', {}, block('table-cell', {}, block('paragraph', {}, text('cell #1'))), block('table-cell', {}, block('paragraph', {}, text('cell #2')))))),
131
+ expected: [
132
+ {
133
+ type: 'table',
134
+ data: {},
135
+ children: [
136
+ {
137
+ type: 'table-row',
138
+ data: {},
139
+ children: [
140
+ {
141
+ type: 'table-header-cell',
142
+ data: {},
143
+ children: [
144
+ {
145
+ type: 'paragraph',
146
+ data: {},
147
+ children: [
148
+ {
149
+ text: 'cell #1',
150
+ data: {}
151
+ }
152
+ ]
153
+ }
154
+ ]
155
+ },
156
+ {
157
+ type: 'table-header-cell',
158
+ data: {},
159
+ children: [
160
+ {
161
+ type: 'paragraph',
162
+ data: {},
163
+ children: [
164
+ {
165
+ text: ''
166
+ }
167
+ ]
168
+ }
169
+ ]
170
+ },
171
+ {
172
+ type: 'table-header-cell',
173
+ data: {},
174
+ children: [
175
+ {
176
+ type: 'paragraph',
177
+ data: {},
178
+ children: [
179
+ {
180
+ text: ''
181
+ }
182
+ ]
183
+ }
184
+ ]
185
+ }
186
+ ]
187
+ },
188
+ {
189
+ type: 'table-row',
190
+ data: {},
191
+ children: [
192
+ {
193
+ type: 'table-cell',
194
+ data: {},
195
+ children: [
196
+ {
197
+ type: 'paragraph',
198
+ data: {},
199
+ children: [
200
+ {
201
+ text: 'cell #1',
202
+ data: {}
203
+ }
204
+ ]
205
+ }
206
+ ]
207
+ },
208
+ {
209
+ type: 'table-cell',
210
+ data: {},
211
+ children: [
212
+ {
213
+ type: 'paragraph',
214
+ data: {},
215
+ children: [
216
+ {
217
+ text: 'cell #2',
218
+ data: {}
219
+ }
220
+ ]
221
+ }
222
+ ]
223
+ },
224
+ {
225
+ type: 'table-cell',
226
+ data: {},
227
+ children: [
228
+ {
229
+ type: 'paragraph',
230
+ data: {},
231
+ children: [
232
+ {
233
+ text: 'cell #3',
234
+ data: {}
235
+ }
236
+ ]
237
+ }
238
+ ]
239
+ }
240
+ ]
241
+ },
242
+ {
243
+ type: 'table-row',
244
+ data: {},
245
+ children: [
246
+ {
247
+ type: 'table-cell',
248
+ data: {},
249
+ children: [
250
+ {
251
+ type: 'paragraph',
252
+ data: {},
253
+ children: [
254
+ {
255
+ text: 'cell #1',
256
+ data: {}
257
+ }
258
+ ]
259
+ }
260
+ ]
261
+ },
262
+ {
263
+ type: 'table-cell',
264
+ data: {},
265
+ children: [
266
+ {
267
+ type: 'paragraph',
268
+ data: {},
269
+ children: [
270
+ {
271
+ text: 'cell #2',
272
+ data: {}
273
+ }
274
+ ]
275
+ }
276
+ ]
277
+ },
278
+ {
279
+ type: 'table-cell',
280
+ data: {},
281
+ children: [
282
+ {
283
+ type: 'paragraph',
284
+ data: {},
285
+ children: [
286
+ {
287
+ text: ''
288
+ }
289
+ ]
290
+ }
291
+ ]
292
+ }
293
+ ]
294
+ }
295
+ ]
296
+ },
297
+ {
298
+ type: 'paragraph',
299
+ data: {},
300
+ children: [
301
+ {
302
+ text: ''
303
+ }
304
+ ]
305
+ }
306
+ ]
307
+ },
308
+ {
309
+ title: 'empty blockquotes',
310
+ input: document(block(BLOCKS.QUOTE, {})),
311
+ expected: [
312
+ {
313
+ type: 'blockquote',
314
+ data: {},
315
+ children: [
316
+ {
317
+ type: 'paragraph',
318
+ data: {},
319
+ children: [
320
+ {
321
+ text: ''
322
+ }
323
+ ]
324
+ }
325
+ ]
326
+ },
327
+ {
328
+ type: 'paragraph',
329
+ data: {},
330
+ children: [
331
+ {
332
+ text: ''
333
+ }
334
+ ]
335
+ }
336
+ ]
337
+ },
338
+ {
339
+ title: 'empty list items',
340
+ input: document(block(BLOCKS.LIST_ITEM, {})),
341
+ expected: [
342
+ {
343
+ type: 'list-item',
344
+ data: {},
345
+ children: [
346
+ {
347
+ type: 'paragraph',
348
+ data: {},
349
+ children: [
350
+ {
351
+ text: ''
352
+ }
353
+ ]
354
+ }
355
+ ]
356
+ },
357
+ {
358
+ type: 'paragraph',
359
+ data: {},
360
+ children: [
361
+ {
362
+ text: ''
363
+ }
364
+ ]
365
+ }
366
+ ]
367
+ },
368
+ {
369
+ title: 'empty lists',
370
+ input: document(block(BLOCKS.UL_LIST, {}), block(BLOCKS.OL_LIST, {})),
371
+ expected: [
372
+ {
373
+ type: 'unordered-list',
374
+ data: {},
375
+ children: [
376
+ {
377
+ type: 'list-item',
378
+ data: {},
379
+ children: [
380
+ {
381
+ type: 'paragraph',
382
+ data: {},
383
+ children: [
384
+ {
385
+ text: ''
386
+ }
387
+ ]
388
+ }
389
+ ]
390
+ }
391
+ ]
392
+ },
393
+ {
394
+ type: 'ordered-list',
395
+ data: {},
396
+ children: [
397
+ {
398
+ type: 'list-item',
399
+ data: {},
400
+ children: [
401
+ {
402
+ type: 'paragraph',
403
+ data: {},
404
+ children: [
405
+ {
406
+ text: ''
407
+ }
408
+ ]
409
+ }
410
+ ]
411
+ }
412
+ ]
413
+ },
414
+ {
415
+ type: 'paragraph',
416
+ data: {},
417
+ children: [
418
+ {
419
+ text: ''
420
+ }
421
+ ]
422
+ }
423
+ ]
424
+ }
425
+ ];
426
+ cases.forEach(({ title, input, expected })=>{
427
+ it(`should normalize ${title}`, ()=>{
428
+ const out = toSlateDoc(input);
429
+ expect(out).toEqual(expected);
430
+ });
431
+ });
432
+ });
@@ -0,0 +1,170 @@
1
+ import { INLINES, BLOCKS } from '@contentful/rich-text-types';
2
+ import { Text as TextInterface, Element as ElementInterface } from 'slate';
3
+ import { isText } from '../internal';
4
+ const inlineTypes = new Set(Object.values(INLINES));
5
+ function isEmptyHyperlink(node) {
6
+ if (node.nodeType !== INLINES.HYPERLINK) {
7
+ return false;
8
+ }
9
+ const link = node;
10
+ if (!link.content?.length) {
11
+ return true;
12
+ }
13
+ return link.content.length === 1 && link.content.at(0)?.value.length === 0;
14
+ }
15
+ function maybeFixUnevenTableRows(el) {
16
+ const rows = el.children;
17
+ const rowSize = Math.max(...rows.map((row)=>row.children?.length ?? 0));
18
+ const fixedRows = [];
19
+ for (const row of rows){
20
+ const missingCells = rowSize - row.children.length;
21
+ if (missingCells === 0) {
22
+ fixedRows.push(row);
23
+ continue;
24
+ }
25
+ const cellType = row.children.at(-1)?.type === BLOCKS.TABLE_HEADER_CELL ? BLOCKS.TABLE_HEADER_CELL : BLOCKS.TABLE_CELL;
26
+ const paddedRow = {
27
+ ...row,
28
+ children: [
29
+ ...row.children
30
+ ]
31
+ };
32
+ for(let i = 0; i < missingCells; i++){
33
+ paddedRow.children.push({
34
+ type: cellType,
35
+ data: {},
36
+ children: [
37
+ {
38
+ type: BLOCKS.PARAGRAPH,
39
+ data: {},
40
+ children: [
41
+ {
42
+ text: ''
43
+ }
44
+ ]
45
+ }
46
+ ]
47
+ });
48
+ }
49
+ fixedRows.push(paddedRow);
50
+ }
51
+ return fixedRows;
52
+ }
53
+ function transformText(node) {
54
+ const text = {
55
+ text: node.value,
56
+ data: node.data ?? {}
57
+ };
58
+ for (const mark of node.marks){
59
+ text[mark.type] = true;
60
+ }
61
+ return text;
62
+ }
63
+ function transformNode(node) {
64
+ const el = {
65
+ type: node.nodeType,
66
+ children: [],
67
+ data: node.data ?? {}
68
+ };
69
+ for (const child of node.content){
70
+ const lastChild = el.children.at(-1);
71
+ if (child.nodeType === 'text') {
72
+ const text = transformText(child);
73
+ if (isText(lastChild) && TextInterface.equals(lastChild, text, {
74
+ loose: true
75
+ })) {
76
+ lastChild.text += child.value;
77
+ } else {
78
+ el.children.push(text);
79
+ }
80
+ continue;
81
+ }
82
+ if (isEmptyHyperlink(child)) {
83
+ continue;
84
+ }
85
+ if (inlineTypes.has(child.nodeType) && !isText(lastChild)) {
86
+ el.children.push({
87
+ text: ''
88
+ });
89
+ }
90
+ el.children.push(transformNode(child));
91
+ }
92
+ if (el.children.length === 0) {
93
+ switch(el.type){
94
+ case BLOCKS.QUOTE:
95
+ case BLOCKS.LIST_ITEM:
96
+ el.children.push({
97
+ type: BLOCKS.PARAGRAPH,
98
+ data: {},
99
+ children: [
100
+ {
101
+ text: ''
102
+ }
103
+ ]
104
+ });
105
+ break;
106
+ case BLOCKS.UL_LIST:
107
+ case BLOCKS.OL_LIST:
108
+ el.children.push({
109
+ type: BLOCKS.LIST_ITEM,
110
+ data: {},
111
+ children: [
112
+ {
113
+ type: BLOCKS.PARAGRAPH,
114
+ data: {},
115
+ children: [
116
+ {
117
+ text: ''
118
+ }
119
+ ]
120
+ }
121
+ ]
122
+ });
123
+ break;
124
+ default:
125
+ el.children.push({
126
+ text: ''
127
+ });
128
+ break;
129
+ }
130
+ }
131
+ const lastChildElement = el.children.at(-1);
132
+ if (ElementInterface.isElement(lastChildElement) && inlineTypes.has(lastChildElement.type)) {
133
+ el.children.push({
134
+ text: ''
135
+ });
136
+ }
137
+ if (el.type === BLOCKS.TABLE) {
138
+ el.children = maybeFixUnevenTableRows(el);
139
+ }
140
+ return el;
141
+ }
142
+ export function toSlateDoc(doc) {
143
+ if (!doc || !doc?.content?.length) {
144
+ return [
145
+ {
146
+ type: 'paragraph',
147
+ children: [
148
+ {
149
+ text: ''
150
+ }
151
+ ],
152
+ data: {}
153
+ }
154
+ ];
155
+ }
156
+ const elements = doc.content.map(transformNode);
157
+ const lastElement = elements.at(-1);
158
+ if (lastElement?.type !== BLOCKS.PARAGRAPH) {
159
+ elements.push({
160
+ type: BLOCKS.PARAGRAPH,
161
+ children: [
162
+ {
163
+ text: ''
164
+ }
165
+ ],
166
+ data: {}
167
+ });
168
+ }
169
+ return elements;
170
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import { Document as CfDocument } from '@contentful/rich-text-types';
2
+ import type { Element } from '../internal';
3
+ export declare function toSlateDoc(doc?: CfDocument): Element[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/field-editor-rich-text",
3
- "version": "4.18.0",
3
+ "version": "4.19.0",
4
4
  "source": "./src/index.tsx",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -71,7 +71,8 @@
71
71
  "slate": "0.94.1",
72
72
  "slate-history": "0.100.0",
73
73
  "slate-hyperscript": "0.77.0",
74
- "slate-react": "0.102.0"
74
+ "slate-react": "0.102.0",
75
+ "use-deep-compare": "^1.3.0"
75
76
  },
76
77
  "peerDependencies": {
77
78
  "@lingui/core": "^5.3.0",
@@ -90,5 +91,5 @@
90
91
  "publishConfig": {
91
92
  "registry": "https://npm.pkg.github.com/"
92
93
  },
93
- "gitHead": "67068016b57ce3057ddddfb8dd3a53ef86499a02"
94
+ "gitHead": "cfe222b9d0e4ca9d69e62697c48bbeaca1e52b51"
94
95
  }
@@ -1,51 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(exports, "toSlateValue", {
6
- enumerable: true,
7
- get: function() {
8
- return toSlateValue;
9
- }
10
- });
11
- const _contentfulslatejsadapter = require("@contentful/contentful-slatejs-adapter");
12
- const _richtexttypes = require("@contentful/rich-text-types");
13
- const _Schema = /*#__PURE__*/ _interop_require_default(require("../constants/Schema"));
14
- function _interop_require_default(obj) {
15
- return obj && obj.__esModule ? obj : {
16
- default: obj
17
- };
18
- }
19
- const isTextElement = (node)=>'text' in node;
20
- function sanitizeIncomingSlateDoc(nodes = []) {
21
- return nodes.map((node)=>{
22
- if (isTextElement(node)) {
23
- return node;
24
- }
25
- if (node.children?.length === 0) {
26
- return {
27
- ...node,
28
- children: [
29
- {
30
- text: '',
31
- data: {}
32
- }
33
- ]
34
- };
35
- }
36
- return {
37
- ...node,
38
- children: sanitizeIncomingSlateDoc(node?.children)
39
- };
40
- });
41
- }
42
- const toSlateValue = (doc)=>{
43
- const hasContent = (doc)=>{
44
- return (doc?.content || []).length > 0;
45
- };
46
- const slateDoc = (0, _contentfulslatejsadapter.toSlatejsDocument)({
47
- document: doc && hasContent(doc) ? doc : _richtexttypes.EMPTY_DOCUMENT,
48
- schema: _Schema.default
49
- });
50
- return sanitizeIncomingSlateDoc(slateDoc);
51
- };