@squiz/formatted-text-editor 1.70.0 → 2.0.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/demo/App.tsx +7 -1
  3. package/demo/AppContext.tsx +49 -18
  4. package/demo/resources.json +44 -0
  5. package/demo/sources.json +4 -0
  6. package/lib/Editor/Editor.js +8 -3
  7. package/lib/Editor/EditorContext.d.ts +2 -0
  8. package/lib/Editor/EditorContext.js +3 -0
  9. package/lib/Extensions/Extensions.d.ts +3 -2
  10. package/lib/Extensions/Extensions.js +4 -2
  11. package/lib/Extensions/FetchUrlExtension/FetchUrlExtension.d.ts +2 -4
  12. package/lib/Extensions/FetchUrlExtension/FetchUrlExtension.js +7 -7
  13. package/lib/index.d.ts +2 -0
  14. package/lib/types.d.ts +4 -0
  15. package/lib/ui/Fields/MatrixAsset/MatrixAsset.js +7 -7
  16. package/lib/utils/converters/htmlToSquizNode/htmlToSquizNode.js +2 -9
  17. package/lib/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.js +19 -4
  18. package/lib/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.js +23 -0
  19. package/package.json +11 -10
  20. package/src/Editor/Editor.spec.tsx +14 -4
  21. package/src/Editor/Editor.tsx +9 -3
  22. package/src/Editor/EditorContext.spec.tsx +1 -0
  23. package/src/Editor/EditorContext.ts +5 -0
  24. package/src/EditorToolbar/Tools/Image/Form/ImageForm.spec.tsx +5 -3
  25. package/src/EditorToolbar/Tools/Image/ImageButton.spec.tsx +6 -0
  26. package/src/EditorToolbar/Tools/Link/Form/LinkForm.spec.tsx +19 -7
  27. package/src/EditorToolbar/Tools/Link/LinkButton.spec.tsx +7 -1
  28. package/src/EditorToolbar/Tools/Link/RemoveLinkButton.spec.tsx +3 -3
  29. package/src/Extensions/Extensions.ts +4 -3
  30. package/src/Extensions/FetchUrlExtension/FetchUrlExtension.ts +12 -17
  31. package/src/Extensions/UnsuportedExtension/UnsupportedNodeExtension.spec.ts +1 -1
  32. package/src/index.ts +2 -0
  33. package/src/types.ts +3 -0
  34. package/src/ui/Fields/MatrixAsset/MatrixAsset.spec.tsx +26 -9
  35. package/src/ui/Fields/MatrixAsset/MatrixAsset.tsx +8 -8
  36. package/src/utils/converters/htmlToSquizNode/htmlToSquizNode.ts +6 -16
  37. package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.spec.ts +457 -0
  38. package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.ts +24 -5
  39. package/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.spec.ts +210 -0
  40. package/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.ts +23 -0
  41. package/tests/mockResourceBrowserContext.tsx +48 -12
  42. package/tests/renderWithContext.tsx +31 -3
  43. package/tests/renderWithEditor.tsx +1 -3
  44. package/vite.config.ts +2 -2
@@ -225,6 +225,463 @@ describe('remirrorNodeToSquizNode', () => {
225
225
  expect(result).toEqual(expected);
226
226
  });
227
227
 
