@capillarytech/creatives-library 9.0.26 → 9.0.29-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 (149) hide show
  1. package/constants/unified.js +29 -0
  2. package/global-styles.js +1 -1
  3. package/package.json +1 -1
  4. package/services/tests/api.test.js +35 -20
  5. package/utils/commonUtils.js +22 -1
  6. package/utils/rcsPayloadUtils.js +102 -0
  7. package/utils/templateVarUtils.js +198 -0
  8. package/utils/tests/rcsPayloadUtils.test.js +295 -0
  9. package/utils/tests/templateVarUtils.test.js +204 -0
  10. package/v2Components/CapActionButton/constants.js +21 -1
  11. package/v2Components/CapActionButton/index.js +260 -144
  12. package/v2Components/CapActionButton/index.scss +245 -14
  13. package/v2Components/CapActionButton/messages.js +32 -3
  14. package/v2Components/CapActionButton/tests/index.test.js +74 -19
  15. package/v2Components/CapImageUpload/index.js +2 -2
  16. package/v2Components/CapTagList/index.js +9 -0
  17. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -48
  18. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
  19. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +214 -21
  20. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
  21. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +83 -9
  22. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
  23. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +58 -11
  24. package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
  25. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +186 -22
  26. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +381 -80
  27. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +155 -8
  28. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
  29. package/v2Components/CommonTestAndPreview/constants.js +35 -2
  30. package/v2Components/CommonTestAndPreview/index.js +770 -231
  31. package/v2Components/CommonTestAndPreview/messages.js +45 -3
  32. package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
  33. package/v2Components/CommonTestAndPreview/sagas.js +25 -6
  34. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +1 -0
  35. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +412 -257
  36. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +337 -63
  37. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
  38. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
  39. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
  40. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +108 -15
  41. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
  42. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
  43. package/v2Components/CommonTestAndPreview/tests/index.test.js +133 -4
  44. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
  45. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +31 -24
  46. package/v2Components/CommonTestAndPreview/tests/utils.test.js +151 -0
  47. package/v2Components/CommonTestAndPreview/utils.js +84 -0
  48. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +91 -0
  49. package/v2Components/SmsFallback/constants.js +94 -0
  50. package/v2Components/SmsFallback/index.js +958 -0
  51. package/v2Components/SmsFallback/index.scss +266 -0
  52. package/v2Components/SmsFallback/messages.js +78 -0
  53. package/v2Components/SmsFallback/smsFallbackUtils.js +120 -0
  54. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
  55. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
  56. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
  57. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +208 -0
  58. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +309 -0
  59. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +471 -0
  60. package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
  61. package/v2Components/TemplatePreview/_templatePreview.scss +45 -25
  62. package/v2Components/TemplatePreview/constants.js +2 -0
  63. package/v2Components/TemplatePreview/index.js +147 -32
  64. package/v2Components/TemplatePreview/tests/index.test.js +142 -0
  65. package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +6 -6
  66. package/v2Components/TestAndPreviewSlidebox/index.js +13 -1
  67. package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
  68. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
  69. package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
  70. package/v2Components/VarSegmentMessageEditor/index.js +125 -0
  71. package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
  72. package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +4 -1
  73. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +4 -0
  74. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +20 -20
  75. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +27 -0
  76. package/v2Containers/CreativesContainer/SlideBoxContent.js +37 -4
  77. package/v2Containers/CreativesContainer/SlideBoxFooter.js +14 -5
  78. package/v2Containers/CreativesContainer/SlideBoxHeader.js +36 -5
  79. package/v2Containers/CreativesContainer/constants.js +11 -0
  80. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +77 -0
  81. package/v2Containers/CreativesContainer/index.js +328 -106
  82. package/v2Containers/CreativesContainer/index.scss +102 -1
  83. package/v2Containers/CreativesContainer/tests/CreativesSlideBoxWrapper.test.js +58 -0
  84. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
  85. package/v2Containers/CreativesContainer/tests/SlideBoxContent.smsDltPreview.test.js +73 -0
  86. package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +37 -0
  87. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +103 -34
  88. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +193 -15
  89. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +88 -0
  90. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +549 -57
  91. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
  92. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
  93. package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
  94. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
  95. package/v2Containers/Email/index.js +3 -54
  96. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +4 -9
  97. package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
  98. package/v2Containers/Rcs/carouselUtils.js +224 -0
  99. package/v2Containers/Rcs/components/CarouselCard.js +317 -0
  100. package/v2Containers/Rcs/components/CarouselCardButtons.js +113 -0
  101. package/v2Containers/Rcs/components/CarouselCardMedia.js +136 -0
  102. package/v2Containers/Rcs/components/CarouselCharacterCount.js +31 -0
  103. package/v2Containers/Rcs/components/CarouselDimensionSelection.js +80 -0
  104. package/v2Containers/Rcs/constants.js +132 -16
  105. package/v2Containers/Rcs/index.js +1808 -949
  106. package/v2Containers/Rcs/index.scss +443 -8
  107. package/v2Containers/Rcs/messages.js +45 -22
  108. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +484 -0
  109. package/v2Containers/Rcs/tests/CarouselCard.test.js +464 -0
  110. package/v2Containers/Rcs/tests/CarouselCardButtons.test.js +211 -0
  111. package/v2Containers/Rcs/tests/CarouselCardMedia.test.js +160 -0
  112. package/v2Containers/Rcs/tests/CarouselCharacterCount.test.js +50 -0
  113. package/v2Containers/Rcs/tests/CarouselDimensionSelection.test.js +119 -0
  114. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +74176 -39409
  115. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +10 -74
  116. package/v2Containers/Rcs/tests/carouselUtils.test.js +916 -0
  117. package/v2Containers/Rcs/tests/index.test.js +219 -40
  118. package/v2Containers/Rcs/tests/mockData.js +38 -0
  119. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +873 -0
  120. package/v2Containers/Rcs/tests/utils.test.js +682 -30
  121. package/v2Containers/Rcs/utils.js +514 -12
  122. package/v2Containers/Sms/Create/index.js +115 -48
  123. package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
  124. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  125. package/v2Containers/SmsTrai/Create/index.js +9 -4
  126. package/v2Containers/SmsTrai/Edit/constants.js +2 -0
  127. package/v2Containers/SmsTrai/Edit/index.js +678 -169
  128. package/v2Containers/SmsTrai/Edit/index.scss +126 -0
  129. package/v2Containers/SmsTrai/Edit/messages.js +14 -4
  130. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +5615 -3014
  131. package/v2Containers/SmsWrapper/index.js +37 -8
  132. package/v2Containers/TagList/index.js +5 -0
  133. package/v2Containers/Templates/TemplatesActionBar.js +101 -0
  134. package/v2Containers/Templates/_templates.scss +196 -12
  135. package/v2Containers/Templates/actions.js +11 -0
  136. package/v2Containers/Templates/constants.js +2 -0
  137. package/v2Containers/Templates/index.js +131 -59
  138. package/v2Containers/Templates/sagas.js +57 -13
  139. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +129 -0
  140. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1060 -1015
  141. package/v2Containers/Templates/tests/sagas.test.js +199 -16
  142. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
  143. package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
  144. package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
  145. package/v2Containers/TemplatesV2/index.js +88 -25
  146. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
  147. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
  148. package/v2Containers/Whatsapp/index.js +3 -20
  149. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
