@capillarytech/creatives-library 8.0.302 → 8.0.304
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/constants/unified.js +0 -1
- package/package.json +1 -1
- package/utils/common.js +1 -7
- package/v2Components/CapDeviceContent/index.js +7 -10
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +0 -3
- package/v2Containers/BeePopupEditor/index.js +2 -9
- package/v2Containers/CreativesContainer/SlideBoxContent.js +3 -35
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +1 -69
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +4 -121
- package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +155 -110
- package/v2Containers/InApp/index.js +14 -183
- package/v2Containers/InApp/tests/mockData.js +1 -1
package/constants/unified.js
CHANGED
|
@@ -45,7 +45,6 @@ export const GIFT_CARDS = 'GIFT_CARDS';
|
|
|
45
45
|
export const PROMO_ENGINE = 'PROMO_ENGINE';
|
|
46
46
|
export const LIQUID_SUPPORT = 'ENABLE_LIQUID_SUPPORT';
|
|
47
47
|
export const ENABLE_NEW_MPUSH = 'ENABLE_NEW_MPUSH';
|
|
48
|
-
export const ENABLE_NEW_EDITOR_FLOW_INAPP = 'ENABLE_NEW_EDITOR_FLOW_INAPP';
|
|
49
48
|
export const SUPPORT_CK_EDITOR = 'SUPPORT_CK_EDITOR';
|
|
50
49
|
export const CUSTOM_TAG = 'CustomTagMessage';
|
|
51
50
|
export const CUSTOMER_EXTENDED_FIELD = 'Customer extended fields';
|
package/package.json
CHANGED
package/utils/common.js
CHANGED
|
@@ -24,8 +24,7 @@ import {
|
|
|
24
24
|
ENABLE_WEBPUSH,
|
|
25
25
|
LIQUID_SUPPORT,
|
|
26
26
|
SUPPORT_CK_EDITOR,
|
|
27
|
-
ENABLE_NEW_MPUSH
|
|
28
|
-
ENABLE_NEW_EDITOR_FLOW_INAPP
|
|
27
|
+
ENABLE_NEW_MPUSH
|
|
29
28
|
} from '../constants/unified';
|
|
30
29
|
import { apiMessageFormatHandler } from './commonUtils';
|
|
31
30
|
|
|
@@ -143,11 +142,6 @@ export const hasNewMobilePushFeatureEnabled = Auth.hasFeatureAccess.bind(
|
|
|
143
142
|
ENABLE_NEW_MPUSH,
|
|
144
143
|
);
|
|
145
144
|
|
|
146
|
-
export const hasNewEditorFlowInAppEnabled = Auth.hasFeatureAccess.bind(
|
|
147
|
-
null,
|
|
148
|
-
ENABLE_NEW_EDITOR_FLOW_INAPP,
|
|
149
|
-
);
|
|
150
|
-
|
|
151
145
|
//filtering tags based on scope
|
|
152
146
|
export const filterTags = (tagsToFilter, tagsList) => tagsList?.filter(
|
|
153
147
|
(tag) => !tagsToFilter?.includes(tag?.definition?.value)
|
|
@@ -63,7 +63,6 @@ const CapDeviceContent = (props) => {
|
|
|
63
63
|
deepLinkValue,
|
|
64
64
|
setDeepLinkValue,
|
|
65
65
|
onCopyTitleAndContent,
|
|
66
|
-
isOtherDeviceSupported,
|
|
67
66
|
tags,
|
|
68
67
|
onTagSelect,
|
|
69
68
|
handleOnTagsContextChange,
|
|
@@ -168,15 +167,13 @@ const CapDeviceContent = (props) => {
|
|
|
168
167
|
return (
|
|
169
168
|
<>
|
|
170
169
|
<CapRow className="creatives-device-content">
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
/>
|
|
179
|
-
)}
|
|
170
|
+
<CapLink
|
|
171
|
+
title={isAndroid
|
|
172
|
+
? formatMessage(messages.copyContentFromIOS)
|
|
173
|
+
: formatMessage(messages.copyCotentFromAndroid)}
|
|
174
|
+
className="inapp-copy-content"
|
|
175
|
+
onClick={onCopyTitleAndContent}
|
|
176
|
+
/>
|
|
180
177
|
<CapRow className="creatives-inapp-title">
|
|
181
178
|
<CapColumn
|
|
182
179
|
className="inapp-content-main"
|
|
@@ -40,11 +40,6 @@ function BeePopupEditor(props) {
|
|
|
40
40
|
const savedCallback = useRef();
|
|
41
41
|
const beeInstanceRef = useRef(null);
|
|
42
42
|
const isInitializedRef = useRef(false);
|
|
43
|
-
const beeJsonRef = useRef(beeJson);
|
|
44
|
-
|
|
45
|
-
useEffect(() => {
|
|
46
|
-
beeJsonRef.current = beeJson;
|
|
47
|
-
}, [beeJson]);
|
|
48
43
|
|
|
49
44
|
const [visibleTaglist, setVisibleTaglist] = useState(false);
|
|
50
45
|
const [selectedTag, setSelectedTag] = useState({});
|
|
@@ -116,10 +111,8 @@ function BeePopupEditor(props) {
|
|
|
116
111
|
window.BeePlugin.create(tokenData, beeConfig, (instance) => {
|
|
117
112
|
beePluginInstance = instance;
|
|
118
113
|
beeInstanceRef.current = instance;
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
const latestBeeJson = beeJsonRef.current;
|
|
122
|
-
const parseJson = typeof latestBeeJson === 'string' ? JSON.parse(latestBeeJson) : latestBeeJson;
|
|
114
|
+
// Check if beeJson is already an object (happens when layout type changes)
|
|
115
|
+
const parseJson = typeof beeJson === 'string' ? JSON.parse(beeJson) : beeJson;
|
|
123
116
|
beePluginInstance.start(parseJson);
|
|
124
117
|
saveBeeInstance(beePluginInstance, device);
|
|
125
118
|
isInitializedRef.current = true;
|
|
@@ -1078,38 +1078,8 @@ export function SlideBoxContent(props) {
|
|
|
1078
1078
|
)}
|
|
1079
1079
|
|
|
1080
1080
|
{isCreateInApp && (
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
(!isFullMode && !isLoyaltyModule && !commonUtil.hasNewEditorFlowInAppEnabled()) ? (
|
|
1084
|
-
<InApp
|
|
1085
|
-
key="creatives-inapp-create"
|
|
1086
|
-
location={{ pathname: '/inapp/create', query, search: '' }}
|
|
1087
|
-
setIsLoadingContent={setIsLoadingContent}
|
|
1088
|
-
isGetFormData={isGetFormData}
|
|
1089
|
-
getFormData={getFormData}
|
|
1090
|
-
getDefaultTags={type}
|
|
1091
|
-
isFullMode={isFullMode}
|
|
1092
|
-
templateData={templateData}
|
|
1093
|
-
cap={cap}
|
|
1094
|
-
showTemplateName={showTemplateName}
|
|
1095
|
-
showLiquidErrorInFooter={showLiquidErrorInFooter}
|
|
1096
|
-
onValidationFail={onValidationFail}
|
|
1097
|
-
forwardedTags={forwardedTags}
|
|
1098
|
-
selectedOfferDetails={selectedOfferDetails}
|
|
1099
|
-
onPreviewContentClicked={onPreviewContentClicked}
|
|
1100
|
-
onTestContentClicked={onTestContentClicked}
|
|
1101
|
-
eventContextTags={eventContextTags}
|
|
1102
|
-
onCreateComplete={onCreateComplete}
|
|
1103
|
-
handleClose={handleClose}
|
|
1104
|
-
moduleType={moduleType}
|
|
1105
|
-
showTestAndPreviewSlidebox={showTestAndPreviewSlidebox}
|
|
1106
|
-
handleTestAndPreview={handleTestAndPreview}
|
|
1107
|
-
handleCloseTestAndPreview={handleCloseTestAndPreview}
|
|
1108
|
-
isTestAndPreviewMode={isTestAndPreviewMode}
|
|
1109
|
-
/>
|
|
1110
|
-
) : (
|
|
1111
|
-
<InAppWrapper
|
|
1112
|
-
key="creatives-inapp-wrapper"
|
|
1081
|
+
<InAppWrapper
|
|
1082
|
+
key="creatives-inapp-wrapper"
|
|
1113
1083
|
date={new Date().getMilliseconds()}
|
|
1114
1084
|
setIsLoadingContent={setIsLoadingContent}
|
|
1115
1085
|
onInAppEditorTypeChange={onInAppEditorTypeChange}
|
|
@@ -1144,12 +1114,10 @@ export function SlideBoxContent(props) {
|
|
|
1144
1114
|
handleCloseTestAndPreview={handleCloseTestAndPreview}
|
|
1145
1115
|
isTestAndPreviewMode={isTestAndPreviewMode}
|
|
1146
1116
|
/>
|
|
1147
|
-
)
|
|
1148
1117
|
)}
|
|
1149
|
-
|
|
1118
|
+
|
|
1150
1119
|
{isEditInApp && (<InApp
|
|
1151
1120
|
isFullMode={isFullMode}
|
|
1152
|
-
isLoyaltyModule={isLoyaltyModule}
|
|
1153
1121
|
templateData={templateData}
|
|
1154
1122
|
getFormData={getFormData}
|
|
1155
1123
|
getDefaultTags={type}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { shallowWithIntl } from '../../../helpers/intl-enzym-test-helpers';
|
|
3
|
+
|
|
3
4
|
import { SlideBoxContent } from '../SlideBoxContent';
|
|
4
5
|
import mockdata from '../../mockdata';
|
|
5
6
|
import { templateDetailsImage, templateDetailsVideo, templateDetailsText, templateDetails_ } from '../../Viber/tests/mockData';
|
|
@@ -41,15 +42,6 @@ jest.mock('../../WebPush', () => ({
|
|
|
41
42
|
),
|
|
42
43
|
}));
|
|
43
44
|
|
|
44
|
-
jest.mock('v2Containers/InApp', () => () => <div data-test="inapp" />);
|
|
45
|
-
jest.mock('v2Containers/InAppWrapper', () => () => <div data-test="inapp-wrapper" />);
|
|
46
|
-
|
|
47
|
-
jest.mock('../../../utils/commonUtils', () => ({
|
|
48
|
-
hasNewEditorFlowInAppEnabled: jest.fn(),
|
|
49
|
-
}));
|
|
50
|
-
|
|
51
|
-
import commonUtil from '../../../utils/commonUtils';
|
|
52
|
-
|
|
53
45
|
describe('Test SlideBoxContent container', () => {
|
|
54
46
|
const onCreateComplete = jest.fn();
|
|
55
47
|
let renderedComponent;
|
|
@@ -918,64 +910,4 @@ describe('Test SlideBoxContent container', () => {
|
|
|
918
910
|
expect(renderedComponent).toMatchSnapshot();
|
|
919
911
|
});
|
|
920
912
|
});
|
|
921
|
-
|
|
922
|
-
describe('InApp vs InAppWrapper rendering conditions', () => {
|
|
923
|
-
|
|
924
|
-
beforeEach(() => {
|
|
925
|
-
jest.clearAllMocks();
|
|
926
|
-
});
|
|
927
|
-
|
|
928
|
-
it('renders InAppWrapper when isFullMode=true and new editor disabled', () => {
|
|
929
|
-
commonUtil.hasNewEditorFlowInAppEnabled.mockReturnValue(false);
|
|
930
|
-
|
|
931
|
-
renderFunction(
|
|
932
|
-
'INAPP',
|
|
933
|
-
'createTemplate',
|
|
934
|
-
{ mode: 'create' },
|
|
935
|
-
{ isFullMode: true, isLoyaltyModule: false }
|
|
936
|
-
);
|
|
937
|
-
|
|
938
|
-
expect(renderedComponent).toMatchSnapshot();
|
|
939
|
-
});
|
|
940
|
-
|
|
941
|
-
it('renders InApp when isFullMode=false and loyalty module enabled', () => {
|
|
942
|
-
commonUtil.hasNewEditorFlowInAppEnabled.mockReturnValue(false);
|
|
943
|
-
|
|
944
|
-
renderFunction(
|
|
945
|
-
'INAPP',
|
|
946
|
-
'createTemplate',
|
|
947
|
-
{ mode: 'create' },
|
|
948
|
-
{ isFullMode: false, isLoyaltyModule: true }
|
|
949
|
-
);
|
|
950
|
-
|
|
951
|
-
expect(renderedComponent).toMatchSnapshot();
|
|
952
|
-
});
|
|
953
|
-
|
|
954
|
-
it('renders InApp when not full mode, not loyalty and new editor disabled', () => {
|
|
955
|
-
commonUtil.hasNewEditorFlowInAppEnabled.mockReturnValue(false);
|
|
956
|
-
|
|
957
|
-
renderFunction(
|
|
958
|
-
'INAPP',
|
|
959
|
-
'createTemplate',
|
|
960
|
-
{ mode: 'create' },
|
|
961
|
-
{ isFullMode: false, isLoyaltyModule: false }
|
|
962
|
-
);
|
|
963
|
-
|
|
964
|
-
expect(renderedComponent).toMatchSnapshot();
|
|
965
|
-
});
|
|
966
|
-
|
|
967
|
-
it('renders InAppWrapper when full mode and new editor enabled', () => {
|
|
968
|
-
commonUtil.hasNewEditorFlowInAppEnabled.mockReturnValue(true);
|
|
969
|
-
|
|
970
|
-
renderFunction(
|
|
971
|
-
'INAPP',
|
|
972
|
-
'createTemplate',
|
|
973
|
-
{ mode: 'create' },
|
|
974
|
-
{ isFullMode: true, isLoyaltyModule: false }
|
|
975
|
-
);
|
|
976
|
-
|
|
977
|
-
expect(renderedComponent).toMatchSnapshot();
|
|
978
|
-
});
|
|
979
|
-
|
|
980
|
-
});
|
|
981
913
|
});
|
|
@@ -202,114 +202,6 @@ exports[`Test SlideBoxContent container Email component isTestAndPreviewMode IIF
|
|
|
202
202
|
</SlideBoxContent__CreativesWrapper>
|
|
203
203
|
`;
|
|
204
204
|
|
|
205
|
-
exports[`Test SlideBoxContent container InApp vs InAppWrapper rendering conditions renders InApp when isFullMode=false and loyalty module enabled 1`] = `
|
|
206
|
-
<SlideBoxContent__CreativesWrapper>
|
|
207
|
-
<Component
|
|
208
|
-
getDefaultTags=""
|
|
209
|
-
isFullMode={false}
|
|
210
|
-
key="creatives-inapp-create"
|
|
211
|
-
location={
|
|
212
|
-
Object {
|
|
213
|
-
"pathname": "/inapp/create",
|
|
214
|
-
"query": Object {
|
|
215
|
-
"isEditFromCampaigns": undefined,
|
|
216
|
-
"module": "library",
|
|
217
|
-
"type": "embedded",
|
|
218
|
-
},
|
|
219
|
-
"search": "",
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
onCreateComplete={[MockFunction]}
|
|
223
|
-
templateData={
|
|
224
|
-
Object {
|
|
225
|
-
"mode": "create",
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
/>
|
|
229
|
-
</SlideBoxContent__CreativesWrapper>
|
|
230
|
-
`;
|
|
231
|
-
|
|
232
|
-
exports[`Test SlideBoxContent container InApp vs InAppWrapper rendering conditions renders InApp when not full mode, not loyalty and new editor disabled 1`] = `
|
|
233
|
-
<SlideBoxContent__CreativesWrapper>
|
|
234
|
-
<Component
|
|
235
|
-
getDefaultTags=""
|
|
236
|
-
isFullMode={false}
|
|
237
|
-
key="creatives-inapp-create"
|
|
238
|
-
location={
|
|
239
|
-
Object {
|
|
240
|
-
"pathname": "/inapp/create",
|
|
241
|
-
"query": Object {
|
|
242
|
-
"isEditFromCampaigns": undefined,
|
|
243
|
-
"module": "library",
|
|
244
|
-
"type": "embedded",
|
|
245
|
-
},
|
|
246
|
-
"search": "",
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
onCreateComplete={[MockFunction]}
|
|
250
|
-
templateData={
|
|
251
|
-
Object {
|
|
252
|
-
"mode": "create",
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
/>
|
|
256
|
-
</SlideBoxContent__CreativesWrapper>
|
|
257
|
-
`;
|
|
258
|
-
|
|
259
|
-
exports[`Test SlideBoxContent container InApp vs InAppWrapper rendering conditions renders InAppWrapper when full mode and new editor enabled 1`] = `
|
|
260
|
-
<SlideBoxContent__CreativesWrapper>
|
|
261
|
-
<Component
|
|
262
|
-
getDefaultTags=""
|
|
263
|
-
isFullMode={true}
|
|
264
|
-
key="creatives-inapp-create"
|
|
265
|
-
location={
|
|
266
|
-
Object {
|
|
267
|
-
"pathname": "/inapp/create",
|
|
268
|
-
"query": Object {
|
|
269
|
-
"isEditFromCampaigns": undefined,
|
|
270
|
-
"module": "default",
|
|
271
|
-
"type": false,
|
|
272
|
-
},
|
|
273
|
-
"search": "",
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
onCreateComplete={[MockFunction]}
|
|
277
|
-
templateData={
|
|
278
|
-
Object {
|
|
279
|
-
"mode": "create",
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
/>
|
|
283
|
-
</SlideBoxContent__CreativesWrapper>
|
|
284
|
-
`;
|
|
285
|
-
|
|
286
|
-
exports[`Test SlideBoxContent container InApp vs InAppWrapper rendering conditions renders InAppWrapper when isFullMode=true and new editor disabled 1`] = `
|
|
287
|
-
<SlideBoxContent__CreativesWrapper>
|
|
288
|
-
<Component
|
|
289
|
-
getDefaultTags=""
|
|
290
|
-
isFullMode={true}
|
|
291
|
-
key="creatives-inapp-create"
|
|
292
|
-
location={
|
|
293
|
-
Object {
|
|
294
|
-
"pathname": "/inapp/create",
|
|
295
|
-
"query": Object {
|
|
296
|
-
"isEditFromCampaigns": undefined,
|
|
297
|
-
"module": "default",
|
|
298
|
-
"type": false,
|
|
299
|
-
},
|
|
300
|
-
"search": "",
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
onCreateComplete={[MockFunction]}
|
|
304
|
-
templateData={
|
|
305
|
-
Object {
|
|
306
|
-
"mode": "create",
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
/>
|
|
310
|
-
</SlideBoxContent__CreativesWrapper>
|
|
311
|
-
`;
|
|
312
|
-
|
|
313
205
|
exports[`Test SlideBoxContent container Should handle isTestAndPreviewMode IIFE implementation correctly 1`] = `
|
|
314
206
|
<SlideBoxContent__CreativesWrapper>
|
|
315
207
|
<ForwardRef
|
|
@@ -1211,26 +1103,17 @@ exports[`Test SlideBoxContent container Should render correct component for what
|
|
|
1211
1103
|
|
|
1212
1104
|
exports[`Test SlideBoxContent container Should render correct component for whatsapp channel create mode 2`] = `
|
|
1213
1105
|
<SlideBoxContent__CreativesWrapper>
|
|
1214
|
-
<
|
|
1106
|
+
<Connect(Connect(UserIsAuthenticated(InjectIntl(CreativesCommon))))
|
|
1107
|
+
date={0}
|
|
1215
1108
|
getDefaultTags=""
|
|
1216
|
-
key="creatives-inapp-
|
|
1217
|
-
location={
|
|
1218
|
-
Object {
|
|
1219
|
-
"pathname": "/inapp/create",
|
|
1220
|
-
"query": Object {
|
|
1221
|
-
"isEditFromCampaigns": undefined,
|
|
1222
|
-
"module": "library",
|
|
1223
|
-
"type": "embedded",
|
|
1224
|
-
},
|
|
1225
|
-
"search": "",
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1109
|
+
key="creatives-inapp-wrapper"
|
|
1228
1110
|
onCreateComplete={[MockFunction]}
|
|
1229
1111
|
templateData={
|
|
1230
1112
|
Object {
|
|
1231
1113
|
"mode": "create",
|
|
1232
1114
|
}
|
|
1233
1115
|
}
|
|
1116
|
+
type=""
|
|
1234
1117
|
/>
|
|
1235
1118
|
</SlideBoxContent__CreativesWrapper>
|
|
1236
1119
|
`;
|
|
@@ -39,58 +39,33 @@ jest.mock('../../../utils/common', () => ({
|
|
|
39
39
|
// Mock HTMLEditor component - must be before imports
|
|
40
40
|
jest.mock('../../../v2Components/HtmlEditor', () => {
|
|
41
41
|
const React = require('react');
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
variant,
|
|
45
|
-
initialContent,
|
|
46
|
-
onContentChange,
|
|
47
|
-
onSave,
|
|
48
|
-
'data-test': dataTest,
|
|
49
|
-
}) {
|
|
50
|
-
return React.createElement(
|
|
51
|
-
'div',
|
|
52
|
-
{ 'data-testid': dataTest || 'inapp-html-editor' },
|
|
42
|
+
const MockHTMLEditor = function MockHTMLEditor({ variant, initialContent, onContentChange, onSave, 'data-test': dataTest }) {
|
|
43
|
+
return React.createElement('div', { 'data-testid': dataTest || 'html-editor' },
|
|
53
44
|
React.createElement('div', { 'data-testid': 'html-editor-variant' }, variant),
|
|
54
|
-
React.createElement(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
),
|
|
79
|
-
React.createElement(
|
|
80
|
-
'button',
|
|
81
|
-
{
|
|
82
|
-
'data-testid': 'html-editor-save-button',
|
|
83
|
-
onClick: () => {
|
|
84
|
-
if (onSave) {
|
|
85
|
-
onSave({
|
|
86
|
-
android: '<p>Saved Android HTML</p>',
|
|
87
|
-
ios: '<p>Saved iOS HTML</p>',
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
},
|
|
92
|
-
'Simulate Save'
|
|
93
|
-
)
|
|
45
|
+
React.createElement('div', { 'data-testid': 'html-editor-initial-android' }, initialContent?.android || ''),
|
|
46
|
+
React.createElement('div', { 'data-testid': 'html-editor-initial-ios' }, initialContent?.ios || ''),
|
|
47
|
+
React.createElement('button', {
|
|
48
|
+
'data-testid': 'html-editor-change-button',
|
|
49
|
+
onClick: () => {
|
|
50
|
+
if (onContentChange) {
|
|
51
|
+
onContentChange({
|
|
52
|
+
android: '<p>Updated Android HTML</p>',
|
|
53
|
+
ios: '<p>Updated iOS HTML</p>',
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}, 'Simulate Content Change'),
|
|
58
|
+
React.createElement('button', {
|
|
59
|
+
'data-testid': 'html-editor-save-button',
|
|
60
|
+
onClick: () => {
|
|
61
|
+
if (onSave) {
|
|
62
|
+
onSave({
|
|
63
|
+
android: '<p>Saved Android HTML</p>',
|
|
64
|
+
ios: '<p>Saved iOS HTML</p>',
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}, 'Simulate Save')
|
|
94
69
|
);
|
|
95
70
|
};
|
|
96
71
|
MockHTMLEditor.displayName = 'MockHTMLEditor';
|
|
@@ -100,25 +75,22 @@ jest.mock('../../../v2Components/HtmlEditor', () => {
|
|
|
100
75
|
};
|
|
101
76
|
});
|
|
102
77
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
78
|
+
import React from 'react';
|
|
79
|
+
import { render, screen, fireEvent, waitFor, act } from '@testing-library/react';
|
|
80
|
+
import '@testing-library/jest-dom';
|
|
81
|
+
import { IntlProvider } from 'react-intl';
|
|
82
|
+
import { Provider } from 'react-redux';
|
|
83
|
+
import { configureStore } from '@capillarytech/vulcan-react-sdk/utils';
|
|
84
|
+
import history from '../../../utils/history';
|
|
85
|
+
import { initialReducer } from '../../../initialReducer';
|
|
86
|
+
import { InApp } from '../index';
|
|
87
|
+
import { INAPP_EDITOR_TYPES } from '../../InAppWrapper/constants';
|
|
109
88
|
|
|
89
|
+
|
|
90
|
+
// Mock other dependencies
|
|
91
|
+
jest.mock('../../../v2Components/CapDeviceContent', () => {
|
|
110
92
|
return function MockCapDeviceContent() {
|
|
111
|
-
return
|
|
112
|
-
<div data-testid="cap-device-content">
|
|
113
|
-
<HTMLEditor
|
|
114
|
-
variant="inapp"
|
|
115
|
-
initialContent={{ android: '', ios: '' }}
|
|
116
|
-
onContentChange={jest.fn()}
|
|
117
|
-
onSave={jest.fn()}
|
|
118
|
-
data-test="inapp-html-editor"
|
|
119
|
-
/>
|
|
120
|
-
</div>
|
|
121
|
-
);
|
|
93
|
+
return <div data-testid="cap-device-content">Device Content</div>;
|
|
122
94
|
};
|
|
123
95
|
});
|
|
124
96
|
|
|
@@ -128,20 +100,7 @@ jest.mock('../../../v2Components/TemplatePreview', () => {
|
|
|
128
100
|
};
|
|
129
101
|
});
|
|
130
102
|
|
|
131
|
-
import React from 'react';
|
|
132
|
-
import { render, screen, fireEvent, act } from '@testing-library/react';
|
|
133
|
-
import '@testing-library/jest-dom';
|
|
134
|
-
import { IntlProvider } from 'react-intl';
|
|
135
|
-
import { Provider } from 'react-redux';
|
|
136
|
-
import { configureStore } from '@capillarytech/vulcan-react-sdk/utils';
|
|
137
|
-
|
|
138
|
-
import history from '../../../utils/history';
|
|
139
|
-
import { initialReducer } from '../../../initialReducer';
|
|
140
|
-
import { InApp } from '../index';
|
|
141
|
-
import { INAPP_EDITOR_TYPES } from '../../InAppWrapper/constants';
|
|
142
|
-
|
|
143
103
|
let store;
|
|
144
|
-
|
|
145
104
|
beforeAll(() => {
|
|
146
105
|
store = configureStore({}, initialReducer, history);
|
|
147
106
|
});
|
|
@@ -176,6 +135,8 @@ const defaultProps = {
|
|
|
176
135
|
configs: {
|
|
177
136
|
android: '1',
|
|
178
137
|
ios: '1',
|
|
138
|
+
accessToken: 'test-token',
|
|
139
|
+
deeplink: '{}',
|
|
179
140
|
},
|
|
180
141
|
},
|
|
181
142
|
},
|
|
@@ -184,6 +145,7 @@ const defaultProps = {
|
|
|
184
145
|
query: {},
|
|
185
146
|
search: '',
|
|
186
147
|
},
|
|
148
|
+
getDefaultTags: null,
|
|
187
149
|
supportedTags: [],
|
|
188
150
|
metaEntities: {
|
|
189
151
|
tags: {
|
|
@@ -196,11 +158,26 @@ const defaultProps = {
|
|
|
196
158
|
currentOrgDetails: {
|
|
197
159
|
accessibleFeatures: [],
|
|
198
160
|
},
|
|
161
|
+
fetchingLiquidValidation: false,
|
|
162
|
+
getTemplateDetailsInProgress: false,
|
|
163
|
+
isEditInApp: false,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// Create stable defaultData objects outside renderComponent to avoid reference changes
|
|
167
|
+
const createStableDefaultData = (editorType) => {
|
|
168
|
+
if (!editorType) return {};
|
|
169
|
+
return { 'editor-type': editorType };
|
|
199
170
|
};
|
|
200
171
|
|
|
201
172
|
const renderComponent = (props = {}) => {
|
|
202
173
|
const mergedProps = { ...defaultProps, ...props };
|
|
203
|
-
|
|
174
|
+
// Ensure defaultData is always an object and stable
|
|
175
|
+
if (!mergedProps.defaultData) {
|
|
176
|
+
mergedProps.defaultData = {};
|
|
177
|
+
} else if (mergedProps.defaultData['editor-type']) {
|
|
178
|
+
// Create a stable reference for defaultData
|
|
179
|
+
mergedProps.defaultData = createStableDefaultData(mergedProps.defaultData['editor-type']);
|
|
180
|
+
}
|
|
204
181
|
return render(
|
|
205
182
|
<Provider store={store}>
|
|
206
183
|
<IntlProvider locale="en" messages={{}}>
|
|
@@ -218,33 +195,36 @@ describe('InApp HTMLEditor Integration', () => {
|
|
|
218
195
|
describe('Template Name Editing', () => {
|
|
219
196
|
test('should not truncate template name when editing', async () => {
|
|
220
197
|
const showTemplateNameMock = jest.fn();
|
|
221
|
-
|
|
222
198
|
renderComponent({
|
|
223
|
-
defaultData: {
|
|
224
|
-
'editor-type': INAPP_EDITOR_TYPES.HTML_EDITOR,
|
|
225
|
-
'template-name': 'abcd',
|
|
226
|
-
},
|
|
199
|
+
defaultData: { 'editor-type': INAPP_EDITOR_TYPES.HTML_EDITOR, 'template-name': 'abcd' },
|
|
227
200
|
showTemplateName: showTemplateNameMock,
|
|
228
201
|
});
|
|
229
202
|
|
|
230
|
-
|
|
231
|
-
|
|
203
|
+
// Wait for HTML editor to render
|
|
204
|
+
await waitFor(() => {
|
|
205
|
+
expect(screen.getByTestId('inapp-html-editor')).toBeInTheDocument();
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Verify showTemplateName was called with the template name
|
|
209
|
+
// The template name input is handled by the wrapper, not the InApp component
|
|
210
|
+
// So we verify that the callback was set up correctly
|
|
232
211
|
expect(showTemplateNameMock).toBeDefined();
|
|
233
212
|
});
|
|
234
213
|
|
|
235
214
|
test('should preserve template name when showTemplateName callback is provided', async () => {
|
|
236
215
|
const showTemplateNameMock = jest.fn();
|
|
237
|
-
|
|
238
216
|
renderComponent({
|
|
239
|
-
defaultData: {
|
|
240
|
-
'editor-type': INAPP_EDITOR_TYPES.HTML_EDITOR,
|
|
241
|
-
'template-name': 'test',
|
|
242
|
-
},
|
|
217
|
+
defaultData: { 'editor-type': INAPP_EDITOR_TYPES.HTML_EDITOR, 'template-name': 'test' },
|
|
243
218
|
showTemplateName: showTemplateNameMock,
|
|
244
219
|
});
|
|
245
220
|
|
|
246
|
-
|
|
247
|
-
|
|
221
|
+
// Wait for HTML editor to render
|
|
222
|
+
await waitFor(() => {
|
|
223
|
+
expect(screen.getByTestId('inapp-html-editor')).toBeInTheDocument();
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// Verify the component renders correctly with template name
|
|
227
|
+
// The template name is managed by the wrapper component
|
|
248
228
|
expect(showTemplateNameMock).toBeDefined();
|
|
249
229
|
});
|
|
250
230
|
});
|
|
@@ -252,7 +232,40 @@ describe('InApp HTMLEditor Integration', () => {
|
|
|
252
232
|
describe('TAG API Calls', () => {
|
|
253
233
|
test('should only make one TAG API call when HTML Editor is used', async () => {
|
|
254
234
|
const fetchSchemaForEntityMock = jest.fn();
|
|
235
|
+
|
|
236
|
+
renderComponent({
|
|
237
|
+
defaultData: { 'editor-type': INAPP_EDITOR_TYPES.HTML_EDITOR },
|
|
238
|
+
globalActions: {
|
|
239
|
+
...defaultProps.globalActions,
|
|
240
|
+
fetchSchemaForEntity: fetchSchemaForEntityMock,
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// Wait for component to mount and initialize
|
|
245
|
+
await waitFor(() => {
|
|
246
|
+
// Component should be rendered
|
|
247
|
+
expect(screen.getByTestId('inapp-html-editor')).toBeInTheDocument();
|
|
248
|
+
});
|
|
255
249
|
|
|
250
|
+
// Wait a bit for all useEffects to complete
|
|
251
|
+
await waitFor(() => {
|
|
252
|
+
// After isHTMLTemplate is set to true, fetchSchemaForEntity should not be called again
|
|
253
|
+
// The initial call might happen before isHTMLTemplate is set, but subsequent calls should not happen
|
|
254
|
+
// For HTML Editor, tags should only be fetched via handleOnTagsContextChange
|
|
255
|
+
}, { timeout: 1000 });
|
|
256
|
+
|
|
257
|
+
// For HTML Editor, fetchSchemaForEntity should not be called after isHTMLTemplate is set
|
|
258
|
+
// (It might be called once initially before isHTMLTemplate is set, but that's acceptable)
|
|
259
|
+
// The key is that it's not called multiple times
|
|
260
|
+
const callCount = fetchSchemaForEntityMock.mock.calls.length;
|
|
261
|
+
// Allow 0 or 1 calls (1 if it was called before isHTMLTemplate was set)
|
|
262
|
+
expect(callCount).toBeLessThanOrEqual(1);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test('should make TAG API call only once when handleOnTagsContextChange is called', async () => {
|
|
266
|
+
const fetchSchemaForEntityMock = jest.fn();
|
|
267
|
+
|
|
268
|
+
// We need to access the handler, but since it's internal, we'll test via HTMLEditor's onContextChange
|
|
256
269
|
renderComponent({
|
|
257
270
|
defaultData: { 'editor-type': INAPP_EDITOR_TYPES.HTML_EDITOR },
|
|
258
271
|
globalActions: {
|
|
@@ -261,8 +274,19 @@ describe('InApp HTMLEditor Integration', () => {
|
|
|
261
274
|
},
|
|
262
275
|
});
|
|
263
276
|
|
|
264
|
-
await
|
|
277
|
+
await waitFor(() => {
|
|
278
|
+
expect(screen.getByTestId('inapp-html-editor')).toBeInTheDocument();
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// Wait a bit for all useEffects to complete
|
|
282
|
+
await waitFor(() => {
|
|
283
|
+
// After isHTMLTemplate is set to true, fetchSchemaForEntity should not be called again
|
|
284
|
+
}, { timeout: 1000 });
|
|
265
285
|
|
|
286
|
+
// Simulate context change from HTMLEditor (which would call onContextChange)
|
|
287
|
+
// Since we can't directly access the handler, we verify the behavior
|
|
288
|
+
// by checking that fetchSchemaForEntity is not called multiple times
|
|
289
|
+
// (It might be called once initially before isHTMLTemplate is set, but that's acceptable)
|
|
266
290
|
const callCount = fetchSchemaForEntityMock.mock.calls.length;
|
|
267
291
|
expect(callCount).toBeLessThanOrEqual(1);
|
|
268
292
|
});
|
|
@@ -274,37 +298,45 @@ describe('InApp HTMLEditor Integration', () => {
|
|
|
274
298
|
defaultData: { 'editor-type': INAPP_EDITOR_TYPES.HTML_EDITOR },
|
|
275
299
|
});
|
|
276
300
|
|
|
277
|
-
await
|
|
301
|
+
await waitFor(() => {
|
|
302
|
+
expect(screen.getByTestId('inapp-html-editor')).toBeInTheDocument();
|
|
303
|
+
});
|
|
278
304
|
|
|
305
|
+
// Simulate content being added
|
|
279
306
|
const changeButton = screen.getByTestId('html-editor-change-button');
|
|
280
|
-
|
|
281
307
|
act(() => {
|
|
282
308
|
fireEvent.click(changeButton);
|
|
283
309
|
});
|
|
284
310
|
|
|
311
|
+
// Get the layout radio buttons
|
|
285
312
|
const layoutRadios = container.querySelectorAll('input[type="radio"]');
|
|
313
|
+
expect(layoutRadios.length).toBeGreaterThan(0);
|
|
286
314
|
|
|
315
|
+
// Change layout type
|
|
287
316
|
if (layoutRadios.length > 1) {
|
|
288
317
|
act(() => {
|
|
289
|
-
fireEvent.change(layoutRadios[1], {
|
|
290
|
-
target: { value: 'HEADER' },
|
|
291
|
-
});
|
|
318
|
+
fireEvent.change(layoutRadios[1], { target: { value: 'HEADER' } });
|
|
292
319
|
});
|
|
293
320
|
|
|
294
|
-
|
|
295
|
-
|
|
321
|
+
// Content should still be present in the editor
|
|
322
|
+
// The HTMLEditor should preserve content via initialContent prop
|
|
323
|
+
await waitFor(() => {
|
|
324
|
+
const editor = screen.getByTestId('inapp-html-editor');
|
|
325
|
+
expect(editor).toBeInTheDocument();
|
|
326
|
+
});
|
|
296
327
|
}
|
|
297
328
|
});
|
|
298
329
|
});
|
|
299
330
|
|
|
300
331
|
describe('Layout Labels', () => {
|
|
301
332
|
test('should use correct layout labels in constants', () => {
|
|
302
|
-
const { INAPP_LAYOUT_DETAILS, INAPP_MESSAGE_LAYOUT_TYPES } =
|
|
303
|
-
|
|
304
|
-
|
|
333
|
+
const { INAPP_LAYOUT_DETAILS, INAPP_MESSAGE_LAYOUT_TYPES } = require('../constants');
|
|
334
|
+
|
|
335
|
+
// Verify that HEADER maps to Top banner
|
|
305
336
|
expect(INAPP_MESSAGE_LAYOUT_TYPES.TOPBANNER).toBe('HEADER');
|
|
306
337
|
expect(INAPP_LAYOUT_DETAILS.HEADER).toBeDefined();
|
|
307
|
-
|
|
338
|
+
|
|
339
|
+
// Verify that FOOTER maps to Bottom banner
|
|
308
340
|
expect(INAPP_MESSAGE_LAYOUT_TYPES.BOTTOMBANNER).toBe('FOOTER');
|
|
309
341
|
expect(INAPP_LAYOUT_DETAILS.FOOTER).toBeDefined();
|
|
310
342
|
});
|
|
@@ -314,16 +346,29 @@ describe('InApp HTMLEditor Integration', () => {
|
|
|
314
346
|
test('should allow creating template with Android-only content when both devices supported', async () => {
|
|
315
347
|
renderComponent({
|
|
316
348
|
defaultData: { 'editor-type': INAPP_EDITOR_TYPES.HTML_EDITOR },
|
|
349
|
+
accountData: {
|
|
350
|
+
selectedWeChatAccount: {
|
|
351
|
+
...defaultProps.accountData.selectedWeChatAccount,
|
|
352
|
+
configs: {
|
|
353
|
+
android: '1',
|
|
354
|
+
ios: '1',
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
},
|
|
317
358
|
});
|
|
318
359
|
|
|
319
|
-
await
|
|
360
|
+
await waitFor(() => {
|
|
361
|
+
expect(screen.getByTestId('inapp-html-editor')).toBeInTheDocument();
|
|
362
|
+
});
|
|
320
363
|
|
|
364
|
+
// Simulate adding content only for Android
|
|
321
365
|
const changeButton = screen.getByTestId('html-editor-change-button');
|
|
322
|
-
|
|
323
366
|
act(() => {
|
|
324
367
|
fireEvent.click(changeButton);
|
|
325
368
|
});
|
|
326
369
|
|
|
370
|
+
// Done button should be enabled (not disabled)
|
|
371
|
+
// We can't directly test isDisableDone, but we can verify the component renders
|
|
327
372
|
expect(screen.getByTestId('inapp-html-editor')).toBeInTheDocument();
|
|
328
373
|
});
|
|
329
374
|
});
|
|
@@ -14,8 +14,6 @@ import CapRow from "@capillarytech/cap-ui-library/CapRow";
|
|
|
14
14
|
import CapColumn from "@capillarytech/cap-ui-library/CapColumn";
|
|
15
15
|
import CapButton from "@capillarytech/cap-ui-library/CapButton";
|
|
16
16
|
import CapNotification from "@capillarytech/cap-ui-library/CapNotification";
|
|
17
|
-
import CapTab from "@capillarytech/cap-ui-library/CapTab";
|
|
18
|
-
import CapInput from "@capillarytech/cap-ui-library/CapInput";
|
|
19
17
|
import { makeSelectInApp, makeSelectAccount, makeSelectGetTemplateDetailsInProgress } from "./selectors";
|
|
20
18
|
import * as globalActions from '../Cap/actions';
|
|
21
19
|
import {
|
|
@@ -54,20 +52,18 @@ import {
|
|
|
54
52
|
IOS_CAPITAL,
|
|
55
53
|
} from "./constants";
|
|
56
54
|
import { INAPP, SMS } from "../CreativesContainer/constants";
|
|
57
|
-
import { AI_CONTENT_BOT_DISABLED } from "../../constants/unified";
|
|
58
55
|
import {
|
|
59
56
|
ALL, TAG, EMBEDDED, DEFAULT, FULL, LIBRARY,
|
|
60
57
|
} from "../Whatsapp/constants";
|
|
61
58
|
import { getCdnUrl } from "../../utils/cdnTransformation";
|
|
62
59
|
import { getCtaObject, hasAnyErrors, getSingleTab } from "./utils";
|
|
63
60
|
import { validateInAppContent } from "../../utils/commonUtils";
|
|
64
|
-
import { hasLiquidSupportFeature
|
|
61
|
+
import { hasLiquidSupportFeature } from "../../utils/common";
|
|
65
62
|
import formBuilderMessages from "../../v2Components/FormBuilder/messages";
|
|
66
63
|
import HTMLEditor from "../../v2Components/HtmlEditor";
|
|
67
64
|
import { HTML_EDITOR_VARIANTS } from "../../v2Components/HtmlEditor/constants";
|
|
68
65
|
import { INAPP_EDITOR_TYPES } from "../InAppWrapper/constants";
|
|
69
66
|
import InappAdvanced from "../InappAdvance/index";
|
|
70
|
-
import CapDeviceContent from "../../v2Components/CapDeviceContent";
|
|
71
67
|
import { ErrorInfoNote } from "../../v2Components/ErrorInfoNote";
|
|
72
68
|
|
|
73
69
|
let editContent = {};
|
|
@@ -78,7 +74,6 @@ export const InApp = (props) => {
|
|
|
78
74
|
actions,
|
|
79
75
|
globalActions,
|
|
80
76
|
isFullMode,
|
|
81
|
-
isLoyaltyModule,
|
|
82
77
|
onCreateComplete,
|
|
83
78
|
params,
|
|
84
79
|
templateData = {},
|
|
@@ -883,7 +878,7 @@ export const InApp = (props) => {
|
|
|
883
878
|
// Use 'html editor template' as title for HTML editor to differentiate from BEE editor
|
|
884
879
|
title: isHTMLTemplate ? 'html editor template' : titleAndroid,
|
|
885
880
|
message: androidMessage,
|
|
886
|
-
bodyType:
|
|
881
|
+
bodyType: templateLayoutType,
|
|
887
882
|
expandableDetails: {
|
|
888
883
|
style: androidExpandableStyle,
|
|
889
884
|
message: androidMessage,
|
|
@@ -1245,13 +1240,11 @@ export const InApp = (props) => {
|
|
|
1245
1240
|
&& !isBEEeditor
|
|
1246
1241
|
&& !isBeeFreeTemplate;
|
|
1247
1242
|
|
|
1248
|
-
const isNewEditorFlowEnabled = !isLoyaltyModule && hasNewEditorFlowInAppEnabled();
|
|
1249
|
-
|
|
1250
1243
|
// Use state if available, otherwise fall back to direct data check
|
|
1251
|
-
const shouldUseHTMLEditor =
|
|
1244
|
+
const shouldUseHTMLEditor = isHTMLTemplate || isHTMLTemplateFromData;
|
|
1252
1245
|
|
|
1253
1246
|
// Only route to Bee editor if it's explicitly a Bee editor AND not an HTML template
|
|
1254
|
-
const shouldUseBeeEditor =
|
|
1247
|
+
const shouldUseBeeEditor = (isBEEeditor || isBeeFreeTemplate) && !shouldUseHTMLEditor;
|
|
1255
1248
|
|
|
1256
1249
|
// Early returns to avoid nested ternary
|
|
1257
1250
|
if (isEditInApp && getTemplateDetailsInProgress) {
|
|
@@ -1287,157 +1280,10 @@ export const InApp = (props) => {
|
|
|
1287
1280
|
);
|
|
1288
1281
|
}
|
|
1289
1282
|
|
|
1290
|
-
// ── Old-flow helpers (used by CapDeviceContent when flag is disabled) ──────
|
|
1291
|
-
const isAiContentBotDisabled = currentOrgDetails?.accessibleFeatures?.includes(AI_CONTENT_BOT_DISABLED);
|
|
1292
|
-
|
|
1293
|
-
const templateDescErrorHandler = (value) => {
|
|
1294
|
-
const { unsupportedTags, isBraceError } = validateTags({
|
|
1295
|
-
content: value,
|
|
1296
|
-
tagsParam: tags,
|
|
1297
|
-
injectedTagsParams: injectedTags,
|
|
1298
|
-
location,
|
|
1299
|
-
tagModule: getDefaultTags,
|
|
1300
|
-
isFullMode,
|
|
1301
|
-
}) || {};
|
|
1302
|
-
if (unsupportedTags?.length > 0) {
|
|
1303
|
-
return formatMessage(globalMessages.unsupportedTagsValidationError, { unsupportedTags });
|
|
1304
|
-
}
|
|
1305
|
-
if (isBraceError) {
|
|
1306
|
-
return formatMessage(globalMessages.braceValidationError);
|
|
1307
|
-
}
|
|
1308
|
-
return '';
|
|
1309
|
-
};
|
|
1310
|
-
|
|
1311
|
-
const onCopyTitleAndContent = () => {
|
|
1312
|
-
if (panes === ANDROID) {
|
|
1313
|
-
setTitleAndroid(titleIos);
|
|
1314
|
-
setTemplateMessageAndroid(templateMessageIos);
|
|
1315
|
-
setInAppImageSrcAndroid(inAppImageSrcIos);
|
|
1316
|
-
} else {
|
|
1317
|
-
setTitleIos(titleAndroid);
|
|
1318
|
-
setTemplateMessageIos(templateMessageAndroid);
|
|
1319
|
-
setInAppImageSrcIos(inAppImageSrcAndroid);
|
|
1320
|
-
}
|
|
1321
|
-
};
|
|
1322
|
-
|
|
1323
|
-
const onTagSelect = (value, index) => {
|
|
1324
|
-
const tag = `{{${value}}}`;
|
|
1325
|
-
if (panes === ANDROID) {
|
|
1326
|
-
if (index === 0) setTitleAndroid((prev) => prev + tag);
|
|
1327
|
-
else setTemplateMessageAndroid((prev) => prev + tag);
|
|
1328
|
-
} else if (index === 0) {
|
|
1329
|
-
setTitleIos((prev) => prev + tag);
|
|
1330
|
-
} else {
|
|
1331
|
-
setTemplateMessageIos((prev) => prev + tag);
|
|
1332
|
-
}
|
|
1333
|
-
};
|
|
1334
|
-
|
|
1335
|
-
// Device support flags (same derivation as InappAdvance)
|
|
1336
|
-
const isAndroidSupported = get(accountData, 'selectedWeChatAccount.configs.android') === DEVICE_SUPPORTED || get(editContent, 'ANDROID.deviceType') === ANDROID;
|
|
1337
|
-
const isIosSupported = get(accountData, 'selectedWeChatAccount.configs.ios') === DEVICE_SUPPORTED || get(editContent, 'IOS.deviceType') === IOS_CAPITAL;
|
|
1338
|
-
|
|
1339
|
-
// CapDeviceContent tab panes (old flow)
|
|
1340
|
-
const DEVICE_PANES = [
|
|
1341
|
-
{
|
|
1342
|
-
content: (
|
|
1343
|
-
<CapDeviceContent
|
|
1344
|
-
panes={ANDROID}
|
|
1345
|
-
actions={actions}
|
|
1346
|
-
editData={editData}
|
|
1347
|
-
isFullMode={isFullMode}
|
|
1348
|
-
inAppImageSrc={inAppImageSrcAndroid}
|
|
1349
|
-
setInAppImageSrc={setInAppImageSrcAndroid}
|
|
1350
|
-
isEditFlow={isEditFlow}
|
|
1351
|
-
ctaData={ctaDataAndroid}
|
|
1352
|
-
setCtaData={setCtaDataAndroid}
|
|
1353
|
-
buttonType={buttonTypeAndroid}
|
|
1354
|
-
setButtonType={setButtonTypeAndroid}
|
|
1355
|
-
templateMediaType={templateMediaType}
|
|
1356
|
-
setTemplateMediaType={setTemplateMediaType}
|
|
1357
|
-
title={titleAndroid}
|
|
1358
|
-
setTitle={setTitleAndroid}
|
|
1359
|
-
templateMessageError={templateMessageErrorAndroid}
|
|
1360
|
-
templateMessage={templateMessageAndroid}
|
|
1361
|
-
setTemplateMessage={setTemplateMessageAndroid}
|
|
1362
|
-
setTemplateMessageError={setTemplateMessageErrorAndroid}
|
|
1363
|
-
addActionLink={addActionLinkAndroid}
|
|
1364
|
-
setAddActionLink={setAddActionLinkAndroid}
|
|
1365
|
-
deepLink={deepLink}
|
|
1366
|
-
deepLinkValue={deepLinkValueAndroid}
|
|
1367
|
-
setDeepLinkValue={setDeepLinkValueAndroid}
|
|
1368
|
-
onCopyTitleAndContent={onCopyTitleAndContent}
|
|
1369
|
-
isOtherDeviceSupported={isIosSupported}
|
|
1370
|
-
tags={tags}
|
|
1371
|
-
onTagSelect={onTagSelect}
|
|
1372
|
-
handleOnTagsContextChange={handleOnTagsContextChange}
|
|
1373
|
-
templateDescErrorHandler={templateDescErrorHandler}
|
|
1374
|
-
templateTitleError={templateTitleErrorAndroid}
|
|
1375
|
-
setTemplateTitleError={setTemplateTitleErrorAndroid}
|
|
1376
|
-
isAiContentBotDisabled={isAiContentBotDisabled}
|
|
1377
|
-
injectedTags={injectedTags}
|
|
1378
|
-
selectedOfferDetails={selectedOfferDetails}
|
|
1379
|
-
location={location}
|
|
1380
|
-
/>
|
|
1381
|
-
),
|
|
1382
|
-
tab: <FormattedMessage {...messages.Android} />,
|
|
1383
|
-
key: ANDROID,
|
|
1384
|
-
isSupported: isAndroidSupported,
|
|
1385
|
-
},
|
|
1386
|
-
{
|
|
1387
|
-
content: (
|
|
1388
|
-
<CapDeviceContent
|
|
1389
|
-
panes={IOS}
|
|
1390
|
-
actions={actions}
|
|
1391
|
-
editData={editData}
|
|
1392
|
-
isFullMode={isFullMode}
|
|
1393
|
-
inAppImageSrc={inAppImageSrcIos}
|
|
1394
|
-
setInAppImageSrc={setInAppImageSrcIos}
|
|
1395
|
-
isEditFlow={isEditFlow}
|
|
1396
|
-
ctaData={ctaDataIos}
|
|
1397
|
-
setCtaData={setCtaDataIos}
|
|
1398
|
-
buttonType={buttonTypeIos}
|
|
1399
|
-
setButtonType={setButtonTypeIos}
|
|
1400
|
-
templateMediaType={templateMediaType}
|
|
1401
|
-
setTemplateMediaType={setTemplateMediaType}
|
|
1402
|
-
title={titleIos}
|
|
1403
|
-
setTitle={setTitleIos}
|
|
1404
|
-
templateMessageError={templateMessageErrorIos}
|
|
1405
|
-
templateMessage={templateMessageIos}
|
|
1406
|
-
setTemplateMessage={setTemplateMessageIos}
|
|
1407
|
-
setTemplateMessageError={setTemplateMessageErrorIos}
|
|
1408
|
-
addActionLink={addActionLinkIos}
|
|
1409
|
-
setAddActionLink={setAddActionLinkIos}
|
|
1410
|
-
deepLink={deepLink}
|
|
1411
|
-
deepLinkValue={deepLinkValueIos}
|
|
1412
|
-
setDeepLinkValue={setDeepLinkValueIos}
|
|
1413
|
-
onCopyTitleAndContent={onCopyTitleAndContent}
|
|
1414
|
-
isOtherDeviceSupported={isAndroidSupported}
|
|
1415
|
-
tags={tags}
|
|
1416
|
-
onTagSelect={onTagSelect}
|
|
1417
|
-
handleOnTagsContextChange={handleOnTagsContextChange}
|
|
1418
|
-
templateDescErrorHandler={templateDescErrorHandler}
|
|
1419
|
-
templateTitleError={templateTitleErrorIos}
|
|
1420
|
-
setTemplateTitleError={setTemplateTitleErrorIos}
|
|
1421
|
-
isAiContentBotDisabled={isAiContentBotDisabled}
|
|
1422
|
-
injectedTags={injectedTags}
|
|
1423
|
-
selectedOfferDetails={selectedOfferDetails}
|
|
1424
|
-
location={location}
|
|
1425
|
-
/>
|
|
1426
|
-
),
|
|
1427
|
-
tab: <FormattedMessage {...messages.Ios} />,
|
|
1428
|
-
key: IOS,
|
|
1429
|
-
isSupported: isIosSupported,
|
|
1430
|
-
},
|
|
1431
|
-
];
|
|
1432
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1433
|
-
|
|
1434
|
-
// Calculate column span: HTML editor = 18, everything else = 24
|
|
1435
|
-
const editorColumnSpan = shouldUseHTMLEditor ? 18 : 24;
|
|
1436
|
-
|
|
1437
1283
|
return (
|
|
1438
1284
|
<CapSpin spinning={spin || fetchingLiquidValidation} tip={fetchingLiquidValidation ? <FormattedMessage {...formBuilderMessages.liquidSpinText} /> : ""}>
|
|
1439
1285
|
<CapRow className="cap-inapp-creatives">
|
|
1440
|
-
<CapColumn span={
|
|
1286
|
+
<CapColumn span={shouldUseHTMLEditor ? 18 : 24}>
|
|
1441
1287
|
{/* Creative layout type */}
|
|
1442
1288
|
{shouldUseHTMLEditor && (
|
|
1443
1289
|
<>
|
|
@@ -1458,7 +1304,7 @@ export const InApp = (props) => {
|
|
|
1458
1304
|
/>
|
|
1459
1305
|
</>
|
|
1460
1306
|
)}
|
|
1461
|
-
{shouldUseHTMLEditor
|
|
1307
|
+
{shouldUseHTMLEditor ? (
|
|
1462
1308
|
<HTMLEditor
|
|
1463
1309
|
key={`inapp-html-editor-v${htmlEditorContentVersion}`}
|
|
1464
1310
|
variant={HTML_EDITOR_VARIANTS.INAPP}
|
|
@@ -1474,16 +1320,19 @@ export const InApp = (props) => {
|
|
|
1474
1320
|
location={location}
|
|
1475
1321
|
selectedOfferDetails={selectedOfferDetails}
|
|
1476
1322
|
onTagSelect={() => {
|
|
1477
|
-
// Tag insertion handled by HTMLEditor's CodeEditorPane at cursor position
|
|
1323
|
+
// Tag insertion is handled by HTMLEditor's CodeEditorPane at cursor position
|
|
1324
|
+
// Content updates will be propagated via onContentChange callback
|
|
1478
1325
|
}}
|
|
1326
|
+
// Don't pass globalActions to prevent duplicate API calls
|
|
1327
|
+
// HTMLEditor will use onContextChange instead
|
|
1479
1328
|
onContextChange={handleOnTagsContextChange}
|
|
1329
|
+
// Pass validation errors to HTMLEditor for display
|
|
1480
1330
|
errors={errorMessage}
|
|
1481
1331
|
isFullMode={isFullMode}
|
|
1482
1332
|
data-test="inapp-html-editor"
|
|
1483
1333
|
style={{ width: '138%' }}
|
|
1484
1334
|
/>
|
|
1485
|
-
)
|
|
1486
|
-
{!shouldUseHTMLEditor && isNewEditorFlowEnabled && (
|
|
1335
|
+
) : (
|
|
1487
1336
|
<InappAdvanced
|
|
1488
1337
|
getFormData={getFormData}
|
|
1489
1338
|
setIsLoadingContent={setIsLoadingContent}
|
|
@@ -1508,29 +1357,11 @@ export const InApp = (props) => {
|
|
|
1508
1357
|
onCreateComplete={onCreateComplete}
|
|
1509
1358
|
/>
|
|
1510
1359
|
)}
|
|
1511
|
-
{!shouldUseHTMLEditor && !isNewEditorFlowEnabled && (
|
|
1512
|
-
<>
|
|
1513
|
-
<CapInput
|
|
1514
|
-
label={<FormattedMessage {...messages.creativeName} />}
|
|
1515
|
-
onChange={({ target: { value } }) => setTempName(value)}
|
|
1516
|
-
value={tempName}
|
|
1517
|
-
labelPosition="top"
|
|
1518
|
-
size="default"
|
|
1519
|
-
/>
|
|
1520
|
-
<CapTab
|
|
1521
|
-
panes={DEVICE_PANES.filter((devicePane) => devicePane?.isSupported === true)}
|
|
1522
|
-
onChange={(value) => setPanes(value)}
|
|
1523
|
-
activeKey={panes}
|
|
1524
|
-
defaultActiveKey={panes}
|
|
1525
|
-
className="inapp-template-device-tab"
|
|
1526
|
-
/>
|
|
1527
|
-
</>
|
|
1528
|
-
)}
|
|
1529
1360
|
</CapColumn>
|
|
1530
1361
|
</CapRow>
|
|
1531
|
-
{/* Footer with Done/Update button - show for HTML editor
|
|
1362
|
+
{/* Footer with Done/Update button - Only show for HTML editor, bee editor has its own footer */}
|
|
1532
1363
|
{
|
|
1533
|
-
|
|
1364
|
+
shouldUseHTMLEditor && (
|
|
1534
1365
|
<>
|
|
1535
1366
|
{hasAnyErrors(errorMessage) && (
|
|
1536
1367
|
<ErrorInfoNote
|