@squiz/formatted-text-editor 1.40.1-alpha.2 → 1.40.1-alpha.21

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.
@@ -18,6 +18,7 @@ const LinkButton_1 = __importDefault(require("./Tools/Link/LinkButton"));
18
18
  const ImageButton_1 = __importDefault(require("./Tools/Image/ImageButton"));
19
19
  const RemoveLinkButton_1 = __importDefault(require("./Tools/Link/RemoveLinkButton"));
20
20
  const ClearFormattingButton_1 = __importDefault(require("./Tools/ClearFormatting/ClearFormattingButton"));
21
+ const ListButtons_1 = __importDefault(require("./Tools/Lists/ListButtons"));
21
22
  const hooks_1 = require("../hooks");
22
23
  const Toolbar = ({ isVisible }) => {
23
24
  const extensionNames = (0, hooks_1.useExtensionNames)();
@@ -31,6 +32,7 @@ const Toolbar = ({ isVisible }) => {
31
32
  extensionNames.italic && react_1.default.createElement(ItalicButton_1.default, null),
32
33
  extensionNames.underline && react_1.default.createElement(UnderlineButton_1.default, null),
33
34
  extensionNames.nodeFormatting && react_1.default.createElement(TextAlignButtons_1.default, null),
35
+ extensionNames.listItem && react_1.default.createElement(ListButtons_1.default, null),
34
36
  extensionNames.link && (react_1.default.createElement(react_1.default.Fragment, null,
35
37
  react_1.default.createElement(LinkButton_1.default, null),
36
38
  react_1.default.createElement(RemoveLinkButton_1.default, null))),
@@ -0,0 +1,2 @@
1
+ declare const ListButtons: () => JSX.Element;
2
+ export default ListButtons;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const react_1 = __importDefault(require("react"));
7
+ const OrderedListButton_1 = __importDefault(require("./OrderedList/OrderedListButton"));
8
+ const UnorderedListButton_1 = __importDefault(require("./UnorderedList/UnorderedListButton"));
9
+ const react_components_1 = require("@remirror/react-components");
10
+ const ListButtons = () => (react_1.default.createElement(react_1.default.Fragment, null,
11
+ react_1.default.createElement(UnorderedListButton_1.default, null),
12
+ react_1.default.createElement(OrderedListButton_1.default, null),
13
+ react_1.default.createElement(react_components_1.VerticalDivider, null)));
14
+ exports.default = ListButtons;
@@ -0,0 +1,2 @@
1
+ declare const OrderedListButton: () => JSX.Element;
2
+ export default OrderedListButton;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const react_1 = __importDefault(require("react"));
7
+ const react_2 = require("@remirror/react");
8
+ const Button_1 = __importDefault(require("../../../../ui/Button/Button"));
9
+ const FormatListNumbered_1 = __importDefault(require("@mui/icons-material/FormatListNumbered"));
10
+ const OrderedListButton = () => {
11
+ const { toggleOrderedList } = (0, react_2.useCommands)();
12
+ const chain = (0, react_2.useChainedCommands)();
13
+ const active = (0, react_2.useActive)();
14
+ const enabled = toggleOrderedList.enabled();
15
+ const handleSelect = () => {
16
+ if (toggleOrderedList.enabled()) {
17
+ chain.toggleOrderedList().focus().run();
18
+ }
19
+ };
20
+ return (react_1.default.createElement(Button_1.default, { handleOnClick: handleSelect, isDisabled: !enabled, isActive: active.orderedList(), icon: react_1.default.createElement(FormatListNumbered_1.default, null), label: "Ordered list" }));
21
+ };
22
+ exports.default = OrderedListButton;
@@ -0,0 +1,2 @@
1
+ declare const UnorderedListButton: () => JSX.Element;
2
+ export default UnorderedListButton;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const react_1 = __importDefault(require("react"));
7
+ const react_2 = require("@remirror/react");
8
+ const Button_1 = __importDefault(require("../../../../ui/Button/Button"));
9
+ const FormatListBulletedRounded_1 = __importDefault(require("@mui/icons-material/FormatListBulletedRounded"));
10
+ const UnorderedListButton = () => {
11
+ const { toggleBulletList } = (0, react_2.useCommands)();
12
+ const chain = (0, react_2.useChainedCommands)();
13
+ const active = (0, react_2.useActive)();
14
+ const enabled = toggleBulletList.enabled();
15
+ const handleSelect = () => {
16
+ if (toggleBulletList.enabled()) {
17
+ chain.toggleBulletList().focus().run();
18
+ }
19
+ };
20
+ return (react_1.default.createElement(Button_1.default, { handleOnClick: handleSelect, isDisabled: !enabled, isActive: active.bulletList(), icon: react_1.default.createElement(FormatListBulletedRounded_1.default, null), label: "Unordered list" }));
21
+ };
22
+ exports.default = UnorderedListButton;
@@ -48,6 +48,9 @@ const createExtensions = (context) => {
48
48
  }),
49
49
  new UnsupportedNodeExtension_1.UnsupportedNodeExtension(),
50
50
  new ClearFormattingExtension_1.ClearFormattingExtension(),
51
+ new extensions_1.BulletListExtension(),
52
+ new extensions_1.ListItemExtension(),
53
+ new extensions_1.OrderedListExtension(),
51
54
  ];
