@capillarytech/creatives-library 8.0.329 → 8.0.330-alpha.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/constants/unified.js +4 -0
- package/package.json +1 -1
- package/utils/commonUtils.js +19 -1
- package/utils/templateVarUtils.js +35 -6
- package/utils/tests/tagValidations.test.js +20 -0
- package/utils/tests/templateVarUtils.test.js +44 -0
- package/v2Components/CapActionButton/constants.js +7 -0
- package/v2Components/CapActionButton/index.js +167 -109
- package/v2Components/CapActionButton/index.scss +157 -6
- package/v2Components/CapActionButton/messages.js +19 -3
- package/v2Components/CapActionButton/tests/index.test.js +41 -17
- package/v2Components/CapTagList/index.js +28 -23
- package/v2Components/CapTagList/style.scss +29 -0
- package/v2Components/CapTagListWithInput/__tests__/CapTagListWithInput.test.js +63 -0
- package/v2Components/CapTagListWithInput/index.js +4 -0
- package/v2Components/CapWhatsappCTA/index.js +2 -0
- package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +1 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +160 -15
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js.rej +18 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +323 -77
- package/v2Components/CommonTestAndPreview/index.js +49 -57
- package/v2Components/CommonTestAndPreview/messages.js +8 -0
- package/v2Components/CommonTestAndPreview/reducer.js +3 -1
- package/v2Components/CommonTestAndPreview/sagas.js +2 -1
- package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
- package/v2Components/FormBuilder/index.js +1 -0
- package/v2Components/HtmlEditor/HTMLEditor.js +6 -1
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -0
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +927 -2
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +3 -0
- package/v2Components/SmsFallback/smsFallbackUtils.js +14 -3
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +16 -0
- package/v2Components/TemplatePreview/_templatePreview.scss +33 -23
- package/v2Components/TemplatePreview/constants.js +2 -0
- package/v2Components/TemplatePreview/index.js +143 -28
- package/v2Components/TemplatePreview/tests/index.test.js +142 -0
- package/v2Components/TestAndPreviewSlidebox/index.js +5 -0
- package/v2Components/mockdata.js +1 -0
- package/v2Containers/BeeEditor/index.js +19 -1
- package/v2Containers/CreativesContainer/SlideBoxContent.js +28 -1
- package/v2Containers/CreativesContainer/index.js +9 -3
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +5 -0
- package/v2Containers/Email/index.js +78 -39
- package/v2Containers/Email/reducer.js +2 -2
- package/v2Containers/Email/sagas.js +3 -1
- package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +2 -2
- package/v2Containers/Email/tests/sagas.test.js +230 -0
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +6 -1
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +3 -0
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +20 -2
- package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +16 -1
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +3 -1
- package/v2Containers/EmailWrapper/index.js +4 -0
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +1 -0
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +9 -0
- package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +1 -0
- package/v2Containers/MobilePush/Create/index.js +2 -0
- package/v2Containers/MobilePush/Edit/index.js +2 -0
- package/v2Containers/MobilepushWrapper/index.js +3 -1
- package/v2Containers/Rcs/constants.js +85 -7
- package/v2Containers/Rcs/index.js +1592 -156
- package/v2Containers/Rcs/index.js.rej +1336 -0
- package/v2Containers/Rcs/index.scss +191 -0
- package/v2Containers/Rcs/index.scss.rej +74 -0
- package/v2Containers/Rcs/messages.js +28 -2
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +20 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +69178 -117691
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap.rej +128 -0
- package/v2Containers/Rcs/tests/index.test.js +132 -94
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +67 -0
- package/v2Containers/Rcs/tests/utils.test.js +276 -38
- package/v2Containers/Rcs/utils.js +130 -7
- package/v2Containers/Sms/Edit/index.js +2 -0
- package/v2Containers/SmsTrai/Edit/index.js +27 -0
- package/v2Containers/SmsTrai/Edit/messages.js +5 -0
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +6 -6
- package/v2Containers/SmsWrapper/index.js +2 -0
- package/v2Containers/TagList/index.js +73 -20
- package/v2Containers/TagList/messages.js +4 -0
- package/v2Containers/TagList/tests/TagList.test.js +124 -20
- package/v2Containers/TagList/tests/mockdata.js +17 -0
- package/v2Containers/Templates/_templates.scss +99 -0
- package/v2Containers/Templates/index.js +29 -14
- package/v2Containers/Viber/index.js +3 -0
- package/v2Containers/WebPush/Create/hooks/useTagManagement.js +0 -2
- package/v2Containers/WebPush/Create/index.js +10 -2
- package/v2Containers/Whatsapp/index.js +5 -0
- package/v2Containers/Zalo/index.js +2 -0
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
TIME_FORMAT_MINUTE_PADDING_THRESHOLD,
|
|
26
26
|
TIME_FORMAT_AM,
|
|
27
27
|
TIME_FORMAT_PM,
|
|
28
|
+
MEDIA_TYPE_VIDEO,
|
|
28
29
|
} from '../constants';
|
|
29
30
|
import messages from '../messages';
|
|
30
31
|
import { RCS_BUTTON_TYPES } from '../../../v2Containers/Rcs/constants';
|
|
@@ -50,41 +51,57 @@ const RcsPreviewContent = ({
|
|
|
50
51
|
const deviceName = device === ANDROID ? ANDROID_DEVICE_NAME : IOS_DEVICE_NAME;
|
|
51
52
|
|
|
52
53
|
// Render RCS suggestions (Quick Reply, CTA, Phone Number)
|
|
53
|
-
|
|
54
|
+
// options.isCarousel: stacked bars below card body with gap (Figma), no dividers between CTAs
|
|
55
|
+
const renderRcsSuggestionsPreview = (suggestionsArg, options = {}) => {
|
|
56
|
+
const { isCarousel = false } = options;
|
|
54
57
|
const renderArray = [];
|
|
55
|
-
const
|
|
58
|
+
const suggestions = Array.isArray(suggestionsArg)
|
|
59
|
+
? suggestionsArg
|
|
60
|
+
: (content?.suggestions || []);
|
|
56
61
|
|
|
57
62
|
if (suggestions.length === 0) {
|
|
58
63
|
return null;
|
|
59
64
|
}
|
|
60
65
|
|
|
66
|
+
const ctaClass = isCarousel ? 'rcs-cta-preview rcs-cta-preview--carousel-bar' : 'rcs-cta-preview';
|
|
67
|
+
|
|
61
68
|
suggestions.forEach((suggestion) => {
|
|
62
69
|
const { type, text } = suggestion || {};
|
|
63
70
|
const suggestionKey = `${type}-${text || Math.random()}`;
|
|
64
71
|
|
|
65
|
-
if (renderArray.length > 0) {
|
|
72
|
+
if (renderArray.length > 0 && !isCarousel) {
|
|
66
73
|
renderArray.push(<CapDivider key={`divider-${suggestionKey}`} className="whatsapp-divider" />);
|
|
67
74
|
}
|
|
68
75
|
|
|
69
76
|
if (type === RCS_BUTTON_TYPES.QUICK_REPLY) {
|
|
70
77
|
renderArray.push(
|
|
71
|
-
<CapLabel key={suggestionKey} type="label21" className=
|
|
78
|
+
<CapLabel key={suggestionKey} type="label21" className={ctaClass}>
|
|
72
79
|
<CapIcon type="small-link" size="xs" />
|
|
73
80
|
{text}
|
|
74
81
|
</CapLabel>
|
|
75
82
|
);
|
|
76
83
|
} else if (type === RCS_BUTTON_TYPES.CTA) {
|
|
77
84
|
renderArray.push(
|
|
78
|
-
<CapLabel key={suggestionKey} type="label21" className=
|
|
85
|
+
<CapLabel key={suggestionKey} type="label21" className={ctaClass}>
|
|
79
86
|
<CapIcon type="launch" size="xs" />
|
|
80
87
|
{text}
|
|
81
88
|
</CapLabel>
|
|
82
89
|
);
|
|
83
90
|
} else if (type === RCS_BUTTON_TYPES.PHONE_NUMBER) {
|
|
91
|
+
// Carousel: label text then phone icon (matches device-style RCS preview)
|
|
84
92
|
renderArray.push(
|
|
85
|
-
<CapLabel key={suggestionKey} type="label21" className=
|
|
86
|
-
|
|
87
|
-
|
|
93
|
+
<CapLabel key={suggestionKey} type="label21" className={ctaClass}>
|
|
94
|
+
{isCarousel ? (
|
|
95
|
+
<>
|
|
96
|
+
{text}
|
|
97
|
+
<CapIcon type="call" size="xs" className="rcs-cta-preview-phone-icon-end" />
|
|
98
|
+
</>
|
|
99
|
+
) : (
|
|
100
|
+
<>
|
|
101
|
+
<CapIcon type="call" size="xs" />
|
|
102
|
+
{text}
|
|
103
|
+
</>
|
|
104
|
+
)}
|
|
88
105
|
</CapLabel>
|
|
89
106
|
);
|
|
90
107
|
}
|
|
@@ -93,6 +110,104 @@ const RcsPreviewContent = ({
|
|
|
93
110
|
return renderArray?.length > 0 ? renderArray : null;
|
|
94
111
|
};
|
|
95
112
|
|
|
113
|
+
// Carousel media box: aspect matches RCS editor card size (SHORT/MEDIUM × SMALL/MEDIUM).
|
|
114
|
+
const getCarouselMediaAspectStyle = (isVideo) => {
|
|
115
|
+
const d = content?.carouselPreviewDimensions;
|
|
116
|
+
if (!d) return undefined;
|
|
117
|
+
if (isVideo) {
|
|
118
|
+
const { videoThumbWidth, videoThumbHeight } = d;
|
|
119
|
+
if (!videoThumbWidth || !videoThumbHeight) return undefined;
|
|
120
|
+
return { aspectRatio: `${videoThumbWidth} / ${videoThumbHeight}` };
|
|
121
|
+
}
|
|
122
|
+
const { imageWidth, imageHeight } = d;
|
|
123
|
+
if (!imageWidth || !imageHeight) return undefined;
|
|
124
|
+
return { aspectRatio: `${imageWidth} / ${imageHeight}` };
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// Carousel preview (WhatsApp-style static horizontal scroll).
|
|
128
|
+
// Caller passes a non-empty array only (see hasCarousel); avoids duplicate guards that Sonar
|
|
129
|
+
// flagged as partially covered because inner checks were unreachable when hasCarousel was true.
|
|
130
|
+
const renderCarouselPreviewContent = (carouselCards) => (
|
|
131
|
+
<CapRow className="msg-container-carousel">
|
|
132
|
+
<CapRow className="scroll-container">
|
|
133
|
+
{carouselCards.map((card, idx) => {
|
|
134
|
+
const key = `rcs-carousel-${idx}-${card?.bodyText || card?.imageSrc || card?.videoPreviewImg || ''}`;
|
|
135
|
+
const isVideo =
|
|
136
|
+
(card?.mediaType || '').toLowerCase() === String(MEDIA_TYPE_VIDEO).toLowerCase();
|
|
137
|
+
const cardSuggestions = Array.isArray(card?.suggestions) ? card.suggestions : [];
|
|
138
|
+
const suggestionsNode = cardSuggestions.length > 0
|
|
139
|
+
? renderRcsSuggestionsPreview(cardSuggestions, { isCarousel: true })
|
|
140
|
+
: null;
|
|
141
|
+
const titleTrimmed = String(card?.title ?? '').trim();
|
|
142
|
+
const bodyTrimmed = String(card?.bodyText ?? '').trim();
|
|
143
|
+
const titleShown = titleTrimmed || formatMessage(messages.rcsCarouselPreviewTitlePlaceholder);
|
|
144
|
+
const bodyShown = bodyTrimmed || formatMessage(messages.rcsCarouselPreviewBodyPlaceholder);
|
|
145
|
+
return (
|
|
146
|
+
<CapRow
|
|
147
|
+
key={key}
|
|
148
|
+
className="message-pop align-left message-pop-carousel"
|
|
149
|
+
>
|
|
150
|
+
<CapRow className="whatsapp-content">
|
|
151
|
+
{!isVideo && (
|
|
152
|
+
<CapRow
|
|
153
|
+
className="whatsapp-image rcs-carousel-media-wrap"
|
|
154
|
+
style={getCarouselMediaAspectStyle(false)}
|
|
155
|
+
>
|
|
156
|
+
<CapImage
|
|
157
|
+
src={card?.imageSrc ? card.imageSrc : rcsImageEmptyPreview}
|
|
158
|
+
className="rcs-carousel-img"
|
|
159
|
+
alt={formatMessage(messages.previewGenerated)}
|
|
160
|
+
/>
|
|
161
|
+
</CapRow>
|
|
162
|
+
)}
|
|
163
|
+
{isVideo && (
|
|
164
|
+
<CapTooltip title={formatMessage(messages.videoPreviewTooltip)}>
|
|
165
|
+
<CapRow className="video-preview">
|
|
166
|
+
<CapRow
|
|
167
|
+
className="whatsapp-image rcs-carousel-media-wrap"
|
|
168
|
+
style={getCarouselMediaAspectStyle(true)}
|
|
169
|
+
>
|
|
170
|
+
<CapImage
|
|
171
|
+
src={card?.videoPreviewImg ? card.videoPreviewImg : rcsVideoEmptyPreview}
|
|
172
|
+
className="rcs-carousel-img"
|
|
173
|
+
alt={formatMessage(messages.previewGenerated)}
|
|
174
|
+
/>
|
|
175
|
+
</CapRow>
|
|
176
|
+
<CapRow className="icon-position">
|
|
177
|
+
<CapImage className="video-icon" src={videoPlay} alt="Play" />
|
|
178
|
+
</CapRow>
|
|
179
|
+
</CapRow>
|
|
180
|
+
</CapTooltip>
|
|
181
|
+
)}
|
|
182
|
+
|
|
183
|
+
<CapRow className="carousel-content">
|
|
184
|
+
<CapLabel
|
|
185
|
+
type={card.titleLabelType || 'label1'}
|
|
186
|
+
className={`carousel-title${titleTrimmed ? '' : ' rcs-carousel-field-placeholder'}`}
|
|
187
|
+
>
|
|
188
|
+
{titleShown}
|
|
189
|
+
</CapLabel>
|
|
190
|
+
<CapLabel
|
|
191
|
+
type={card.bodyLabelType || 'label2'}
|
|
192
|
+
className={`carousel-message${bodyTrimmed ? '' : ' rcs-carousel-field-placeholder'}`}
|
|
193
|
+
>
|
|
194
|
+
{bodyShown}
|
|
195
|
+
</CapLabel>
|
|
196
|
+
</CapRow>
|
|
197
|
+
|
|
198
|
+
{!!suggestionsNode && (
|
|
199
|
+
<CapRow className="rcs-carousel-cta-stack" gutter={0}>
|
|
200
|
+
{suggestionsNode}
|
|
201
|
+
</CapRow>
|
|
202
|
+
)}
|
|
203
|
+
</CapRow>
|
|
204
|
+
</CapRow>
|
|
205
|
+
);
|
|
206
|
+
})}
|
|
207
|
+
</CapRow>
|
|
208
|
+
</CapRow>
|
|
209
|
+
);
|
|
210
|
+
|
|
96
211
|
// Render text preview content
|
|
97
212
|
const renderTextPreviewContent = () => {
|
|
98
213
|
const {
|
|
@@ -127,6 +242,9 @@ const RcsPreviewContent = ({
|
|
|
127
242
|
{description}
|
|
128
243
|
</CapLabel>
|
|
129
244
|
)}
|
|
245
|
+
{Array.isArray(content?.suggestions) && content.suggestions.length > 0 && (
|
|
246
|
+
<CapDivider className="whatsapp-divider" />
|
|
247
|
+
)}
|
|
130
248
|
{renderRcsSuggestionsPreview()}
|
|
131
249
|
</>
|
|
132
250
|
);
|
|
@@ -226,11 +344,22 @@ const RcsPreviewContent = ({
|
|
|
226
344
|
const timestamp = `${displayHours}:${displayMinutes} ${ampm}`;
|
|
227
345
|
|
|
228
346
|
// Render normal RCS preview (same structure as SMS up to sms-message-container)
|
|
347
|
+
const carouselCardsNormalized = Array.isArray(content?.carouselData) ? content?.carouselData : [];
|
|
348
|
+
const hasCarousel = carouselCardsNormalized?.length > 0;
|
|
229
349
|
const hasMedia = !!(content?.rcsImageSrc || content?.rcsVideoSrc);
|
|
230
|
-
|
|
350
|
+
let contentToRender;
|
|
351
|
+
if (hasCarousel) {
|
|
352
|
+
contentToRender = renderCarouselPreviewContent(carouselCardsNormalized);
|
|
353
|
+
} else if (hasMedia) {
|
|
354
|
+
contentToRender = renderMediaPreviewContent();
|
|
355
|
+
} else {
|
|
356
|
+
contentToRender = renderTextPreviewContent();
|
|
357
|
+
}
|
|
231
358
|
|
|
232
359
|
return (
|
|
233
|
-
<CapRow
|
|
360
|
+
<CapRow
|
|
361
|
+
className={`sms-device-container rcs-device-container ${hasCarousel ? 'rcs-device-container-carousel' : ''}`}
|
|
362
|
+
>
|
|
234
363
|
{/* Device Background Image */}
|
|
235
364
|
<CapImage
|
|
236
365
|
className="sms-device-image"
|
|
@@ -239,7 +368,9 @@ const RcsPreviewContent = ({
|
|
|
239
368
|
/>
|
|
240
369
|
|
|
241
370
|
{/* Content Overlay */}
|
|
242
|
-
<CapRow
|
|
371
|
+
<CapRow
|
|
372
|
+
className={`sms-content-overlay sms-content-overlay-${device} sms-preview sms-${device} rcs-content-overlay`}
|
|
373
|
+
>
|
|
243
374
|
{/* Navigation Bar - Same as SMS */}
|
|
244
375
|
<CapRow className={`sms-navigation-bar ${!showHeader ? 'sms-navigation-bar-no-header' : ''}`}>
|
|
245
376
|
<CapIcon type="chevron-left" className="sms-back-arrow" />
|
|
@@ -255,8 +386,8 @@ const RcsPreviewContent = ({
|
|
|
255
386
|
</CapRow>
|
|
256
387
|
|
|
257
388
|
{/* Message Container - Same structure as SMS, only content inside is RCS-specific */}
|
|
258
|
-
|
|
259
|
-
<CapRow className=
|
|
389
|
+
<CapRow className={`sms-message-container ${hasCarousel ? 'rcs-message-container-carousel' : ''}`}>
|
|
390
|
+
<CapRow className={`sms-message-bubble ${hasCarousel ? 'rcs-message-bubble-carousel' : ''}`}>
|
|
260
391
|
<CapRow className="sms-message-text">
|
|
261
392
|
{/* RCS-specific content rendering */}
|
|
262
393
|
<CapRow className="message-pop sms">
|
|
@@ -270,8 +401,10 @@ const RcsPreviewContent = ({
|
|
|
270
401
|
</CapRow>
|
|
271
402
|
</CapRow>
|
|
272
403
|
</CapRow>
|
|
273
|
-
)
|
|
274
|
-
}
|
|
404
|
+
)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
|
|
275
408
|
|
|
276
409
|
RcsPreviewContent.propTypes = {
|
|
277
410
|
content: PropTypes.shape({
|
|
@@ -282,6 +415,18 @@ RcsPreviewContent.propTypes = {
|
|
|
282
415
|
rcsImageSrc: PropTypes.string,
|
|
283
416
|
rcsVideoSrc: PropTypes.string,
|
|
284
417
|
rcsThumbnailSrc: PropTypes.string,
|
|
418
|
+
carouselData: PropTypes.arrayOf(
|
|
419
|
+
PropTypes.shape({
|
|
420
|
+
titleLabelType: PropTypes.string,
|
|
421
|
+
bodyLabelType: PropTypes.string,
|
|
422
|
+
})
|
|
423
|
+
),
|
|
424
|
+
carouselPreviewDimensions: PropTypes.shape({
|
|
425
|
+
imageWidth: PropTypes.number,
|
|
426
|
+
imageHeight: PropTypes.number,
|
|
427
|
+
videoThumbWidth: PropTypes.number,
|
|
428
|
+
videoThumbHeight: PropTypes.number,
|
|
429
|
+
}),
|
|
285
430
|
suggestions: PropTypes.arrayOf(
|
|
286
431
|
PropTypes.shape({
|
|
287
432
|
type: PropTypes.string,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
diff a/app/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js b/app/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js (rejected hunks)
|
|
2
|
+
@@ -226,11 +361,16 @@ const RcsPreviewContent = ({
|
|
3
|
+
const timestamp = `${displayHours}:${displayMinutes} ${ampm}`;
|
|
4
|
+
|
|
5
|
+
// Render normal RCS preview (same structure as SMS up to sms-message-container)
|
|
6
|
+
+ const hasCarousel = Array.isArray(content?.carouselData) && content.carouselData.length > 0;
|
|
7
|
+
const hasMedia = !!(content?.rcsImageSrc || content?.rcsVideoSrc);
|
|
8
|
+
+ const contentToRender = hasCarousel
|
|
9
|
+
+ ? renderCarouselPreviewContent()
|
|
10
|
+
+ : (hasMedia ? renderMediaPreviewContent() : renderTextPreviewContent());
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
+ <CapRow
|
|
14
|
+
+ className={`sms-device-container rcs-device-container ${hasCarousel ? 'rcs-device-container-carousel' : ''}`}
|
|
15
|
+
+ >
|
|
16
|
+
{/* Device Background Image */}
|
|
17
|
+
<CapImage
|
|
18
|
+
className="sms-device-image"
|