@capillarytech/creatives-library 8.0.90 → 8.0.91

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 (29) hide show
  1. package/package.json +1 -1
  2. package/services/api.js +7 -1
  3. package/services/tests/api.test.js +5 -1
  4. package/v2Components/CapImageUpload/index.js +13 -10
  5. package/v2Components/CapVideoUpload/index.js +12 -9
  6. package/v2Components/CapWhatsappCTA/messages.js +4 -0
  7. package/v2Components/CapWhatsappCarouselButton/constant.js +56 -0
  8. package/v2Components/CapWhatsappCarouselButton/index.js +446 -0
  9. package/v2Components/CapWhatsappCarouselButton/index.scss +39 -0
  10. package/v2Components/CapWhatsappCarouselButton/tests/index.test.js +237 -0
  11. package/v2Components/TemplatePreview/_templatePreview.scss +9 -0
  12. package/v2Components/TemplatePreview/assets/images/empty_image_preview.svg +4 -0
  13. package/v2Components/TemplatePreview/assets/images/empty_video_preview.svg +4 -0
  14. package/v2Components/TemplatePreview/index.js +171 -105
  15. package/v2Components/TemplatePreview/tests/__snapshots__/index.test.js.snap +6 -6
  16. package/v2Containers/CreativesContainer/index.js +91 -4
  17. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +1 -0
  18. package/v2Containers/Templates/_templates.scss +47 -0
  19. package/v2Containers/Templates/index.js +55 -5
  20. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +177 -156
  21. package/v2Containers/Whatsapp/constants.js +87 -1
  22. package/v2Containers/Whatsapp/index.js +707 -189
  23. package/v2Containers/Whatsapp/index.scss +52 -1
  24. package/v2Containers/Whatsapp/messages.js +38 -2
  25. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +30183 -90694
  26. package/v2Containers/Whatsapp/tests/__snapshots__/utils.test.js.snap +6 -0
  27. package/v2Containers/Whatsapp/tests/utils.test.js +80 -1
  28. package/v2Containers/Whatsapp/utils.js +34 -0
  29. package/v2Containers/mockdata.js +2 -0