52
55
  };
53
56
  };
package/lib/index.css CHANGED
@@ -643,6 +643,10 @@
643
643
  color: rgb(7 116 210 / var(--tw-text-opacity));
644
644
  text-decoration: underline;
645
645
  }
646
+ .squiz-fte-scope .remirror-editor p {
647
+ -webkit-margin-after: 0.8rem;
648
+ margin-block-end: 0.8rem;
649
+ }
646
650
  .squiz-fte-scope .remirror-editor h1 {
647
651
  font-size: 1.625rem;
648
652
  font-weight: 600;
@@ -709,6 +713,15 @@
709
713
  padding-top: 0.75rem;
710
714
  padding-bottom: 0.75rem;
711
715
  }
716
+ .squiz-fte-scope .remirror-editor ul,
717
+ .squiz-fte-scope .remirror-editor ol {
718
+ list-style-type: disc;
719
+ padding: 0 0 0 2.5rem;
720
+ margin: 1rem 0;
721
+ }
722
+ .squiz-fte-scope .remirror-editor ol {
723
+ list-style-type: decimal;
724
+ }
712
725
  .squiz-fte-scope .squiz-fte-form-group {
713
726
  display: flex;
714
727
  flex-direction: column;
@@ -19,6 +19,9 @@ const getNodeType = (node) => {
19
19
  img: 'image',
20
20
  pre: 'preformatted',
21
21
  p: 'paragraph',
22
+ ol: 'orderedList',
23
+ li: 'listItem',
24
+ ul: 'bulletList',
22
25
  a: Extensions_1.NodeName.Text,
23
26
  span: Extensions_1.NodeName.Text,
24
27
  code: Extensions_1.NodeName.CodeBlock,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squiz/formatted-text-editor",
3
- "version": "1.40.1-alpha.2",
3
+ "version": "1.40.1-alpha.21",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "scripts": {
@@ -20,8 +20,8 @@
20
20
  "@headlessui/react": "1.7.11",
21
21
  "@mui/icons-material": "5.11.16",
22
22
  "@remirror/react": "2.0.25",
23
- "@squiz/dx-json-schema-lib": "1.40.1-alpha.2",
24
- "@squiz/resource-browser": "1.40.1-alpha.2",
23
+ "@squiz/dx-json-schema-lib": "1.40.1-alpha.21",
24
+ "@squiz/resource-browser": "1.40.1-alpha.21",
25
25
  "clsx": "1.2.1",
26
26
  "react-hook-form": "7.43.2",
27
27
  "react-image-size": "2.0.0",
@@ -75,5 +75,5 @@
75
75
  "volta": {
76
76
  "node": "18.15.0"
77
77
  },
78
- "gitHead": "191b8aed0a7898d6bc6d21596633c0de2c2e7995"
78
+ "gitHead": "9b861f79139fdad14ec243b54c05874c5155dcc7"
79
79
  }
@@ -12,6 +12,7 @@ import LinkButton from './Tools/Link/LinkButton';
12
12
  import ImageButton from './Tools/Image/ImageButton';
13
13
  import RemoveLinkButton from './Tools/Link/RemoveLinkButton';
14
14
  import ClearFormattingButton from './Tools/ClearFormatting/ClearFormattingButton';
15
+ import ListButtons from './Tools/Lists/ListButtons';
15
16
  import { useExtensionNames } from '../hooks';
16
17
 
17
18
  type ToolbarProps = {
@@ -38,6 +39,7 @@ export const Toolbar = ({ isVisible }: ToolbarProps) => {
38
39
  {extensionNames.italic && <ItalicButton />}
39
40
  {extensionNames.underline && <UnderlineButton />}
40
41
  {extensionNames.nodeFormatting && <TextAlignButtons />}
42
+ {extensionNames.listItem && <ListButtons />}
41
43
  {extensionNames.link && (
42
44
  <>
43
45
  <LinkButton />
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import OrderedListButton from './OrderedList/OrderedListButton';
3
+ import UnorderedListButton from './UnorderedList/UnorderedListButton';
4
+ import { VerticalDivider } from '@remirror/react-components';
5
+
6
+ const ListButtons = () => (
7
+ <>
8
+ <UnorderedListButton />
9
+ <OrderedListButton />
10
+ <VerticalDivider />
11
+ </>
12
+ );
13
+
14
+ export default ListButtons;
@@ -0,0 +1,39 @@
1
+ import '@testing-library/jest-dom';
2
+ import { render, fireEvent } from '@testing-library/react';
3
+ import Editor from '../../../../Editor/Editor';
4
+ import React from 'react';
5
+
6
+ describe('Ordered list button', () => {
7
+ it('Renders the ordered list button', () => {
8
+ const { baseElement, getByRole } = render(<Editor />);
9
+ expect(baseElement).toBeTruthy();
10
+ expect(getByRole('button', { name: 'Ordered list' })).toBeTruthy();
11
+ });
12
+
13
+ it('Applies ordered list after selecting ordered list button', () => {
14
+ const { baseElement, getByRole } = render(<Editor />);
15
+ expect(baseElement).toBeTruthy();
16
+
17
+ const OrderedListButton = getByRole('button', { name: 'Ordered list' });
18
+ expect(OrderedListButton).toBeTruthy();
19
+ expect(OrderedListButton.classList.contains('is-active')).toBeFalsy();
20
+
21
+ fireEvent.click(OrderedListButton);
22
+
23
+ setTimeout(() => {
24
+ expect(OrderedListButton.classList.contains('is-active')).toBeTruthy();
25
+ }, 50);
26
+ });
27
+
28
+ it('Should apply order list to editor after clicking button', () => {
29
+ const { baseElement, getByRole } = render(<Editor />);
30
+ expect(baseElement).toBeTruthy();
31
+ expect(baseElement.querySelector('ol')).toBeFalsy();
32
+
33
+ const OrderedListButton = getByRole('button', { name: 'Ordered list' });
34
+ expect(OrderedListButton).toBeTruthy();
35
+ fireEvent.click(OrderedListButton);
36
+
37
+ expect(baseElement.querySelector('ol')).toBeTruthy();
38
+ });
39
+ });
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import { useCommands, useActive, useChainedCommands } from '@remirror/react';
3
+ import { OrderedListExtension } from '@remirror/extension-list';
4
+ import Button from '../../../../ui/Button/Button';
5
+ import FormatListNumberedIcon from '@mui/icons-material/FormatListNumbered';
6
+
7
+ const OrderedListButton = () => {
8
+ const { toggleOrderedList } = useCommands();
9
+ const chain = useChainedCommands();
10
+
11
+ const active = useActive<OrderedListExtension>();
12
+ const enabled = toggleOrderedList.enabled();
13
+ const handleSelect = () => {
14
+ if (toggleOrderedList.enabled()) {
15
+ chain.toggleOrderedList().focus().run();
16
+ }
17
+ };
18
+
19
+ return (
20
+ <Button
21
+ handleOnClick={handleSelect}
22
+ isDisabled={!enabled}
23
+ isActive={active.orderedList()}
24
+ icon={<FormatListNumberedIcon />}
25
+ label="Ordered list"
26
+ />
27
+ );
28
+ };
29
+
30
+ export default OrderedListButton;
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ import '@testing-library/jest-dom';
3
+ import { render, screen, fireEvent } from '@testing-library/react';
4
+ import Editor from '../../../../Editor/Editor';
5
+
6
+ describe('Unordered list button', () => {
7
+ it('Renders the unordered list button', () => {
8
+ render(<Editor />);
9
+ expect(screen.getByRole('button', { name: 'Unordered list' })).toBeInTheDocument();
10
+ });
11
+
12
+ it('Activates the button if clicked', () => {
13
+ render(<Editor />);
14
+ expect(screen.getByRole('button', { name: 'Unordered list' }).classList.contains('squiz-fte-btn')).toBeTruthy();
15
+ const ul = screen.getByRole('button', { name: 'Unordered list' });
16
+ fireEvent.click(ul);
17
+ expect(ul.classList.contains('squiz-fte-btn--is-active')).toBeTruthy();
18
+ });
19
+ });
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import { useCommands, useActive, useChainedCommands } from '@remirror/react';
3
+ import { BulletListExtension } from 'remirror/extensions';
4
+ import Button from '../../../../ui/Button/Button';
5
+ import FormatListBulletedRoundedIcon from '@mui/icons-material/FormatListBulletedRounded';
6
+
7
+ const UnorderedListButton = () => {
8
+ const { toggleBulletList } = useCommands();
9
+ const chain = useChainedCommands();
10
+
11
+ const active = useActive<BulletListExtension>();
12
+ const enabled = toggleBulletList.enabled();
13
+ const handleSelect = () => {
14
+ if (toggleBulletList.enabled()) {
15
+ chain.toggleBulletList().focus().run();
16
+ }
17
+ };
18
+
19
+ return (
20
+ <Button
21
+ handleOnClick={handleSelect}
22
+ isDisabled={!enabled}
23
+ isActive={active.bulletList()}
24
+ icon={<FormatListBulletedRoundedIcon />}
25
+ label="Unordered list"
26
+ />
27
+ );
28
+ };
29
+
30
+ export default UnorderedListButton;
@@ -6,6 +6,9 @@ import {
6
6
  ParagraphExtension,
7
7
  UnderlineExtension,
8
8
  HistoryExtension,
9
+ OrderedListExtension,
10
+ BulletListExtension,
11
+ ListItemExtension,
9
12
  } from 'remirror/extensions';
10
13
  import { Extension } from '@remirror/core';
11
14
  import { PreformattedExtension } from './PreformattedExtension/PreformattedExtension';
@@ -56,6 +59,9 @@ export const createExtensions = (context: EditorContextOptions) => {
56
59
  }),
57
60
  new UnsupportedNodeExtension(),
58
61
  new ClearFormattingExtension(),
62
+ new BulletListExtension(),
63
+ new ListItemExtension(),
64
+ new OrderedListExtension(),
59
65
  ];
60
66
  };
61
67
  };
@@ -1,9 +1,14 @@
1
+ // Add any formatting we want the editor to have for the tools we have added - all default styling has been removed by the reset
1
2
  .remirror-editor {
2
3
  a {
3
4
  @apply text-blue-300;
4
5
  text-decoration: underline;
5
6
  }
6
7
 
8
+ p {
9
+ margin-block-end: 0.8rem;
10
+ }
11
+
7
12
  h1 {
8
13
  font-size: 1.625rem;
9
14
  font-weight: 600;
@@ -71,4 +76,15 @@
71
76
  padding-bottom: 0.75rem;
72
77
  }
73
78
  }
79
+
80
+ ul,
81
+ ol {
82
+ list-style-type: disc;
83
+ padding: 0 0 0 2.5rem;
84
+ margin: 1rem 0;
85
+ }
86
+
87
+ ol {
88
+ list-style-type: decimal;
89
+ }
74
90
  }
@@ -425,4 +425,195 @@ describe('squizNodeToRemirrorNode', () => {
425
425
  });
426
426
  },
