@squiz/formatted-text-editor 1.70.0 → 2.0.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.
- package/CHANGELOG.md +18 -0
- package/demo/App.tsx +7 -1
- package/demo/AppContext.tsx +49 -18
- package/demo/resources.json +44 -0
- package/demo/sources.json +4 -0
- package/lib/Editor/Editor.js +8 -3
- package/lib/Editor/EditorContext.d.ts +2 -0
- package/lib/Editor/EditorContext.js +3 -0
- package/lib/Extensions/Extensions.d.ts +3 -2
- package/lib/Extensions/Extensions.js +4 -2
- package/lib/Extensions/FetchUrlExtension/FetchUrlExtension.d.ts +2 -4
- package/lib/Extensions/FetchUrlExtension/FetchUrlExtension.js +7 -7
- package/lib/index.d.ts +2 -0
- package/lib/types.d.ts +4 -0
- package/lib/ui/Fields/MatrixAsset/MatrixAsset.js +7 -7
- package/lib/utils/converters/htmlToSquizNode/htmlToSquizNode.js +2 -9
- package/lib/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.js +19 -4
- package/lib/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.js +23 -0
- package/package.json +11 -10
- package/src/Editor/Editor.spec.tsx +14 -4
- package/src/Editor/Editor.tsx +9 -3
- package/src/Editor/EditorContext.spec.tsx +1 -0
- package/src/Editor/EditorContext.ts +5 -0
- package/src/EditorToolbar/Tools/Image/Form/ImageForm.spec.tsx +5 -3
- package/src/EditorToolbar/Tools/Image/ImageButton.spec.tsx +6 -0
- package/src/EditorToolbar/Tools/Link/Form/LinkForm.spec.tsx +19 -7
- package/src/EditorToolbar/Tools/Link/LinkButton.spec.tsx +7 -1
- package/src/EditorToolbar/Tools/Link/RemoveLinkButton.spec.tsx +3 -3
- package/src/Extensions/Extensions.ts +4 -3
- package/src/Extensions/FetchUrlExtension/FetchUrlExtension.ts +12 -17
- package/src/Extensions/UnsuportedExtension/UnsupportedNodeExtension.spec.ts +1 -1
- package/src/index.ts +2 -0
- package/src/types.ts +3 -0
- package/src/ui/Fields/MatrixAsset/MatrixAsset.spec.tsx +26 -9
- package/src/ui/Fields/MatrixAsset/MatrixAsset.tsx +8 -8
- package/src/utils/converters/htmlToSquizNode/htmlToSquizNode.ts +6 -16
- package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.spec.ts +457 -0
- package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.ts +24 -5
- package/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.spec.ts +210 -0
- package/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.ts +23 -0
- package/tests/mockResourceBrowserContext.tsx +48 -12
- package/tests/renderWithContext.tsx +31 -3
- package/tests/renderWithEditor.tsx +1 -3
- package/vite.config.ts +2 -2
@@ -1,9 +1,9 @@
|
|
1
1
|
import '@testing-library/jest-dom';
|
2
2
|
import React from 'react';
|
3
3
|
import { act, fireEvent, render, screen, within } from '@testing-library/react';
|
4
|
-
import {
|
4
|
+
import { MatrixResourceBrowserPluginProps } from '@squiz/matrix-resource-browser-plugin';
|
5
5
|
import Editor from './Editor';
|
6
|
-
import { renderWithEditor } from '../../tests';
|
6
|
+
import { renderWithEditor, mockResourceBrowserContext } from '../../tests';
|
7
7
|
import ImageButton from '../EditorToolbar/Tools/Image/ImageButton';
|
8
8
|
import * as useFocus from '../hooks/useFocus';
|
9
9
|
|
@@ -432,10 +432,20 @@ describe('Formatted text editor', () => {
|
|
432
432
|
childCount: '1',
|
433
433
|
});
|
434
434
|
|
435
|
+
const { MockResourceBrowserContext } = mockResourceBrowserContext({
|
436
|
+
sources: [],
|
437
|
+
resources: [],
|
438
|
+
pluginProps: {
|
439
|
+
onRequestSources,
|
440
|
+
onRequestChildren,
|
441
|
+
onRequestResource,
|
442
|
+
} as MatrixResourceBrowserPluginProps,
|
443
|
+
});
|
444
|
+
|
435
445
|
render(
|
436
|
-
<
|
446
|
+
<MockResourceBrowserContext>
|
437
447
|
<Editor />
|
438
|
-
</
|
448
|
+
</MockResourceBrowserContext>,
|
439
449
|
);
|
440
450
|
|
441
451
|
await act(() => fireEvent.click(screen.getByRole('button', { name: 'Link (Ctrl+K)' })));
|
package/src/Editor/Editor.tsx
CHANGED
@@ -7,7 +7,6 @@ import { Toolbar, FloatingToolbar } from '../EditorToolbar';
|
|
7
7
|
import { EditorContext } from './EditorContext';
|
8
8
|
import { createExtensions } from '../Extensions/Extensions';
|
9
9
|
import useFocus from '../hooks/useFocus';
|
10
|
-
import { ResourceBrowserContext } from '@squiz/resource-browser';
|
11
10
|
import { TableComponents } from '@remirror/extension-react-tables';
|
12
11
|
|
13
12
|
type EditorProps = {
|
@@ -56,7 +55,7 @@ const Editor = ({
|
|
56
55
|
enableTableTool = false,
|
57
56
|
}: EditorProps) => {
|
58
57
|
const { manager, state, setState } = useRemirror({
|
59
|
-
extensions: createExtensions(useContext(EditorContext)
|
58
|
+
extensions: createExtensions(useContext(EditorContext)),
|
60
59
|
content,
|
61
60
|
selection: 'start',
|
62
61
|
stringHandler: 'html',
|
@@ -74,6 +73,13 @@ const Editor = ({
|
|
74
73
|
if (isFocused) {
|
75
74
|
manager.view.dom.focus();
|
76
75
|
}
|
76
|
+
|
77
|
+
// TODO: May want to come back to this and see if there's a better solution
|
78
|
+
// We have to add a type button attribute to the delete buttons so they don't cause a submit by accident.
|
79
|
+
const tableDeleteButtons = document.querySelectorAll('.remirror-table-delete-inner-button');
|
80
|
+
tableDeleteButtons.forEach((button) => {
|
81
|
+
button.setAttribute('type', 'button');
|
82
|
+
});
|
77
83
|
}, []);
|
78
84
|
|
79
85
|
return (
|
@@ -91,7 +97,7 @@ const Editor = ({
|
|
91
97
|
>
|
92
98
|
<Remirror
|
93
99
|
manager={manager}
|
94
|
-
|
100
|
+
initialContent={state}
|
95
101
|
editable={editable}
|
96
102
|
onChange={handleChange}
|
97
103
|
placeholder="Write something"
|
@@ -1,15 +1,20 @@
|
|
1
1
|
import React from 'react';
|
2
|
+
import { ResolveNodeToUrl } from '../types';
|
2
3
|
|
3
4
|
export type EditorContextOptions = {
|
4
5
|
matrix: {
|
5
6
|
matrixDomain: string;
|
6
7
|
};
|
8
|
+
resolveNodeToUrl: ResolveNodeToUrl;
|
7
9
|
};
|
8
10
|
|
9
11
|
export const defaultEditorContext: EditorContextOptions = {
|
10
12
|
matrix: {
|
11
13
|
matrixDomain: '',
|
12
14
|
},
|
15
|
+
resolveNodeToUrl: () => {
|
16
|
+
throw new Error('resolveNodeToUrl has not been configured.');
|
17
|
+
},
|
13
18
|
};
|
14
19
|
|
15
20
|
export const EditorContext = React.createContext(defaultEditorContext);
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import '@testing-library/jest-dom';
|
2
|
-
import { render, screen, act, fireEvent } from '@testing-library/react';
|
2
|
+
import { render, screen, act, fireEvent, waitFor } from '@testing-library/react';
|
3
3
|
import React from 'react';
|
4
4
|
import ImageForm from './ImageForm';
|
5
5
|
import { NodeName } from '../../../../Extensions/Extensions';
|
@@ -48,7 +48,7 @@ describe('Image Form', () => {
|
|
48
48
|
data={{
|
49
49
|
...data,
|
50
50
|
imageType: NodeName.AssetImage,
|
51
|
-
assetImage: { matrixAssetId: '100', matrixIdentifier: 'matrix-identifier' },
|
51
|
+
assetImage: { matrixAssetId: '100', matrixIdentifier: 'matrix-api-identifier' },
|
52
52
|
}}
|
53
53
|
onSubmit={handleSubmit}
|
54
54
|
/>
|
@@ -56,7 +56,9 @@ describe('Image Form', () => {
|
|
56
56
|
);
|
57
57
|
|
58
58
|
expect(document.querySelector('div[data-headlessui-state="selected"]')).toHaveTextContent('From source');
|
59
|
-
|
59
|
+
await waitFor(() => {
|
60
|
+
expect(screen.getByText('My selected image')).toBeInTheDocument();
|
61
|
+
});
|
60
62
|
});
|
61
63
|
|
62
64
|
it('calculates the height when width changes and aspect ratio is locked', () => {
|
@@ -269,6 +269,9 @@ describe('ImageButton', () => {
|
|
269
269
|
// open the modal and add an image.
|
270
270
|
await openModal();
|
271
271
|
fireEvent.click(screen.getByRole('button', { name: 'From source' }));
|
272
|
+
await waitFor(() => {
|
273
|
+
screen.getByRole('button', { name: 'Choose image' });
|
274
|
+
});
|
272
275
|
await selectResource(screen.getByRole('button', { name: 'Choose image' }), 'My image resource');
|
273
276
|
fireEvent.click(screen.getByRole('button', { name: 'Apply' }));
|
274
277
|
|
@@ -329,6 +332,9 @@ describe('ImageButton', () => {
|
|
329
332
|
|
330
333
|
await openModal();
|
331
334
|
fireEvent.click(screen.getByRole('button', { name: 'From source' }));
|
335
|
+
await waitFor(() => {
|
336
|
+
screen.getByRole('button', { name: 'Choose image' });
|
337
|
+
});
|
332
338
|
await selectResource(screen.getByRole('button', { name: 'Choose image' }), 'My image resource');
|
333
339
|
fireEvent.click(screen.getByRole('button', { name: 'Apply' }));
|
334
340
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import '@testing-library/jest-dom';
|
2
|
-
import { render, screen } from '@testing-library/react';
|
2
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
3
3
|
import React from 'react';
|
4
4
|
import { LinkForm } from './LinkForm';
|
5
5
|
import { LinkTarget } from '../../../../Extensions/LinkExtension/common';
|
@@ -19,20 +19,28 @@ describe('Link Form', () => {
|
|
19
19
|
},
|
20
20
|
assetLink: {
|
21
21
|
matrixAssetId: '100',
|
22
|
-
matrixIdentifier: 'matrix-identifier',
|
22
|
+
matrixIdentifier: 'matrix-api-identifier',
|
23
23
|
matrixDomain: 'my-matrix.squiz.net',
|
24
24
|
target: LinkTarget.Blank,
|
25
25
|
},
|
26
26
|
};
|
27
27
|
|
28
|
-
it('Renders the form with expected default values when no data is provided', () => {
|
29
|
-
|
28
|
+
it('Renders the form with expected default values when no data is provided', async () => {
|
29
|
+
const { MockResourceBrowserContext } = mockResourceBrowserContext({ sources: [], resources: [] });
|
30
|
+
render(
|
31
|
+
<MockResourceBrowserContext>
|
32
|
+
<LinkForm onSubmit={handleSubmit} />
|
33
|
+
</MockResourceBrowserContext>,
|
34
|
+
);
|
30
35
|
|
31
36
|
expect(document.querySelector('div[data-headlessui-state="selected"]')).toHaveTextContent('From source');
|
32
|
-
expect(screen.queryByRole('button', { name: 'Choose asset' })).toBeInTheDocument();
|
33
37
|
expect(screen.getByLabelText('Text')).toHaveValue('');
|
34
38
|
expect(document.querySelector('div.squiz-fte-checkbox')).toHaveTextContent('Open link in new window');
|
35
39
|
expect(document.querySelectorAll('label')).toHaveLength(1);
|
40
|
+
|
41
|
+
await waitFor(() => {
|
42
|
+
expect(screen.queryByRole('button', { name: 'Choose asset' })).toBeInTheDocument();
|
43
|
+
});
|
36
44
|
});
|
37
45
|
|
38
46
|
it('Renders the form with the expected fields for arbitrary links', () => {
|
@@ -46,7 +54,7 @@ describe('Link Form', () => {
|
|
46
54
|
expect(document.querySelectorAll('label')).toHaveLength(3);
|
47
55
|
});
|
48
56
|
|
49
|
-
it('Renders the form with the expected fields for asset links', () => {
|
57
|
+
it('Renders the form with the expected fields for asset links', async () => {
|
50
58
|
const { MockResourceBrowserContext } = mockResourceBrowserContext({
|
51
59
|
sources: [{ id: 'my-source-id' }],
|
52
60
|
resources: [{ id: '100', name: 'My selected resource' }],
|
@@ -59,9 +67,13 @@ describe('Link Form', () => {
|
|
59
67
|
);
|
60
68
|
|
61
69
|
expect(document.querySelector('div[data-headlessui-state="selected"]')).toHaveTextContent('From source');
|
62
|
-
|
70
|
+
|
63
71
|
expect(screen.getByLabelText('Text')).toHaveValue('Link text');
|
64
72
|
expect(document.querySelector('div.squiz-fte-checkbox')).toHaveTextContent('Open link in new window');
|
65
73
|
expect(document.querySelectorAll('label')).toHaveLength(1);
|
74
|
+
|
75
|
+
await waitFor(() => {
|
76
|
+
expect(screen.getByText('My selected resource')).toBeInTheDocument();
|
77
|
+
});
|
66
78
|
});
|
67
79
|
});
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import '@testing-library/jest-dom';
|
2
|
-
import { act, screen, fireEvent, waitForElementToBeRemoved } from '@testing-library/react';
|
2
|
+
import { act, screen, fireEvent, waitForElementToBeRemoved, waitFor } from '@testing-library/react';
|
3
3
|
import React from 'react';
|
4
4
|
import { renderWithEditor, mockResourceBrowserContext } from '../../../../tests';
|
5
5
|
import LinkButton from './LinkButton';
|
@@ -258,6 +258,9 @@ describe('LinkButton', () => {
|
|
258
258
|
|
259
259
|
await openModal();
|
260
260
|
fireEvent.click(screen.getByRole('button', { name: 'From source' }));
|
261
|
+
await waitFor(() => {
|
262
|
+
expect(screen.getByRole('button', { name: 'Choose asset' })).toBeInTheDocument();
|
263
|
+
});
|
261
264
|
await selectResource(screen.getByRole('button', { name: 'Choose asset' }), 'My resource');
|
262
265
|
fireEvent.change(screen.getByLabelText('Text'), { target: { value: 'Link text' } });
|
263
266
|
fireEvent.click(screen.getByRole('button', { name: 'Apply' }));
|
@@ -318,6 +321,9 @@ describe('LinkButton', () => {
|
|
318
321
|
|
319
322
|
await openModal();
|
320
323
|
fireEvent.click(screen.getByRole('button', { name: 'From source' }));
|
324
|
+
await waitFor(() => {
|
325
|
+
expect(screen.getByRole('button', { name: 'Choose asset' })).toBeInTheDocument();
|
326
|
+
});
|
321
327
|
await selectResource(screen.getByRole('button', { name: 'Choose asset' }), 'My resource');
|
322
328
|
fireEvent.click(document.querySelector('div.squiz-fte-checkbox button') as HTMLButtonElement);
|
323
329
|
fireEvent.click(screen.getByRole('button', { name: 'Apply' }));
|
@@ -20,7 +20,7 @@ describe('RemoveLinkButton', () => {
|
|
20
20
|
marks: [
|
21
21
|
{
|
22
22
|
type: 'assetLink',
|
23
|
-
attrs: { matrixAssetId: '123', matrixIdentifier: 'matrix-identifier', target: '_blank' },
|
23
|
+
attrs: { matrixAssetId: '123', matrixIdentifier: 'matrix-api-identifier', target: '_blank' },
|
24
24
|
},
|
25
25
|
],
|
26
26
|
},
|
@@ -65,7 +65,7 @@ describe('RemoveLinkButton', () => {
|
|
65
65
|
marks: [
|
66
66
|
{
|
67
67
|
type: 'assetLink',
|
68
|
-
attrs: { matrixAssetId: '123', matrixIdentifier: 'matrix-identifier', target: '_blank' },
|
68
|
+
attrs: { matrixAssetId: '123', matrixIdentifier: 'matrix-api-identifier', target: '_blank' },
|
69
69
|
},
|
70
70
|
],
|
71
71
|
},
|
@@ -123,7 +123,7 @@ describe('RemoveLinkButton', () => {
|
|
123
123
|
marks: [
|
124
124
|
{
|
125
125
|
type: 'assetLink',
|
126
|
-
attrs: { matrixAssetId: '123', matrixIdentifier: 'matrix-identifier', target: '_blank' },
|
126
|
+
attrs: { matrixAssetId: '123', matrixIdentifier: 'matrix-api-identifier', target: '_blank' },
|
127
127
|
},
|
128
128
|
],
|
129
129
|
},
|
@@ -26,7 +26,6 @@ import { AssetImageExtension } from './ImageExtension/AssetImageExtension';
|
|
26
26
|
import { ExtendedCodeBlockExtension } from './CodeBlockExtension/CodeBlockExtension';
|
27
27
|
import { ClearFormattingExtension } from './ClearFormattingExtension/ClearFormattingExtension';
|
28
28
|
import { UnsupportedNodeExtension } from './UnsuportedExtension/UnsupportedNodeExtension';
|
29
|
-
import { ResourceBrowserContextProps } from '@squiz/resource-browser';
|
30
29
|
import { FetchUrlExtension } from './FetchUrlExtension/FetchUrlExtension';
|
31
30
|
import { TableExtension } from '@remirror/extension-react-tables';
|
32
31
|
import { ReactComponentExtension } from '@remirror/extension-react-component';
|
@@ -36,6 +35,8 @@ export enum NodeName {
|
|
36
35
|
CodeBlock = 'codeBlock',
|
37
36
|
AssetImage = 'assetImage',
|
38
37
|
Text = 'text',
|
38
|
+
TableControllerCell = 'tableControllerCell',
|
39
|
+
tableCell = 'tableCell',
|
39
40
|
hardBreak = 'hardBreak',
|
40
41
|
Unsupported = 'unsupportedNode',
|
41
42
|
}
|
@@ -45,7 +46,7 @@ export enum MarkName {
|
|
45
46
|
AssetLink = 'assetLink',
|
46
47
|
}
|
47
48
|
|
48
|
-
export const createExtensions = (context: EditorContextOptions
|
49
|
+
export const createExtensions = (context: EditorContextOptions) => {
|
49
50
|
return (): Extension[] => {
|
50
51
|
return [
|
51
52
|
new CommandsExtension(),
|
@@ -77,7 +78,7 @@ export const createExtensions = (context: EditorContextOptions, browserContext:
|
|
77
78
|
new HorizontalRuleExtension(),
|
78
79
|
new PlaceholderExtension(),
|
79
80
|
new FetchUrlExtension({
|
80
|
-
fetchUrl:
|
81
|
+
fetchUrl: context.resolveNodeToUrl,
|
81
82
|
}),
|
82
83
|
new TextExtension(),
|
83
84
|
new TableExtension(),
|
@@ -1,15 +1,14 @@
|
|
1
1
|
import { extension, PlainExtension, Mark } from '@remirror/core';
|
2
2
|
import { Dispose, EditorView } from '@remirror/core-types';
|
3
|
-
import { Resource } from '@squiz/resource-browser';
|
4
3
|
import { MarkName, NodeName } from '../Extensions';
|
4
|
+
import { ResolveNodeToUrl, ResolveNodeType } from '../../types';
|
5
5
|
|
6
6
|
export type FetchUrlOptions = {
|
7
|
-
fetchUrl?:
|
7
|
+
fetchUrl?: ResolveNodeToUrl;
|
8
8
|
};
|
9
|
-
|
10
9
|
@extension<FetchUrlOptions>({
|
11
10
|
defaultOptions: {
|
12
|
-
fetchUrl: () => Promise.resolve(),
|
11
|
+
fetchUrl: () => Promise.resolve(''),
|
13
12
|
},
|
14
13
|
})
|
15
14
|
export class FetchUrlExtension extends PlainExtension<FetchUrlOptions> {
|
@@ -27,7 +26,7 @@ export class FetchUrlExtension extends PlainExtension<FetchUrlOptions> {
|
|
27
26
|
state.doc.descendants((node, pos) => {
|
28
27
|
if (node.type.name === NodeName.AssetImage && node.attrs.src === '') {
|
29
28
|
promises.push(
|
30
|
-
this.fetchAndReplace(node.attrs
|
29
|
+
this.fetchAndReplace(node.attrs, (url: string) => {
|
31
30
|
const newNode = state.schema.nodes[NodeName.AssetImage].create({ ...node.attrs, src: url });
|
32
31
|
tr.replaceWith(pos, pos + node.nodeSize, newNode);
|
33
32
|
}),
|
@@ -37,14 +36,10 @@ export class FetchUrlExtension extends PlainExtension<FetchUrlOptions> {
|
|
37
36
|
const assetLinkMark = this.findAssetLinkMark(node.marks as Mark[]);
|
38
37
|
if (node.type.name === 'text' && assetLinkMark) {
|
39
38
|
promises.push(
|
40
|
-
this.fetchAndReplace(
|
41
|
-
assetLinkMark.attrs
|
42
|
-
|
43
|
-
|
44
|
-
const updatedMark = assetLinkMark.type.create({ ...assetLinkMark.attrs, href: url });
|
45
|
-
tr.addMark(pos, pos + node.nodeSize, updatedMark);
|
46
|
-
},
|
47
|
-
),
|
39
|
+
this.fetchAndReplace(assetLinkMark.attrs, (url: string) => {
|
40
|
+
const updatedMark = assetLinkMark.type.create({ ...assetLinkMark.attrs, href: url });
|
41
|
+
tr.addMark(pos, pos + node.nodeSize, updatedMark);
|
42
|
+
}),
|
48
43
|
);
|
49
44
|
}
|
50
45
|
});
|
@@ -60,11 +55,11 @@ export class FetchUrlExtension extends PlainExtension<FetchUrlOptions> {
|
|
60
55
|
return marks.find((mark) => mark.type.name === MarkName.AssetLink && mark.attrs.href === '');
|
61
56
|
}
|
62
57
|
|
63
|
-
private fetchAndReplace(
|
58
|
+
private fetchAndReplace(nodeAttrs: ResolveNodeType, onFetched: (url: string) => void): Promise<void> {
|
64
59
|
return this.options
|
65
|
-
.fetchUrl(
|
66
|
-
.then((
|
67
|
-
onFetched(
|
60
|
+
.fetchUrl(nodeAttrs)
|
61
|
+
.then((url: string) => {
|
62
|
+
onFetched(url);
|
68
63
|
})
|
69
64
|
.catch((error) => {
|
70
65
|
console.error('Error fetching URL:', error);
|
@@ -131,7 +131,7 @@ describe('UnsupportedNodeExtension', () => {
|
|
131
131
|
});
|
132
132
|
|
133
133
|
expect(getHtmlContent()).toBe(
|
134
|
-
'<span class="unsupported-node-node-view-wrapper" originalnode="null" errormessage="null" data-unsupported-node="{"originalNode":null,"errorMessage":null}"><div class="collapse-box" contenteditable="false"><button class="collapse-box__header" type="button"><svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium collapse-box__icon--warning css-i4bv87-MuiSvgIcon-root" focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="MotionPhotosOffOutlinedIcon"><path d="M2.81 2.81 1.39 4.22l2.27 2.27C2.61 8.07 2 9.96 2 12c0 5.52 4.48 10 10 10 2.04 0 3.93-.61 5.51-1.66l2.27 2.27 1.41-1.
|
134
|
+
'<span class="unsupported-node-node-view-wrapper" originalnode="null" errormessage="null" data-unsupported-node="{"originalNode":null,"errorMessage":null}"><div class="collapse-box" contenteditable="false"><button class="collapse-box__header" type="button"><svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium collapse-box__icon--warning css-i4bv87-MuiSvgIcon-root" focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="MotionPhotosOffOutlinedIcon"><path d="M2.81 2.81 1.39 4.22l2.27 2.27C2.61 8.07 2 9.96 2 12c0 5.52 4.48 10 10 10 2.04 0 3.93-.61 5.51-1.66l2.27 2.27 1.41-1.42zM12 20c-4.41 0-8-3.59-8-8 0-1.48.41-2.86 1.12-4.06l10.93 10.94C14.86 19.59 13.48 20 12 20m0-16c4.41 0 8 3.59 8 8 0 1.48-.41 2.86-1.12 4.05l1.45 1.45C21.39 15.93 22 14.04 22 12c0-5.52-4.48-10-10-10-2.04 0-3.93.61-5.51 1.66l1.45 1.45C9.14 4.41 10.52 4 12 4"></path></svg><div class="collapse-box__label">This section cannot be displayed here due to unsupported HTML elements. The front-end view of your page won’t be affected.</div><svg class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium css-i4bv87-MuiSvgIcon-root" focusable="false" aria-hidden="true" viewBox="0 0 24 24" data-testid="UnfoldLessOutlinedIcon"><path d="M7.41 18.59 8.83 20 12 16.83 15.17 20l1.41-1.41L12 14zm9.18-13.18L15.17 4 12 7.17 8.83 4 7.41 5.41 12 10z"></path></svg></button><div class="collapse-box__content" hidden="" data-testid="content"><br>null</div></div></span>',
|
135
135
|
);
|
136
136
|
});
|
137
137
|
});
|
package/src/index.ts
CHANGED
@@ -3,5 +3,7 @@ import { EditorContext } from './Editor/EditorContext';
|
|
3
3
|
import { remirrorNodeToSquizNode } from './utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode';
|
4
4
|
import { squizNodeToRemirrorNode } from './utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode';
|
5
5
|
import { htmlToSquizNode } from './utils/converters/htmlToSquizNode/htmlToSquizNode';
|
6
|
+
import { ResolveNodeType, ResolveNodeToUrl } from './types';
|
6
7
|
|
7
8
|
export { Editor, EditorContext, remirrorNodeToSquizNode, squizNodeToRemirrorNode, htmlToSquizNode };
|
9
|
+
export type { ResolveNodeType, ResolveNodeToUrl };
|
package/src/types.ts
CHANGED
@@ -1,17 +1,27 @@
|
|
1
1
|
import '@testing-library/jest-dom';
|
2
2
|
import React from 'react';
|
3
|
-
import { fireEvent, render, screen } from '@testing-library/react';
|
3
|
+
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
4
4
|
import { MatrixAsset } from './MatrixAsset';
|
5
5
|
import { mockResourceBrowserContext } from '../../../../tests';
|
6
6
|
|
7
7
|
describe('MatrixAsset', () => {
|
8
|
-
it('Renders empty state when no value is provided', () => {
|
9
|
-
|
8
|
+
it('Renders empty state when no value is provided', async () => {
|
9
|
+
const { MockResourceBrowserContext } = mockResourceBrowserContext({
|
10
|
+
sources: [],
|
11
|
+
resources: [],
|
12
|
+
});
|
13
|
+
render(
|
14
|
+
<MockResourceBrowserContext>
|
15
|
+
<MatrixAsset modalTitle="Insert asset" onChange={jest.fn()} />
|
16
|
+
</MockResourceBrowserContext>,
|
17
|
+
);
|
10
18
|
|
11
|
-
|
19
|
+
await waitFor(() => {
|
20
|
+
expect(screen.getByRole('button', { name: 'Choose asset' })).toBeInTheDocument();
|
21
|
+
});
|
12
22
|
});
|
13
23
|
|
14
|
-
it('Renders a selected state when a value is provided', () => {
|
24
|
+
it('Renders a selected state when a value is provided', async () => {
|
15
25
|
const { MockResourceBrowserContext } = mockResourceBrowserContext({
|
16
26
|
sources: [{ id: 'my-source-id' }],
|
17
27
|
resources: [{ id: 'my-resource-id', name: 'My resource' }],
|
@@ -22,7 +32,7 @@ describe('MatrixAsset', () => {
|
|
22
32
|
<MatrixAsset
|
23
33
|
modalTitle="Insert asset"
|
24
34
|
value={{
|
25
|
-
matrixIdentifier: '
|
35
|
+
matrixIdentifier: 'matrix-api-identifier',
|
26
36
|
matrixAssetId: 'my-resource-id',
|
27
37
|
addional: 'addditional data',
|
28
38
|
}}
|
@@ -30,8 +40,9 @@ describe('MatrixAsset', () => {
|
|
30
40
|
/>
|
31
41
|
</MockResourceBrowserContext>,
|
32
42
|
);
|
33
|
-
|
34
|
-
|
43
|
+
await waitFor(() => {
|
44
|
+
expect(screen.getByText('My resource')).toBeInTheDocument();
|
45
|
+
});
|
35
46
|
});
|
36
47
|
|
37
48
|
it('Calls onChange with expected value when resources is selected', async () => {
|
@@ -51,6 +62,9 @@ describe('MatrixAsset', () => {
|
|
51
62
|
</MockResourceBrowserContext>,
|
52
63
|
);
|
53
64
|
|
65
|
+
await waitFor(() => {
|
66
|
+
expect(screen.getByRole('button', { name: 'Choose asset' })).toBeInTheDocument();
|
67
|
+
});
|
54
68
|
await selectResource(screen.getByRole('button', { name: 'Choose asset' }), 'My resource');
|
55
69
|
|
56
70
|
expect(handleChange).toHaveBeenCalledWith({
|
@@ -58,7 +72,7 @@ describe('MatrixAsset', () => {
|
|
58
72
|
value: {
|
59
73
|
additional: 'additional data',
|
60
74
|
matrixAssetId: 'my-resource-id',
|
61
|
-
matrixIdentifier: '
|
75
|
+
matrixIdentifier: 'matrix-api-identifier',
|
62
76
|
url: 'https://default-resource/',
|
63
77
|
},
|
64
78
|
},
|
@@ -89,6 +103,9 @@ describe('MatrixAsset', () => {
|
|
89
103
|
</MockResourceBrowserContext>,
|
90
104
|
);
|
91
105
|
|
106
|
+
await waitFor(() => {
|
107
|
+
expect(screen.getByRole('button', { name: 'Remove selection' })).toBeInTheDocument();
|
108
|
+
});
|
92
109
|
fireEvent.click(screen.getByRole('button', { name: 'Remove selection' }));
|
93
110
|
|
94
111
|
expect(handleChange).toHaveBeenCalledWith({
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import React from 'react';
|
2
|
-
import {
|
2
|
+
import { ResourceBrowserResource, ResourceBrowser } from '@squiz/resource-browser';
|
3
3
|
import { InputContainer, InputContainerProps } from '../InputContainer/InputContainer';
|
4
4
|
|
5
5
|
type MatrixAssetValue = {
|
@@ -28,25 +28,25 @@ export const MatrixAsset = <T extends MatrixAssetValue>({
|
|
28
28
|
}: MatrixAssetProps<T>) => {
|
29
29
|
return (
|
30
30
|
<InputContainer {...props}>
|
31
|
-
<
|
31
|
+
<ResourceBrowser
|
32
32
|
modalTitle={modalTitle}
|
33
33
|
allowedTypes={allowedTypes}
|
34
34
|
value={
|
35
35
|
value && value.matrixIdentifier && value.matrixAssetId
|
36
36
|
? {
|
37
|
-
|
38
|
-
|
37
|
+
sourceId: value.matrixIdentifier,
|
38
|
+
resourceId: value.matrixAssetId,
|
39
39
|
}
|
40
40
|
: null
|
41
41
|
}
|
42
|
-
onChange={(
|
42
|
+
onChange={(resource: ResourceBrowserResource | null) => {
|
43
43
|
onChange({
|
44
44
|
target: {
|
45
45
|
value: {
|
46
46
|
...value,
|
47
|
-
matrixIdentifier:
|
48
|
-
matrixAssetId:
|
49
|
-
url:
|
47
|
+
matrixIdentifier: resource?.source?.id,
|
48
|
+
matrixAssetId: resource?.id,
|
49
|
+
url: resource?.url,
|
50
50
|
} as T,
|
51
51
|
},
|
52
52
|
});
|
@@ -8,24 +8,14 @@ import { HtmlToSquizNodeProps } from './htmlToSquizNode.props';
|
|
8
8
|
type FormattedText = FORMATTED_TEXT_MODELS.v1.FormattedText;
|
9
9
|
|
10
10
|
export const htmlToSquizNode = ({ content }: HtmlToSquizNodeProps): FormattedText | undefined => {
|
11
|
-
const extensions = createExtensions(
|
12
|
-
{
|
13
|
-
|
14
|
-
matrixDomain: 'unsupported',
|
15
|
-
},
|
11
|
+
const extensions = createExtensions({
|
12
|
+
matrix: {
|
13
|
+
matrixDomain: 'unsupported',
|
16
14
|
},
|
17
|
-
{
|
18
|
-
|
19
|
-
throw new Error('Resolving Matrix assets is not supported.');
|
20
|
-
},
|
21
|
-
onRequestSources: () => {
|
22
|
-
throw new Error('Resolving Matrix assets is not supported.');
|
23
|
-
},
|
24
|
-
onRequestChildren: () => {
|
25
|
-
throw new Error('Resolving Matrix assets is not supported.');
|
26
|
-
},
|
15
|
+
resolveNodeToUrl: () => {
|
16
|
+
throw new Error('Resolving Url is not supported.');
|
27
17
|
},
|
28
|
-
);
|
18
|
+
});
|
29
19
|
const manager = RemirrorManager.create(extensions);
|
30
20
|
|
31
21
|
return remirrorNodeToSquizNode(
|