228
+ it('should handle tables', async () => {
229
+ const content: RemirrorJSON = {
230
+ type: 'doc',
231
+ content: [
232
+ {
233
+ type: 'table',
234
+ attrs: {
235
+ isControllersInjected: true,
236
+ insertButtonAttrs: null,
237
+ },
238
+ content: [
239
+ {
240
+ type: 'tableRow',
241
+ content: [
242
+ {
243
+ type: 'tableControllerCell',
244
+ attrs: {
245
+ colspan: 1,
246
+ rowspan: 1,
247
+ colwidth: null,
248
+ background: null,
249
+ },
250
+ },
251
+ {
252
+ type: 'tableControllerCell',
253
+ attrs: {
254
+ colspan: 1,
255
+ rowspan: 1,
256
+ colwidth: [100],
257
+ background: null,
258
+ },
259
+ },
260
+ {
261
+ type: 'tableControllerCell',
262
+ attrs: {
263
+ colspan: 1,
264
+ rowspan: 1,
265
+ colwidth: null,
266
+ background: null,
267
+ },
268
+ },
269
+ {
270
+ type: 'tableControllerCell',
271
+ attrs: {
272
+ colspan: 1,
273
+ rowspan: 1,
274
+ colwidth: null,
275
+ background: null,
276
+ },
277
+ },
278
+ ],
279
+ },
280
+ {
281
+ type: 'tableRow',
282
+ content: [
283
+ {
284
+ type: 'tableControllerCell',
285
+ attrs: {
286
+ colspan: 1,
287
+ rowspan: 1,
288
+ colwidth: null,
289
+ background: null,
290
+ },
291
+ },
292
+ {
293
+ type: 'tableCell',
294
+ attrs: {
295
+ colspan: 1,
296
+ rowspan: 1,
297
+ colwidth: [100],
298
+ background: null,
299
+ },
300
+ content: [
301
+ {
302
+ type: 'paragraph',
303
+ attrs: {
304
+ nodeIndent: null,
305
+ nodeTextAlignment: '',
306
+ nodeLineHeight: null,
307
+ style: '',
308
+ },
309
+ content: [
310
+ {
311
+ type: 'text',
312
+ text: 'Col1Row1',
313
+ },
314
+ ],
315
+ },
316
+ ],
317
+ },
318
+ {
319
+ type: 'tableCell',
320
+ attrs: {
321
+ colspan: 1,
322
+ rowspan: 1,
323
+ colwidth: null,
324
+ background: null,
325
+ },
326
+ content: [
327
+ {
328
+ type: 'paragraph',
329
+ attrs: {
330
+ nodeIndent: null,
331
+ nodeTextAlignment: '',
332
+ nodeLineHeight: null,
333
+ style: '',
334
+ },
335
+ content: [
336
+ {
337
+ type: 'text',
338
+ text: 'Col2Row1',
339
+ },
340
+ ],
341
+ },
342
+ ],
343
+ },
344
+ {
345
+ type: 'tableCell',
346
+ attrs: {
347
+ colspan: 1,
348
+ rowspan: 1,
349
+ colwidth: null,
350
+ background: null,
351
+ },
352
+ content: [
353
+ {
354
+ type: 'paragraph',
355
+ attrs: {
356
+ nodeIndent: null,
357
+ nodeTextAlignment: '',
358
+ nodeLineHeight: null,
359
+ style: '',
360
+ },
361
+ content: [
362
+ {
363
+ type: 'text',
364
+ text: 'Col3Row1',
365
+ },
366
+ ],
367
+ },
368
+ ],
369
+ },
370
+ ],
371
+ },
372
+ {
373
+ type: 'tableRow',
374
+ content: [
375
+ {
376
+ type: 'tableControllerCell',
377
+ attrs: {
378
+ colspan: 1,
379
+ rowspan: 1,
380
+ colwidth: null,
381
+ background: null,
382
+ },
383
+ },
384
+ {
385
+ type: 'tableCell',
386
+ attrs: {
387
+ colspan: 1,
388
+ rowspan: 1,
389
+ colwidth: [100],
390
+ background: null,
391
+ },
392
+ content: [
393
+ {
394
+ type: 'paragraph',
395
+ attrs: {
396
+ nodeIndent: null,
397
+ nodeTextAlignment: '',
398
+ nodeLineHeight: null,
399
+ style: '',
400
+ },
401
+ content: [
402
+ {
403
+ type: 'text',
404
+ text: 'Col1Row2',
405
+ },
406
+ ],
407
+ },
408
+ ],
409
+ },
410
+ {
411
+ type: 'tableCell',
412
+ attrs: {
413
+ colspan: 1,
414
+ rowspan: 1,
415
+ colwidth: null,
416
+ background: null,
417
+ },
418
+ content: [
419
+ {
420
+ type: 'paragraph',
421
+ attrs: {
422
+ nodeIndent: null,
423
+ nodeTextAlignment: '',
424
+ nodeLineHeight: null,
425
+ style: '',
426
+ },
427
+ content: [
428
+ {
429
+ type: 'text',
430
+ text: 'Col2Row2',
431
+ },
432
+ ],
433
+ },
434
+ ],
435
+ },
436
+ {
437
+ type: 'tableCell',
438
+ attrs: {
439
+ colspan: 1,
440
+ rowspan: 1,
441
+ colwidth: null,
442
+ background: null,
443
+ },
444
+ content: [
445
+ {
446
+ type: 'paragraph',
447
+ attrs: {
448
+ nodeIndent: null,
449
+ nodeTextAlignment: '',
450
+ nodeLineHeight: null,
451
+ style: '',
452
+ },
453
+ content: [
454
+ {
455
+ type: 'text',
456
+ text: 'Col3Row2',
457
+ },
458
+ ],
459
+ },
460
+ ],
461
+ },
462
+ ],
463
+ },
464
+ ],
465
+ },
466
+ ],
467
+ };
468
+
469
+ const { editor } = await renderWithEditor(null, { content });
470
+
471
+ const expected: FormattedText = [
472
+ {
473
+ type: 'tag',
474
+ tag: 'table',
475
+ children: [
476
+ {
477
+ type: 'tag',
478
+ tag: 'tr',
479
+ children: [
480
+ {
481
+ type: 'tag',
482
+ tag: 'th',
483
+ children: [],
484
+ attributes: {
485
+ colspan: '1',
486
+ rowspan: '1',
487
+ tableControllerCell: 'true',
488
+ },
489
+ },
490
+ {
491
+ type: 'tag',
492
+ tag: 'th',
493
+ children: [],
494
+ attributes: {
495
+ colspan: '1',
496
+ rowspan: '1',
497
+ tableControllerCell: 'true',
498
+ colwidth: '100',
499
+ },
500
+ },
501
+ {
502
+ type: 'tag',
503
+ tag: 'th',
504
+ children: [],
505
+ attributes: {
506
+ colspan: '1',
507
+ rowspan: '1',
508
+ tableControllerCell: 'true',
509
+ },
510
+ },
511
+ {
512
+ type: 'tag',
513
+ tag: 'th',
514
+ children: [],
515
+ attributes: {
516
+ colspan: '1',
517
+ rowspan: '1',
518
+ tableControllerCell: 'true',
519
+ },
520
+ },
521
+ ],
522
+ },
523
+ {
524
+ type: 'tag',
525
+ tag: 'tr',
526
+ children: [
527
+ {
528
+ type: 'tag',
529
+ tag: 'th',
530
+ children: [],
531
+ attributes: {
532
+ colspan: '1',
533
+ rowspan: '1',
534
+ tableControllerCell: 'true',
535
+ },
536
+ },
537
+ {
538
+ type: 'tag',
539
+ tag: 'td',
540
+ children: [
541
+ {
542
+ type: 'tag',
543
+ tag: 'p',
544
+ children: [
545
+ {
546
+ type: 'text',
547
+ value: 'Col1Row1',
548
+ },
549
+ ],
550
+ },
551
+ ],
552
+ attributes: {
553
+ colspan: '1',
554
+ rowspan: '1',
555
+ colwidth: '100',
556
+ },
557
+ },
558
+ {
559
+ type: 'tag',
560
+ tag: 'td',
561
+ children: [
562
+ {
563
+ type: 'tag',
564
+ tag: 'p',
565
+ children: [
566
+ {
567
+ type: 'text',
568
+ value: 'Col2Row1',
569
+ },
570
+ ],
571
+ },
572
+ ],
573
+ attributes: {
574
+ colspan: '1',
575
+ rowspan: '1',
576
+ },
577
+ },
578
+ {
579
+ type: 'tag',
580
+ tag: 'td',
581
+ children: [
582
+ {
583
+ type: 'tag',
584
+ tag: 'p',
585
+ children: [
586
+ {
587
+ type: 'text',
588
+ value: 'Col3Row1',
589
+ },
590
+ ],
591
+ },
592
+ ],
593
+ attributes: {
594
+ colspan: '1',
595
+ rowspan: '1',
596
+ },
597
+ },
598
+ ],
599
+ },
600
+ {
601
+ type: 'tag',
602
+ tag: 'tr',
603
+ children: [
604
+ {
605
+ type: 'tag',
606
+ tag: 'th',
607
+ children: [],
608
+ attributes: {
609
+ colspan: '1',
610
+ rowspan: '1',
611
+ tableControllerCell: 'true',
612
+ },
613
+ },
614
+ {
615
+ type: 'tag',
616
+ tag: 'td',
617
+ children: [
618
+ {
619
+ type: 'tag',
620
+ tag: 'p',
621
+ children: [
622
+ {
623
+ type: 'text',
624
+ value: 'Col1Row2',
625
+ },
626
+ ],
627
+ },
628
+ ],
629
+ attributes: {
630
+ colspan: '1',
631
+ rowspan: '1',
632
+ colwidth: '100',
633
+ },
634
+ },
635
+ {
636
+ type: 'tag',
637
+ tag: 'td',
638
+ children: [
639
+ {
640
+ type: 'tag',
641
+ tag: 'p',
642
+ children: [
643
+ {
644
+ type: 'text',
645
+ value: 'Col2Row2',
646
+ },
647
+ ],
648
+ },
649
+ ],
650
+ attributes: {
651
+ colspan: '1',
652
+ rowspan: '1',
653
+ },
654
+ },
655
+ {
656
+ type: 'tag',
657
+ tag: 'td',
658
+ children: [
659
+ {
660
+ type: 'tag',
661
+ tag: 'p',
662
+ children: [
663
+ {
664
+ type: 'text',
665
+ value: 'Col3Row2',
666
+ },
667
+ ],
668
+ },
669
+ ],
670
+ attributes: {
671
+ colspan: '1',
672
+ rowspan: '1',
673
+ },
674
+ },
675
+ ],
676
+ },
677
+ ],
678
+ },
679
+ ];
680
+
681
+ const result = remirrorNodeToSquizNode(editor.doc);
682
+ expect(result).toEqual(expected);
683
+ });
684
+
228
685
  it.each([1, 2, 3, 4, 5, 6])('should handle heading %s formatting', async (level) => {
229
686
  const content: RemirrorJSON = {
230
687
  type: 'doc',
@@ -80,7 +80,7 @@ const resolveFontOptions = (node: ProsemirrorNode): FormattedNodeFontProperties
80
80
  return fontOptions;
81
81
  };
82
82
 
83
- const transformAttributes = (attributes: Attrs): Record<string, string> => {
83
+ const transformAttributes = (attributes: Attrs, nodeType?: string): Record<string, string> => {
84
84
  const transformed: Record<string, string> = {};
85
85
 
86
86
  Object.keys(attributes).forEach((key) => {
@@ -90,6 +90,19 @@ const transformAttributes = (attributes: Attrs): Record<string, string> => {
90
90
  }
91
91
  });
92
92
 
93
+ // We assign an attribute here for table controller cells as we need to differentiate
94
+ // between them and regular table headers (th)
95
+ if (nodeType === NodeName.TableControllerCell) {
96
+ transformed[nodeType] = 'true';
97
+ }
98
+
99
+ // Another check here for more specific attributes for tables (column widths)
100
+ if (nodeType === NodeName.TableControllerCell || nodeType === NodeName.tableCell) {
101
+ if (Array.isArray(attributes.colwidth) && attributes.colwidth[0]) {
102
+ transformed.colwidth = String(attributes.colwidth[0]);
103
+ }
104
+ }
105
+
93
106
  return transformed;
94
107
  };
95
108
 
@@ -106,10 +119,16 @@ const transformFragment = (fragment: Fragment): FormattedText => {
106
119
  const transformNode = (node: ProsemirrorNode): FormattedNode => {
107
120
  const formattingOptions = undefinedIfEmpty(resolveFormattingOptions(node));
108
121
  const font = undefinedIfEmpty(resolveFontOptions(node));
109
- const attributes =
110
- node.type.name === NodeName.Image || node.type.name === NodeName.CodeBlock
111
- ? transformAttributes(node.attrs)
112
- : undefined;
122
+ let attributes;
123
+
124
+ if (
125
+ node.type.name === NodeName.Image ||
126
+ node.type.name === NodeName.CodeBlock ||
127
+ node.type.name === NodeName.TableControllerCell ||
128
+ node.type.name === NodeName.tableCell
129
+ ) {
130
+ attributes = transformAttributes(node.attrs, node.type.name);
131
+ }
113
132
 
114
133
  let transformedNode: FormattedNode = { type: 'text', value: node.text || '' };
115
134