427
427
  );
428
+
429
+ it('should handle ordered lists', () => {
430
+ const squizComponentJSON: FormattedText = [
431
+ {
432
+ type: 'tag',
433
+ tag: 'ol',
434
+ children: [
435
+ {
436
+ type: 'tag',
437
+ tag: 'li',
438
+ children: [
439
+ {
440
+ type: 'tag',
441
+ tag: 'p',
442
+ children: [
443
+ {
444
+ type: 'text',
445
+ value: 'ddd',
446
+ },
447
+ ],
448
+ },
449
+ ],
450
+ },
451
+ ],
452
+ },
453
+ ];
454
+
455
+ const expected: RemirrorJSON = {
456
+ content: [
457
+ {
458
+ attrs: { level: undefined, nodeIndent: null, nodeLineHeight: null, nodeTextAlignment: null, style: '' },
459
+ type: 'orderedList',
460
+ marks: undefined,
461
+ text: undefined,
462
+ content: [
463
+ {
464
+ attrs: { level: undefined, nodeIndent: null, nodeLineHeight: null, nodeTextAlignment: null, style: '' },
465
+ type: 'listItem',
466
+ marks: undefined,
467
+ text: undefined,
468
+ content: [
469
+ {
470
+ attrs: {
471
+ level: undefined,
472
+ nodeIndent: null,
473
+ nodeLineHeight: null,
474
+ nodeTextAlignment: null,
475
+ style: '',
476
+ },
477
+ marks: undefined,
478
+ text: undefined,
479
+ type: 'paragraph',
480
+ content: [
481
+ {
482
+ attrs: undefined,
483
+ content: undefined,
484
+ marks: undefined,
485
+ text: 'ddd',
486
+ type: 'text',
487
+ },
488
+ ],
489
+ },
490
+ ],
491
+ },
492
+ ],
493
+ },
494
+ ],
495
+ type: 'doc',
496
+ };
497
+
498
+ const result = squizNodeToRemirrorNode(squizComponentJSON);
499
+ expect(result).toEqual(expected);
500
+ });
501
+
502
+ it('should handle unordered lists', () => {
503
+ const squizComponentJSON: FormattedText = [
504
+ {
505
+ type: 'tag',
506
+ tag: 'ul',
507
+ children: [
508
+ {
509
+ type: 'tag',
510
+ tag: 'li',
511
+ children: [
512
+ {
513
+ type: 'tag',
514
+ tag: 'p',
515
+ children: [
516
+ {
517
+ type: 'text',
518
+ value: 'chicken',
519
+ },
520
+ ],
521
+ },
522
+ ],
523
+ },
524
+ {
525
+ type: 'tag',
526
+ tag: 'li',
527
+ children: [
528
+ {
529
+ type: 'tag',
530
+ tag: 'p',
531
+ children: [
532
+ {
533
+ type: 'text',
534
+ value: 'egg',
535
+ },
536
+ ],
537
+ },
538
+ ],
539
+ },
540
+ ],
541
+ },
542
+ ];
543
+
544
+ const expected: RemirrorJSON = {
545
+ content: [
546
+ {
547
+ attrs: { level: undefined, nodeIndent: null, nodeLineHeight: null, nodeTextAlignment: null, style: '' },
548
+ type: 'bulletList',
549
+ marks: undefined,
550
+ text: undefined,
551
+ content: [
552
+ {
553
+ attrs: { level: undefined, nodeIndent: null, nodeLineHeight: null, nodeTextAlignment: null, style: '' },
554
+ type: 'listItem',
555
+ marks: undefined,
556
+ text: undefined,
557
+ content: [
558
+ {
559
+ attrs: {
560
+ level: undefined,
561
+ nodeIndent: null,
562
+ nodeLineHeight: null,
563
+ nodeTextAlignment: null,
564
+ style: '',
565
+ },
566
+ marks: undefined,
567
+ text: undefined,
568
+ type: 'paragraph',
569
+ content: [
570
+ {
571
+ attrs: undefined,
572
+ content: undefined,
573
+ marks: undefined,
574
+ text: 'chicken',
575
+ type: 'text',
576
+ },
577
+ ],
578
+ },
579
+ ],
580
+ },
581
+ {
582
+ attrs: { level: undefined, nodeIndent: null, nodeLineHeight: null, nodeTextAlignment: null, style: '' },
583
+ type: 'listItem',
584
+ marks: undefined,
585
+ text: undefined,
586
+ content: [
587
+ {
588
+ attrs: {
589
+ level: undefined,
590
+ nodeIndent: null,
591
+ nodeLineHeight: null,
592
+ nodeTextAlignment: null,
593
+ style: '',
594
+ },
595
+ marks: undefined,
596
+ text: undefined,
597
+ type: 'paragraph',
598
+ content: [
599
+ {
600
+ attrs: undefined,
601
+ content: undefined,
602
+ marks: undefined,
603
+ text: 'egg',
604
+ type: 'text',
605
+ },
606
+ ],
607
+ },
608
+ ],
609
+ },
610
+ ],
611
+ },
612
+ ],
613
+ type: 'doc',
614
+ };
615
+
616
+ const result = squizNodeToRemirrorNode(squizComponentJSON);
617
+ expect(result).toEqual(expected);
618
+ });
428
619
  });
