@capillarytech/creatives-library 8.0.353-alpha.3 → 8.0.353-alpha.5

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/v2Components/CommonTestAndPreview/UnifiedPreview/PreviewHeader.js +17 -0
  3. package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberPreviewContent.js +14 -132
  4. package/v2Components/CommonTestAndPreview/UnifiedPreview/WebPushPreviewContent.js +169 -0
  5. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +70 -163
  6. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +44 -5
  7. package/v2Components/CommonTestAndPreview/constants.js +2 -0
  8. package/v2Components/CommonTestAndPreview/index.js +58 -49
  9. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/PreviewHeader.test.js +159 -0
  10. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/ViberPreviewContent.test.js +0 -364
  11. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/WebPushPreviewContent.test.js +522 -0
  12. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +255 -0
  13. package/v2Components/CommonTestAndPreview/tests/constants.test.js +2 -1
  14. package/v2Components/CommonTestAndPreview/tests/index.test.js +194 -0
  15. package/v2Components/TestAndPreviewSlidebox/index.js +2 -2
  16. package/v2Containers/App/constants.js +3 -0
  17. package/v2Containers/App/tests/constants.test.js +61 -0
  18. package/v2Containers/Templates/_templates.scss +0 -77
  19. package/v2Containers/Templates/index.js +70 -77
  20. package/v2Containers/Templates/tests/webpush.test.js +375 -0
  21. package/v2Containers/Viber/constants.js +0 -19
  22. package/v2Containers/Viber/index.js +47 -714
  23. package/v2Containers/Viber/index.scss +0 -148
  24. package/v2Containers/Viber/messages.js +0 -116
  25. package/v2Containers/Viber/tests/index.test.js +0 -80
  26. package/v2Containers/WebPush/Create/index.js +91 -8
  27. package/v2Containers/WebPush/Create/index.scss +7 -0
  28. package/v2Containers/WebPush/Create/tests/getTemplateContent.test.js +338 -0
  29. package/v2Containers/WebPush/Create/tests/testAndPreviewIntegration.test.js +325 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.353-alpha.3",