@@ -0,0 +1,237 @@
1
+ import React from 'react';
2
+ import { injectIntl } from 'react-intl';
3
+ import '@testing-library/jest-dom';
4
+ import { render, screen, fireEvent } from '../../../utils/test-utils';
5
+ import { CapWhatsappCarouselButton } from '../index';
6
+ import {
7
+ INITIAL_CAROUSEL_PHONE_NUMBER_DATA,
8
+ INITIAL_CAROUSEL_URL_DATA
9
+ } from '../constant';
10
+ import { HOST_TWILIO } from "../../../v2Containers/Whatsapp/constants";
11
+
12
+ const setCarouselData = jest.fn();
13
+
14
+ const initializeComponent = (
15
+ carouselData = [],
16
+ isEditFlow = false,
17
+ hostName = HOST_TWILIO,
18
+ carouselIndex = 0
19
+ ) => {
20
+ const Component = injectIntl(CapWhatsappCarouselButton);
21
+ return render(
22
+ <Component
23
+ carouselData={carouselData}
24
+ setCarouselData={setCarouselData}
25
+ carouselIndex={carouselIndex}
26
+ hostName={hostName}
27
+ isEditFlow={isEditFlow}
28
+ tags={[]}
29
+ injectedTags={{}}
30
+ selectedOfferDetails={[]}
31
+ />,
32
+ );
33
+ };
34
+
35
+ describe('CapWhatsappCarouselButton', () => {
36
+ beforeEach(() => {
37
+ jest.clearAllMocks();
38
+ });
39
+
40
+ it('renders initial phone number button type', () => {
41
+ initializeComponent([{
42
+ buttons: [INITIAL_CAROUSEL_PHONE_NUMBER_DATA]
43
+ }]);
44
+
45
+ expect(screen.getByText('Button type')).toBeInTheDocument();
46
+ expect(screen.getByText('Button text')).toBeInTheDocument();
47
+ expect(screen.getAllByText('Phone number')).toHaveLength(2);
48
+ expect(screen.getByRole('button', { name: /save/i })).toBeDisabled();
49
+ });
50
+
51
+ it('handles button text change', () => {
52
+ initializeComponent([{
53
+ buttons: [INITIAL_CAROUSEL_PHONE_NUMBER_DATA]
54
+ }]);
55
+
56
+ const buttonTextInput = screen.getByPlaceholderText('Enter button text');
57
+ fireEvent.change(buttonTextInput, { target: { value: 'Call now' } });
58
+
59
+ expect(setCarouselData).toHaveBeenCalledWith([{
60
+ buttons: [{
61
+ ...INITIAL_CAROUSEL_PHONE_NUMBER_DATA,
62
+ text: 'Call now'
63
+ }]
64
+ }]);
65
+ });
66
+
67
+ it('handles phone number change', () => {
68
+ initializeComponent([{
69
+ buttons: [INITIAL_CAROUSEL_PHONE_NUMBER_DATA]
70
+ }]);
71
+
72
+ const phoneInput = screen.getByPlaceholderText('Enter phone number');
73
+ fireEvent.change(phoneInput, { target: { value: '9112345678' } });
74
+ expect(screen.getByText('Phone')).toBeInTheDocument();
75
+ });
76
+
77
+ it('handles URL type button with static URL', () => {
78
+ initializeComponent([{
79
+ buttons: [INITIAL_CAROUSEL_URL_DATA]
80
+ }]);
81
+
82
+ const urlInput = screen.getByPlaceholderText('Enter website URL');
83
+ fireEvent.change(urlInput, {
84
+ target: { value: 'https://www.example.com' }
85
+ });
86
+
87
+ expect(setCarouselData).toHaveBeenCalled();
88
+ });
89
+
90
+ it('handles URL type button with dynamic URL', () => {
91
+ const urlData = {
92
+ ...INITIAL_CAROUSEL_URL_DATA,
93
+ urlType: 'DYNAMIC_URL'
94
+ };
95
+
96
+ initializeComponent([{
97
+ buttons: [urlData]
98
+ }]);
99
+
100
+ const urlInput = screen.getByPlaceholderText('Enter URL, variable is added by default');
101
+ fireEvent.change(urlInput, {
102
+ target: { value: 'https://www.example.com/{{1}}' }
103
+ });
104
+
105
+ expect(setCarouselData).toHaveBeenCalled();
106
+ });
107
+
108
+ it('shows saved button state', () => {
109
+ initializeComponent([{
110
+ buttons: [{
111
+ ...INITIAL_CAROUSEL_PHONE_NUMBER_DATA,
112
+ text: 'Call us',
113
+ phone_number: '9112345678',
114
+ isSaved: true
115
+ }]
116
+ }]);
117
+
118
+ expect(screen.getByText('Call us')).toBeInTheDocument();
119
+ expect(screen.getByText('9112345678')).toBeInTheDocument();
120
+ });
121
+
122
+ it('handles add button click', () => {
123
+ initializeComponent([{
124
+ buttons: [{
125
+ ...INITIAL_CAROUSEL_PHONE_NUMBER_DATA,
126
+ text: 'Call us',
127
+ phone_number: '9112345678',
128
+ isSaved: true
129
+ }]
130
+ }]);
131
+
132
+ const addButton = screen.getByRole('button', { name: /add button/i });
133
+ fireEvent.click(addButton);
134
+
135
+ expect(setCarouselData).toHaveBeenCalled();
136
+ });
137
+
138
+ it('handles delete button click', () => {
139
+ initializeComponent([{
140
+ buttons: [
141
+ {
142
+ ...INITIAL_CAROUSEL_PHONE_NUMBER_DATA,
143
+ text: 'Call us',
144
+ phone_number: '9112345678',
145
+ isSaved: true
146
+ },
147
+ {
148
+ ...INITIAL_CAROUSEL_URL_DATA,
149
+ text: 'Visit us',
150
+ url: 'https://example.com',
151
+ isSaved: true
152
+ }
153
+ ]
154
+ }]);
155
+
156
+ const deleteButton = screen.getAllByRole('button', {
157
+ class: 'ant-btn cap-button-v2 flat-btn whatsapp-carousel-delete-icon-btn ant-btn-flat'
158
+ });
159
+ fireEvent.click(deleteButton[0]);
160
+
161
+ expect(setCarouselData).toHaveBeenCalledTimes(1);
162
+ });
163
+
164
+ // Additional test case for delete button disabled state
165
+ it('delete button should be disabled when only one button exists', () => {
166
+ initializeComponent([{
167
+ buttons: [
168
+ {
169
+ ...INITIAL_CAROUSEL_PHONE_NUMBER_DATA,
170
+ text: 'Call us',
171
+ phone_number: '9112345678',
172
+ isSaved: true
173
+ }
174
+ ]
175
+ }]);
176
+
177
+ const deleteButton = screen.getAllByRole('button', {
178
+ class: 'ant-btn cap-button-v2 flat-btn whatsapp-carousel-delete-icon-btn ant-btn-flat'
179
+ });
180
+
181
+ expect(deleteButton[0]).toBeDisabled();
182
+ });
183
+
184
+ it('handles edit mode', () => {
185
+ initializeComponent([{
186
+ buttons: [{
187
+ ...INITIAL_CAROUSEL_URL_DATA,
188
+ text: 'Visit us',
189
+ url: 'https://example.com/{{1}}',
190
+ urlType: 'DYNAMIC_URL',
191
+ isSaved: true,
192
+ isEditFlow: true,
193
+ }]
194
+ }]);
195
+
196
+ expect(screen.getByText('Visit us')).toBeInTheDocument();
197
+ expect(screen.getByText('https://example.com/{{1}}')).toBeInTheDocument();
198
+ });
199
+
200
+ it('validates invalid button text', () => {
201
+ initializeComponent([{
202
+ buttons: [INITIAL_CAROUSEL_PHONE_NUMBER_DATA]
203
+ }]);
204
+
205
+ const buttonTextInput = screen.getByPlaceholderText('Enter button text');
206
+ fireEvent.change(buttonTextInput, { target: { value: '!@#$' } });
207
+
208
+ expect(setCarouselData).toHaveBeenCalledWith(expect.arrayContaining([
209
+ expect.objectContaining({
210
+ buttons: expect.arrayContaining([
211
+ expect.objectContaining({
212
+ textError: expect.any(String)
213
+ })
214
+ ])
215
+ })
216
+ ]));
217
+ });
218
+
219
+ it('validates invalid URL', () => {
220
+ initializeComponent([{
221
+ buttons: [INITIAL_CAROUSEL_URL_DATA]
222
+ }]);
223
+
224
+ const urlInput = screen.getByPlaceholderText('Enter website URL');
225
+ fireEvent.change(urlInput, { target: { value: 'invalid-url' } });
226
+
227
+ expect(setCarouselData).toHaveBeenCalledWith(expect.arrayContaining([
228
+ expect.objectContaining({
229
+ buttons: expect.arrayContaining([
230
+ expect.objectContaining({
231
+ urlError: expect.any(String)
232
+ })
233
+ ])
234
+ })
235
+ ]));
236
+ });
237
+ });
@@ -567,6 +567,10 @@
567
567
  font-size: 10px;
