@capillarytech/creatives-library 8.0.143 → 8.0.145

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.143",
4
+ "version": "8.0.145",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
@@ -87,8 +87,8 @@ class MobilePushPreviewV2 extends React.Component { // eslint-disable-line react
87
87
 
88
88
  if (media?.length > 0) {
89
89
  const mediaItem = media[0];
90
- const { type, url } = mediaItem;
91
- if (type === VIDEO) {
90
+ const { type: mediaType, url } = mediaItem;
91
+ if (mediaType === VIDEO) {
92
92
  // Distinguish between actual video and GIF based on URL extension
93
93
  if (url && url?.toLowerCase()?.includes('.gif')) {
94
94
  bodyGif = url;
@@ -182,17 +182,17 @@ export class TemplatePreview extends React.Component { // eslint-disable-line re
182
182
  key={`carousel-card-${idx}`}
183
183
  className="mobile-push-carousel-card message-pop align-left message-pop-carousel"
184
184
  >
185
- {item?.mediaType === IMAGE.toLowerCase() && item?.imageUrl && (
185
+ {item?.mediaType?.toLowerCase() === IMAGE && (item?.imageUrl || item?.url) && (
186
186
  <CapImage
187
- src={item?.imageUrl}
187
+ src={item?.imageUrl || item?.url}
188
188
  alt="carousel-img"
189
189
  className="carousel-image"
190
190
  />
191
191
  )}
192
- {item?.mediaType === VIDEO.toLowerCase() && item?.videoPreviewImg && (
192
+ {item?.mediaType?.toLowerCase() === VIDEO && item?.videoPreviewImg && (
193
193
  <CapRow className="video-preview">
194
194
  <CapImage
195
- src={item?.videoPreviewImg}
195
+ src={item?.videoPreviewImg || item?.url}
196
196
  alt="carousel-video-preview"
197
197
  className="carousel-video-preview"
198
198
  />
@@ -0,0 +1,99 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import { IntlProvider } from 'react-intl';
4
+ import PropTypes from 'prop-types';
5
+ import '@testing-library/jest-dom';
6
+ import PreviewSection from '../PreviewSection';
7
+
8
+ // Mock messages for testing
9
+ const mockMessages = {
10
+ 'app.v2Components.TestAndPreviewSlidebox.updatingPreview': 'Updating preview with the latest changes',
11
+ };
12
+
13
+ // Test wrapper with IntlProvider
14
+ const TestWrapper = ({ children, locale = 'en' }) => (
15
+ <IntlProvider locale={locale} messages={mockMessages}>
16
+ {children}
17
+ </IntlProvider>
18
+ );
19
+
20
+ TestWrapper.propTypes = {
21
+ children: PropTypes.node.isRequired,
22
+ locale: PropTypes.string,
23
+ };
24
+
25
+ TestWrapper.defaultProps = {
26
+ locale: 'en',
27
+ };
28
+
29
+ // Mock PreviewChrome component
30
+ const MockPreviewChrome = ({
31
+ children,
32
+ device,
33
+ subject,
34
+ }) => (
35
+ <div data-testid="preview-chrome" data-device={device} data-subject={subject}>
36
+ {children}
37
+ </div>
38
+ );
39
+
40
+ MockPreviewChrome.propTypes = {
41
+ children: PropTypes.node,
42
+ device: PropTypes.string,
43
+ subject: PropTypes.string,
44
+ };
45
+
46
+ MockPreviewChrome.defaultProps = {
47
+ children: null,
48
+ device: 'desktop',
49
+ subject: '',
50
+ };
51
+
52
+ // Default props for testing
53
+ const defaultProps = {
54
+ previewDevice: 'desktop',
55
+ setPreviewDevice: jest.fn(),
56
+ selectedCustomer: { id: 'customer-1', name: 'John Doe' },
57
+ formData: { 'template-subject': 'Test Email Subject' },
58
+ isUpdatingPreview: false,
59
+ previewDataHtml: null,
60
+ content: '<p>Default email content</p>',
61
+ formatMessage: jest.fn((msg) => msg.defaultMessage || 'Updating preview with the latest changes'),
62
+ PreviewChrome: MockPreviewChrome,
63
+ };
64
+
65
+ describe('PreviewSection Component', () => {
66
+ beforeEach(() => {
67
+ jest.clearAllMocks();
68
+ });
69
+
70
+ it('should render the component with basic structure', () => {
71
+ render(
72
+ <TestWrapper>
73
+ <PreviewSection {...defaultProps} />
74
+ </TestWrapper>
75
+ );
76
+
77
+ // Check if the main container is rendered
78
+ expect(screen.getByTestId('preview-chrome')).toBeInTheDocument();
79
+
80
+ // Check if PreviewChrome receives the correct props
81
+ const previewChrome = screen.getByTestId('preview-chrome');
82
+ expect(previewChrome).toHaveAttribute('data-device', 'desktop');
83
+ expect(previewChrome).toHaveAttribute('data-subject', 'Test Email Subject');
84
+ });
85
+
86
+ it('should show loading spinner when updating preview', () => {
87
+ render(
88
+ <TestWrapper>
89
+ <PreviewSection {...defaultProps} isUpdatingPreview />
90
+ </TestWrapper>
91
+ );
92
+
93
+ // Check if loading container is displayed
94
+ expect(document.querySelector('.loading-container')).toBeInTheDocument();
95
+
96
+ // Check if CapSpin component is rendered (ant-spin class)
97
+ expect(document.querySelector('.ant-spin')).toBeInTheDocument();
98
+ });
99
+ });
@@ -363,16 +363,16 @@ export class Creatives extends React.Component {
363
363
  const iosContent = { ...(templateData?.iosContent || {}) };
364
364
  if (androidCarouselMedia?.length) {
365
365
  androidContent.expandableDetails.carouselData = androidCarouselMedia.map((media) => ({
366
- mediaType: media.type,
367
- imageUrl: media.url,
366
+ mediaType: media?.type,
367
+ imageUrl: media?.url,
368
368
  buttons: [],
369
369
  videoSrc: '',
370
370
  }));
371
371
  }
372
372
  if (iosCarouselMedia?.length) {
373
373
  iosContent.expandableDetails.carouselData = iosCarouselMedia.map((media) => ({
374
- mediaType: media.type,
375
- imageUrl: media.url,
374
+ mediaType: media?.type,
375
+ imageUrl: media?.url,
376
376
  buttons: [],
377
377
  videoSrc: '',
378
378
  }));
@@ -398,8 +398,22 @@ export class Creatives extends React.Component {
398
398
  versions: {
399
399
  base: {
400
400
  content: {
401
- ANDROID: templateData?.androidContent,
402
- IOS: templateData?.iosContent,
401
+ ANDROID: templateData?.androidContent?.type === 'HTML' ? {
402
+ type: templateData?.androidContent?.type,
403
+ bodyType: templateData?.androidContent?.bodyType,
404
+ deviceType: constants.ANDROID,
405
+ beeHtml: { value: templateData?.androidContent?.message },
406
+ beeJson: templateData?.androidContent?.expandableDetails?.message,
407
+ isBEEeditor: true,
408
+ } : templateData?.androidContent,
409
+ IOS: templateData?.iosContent?.type === 'HTML' ? {
410
+ type: templateData?.iosContent?.type,
411
+ bodyType: templateData?.iosContent?.bodyType,
412
+ deviceType: constants.IOS,
413
+ beeHtml: { value: templateData?.iosContent?.message },
414
+ beeJson: templateData?.iosContent?.expandableDetails?.message,
415
+ isBEEeditor: true,
416
+ } : templateData?.iosContent,
403
417
  },
404
418
  },
405
419
  },
@@ -801,9 +815,24 @@ export class Creatives extends React.Component {
801
815
  if (channel === constants.MOBILE_PUSH && androidContent?.expandableDetails?.carouselData?.length) {
802
816
  androidContent.expandableDetails = this.getMobilePushCarouselData({...androidContent?.expandableDetails});
803
817
  }
804
- templateData.androidContent = androidContent;
805
- templateData.androidContent.type = androidContent?.type || get(channelTemplate, 'definition.mode', '')?.toUpperCase() || constants.TEXT;
806
- templateData.androidContent.deviceType = constants.ANDROID;
818
+ if (androidContent?.isBEEeditor && androidContent?.beeHtml?.value) {
819
+ templateData.androidContent = {};
820
+ templateData.androidContent.type = 'TEXT';
821
+ // replace the TEXT in above line when backend supports HTML
822
+ templateData.androidContent.message = androidContent?.beeHtml?.value || '';
823
+ templateData.androidContent.title = 'bee free template';
824
+ templateData.androidContent.bodyType = androidContent?.bodyType;
825
+ templateData.androidContent.deviceType = constants.ANDROID;
826
+ templateData.androidContent.expandableDetails = {
827
+ style: 'BIG_TEXT',
828
+ // replace the BIG_TEXT in above line when backend supports HTML
829
+ message: androidContent?.beeJson || '',
830
+ };
831
+ } else if (!androidContent?.isBEEeditor) {
832
+ templateData.androidContent = androidContent;
833
+ templateData.androidContent.type = androidContent?.type || get(channelTemplate, 'definition.mode', '')?.toUpperCase() || constants.TEXT;
834
+ templateData.androidContent.deviceType = constants.ANDROID;
835
+ }
807
836
  }
808
837
  const iosContent = channel === constants.INAPP ? get(channelTemplate, 'versions.base.content.IOS') : get(channelTemplate, 'versions.base.IOS');
809
838
  if (!isEmpty(iosContent)) {
@@ -823,9 +852,24 @@ export class Creatives extends React.Component {
823
852
  if (channel === constants.MOBILE_PUSH && iosContent?.expandableDetails?.carouselData?.length) {
824
853
  iosContent.expandableDetails = this.getMobilePushCarouselData({...iosContent?.expandableDetails});
825
854
  }
826
- templateData.iosContent = iosContent;
827
- templateData.iosContent.type = iosContent?.type || get(channelTemplate, 'definition.mode', '')?.toUpperCase() || 'TEXT';
828
- templateData.iosContent.deviceType = constants.IOS;
855
+ if (iosContent?.isBEEeditor && iosContent?.beeHtml?.value) {
856
+ templateData.iosContent = {};
857
+ templateData.iosContent.type = 'TEXT';
858
+ // replace the TEXT in above line when backend supports HTML
859
+ templateData.iosContent.message = iosContent?.beeHtml?.value || '';
860
+ templateData.iosContent.title = 'bee free template';
861
+ templateData.iosContent.bodyType = iosContent?.bodyType;
862
+ templateData.iosContent.deviceType = constants.IOS;
863
+ templateData.iosContent.expandableDetails = {
864
+ style: 'BIG_TEXT',
865
+ // replace the BIG_TEXT in above line when backend supports HTML
866
+ message: iosContent?.beeJson || '',
867
+ };
868
+ } else if (!iosContent?.isBEEeditor) {
869
+ templateData.iosContent = iosContent;
870
+ templateData.iosContent.type = iosContent?.type || get(channelTemplate, 'definition.mode', '')?.toUpperCase() || 'TEXT';
871
+ templateData.iosContent.deviceType = constants.IOS;
872
+ }
829
873
  }
830
874
  templateData.messageSubject = channelTemplate?.name ? channelTemplate?.name : "messageSubject";
831
875
  }
@@ -104,12 +104,12 @@ export const LineText = ({
104
104
  layout: 'LINE',
105
105
  type: 'TAG',
106
106
  context:
107
- location.query.type === 'embedded'
107
+ location?.query?.type === 'embedded'
108
108
  ? 'outbound'
109
109
  : 'default',
110
110
  embedded:
111
- location.query.type === 'embedded'
112
- ? location.query.type
111
+ location?.query?.type === 'embedded'
112
+ ? location?.query?.type
113
113
  : 'full',
114
114
  };
115
115
  if (getDefaultTags) {
@@ -125,8 +125,8 @@ export const LineText = ({
125
125
  context:
126
126
  (data || '').toLowerCase() === 'all' ? 'default' : (data || '').toLowerCase(),
127
127
  embedded:
128
- location.query.type === 'embedded'
129
- ? location.query.type
128
+ location?.query?.type === 'embedded'
129
+ ? location?.query?.type
130
130
  : 'full',
131
131
  };
132
132
  globalActions.fetchSchemaForEntity(query);
@@ -266,7 +266,7 @@ export const LineText = ({
266
266
  />
267
267
  <TagList
268
268
  key={`${id}_tags`}
269
- moduleFilterEnabled={location && location.query && location.query.type !== 'embedded'}
269
+ moduleFilterEnabled={location && location?.query && location?.query?.type !== 'embedded'}
270
270
  label={formatMessage(messages.addLabels)}
271
271
  onTagSelect={onTagSelect}
272
272
  onContextChange={handleOnTagsContextChange}
@@ -646,10 +646,7 @@
646
646
  opacity: 0.8;
647
647
  height: 32px;
648
648
  line-height: 26px;
649
- margin-top: 4px;
650
- border-radius: 6px;
651
649
  text-align: center;
652
- border: solid 1px #979797;
653
650
  .action{
654
651
  font-size: 12px;
655
652
  color: #000000;
@@ -1309,9 +1309,9 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1309
1309
  </div>
1310
1310
  </div>
1311
1311
  )}
1312
- {(style === MANUAL_CAROUSEL || style === AUTO_CAROUSEL || style === FILMSTRIP_CAROUSEL) && (
1312
+ {[MANUAL_CAROUSEL, AUTO_CAROUSEL, FILMSTRIP_CAROUSEL].includes(style) && (
1313
1313
  <div className="scroll-container">
1314
- {carouselData.map((data, index) => {
1314
+ {carouselData?.map((data, index) => {
1315
1315
  return (
1316
1316
  <div
1317
1317
  key={`carousel-${index + 1}`}
@@ -1321,14 +1321,14 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
1321
1321
  <div className="whatsapp-carousel-card">
1322
1322
  {data?.mediaType === IMAGE.toLowerCase() && (
1323
1323
  <CapImage
1324
- src={data?.imageUrl ? data?.imageUrl : whatsappImageEmptyPreview}
1324
+ src={data?.imageUrl ? data?.imageUrl : data?.url ? data?.url : whatsappImageEmptyPreview}
1325
1325
  className="whatsapp-image"
1326
1326
  />
1327
1327
  )}
1328
1328
  {data?.mediaType === VIDEO.toLowerCase() && (
1329
1329
  <div className="video-preview">
1330
1330
  <CapImage
1331
- src={data?.videoPreviewImg ? data?.videoPreviewImg : whatsappVideoEmptyPreview}
1331
+ src={data?.videoPreviewImg ? data?.videoPreviewImg : data?.url ? data?.url : whatsappVideoEmptyPreview}
1332
1332
  className="whatsapp-image"
1333
1333
  />
1334
1334
  <div className="icon-position">
Binary file