@capillarytech/creatives-library 8.0.168 → 8.0.170-beta.1
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/app.js +4 -0
- package/containers/App/constants.js +2 -0
- package/package.json +2 -1
- package/utils/commonUtils.js +50 -0
- package/utils/test-utils.js +6 -2
- package/utils/tests/newrelic.test.js +546 -0
- package/v2Components/CapActionButton/index.js +52 -12
- package/v2Components/CapActionButton/messages.js +4 -0
- package/v2Components/CapActionButton/tests/index.test.js +135 -0
- package/v2Components/CapDeviceContent/index.js +5 -0
- package/v2Components/CapInAppCTA/index.js +29 -14
- package/v2Components/CapInAppCTA/index.scss +0 -2
- package/v2Components/CapInAppCTA/messages.js +4 -0
- package/v2Components/CapMpushCTA/index.js +54 -38
- package/v2Components/CapMpushCTA/index.scss +2 -2
- package/v2Components/CapMpushCTA/messages.js +4 -0
- package/v2Components/CapTagListWithInput/index.js +169 -0
- package/v2Components/CapTagListWithInput/messages.js +10 -0
- package/v2Components/FormBuilder/index.js +93 -1
- package/v2Components/TestAndPreviewSlidebox/PreviewSection.js +1 -1
- package/v2Components/TestAndPreviewSlidebox/index.js +24 -4
- package/v2Containers/Email/index.js +64 -3
- package/v2Containers/Email/initialSchema.js +7 -21
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +2 -2
- package/v2Containers/Line/Container/Wrapper/tests/index.test.js +56 -1
- package/v2Containers/Line/Container/Wrapper/utils.js +6 -4
- package/v2Containers/MobilePush/Create/index.js +24 -3
- package/v2Containers/MobilePush/commonMethods.js +25 -3
- package/v2Containers/MobilePushNew/components/CtaButtons.js +20 -0
- package/v2Containers/MobilePushNew/components/MediaUploaders.js +31 -3
- package/v2Containers/MobilePushNew/components/PlatformContentFields.js +34 -3
- package/v2Containers/MobilePushNew/components/tests/MediaUploaders.test.js +200 -5
- package/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +59 -1
- package/v2Containers/MobilePushNew/index.js +9 -0
- package/v2Containers/MobilePushNew/index.scss +2 -1
- package/v2Containers/Rcs/index.js +77 -71
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +15270 -492
- package/v2Containers/Viber/index.js +102 -76
|
@@ -202,11 +202,33 @@ function getLinkTypeFields({inputFieldsArgs, fieldIndex, deepLinkOptions, formDa
|
|
|
202
202
|
identifier: inputId,
|
|
203
203
|
|
|
204
204
|
cols: [
|
|
205
|
-
|
|
205
|
+
{
|
|
206
|
+
// Add label control for external link input
|
|
207
|
+
id: "title-tagList",
|
|
208
|
+
label: "Add label",
|
|
209
|
+
type: "tag-list",
|
|
210
|
+
metaType: "List",
|
|
211
|
+
datatype: "select",
|
|
212
|
+
required: true,
|
|
213
|
+
fluid: true,
|
|
214
|
+
onlyDisplay: true,
|
|
215
|
+
style: {
|
|
216
|
+
marginRight: "10%",
|
|
217
|
+
marginTop: "-3%",
|
|
218
|
+
},
|
|
219
|
+
styling: "semantic",
|
|
220
|
+
order: 2,
|
|
221
|
+
customComponent: true,
|
|
222
|
+
// Helps handler identify the destination input
|
|
223
|
+
target: inputId,
|
|
224
|
+
supportedEvents: [
|
|
225
|
+
"onTagSelect",
|
|
226
|
+
],
|
|
227
|
+
},
|
|
206
228
|
{
|
|
207
229
|
id: inputId,
|
|
208
230
|
placeholder: "External Link",
|
|
209
|
-
type: "input",
|
|
231
|
+
type: "input", //Resembles component to be used
|
|
210
232
|
metaType: "text",
|
|
211
233
|
datatype: "string",
|
|
212
234
|
errorMessage: <FormattedMessage {...messages.invalidExternalLink}/>,
|
|
@@ -215,7 +237,7 @@ function getLinkTypeFields({inputFieldsArgs, fieldIndex, deepLinkOptions, formDa
|
|
|
215
237
|
width: 18,
|
|
216
238
|
offset: 1,
|
|
217
239
|
style: {
|
|
218
|
-
width: '
|
|
240
|
+
width: '95%',
|
|
219
241
|
},
|
|
220
242
|
styling: "semantic",
|
|
221
243
|
order: 1,
|
|
@@ -19,6 +19,11 @@ const CtaButtons = ({
|
|
|
19
19
|
updateHandler,
|
|
20
20
|
deleteHandler,
|
|
21
21
|
deepLink,
|
|
22
|
+
location,
|
|
23
|
+
tags,
|
|
24
|
+
injectedTags,
|
|
25
|
+
selectedOfferDetails,
|
|
26
|
+
handleOnTagsContextChange,
|
|
22
27
|
}) => {
|
|
23
28
|
// Local state to control CTA form visibility
|
|
24
29
|
const [showPrimaryCTA, setShowPrimaryCTA] = useState(false);
|
|
@@ -130,6 +135,11 @@ const CtaButtons = ({
|
|
|
130
135
|
deleteHandler={handleDelete}
|
|
131
136
|
deepLink={deepLink}
|
|
132
137
|
buttonType={PRIMARY}
|
|
138
|
+
location={location}
|
|
139
|
+
tags={tags}
|
|
140
|
+
injectedTags={injectedTags}
|
|
141
|
+
selectedOfferDetails={selectedOfferDetails}
|
|
142
|
+
handleOnTagsContextChange={handleOnTagsContextChange}
|
|
133
143
|
/>
|
|
134
144
|
)}
|
|
135
145
|
{isPrimaryButtonSaved && !isSecondaryButtonSaved && !shouldShowSecondaryCTA && (
|
|
@@ -174,10 +184,20 @@ CtaButtons.propTypes = {
|
|
|
174
184
|
updateHandler: PropTypes.func.isRequired,
|
|
175
185
|
deleteHandler: PropTypes.func.isRequired,
|
|
176
186
|
deepLink: PropTypes.array,
|
|
187
|
+
handleOnTagsContextChange: PropTypes.func,
|
|
188
|
+
location: PropTypes.object,
|
|
189
|
+
tags: PropTypes.array,
|
|
190
|
+
injectedTags: PropTypes.object,
|
|
191
|
+
selectedOfferDetails: PropTypes.array,
|
|
177
192
|
};
|
|
178
193
|
|
|
179
194
|
CtaButtons.defaultProps = {
|
|
180
195
|
deepLink: [],
|
|
196
|
+
handleOnTagsContextChange: () => {},
|
|
197
|
+
location: {},
|
|
198
|
+
tags: [],
|
|
199
|
+
injectedTags: {},
|
|
200
|
+
selectedOfferDetails: [],
|
|
181
201
|
};
|
|
182
202
|
|
|
183
203
|
export default memo(CtaButtons);
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
} from "../constants";
|
|
44
44
|
import messages from "../messages";
|
|
45
45
|
import { validateExternalLink, validateDeepLink } from "../utils";
|
|
46
|
+
import TagList from "../../TagList";
|
|
46
47
|
|
|
47
48
|
// Initial carousel data structure
|
|
48
49
|
const CAROUSEL_INITIAL_DATA = {
|
|
@@ -102,6 +103,11 @@ const MediaUploaders = ({
|
|
|
102
103
|
setCarouselActiveTabIndex,
|
|
103
104
|
carouselLinkErrors = {}, // Carousel link errors from parent
|
|
104
105
|
updateCarouselLinkError, // Function to update carousel link errors in parent
|
|
106
|
+
location,
|
|
107
|
+
tags,
|
|
108
|
+
injectedTags,
|
|
109
|
+
selectedOfferDetails,
|
|
110
|
+
handleOnTagsContextChange,
|
|
105
111
|
}) => {
|
|
106
112
|
// Carousel state management - separate for Android and iOS
|
|
107
113
|
const [carouselMediaType, setCarouselMediaType] = useState(IMAGE.toLowerCase());
|
|
@@ -563,6 +569,14 @@ const MediaUploaders = ({
|
|
|
563
569
|
|
|
564
570
|
const button = card?.buttons[0]; // We're handling only one button for now
|
|
565
571
|
|
|
572
|
+
const onButtonTagSelect = useCallback(
|
|
573
|
+
(value) => {
|
|
574
|
+
const newUrl = `${button?.externalLinkValue}{{${value}}}`;
|
|
575
|
+
handleCarouselExternalLinkChange(cardIndex, newUrl);
|
|
576
|
+
},
|
|
577
|
+
[handleCarouselExternalLinkChange, button?.externalLinkValue]
|
|
578
|
+
);
|
|
579
|
+
|
|
566
580
|
return (
|
|
567
581
|
<>
|
|
568
582
|
<CapDivider />
|
|
@@ -633,9 +647,23 @@ const MediaUploaders = ({
|
|
|
633
647
|
)}
|
|
634
648
|
{button?.linkType === EXTERNAL_LINK && (
|
|
635
649
|
<CapColumn span={14}>
|
|
636
|
-
<
|
|
637
|
-
|
|
638
|
-
|
|
650
|
+
<CapRow>
|
|
651
|
+
<CapHeading type="h4" className="buttons-heading">
|
|
652
|
+
{formatMessage(messages.externalLink)}
|
|
653
|
+
</CapHeading>
|
|
654
|
+
<TagList
|
|
655
|
+
moduleFilterEnabled={
|
|
656
|
+
location?.query?.type !== "embedded"
|
|
657
|
+
}
|
|
658
|
+
label={formatMessage(messages.addLabels)}
|
|
659
|
+
onTagSelect={onButtonTagSelect}
|
|
660
|
+
onContextChange={handleOnTagsContextChange}
|
|
661
|
+
location={location}
|
|
662
|
+
tags={tags}
|
|
663
|
+
injectedTags={injectedTags}
|
|
664
|
+
selectedOfferDetails={selectedOfferDetails}
|
|
665
|
+
/>
|
|
666
|
+
</CapRow>
|
|
639
667
|
<CapInput
|
|
640
668
|
id={`mobile-push-external-link-input-${activeTab}-${cardIndex}`}
|
|
641
669
|
onChange={(e) => handleCarouselExternalLinkChange(cardIndex, e.target.value)}
|
|
@@ -36,6 +36,10 @@ const PlatformContentFields = ({
|
|
|
36
36
|
linkProps,
|
|
37
37
|
sameContent,
|
|
38
38
|
formatMessage,
|
|
39
|
+
location,
|
|
40
|
+
tags,
|
|
41
|
+
injectedTags,
|
|
42
|
+
selectedOfferDetails,
|
|
39
43
|
}) => {
|
|
40
44
|
const { title: titleError, message: messageError } = errors;
|
|
41
45
|
const {
|
|
@@ -145,6 +149,14 @@ const PlatformContentFields = ({
|
|
|
145
149
|
[tagListProps, onTagSelect, handleOnTagsContextChange]
|
|
146
150
|
);
|
|
147
151
|
|
|
152
|
+
const onButtonTagSelect = useCallback(
|
|
153
|
+
(value) => {
|
|
154
|
+
const newUrl = `${externalLinkValue}{{${value}}}`;
|
|
155
|
+
handleExternalLinkChange(newUrl);
|
|
156
|
+
},
|
|
157
|
+
[handleExternalLinkChange, externalLinkValue]
|
|
158
|
+
);
|
|
159
|
+
|
|
148
160
|
return (
|
|
149
161
|
<>
|
|
150
162
|
{sameContent && (
|
|
@@ -214,6 +226,11 @@ const PlatformContentFields = ({
|
|
|
214
226
|
mediaType={content.mediaType}
|
|
215
227
|
{...mediaUploaderProps}
|
|
216
228
|
formatMessage={formatMessage}
|
|
229
|
+
location={location}
|
|
230
|
+
tags={tags}
|
|
231
|
+
injectedTags={injectedTags}
|
|
232
|
+
selectedOfferDetails={selectedOfferDetails}
|
|
233
|
+
handleOnTagsContextChange={handleOnTagsContextChange}
|
|
217
234
|
/>
|
|
218
235
|
</CapRow>
|
|
219
236
|
{content?.mediaType !== CAROUSEL && (
|
|
@@ -281,9 +298,23 @@ const PlatformContentFields = ({
|
|
|
281
298
|
)}
|
|
282
299
|
{content.linkType === EXTERNAL_LINK && (
|
|
283
300
|
<CapColumn span={14}>
|
|
284
|
-
<
|
|
285
|
-
|
|
286
|
-
|
|
301
|
+
<CapRow style={{ display: "flex" }}>
|
|
302
|
+
<CapHeading type="h4" className="buttons-heading">
|
|
303
|
+
{formatMessage(messages.externalLink)}
|
|
304
|
+
</CapHeading>
|
|
305
|
+
<TagList
|
|
306
|
+
moduleFilterEnabled={
|
|
307
|
+
location?.query?.type !== "embedded"
|
|
308
|
+
}
|
|
309
|
+
label={formatMessage(messages.addLabels)}
|
|
310
|
+
onTagSelect={onButtonTagSelect}
|
|
311
|
+
onContextChange={handleOnTagsContextChange}
|
|
312
|
+
location={location}
|
|
313
|
+
tags={tags}
|
|
314
|
+
injectedTags={injectedTags}
|
|
315
|
+
selectedOfferDetails={selectedOfferDetails}
|
|
316
|
+
/>
|
|
317
|
+
</CapRow>
|
|
287
318
|
<CapInput
|
|
288
319
|
id="mobile-push-external-link-input"
|
|
289
320
|
onChange={onExternalLinkChange}
|
|
@@ -53,6 +53,149 @@ jest.mock('../../../../v2Components/CapImageUpload', () =>
|
|
|
53
53
|
}
|
|
54
54
|
);
|
|
55
55
|
|
|
56
|
+
// Mock Cap UI Library components that use styled-components
|
|
57
|
+
jest.mock('@capillarytech/cap-ui-library/CapButton', () =>
|
|
58
|
+
function MockCapButton({ children, ...props }) {
|
|
59
|
+
return <button {...props}>{children}</button>;
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
jest.mock('@capillarytech/cap-ui-library/CapCard', () =>
|
|
64
|
+
function MockCapCard({ children, title, extra, ...props }) {
|
|
65
|
+
return (
|
|
66
|
+
<div {...props} data-testid="cap-card">
|
|
67
|
+
{title && <div data-testid="card-title">{title}</div>}
|
|
68
|
+
{extra && <div data-testid="card-extra">{extra}</div>}
|
|
69
|
+
{children}
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
jest.mock('@capillarytech/cap-ui-library/CapIcon', () =>
|
|
76
|
+
function MockCapIcon({ type, ...props }) {
|
|
77
|
+
return <span {...props} data-testid={`cap-icon-${type}`}>Icon</span>;
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
jest.mock('@capillarytech/cap-ui-library/CapRadioGroup', () =>
|
|
82
|
+
function MockCapRadioGroup({ options, value, onChange, ...props }) {
|
|
83
|
+
return (
|
|
84
|
+
<div {...props}>
|
|
85
|
+
{options?.map((option, index) => (
|
|
86
|
+
<label key={index}>
|
|
87
|
+
<input
|
|
88
|
+
type="radio"
|
|
89
|
+
checked={value === option.value}
|
|
90
|
+
onChange={() => onChange(option.value)}
|
|
91
|
+
/>
|
|
92
|
+
{option.label}
|
|
93
|
+
</label>
|
|
94
|
+
))}
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
jest.mock('@capillarytech/cap-ui-library/CapSelect', () => ({
|
|
101
|
+
CapCustomSelect: function MockCapCustomSelect({ options, value, onChange, placeholder, ...props }) {
|
|
102
|
+
return (
|
|
103
|
+
<select {...props} value={value} onChange={(e) => onChange(e.target.value)}>
|
|
104
|
+
<option value="">{placeholder}</option>
|
|
105
|
+
{options?.map((option, index) => (
|
|
106
|
+
<option key={index} value={option.value}>
|
|
107
|
+
{option.label}
|
|
108
|
+
</option>
|
|
109
|
+
))}
|
|
110
|
+
</select>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}));
|
|
114
|
+
|
|
115
|
+
jest.mock('@capillarytech/cap-ui-library/CapRow', () =>
|
|
116
|
+
function MockCapRow({ children, ...props }) {
|
|
117
|
+
return <div {...props} data-testid="cap-row">{children}</div>;
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
jest.mock('@capillarytech/cap-ui-library/CapColumn', () =>
|
|
122
|
+
function MockCapColumn({ children, span, ...props }) {
|
|
123
|
+
return <div {...props} data-testid="cap-column">{children}</div>;
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
jest.mock('@capillarytech/cap-ui-library/CapHeading', () =>
|
|
128
|
+
function MockCapHeading({ children, type, ...props }) {
|
|
129
|
+
const Tag = type === 'h4' ? 'h4' : 'div';
|
|
130
|
+
return <Tag {...props} data-testid="cap-heading">{children}</Tag>;
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
jest.mock('@capillarytech/cap-ui-library/CapDivider', () =>
|
|
135
|
+
function MockCapDivider({ type, ...props }) {
|
|
136
|
+
return <hr {...props} data-testid="cap-divider" />;
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
jest.mock('@capillarytech/cap-ui-library/CapCheckbox', () =>
|
|
141
|
+
function MockCapCheckbox({ children, checked, onChange, ...props }) {
|
|
142
|
+
return (
|
|
143
|
+
<label>
|
|
144
|
+
<input
|
|
145
|
+
type="checkbox"
|
|
146
|
+
checked={checked}
|
|
147
|
+
onChange={(e) => onChange(e)}
|
|
148
|
+
{...props}
|
|
149
|
+
/>
|
|
150
|
+
{children}
|
|
151
|
+
</label>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
jest.mock('@capillarytech/cap-ui-library/CapLabel', () =>
|
|
157
|
+
function MockCapLabel({ children, ...props }) {
|
|
158
|
+
return <label {...props} data-testid="cap-label">{children}</label>;
|
|
159
|
+
}
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
jest.mock('@capillarytech/cap-ui-library/CapInput', () =>
|
|
163
|
+
function MockCapInput({ onChange, value, placeholder, error, ...props }) {
|
|
164
|
+
return (
|
|
165
|
+
<input
|
|
166
|
+
type="text"
|
|
167
|
+
value={value || ''}
|
|
168
|
+
onChange={onChange}
|
|
169
|
+
placeholder={placeholder}
|
|
170
|
+
{...props}
|
|
171
|
+
data-testid="cap-input"
|
|
172
|
+
/>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
jest.mock('@capillarytech/cap-ui-library/CapError', () =>
|
|
178
|
+
function MockCapError({ children, ...props }) {
|
|
179
|
+
return <div {...props} data-testid="cap-error">{children}</div>;
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
jest.mock('../../../../v2Containers/TagList', () =>
|
|
184
|
+
function MockTagList({ label, onTagSelect, onContextChange, ...props }) {
|
|
185
|
+
return (
|
|
186
|
+
<div data-testid="tag-list" {...props}>
|
|
187
|
+
<button
|
|
188
|
+
type="button"
|
|
189
|
+
onClick={() => onTagSelect && onTagSelect('test-tag')}
|
|
190
|
+
data-testid="tag-select-button"
|
|
191
|
+
>
|
|
192
|
+
{label || 'Add Label'}
|
|
193
|
+
</button>
|
|
194
|
+
</div>
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
);
|
|
198
|
+
|
|
56
199
|
jest.mock('../../../../v2Components/CapVideoUpload', () =>
|
|
57
200
|
function MockCapVideoUpload(props) {
|
|
58
201
|
return (
|
|
@@ -352,7 +495,7 @@ const mockStore = configureStore({}, initialReducer, history);
|
|
|
352
495
|
|
|
353
496
|
// Helper function for formatMessage
|
|
354
497
|
const mockFormatMessage = (message, values = {}) => {
|
|
355
|
-
if (typeof message === 'object' && message.defaultMessage) {
|
|
498
|
+
if (message && typeof message === 'object' && message.defaultMessage) {
|
|
356
499
|
let result = message.defaultMessage;
|
|
357
500
|
if (values && typeof values === 'object') {
|
|
358
501
|
Object.keys(values).forEach(key => {
|
|
@@ -361,7 +504,7 @@ const mockFormatMessage = (message, values = {}) => {
|
|
|
361
504
|
}
|
|
362
505
|
return result;
|
|
363
506
|
}
|
|
364
|
-
return message;
|
|
507
|
+
return message || '';
|
|
365
508
|
};
|
|
366
509
|
|
|
367
510
|
const defaultProps = {
|
|
@@ -381,7 +524,7 @@ const defaultProps = {
|
|
|
381
524
|
setUpdateMpushVideoSrc: jest.fn(),
|
|
382
525
|
videoDataForVideo: {},
|
|
383
526
|
videoDataForGif: {},
|
|
384
|
-
formatMessage: jest.fn(
|
|
527
|
+
formatMessage: jest.fn(mockFormatMessage),
|
|
385
528
|
linkProps: {
|
|
386
529
|
deepLink: [],
|
|
387
530
|
},
|
|
@@ -395,6 +538,11 @@ const defaultProps = {
|
|
|
395
538
|
setCarouselActiveTabIndex: jest.fn(),
|
|
396
539
|
carouselLinkErrors: {},
|
|
397
540
|
updateCarouselLinkError: jest.fn(),
|
|
541
|
+
location: {},
|
|
542
|
+
tags: [],
|
|
543
|
+
injectedTags: {},
|
|
544
|
+
selectedOfferDetails: [],
|
|
545
|
+
handleOnTagsContextChange: jest.fn(),
|
|
398
546
|
};
|
|
399
547
|
|
|
400
548
|
const renderComponent = (props = {}) => {
|
|
@@ -515,7 +663,7 @@ describe('MediaUploaders', () => {
|
|
|
515
663
|
onCarouselDataChange: mockOnCarouselDataChange,
|
|
516
664
|
carouselActiveTabIndex: 0,
|
|
517
665
|
activeTab: 'ANDROID',
|
|
518
|
-
formatMessage:
|
|
666
|
+
formatMessage: mockFormatMessage,
|
|
519
667
|
});
|
|
520
668
|
|
|
521
669
|
// Check if the carousel component is rendered
|
|
@@ -601,7 +749,7 @@ describe('MediaUploaders', () => {
|
|
|
601
749
|
onCarouselDataChange: mockOnCarouselDataChange,
|
|
602
750
|
carouselActiveTabIndex: 0,
|
|
603
751
|
activeTab: 'ANDROID',
|
|
604
|
-
formatMessage:
|
|
752
|
+
formatMessage: mockFormatMessage,
|
|
605
753
|
});
|
|
606
754
|
|
|
607
755
|
// First check if the checkbox is checked
|
|
@@ -4026,5 +4174,52 @@ describe('MediaUploaders', () => {
|
|
|
4026
4174
|
// Verify the component renders without errors
|
|
4027
4175
|
expect(document.querySelector('.mobile-push-carousel-tab')).toBeTruthy();
|
|
4028
4176
|
});
|
|
4177
|
+
|
|
4178
|
+
it('should handle tag selection for external link in carousel buttons', () => {
|
|
4179
|
+
const mockHandleCarouselExternalLinkChange = jest.fn();
|
|
4180
|
+
|
|
4181
|
+
const { container } = renderComponent({
|
|
4182
|
+
mediaType: 'CAROUSEL',
|
|
4183
|
+
carouselData: [
|
|
4184
|
+
{
|
|
4185
|
+
mediaType: 'image',
|
|
4186
|
+
imageUrl: 'test.jpg',
|
|
4187
|
+
buttons: [{
|
|
4188
|
+
actionOnClick: true,
|
|
4189
|
+
linkType: 'EXTERNAL_LINK',
|
|
4190
|
+
deepLinkValue: '',
|
|
4191
|
+
deepLinkKeys: [],
|
|
4192
|
+
externalLinkValue: 'https://example.com/',
|
|
4193
|
+
}],
|
|
4194
|
+
}
|
|
4195
|
+
],
|
|
4196
|
+
carouselActiveTabIndex: 0,
|
|
4197
|
+
activeTab: 'ANDROID',
|
|
4198
|
+
formatMessage: mockFormatMessage,
|
|
4199
|
+
onCarouselDataChange: mockHandleCarouselExternalLinkChange,
|
|
4200
|
+
});
|
|
4201
|
+
|
|
4202
|
+
// Find the TagList component and click the tag select button to trigger the real useCallback
|
|
4203
|
+
const tagSelectButton = container.querySelector('[data-testid="tag-select-button"]');
|
|
4204
|
+
expect(tagSelectButton).toBeTruthy();
|
|
4205
|
+
|
|
4206
|
+
// Click the button to trigger the real onButtonTagSelect useCallback
|
|
4207
|
+
fireEvent.click(tagSelectButton);
|
|
4208
|
+
|
|
4209
|
+
// Verify the handler was called with the correct parameters
|
|
4210
|
+
// The mock TagList calls onTagSelect with 'test-tag', so we expect that
|
|
4211
|
+
expect(mockHandleCarouselExternalLinkChange).toHaveBeenCalledWith(
|
|
4212
|
+
'ANDROID', // platform
|
|
4213
|
+
expect.arrayContaining([
|
|
4214
|
+
expect.objectContaining({
|
|
4215
|
+
buttons: expect.arrayContaining([
|
|
4216
|
+
expect.objectContaining({
|
|
4217
|
+
externalLinkValue: 'https://example.com/{{test-tag}}'
|
|
4218
|
+
})
|
|
4219
|
+
])
|
|
4220
|
+
})
|
|
4221
|
+
])
|
|
4222
|
+
);
|
|
4223
|
+
});
|
|
4029
4224
|
});
|
|
4030
4225
|
});
|
|
@@ -42,7 +42,25 @@ jest.mock("@capillarytech/cap-ui-library/CapLabel", () => ({ children }) => <lab
|
|
|
42
42
|
jest.mock("@capillarytech/cap-ui-library/CapInfoNote", () => ({ message }) => <div data-testid="info-note">{message}</div>);
|
|
43
43
|
|
|
44
44
|
// Mock child components
|
|
45
|
-
jest.mock("../../../TagList", () => () =>
|
|
45
|
+
jest.mock("../../../TagList", () => ({ onTagSelect, onContextChange, label, ...props }) => (
|
|
46
|
+
<div data-testid="tag-list">
|
|
47
|
+
<button
|
|
48
|
+
type="button"
|
|
49
|
+
onClick={() => onTagSelect && onTagSelect('test_tag')}
|
|
50
|
+
data-testid="tag-select-button"
|
|
51
|
+
>
|
|
52
|
+
Select Tag
|
|
53
|
+
</button>
|
|
54
|
+
<button
|
|
55
|
+
type="button"
|
|
56
|
+
onClick={() => onContextChange && onContextChange('test_context')}
|
|
57
|
+
data-testid="tag-context-button"
|
|
58
|
+
>
|
|
59
|
+
Change Context
|
|
60
|
+
</button>
|
|
61
|
+
<span data-testid="tag-label">{label}</span>
|
|
62
|
+
</div>
|
|
63
|
+
));
|
|
46
64
|
jest.mock("../MediaUploaders", () => () => <div data-testid="media-uploaders">MediaUploaders</div>);
|
|
47
65
|
jest.mock("../CtaButtons", () => () => <div data-testid="cta-buttons">CtaButtons</div>);
|
|
48
66
|
|
|
@@ -840,4 +858,44 @@ describe("PlatformContentFields", () => {
|
|
|
840
858
|
expect(getByText('query')).toBeInTheDocument();
|
|
841
859
|
});
|
|
842
860
|
});
|
|
861
|
+
|
|
862
|
+
it('should handle tag selection for external link in buttons', () => {
|
|
863
|
+
const mockHandleExternalLinkChange = jest.fn();
|
|
864
|
+
|
|
865
|
+
// Create a test that actually renders the component and triggers the useCallback
|
|
866
|
+
const { container } = renderComponent({
|
|
867
|
+
content: {
|
|
868
|
+
...defaultProps.content,
|
|
869
|
+
actionOnClick: true,
|
|
870
|
+
linkType: EXTERNAL_LINK,
|
|
871
|
+
},
|
|
872
|
+
linkProps: {
|
|
873
|
+
...defaultProps.linkProps,
|
|
874
|
+
externalLinkValue: 'https://example.com/',
|
|
875
|
+
},
|
|
876
|
+
handlers: {
|
|
877
|
+
...defaultProps.handlers,
|
|
878
|
+
handleExternalLinkChange: mockHandleExternalLinkChange,
|
|
879
|
+
},
|
|
880
|
+
});
|
|
881
|
+
|
|
882
|
+
// Find the TagList component that's specifically for external link (there should be 3 TagLists total)
|
|
883
|
+
// One for title, one for message, and one for external link
|
|
884
|
+
const tagLists = container.querySelectorAll('[data-testid="tag-list"]');
|
|
885
|
+
expect(tagLists).toHaveLength(3); // title, message, and external link TagLists
|
|
886
|
+
|
|
887
|
+
// The external link TagList should be the third one (index 2)
|
|
888
|
+
const externalLinkTagList = tagLists[2];
|
|
889
|
+
const tagSelectButton = externalLinkTagList.querySelector('[data-testid="tag-select-button"]');
|
|
890
|
+
expect(tagSelectButton).toBeTruthy();
|
|
891
|
+
|
|
892
|
+
// Click the button to trigger the real onButtonTagSelect useCallback
|
|
893
|
+
fireEvent.click(tagSelectButton);
|
|
894
|
+
|
|
895
|
+
// Verify the handler was called with the correct parameters
|
|
896
|
+
// The mock TagList calls onTagSelect with 'test_tag', so we expect that
|
|
897
|
+
expect(mockHandleExternalLinkChange).toHaveBeenCalledWith(
|
|
898
|
+
'https://example.com/{{test_tag}}' // newUrl with appended tag
|
|
899
|
+
);
|
|
900
|
+
});
|
|
843
901
|
});
|
|
@@ -2057,6 +2057,11 @@ const MobilePushNew = ({
|
|
|
2057
2057
|
updateHandler,
|
|
2058
2058
|
deleteHandler,
|
|
2059
2059
|
deepLink,
|
|
2060
|
+
location,
|
|
2061
|
+
tags,
|
|
2062
|
+
injectedTags,
|
|
2063
|
+
selectedOfferDetails,
|
|
2064
|
+
handleOnTagsContextChange,
|
|
2060
2065
|
};
|
|
2061
2066
|
|
|
2062
2067
|
const linkProps = {
|
|
@@ -2085,6 +2090,10 @@ const MobilePushNew = ({
|
|
|
2085
2090
|
linkProps={linkProps}
|
|
2086
2091
|
sameContent={sameContent}
|
|
2087
2092
|
formatMessage={formatMessage}
|
|
2093
|
+
location={location}
|
|
2094
|
+
tags={tags}
|
|
2095
|
+
injectedTags={injectedTags}
|
|
2096
|
+
selectedOfferDetails={selectedOfferDetails}
|
|
2088
2097
|
/>
|
|
2089
2098
|
);
|
|
2090
2099
|
}, [androidContent, iosContent, androidTitleError, iosTitleError, androidMessageError, iosMessageError, androidExternalLinkError, iosExternalLinkError, androidDeepLinkError, iosDeepLinkError, androidDeepLinkKeysError, iosDeepLinkKeysError, formatMessage, activeTab, imageSrc, isFullMode, imageData, androidAssetList, iosAssetList, videoState, videoData, location, tags, injectedTags, selectedOfferDetails, primaryButtonAndroid, secondaryButtonAndroid, primaryButtonIos, secondaryButtonIos, ctaData, deepLink, mobilePushActions, carouselActiveTabIndex, carouselLinkErrors, handleTitleChange, handleMessageChange, handleMediaTypeChange, handleActionOnClickChange, handleLinkTypeChange, handleDeepLinkChange, handleDeepLinkKeysChange, handleExternalLinkChange, onTagSelect, handleOnTagsContextChange, setUpdateMpushImageSrc, updateOnMpushImageReUpload, setUpdateMpushVideoSrc, updateOnMpushVideoReUpload, clearImageDataByMediaType, handleCarouselDataChange, updateCarouselLinkError, sameContent, updateHandler, deleteHandler]
|