@squiz/formatted-text-editor 2.6.4 → 2.6.6
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 +14 -0
- package/demo/diff/AppContext.tsx +36 -2
- package/demo/diff/contentApi.json +21 -0
- package/demo/diff/index.scss +2 -0
- package/lib/EditorToolbar/Tools/Link/Form/LinkForm.d.ts +3 -2
- package/lib/EditorToolbar/Tools/Link/Form/LinkForm.js +18 -4
- package/lib/EditorToolbar/Tools/Link/LinkButton.js +11 -7
- package/lib/EditorToolbar/Tools/Link/LinkModal.js +8 -4
- package/lib/EditorToolbar/Tools/Link/RemoveLinkButton.js +2 -2
- package/lib/Extensions/Extensions.d.ts +2 -1
- package/lib/Extensions/Extensions.js +3 -0
- package/lib/Extensions/FetchUrlExtension/FetchUrlExtension.d.ts +1 -0
- package/lib/Extensions/FetchUrlExtension/FetchUrlExtension.js +10 -0
- package/lib/Extensions/LinkExtension/AssetLinkExtension.js +1 -1
- package/lib/Extensions/LinkExtension/DamLinkExtension.d.ts +29 -0
- package/lib/Extensions/LinkExtension/DamLinkExtension.js +111 -0
- package/lib/Extensions/LinkExtension/LinkExtension.js +1 -1
- package/lib/ui/Fields/MatrixAsset/MatrixAsset.d.ts +16 -2
- package/lib/ui/Fields/MatrixAsset/MatrixAsset.js +71 -20
- package/lib/ui/Fields/ResourceBrowserSelector/ResourceBrowserSelector.d.ts +3 -0
- package/lib/ui/Fields/ResourceBrowserSelector/ResourceBrowserSelector.js +8 -0
- package/lib/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.js +15 -1
- package/lib/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.js +13 -0
- package/package.json +9 -9
- package/src/Editor/Editor.spec.tsx +1 -0
- package/src/EditorToolbar/Tools/Link/Form/LinkForm.tsx +26 -7
- package/src/EditorToolbar/Tools/Link/LinkButton.spec.tsx +74 -1
- package/src/EditorToolbar/Tools/Link/LinkButton.tsx +19 -8
- package/src/EditorToolbar/Tools/Link/LinkModal.tsx +10 -5
- package/src/EditorToolbar/Tools/Link/RemoveLinkButton.tsx +4 -3
- package/src/Extensions/Extensions.ts +3 -0
- package/src/Extensions/FetchUrlExtension/FetchUrlExtension.ts +14 -0
- package/src/Extensions/LinkExtension/AssetLinkExtension.ts +1 -1
- package/src/Extensions/LinkExtension/DamLinkExtension.spec.ts +110 -0
- package/src/Extensions/LinkExtension/DamLinkExtension.ts +137 -0
- package/src/Extensions/LinkExtension/LinkExtension.ts +1 -1
- package/src/ui/Fields/MatrixAsset/MatrixAsset.tsx +83 -26
- package/src/ui/Fields/ResourceBrowserSelector/ResourceBrowserSelector.spec.tsx +71 -1
- package/src/ui/Fields/ResourceBrowserSelector/ResourceBrowserSelector.tsx +30 -16
- package/src/utils/converters/mocks/squizNodeJson.mock.ts +48 -0
- package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.spec.ts +63 -0
- package/src/utils/converters/remirrorNodeToSquizNode/remirrorNodeToSquizNode.ts +16 -1
- package/src/utils/converters/squizNodeToRemirrorNode/squizNodeToRemirrorNode.ts +12 -0
- package/src/utils/getMarkNamesByGroup.spec.ts +1 -1
- package/tests/renderWithContext.tsx +2 -0
@@ -0,0 +1,137 @@
|
|
1
|
+
import {
|
2
|
+
ApplySchemaAttributes,
|
3
|
+
ExtensionPriority,
|
4
|
+
FromToProps,
|
5
|
+
isElementDomNode,
|
6
|
+
MarkExtensionSpec,
|
7
|
+
MarkSpecOverride,
|
8
|
+
} from 'remirror';
|
9
|
+
import { command, CommandFunction, extension, MarkExtension, omitExtraAttributes, removeMark } from '@remirror/core';
|
10
|
+
import { LinkTarget, validateTarget } from './common';
|
11
|
+
import { CommandsExtension } from '../CommandsExtension/CommandsExtension';
|
12
|
+
import { MarkName } from '../Extensions';
|
13
|
+
|
14
|
+
export type DAMLinkAttributes = {
|
15
|
+
damObjectId: string;
|
16
|
+
damSystemIdentifier: string;
|
17
|
+
damSystemType: string;
|
18
|
+
damAdditional?: {
|
19
|
+
variant: string;
|
20
|
+
};
|
21
|
+
target: LinkTarget;
|
22
|
+
href: string;
|
23
|
+
url: string;
|
24
|
+
};
|
25
|
+
|
26
|
+
export type DAMLinkOptions = {
|
27
|
+
defaultTarget?: LinkTarget;
|
28
|
+
supportedTargets?: LinkTarget[];
|
29
|
+
};
|
30
|
+
|
31
|
+
export type UpdateDAMLinkProps = {
|
32
|
+
text: string;
|
33
|
+
attrs: Partial<DAMLinkAttributes>;
|
34
|
+
range: FromToProps;
|
35
|
+
};
|
36
|
+
|
37
|
+
@extension<DAMLinkOptions>({
|
38
|
+
defaultOptions: {
|
39
|
+
defaultTarget: LinkTarget.Self,
|
40
|
+
supportedTargets: [LinkTarget.Self, LinkTarget.Blank],
|
41
|
+
},
|
42
|
+
defaultPriority: ExtensionPriority.High,
|
43
|
+
})
|
44
|
+
export class DAMLinkExtension extends MarkExtension<DAMLinkOptions> {
|
45
|
+
get name(): string {
|
46
|
+
return MarkName.DAMLink;
|
47
|
+
}
|
48
|
+
|
49
|
+
createMarkSpec(extra: ApplySchemaAttributes, override: MarkSpecOverride): MarkExtensionSpec {
|
50
|
+
return {
|
51
|
+
inclusive: false,
|
52
|
+
excludes: [this.name, MarkName.Link, MarkName.AssetLink].join(' '),
|
53
|
+
...override,
|
54
|
+
attrs: {
|
55
|
+
...extra.defaults(),
|
56
|
+
damObjectId: {},
|
57
|
+
damSystemIdentifier: {},
|
58
|
+
damSystemType: {},
|
59
|
+
damAdditional: { default: undefined },
|
60
|
+
href: { default: '' },
|
61
|
+
target: { default: this.options.defaultTarget },
|
62
|
+
},
|
63
|
+
parseDOM: [
|
64
|
+
{
|
65
|
+
tag: 'a[data-dam-object-id]',
|
66
|
+
getAttrs: (node) => {
|
67
|
+
if (!isElementDomNode(node)) {
|
68
|
+
return false;
|
69
|
+
}
|
70
|
+
const damObjectId = node.getAttribute('data-dam-object-id');
|
71
|
+
const damSystemIdentifier = node.getAttribute('data-dam-system-identifier');
|
72
|
+
const damSystemType = node.getAttribute('data-dam-system-type');
|
73
|
+
let damAdditional = node.getAttribute('data-dam-additional') || undefined;
|
74
|
+
|
75
|
+
if (!damObjectId || !damSystemIdentifier || !damSystemType) {
|
76
|
+
return false;
|
77
|
+
}
|
78
|
+
|
79
|
+
if (damAdditional) {
|
80
|
+
damAdditional = JSON.parse(damAdditional);
|
81
|
+
}
|
82
|
+
|
83
|
+
return {
|
84
|
+
...extra.parse(node),
|
85
|
+
damObjectId,
|
86
|
+
damSystemIdentifier,
|
87
|
+
damSystemType,
|
88
|
+
damAdditional,
|
89
|
+
target: validateTarget(
|
90
|
+
node.getAttribute('target'),
|
91
|
+
this.options.supportedTargets,
|
92
|
+
this.options.defaultTarget,
|
93
|
+
),
|
94
|
+
href: node.getAttribute('href') || '',
|
95
|
+
};
|
96
|
+
},
|
97
|
+
},
|
98
|
+
],
|
99
|
+
toDOM: (node) => {
|
100
|
+
const { damObjectId, damSystemIdentifier, damSystemType, damAdditional, target, href, ...rest } =
|
101
|
+
omitExtraAttributes(node.attrs, extra);
|
102
|
+
const rel = 'noopener noreferrer nofollow';
|
103
|
+
return [
|
104
|
+
'a',
|
105
|
+
{
|
106
|
+
...extra.dom(node),
|
107
|
+
...rest,
|
108
|
+
rel,
|
109
|
+
href,
|
110
|
+
target: validateTarget(target, this.options.supportedTargets, this.options.defaultTarget),
|
111
|
+
'data-dam-object-id': damObjectId,
|
112
|
+
'data-dam-system-identifier': damSystemIdentifier,
|
113
|
+
'data-dam-system-type': damSystemType,
|
114
|
+
'data-dam-additional': damAdditional ? JSON.stringify(damAdditional) : undefined,
|
115
|
+
},
|
116
|
+
0,
|
117
|
+
];
|
118
|
+
},
|
119
|
+
};
|
120
|
+
}
|
121
|
+
|
122
|
+
@command()
|
123
|
+
updateDAMLink({ text, attrs, range }: UpdateDAMLinkProps): CommandFunction {
|
124
|
+
return this.store.getExtension(CommandsExtension).updateMark({
|
125
|
+
attrs: { ...attrs, href: attrs.url },
|
126
|
+
text,
|
127
|
+
range,
|
128
|
+
mark: this.type,
|
129
|
+
removeMark: !attrs.damObjectId,
|
130
|
+
});
|
131
|
+
}
|
132
|
+
|
133
|
+
@command()
|
134
|
+
removeDAMLink(): CommandFunction {
|
135
|
+
return removeMark({ type: this.type });
|
136
|
+
}
|
137
|
+
}
|
@@ -43,7 +43,7 @@ export class LinkExtension extends MarkExtension<LinkOptions> {
|
|
43
43
|
createMarkSpec(extra: ApplySchemaAttributes, override: MarkSpecOverride): MarkExtensionSpec {
|
44
44
|
return {
|
45
45
|
inclusive: false,
|
46
|
-
excludes: [this.name, MarkName.AssetLink].join(' '),
|
46
|
+
excludes: [this.name, MarkName.AssetLink, MarkName.DAMLink].join(' '),
|
47
47
|
...override,
|
48
48
|
attrs: {
|
49
49
|
...extra.defaults(),
|
@@ -1,14 +1,33 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import {
|
1
|
+
import React, { useCallback } from 'react';
|
2
|
+
import { DAMResource, DamResourceBrowserSource } from '@squiz/dam-resource-browser-plugin';
|
3
|
+
import { ResourceBrowser, ResourceBrowserResource, ResourceBrowserUnresolvedResource } from '@squiz/resource-browser';
|
3
4
|
import { InputContainer, InputContainerProps } from '../InputContainer/InputContainer';
|
5
|
+
import { LinkTarget } from '../../../Extensions/LinkExtension/common';
|
4
6
|
|
5
7
|
type MatrixAssetValue = {
|
6
8
|
matrixIdentifier?: string;
|
7
9
|
matrixAssetId?: string;
|
8
|
-
|
10
|
+
matrixDomain?: string;
|
9
11
|
};
|
10
12
|
|
11
|
-
|
13
|
+
type DAMAssetValue = {
|
14
|
+
damSystemIdentifier?: string;
|
15
|
+
damObjectId?: string;
|
16
|
+
damSystemType?: string;
|
17
|
+
damAdditional?: { variant: string };
|
18
|
+
};
|
19
|
+
|
20
|
+
export type ResourceBrowserSelectorValue = MatrixAssetValue &
|
21
|
+
DAMAssetValue & {
|
22
|
+
url?: string;
|
23
|
+
target?: LinkTarget;
|
24
|
+
subType?: 'matrix' | 'dam';
|
25
|
+
};
|
26
|
+
|
27
|
+
export type ResourceBrowserSelectorProps<T extends ResourceBrowserSelectorValue> = Omit<
|
28
|
+
InputContainerProps,
|
29
|
+
'children'
|
30
|
+
> & {
|
12
31
|
modalTitle: string;
|
13
32
|
allowedTypes?: string[];
|
14
33
|
value?: T | null;
|
@@ -19,38 +38,76 @@ export type MatrixAssetProps<T extends MatrixAssetValue> = Omit<InputContainerPr
|
|
19
38
|
onChange: (value: { target: { value: T } }) => void;
|
20
39
|
};
|
21
40
|
|
22
|
-
export const MatrixAsset = <T extends
|
41
|
+
export const MatrixAsset = <T extends ResourceBrowserSelectorValue>({
|
23
42
|
modalTitle,
|
24
43
|
allowedTypes,
|
25
44
|
value,
|
26
45
|
onChange,
|
27
46
|
...props
|
28
|
-
}:
|
47
|
+
}: ResourceBrowserSelectorProps<T>) => {
|
48
|
+
const convertFormDataToResourceBrowserValue = useCallback(
|
49
|
+
(value: T | null): ResourceBrowserUnresolvedResource | null => {
|
50
|
+
if (value && value.damSystemIdentifier && value.damObjectId) {
|
51
|
+
return {
|
52
|
+
sourceId: value.damSystemIdentifier,
|
53
|
+
resourceId: value.damObjectId,
|
54
|
+
variant: value.damAdditional?.variant,
|
55
|
+
};
|
56
|
+
}
|
57
|
+
if (value && value.matrixIdentifier && value.matrixAssetId) {
|
58
|
+
return {
|
59
|
+
sourceId: value.matrixIdentifier,
|
60
|
+
resourceId: value.matrixAssetId,
|
61
|
+
};
|
62
|
+
}
|
63
|
+
return null;
|
64
|
+
},
|
65
|
+
[],
|
66
|
+
);
|
67
|
+
|
68
|
+
const handleResourceChange = useCallback(
|
69
|
+
(resource: ResourceBrowserResource | null) => {
|
70
|
+
let onChangeData: ResourceBrowserSelectorValue = {
|
71
|
+
...value,
|
72
|
+
matrixIdentifier: undefined,
|
73
|
+
matrixAssetId: undefined,
|
74
|
+
damSystemIdentifier: undefined,
|
75
|
+
damObjectId: undefined,
|
76
|
+
damSystemType: undefined,
|
77
|
+
damAdditional: undefined,
|
78
|
+
};
|
79
|
+
|
80
|
+
if (resource?.source?.type === 'dam') {
|
81
|
+
const damResource = resource as DAMResource;
|
82
|
+
onChangeData = {
|
83
|
+
...onChangeData,
|
84
|
+
damSystemIdentifier: resource.source.id,
|
85
|
+
damObjectId: resource.id,
|
86
|
+
damSystemType: (resource.source as DamResourceBrowserSource).configuration.externalType,
|
87
|
+
damAdditional: damResource.variant ? { variant: damResource.variant } : undefined,
|
88
|
+
url: resource.url,
|
89
|
+
};
|
90
|
+
}
|
91
|
+
if (resource?.source.type === 'matrix') {
|
92
|
+
onChangeData = {
|
93
|
+
...onChangeData,
|
94
|
+
matrixIdentifier: resource?.source?.id,
|
95
|
+
matrixAssetId: resource?.id,
|
96
|
+
url: resource?.url,
|
97
|
+
};
|
98
|
+
}
|
99
|
+
onChange({ target: { value: onChangeData as T } });
|
100
|
+
},
|
101
|
+
[value],
|
102
|
+
);
|
103
|
+
|
29
104
|
return (
|
30
105
|
<InputContainer {...props}>
|
31
106
|
<ResourceBrowser
|
32
107
|
modalTitle={modalTitle}
|
33
108
|
allowedTypes={allowedTypes}
|
34
|
-
value={
|
35
|
-
|
36
|
-
? {
|
37
|
-
sourceId: value.matrixIdentifier,
|
38
|
-
resourceId: value.matrixAssetId,
|
39
|
-
}
|
40
|
-
: null
|
41
|
-
}
|
42
|
-
onChange={(resource: ResourceBrowserResource | null) => {
|
43
|
-
onChange({
|
44
|
-
target: {
|
45
|
-
value: {
|
46
|
-
...value,
|
47
|
-
matrixIdentifier: resource?.source?.id,
|
48
|
-
matrixAssetId: resource?.id,
|
49
|
-
url: resource?.url,
|
50
|
-
} as T,
|
51
|
-
},
|
52
|
-
});
|
53
|
-
}}
|
109
|
+
value={convertFormDataToResourceBrowserValue(value as T)}
|
110
|
+
onChange={handleResourceChange}
|
54
111
|
/>
|
55
112
|
</InputContainer>
|
56
113
|
);
|
@@ -57,7 +57,6 @@ describe('ResourceBrowserSelector', () => {
|
|
57
57
|
value={{
|
58
58
|
damObjectId: '123-456-789',
|
59
59
|
damSystemIdentifier: 'zzz-xxx-ccc',
|
60
|
-
additional: 'addditional data',
|
61
60
|
}}
|
62
61
|
onChange={jest.fn()}
|
63
62
|
/>,
|
@@ -71,6 +70,30 @@ describe('ResourceBrowserSelector', () => {
|
|
71
70
|
},
|
72
71
|
}),
|
73
72
|
);
|
73
|
+
|
74
|
+
render(
|
75
|
+
<ResourceBrowserSelector
|
76
|
+
modalTitle="Insert asset"
|
77
|
+
value={{
|
78
|
+
damObjectId: '123-456-789',
|
79
|
+
damSystemIdentifier: 'zzz-xxx-ccc',
|
80
|
+
damAdditional: {
|
81
|
+
variant: 'v1',
|
82
|
+
},
|
83
|
+
}}
|
84
|
+
onChange={jest.fn()}
|
85
|
+
/>,
|
86
|
+
);
|
87
|
+
|
88
|
+
expect(resourceBrowserSpy).toHaveBeenCalledWith(
|
89
|
+
expect.objectContaining({
|
90
|
+
value: {
|
91
|
+
resourceId: '123-456-789',
|
92
|
+
sourceId: 'zzz-xxx-ccc',
|
93
|
+
variant: 'v1',
|
94
|
+
},
|
95
|
+
}),
|
96
|
+
);
|
74
97
|
});
|
75
98
|
|
76
99
|
it('AssetImage: Calls onChange with expected value when resources is selected', async () => {
|
@@ -155,6 +178,53 @@ describe('ResourceBrowserSelector', () => {
|
|
155
178
|
});
|
156
179
|
});
|
157
180
|
|
181
|
+
it('DAMImage: Calls onChange with expected value when variant resources is selected', async () => {
|
182
|
+
const handleChange = jest.fn();
|
183
|
+
setSelectedResource({
|
184
|
+
id: 'my-resource-id',
|
185
|
+
name: 'My resource',
|
186
|
+
url: 'myResourceUrl',
|
187
|
+
source: {
|
188
|
+
id: 'my-source-id',
|
189
|
+
type: 'dam',
|
190
|
+
configuration: {
|
191
|
+
externalType: 'bynder',
|
192
|
+
},
|
193
|
+
},
|
194
|
+
variant: 'v1',
|
195
|
+
} as unknown as ResourceBrowserResource);
|
196
|
+
|
197
|
+
render(
|
198
|
+
<ResourceBrowserSelector
|
199
|
+
modalTitle="Insert asset"
|
200
|
+
value={{ matrixIdentifier: undefined, matrixAssetId: undefined, additional: 'additional data' }}
|
201
|
+
onChange={handleChange}
|
202
|
+
/>,
|
203
|
+
);
|
204
|
+
|
205
|
+
// Trigger the onChange handler to be called
|
206
|
+
await waitFor(() => {
|
207
|
+
expect(screen.getByRole('button', { name: 'Select ResourceBrowser Resource' })).toBeInTheDocument();
|
208
|
+
});
|
209
|
+
fireEvent.click(await screen.findByRole('button', { name: 'Select ResourceBrowser Resource' }));
|
210
|
+
|
211
|
+
expect(handleChange).toHaveBeenCalledWith({
|
212
|
+
target: {
|
213
|
+
value: {
|
214
|
+
additional: 'additional data',
|
215
|
+
damObjectId: 'my-resource-id',
|
216
|
+
damSystemIdentifier: 'my-source-id',
|
217
|
+
damSystemType: 'bynder',
|
218
|
+
damAdditional: {
|
219
|
+
variant: 'v1',
|
220
|
+
},
|
221
|
+
url: 'myResourceUrl',
|
222
|
+
nodeType: 'DAMImage',
|
223
|
+
},
|
224
|
+
},
|
225
|
+
});
|
226
|
+
});
|
227
|
+
|
158
228
|
it('ImageAsset: Calls onChange with expected value when resources is cleared', async () => {
|
159
229
|
const handleChange = jest.fn();
|
160
230
|
setSelectedResource(null);
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import React, { useCallback } from 'react';
|
2
|
-
import { ResourceBrowserResource, ResourceBrowser } from '@squiz/resource-browser';
|
3
|
-
import { DamResourceBrowserSource } from '@squiz/dam-resource-browser-plugin';
|
2
|
+
import { ResourceBrowserUnresolvedResource, ResourceBrowserResource, ResourceBrowser } from '@squiz/resource-browser';
|
3
|
+
import { DamResourceBrowserSource, DAMResource } from '@squiz/dam-resource-browser-plugin';
|
4
4
|
import { InputContainer, InputContainerProps } from '../InputContainer/InputContainer';
|
5
5
|
import { NodeName } from '../../../Extensions/Extensions';
|
6
6
|
|
@@ -12,6 +12,9 @@ type DAMAsset = {
|
|
12
12
|
damSystemIdentifier?: string;
|
13
13
|
damObjectId?: string;
|
14
14
|
damSystemType?: string;
|
15
|
+
damAdditional?: {
|
16
|
+
variant: string;
|
17
|
+
};
|
15
18
|
};
|
16
19
|
type ResourceBrowserSelectorValue = MatrixAsset &
|
17
20
|
DAMAsset & {
|
@@ -40,21 +43,25 @@ export const ResourceBrowserSelector = <T extends ResourceBrowserSelectorValue>(
|
|
40
43
|
onChange,
|
41
44
|
...props
|
42
45
|
}: ResourceBrowserSelectorProps<T>) => {
|
43
|
-
const convertFormDataToResourceBrowserValue = useCallback(
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
46
|
+
const convertFormDataToResourceBrowserValue = useCallback(
|
47
|
+
(value: T | null): ResourceBrowserUnresolvedResource | null => {
|
48
|
+
if (value?.matrixIdentifier && value?.matrixAssetId) {
|
49
|
+
return {
|
50
|
+
sourceId: value.matrixIdentifier,
|
51
|
+
resourceId: value.matrixAssetId,
|
52
|
+
};
|
53
|
+
} else if (value?.damSystemIdentifier && value?.damObjectId) {
|
54
|
+
return {
|
55
|
+
sourceId: value.damSystemIdentifier,
|
56
|
+
resourceId: value.damObjectId,
|
57
|
+
variant: value.damAdditional?.variant,
|
58
|
+
};
|
59
|
+
}
|
55
60
|
|
56
|
-
|
57
|
-
|
61
|
+
return null;
|
62
|
+
},
|
63
|
+
[],
|
64
|
+
);
|
58
65
|
|
59
66
|
const handleResourceChange = useCallback((resource: ResourceBrowserResource | null) => {
|
60
67
|
// Clear out any key properties for clear resource use case
|
@@ -65,6 +72,7 @@ export const ResourceBrowserSelector = <T extends ResourceBrowserSelectorValue>(
|
|
65
72
|
damSystemIdentifier: undefined,
|
66
73
|
damObjectId: undefined,
|
67
74
|
damSystemType: undefined,
|
75
|
+
damAdditional: undefined,
|
68
76
|
url: undefined,
|
69
77
|
};
|
70
78
|
|
@@ -77,11 +85,17 @@ export const ResourceBrowserSelector = <T extends ResourceBrowserSelectorValue>(
|
|
77
85
|
nodeType: NodeName.AssetImage,
|
78
86
|
};
|
79
87
|
} else if (resource?.source?.type === 'dam') {
|
88
|
+
const damResource = resource as DAMResource;
|
80
89
|
onChangeData = {
|
81
90
|
...value,
|
82
91
|
damSystemIdentifier: resource?.source?.id,
|
83
92
|
damObjectId: resource?.id,
|
84
93
|
damSystemType: (resource?.source as DamResourceBrowserSource).configuration.externalType,
|
94
|
+
damAdditional: damResource.variant
|
95
|
+
? {
|
96
|
+
variant: damResource.variant,
|
97
|
+
}
|
98
|
+
: undefined,
|
85
99
|
url: resource?.url,
|
86
100
|
nodeType: NodeName.DAMImage,
|
87
101
|
};
|
@@ -288,4 +288,52 @@ export const squizOnlyNodeExamples: NodeExample[] = [
|
|
288
288
|
],
|
289
289
|
},
|
290
290
|
},
|
291
|
+
{
|
292
|
+
description: 'Link to DAM asset',
|
293
|
+
squizNode: [
|
294
|
+
{
|
295
|
+
type: 'tag',
|
296
|
+
tag: 'span',
|
297
|
+
children: [
|
298
|
+
{
|
299
|
+
type: 'link-to-dam-asset',
|
300
|
+
damObjectId: '123-456-789',
|
301
|
+
damSystemIdentifier: 'zzz-xxx-ccc',
|
302
|
+
damSystemType: 'bynder',
|
303
|
+
damAdditional: { variant: 'xyz' },
|
304
|
+
target: '_blank',
|
305
|
+
children: [
|
306
|
+
{
|
307
|
+
type: 'tag',
|
308
|
+
tag: 'span',
|
309
|
+
children: [
|
310
|
+
{
|
311
|
+
type: 'tag',
|
312
|
+
tag: 'span',
|
313
|
+
children: [{ type: 'text', value: 'Hello' }],
|
314
|
+
},
|
315
|
+
],
|
316
|
+
},
|
317
|
+
],
|
318
|
+
},
|
319
|
+
],
|
320
|
+
},
|
321
|
+
],
|
322
|
+
remirrorNode: {
|
323
|
+
type: 'text',
|
324
|
+
text: 'Hello',
|
325
|
+
marks: [
|
326
|
+
{
|
327
|
+
type: 'DAMLink',
|
328
|
+
attrs: {
|
329
|
+
target: '_blank',
|
330
|
+
damObjectId: '123-456-789',
|
331
|
+
damSystemIdentifier: 'zzz-xxx-ccc',
|
332
|
+
damSystemType: 'bynder',
|
333
|
+
damAdditional: JSON.stringify({ variant: 'xyz' }),
|
334
|
+
},
|
335
|
+
},
|
336
|
+
],
|
337
|
+
},
|
338
|
+
},
|
291
339
|
];
|
@@ -1053,4 +1053,67 @@ describe('resolveNodeTag', () => {
|
|
1053
1053
|
};
|
1054
1054
|
expect(() => resolveNodeTag(node)).toThrow('Unexpected Remirror node encountered, cannot resolve tag.');
|
1055
1055
|
});
|
1056
|
+
|
1057
|
+
it('should convert a DAMLink mark to a link-to-dam-asset node', async () => {
|
1058
|
+
const content: RemirrorJSON = {
|
1059
|
+
type: 'doc',
|
1060
|
+
content: [
|
1061
|
+
{
|
1062
|
+
type: 'paragraph',
|
1063
|
+
attrs: {
|
1064
|
+
nodeIndent: null,
|
1065
|
+
nodeTextAlignment: null,
|
1066
|
+
nodeLineHeight: null,
|
1067
|
+
style: '',
|
1068
|
+
},
|
1069
|
+
content: [
|
1070
|
+
{
|
1071
|
+
type: 'text',
|
1072
|
+
text: 'Click here for DAM asset',
|
1073
|
+
marks: [
|
1074
|
+
{
|
1075
|
+
type: 'DAMLink',
|
1076
|
+
attrs: {
|
1077
|
+
target: '_blank',
|
1078
|
+
damSystemType: 'bynder',
|
1079
|
+
damSystemIdentifier: 'idA',
|
1080
|
+
damObjectId: 'objA',
|
1081
|
+
damAdditional: { variant: 'primary' },
|
1082
|
+
},
|
1083
|
+
},
|
1084
|
+
],
|
1085
|
+
},
|
1086
|
+
],
|
1087
|
+
},
|
1088
|
+
],
|
1089
|
+
};
|
1090
|
+
|
1091
|
+
const { editor } = await renderWithEditor(null, { content });
|
1092
|
+
|
1093
|
+
const expected: FormattedTextModels.v1.FormattedText = [
|
1094
|
+
{
|
1095
|
+
type: 'tag',
|
1096
|
+
tag: 'p',
|
1097
|
+
children: [
|
1098
|
+
{
|
1099
|
+
type: 'link-to-dam-asset',
|
1100
|
+
target: '_blank',
|
1101
|
+
damSystemType: 'bynder',
|
1102
|
+
damSystemIdentifier: 'idA',
|
1103
|
+
damObjectId: 'objA',
|
1104
|
+
damAdditional: { variant: 'primary' },
|
1105
|
+
children: [
|
1106
|
+
{
|
1107
|
+
type: 'text',
|
1108
|
+
value: 'Click here for DAM asset',
|
1109
|
+
},
|
1110
|
+
],
|
1111
|
+
},
|
1112
|
+
],
|
1113
|
+
},
|
1114
|
+
];
|
1115
|
+
|
1116
|
+
const result = remirrorNodeToSquizNode(editor.doc);
|
1117
|
+
expect(result).toEqual(expected);
|
1118
|
+
});
|
1056
1119
|
});
|
@@ -154,12 +154,17 @@ const transformNode = (node: ProsemirrorNode): FormattedNode => {
|
|
154
154
|
}
|
155
155
|
|
156
156
|
if (node.type.name === NodeName.DAMImage) {
|
157
|
+
let damAdditional = node.attrs.damAdditional;
|
158
|
+
if (damAdditional && typeof node.attrs.damAdditional === 'string') {
|
159
|
+
damAdditional = JSON.parse(node.attrs.damAdditional);
|
160
|
+
}
|
161
|
+
|
157
162
|
transformedNode = {
|
158
163
|
type: 'dam-image',
|
159
164
|
damObjectId: node.attrs.damObjectId,
|
160
165
|
damSystemIdentifier: node.attrs.damSystemIdentifier,
|
161
166
|
damSystemType: node.attrs.damSystemType,
|
162
|
-
damAdditional
|
167
|
+
damAdditional,
|
163
168
|
};
|
164
169
|
}
|
165
170
|
|
@@ -277,6 +282,16 @@ const transformMark = (mark: Mark, node: FormattedNode): FormattedNode => {
|
|
277
282
|
matrixAssetId: mark.attrs.matrixAssetId,
|
278
283
|
children: [],
|
279
284
|
});
|
285
|
+
case 'DAMLink':
|
286
|
+
return wrapNodeIfNeeded(node, {
|
287
|
+
type: 'link-to-dam-asset',
|
288
|
+
target: mark.attrs.target,
|
289
|
+
damSystemType: mark.attrs.damSystemType,
|
290
|
+
damSystemIdentifier: mark.attrs.damSystemIdentifier,
|
291
|
+
damObjectId: mark.attrs.damObjectId,
|
292
|
+
damAdditional: mark.attrs.damAdditional,
|
293
|
+
children: [],
|
294
|
+
});
|
280
295
|
}
|
281
296
|
|
282
297
|
throw new Error(`Unsupported mark "${mark.type.name}" was applied to node.`);
|
@@ -12,6 +12,7 @@ const getNodeType = (node: FormattedNodes): string => {
|
|
12
12
|
'link-to-matrix-asset': NodeName.Text,
|
13
13
|
'matrix-image': NodeName.AssetImage,
|
14
14
|
'dam-image': NodeName.DAMImage,
|
15
|
+
'link-to-dam-asset': NodeName.Text,
|
15
16
|
text: 'text',
|
16
17
|
};
|
17
18
|
|
@@ -134,6 +135,17 @@ const getNodeMarks = (node: FormattedNodes): ObjectMark[] => {
|
|
134
135
|
target: node.target,
|
135
136
|
},
|
136
137
|
});
|
138
|
+
} else if (node.type === 'link-to-dam-asset') {
|
139
|
+
marks.push({
|
140
|
+
type: MarkName.DAMLink,
|
141
|
+
attrs: {
|
142
|
+
damSystemType: node.damSystemType,
|
143
|
+
damSystemIdentifier: node.damSystemIdentifier,
|
144
|
+
damObjectId: node.damObjectId,
|
145
|
+
damAdditional: node.damAdditional ? JSON.stringify(node.damAdditional) : undefined,
|
146
|
+
target: node.target,
|
147
|
+
},
|
148
|
+
});
|
137
149
|
} else if (node.type === 'tag' && node.tag === 'strong') {
|
138
150
|
marks.push({ type: 'bold' });
|
139
151
|
} else if (node.type === 'tag' && node.tag === 'em') {
|
@@ -15,6 +15,6 @@ describe('getMarkNamesGroup', () => {
|
|
15
15
|
// Marks in the first array will be transformed to a paragraph when formatting is cleared.
|
16
16
|
// Mark in the second array will be left as-is.
|
17
17
|
expect(formattingMarkNames).toEqual(['bold', 'italic', 'underline']);
|
18
|
-
expect(otherMarkNames).toEqual(['assetLink', 'link']);
|
18
|
+
expect(otherMarkNames).toEqual(['DAMLink', 'assetLink', 'link']);
|
19
19
|
});
|
20
20
|
});
|
@@ -46,6 +46,7 @@ export const renderWithContext = (ui: ReactElement | null, options?: ContextRend
|
|
46
46
|
const onRequestChildren = jest.fn().mockResolvedValue(resources);
|
47
47
|
const onRequestResource = jest.fn(() => Promise.resolve(resources[0]));
|
48
48
|
const onSearchRequest = jest.fn();
|
49
|
+
const onRequestMatrixAsset = jest.fn();
|
49
50
|
editorContext.resolveNodeToUrl = jest.fn(() => Promise.resolve(resources[0].url));
|
50
51
|
|
51
52
|
return render(
|
@@ -67,6 +68,7 @@ export const renderWithContext = (ui: ReactElement | null, options?: ContextRend
|
|
67
68
|
onRequestChildren: onRequestChildren,
|
68
69
|
onRequestResource: onRequestResource,
|
69
70
|
onSearchRequest,
|
71
|
+
onRequestMatrixAsset,
|
70
72
|
}),
|
71
73
|
],
|
72
74
|
}}
|