@squiz/formatted-text-editor 1.34.1-alpha.0 → 1.34.1-alpha.10

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 (64) hide show
  1. package/lib/EditorToolbar/FloatingToolbar.js +7 -1
  2. package/lib/EditorToolbar/Toolbar.js +4 -2
  3. package/lib/EditorToolbar/Tools/ClearFormatting/ClearFormattingButton.d.ts +2 -0
  4. package/lib/EditorToolbar/Tools/ClearFormatting/ClearFormattingButton.js +56 -0
  5. package/lib/EditorToolbar/Tools/Image/ImageButton.js +1 -1
  6. package/lib/EditorToolbar/Tools/Link/LinkButton.js +1 -1
  7. package/lib/EditorToolbar/Tools/TextAlign/TextAlignButtons.js +2 -2
  8. package/lib/EditorToolbar/Tools/TextType/CodeBlock/CodeBlockButton.d.ts +2 -0
  9. package/lib/EditorToolbar/Tools/TextType/CodeBlock/CodeBlockButton.js +22 -0
  10. package/lib/EditorToolbar/Tools/TextType/Preformatted/PreformattedButton.js +3 -2
  11. package/lib/EditorToolbar/Tools/TextType/TextTypeDropdown.js +8 -2
  12. package/lib/Extensions/ClearFormattingExtension/ClearFormattingExtension.d.ts +5 -0
  13. package/lib/Extensions/ClearFormattingExtension/ClearFormattingExtension.js +63 -0
  14. package/lib/Extensions/CodeBlockExtension/CodeBlockExtension.d.ts +5 -0
  15. package/lib/Extensions/CodeBlockExtension/CodeBlockExtension.js +30 -0
  16. package/lib/Extensions/Extensions.d.ts +1 -0
  17. package/lib/Extensions/Extensions.js +5 -0
  18. package/lib/Extensions/PreformattedExtension/PreformattedExtension.d.ts +2 -0
  19. package/lib/Extensions/PreformattedExtension/PreformattedExtension.js +23 -0
  20. package/lib/index.css +50 -9
  21. package/lib/ui/ToolbarDropdownButton/ToolbarDropdownButton.d.ts +2 -1
  22. package/lib/ui/ToolbarDropdownButton/ToolbarDropdownButton.js +6 -4
  23. package/lib/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.js +7 -2
  24. package/lib/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.js +7 -0
  25. package/lib/utils/getMarkNamesByGroup.d.ts +2 -0
  26. package/lib/utils/getMarkNamesByGroup.js +9 -0
  27. package/lib/utils/getNodeNamesByGroup.d.ts +2 -0
  28. package/lib/utils/getNodeNamesByGroup.js +9 -0
  29. package/package.json +4 -4
  30. package/postcss.config.js +1 -1
  31. package/src/EditorToolbar/FloatingToolbar.tsx +10 -5
  32. package/src/EditorToolbar/Toolbar.tsx +3 -1
  33. package/src/EditorToolbar/Tools/ClearFormatting/ClearFormattingButton.spec.tsx +34 -0
  34. package/src/EditorToolbar/Tools/ClearFormatting/ClearFormattingButton.tsx +45 -0
  35. package/src/EditorToolbar/Tools/Image/ImageButton.tsx +3 -2
  36. package/src/EditorToolbar/Tools/Link/LinkButton.tsx +3 -2
  37. package/src/EditorToolbar/Tools/TextAlign/TextAlignButtons.tsx +2 -2
  38. package/src/EditorToolbar/Tools/TextType/CodeBlock/CodeBlockButton.spec.tsx +47 -0
  39. package/src/EditorToolbar/Tools/TextType/CodeBlock/CodeBlockButton.tsx +32 -0
  40. package/src/EditorToolbar/Tools/TextType/Heading/HeadingButton.spec.tsx +2 -2
  41. package/src/EditorToolbar/Tools/TextType/Paragraph/ParagraphButton.spec.tsx +1 -1
  42. package/src/EditorToolbar/Tools/TextType/Preformatted/PreformattedButton.spec.tsx +2 -2
  43. package/src/EditorToolbar/Tools/TextType/Preformatted/PreformattedButton.tsx +3 -1
  44. package/src/EditorToolbar/Tools/TextType/TextTypeDropdown.tsx +12 -2
  45. package/src/EditorToolbar/_floating-toolbar.scss +1 -1
  46. package/src/EditorToolbar/_toolbar.scss +2 -2
  47. package/src/Extensions/ClearFormattingExtension/ClearFormattingExtension.ts +57 -0
  48. package/src/Extensions/CodeBlockExtension/CodeBlockExtension.ts +34 -0
  49. package/src/Extensions/Extensions.ts +5 -0
  50. package/src/Extensions/PreformattedExtension/PreformattedExtension.spec.ts +4 -2
  51. package/src/Extensions/PreformattedExtension/PreformattedExtension.ts +31 -0
  52. package/src/index.scss +3 -0
  53. package/src/ui/ToolbarDropdownButton/ToolbarDropdownButton.tsx +8 -4
  54. package/src/ui/ToolbarDropdownButton/_toolbar-dropdown-button.scss +11 -0
  55. package/src/ui/_typography.scss +26 -0
  56. package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.spec.ts +37 -0
  57. package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.ts +10 -2
  58. package/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.spec.ts +39 -2
  59. package/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.ts +6 -0
  60. package/src/utils/getMarkNamesByGroup.spec.ts +20 -0
  61. package/src/utils/getMarkNamesByGroup.ts +7 -0
  62. package/src/utils/getNodeNamesByGroup.spec.ts +20 -0
  63. package/src/utils/getNodeNamesByGroup.ts +7 -0
  64. package/tailwind.config.cjs +4 -3
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import CheckIcon from '@mui/icons-material/Check';
2
+ import CheckRoundedIcon from '@mui/icons-material/CheckRounded';
3
3
 