@@ -24,6 +24,9 @@ const getNodeType = (node: FormattedNodes): string => {
24
24
  img: 'image',
25
25
  pre: 'preformatted',
26
26
  p: 'paragraph',
27
+ ol: 'orderedList',
28
+ li: 'listItem',
29
+ ul: 'bulletList',
27
30
  a: NodeName.Text,
28
31
  span: NodeName.Text,
29
32
  code: NodeName.CodeBlock,
@@ -15,6 +15,16 @@ describe('getNodeNamesByGroup', () => {
15
15
  // Nodes in the first array will be transformed to a paragraph when formatting is cleared.
16
16
  // Nodes in the second array will be left as-is.
17
17
  expect(formattingNodeNames).toEqual(['paragraph', 'heading', 'preformatted']);
18
- expect(otherNodeNames).toEqual(['assetImage', 'doc', 'text', 'codeBlock', 'image', 'unsupportedNode']);
18
+ expect(otherNodeNames).toEqual([
19
+ 'assetImage',
20
+ 'doc',
21
+ 'text',
22
+ 'codeBlock',
23
+ 'image',
24
+ 'unsupportedNode',
25
+ 'bulletList',
26
+ 'listItem',
27
+ 'orderedList',
28
+ ]);
19
29
  });
20
30
  });