568
568
  font-family: 'open-sans';
569
569
  }
570
+ &.message-pop-carousel {
571
+ position: relative;
572
+ margin-right: $CAP_SPACE_04;
573
+ }
570
574
  }
571
575
  }
572
576
  .android-push-message-Container, .iphone-push-message-Container{
@@ -747,6 +751,11 @@
747
751
  width: 153px;
748
752
  }
749
753
  }
754
+
755
+ .msg-container-carousel {
756
+ display: flex;
757
+ flex-direction: column;
758
+ }
750
759
  }
751
760
 
752
761
  .align-center {
@@ -0,0 +1,4 @@
1
+ <svg width="258" height="140" viewBox="0 0 258 140" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="258" height="140" rx="4" fill="#F4F5F7"/>
3
+ <path d="M122 79C121.45 79 120.979 78.8042 120.588 78.4125C120.196 78.0208 120 77.55 120 77V63C120 62.45 120.196 61.9792 120.588 61.5875C120.979 61.1958 121.45 61 122 61H136C136.55 61 137.021 61.1958 137.413 61.5875C137.804 61.9792 138 62.45 138 63V77C138 77.55 137.804 78.0208 137.413 78.4125C137.021 78.8042 136.55 79 136 79H122ZM122 77H136V63H122V77ZM124 75H134C134.2 75 134.35 74.9083 134.45 74.725C134.55 74.5417 134.533 74.3667 134.4 74.2L131.65 70.525C131.55 70.3917 131.417 70.325 131.25 70.325C131.083 70.325 130.95 70.3917 130.85 70.525L128.25 74L126.4 71.525C126.3 71.3917 126.167 71.325 126 71.325C125.833 71.325 125.7 71.3917 125.6 71.525L123.6 74.2C123.467 74.3667 123.45 74.5417 123.55 74.725C123.65 74.9083 123.8 75 124 75Z" fill="#5E6C84"/>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg width="258" height="140" viewBox="0 0 258 140" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="258" height="140" rx="4" fill="#F4F5F7"/>
3
+ <path d="M123.025 74H130.975C131.192 74 131.346 73.9083 131.438 73.725C131.529 73.5417 131.508 73.3667 131.375 73.2L128.95 70.025C128.9 69.9583 128.842 69.9083 128.775 69.875C128.708 69.8417 128.633 69.825 128.55 69.825C128.467 69.825 128.392 69.8417 128.325 69.875C128.258 69.9083 128.2 69.9583 128.15 70.025L126.65 71.975C126.6 72.0417 126.542 72.0917 126.475 72.125C126.408 72.1583 126.333 72.175 126.25 72.175C126.167 72.175 126.092 72.1583 126.025 72.125C125.958 72.0917 125.9 72.0417 125.85 71.975L125.1 71C125.05 70.9333 124.992 70.8875 124.925 70.8625C124.858 70.8375 124.783 70.825 124.7 70.825C124.617 70.825 124.542 70.8375 124.475 70.8625C124.408 70.8875 124.35 70.9333 124.3 71L122.625 73.2C122.492 73.3667 122.471 73.5417 122.562 73.725C122.654 73.9083 122.808 74 123.025 74ZM121 78C120.45 78 119.979 77.8042 119.588 77.4125C119.196 77.0208 119 76.55 119 76V64C119 63.45 119.196 62.9792 119.588 62.5875C119.979 62.1958 120.45 62 121 62H133C133.55 62 134.021 62.1958 134.413 62.5875C134.804 62.9792 135 63.45 135 64V68.5L138.15 65.35C138.317 65.1833 138.5 65.1417 138.7 65.225C138.9 65.3083 139 65.4667 139 65.7V74.3C139 74.5333 138.9 74.6917 138.7 74.775C138.5 74.8583 138.317 74.8167 138.15 74.65L135 71.5V76C135 76.55 134.804 77.0208 134.413 77.4125C134.021 77.8042 133.55 78 133 78H121ZM121 76H133V64H121V76Z" fill="#5E6C84"/>
4
+ </svg>
@@ -20,6 +20,7 @@ import {
20
20
  CAP_PURPLE01,
21
21
  CAP_G16,
22
22
  CAP_SPACE_08,
23
+ CAP_SPACE_04,
23
24
  } from '@capillarytech/cap-ui-library/styled/variables';
24
25
  import isEmpty from 'lodash/isEmpty';
25
26
  import './_templatePreview.scss';
@@ -43,14 +44,16 @@ import inAppMobileIOSModal from './assets/images/inapp_mobile_ios_modal.svg';
43
44
  import inAppMobileIOSTop from './assets/images/inapp_mobile_ios_top.svg';
44
45
  import inAppMobileIOSBottom from './assets/images/inapp_mobile_ios_bottom.svg';
45
46
  import inAppMobileIOSFull from './assets/images/inapp_mobile_ios_full.svg';
47
+ import whatsappImageEmptyPreview from './assets/images/empty_image_preview.svg';
48
+ import whatsappVideoEmptyPreview from './assets/images/empty_video_preview.svg';
46
49
  import videoPlay from '../../assets/videoPlay.svg';
47
50
  import zaloMessage from '../../v2Containers/Zalo/messages';
48
51
  import { handlePreviewInNewTab } from '../../utils/common';
49
52
  import { TEMPLATE, IMAGE_CAROUSEL, IMAGE, STICKER, TEXT, IMAGE_MAP, VIDEO } from '../../v2Containers/Line/Container/constants';
50
53
  import CapFacebookPreview from '../../v2Containers/CapFacebookPreview';
51
54
  import WhatsappStatusContainer from '../WhatsappStatusContainer';
52
- import { getWhatsappQuickReply } from '../../v2Containers/Whatsapp/utils';
53
- import { QUICK_REPLY, WHATSAPP_CATEGORIES } from '../../v2Containers/Whatsapp/constants';
55
+ import { getWhatsappQuickReply, getWhatsappCarouselButtonView } from '../../v2Containers/Whatsapp/utils';
56
+ import { QUICK_REPLY, WHATSAPP_CATEGORIES, PHONE_NUMBER } from '../../v2Containers/Whatsapp/constants';
54
57
  import { ANDROID, INAPP_MESSAGE_LAYOUT_TYPES } from '../../v2Containers/InApp/constants';
55
58
 
56
59
  const wechatBodyNew = require('./assets/images/wechat_mobile_android.svg');
@@ -213,7 +216,7 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
213
216
  iosActions = _.map(content.actions, (action) => {
214
217
  if (action.label) {
215
218
  return (<div className="actions" key={`action-${action.label}`}>
216
- <span className="action">{action.label.toUpperCase()}</span>
219
+ <span className="action">{action.label.toUpperCase()}</span>
217
220
  </div>);
218
221
  }
219
222
  return undefined;
@@ -253,6 +256,15 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
253
256
  'width': '88%',
254
257
  'left': 0,
255
258
  };
259
+ const carouselWhatsappSectionStyle = {
260
+ 'padding': `${CAP_SPACE_04} 0 ${CAP_SPACE_08}`,
261
+ 'border-radius': '0.428rem',
262
+ 'background-color': CAP_WHITE,
263
+ 'width': '11.857rem',
264
+ 'cursor': 'pointer',
265
+ 'flex-shrink': 0,
266
+ 'left': 0,
267
+ };
256
268
  const getVideoContent = ({
257
269
  video,
258
270
  actionUrl,
@@ -274,53 +286,53 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
274
286
  >
275
287
  {
276
288
  video
277
- ? (
278
- <>
279
- <video style={{ opacity: 0.5, width: '100%' }}>
280
- <source src={video} type="video/mp4" />
281
- </video>
282
- <div
283
- style={{
284
- position: 'absolute',
285
- display: 'flex',
286
- flexDirection: 'column',
287
- }}
288
- >
289
- <CapButton
290
- className="preview-video-btn"
291
- type="flat"
289
+ ? (
290
+ <>
291
+ <video style={{ opacity: 0.5, width: '100%' }}>
292
+ <source src={video} type="video/mp4" />
293
+ </video>
294
+ <div
295
+ style={{
296
+ position: 'absolute',
297
+ display: 'flex',
298
+ flexDirection: 'column',
299
+ }}
292
300
  >
301
+ <CapButton
302
+ className="preview-video-btn"
303
+ type="flat"
304
+ >
293
305
  <CapIcon type="play"/>
294
- <FormattedMessage {...messages.playVideo} />
295
- </CapButton>
296
- {
297
- actionUrl
298
- ? (
299
- <CapButton
300
- className="preview-video-btn"
301
- type="flat"
302
- >
306
+ <FormattedMessage {...messages.playVideo} />
307
+ </CapButton>
308
+ {
309
+ actionUrl
310
+ ? (
311
+ <CapButton
312
+ className="preview-video-btn"
313
+ type="flat"
314
+ >
303
315
  <CapIcon type="reply" style={{ fontSize: 18 }}/>
304
- <FormattedMessage {...messages.showDetails} />
305
- </CapButton>
306
- )
307
- : null
308
- }
309
- </div>
310
- </>
311
- )
312
- : (
313
- <CapImage
314
- src={lineVideoPlaceholder}
315
- alt="brand-name"
316
- rest={{
317
- style: {
318
- width: 126,
319
- marginBottom: 5,
320
- },
321
- }}
322
- />
323
- )
316
+ <FormattedMessage {...messages.showDetails} />
317
+ </CapButton>
318
+ )
319
+ : null
320
+ }
321
+ </div>
322
+ </>
323
+ )
324
+ : (
325
+ <CapImage
326
+ src={lineVideoPlaceholder}
327
+ alt="brand-name"
328
+ rest={{
329
+ style: {
330
+ width: 126,
331
+ marginBottom: 5,
332
+ },
333
+ }}
334
+ />
335
+ )
324
336
  }
325
337
  </div>
326
338
  );
@@ -336,10 +348,10 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
336
348
  renderArray.push(
337
349
  <CapLabel type="label21" className="whatsapp-cta-preview">
338
350
  {type !== WHATSAPP_CATEGORIES.authentication && <CapIcon
339
- type={
340
- (ctaType || type) === 'PHONE_NUMBER' ? 'call' : 'launch'
341
- }
342
- size="xs"
351
+ type={
352
+ (ctaType || type) === PHONE_NUMBER ? 'call' : 'launch'
353
+ }
354
+ size="xs"
343
355
  />}
344
356
  {text}
345
357
  </CapLabel>,
@@ -430,42 +442,42 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
430
442
  {this.props.showCount &&
431
443
  channel &&
432
444
  channel.toLowerCase() !== FACEBOOK && (
433
- <CapColumn span={16}>
434
- {channel && channel.toLowerCase() === "sms" && (
435
- <CapHeading type="h3">
436
- <FormattedMessage
437
- {...messages.charactersTotal}
438
- values={{
439
- smsCount: smsDetails.parts,
440
- charCount: smsDetails.chars_used,
441
- }}
442
- />
443
- {smsDetails.unicode && (
444
- <FormattedMessage {...messages.smsFormatType} />
445
- )}
446
- </CapHeading>
447
- )}
448
- {smsDetails.optoutUrlPresent && (
449
- <CapHeading type="h6">
445
+ <CapColumn span={16}>
446
+ {channel && channel.toLowerCase() === "sms" && (
447
+ <CapHeading type="h3">
448
+ <FormattedMessage
449
+ {...messages.charactersTotal}
450
+ values={{
451
+ smsCount: smsDetails.parts,
452
+ charCount: smsDetails.chars_used,
453
+ }}
454
+ />
455
+ {smsDetails.unicode && (
456
+ <FormattedMessage {...messages.smsFormatType} />
457
+ )}
458
+ </CapHeading>
459
+ )}
460
+ {smsDetails.optoutUrlPresent && (
461
+ <CapHeading type="h6">
462
+ <FormattedMessage
463
+ {...messages.optoutCharactersTotal}
464
+ values={{ optoutUrlLength: smsDetails.optouturlLength }}
465
+ />
466
+ </CapHeading>
467
+ )}
468
+ {channel?.toLowerCase() === WHATSAPP.toLowerCase() ? (
469
+ <>
470
+ <WhatsappStatusContainer template={templateData} />
471
+ <CapHeading type="h3" className="margin-t-12">
450
472
  <FormattedMessage
451
- {...messages.optoutCharactersTotal}
452
- values={{ optoutUrlLength: smsDetails.optouturlLength }}
473
+ {...messages.whatsappMessageLength}
474
+ values={{ length: whatsappUpdatedLen }}
453
475
  />
454
476
  </CapHeading>
455
- )}
456
- {channel?.toLowerCase() === WHATSAPP.toLowerCase() ? (
457
- <>
458
- <WhatsappStatusContainer template={templateData} />
459
- <CapHeading type="h3" className="margin-t-12">
460
- <FormattedMessage
461
- {...messages.whatsappMessageLength}
462
- values={{ length: whatsappUpdatedLen }}
463
- />
464
- </CapHeading>
465
- </>
466
- ) : null}
467
- </CapColumn>
468
- )}
477
+ </>
478
+ ) : null}
479
+ </CapColumn>
480
+ )}
469
481
  <CapColumn span={this.props.showCount ? 8 : 24}>
470
482
  {showTestAndPreview && (
471
483
  <div className="test-and-preview-container">
@@ -628,9 +640,9 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
628
640
  )}
629
641
  {content.actions.filter((action) => action.label)
630
642
  .length ? (
631
- <div className="actions">
632
- {_.map(
633
- content.actions,
643
+ <div className="actions">
644
+ {_.map(
645
+ content.actions,
634
646
  (action) =>
635
647
  !!action.label && (
636
648
  <span
@@ -640,11 +652,11 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
640
652
  {action.label && action.label.toUpperCase()}
641
653
  </span>
642
654
  )
643
- )}
644
- </div>
645
- ) : (
646
- ""
647
- )}
655
+ )}
656
+ </div>
657
+ ) : (
658
+ ""
659
+ )}
648
660
  </div>