4
+ "version": "8.0.353-alpha.5",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
@@ -23,6 +23,7 @@ const PreviewHeader = ({
23
23
  showDeviceToggle,
24
24
  onDeviceChange,
25
25
  channel,
26
+ setIsFullscreenOpen,
26
27
  }) => {
27
28
  // Determine if this is SMS, WhatsApp, RCS, InApp, MobilePush, or Viber channel (uses Android/iOS) or other channels (uses Desktop/Mobile)
28
29
  const isSmsChannel = channel === CHANNELS.SMS;
@@ -31,8 +32,13 @@ const PreviewHeader = ({
31
32
  const isInAppChannel = channel === CHANNELS.INAPP;
32
33
  const isMobilePushChannel = channel === CHANNELS.MOBILEPUSH;
33
34
  const isViberChannel = channel === CHANNELS.VIBER;
35
+ const isWebPushChannel = channel === CHANNELS.WEBPUSH;
34
36
  const isAndroidIosToggle = isSmsChannel || isWhatsappChannel || isRcsChannel || isInAppChannel || isMobilePushChannel || isViberChannel;
35
37
 
38
+ const handleOpenFullscreen = () => {
39
+ setIsFullscreenOpen(true);
40
+ };
41
+
36
42
  return (
37
43
  <CapRow className="preview-chrome">
38
44
  <div className="preview-header">
@@ -80,6 +86,13 @@ const PreviewHeader = ({
80
86
  )}
81
87
  </CapRow>
82
88
  )}
89
+ {isWebPushChannel && (
90
+ <CapIcon
91
+ type="expander"
92
+ className={device === MOBILE ? ACTIVE : ''}
93
+ onClick={() => handleOpenFullscreen()}
94
+ />
95
+ )}
83
96
  </div>
84
97
  </CapRow>
85
98
  );
@@ -95,6 +108,8 @@ PreviewHeader.propTypes = {
95
108
  showDeviceToggle: PropTypes.bool,
96
109
  onDeviceChange: PropTypes.func,
97
110
  channel: PropTypes.string,
111
+ isFullscreenOpen: PropTypes.bool,
112
+ setIsFullscreenOpen: PropTypes.func,
98
113
  };
99
114
 
100
115
  PreviewHeader.defaultProps = {
@@ -103,6 +118,8 @@ PreviewHeader.defaultProps = {
103
118
  showDeviceToggle: false,
104
119
  onDeviceChange: () => {},
105
120
  channel: null,
121
+ isFullscreenOpen: () => {},
122
+ setIsFullscreenOpen: () => false,
106
123
  };
107
124
 
108
125
  export default PreviewHeader;
@@ -14,14 +14,7 @@ import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
14
14
  import CapImage from '@capillarytech/cap-ui-library/CapImage';
15
15
  import CapTooltip from '@capillarytech/cap-ui-library/CapTooltip';
16
16
  import CapRow from '@capillarytech/cap-ui-library/CapRow';
17
- import {
18
- ANDROID,
19
- IOS,
20
- ANDROID_DEVICE_NAME,
21
- IOS_DEVICE_NAME,
22
- VIBER_ACCOUNT_NAME,
23
- MEDIA_TYPE_CAROUSEL,
24
- } from '../constants';
17
+ import { ANDROID, IOS, ANDROID_DEVICE_NAME, IOS_DEVICE_NAME, VIBER_ACCOUNT_NAME } from '../constants';
25
18
  import messages from '../messages';
26
19
  import videoPlay from '../../../assets/videoPlay.svg';
27
20
 
@@ -29,9 +22,6 @@ import videoPlay from '../../../assets/videoPlay.svg';
29
22
  const smsMobileAndroid = require('../../../assets/Android.png');
30
23
  const smsMobileIos = require('../../../assets/iOS.png');
31
24
 
32
- const getTrimmedText = (value = '') => (value ?? '').trim();
33
- const hasTrimmedText = (value = '') => Boolean(getTrimmedText(value));
34
-
35
25
  const ViberPreviewContent = ({
36
26
  content,
37
27
  device,
@@ -53,34 +43,7 @@ const ViberPreviewContent = ({
53
43
  imageURL = '',
54
44
  videoParams = {},
55
45
  buttonText = '',
56
- type = '',
57
- cards = [],
58
- showCarouselEditorPreview = false,
59
46
  } = viberContent;
60
- const hasCarouselContent = type === MEDIA_TYPE_CAROUSEL;
61
-
62
- const cardHasMeaningfulContent = (card) => {
63
- if (!card || typeof card !== 'object') return false;
64
- if (hasTrimmedText(card?.text)) return true;
65
- if (hasTrimmedText(card?.mediaUrl)) return true;
66
- const buttons = card?.buttons ?? [];
67
- return buttons.some((button) => hasTrimmedText(button?.title));
68
- };
69
-
70
- const hasMeaningfulCarousel =
71
- hasCarouselContent &&
72
- Array.isArray(cards) &&
73
- cards.length > 0 &&
74
- cards.some(cardHasMeaningfulContent);
75
-
76
- const showCarouselInPreview =
77
- hasCarouselContent && (Boolean(showCarouselEditorPreview) || hasMeaningfulCarousel);
78
-
79
- const previewCarouselCards =
80
- hasCarouselContent && Array.isArray(cards) && cards.length ? cards : hasCarouselContent ? [{}] : [];
81
-
82
- const trimmedMessageContent = getTrimmedText(messageContent);
83
- const trimmedButtonText = getTrimmedText(buttonText);
84
47
 
85
48
  // Get account name (first letter for icon)
86
49
  const accountIcon = (accountName || brandName || 'V')[0]?.toUpperCase();
@@ -136,15 +99,8 @@ const ViberPreviewContent = ({
136
99
  );
137
100
  }
138
101
 
139
- // Check if there's any content to display (whitespace-only strings do not count)
140
- const hasContent = Boolean(
141
- trimmedMessageContent
142
- || hasTrimmedText(imageURL)
143
- || videoParams?.viberVideoSrc
144
- || trimmedButtonText
145
- || hasMeaningfulCarousel
146
- || (hasCarouselContent && showCarouselEditorPreview)
147
- );
102
+ // Check if there's any content to display
103
+ const hasContent = messageContent || imageURL || videoParams?.viberVideoSrc || buttonText;
148
104
 
149
105
  // Render normal Viber preview
150
106
  return (
@@ -171,23 +127,21 @@ const ViberPreviewContent = ({
171
127
  {hasContent && (
172
128
  <CapRow className={`viber-message-container ${device !== ANDROID ? 'viber-message-container-ios' : ''}`}>
173
129
  {/* Brand Name Display (from TemplatePreview line 1136) */}
174
- <CapRow className={`msg-container viber-preview ${showCarouselInPreview ? 'viber-preview-carousel' : ''}`}>
130
+ <CapRow className="msg-container viber-preview">
175
131
  {/* Account Icon (from TemplatePreview line 1146-1160) */}
176
- {!hasCarouselContent && (
177
- <CapRow className="viber-account-icon">
178
- {accountIcon}
179
- </CapRow>
180
- )}
132
+ <CapRow className="viber-account-icon">
133
+ {accountIcon}
134
+ </CapRow>
181
135
 
182
136
  {/* Message Bubble (from TemplatePreview line 1161-1223) */}
183
- <CapRow className={`message-pop align-left viber-message-pop ${showCarouselInPreview ? 'viber-message-pop-carousel' : ''}`}>
137
+ <CapRow className="message-pop align-left viber-message-pop">
184
138
  {/* Text Viber preview */}
185
- {trimmedMessageContent && !hasCarouselContent && (
139
+ {messageContent && (
186
140
  <CapLabel type="label15" className="viber-message-text">{messageContent}</CapLabel>
187
141
  )}
188
142
 
189
143
  {/* Image Viber preview */}
190
- {hasTrimmedText(imageURL) && (
144
+ {imageURL && (
191
145
  <CapImage
192
146
  src={imageURL}
193
147
  className="viber-image-preview"
@@ -217,78 +171,16 @@ const ViberPreviewContent = ({
217
171
  )}
218
172
 
219
173
  {/* Button Viber preview */}
220
- {trimmedButtonText && (
174
+ {buttonText && (
221
175
  <CapLabel className="viber-button-base">
222
176
  <p className="viber-button-card-text">
223
177
  {buttonText}
224
178
  </p>
225
179
  </CapLabel>
226
180
  )}
227
- {/* Carousel Viber preview */}
228
- {showCarouselInPreview && (
229
- <>
230
- <CapRow className="viber-carousel-message-pop">
231
- {trimmedMessageContent ? (
232
- <CapLabel
233
- type="label15"
234
- className="message-pop-item align-left viber-message-text viber-carousel-message-box-text"
235
- >
236
- {messageContent}
237
- </CapLabel>
238
- ) : (
239
- <CapRow className="viber-carousel-message-box-placeholder" />
240
- )}
241
- <CapLabel type="label1" className="viber-carousel-message-timestamp">
242
- {timestamp}
243
- </CapLabel>
244
- </CapRow>
245
-
246
- <CapRow className="viber-carousel-cards-pop">
247
- <CapRow className="viber-carousel-preview-scroll">
248
- {previewCarouselCards?.map((card, index) => (
249
- <CapRow className="viber-carousel-preview-card" key={`viber-carousel-preview-card-${index}`}>
250
- {hasTrimmedText(card?.mediaUrl) ? (
251
- <CapImage
252
- src={card?.mediaUrl}
253
- className="viber-carousel-preview-image"
254
- alt="Viber carousel card"
255
- />
256
- ) : (
257
- <CapRow className="viber-carousel-preview-image-placeholder" />
258
- )}
259
- <CapRow className="viber-carousel-preview-card-body">
260
- {hasTrimmedText(card?.text) ? (
261
- <CapLabel type="label15" className="viber-carousel-preview-text">
262
- {card?.text}
263
- </CapLabel>
264
- ) : (
265
- <CapLabel type="label15" className="viber-carousel-preview-text-placeholder" />
266
- )}
267
- {(card?.buttons?.filter((cardButton) => hasTrimmedText(cardButton?.title)) ?? [])
268
- .slice(0, 2)
269
- .map((cardButton, btnIndex) => {
270
- const trimmedCardButtonTitle = getTrimmedText(cardButton?.title);
271
- return (
272
- <CapLabel
273
- className={`viber-carousel-preview-button ${btnIndex === 1 ? 'viber-carousel-preview-button-secondary' : ''}`}
274
- key={`viber-carousel-preview-btn-${index}-${btnIndex}-${trimmedCardButtonTitle}`}
275
- >
276
- {trimmedCardButtonTitle}
277
- </CapLabel>
278
- );
279
- })}
280
- </CapRow>
281
- </CapRow>
282
- ))}
283
- </CapRow>
284
- </CapRow>
285
- </>
286
- )}
287
- {!showCarouselInPreview && (
288
- <CapLabel type="label1" className="viber-timestamp">
289
- {timestamp}
290
- </CapLabel>
291
- )}
181
+ <CapLabel type="label1" className="viber-timestamp">
182
+ {timestamp}
183
+ </CapLabel>
292
184
  <CapRow className="empty-placeholder" />
293
185
  </CapRow>
294
186
 
@@ -322,16 +214,6 @@ ViberPreviewContent.propTypes = {
322
214
  viberVideoPreviewImg: PropTypes.string,
323
215
  }),
324
216
  buttonText: PropTypes.string,
325
- type: PropTypes.string,
326
- cards: PropTypes.arrayOf(PropTypes.shape({
327
- text: PropTypes.string,
328
- mediaUrl: PropTypes.string,
329
- buttons: PropTypes.arrayOf(PropTypes.shape({
330
- title: PropTypes.string,
331
- action: PropTypes.string,
332
- })),
333
- })),
334
- showCarouselEditorPreview: PropTypes.bool,
335
217
  }),
336
218
  }),
337
219
  device: PropTypes.oneOf([ANDROID, IOS]),
@@ -0,0 +1,169 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { FormattedMessage } from 'react-intl';
4
+ import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
5
+ import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
6
+ import CapModal from '@capillarytech/cap-ui-library/CapModal';
7
+ import CapRow from '@capillarytech/cap-ui-library/CapRow';
8
+ import CapDivider from '@capillarytech/cap-ui-library/CapDivider';
9
+ import PreviewControls from '../../../v2Containers/WebPush/Create/preview/PreviewControls';
10
+ import PreviewContent from '../../../v2Containers/WebPush/Create/preview/PreviewContent';
11
+ import DevicePreviewContent from '../../../v2Containers/WebPush/Create/preview/DevicePreviewContent';
12
+ import {
13
+ OS_OPTIONS,
14
+ DEFAULT_OS,
15
+ DEFAULT_BROWSER,
16
+ } from '../../../v2Containers/WebPush/Create/preview/constants';
17
+ import { getBrowserOptionsForOS } from '../../../v2Containers/WebPush/Create/preview/config/notificationMappings';
18
+ import messages from '../messages';
19
+
20
+ const WebPushPreviewContent = ({
21
+ notificationTitle,
22
+ notificationBody,
23
+ imageSrc,
24
+ brandIconSrc,
25
+ buttons,
26
+ url,
27
+ isUpdating,
28
+ error,
29
+ isFullscreenOpen,
30
+ setIsFullscreenOpen,
31
+ selectedCustomer,
32
+ }) => {
33
+ const [selectedOS, setSelectedOS] = useState(DEFAULT_OS);
34
+ const [selectedBrowser, setSelectedBrowser] = useState(DEFAULT_BROWSER);
35
+
36
+ const browserOptionsForOS = getBrowserOptionsForOS(selectedOS);
37
+
38
+ // Coerce browser when OS change removes current browser from available options
39
+ useEffect(() => {
40
+ const isValid = browserOptionsForOS.some((opt) => opt.value === selectedBrowser);
41
+ if (!isValid && browserOptionsForOS.length) {
42
+ setSelectedBrowser(browserOptionsForOS[0].value);
43
+ }
44
+ }, [browserOptionsForOS, selectedBrowser]);
45
+
46
+ const handleOSChange = (value) => {
47
+ setSelectedOS(value);
48
+ };
49
+
50
+ const handleBrowserChange = (value) => {
51
+ setSelectedBrowser(value);
52
+ };
53
+
54
+ const handleCloseFullscreen = () => {
55
+ setIsFullscreenOpen(false);
56
+ };
57
+
58
+ if (isUpdating) {
59
+ return null;
60
+ }
61
+
62
+ if (error) {
63
+ return null;
64
+ }
65
+
66
+ return (
67
+ <CapRow className="webpush-test-preview-container webpush-preview-container">
68
+ <div className="webpush-preview-panel">
69
+ <PreviewControls
70
+ selectedOS={selectedOS}
71
+ selectedBrowser={selectedBrowser}
72
+ osOptions={OS_OPTIONS}
73
+ browserOptions={browserOptionsForOS}
74
+ onOSChange={handleOSChange}
75
+ onBrowserChange={handleBrowserChange}
76
+ layoutMode="newRow"
77
+ showStateDropdown={false}
78
+ />
79
+
80
+ <PreviewContent
81
+ notificationTitle={notificationTitle}
82
+ notificationBody={notificationBody}
83
+ url={url}
84
+ selectedOS={selectedOS}
85
+ selectedBrowser={selectedBrowser}
86
+ notificationState="Collapsed"
87
+ imageSrc={imageSrc}
88
+ brandIconSrc={brandIconSrc}
89
+ buttons={buttons}
90
+ />
91
+ </div>
92
+
93
+ <CapModal
94
+ visible={isFullscreenOpen}
95
+ onCancel={handleCloseFullscreen}
96
+ width="90%"
97
+ centered
98
+ closable={false}
99
+ footer={null}
100
+ title={null}
101
+ maskClosable
102
+ className="webpush-fullscreen-modal webpush-preview-container"
103
+ >
104
+ <CapRow className="webpush-preview-header">
105
+ <div className="webpush-heading-container">
106
+ <CapRow type="flex" className="preview-for">
107
+ <CapLabel type="label16">
108
+ <FormattedMessage {...messages.previewFor} />
109
+ </CapLabel>
110
+ <CapLabel type="label2">
111
+ {selectedCustomer ? selectedCustomer.name : <FormattedMessage {...messages.defaultPreview} />}
112
+ </CapLabel>
113
+ </CapRow>
114
+ <CapIcon
115
+ type="collapse2"
116
+ onClick={() => handleCloseFullscreen()}
117
+ className="webpush-fullscreen-close-icon"
118
+ />
119
+ </div>
120
+ </CapRow>
121
+ <CapDivider className="webpush-fullscreen-divider" />
122
+ <DevicePreviewContent
123
+ notificationTitle={notificationTitle}
124
+ notificationBody={notificationBody}
125
+ url={url}
126
+ imageSrc={imageSrc}
127
+ brandIconSrc={brandIconSrc}
128
+ buttons={buttons}
129
+ />
130
+ </CapModal>
131
+ </CapRow>
132
+ );
133
+ };
134
+
135
+ WebPushPreviewContent.propTypes = {
136
+ notificationTitle: PropTypes.string,
137
+ notificationBody: PropTypes.string,
138
+ imageSrc: PropTypes.string,
139
+ brandIconSrc: PropTypes.string,
140
+ buttons: PropTypes.arrayOf(
141
+ PropTypes.shape({
142
+ text: PropTypes.string,
143
+ url: PropTypes.string,
144
+ type: PropTypes.string,
145
+ })
146
+ ),
147
+ url: PropTypes.string,
148
+ isUpdating: PropTypes.bool,
149
+ error: PropTypes.string,
150
+ isFullscreenOpen: PropTypes.bool,
151
+ setIsFullscreenOpen: PropTypes.func,
152
+ selectedCustomer: PropTypes.object,
153
+ };
154
+
155
+ WebPushPreviewContent.defaultProps = {
156
+ notificationTitle: '',
157
+ notificationBody: '',
158
+ imageSrc: '',
159
+ brandIconSrc: '',
160
+ buttons: [],
161
+ url: '',
162
+ isUpdating: false,
163
+ error: null,
164
+ isFullscreenOpen: false,
165
+ setIsFullscreenOpen: () => {},
166
+ selectedCustomer: null,
167
+ };
168
+
169
+ export default WebPushPreviewContent;