@squiz/formatted-text-editor 1.21.1-alpha.41 → 1.21.1-alpha.43

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.
@@ -11,10 +11,11 @@ const Extensions_1 = require("../../../Extensions/Extensions");
11
11
  const ImageModal = ({ onCancel, onSubmit }) => {
12
12
  const selection = (0, react_2.useCurrentSelection)();
13
13
  const currentImage = selection?.node;
14
+ const currentImageAttrs = { ...currentImage?.attrs };
14
15
  const formData = {
15
16
  imageType: currentImage?.type.name === Extensions_1.NodeName.AssetImage ? Extensions_1.NodeName.AssetImage : Extensions_1.NodeName.Image,
16
- image: currentImage?.type?.name === Extensions_1.NodeName.Image ? currentImage?.attrs : {},
17
- assetImage: currentImage?.type?.name === Extensions_1.NodeName.AssetImage ? currentImage?.attrs : {},
17
+ image: currentImage?.type?.name === Extensions_1.NodeName.Image ? currentImageAttrs : {},
18
+ assetImage: currentImage?.type?.name === Extensions_1.NodeName.AssetImage ? currentImageAttrs : {},
18
19
  };
19
20
  return (react_1.default.createElement(FormModal_1.default, { title: "Image", onCancel: onCancel },
20
21
  react_1.default.createElement(ImageForm_1.default, { data: formData, onSubmit: onSubmit })));
@@ -16,8 +16,8 @@ const LinkModal = ({ onCancel, onSubmit }) => {
16
16
  const data = {
17
17
  linkType: marks[0]?.type?.name === Extensions_1.MarkName.AssetLink ? Extensions_1.MarkName.AssetLink : Extensions_1.MarkName.Link,
18
18
  text: selectedText,
19
- link: marks.find((mark) => mark.type.name === 'link')?.attrs || {},
20
- assetLink: marks.find((mark) => mark.type.name === Extensions_1.MarkName.AssetLink)?.attrs || {},
19
+ link: { ...marks.find((mark) => mark.type.name === 'link')?.attrs },
20
+ assetLink: { ...marks.find((mark) => mark.type.name === Extensions_1.MarkName.AssetLink)?.attrs },
21
21
  range: { from: selection.from, to: selection.to },
22
22
  };
23
23
  return (react_1.default.createElement(FormModal_1.default, { title: "Link", onCancel: onCancel },
@@ -24,9 +24,13 @@ let PreformattedExtension = class PreformattedExtension extends core_1.NodeExten
24
24
  attrs: {
25
25
  ...extra.defaults(),
26
26
  },
27
- parseDOM: [...(override.parseDOM ?? [])],
27
+ parseDOM: [
28
+ {
29
+ tag: 'pre',
30
+ },
31
+ ],
28
32
  toDOM: (node) => {
29
- return [`pre`, extra.dom(node), 0];
33
+ return ['pre', extra.dom(node), 0];
30
34
  },
31
35
  };
32
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squiz/formatted-text-editor",
3
- "version": "1.21.1-alpha.41",
3
+ "version": "1.21.1-alpha.43",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "scripts": {
@@ -74,5 +74,5 @@
74
74
  "volta": {
75
75
  "node": "18.15.0"
76
76
  },
77
- "gitHead": "d42eea53de5e9ccc4e3b3e59f45fffa06baaa00f"
77
+ "gitHead": "061e03f271b17ef1449370217bc5ee3dd92c0f62"
78
78
  }
@@ -1,5 +1,5 @@
1
1
  import '@testing-library/jest-dom';
2
- import { screen, fireEvent, waitForElementToBeRemoved, act } from '@testing-library/react';
2
+ import { screen, fireEvent, waitForElementToBeRemoved, act, waitFor } from '@testing-library/react';
3
3
  import { NodeSelection } from 'prosemirror-state';
4
4
  import React from 'react';
5
5
  import { renderWithEditor, select } from '../../../../tests';
@@ -82,10 +82,43 @@ describe('ImageButton', () => {
82
82
  await openModal();
83
83
  fireEvent.change(screen.getByLabelText('Source'), { target: { value: 'https://httpcats.com/303.jpg' } });
84
84
  fireEvent.change(screen.getByLabelText('Alternative description'), { target: { value: 'Updated cats!' } });
85
+
86
+ // wait for the new image dimensions to be calculated
87
+ await waitFor(() => expect(screen.getByLabelText('Height')).toHaveValue(2));
88
+
89
+ // verify the content matches what was initially set prior to applying
90
+ expect(getJsonContent()).toEqual({
91
+ type: 'paragraph',
92
+ attrs: expect.any(Object),
93
+ content: [
94
+ {
95
+ text: 'Some ',
96
+ type: 'text',
97
+ },
98
+ {
99
+ type: 'image',
100
+ attrs: {
101
+ alt: 'hi',
102
+ crop: null,
103
+ height: null,
104
+ width: null,
105
+ rotate: null,
106
+ src: 'https://httpcats.com/529.jpg',
107
+ title: '',
108
+ fileName: null,
109
+ resizable: false,
110
+ },
111
+ },
112
+ { type: 'text', text: ' nonsense' },
113
+ ],
114
+ });
115
+
116
+ // apply the changes
85
117
  fireEvent.click(screen.getByRole('button', { name: 'Apply' }));
86
118
 
87
119
  await waitForElementToBeRemoved(() => screen.getByRole('button', { name: 'Apply' }));
88
120
 
121
+ // asset the content has been updated
89
122
  expect(getJsonContent()).toEqual({
90
123
  type: 'paragraph',
91
124
  attrs: expect.any(Object),
@@ -11,8 +11,8 @@ beforeEach(() => {
11
11
  });
12
12
  const mockSubmitFunction = jest.fn();
13
13
  const mockCancelFunction = jest.fn();
14
- const setup = () => {
15
- const utils = renderWithEditor(<ImageModal onCancel={mockCancelFunction} onSubmit={mockSubmitFunction} />);
14
+ const setup = async () => {
15
+ const utils = await renderWithEditor(<ImageModal onCancel={mockCancelFunction} onSubmit={mockSubmitFunction} />);
16
16
  const sourceInput = screen.getByRole('textbox', { name: /source/i }) as HTMLInputElement;
17
17
  const altInput = screen.getByRole('textbox', { name: /alt/i }) as HTMLInputElement;
18
18
  const widthInput = screen.getByRole('spinbutton', { name: /Width/i }) as HTMLInputElement;
@@ -34,7 +34,7 @@ describe('ImageModal', () => {
34
34
  });
35
35
 
36
36
  it('Populates the source field when a source is supplied', async () => {
37
- const { sourceInput } = setup();
37
+ const { sourceInput } = await setup();
38
38
  fireEvent.change(sourceInput, { target: { value: 'https://httpcats.com/302.jpg' } });
39
39
  await waitFor(() => {
40
40
  expect(sourceInput.value).toBe('https://httpcats.com/302.jpg');
@@ -42,7 +42,7 @@ describe('ImageModal', () => {
42
42
  });
43
43
 
44
44
  it('Renders empty width and height fields if the image source is empty', async () => {
45
- const { sourceInput, widthInput, heightInput } = setup();
45
+ const { sourceInput, widthInput, heightInput } = await setup();
46
46
  fireEvent.change(sourceInput, { target: { value: '' } });
47
47
  await waitFor(() => {
48
48
  expect(widthInput.value).toBe('');
@@ -51,7 +51,7 @@ describe('ImageModal', () => {
51
51
  });
52
52
 
53
53
  it('Updates the height field with aspect ratio based value from width', async () => {
54
- const { widthInput, heightInput } = setup();
54
+ const { widthInput, heightInput } = await setup();
55
55
  fireEvent.change(widthInput, { target: { value: '300' } });
56
56
  await waitFor(() => {
57
57
  expect(widthInput.value).toBe('300');
@@ -60,7 +60,7 @@ describe('ImageModal', () => {
60
60
  });
61
61
 
62
62
  it('Updates the width field with aspect ratio based value from height', async () => {
63
- const { widthInput, heightInput } = setup();
63
+ const { widthInput, heightInput } = await setup();
64
64
  fireEvent.change(heightInput, { target: { value: '100' } });
65
65
  await waitFor(() => {
66
66
  expect(heightInput.value).toBe('100');
@@ -68,8 +68,8 @@ describe('ImageModal', () => {
68
68
  });
69
69
  });
70
70
 
71
- it('Does not change the width when height is changed and aspect ratio link is off', () => {
72
- const { widthInput, heightInput } = setup();
71
+ it('Does not change the width when height is changed and aspect ratio link is off', async () => {
72
+ const { widthInput, heightInput } = await setup();
73
73
  fireEvent.change(heightInput, { target: { value: '100' } });
74
74
  expect(heightInput.value).toBe('100');
75
75
  expect(widthInput.value).toBe('177.78');
@@ -79,8 +79,8 @@ describe('ImageModal', () => {
79
79
  expect(widthInput.value).toBe('177.78');
80
80
  });
81
81
 
82
- it('Does not change the height when width is changed and aspect ratio link is off', () => {
83
- const { widthInput, heightInput } = setup();
82
+ it('Does not change the height when width is changed and aspect ratio link is off', async () => {
83
+ const { widthInput, heightInput } = await setup();
84
84
  fireEvent.change(widthInput, { target: { value: '450' } });
85
85
  expect(widthInput.value).toBe('450');
86
86
  expect(heightInput.value).toBe('253.13');
@@ -90,8 +90,8 @@ describe('ImageModal', () => {
90
90
  expect(heightInput.value).toBe('253.13');
91
91
  });
92
92
 
93
- it('Changes the icon when aspect ratio button is toggled', () => {
94
- renderWithEditor(<ImageModal onCancel={mockCancelFunction} onSubmit={mockSubmitFunction} />);
93
+ it('Changes the icon when aspect ratio button is toggled', async () => {
94
+ await setup();
95
95
  expect(screen.getByTestId('InsertLinkRoundedIcon')).toBeInTheDocument();
96
96
  fireEvent.click(screen.getByRole('button', { name: /constrain properties/i }));
97
97
  expect(screen.queryByTestId('InsertLinkRoundedIcon')).not.toBeInTheDocument();
@@ -99,7 +99,7 @@ describe('ImageModal', () => {
99
99
  });
100
100
 
101
101
  it('Returns relevant error message if the width is not higher than 0', async () => {
102
- const { widthInput } = setup();
102
+ const { widthInput } = await setup();
103
103
 
104
104
  fireEvent.change(widthInput, { target: { value: '0' } });
105
105
  fireEvent.click(screen.getByRole('button', { name: /Apply/i }));
@@ -110,7 +110,7 @@ describe('ImageModal', () => {
110
110
  });
111
111
 
112
112
  it('Returns relevant error message if the height is not higher than 0', async () => {
113
- const { heightInput } = setup();
113
+ const { heightInput } = await setup();
114
114
 
115
115
  fireEvent.change(heightInput, { target: { value: '0' } });
116
116
  fireEvent.click(screen.getByRole('button', { name: /Apply/i }));
@@ -14,10 +14,11 @@ type ImageModalProps = {
14
14
  const ImageModal = ({ onCancel, onSubmit }: ImageModalProps) => {
15
15
  const selection = useCurrentSelection() as NodeSelection;
16
16
  const currentImage = selection?.node;
17
+ const currentImageAttrs = { ...currentImage?.attrs };
17
18
  const formData = {
18
19
  imageType: currentImage?.type.name === NodeName.AssetImage ? NodeName.AssetImage : NodeName.Image,
19
- image: currentImage?.type?.name === NodeName.Image ? currentImage?.attrs : {},
20
- assetImage: currentImage?.type?.name === NodeName.AssetImage ? currentImage?.attrs : {},
20
+ image: currentImage?.type?.name === NodeName.Image ? currentImageAttrs : {},
21
+ assetImage: currentImage?.type?.name === NodeName.AssetImage ? currentImageAttrs : {},
21
22
  };
22
23
 
23
24
  return (
@@ -58,6 +58,27 @@ describe('LinkButton', () => {
58
58
  await openModal();
59
59
  fireEvent.change(screen.getByLabelText('URL'), { target: { value: 'https://www.example.org/updated-link' } });
60
60
  fireEvent.change(screen.getByLabelText('Text'), { target: { value: 'Updated sample link' } });
61
+
62
+ // verify the content matches what was initially set prior to applying.
63
+ expect(getJsonContent()).toEqual({
64
+ type: 'paragraph',
65
+ attrs: expect.any(Object),
66
+ content: [
67
+ {
68
+ type: 'text',
69
+ text: 'Sample link',
70
+ marks: [
71
+ {
72
+ type: 'link',
73
+ attrs: { href: 'https://www.example.org/my-link', target: '_self', title: null },
74
+ },
75
+ ],
76
+ },
77
+ { type: 'text', text: ' with some other content.' },
78
+ ],
79
+ });
80
+
81
+ // apply the changes.
61
82
  fireEvent.click(screen.getByRole('button', { name: 'Apply' }));
62
83
 
63
84
  await waitForElementToBeRemoved(() => screen.getByRole('button', { name: 'Apply' }));
@@ -21,8 +21,8 @@ const LinkModal = ({ onCancel, onSubmit }: LinkModalProps) => {
21
21
  const data = {
22
22
  linkType: marks[0]?.type?.name === MarkName.AssetLink ? MarkName.AssetLink : MarkName.Link,
23
23
  text: selectedText,
24
- link: marks.find((mark) => mark.type.name === 'link')?.attrs || {},
25
- assetLink: marks.find((mark) => mark.type.name === MarkName.AssetLink)?.attrs || {},
24
+ link: { ...marks.find((mark) => mark.type.name === 'link')?.attrs },
25
+ assetLink: { ...marks.find((mark) => mark.type.name === MarkName.AssetLink)?.attrs },
26
26
  range: { from: selection.from, to: selection.to },
27
27
  };
28
28
 
@@ -0,0 +1,41 @@
1
+ import { renderWithEditor } from '../../../tests';
2
+
3
+ describe('AssetLinkExtension', () => {
4
+ it('Parses HTML content with preformatted text', async () => {
5
+ const { getJsonContent } = await renderWithEditor(null, {
6
+ content: `<pre>This is some preformatted text</pre>`,
7
+ });
8
+
9
+ expect(getJsonContent()).toEqual({
10
+ type: 'preformatted',
11
+ attrs: expect.any(Object),
12
+ content: [
13
+ {
14
+ type: 'text',
15
+ text: 'This is some preformatted text',
16
+ },
17
+ ],
18
+ });
19
+ });
20
+
21
+ it('Outputs expected HTML', async () => {
22
+ const { getHtmlContent } = await renderWithEditor(null, {
23
+ content: {
24
+ type: 'doc',
25
+ content: [
26
+ {
27
+ type: 'preformatted',
28
+ content: [
29
+ {
30
+ type: 'text',
31
+ text: 'This is some preformatted text',
32
+ },
33
+ ],
34
+ },
35
+ ],
36
+ },
37
+ });
38
+
39
+ expect(getHtmlContent()).toBe('<pre style="">This is some preformatted text</pre>');
40
+ });
41
+ });
@@ -30,9 +30,13 @@ export class PreformattedExtension extends NodeExtension {
30
30
  attrs: {
31
31
  ...extra.defaults(),
32
32
  },
33
- parseDOM: [...(override.parseDOM ?? [])],
33
+ parseDOM: [
34
+ {
35
+ tag: 'pre',
36
+ },
37
+ ],
34
38
  toDOM: (node: ProsemirrorNode) => {
35
- return [`pre`, extra.dom(node), 0];
39
+ return ['pre', extra.dom(node), 0];
36
40
  },
37
41
  };
38
42
  }