@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.
Files changed (44) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/demo/App.tsx +7 -1
  3. package/demo/AppContext.tsx +49 -18
  4. package/demo/resources.json +44 -0
  5. package/demo/sources.json +4 -0
  6. package/lib/Editor/Editor.js +8 -3
  7. package/lib/Editor/EditorContext.d.ts +2 -0
  8. package/lib/Editor/EditorContext.js +3 -0
  9. package/lib/Extensions/Extensions.d.ts +3 -2
  10. package/lib/Extensions/Extensions.js +4 -2
  11. package/lib/Extensions/FetchUrlExtension/FetchUrlExtension.d.ts +2 -4
  12. package/lib/Extensions/FetchUrlExtension/FetchUrlExtension.js +7 -7
  13. package/lib/index.d.ts +2 -0
  14. package/lib/types.d.ts +4 -0
  15. package/lib/ui/Fields/MatrixAsset/MatrixAsset.js +7 -7
  16. package/lib/utils/converters/htmlToSquizNode/htmlToSquizNode.js +2 -9
  17. package/lib/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.js +19 -4
  18. package/lib/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.js +23 -0
  19. package/package.json +11 -10
  20. package/src/Editor/Editor.spec.tsx +14 -4
  21. package/src/Editor/Editor.tsx +9 -3
  22. package/src/Editor/EditorContext.spec.tsx +1 -0
  23. package/src/Editor/EditorContext.ts +5 -0
  24. package/src/EditorToolbar/Tools/Image/Form/ImageForm.spec.tsx +5 -3
  25. package/src/EditorToolbar/Tools/Image/ImageButton.spec.tsx +6 -0
  26. package/src/EditorToolbar/Tools/Link/Form/LinkForm.spec.tsx +19 -7
  27. package/src/EditorToolbar/Tools/Link/LinkButton.spec.tsx +7 -1
  28. package/src/EditorToolbar/Tools/Link/RemoveLinkButton.spec.tsx +3 -3
  29. package/src/Extensions/Extensions.ts +4 -3
  30. package/src/Extensions/FetchUrlExtension/FetchUrlExtension.ts +12 -17
  31. package/src/Extensions/UnsuportedExtension/UnsupportedNodeExtension.spec.ts +1 -1
  32. package/src/index.ts +2 -0
  33. package/src/types.ts +3 -0
  34. package/src/ui/Fields/MatrixAsset/MatrixAsset.spec.tsx +26 -9
  35. package/src/ui/Fields/MatrixAsset/MatrixAsset.tsx +8 -8
  36. package/src/utils/converters/htmlToSquizNode/htmlToSquizNode.ts +6 -16
  37. package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.spec.ts +457 -0
  38. package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.ts +24 -5
  39. package/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.spec.ts +210 -0
  40. package/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.ts +23 -0
  41. package/tests/mockResourceBrowserContext.tsx +48 -12
  42. package/tests/renderWithContext.tsx +31 -3
  43. package/tests/renderWithEditor.tsx +1 -3
  44. 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 { ResourceBrowserContext } from '@squiz/resource-browser';
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
- <ResourceBrowserContext.Provider value={{ onRequestSources, onRequestChildren, onRequestResource }}>
446
+ <MockResourceBrowserContext>
437
447
  <Editor />
438
- </ResourceBrowserContext.Provider>,
448
+ </MockResourceBrowserContext>,
439
449
  );
440
450
 
441
451
  await act(() => fireEvent.click(screen.getByRole('button', { name: 'Link (Ctrl+K)' })));
@@ -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), useContext(ResourceBrowserContext)),
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
- state={state}
100
+ initialContent={state}
95
101
  editable={editable}
96
102
  onChange={handleChange}
97
103
  placeholder="Write something"
@@ -19,6 +19,7 @@ describe('EditorContext', () => {
19
19
  matrix: {
20
20
  matrixDomain: '',
21
21
  },
22
+ resolveNodeToUrl: expect.any(Function),
22
23
  });
23
24
  });
24
25
  });
@@ -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
- expect(screen.getByText('My selected image')).toBeInTheDocument();
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
- render(<LinkForm onSubmit={handleSubmit} />);
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
- expect(screen.getByText('My selected resource')).toBeInTheDocument();
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, browserContext: ResourceBrowserContextProps) => {
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: browserContext.onRequestResource,
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?: (params: { resource: string; source: string }) => Promise<any>;
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.matrixAssetId, node.attrs.matrixIdentifier, (url: string) => {
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.matrixAssetId,
42
- assetLinkMark.attrs.matrixIdentifier,
43
- (url: string) => {
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(resource: string, source: string, onFetched: (url: string) => void): Promise<void> {
58
+ private fetchAndReplace(nodeAttrs: ResolveNodeType, onFetched: (url: string) => void): Promise<void> {
64
59
  return this.options
65
- .fetchUrl({ resource, source })
66
- .then((asset: Resource) => {
67
- onFetched(asset.url);
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="{&quot;originalNode&quot;:null,&quot;errorMessage&quot;: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.42L2.81 2.81zM12 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 20zm0-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 4z"></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 14l-4.59 4.59zm9.18-13.18L15.17 4 12 7.17 8.83 4 7.41 5.41 12 10l4.59-4.59z"></path></svg></button><div class="collapse-box__content" hidden="" data-testid="content"><br>null</div></div></span>',
134
+ '<span class="unsupported-node-node-view-wrapper" originalnode="null" errormessage="null" data-unsupported-node="{&quot;originalNode&quot;:null,&quot;errorMessage&quot;: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
@@ -5,3 +5,6 @@ export type DeepPartial<T> = {
5
5
  ? ReadonlyArray<DeepPartial<U>>
6
6
  : DeepPartial<T[P]>;
7
7
  };
8
+
9
+ export type ResolveNodeType = { [attr: string]: any };
10
+ export type ResolveNodeToUrl = (node: ResolveNodeType) => Promise<string>;
@@ -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
- render(<MatrixAsset modalTitle="Insert asset" onChange={jest.fn()} />);
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
- expect(screen.getByRole('button', { name: 'Choose asset' })).toBeInTheDocument();
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: 'my-source-id',
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
- expect(screen.getByText('My resource')).toBeInTheDocument();
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: 'my-source-id',
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 { HydratedResourceReference, ResourceBrowserInput } from '@squiz/resource-browser';
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
- <ResourceBrowserInput
31
+ <ResourceBrowser
32
32
  modalTitle={modalTitle}
33
33
  allowedTypes={allowedTypes}
34
34
  value={
35
35
  value && value.matrixIdentifier && value.matrixAssetId
36
36
  ? {
37
- source: value.matrixIdentifier,
38
- resource: value.matrixAssetId,
37
+ sourceId: value.matrixIdentifier,
38
+ resourceId: value.matrixAssetId,
39
39
  }
40
40
  : null
41
41
  }
42
- onChange={(reference: HydratedResourceReference | null) => {
42
+ onChange={(resource: ResourceBrowserResource | null) => {
43
43
  onChange({
44
44
  target: {
45
45
  value: {
46
46
  ...value,
47
- matrixIdentifier: reference?.source?.id,
48
- matrixAssetId: reference?.resource?.id,
49
- url: reference?.resource?.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
- matrix: {
14
- matrixDomain: 'unsupported',
15
- },
11
+ const extensions = createExtensions({
12
+ matrix: {
13
+ matrixDomain: 'unsupported',
16
14
  },
17
- {
18
- onRequestResource: () => {
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(