@@ -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,39 +51,56 @@ 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 rawSuggestions = Array.isArray(suggestionsArg)
59
+ ? suggestionsArg
60
+ : (content?.suggestions || []);
61
+ // Skip unsaved/empty draft suggestions (e.g. the pre-seeded default phone-number slot
62
+ // on carousel card 1, or a suggestion mid-edit via the pencil icon — both explicitly set
63
+ // `isSaved: false`) — but suggestions hydrated from an already-persisted template never
64
+ // carry `isSaved` at all (it's a client-only "saved this session" flag), so `undefined`
65
+ // must still render; only an explicit `false` means "hide this".
66
+ const suggestions = rawSuggestions.filter(
67
+ (suggestion) => suggestion?.isSaved !== false && String(suggestion?.text || '').trim(),
68
+ );
56
69
 
57
70
  if (suggestions.length === 0) {
58
71
  return null;
59
72
  }
60
73
 
74
+ const ctaClass = isCarousel ? 'rcs-cta-preview rcs-cta-preview--carousel-bar' : 'rcs-cta-preview';
75
+
61
76
  suggestions.forEach((suggestion) => {
62
77
  const { type, text } = suggestion || {};
63
78
  const suggestionKey = `${type}-${text || Math.random()}`;
64
79
 
65
- if (renderArray.length > 0) {
80
+ if (renderArray.length > 0 && !isCarousel) {
66
81
  renderArray.push(<CapDivider key={`divider-${suggestionKey}`} className="whatsapp-divider" />);
67
82
  }
68
83
 
69
84
  if (type === RCS_BUTTON_TYPES.QUICK_REPLY) {
70
85
  renderArray.push(
71
- <CapLabel key={suggestionKey} type="label21" className="rcs-cta-preview">
86
+ <CapLabel key={suggestionKey} type="label21" className={ctaClass}>
72
87
  <CapIcon type="small-link" size="xs" />
73
88
  {text}
74
89
  </CapLabel>
75
90
  );
76
91
  } else if (type === RCS_BUTTON_TYPES.CTA) {
77
92
  renderArray.push(
78
- <CapLabel key={suggestionKey} type="label21" className="rcs-cta-preview">
79
- <CapIcon type="launch" size="xs" />
93
+ <CapLabel key={suggestionKey} type="label21" className={ctaClass}>
94
+ {/* "launch" icon's SVG has hardcoded <defs>/<mask> ids, which collide
95
+ and render as a solid square when 2+ CTA buttons appear together —
96
+ "open-in-new" is a single flat path with no ids, so it can't collide. */}
97
+ <CapIcon type="open-in-new" size="xs" />
80
98
  {text}
81
99
  </CapLabel>
82
100
  );
83
101
  } else if (type === RCS_BUTTON_TYPES.PHONE_NUMBER) {
84
102
  renderArray.push(
85
- <CapLabel key={suggestionKey} type="label21" className="rcs-cta-preview">
103
+ <CapLabel key={suggestionKey} type="label21" className={ctaClass}>
86
104
  <CapIcon type="call" size="xs" />
87
105
  {text}
88
106
  </CapLabel>
@@ -93,6 +111,109 @@ const RcsPreviewContent = ({
93
111
  return renderArray?.length > 0 ? renderArray : null;
94
112
  };
95
113
 
114
+ // Carousel media box: aspect matches RCS editor card size (SHORT/MEDIUM × SMALL/MEDIUM).
115
+ const getCarouselMediaAspectStyle = (isVideo) => {
116
+ const carouselPreviewDimensions = content?.carouselPreviewDimensions;
117
+ if (!carouselPreviewDimensions) return undefined;
118
+ if (isVideo) {
119
+ const { videoThumbWidth, videoThumbHeight } = carouselPreviewDimensions;
120
+ if (!videoThumbWidth || !videoThumbHeight) return undefined;
121
+ return { aspectRatio: `${videoThumbWidth} / ${videoThumbHeight}` };
122
+ }
123
+ const { imageWidth, imageHeight } = carouselPreviewDimensions;
124
+ if (!imageWidth || !imageHeight) return undefined;
125
+ return { aspectRatio: `${imageWidth} / ${imageHeight}` };
126
+ };
127
+
128
+ // Carousel preview (WhatsApp-style static horizontal scroll).
129
+ // Caller passes a non-empty array only (see hasCarousel); avoids duplicate guards that Sonar
130
+ // flagged as partially covered because inner checks were unreachable when hasCarousel was true.
131
+ const renderCarouselPreviewContent = (carouselCards) => (
132
+ <CapRow className="msg-container-carousel">
133
+ <CapRow className="scroll-container" type="flex" noWrap>
134
+ {carouselCards.map((card, idx) => {
135
+ const key = `rcs-carousel-${idx}-${card?.bodyText || card?.imageSrc || card?.videoPreviewImg || ''}`;
136
+ const isVideo =
137
+ (card?.mediaType || '').toLowerCase() === String(MEDIA_TYPE_VIDEO).toLowerCase();
138
+ const cardSuggestions = Array.isArray(card?.suggestions) ? card.suggestions : [];
139
+ const suggestionsNode = cardSuggestions.length > 0
140
+ ? renderRcsSuggestionsPreview(cardSuggestions, { isCarousel: true })
141
+ : null;
142
+ const titleTrimmed = String(card?.title ?? '').trim();
143
+ const bodyTrimmed = String(card?.bodyText ?? '').trim();
144
+ const titleShown = titleTrimmed || formatMessage(messages.rcsCarouselPreviewTitlePlaceholder);
145
+ const bodyShown = bodyTrimmed || formatMessage(messages.rcsCarouselPreviewBodyPlaceholder);
146
+ return (
147
+ <CapRow key={key} className="rcs-carousel-item">
148
+ <CapRow className="message-pop align-left message-pop-carousel">
149
+ <CapRow className="whatsapp-content">
150
+ {!isVideo && (
151
+ <CapRow
152
+ className="whatsapp-image rcs-carousel-media-wrap"
153
+ style={getCarouselMediaAspectStyle(false)}
154
+ >
155
+ <CapImage
156
+ src={card?.imageSrc ? card.imageSrc : rcsImageEmptyPreview}
157
+ className="rcs-carousel-img"
158
+ alt={formatMessage(messages.previewGenerated)}
159
+ />
160
+ </CapRow>
161
+ )}
162
+ {isVideo && (
163
+ <CapTooltip
164
+ title={formatMessage(messages.videoPreviewTooltip)}
165
+ wrapperClassName="rcs-carousel-video-tooltip-wrapper"
166
+ triggerClassName="rcs-carousel-video-tooltip-trigger"
167
+ >
168
+ <CapRow className="video-preview">
169
+ <CapRow
170
+ className="whatsapp-image rcs-carousel-media-wrap"
171
+ style={getCarouselMediaAspectStyle(true)}
172
+ >
173
+ <CapImage
174
+ src={card?.videoPreviewImg ? card.videoPreviewImg : rcsVideoEmptyPreview}
175
+ className="rcs-carousel-img"
176
+ alt={formatMessage(messages.previewGenerated)}
177
+ />
178
+ </CapRow>
179
+ <CapRow className="icon-position">
180
+ <CapImage className="video-icon" src={videoPlay} alt="Play" />
181
+ </CapRow>
182
+ </CapRow>
183
+ </CapTooltip>
184
+ )}
185
+
186
+ <CapRow className="carousel-content">
187
+ <CapLabel
188
+ type={card.titleLabelType || 'label1'}
189
+ className={`carousel-title${titleTrimmed ? '' : ' rcs-carousel-field-placeholder'}`}
190
+ >
191
+ {titleShown}
192
+ </CapLabel>
193
+ <CapLabel
194
+ type={card.bodyLabelType || 'label2'}
195
+ className={`carousel-message${bodyTrimmed ? '' : ' rcs-carousel-field-placeholder'}`}
196
+ >
197
+ {bodyShown}
198
+ </CapLabel>
199
+ </CapRow>
200
+ </CapRow>
201
+ </CapRow>
202
+
203
+ {/* Deliberately OUTSIDE .message-pop-carousel — the CTA bar is its own
204
+ floating element below the card, not clipped/rounded as part of it. */}
205
+ {!!suggestionsNode && (
206
+ <CapRow className="rcs-carousel-cta-stack" gutter={0}>
207
+ {suggestionsNode}
208
+ </CapRow>
209
+ )}
210
+ </CapRow>
211
+ );
212
+ })}
213
+ </CapRow>
214
+ </CapRow>
215
+ );
216
+
96
217
  // Render text preview content
97
218
  const renderTextPreviewContent = () => {
98
219
  const {
@@ -226,11 +347,24 @@ const RcsPreviewContent = ({
226
347
  const timestamp = `${displayHours}:${displayMinutes} ${ampm}`;
227
348
 
228
349
  // Render normal RCS preview (same structure as SMS up to sms-message-container)
350
+ const carouselCardsNormalized = Array.isArray(content?.carouselData) ? content?.carouselData : [];
351
+ const hasCarousel = carouselCardsNormalized?.length > 0;
229
352
  const hasMedia = !!(content?.rcsImageSrc || content?.rcsVideoSrc);
230
- const contentToRender = hasMedia ? renderMediaPreviewContent() : renderTextPreviewContent();
353
+ let contentToRender;
354
+ if (hasCarousel) {
355
+ contentToRender = renderCarouselPreviewContent(carouselCardsNormalized);
356
+ } else if (hasMedia) {
357
+ contentToRender = renderMediaPreviewContent();
358
+ } else {
359
+ contentToRender = renderTextPreviewContent();
360
+ }
231
361
 
232
362
  return (
233
- <CapRow type='flex' align='middle' justify='center' className="sms-device-container">
363
+ <CapRow
364
+ type='flex' align='middle' justify='center'
365
+ className={`sms-device-container rcs-device-container ${hasCarousel ? 'rcs-device-container-carousel' : ''}`}
366
+ style={{ position: 'relative' }}
367
+ >
234
368
  {/* Device Background Image */}
235
369
  <CapImage
236
370
  className="sms-device-image"
@@ -238,8 +372,10 @@ const RcsPreviewContent = ({
238
372
  alt={`${deviceName} Device`}
239
373
  />
240
374
 
241
- {/* Content Overlay */}
242
- <CapRow className={`sms-content-overlay sms-content-overlay-${device} sms-preview sms-${device}`}>
375
+ {/* Content Overlay - nav bar + date separator always; message content only for non-carousel */}
376
+ <CapRow
377
+ className={`sms-content-overlay sms-content-overlay-${device} sms-preview sms-${device} rcs-content-overlay`}
378
+ >
243
379
  {/* Navigation Bar - Same as SMS */}
244
380
  <CapRow type='flex' className={`sms-navigation-bar ${!showHeader ? 'sms-navigation-bar-no-header' : ''}`}>
245
381
  <CapIcon type="chevron-left" className="sms-back-arrow" />
@@ -254,24 +390,40 @@ const RcsPreviewContent = ({
254
390
  <FormattedMessage {...messages.timeAgo} defaultMessage="Today" />
255
391
  </CapRow>
256
392
 
257
- {/* Message Container - Same structure as SMS, only content inside is RCS-specific */}
258
- <CapRow className="sms-message-container">
259
- <CapRow useLegacy className="sms-message-bubble">
260
- <CapRow useLegacy className="sms-message-text">
261
- {/* RCS-specific content rendering */}
262
- <CapRow useLegacy className="message-pop sms">
263
- {contentToRender}
393
+ {/* Message Container - non-carousel only; carousel is rendered outside this overflow-x:hidden ancestor */}
394
+ {!hasCarousel && (
395
+ <CapRow className="sms-message-container">
396
+ <CapRow useLegacy className="sms-message-bubble">
397
+ <CapRow useLegacy className="sms-message-text">
398
+ <CapRow useLegacy className="message-pop sms">
399
+ {contentToRender}
400
+ </CapRow>
264
401
  </CapRow>
265
402
  </CapRow>
403
+ <CapRow useLegacy className="sms-timestamp">
404
+ {timestamp}
405
+ </CapRow>
266
406
  </CapRow>
267
- <CapRow useLegacy className="sms-timestamp">
407
+ )}
408
+ </CapRow>
409
+
410
+ {/* Carousel panel: sibling of overlay, no overflow-x:hidden ancestor → scroll-container
411
+ can receive horizontal scroll events. Positioned absolutely over the device image. */}
412
+ {hasCarousel && (
413
+ <CapRow
414
+ className={`rcs-carousel-panel rcs-carousel-panel-${device}`}
415
+ >
416
+ {renderCarouselPreviewContent(carouselCardsNormalized)}
417
+ <CapRow useLegacy className="sms-timestamp rcs-carousel-timestamp">
268
418
  {timestamp}
269
419
  </CapRow>
270
420
  </CapRow>
271
- </CapRow>
421
+ )}
272
422
  </CapRow>
273
- );
274
- };
423
+ )
424
+ }
425
+
426
+
275
427
 
276
428
  RcsPreviewContent.propTypes = {
277
429
  content: PropTypes.shape({
@@ -282,6 +434,18 @@ RcsPreviewContent.propTypes = {
282
434
  rcsImageSrc: PropTypes.string,
283
435
  rcsVideoSrc: PropTypes.string,
284
436
  rcsThumbnailSrc: PropTypes.string,
437
+ carouselData: PropTypes.arrayOf(
438
+ PropTypes.shape({
439
+ titleLabelType: PropTypes.string,
440
+ bodyLabelType: PropTypes.string,
441
+ })
442
+ ),
443
+ carouselPreviewDimensions: PropTypes.shape({
444
+ imageWidth: PropTypes.number,
445
+ imageHeight: PropTypes.number,
446
+ videoThumbWidth: PropTypes.number,
447
+ videoThumbHeight: PropTypes.number,
448
+ }),
285
449
  suggestions: PropTypes.arrayOf(
286
450
  PropTypes.shape({
287
451
  type: PropTypes.string,