@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.
Files changed (89) hide show
  1. package/constants/unified.js +4 -0
  2. package/package.json +1 -1
  3. package/utils/commonUtils.js +19 -1
  4. package/utils/templateVarUtils.js +35 -6
  5. package/utils/tests/tagValidations.test.js +20 -0
  6. package/utils/tests/templateVarUtils.test.js +44 -0
  7. package/v2Components/CapActionButton/constants.js +7 -0
  8. package/v2Components/CapActionButton/index.js +167 -109
  9. package/v2Components/CapActionButton/index.scss +157 -6
  10. package/v2Components/CapActionButton/messages.js +19 -3
  11. package/v2Components/CapActionButton/tests/index.test.js +41 -17
  12. package/v2Components/CapTagList/index.js +28 -23
  13. package/v2Components/CapTagList/style.scss +29 -0
  14. package/v2Components/CapTagListWithInput/__tests__/CapTagListWithInput.test.js +63 -0
  15. package/v2Components/CapTagListWithInput/index.js +4 -0
  16. package/v2Components/CapWhatsappCTA/index.js +2 -0
  17. package/v2Components/CommonTestAndPreview/ExistingCustomerModal.js +1 -0
  18. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +160 -15
  19. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js.rej +18 -0
  20. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +323 -77
  21. package/v2Components/CommonTestAndPreview/index.js +49 -57
  22. package/v2Components/CommonTestAndPreview/messages.js +8 -0
  23. package/v2Components/CommonTestAndPreview/reducer.js +3 -1
  24. package/v2Components/CommonTestAndPreview/sagas.js +2 -1
  25. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
  26. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
  27. package/v2Components/FormBuilder/index.js +1 -0
  28. package/v2Components/HtmlEditor/HTMLEditor.js +6 -1
  29. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -0
  30. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +927 -2
  31. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +3 -0
  32. package/v2Components/SmsFallback/smsFallbackUtils.js +14 -3
  33. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +16 -0
  34. package/v2Components/TemplatePreview/_templatePreview.scss +33 -23
  35. package/v2Components/TemplatePreview/constants.js +2 -0
  36. package/v2Components/TemplatePreview/index.js +143 -28
  37. package/v2Components/TemplatePreview/tests/index.test.js +142 -0
  38. package/v2Components/TestAndPreviewSlidebox/index.js +5 -0
  39. package/v2Components/mockdata.js +1 -0
  40. package/v2Containers/BeeEditor/index.js +19 -1
  41. package/v2Containers/CreativesContainer/SlideBoxContent.js +28 -1
  42. package/v2Containers/CreativesContainer/index.js +9 -3
  43. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +5 -0
  44. package/v2Containers/Email/index.js +78 -39
  45. package/v2Containers/Email/reducer.js +2 -2
  46. package/v2Containers/Email/sagas.js +3 -1
  47. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +2 -2
  48. package/v2Containers/Email/tests/sagas.test.js +230 -0
  49. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +6 -1
  50. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +3 -0
  51. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +20 -2
  52. package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +16 -1
  53. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +3 -1
  54. package/v2Containers/EmailWrapper/index.js +4 -0
  55. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +1 -0
  56. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +9 -0
  57. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +1 -0
  58. package/v2Containers/MobilePush/Create/index.js +2 -0
  59. package/v2Containers/MobilePush/Edit/index.js +2 -0
  60. package/v2Containers/MobilepushWrapper/index.js +3 -1
  61. package/v2Containers/Rcs/constants.js +85 -7
  62. package/v2Containers/Rcs/index.js +1592 -156
  63. package/v2Containers/Rcs/index.js.rej +1336 -0
  64. package/v2Containers/Rcs/index.scss +191 -0
  65. package/v2Containers/Rcs/index.scss.rej +74 -0
  66. package/v2Containers/Rcs/messages.js +28 -2
  67. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +20 -0
  68. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +69178 -117691
  69. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap.rej +128 -0
  70. package/v2Containers/Rcs/tests/index.test.js +132 -94
  71. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +67 -0
  72. package/v2Containers/Rcs/tests/utils.test.js +276 -38
  73. package/v2Containers/Rcs/utils.js +130 -7
  74. package/v2Containers/Sms/Edit/index.js +2 -0
  75. package/v2Containers/SmsTrai/Edit/index.js +27 -0
  76. package/v2Containers/SmsTrai/Edit/messages.js +5 -0
  77. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +6 -6
  78. package/v2Containers/SmsWrapper/index.js +2 -0
  79. package/v2Containers/TagList/index.js +73 -20
  80. package/v2Containers/TagList/messages.js +4 -0
  81. package/v2Containers/TagList/tests/TagList.test.js +124 -20
  82. package/v2Containers/TagList/tests/mockdata.js +17 -0
  83. package/v2Containers/Templates/_templates.scss +99 -0
  84. package/v2Containers/Templates/index.js +29 -14
  85. package/v2Containers/Viber/index.js +3 -0
  86. package/v2Containers/WebPush/Create/hooks/useTagManagement.js +0 -2
  87. package/v2Containers/WebPush/Create/index.js +10 -2
  88. package/v2Containers/Whatsapp/index.js +5 -0
  89. 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
- const renderRcsSuggestionsPreview = () => {
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 { suggestions = [] } = content || {};
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="rcs-cta-preview">
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="rcs-cta-preview">
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="rcs-cta-preview">
86
- <CapIcon type="call" size="xs" />
87
- {text}
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
- const contentToRender = hasMedia ? renderMediaPreviewContent() : renderTextPreviewContent();
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 className="sms-device-container">
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 className={`sms-content-overlay sms-content-overlay-${device} sms-preview sms-${device}`}>
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
- <CapRow className="sms-message-container">
259
- <CapRow className="sms-message-bubble">
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"