@squiz/formatted-text-editor 1.69.0 → 1.71.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.
@@ -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