4
4
  type DropdownButtonProps = {
5
5
  children?: JSX.Element;
@@ -7,9 +7,10 @@ type DropdownButtonProps = {
7
7
  isDisabled: boolean;
8
8
  isActive: boolean;
9
9
  label: string;
10
+ icon?: JSX.Element;
10
11
  };
11
12
 
12
- const DropdownButton = ({ children, handleOnClick, isDisabled, isActive, label }: DropdownButtonProps) => {
13
+ const DropdownButton = ({ children, handleOnClick, isDisabled, isActive, label, icon }: DropdownButtonProps) => {
13
14
  return (
14
15
  <button
15
16
  aria-label={label}
@@ -20,8 +21,11 @@ const DropdownButton = ({ children, handleOnClick, isDisabled, isActive, label }
20
21
  disabled={isDisabled}
21
22
  className={`btn dropdown-button ${isActive ? 'is-active' : ''}`}
22
23
  >
23
- <span>{children || label}</span>
24
- {isActive && <CheckIcon className="dropdown-button-icon" />}
24
+ <div className="dropdown-button-label">
25
+ {icon && icon}
26
+ {children || label}
27
+ </div>
28
+ {isActive && <CheckRoundedIcon className="dropdown-button-icon" />}
25
29
  </button>
26
30
  );
27
31
  };
@@ -11,4 +11,15 @@
11
11
  &:focus {
12
12
  background-color: rgba(black, 0.04);
13
13
  }
14
+
15
+ svg {
16
+ font-size: 18px;
17
+ }
18
+
19
+ .dropdown-button-label {
20
+ display: flex;
21
+ align-items: center;
22
+ font-size: 14px;
23
+ gap: 5px;
24
+ }
14
25
  }
@@ -44,3 +44,29 @@ h6 {
44
44
  letter-spacing: -0.2px;
45
45
  line-height: 1.25rem;
46
46
  }
47
+
48
+ .code-block,
49
+ .preformatted {
50
+ display: flex;
51
+
52
+ .material-symbols-rounded {
53
+ @apply text-gray-700 text-xlg;
54
+ pointer-events: none;
55
+ margin-right: 0.375rem;
56
+ }
57
+
58
+ .block-divider {
59
+ @apply bg-gray-200;
60
+ width: 4px;
61
+ border-radius: 0.75rem;
62
+ margin-right: 0.625rem;
63
+ }
64
+
65
+ code,
66
+ pre {
67
+ @apply text-gray-700;
68
+ font-size: 0.75rem;
69
+ padding-top: 0.75rem;
70
+ padding-bottom: 0.75rem;
71
+ }
72
+ }
@@ -74,6 +74,43 @@ describe('remirrorNodeToSquizNode', () => {
74
74
  expect(result).toEqual(expected);
75
75
  });
76
76
 
77
+ it('should handle code block formatting', async () => {
78
+ const content: RemirrorJSON = {
79
+ type: 'doc',
80
+ content: [
81
+ {
82
+ type: 'codeBlock',
83
+ attrs: {
84
+ language: 'js',
85
+ wrap: true,
86
+ },
87
+ content: [
88
+ {
89
+ type: 'text',
90
+ text: 'Hello world',
91
+ },
92
+ ],
93
+ },
94
+ ],
95
+ };
96
+
97
+ const { editor } = await renderWithEditor(null, { content });
98
+
99
+ const expected: FormattedText = [
100
+ {
101
+ type: 'tag',
102
+ tag: 'code',
103
+ children: [{ type: 'text', value: 'Hello world' }],
104
+ attributes: {
105
+ language: 'js',
106
+ },
107
+ },
108
+ ];
109
+
110
+ const result = remirrorNodeToSquizNode(editor.doc);
111
+ expect(result).toEqual(expected);
112
+ });
113
+
77
114
  it('should handle images', async () => {
78
115
  const content: RemirrorJSON = {
79
116
  type: 'doc',
@@ -16,10 +16,14 @@ type FormattedNodeFontProperties = FormattedTextModels.v1.FormattedNodeFontPrope
16
16
  type FormattedNodeWithChildren = Extract<FormattedNode, { children: FormattedNode[] }>;
17
17
 
18
18
  export const resolveNodeTag = (node: ProsemirrorNode): string => {
19
- if (node.type.name === 'text') {
19
+ if (node.type.name === NodeName.Text) {
20
20
  return 'span';
21
21
  }
22
22
 
23
+ if (node.type.name === NodeName.CodeBlock) {
24
+ return 'code';
25
+ }
26
+
23
27
  if (node.type.spec?.toDOM) {
24
28
  const domNode = node.type.spec.toDOM(node);
25
29
 
@@ -92,9 +96,13 @@ const transformFragment = (fragment: Fragment): FormattedText => {
92
96
  };
93
97
 
94
98
  const transformNode = (node: ProsemirrorNode): FormattedNode => {
95
- const attributes = node.type.name === NodeName.Image ? transformAttributes(node.attrs) : undefined;
96
99
  const formattingOptions = undefinedIfEmpty(resolveFormattingOptions(node));
97
100
  const font = undefinedIfEmpty(resolveFontOptions(node));
101
+ const attributes =
102
+ node.type.name === NodeName.Image || node.type.name === NodeName.CodeBlock
103
+ ? transformAttributes(node.attrs)
104
+ : undefined;
105
+
98
106
  let transformedNode: FormattedNode = { type: 'text', value: node.text || '' };
99
107
 
100
108
  // Squiz "text" nodes can't have formatting/font options but Remirror "text" nodes can.
@@ -139,10 +139,10 @@ describe('squizNodeToRemirrorNode', () => {
139
139
  },
140
140
  ],
141
141
  type: 'tag',
142
- tag: 'code',
142
+ tag: 'video',
143
143
  },
144
144
  ],
145
- 'Unsupported node type provided: tag (tag: code)',
145
+ 'Unsupported node type provided: tag (tag: video)',
146
146
  ],
147
147
  [
148
148
  'Unsupported node type',
@@ -192,6 +192,43 @@ describe('squizNodeToRemirrorNode', () => {
192
192
  expect(result).toEqual(expected);
193
193
  });
194
194
 
195
+ it('should handle code block text', () => {
196
+ const squizComponentJSON: FormattedText = [
197
+ {
198
+ children: [
199
+ {
200
+ type: 'text',
201
+ value: 'Hello world!',
202
+ },
203
+ ],
204
+ type: 'tag',
205
+ tag: 'code',
206
+ },
207
+ ];
208
+
209
+ const expected: RemirrorJSON = {
210
+ content: [
211
+ {
212
+ type: 'codeBlock',
213
+ attrs: {
214
+ language: 'markup',
215
+ wrap: true,
216
+ },
217
+ content: [
218
+ {
219
+ type: 'text',
220
+ text: 'Hello world!',
221
+ },
222
+ ],
223
+ },
224
+ ],
225
+ type: 'doc',
226
+ };
227
+
228
+ const result = squizNodeToRemirrorNode(squizComponentJSON);
229
+ expect(result).toEqual(expected);
230
+ });
231
+
195
232
  it('should handle images', () => {
196
233
  const squizComponentJSON: FormattedText = [
197
234
  {
@@ -26,6 +26,7 @@ const getNodeType = (node: FormattedNodes): string => {
26
26
  p: 'paragraph',
27
27
  a: NodeName.Text,
28
28
  span: NodeName.Text,
29
+ code: NodeName.CodeBlock,
29
30
  };
30
31
 
31
32
  if (typeMap[node.type]) {
@@ -53,6 +54,11 @@ const getNodeAttributes = (node: FormattedNodes): Attrs => {
53
54
  src: node.attributes?.src,
54
55
  title: node.attributes?.title,
55
56
  };
57
+ } else if (node.type === 'tag' && node.tag === 'code') {
58
+ return {
59
+ language: node.attributes?.language || 'markup',
60
+ wrap: node.attributes?.wrap || true,
61
+ };
56
62
  } else if (node.type === 'matrix-image') {
57
63
  return {
58
64
  matrixAssetId: node.matrixAssetId,
@@ -0,0 +1,20 @@
1
+ import { EditorSchema, ExtensionTag } from '@remirror/core';
2
+ import { renderWithEditor } from '../../tests';
3
+ import { getMarkNamesByGroup } from './getMarkNamesByGroup';
4
+
5
+ describe('getMarkNamesGroup', () => {
6
+ it('Should return expected extensions with "FormattingMark" tag', async () => {
7
+ const { editor } = await renderWithEditor(null);
8
+ const schema: EditorSchema = editor.schema;
9
+ const formattingMarkNames = getMarkNamesByGroup(schema, ExtensionTag.FormattingMark);
10
+ const otherMarkNames = Object.values(schema.marks)
11
+ .map((mark) => mark.name)
12
+ .filter((mark) => !formattingMarkNames.includes(mark));
13
+
14
+ // This tag is used by the clear formatting extension.
15
+ // Marks in the first array will be transformed to a paragraph when formatting is cleared.
16
+ // Mark in the second array will be left as-is.
17
+ expect(formattingMarkNames).toEqual(['bold', 'italic', 'underline']);
18
+ expect(otherMarkNames).toEqual(['assetLink', 'link']);
19
+ });
20
+ });
@@ -0,0 +1,7 @@
1
+ import { EditorSchema, ExtensionTagType } from '@remirror/core';
2
+
3
+ export const getMarkNamesByGroup = (schema: EditorSchema, group: ExtensionTagType): string[] => {
4
+ return Object.values(schema.marks)
5
+ .filter((mark) => mark.spec.group?.includes(group))
6
+ .map((mark) => mark.name);
7
+ };
@@ -0,0 +1,20 @@
1
+ import { EditorSchema, ExtensionTag } from '@remirror/core';
2
+ import { renderWithEditor } from '../../tests';
3
+ import { getNodeNamesByGroup } from './getNodeNamesByGroup';
4
+
5
+ describe('getNodeNamesByGroup', () => {
6
+ it('Should return expected extensions with "FormattingNode" tag', async () => {
7
+ const { editor } = await renderWithEditor(null);
8
+ const schema: EditorSchema = editor.schema;
9
+ const formattingNodeNames = getNodeNamesByGroup(schema, ExtensionTag.FormattingNode);
10
+ const otherNodeNames = Object.values(schema.nodes)
11
+ .map((node) => node.name)
12
+ .filter((node) => !formattingNodeNames.includes(node));
13
+
14
+ // This tag is used by the clear formatting extension.
15
+ // Nodes in the first array will be transformed to a paragraph when formatting is cleared.
16
+ // Nodes in the second array will be left as-is.
17
+ expect(formattingNodeNames).toEqual(['paragraph', 'heading', 'preformatted']);
18
+ expect(otherNodeNames).toEqual(['assetImage', 'doc', 'text', 'codeBlock', 'image']);
19
+ });
20
+ });
@@ -0,0 +1,7 @@
1
+ import { EditorSchema, ExtensionTagType } from '@remirror/core';
2
+
3
+ export const getNodeNamesByGroup = (schema: EditorSchema, group: ExtensionTagType): string[] => {
4
+ return Object.values(schema.nodes)
5
+ .filter((node) => node.spec.group?.includes(group))
6
+ .map((node) => node.name);
7
+ };
@@ -27,9 +27,6 @@ module.exports = {
27
27
  'heading-3': ['1.125rem', '1.375rem'],
28
28
  'heading-4': ['1rem', '1.25rem'],
29
29
  },
30
- fontFamily: {
31
- base: 'Open Sans, Arial, sans-serif',
32
- },
33
30
  boxShadow: {
34
31
  outline: '0 0 0 1px rgba(0,0,0,0.10)',
35
32
  sm: '0 0 0 1px rgba(0,0,0,0.04), 0 1px 4px 2px rgba(0,0,0,0.08)',
@@ -75,6 +72,10 @@ module.exports = {
75
72
  300: '#0774d2',
76
73
  400: '#044985',
77
74
  },
75
+ teal: {
76
+ 100: '#E6F4F6',
77
+ 400: '#024752',
78
+ },
78
79
  red: {
79
80
  300: '#d72321',
80
81
  },