@squiz/formatted-text-editor 1.66.4 → 1.67.1

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Change Log
2
2
 
3
+ ## 1.67.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 928cc88: Made children optional for formatted text nodes/tags
8
+ - Updated dependencies [928cc88]
9
+ - @squiz/dx-json-schema-lib@1.65.1
10
+
11
+ ## 1.67.0
12
+
13
+ ### Minor Changes
14
+
15
+ - 6867717: Changed output of FTE to use semantic HTML where applicable
16
+
3
17
  ## 1.66.4
4
18
 
5
19
  ### Patch Changes
@@ -48,12 +48,6 @@ const resolveFontOptions = (node) => {
48
48
  const fontOptions = {};
49
49
  node.marks.forEach((mark) => {
50
50
  switch (mark.type.name) {
51
- case 'bold':
52
- fontOptions.bold = true;
53
- break;
54
- case 'italic':
55
- fontOptions.italics = true;
56
- break;
57
51
  case 'underline':
58
52
  fontOptions.underline = true;
59
53
  break;
@@ -109,14 +103,7 @@ const transformNode = (node) => {
109
103
  };
110
104
  }
111
105
  node.marks.forEach((mark) => {
112
- switch (mark.type.name) {
113
- case 'bold':
114
- case 'italic':
115
- case 'underline':
116
- break;
117
- default:
118
- transformedNode = transformMark(mark, transformedNode);
119
- }
106
+ transformedNode = transformMark(mark, transformedNode);
120
107
  });
121
108
  if (node.type.name === Extensions_1.NodeName.Unsupported) {
122
109
  const unsupportedNode = node.attrs?.originalNode;
@@ -133,8 +120,10 @@ const transformNode = (node) => {
133
120
  *
134
121
  * @return {FormattedNode}
135
122
  */
136
- const wrapNodeIfNeeded = (node, wrappingNode) => {
123
+ const wrapNodeIfNeeded = (node, wrappingNode, copyFont = true) => {
124
+ const wrappingNodeChildren = wrappingNode.children || [];
137
125
  if (node.type === 'tag' && wrappingNode.type === 'tag' && (node.tag === 'span' || node.tag === wrappingNode.tag)) {
126
+ const nodeChildren = node.children || [];
138
127
  // if the node we are wrapping with is a DOM node, and the node being wrapped is
139
128
  // a plain looking DOM node merge the 2 nodes.
140
129
  return {
@@ -148,28 +137,51 @@ const wrapNodeIfNeeded = (node, wrappingNode) => {
148
137
  ...node.attributes,
149
138
  ...wrappingNode.attributes,
150
139
  }),
151
- font: (0, undefinedIfEmpty_1.undefinedIfEmpty)({
152
- ...node.font,
153
- ...wrappingNode.font,
154
- }),
155
- children: [...node.children, ...wrappingNode.children],
140
+ font: (0, undefinedIfEmpty_1.undefinedIfEmpty)(copyFont
141
+ ? {
142
+ ...node.font,
143
+ ...wrappingNode.font,
144
+ }
145
+ : {}),
146
+ children: [...nodeChildren, ...wrappingNodeChildren],
156
147
  };
157
148
  }
158
149
  // if the node we are wrapping or the wrapping nodes are not compatible merge them.
159
150
  return {
160
151
  ...wrappingNode,
161
- children: [node, ...wrappingNode.children],
152
+ children: [node, ...wrappingNodeChildren],
162
153
  };
163
154
  };
164
155
  const transformMark = (mark, node) => {
165
156
  switch (mark.type.name) {
157
+ case 'bold':
158
+ return wrapNodeIfNeeded(node, {
159
+ type: 'tag',
160
+ tag: 'strong',
161
+ children: [],
162
+ }, false);
163
+ case 'italic':
164
+ return wrapNodeIfNeeded(node, {
165
+ type: 'tag',
166
+ tag: 'em',
167
+ children: [],
168
+ }, false);
169
+ case 'underline':
170
+ return wrapNodeIfNeeded(node, {
171
+ type: 'tag',
172
+ tag: 'span',
173
+ children: [],
174
+ font: {
175
+ underline: mark.type.name === 'underline',
176
+ },
177
+ });
166
178
  case 'link':
167
179
  return wrapNodeIfNeeded(node, {
168
180
  type: 'tag',
169
181
  tag: 'a',
170
182
  attributes: transformAttributes(mark.attrs),
171
183
  children: [],
172
- });
184
+ }, false);
173
185
  case 'assetLink':
174
186
  return wrapNodeIfNeeded(node, {
175
187
  type: 'link-to-matrix-asset',
@@ -24,7 +24,9 @@ const getNodeType = (node) => {
24
24
  ul: 'bulletList',
25
25
  hr: 'horizontalRule',
26
26
  a: Extensions_1.NodeName.Text,
27
+ em: Extensions_1.NodeName.Text,
27
28
  span: Extensions_1.NodeName.Text,
29
+ strong: Extensions_1.NodeName.Text,
28
30
  code: Extensions_1.NodeName.CodeBlock,
29
31
  };
30
32
  if (typeMap[node.type]) {
@@ -96,6 +98,12 @@ const getNodeMarks = (node) => {
96
98
  },
97
99
  });
98
100
  }
101
+ else if (node.type === 'tag' && node.tag === 'strong') {
102
+ marks.push({ type: 'bold' });
103
+ }
104
+ else if (node.type === 'tag' && node.tag === 'em') {
105
+ marks.push({ type: 'italic' });
106
+ }
99
107
  // Handle font formatting
100
108
  if ('font' in node && node.font !== undefined) {
101
109
  for (const [type, enabled] of Object.entries(node.font)) {
@@ -125,7 +133,7 @@ const unwrapNodeIfNeeded = (node) => {
125
133
  const formatNode = (node) => {
126
134
  const children = [];
127
135
  if ('children' in node) {
128
- node.children.forEach((child) => {
136
+ node.children?.forEach((child) => {
129
137
  children.push(...formatNode(child));
130
138
  });
131
139
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squiz/formatted-text-editor",
3
- "version": "1.66.4",
3
+ "version": "1.67.1",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "private": false,
@@ -23,7 +23,7 @@
23
23
  "@headlessui/react": "1.7.11",
24
24
  "@mui/icons-material": "5.11.16",
25
25
  "@remirror/react": "2.0.25",
26
- "@squiz/dx-json-schema-lib": "^1.65.0",
26
+ "@squiz/dx-json-schema-lib": "^1.65.1",
27
27
  "@squiz/resource-browser": "^1.66.3",
28
28
  "clsx": "1.2.1",
29
29
  "react-hook-form": "7.43.2",
@@ -74,16 +74,13 @@ describe('htmlToSquizNode', () => {
74
74
  children: [
75
75
  {
76
76
  type: 'tag',
77
- tag: 'span',
77
+ tag: 'em',
78
78
  children: [
79
79
  {
80
80
  type: 'text',
81
81
  value: 'Italicised text',
82
82
  },
83
83
  ],
84
- font: {
85
- italics: true,
86
- },
87
84
  },
88
85
  {
89
86
  type: 'text',
@@ -91,16 +88,13 @@ describe('htmlToSquizNode', () => {
91
88
  },
92
89
  {
93
90
  type: 'tag',
94
- tag: 'span',
91
+ tag: 'strong',
95
92
  children: [
96
93
  {
97
94
  type: 'text',
98
95
  value: 'Bold text',
99
96
  },
100
97
  ],
101
- font: {
102
- bold: true,
103
- },
104
98
  },
105
99
  ],
106
100
  tag: 'p',
@@ -116,7 +116,6 @@ export const sharedNodeExamples: NodeExample[] = [
116
116
  type: 'text',
117
117
  text: 'Hello',
118
118
  marks: [
119
- { type: 'bold' },
120
119
  {
121
120
  type: 'assetLink',
122
121
  attrs: {
@@ -126,20 +125,20 @@ export const sharedNodeExamples: NodeExample[] = [
126
125
  matrixIdentifier: 'matrix-api-identifier',
127
126
  },
128
127
  },
128
+ { type: 'bold' },
129
129
  ],
130
130
  },
131
131
  squizNode: [
132
132
  {
133
- type: 'link-to-matrix-asset',
134
- target: '_blank',
135
- matrixAssetId: '123',
136
- matrixDomain: 'https://my-matrix.squiz.net',
137
- matrixIdentifier: 'matrix-api-identifier',
133
+ type: 'tag',
134
+ tag: 'strong',
138
135
  children: [
139
136
  {
140
- type: 'tag',
141
- tag: 'span',
142
- font: { bold: true },
137
+ type: 'link-to-matrix-asset',
138
+ target: '_blank',
139
+ matrixAssetId: '123',
140
+ matrixDomain: 'https://my-matrix.squiz.net',
141
+ matrixIdentifier: 'matrix-api-identifier',
143
142
  children: [{ type: 'text', value: 'Hello' }],
144
143
  },
145
144
  ],
@@ -329,10 +329,7 @@ describe('remirrorNodeToSquizNode', () => {
329
329
  value: ' click',
330
330
  },
331
331
  ],
332
- font: {
333
- bold: true,
334
- },
335
- tag: 'span',
332
+ tag: 'strong',
336
333
  type: 'tag',
337
334
  },
338
335
  {
@@ -342,18 +339,22 @@ describe('remirrorNodeToSquizNode', () => {
342
339
  {
343
340
  children: [
344
341
  {
345
- type: 'text',
346
- value: 'here',
342
+ type: 'tag',
343
+ tag: 'a',
344
+ children: [
345
+ {
346
+ type: 'text',
347
+ value: 'here',
348
+ },
349
+ ],
350
+ attributes: {
351
+ href: 'https://www.google.com/',
352
+ title: '',
353
+ },
347
354
  },
348
355
  ],
349
- attributes: {
350
- href: 'https://www.google.com/',
351
- title: '',
352
- },
353
- font: {
354
- italics: true,
355
- },
356
- tag: 'a',
356
+
357
+ tag: 'em',
357
358
  type: 'tag',
358
359
  },
359
360
  {
@@ -363,10 +364,7 @@ describe('remirrorNodeToSquizNode', () => {
363
364
  value: ' ',
364
365
  },
365
366
  ],
366
- font: {
367
- italics: true,
368
- },
369
- tag: 'span',
367
+ tag: 'em',
370
368
  type: 'tag',
371
369
  },
372
370
  {
@@ -376,13 +374,23 @@ describe('remirrorNodeToSquizNode', () => {
376
374
  {
377
375
  children: [
378
376
  {
379
- type: 'text',
380
- value: ' GOOGLE!',
377
+ type: 'tag',
378
+ tag: 'em',
379
+ children: [
380
+ {
381
+ type: 'tag',
382
+ tag: 'strong',
383
+ children: [
384
+ {
385
+ type: 'text',
386
+ value: ' GOOGLE!',
387
+ },
388
+ ],
389
+ },
390
+ ],
381
391
  },
382
392
  ],
383
393
  font: {
384
- bold: true,
385
- italics: true,
386
394
  underline: true,
387
395
  },
388
396
  tag: 'span',
@@ -13,7 +13,7 @@ type FontOptions = FormattedTextModels.v1.FormattedNodeFontProperties;
13
13
  type FormattedText = FormattedTextModels.v1.FormattedText;
14
14
  type FormattedNode = FormattedTextModels.v1.FormattedNodes;
15
15
  type FormattedNodeFontProperties = FormattedTextModels.v1.FormattedNodeFontProperties;
16
- type FormattedNodeWithChildren = Extract<FormattedNode, { children: FormattedNode[] }>;
16
+ type FormattedNodeWithChildren = Extract<FormattedNode, { children?: FormattedNode[] }>;
17
17
  type RemirrorTextAlignment = Exclude<Remirror.Attributes['nodeTextAlignment'], undefined>;
18
18
  type FormattedTextAlignment = FormattingOptions['alignment'];
19
19
 
@@ -71,12 +71,6 @@ const resolveFontOptions = (node: ProsemirrorNode): FormattedNodeFontProperties
71
71
 
72
72
  node.marks.forEach((mark) => {
73
73
  switch (mark.type.name) {
74
- case 'bold':
75
- fontOptions.bold = true;
76
- break;
77
- case 'italic':
78
- fontOptions.italics = true;
79
- break;
80
74
  case 'underline':
81
75
  fontOptions.underline = true;
82
76
  break;
@@ -145,14 +139,7 @@ const transformNode = (node: ProsemirrorNode): FormattedNode => {
145
139
  }
146
140
 
147
141
  node.marks.forEach((mark) => {
148
- switch (mark.type.name) {
149
- case 'bold':
150
- case 'italic':
151
- case 'underline':
152
- break;
153
- default:
154
- transformedNode = transformMark(mark, transformedNode);
155
- }
142
+ transformedNode = transformMark(mark, transformedNode);
156
143
  });
157
144
 
158
145
  if (node.type.name === NodeName.Unsupported) {
@@ -172,8 +159,16 @@ const transformNode = (node: ProsemirrorNode): FormattedNode => {
172
159
  *
173
160
  * @return {FormattedNode}
174
161
  */
175
- const wrapNodeIfNeeded = (node: FormattedNode, wrappingNode: FormattedNodeWithChildren): FormattedNode => {
162
+ const wrapNodeIfNeeded = (
163
+ node: FormattedNode,
164
+ wrappingNode: FormattedNodeWithChildren,
165
+ copyFont: boolean = true,
166
+ ): FormattedNode => {
167
+ const wrappingNodeChildren = wrappingNode.children || [];
168
+
176
169
  if (node.type === 'tag' && wrappingNode.type === 'tag' && (node.tag === 'span' || node.tag === wrappingNode.tag)) {
170
+ const nodeChildren = node.children || [];
171
+
177
172
  // if the node we are wrapping with is a DOM node, and the node being wrapped is
178
173
  // a plain looking DOM node merge the 2 nodes.
179
174
  return {
@@ -187,30 +182,67 @@ const wrapNodeIfNeeded = (node: FormattedNode, wrappingNode: FormattedNodeWithCh
187
182
  ...node.attributes,
188
183
  ...wrappingNode.attributes,
189
184
  }),
190
- font: undefinedIfEmpty({
191
- ...node.font,
192
- ...wrappingNode.font,
193
- }),
194
- children: [...node.children, ...wrappingNode.children],
185
+ font: undefinedIfEmpty(
186
+ copyFont
187
+ ? {
188
+ ...node.font,
189
+ ...wrappingNode.font,
190
+ }
191
+ : {},
192
+ ),
193
+ children: [...nodeChildren, ...wrappingNodeChildren],
195
194
  };
196
195
  }
197
196
 
198
197
  // if the node we are wrapping or the wrapping nodes are not compatible merge them.
199
198
  return {
200
199
  ...wrappingNode,
201
- children: [node, ...wrappingNode.children],
200
+ children: [node, ...wrappingNodeChildren],
202
201
  };
203
202
  };
204
203
 
205
204
  const transformMark = (mark: Mark, node: FormattedNode): FormattedNode => {
206
205
  switch (mark.type.name) {
207
- case 'link':
206
+ case 'bold':
207
+ return wrapNodeIfNeeded(
208
+ node,
209
+ {
210
+ type: 'tag',
211
+ tag: 'strong',
212
+ children: [],
213
+ },
214
+ false,
215
+ );
216
+ case 'italic':
217
+ return wrapNodeIfNeeded(
218
+ node,
219
+ {
220
+ type: 'tag',
221
+ tag: 'em',
222
+ children: [],
223
+ },
224
+ false,
225
+ );
226
+ case 'underline':
208
227
  return wrapNodeIfNeeded(node, {
209
228
  type: 'tag',
210
- tag: 'a',
211
- attributes: transformAttributes(mark.attrs),
229
+ tag: 'span',
212
230
  children: [],
231
+ font: {
232
+ underline: mark.type.name === 'underline',
233
+ },
213
234
  });
235
+ case 'link':
236
+ return wrapNodeIfNeeded(
237
+ node,
238
+ {
239
+ type: 'tag',
240
+ tag: 'a',
241
+ attributes: transformAttributes(mark.attrs),
242
+ children: [],
243
+ },
244
+ false,
245
+ );
214
246
  case 'assetLink':
215
247
  return wrapNodeIfNeeded(node, {
216
248
  type: 'link-to-matrix-asset',
@@ -29,7 +29,9 @@ const getNodeType = (node: FormattedNodes): string => {
29
29
  ul: 'bulletList',
30
30
  hr: 'horizontalRule',
31
31
  a: NodeName.Text,
32
+ em: NodeName.Text,
32
33
  span: NodeName.Text,
34
+ strong: NodeName.Text,
33
35
  code: NodeName.CodeBlock,
34
36
  };
35
37
 
@@ -105,6 +107,10 @@ const getNodeMarks = (node: FormattedNodes): ObjectMark[] => {
105
107
  target: node.target,
106
108
  },
107
109
  });
110
+ } else if (node.type === 'tag' && node.tag === 'strong') {
111
+ marks.push({ type: 'bold' });
112
+ } else if (node.type === 'tag' && node.tag === 'em') {
113
+ marks.push({ type: 'italic' });
108
114
  }
109
115
 
110
116
  // Handle font formatting
@@ -140,7 +146,7 @@ const formatNode = (node: FormattedNodes): RemirrorJSON[] => {
140
146
  const children: RemirrorJSON[] = [];
141
147
 
142
148
  if ('children' in node) {
143
- node.children.forEach((child: FormattedNodes) => {
149
+ node.children?.forEach((child: FormattedNodes) => {
144
150
  children.push(...formatNode(child));
145
151
  });
146
152
  }