649
661
  ) : (
650
662
  <div className="message-pop align-left">
@@ -980,14 +992,14 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
980
992
  <CapLabel className="whatsapp-brand-name">
981
993
  {whatsappUpdatedAccountName || ""}
982
994
  </CapLabel>
983
- <div className="msg-container whatsapp-message-container">
995
+ <div className={`msg-container whatsapp-message-container ${content?.carouselData && 'msg-container-carousel'}`}>
984
996
  <div
985
- className="message-pop align-left"
997
+ className={`message-pop align-left ${content?.carouselData && 'message-pop-carousel'}`}
986
998
  style={whatsappSectionStyle}
987
999
  >
988
1000
  <div className="whatsapp-content">
989
- {content?.showUrlPreview &&
990
- renderUrlPreview(content?.metaTagsDetails)}
1001
+ {content?.showUrlPreview
1002
+ && renderUrlPreview(content?.metaTagsDetails)}
991
1003
  {content?.whatsappImageSrc && (
992
1004
  <CapImage
993
1005
  src={content.whatsappImageSrc}
@@ -1026,6 +1038,60 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
1026
1038
  {renderQuickReplyPreview()}
1027
1039
  </div>
1028
1040
  </div>
1041
+ {content?.carouselData && (
1042
+ <div
1043
+ style={{
1044
+ overflowX: "auto",
1045
+ display: "flex",
1046
+ paddingTop: "5px",
1047
+ paddingRight: "5px",
1048
+ whiteSpace: "nowrap",
1049
+ scrollbarWidth: "none", // Hide scrollbar in Firefox
1050
+ msOverflowStyle: "none", // Hide scrollbar in IE/Edge
1051
+ }}
1052
+ className="scroll-container"
1053
+ >
1054
+ {content?.carouselData?.map((data, index) => (
1055
+ <div
1056
+ key={`carousel-${index}`}
1057
+ className="message-pop align-left message-pop-carousel"
1058
+ style={carouselWhatsappSectionStyle}>
1059
+ <div className="whatsapp-content">
1060
+ {content?.carouselMediaType === "image" && (
1061
+ <CapImage
1062
+ src={data?.imageSrc ? data?.imageSrc : whatsappImageEmptyPreview}
1063
+ className="whatsapp-image"
1064
+ alt={formatMessage(messages.previewGenerated)}
1065
+ />
1066
+ )}
1067
+ {content?.carouselMediaType === "video" && (
1068
+ <CapTooltip
1069
+ title={formatMessage(messages.videoPreviewTooltip)}
1070
+ >
1071
+ <div className="video-preview">
1072
+ <CapImage
1073
+ src={data?.videoPreviewImg ? data?.videoPreviewImg : whatsappVideoEmptyPreview}
1074
+ className="whatsapp-image"
1075
+ alt={formatMessage(messages.previewGenerated)}
1076
+ />
1077
+ <div className="icon-position">
1078
+ <CapImage
1079
+ className="video-icon"
1080
+ src={videoPlay}
1081
+ />
1082
+ </div>
1083
+ </div>
1084
+ </CapTooltip>
1085
+ )}
1086
+ <CapLabel type="label5">
1087
+ {content?.isEditFlow ? data?.updatedBodyText?.join("") : data?.bodyText}
1088
+ </CapLabel>
1089
+ {getWhatsappCarouselButtonView(data?.buttons, true)}
1090
+ </div>
1091
+ </div>
1092
+ ))}
1093
+ </div>
1094
+ )}
1029
1095
  </div>
1030
1096
  </div>
1031
1097
  <CapHeading
@@ -1205,21 +1271,21 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
1205
1271
  {ctaData &&
1206
1272
  !isEmpty(ctaData) &&
1207
1273
  ctaData[0]?.text !== "" && (
1208
- <CapButton
1209
- type="primary"
1210
- className={`inapp-button-${templateLayoutType}-${device} ${
1274
+ <CapButton
1275
+ type="primary"
1276
+ className={`inapp-button-${templateLayoutType}-${device} ${
1211
1277
  mediaPreview?.inAppImageSrcAndroid
1212
1278
  ? ""
1213
1279
  : `without-image-button-${templateLayoutType}-android`
1214
- } ${
1280
+ } ${
1215
1281
  mediaPreview?.inAppImageSrcIos
1216
1282
  ? ""
1217
1283
  : `without-image-button-${templateLayoutType}-ios`
1218
- }`}
1219
- >
1220
- {ctaData[0]?.text}
1221
- </CapButton>
1222
- )}
1284
+ }`}
1285
+ >
1286
+ {ctaData[0]?.text}
1287
+ </CapButton>
1288
+ )}
1223
1289
  </div>
1224
1290
  </div>
1225
1291
  </div>