@capillarytech/creatives-library 8.0.353-alpha.2 → 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.
- package/package.json +1 -1
- package/v2Components/CommonTestAndPreview/UnifiedPreview/PreviewHeader.js +17 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/WebPushPreviewContent.js +169 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +70 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +44 -5
- package/v2Components/CommonTestAndPreview/constants.js +2 -0
- package/v2Components/CommonTestAndPreview/index.js +51 -2
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/PreviewHeader.test.js +159 -0
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/WebPushPreviewContent.test.js +522 -0
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +255 -0
- package/v2Components/CommonTestAndPreview/tests/constants.test.js +2 -1
- package/v2Components/CommonTestAndPreview/tests/index.test.js +194 -0
- package/v2Components/FormBuilder/index.js +10 -48
- package/v2Components/TestAndPreviewSlidebox/index.js +2 -2
- package/v2Containers/App/constants.js +3 -0
- package/v2Containers/App/tests/constants.test.js +61 -0
- package/v2Containers/CreativesContainer/index.js +25 -61
- package/v2Containers/Email/index.js +2 -33
- package/v2Containers/Templates/index.js +68 -2
- package/v2Containers/Templates/tests/webpush.test.js +375 -0
- package/v2Containers/WebPush/Create/index.js +91 -8
- package/v2Containers/WebPush/Create/index.scss +7 -0
- package/v2Containers/WebPush/Create/tests/getTemplateContent.test.js +338 -0
- package/v2Containers/WebPush/Create/tests/testAndPreviewIntegration.test.js +325 -0
package/package.json
CHANGED
|
@@ -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;
|
|
@@ -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;
|
|
@@ -2350,6 +2350,76 @@
|
|
|
2350
2350
|
}
|
|
2351
2351
|
}
|
|
2352
2352
|
|
|
2353
|
+
// WebPush Test & Preview Styles
|
|
2354
|
+
.webpush-test-preview-container {
|
|
2355
|
+
width: 100%;
|
|
2356
|
+
position: relative;
|
|
2357
|
+
padding-left: $CAP_SPACE_16;
|
|
2358
|
+
padding-right: $CAP_SPACE_16;
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2361
|
+
.webpush-preview-panel {
|
|
2362
|
+
position: relative;
|
|
2363
|
+
width: 100%;
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
.webpush-expand-btn {
|
|
2367
|
+
position: absolute;
|
|
2368
|
+
top: 8px;
|
|
2369
|
+
right: 8px;
|
|
2370
|
+
z-index: 10;
|
|
2371
|
+
padding: 4px;
|
|
2372
|
+
display: flex;
|
|
2373
|
+
align-items: center;
|
|
2374
|
+
justify-content: center;
|
|
2375
|
+
|
|
2376
|
+
&:hover {
|
|
2377
|
+
background-color: rgba(0, 0, 0, 0.06);
|
|
2378
|
+
border-radius: 4px;
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
.webpush-fullscreen-modal {
|
|
2383
|
+
.webpush-fullscreen-divider {
|
|
2384
|
+
margin-top: 0;
|
|
2385
|
+
margin-bottom: $CAP_SPACE_16;
|
|
2386
|
+
}
|
|
2387
|
+
.ant-modal.cap-modal-v2 {
|
|
2388
|
+
width: 90%;
|
|
2389
|
+
max-width: 100%;
|
|
2390
|
+
margin-top: $CAP_SPACE_40;
|
|
2391
|
+
}
|
|
2392
|
+
// Preview Chrome wrapper (matches old TestAndPreviewSlidebox design)
|
|
2393
|
+
.webpush-preview-header {
|
|
2394
|
+
background: $CAP_WHITE;
|
|
2395
|
+
overflow: hidden;
|
|
2396
|
+
|
|
2397
|
+
.preview-divider {
|
|
2398
|
+
margin: 0;
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2401
|
+
.webpush-heading-container {
|
|
2402
|
+
display: flex;
|
|
2403
|
+
justify-content: space-between;
|
|
2404
|
+
align-items: center;
|
|
2405
|
+
padding: $CAP_SPACE_16 0 $CAP_SPACE_16 0;
|
|
2406
|
+
|
|
2407
|
+
.preview-for {
|
|
2408
|
+
gap: $CAP_SPACE_04;
|
|
2409
|
+
align-items: center;
|
|
2410
|
+
b {
|
|
2411
|
+
margin-left: $CAP_SPACE_08;
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2415
|
+
.webpush-fullscreen-close-icon {
|
|
2416
|
+
width: $CAP_SPACE_24;
|
|
2417
|
+
height: $CAP_SPACE_24;
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2353
2423
|
// Responsive adjustments
|
|
2354
2424
|
@media (max-width: 85.714rem) {
|
|
2355
2425
|
.unified-preview {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Routes to channel-specific preview content components
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import React from 'react';
|
|
9
|
+
import React, { useState } from 'react';
|
|
10
10
|
import PropTypes from 'prop-types';
|
|
11
11
|
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
12
12
|
import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
|
|
@@ -21,6 +21,7 @@ import InAppPreviewContent from './InAppPreviewContent';
|
|
|
21
21
|
import MobilePushPreviewContent from './MobilePushPreviewContent';
|
|
22
22
|
import ViberPreviewContent from './ViberPreviewContent';
|
|
23
23
|
import ZaloPreviewContent from './ZaloPreviewContent';
|
|
24
|
+
import WebPushPreviewContent from './WebPushPreviewContent';
|
|
24
25
|
import { CHANNELS, DESKTOP, TABLET, MOBILE, ANDROID, IOS } from '../constants';
|
|
25
26
|
import messages from '../messages';
|
|
26
27
|
import './_unifiedPreview.scss';
|
|
@@ -44,6 +45,7 @@ const UnifiedPreview = ({
|
|
|
44
45
|
* For Phase 5, we'll render placeholders
|
|
45
46
|
* Phase 6+ will implement actual content components
|
|
46
47
|
*/
|
|
48
|
+
const [isFullscreenOpen, setIsFullscreenOpen] = useState(false);
|
|
47
49
|
const renderChannelContent = () => {
|
|
48
50
|
// Phase 5: Placeholder content for all channels
|
|
49
51
|
// Phase 6+: Import and render actual channel-specific components
|
|
@@ -197,6 +199,40 @@ const UnifiedPreview = ({
|
|
|
197
199
|
);
|
|
198
200
|
}
|
|
199
201
|
|
|
202
|
+
case CHANNELS.WEBPUSH: {
|
|
203
|
+
// WebPush content arrives as a JSON string (from getCurrentContent or preview API response)
|
|
204
|
+
let webPushOuter = {};
|
|
205
|
+
try {
|
|
206
|
+
webPushOuter = typeof content === 'string' ? JSON.parse(content) : (typeof content === 'object' && content !== null ? content : {});
|
|
207
|
+
} catch (e) {
|
|
208
|
+
webPushOuter = {};
|
|
209
|
+
}
|
|
210
|
+
const webPushInner = webPushOuter?.content || {};
|
|
211
|
+
const webPushExpandable = webPushInner?.expandableDetails || {};
|
|
212
|
+
const webPushMedia = webPushExpandable?.media || [];
|
|
213
|
+
const webPushCtas = webPushExpandable?.ctas || [];
|
|
214
|
+
const webPushButtons = webPushCtas.map((cta) => ({
|
|
215
|
+
text: cta?.title || '',
|
|
216
|
+
url: cta?.actionLink || '',
|
|
217
|
+
type: cta?.type || '',
|
|
218
|
+
}));
|
|
219
|
+
return (
|
|
220
|
+
<WebPushPreviewContent
|
|
221
|
+
notificationTitle={webPushInner?.title || ''}
|
|
222
|
+
notificationBody={webPushInner?.message || ''}
|
|
223
|
+
imageSrc={webPushMedia[0]?.url || ''}
|
|
224
|
+
brandIconSrc={webPushInner?.iconImageUrl || ''}
|
|
225
|
+
buttons={webPushButtons}
|
|
226
|
+
url={webPushInner?.cta?.actionLink || ''}
|
|
227
|
+
isUpdating={isUpdating}
|
|
228
|
+
error={error}
|
|
229
|
+
isFullscreenOpen={isFullscreenOpen}
|
|
230
|
+
setIsFullscreenOpen={setIsFullscreenOpen}
|
|
231
|
+
selectedCustomer={selectedCustomer}
|
|
232
|
+
/>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
200
236
|
default:
|
|
201
237
|
return (
|
|
202
238
|
<div className="channel-preview-placeholder">
|
|
@@ -222,9 +258,10 @@ const UnifiedPreview = ({
|
|
|
222
258
|
<PreviewHeader
|
|
223
259
|
selectedCustomer={selectedCustomer}
|
|
224
260
|
device={device}
|
|
225
|
-
showDeviceToggle={showDeviceToggle}
|
|
261
|
+
showDeviceToggle={channel !== CHANNELS.WEBPUSH && showDeviceToggle}
|
|
226
262
|
onDeviceChange={onDeviceChange}
|
|
227
263
|
channel={channel}
|
|
264
|
+
setIsFullscreenOpen={setIsFullscreenOpen}
|
|
228
265
|
/>
|
|
229
266
|
)}
|
|
230
267
|
<CapRow className="preview-loading-container">
|
|
@@ -247,9 +284,10 @@ const UnifiedPreview = ({
|
|
|
247
284
|
<PreviewHeader
|
|
248
285
|
selectedCustomer={selectedCustomer}
|
|
249
286
|
device={device}
|
|
250
|
-
showDeviceToggle={showDeviceToggle}
|
|
287
|
+
showDeviceToggle={channel !== CHANNELS.WEBPUSH && showDeviceToggle}
|
|
251
288
|
onDeviceChange={onDeviceChange}
|
|
252
289
|
channel={channel}
|
|
290
|
+
setIsFullscreenOpen={setIsFullscreenOpen}
|
|
253
291
|
/>
|
|
254
292
|
)}
|
|
255
293
|
<CapRow className="preview-error-container">
|
|
@@ -275,15 +313,16 @@ const UnifiedPreview = ({
|
|
|
275
313
|
<PreviewHeader
|
|
276
314
|
selectedCustomer={selectedCustomer}
|
|
277
315
|
device={device}
|
|
278
|
-
showDeviceToggle={showDeviceToggle}
|
|
316
|
+
showDeviceToggle={channel !== CHANNELS.WEBPUSH && showDeviceToggle}
|
|
279
317
|
onDeviceChange={onDeviceChange}
|
|
280
318
|
channel={channel}
|
|
319
|
+
setIsFullscreenOpen={setIsFullscreenOpen}
|
|
281
320
|
/>
|
|
282
321
|
)}
|
|
283
322
|
|
|
284
323
|
{/* Channel-specific preview content */}
|
|
285
324
|
<CapRow className={`preview-content-container ${!showHeader ? 'preview-content-container-no-header' : ''}`}>
|
|
286
|
-
{[CHANNELS.EMAIL, CHANNELS.SMS, CHANNELS.WHATSAPP, CHANNELS.RCS, CHANNELS.INAPP, CHANNELS.MOBILEPUSH, CHANNELS.VIBER].includes(channel) ? (
|
|
325
|
+
{[CHANNELS.EMAIL, CHANNELS.SMS, CHANNELS.WHATSAPP, CHANNELS.RCS, CHANNELS.INAPP, CHANNELS.MOBILEPUSH, CHANNELS.VIBER, CHANNELS.WEBPUSH].includes(channel) ? (
|
|
287
326
|
renderChannelContent()
|
|
288
327
|
) : (
|
|
289
328
|
<DeviceFrame device={device || DESKTOP}>
|
|
@@ -85,6 +85,7 @@ export const CHANNELS = {
|
|
|
85
85
|
MOBILEPUSH: 'MOBILEPUSH',
|
|
86
86
|
VIBER: 'VIBER',
|
|
87
87
|
ZALO: 'ZALO',
|
|
88
|
+
WEBPUSH: 'WEBPUSH',
|
|
88
89
|
};
|
|
89
90
|
export const CHANNELS_USING_ANDROID_PREVIEW_DEVICE = [CHANNELS.SMS, CHANNELS.RCS, CHANNELS.WHATSAPP, CHANNELS.VIBER, CHANNELS.ZALO, CHANNELS.INAPP, CHANNELS.MOBILEPUSH];
|
|
90
91
|
|
|
@@ -203,6 +204,7 @@ export const DYNAMIC_URL = 'DYNAMIC_URL';
|
|
|
203
204
|
export const IMAGE = 'IMAGE';
|
|
204
205
|
export const VIDEO = 'VIDEO';
|
|
205
206
|
export const URL = 'URL';
|
|
207
|
+
export const DAYS = 'DAYS';
|
|
206
208
|
|
|
207
209
|
// Initial Payload Template (for reference)
|
|
208
210
|
export const INITIAL_PAYLOAD = {
|
|
@@ -81,7 +81,8 @@ import {
|
|
|
81
81
|
IMAGE,
|
|
82
82
|
VIDEO,
|
|
83
83
|
URL,
|
|
84
|
-
CHANNELS_USING_ANDROID_PREVIEW_DEVICE
|
|
84
|
+
CHANNELS_USING_ANDROID_PREVIEW_DEVICE,
|
|
85
|
+
DAYS,
|
|
85
86
|
} from './constants';
|
|
86
87
|
|
|
87
88
|
// Import utilities
|
|
@@ -610,6 +611,12 @@ const CommonTestAndPreview = (props) => {
|
|
|
610
611
|
messageBody: contentStr,
|
|
611
612
|
};
|
|
612
613
|
|
|
614
|
+
case CHANNELS.WEBPUSH:
|
|
615
|
+
return {
|
|
616
|
+
...basePayload,
|
|
617
|
+
messageBody: contentStr,
|
|
618
|
+
};
|
|
619
|
+
|
|
613
620
|
default:
|
|
614
621
|
return basePayload;
|
|
615
622
|
}
|
|
@@ -662,6 +669,12 @@ const CommonTestAndPreview = (props) => {
|
|
|
662
669
|
templateContent: contentStr,
|
|
663
670
|
};
|
|
664
671
|
|
|
672
|
+
case CHANNELS.WEBPUSH:
|
|
673
|
+
return {
|
|
674
|
+
templateSubject: formDataObj?.content?.title || '',
|
|
675
|
+
templateContent: contentStr,
|
|
676
|
+
};
|
|
677
|
+
|
|
665
678
|
case CHANNELS.ZALO: {
|
|
666
679
|
// For Zalo, extract content from templateListParams array
|
|
667
680
|
// Combine all variable values into a single string for tag extraction
|
|
@@ -1801,6 +1814,42 @@ const CommonTestAndPreview = (props) => {
|
|
|
1801
1814
|
};
|
|
1802
1815
|
}
|
|
1803
1816
|
|
|
1817
|
+
case CHANNELS.WEBPUSH: {
|
|
1818
|
+
const webpushData = (typeof formDataObj === 'object' && formDataObj !== null)
|
|
1819
|
+
? formDataObj
|
|
1820
|
+
: {};
|
|
1821
|
+
const innerContent = webpushData?.content || {};
|
|
1822
|
+
|
|
1823
|
+
const resolvedTitle = resolveTagsInText(innerContent?.title || '', customValuesObj);
|
|
1824
|
+
const resolvedMessage = resolveTagsInText(innerContent?.message || '', customValuesObj);
|
|
1825
|
+
|
|
1826
|
+
return {
|
|
1827
|
+
...basePayload,
|
|
1828
|
+
webPushMessageContent: {
|
|
1829
|
+
channel: CHANNELS.WEBPUSH,
|
|
1830
|
+
accountId: webpushData?.accountId || null,
|
|
1831
|
+
content: {
|
|
1832
|
+
title: resolvedTitle,
|
|
1833
|
+
message: resolvedMessage,
|
|
1834
|
+
...(innerContent?.iconImageUrl && { iconImageUrl: innerContent.iconImageUrl }),
|
|
1835
|
+
...(innerContent?.cta && { cta: innerContent.cta }),
|
|
1836
|
+
...(innerContent?.expandableDetails && { expandableDetails: innerContent.expandableDetails }),
|
|
1837
|
+
},
|
|
1838
|
+
messageSubject: webpushData?.messageSubject || resolvedTitle || '',
|
|
1839
|
+
},
|
|
1840
|
+
webPushDeliverySettings: {
|
|
1841
|
+
channelSettings: {
|
|
1842
|
+
channel: CHANNELS.WEBPUSH,
|
|
1843
|
+
notificationTtl: {
|
|
1844
|
+
duration: 7,
|
|
1845
|
+
timeUnit: DAYS,
|
|
1846
|
+
},
|
|
1847
|
+
},
|
|
1848
|
+
additionalSettings: {},
|
|
1849
|
+
},
|
|
1850
|
+
};
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1804
1853
|
default:
|
|
1805
1854
|
return basePayload;
|
|
1806
1855
|
}
|
|
@@ -1832,7 +1881,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
1832
1881
|
contentObj = hasPreviewCallBeenMade && previewDataHtml?.resolvedBody
|
|
1833
1882
|
? previewDataHtml.resolvedBody
|
|
1834
1883
|
: getCurrentContent || '';
|
|
1835
|
-
} else if (channel === CHANNELS.WHATSAPP) {
|
|
1884
|
+
} else if (channel === CHANNELS.WHATSAPP || channel === CHANNELS.WEBPUSH) {
|
|
1836
1885
|
// For WhatsApp, content is an object with templateMsg, media, CTA, etc.
|
|
1837
1886
|
// Content comes from WhatsApp component state, passed via content prop
|
|
1838
1887
|
let resolvedContent = null;
|
|
@@ -514,4 +514,163 @@ describe('PreviewHeader', () => {
|
|
|
514
514
|
consoleSpy.mockRestore();
|
|
515
515
|
});
|
|
516
516
|
});
|
|
517
|
+
|
|
518
|
+
describe('WEBPUSH channel - Fullscreen expander', () => {
|
|
519
|
+
it('should render expander icon for WEBPUSH channel', () => {
|
|
520
|
+
const setIsFullscreenOpen = jest.fn();
|
|
521
|
+
const props = {
|
|
522
|
+
...defaultProps,
|
|
523
|
+
channel: CHANNELS.WEBPUSH,
|
|
524
|
+
setIsFullscreenOpen,
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
const { container } = render(
|
|
528
|
+
<TestWrapper>
|
|
529
|
+
<PreviewHeader {...props} />
|
|
530
|
+
</TestWrapper>
|
|
531
|
+
);
|
|
532
|
+
|
|
533
|
+
// expander icon should be present for WEBPUSH
|
|
534
|
+
const expanderIcon = container.querySelector('[class*="expander"], [aria-label*="expander"], .anticon');
|
|
535
|
+
expect(expanderIcon).toBeTruthy();
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
it('should NOT render expander icon for non-WEBPUSH channels', () => {
|
|
539
|
+
const setIsFullscreenOpen = jest.fn();
|
|
540
|
+
const props = {
|
|
541
|
+
...defaultProps,
|
|
542
|
+
channel: CHANNELS.EMAIL,
|
|
543
|
+
setIsFullscreenOpen,
|
|
544
|
+
showDeviceToggle: false,
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
const { container } = render(
|
|
548
|
+
<TestWrapper>
|
|
549
|
+
<PreviewHeader {...props} />
|
|
550
|
+
</TestWrapper>
|
|
551
|
+
);
|
|
552
|
+
|
|
553
|
+
// No expander icon for EMAIL channel (only device toggle icons may appear)
|
|
554
|
+
const expanderIcon = container.querySelector('[class*="expander"]');
|
|
555
|
+
expect(expanderIcon).toBeNull();
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
it('should call setIsFullscreenOpen(true) when expander icon is clicked', () => {
|
|
559
|
+
const setIsFullscreenOpen = jest.fn();
|
|
560
|
+
const props = {
|
|
561
|
+
...defaultProps,
|
|
562
|
+
channel: CHANNELS.WEBPUSH,
|
|
563
|
+
setIsFullscreenOpen,
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
const { container } = render(
|
|
567
|
+
<TestWrapper>
|
|
568
|
+
<PreviewHeader {...props} />
|
|
569
|
+
</TestWrapper>
|
|
570
|
+
);
|
|
571
|
+
|
|
572
|
+
const expanderIcon = container.querySelector('[class*="expander"]') ||
|
|
573
|
+
container.querySelector('.anticon');
|
|
574
|
+
if (expanderIcon) {
|
|
575
|
+
fireEvent.click(expanderIcon);
|
|
576
|
+
expect(setIsFullscreenOpen).toHaveBeenCalledWith(true);
|
|
577
|
+
}
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
it('should render expander icon with ACTIVE class when device is MOBILE', () => {
|
|
581
|
+
const setIsFullscreenOpen = jest.fn();
|
|
582
|
+
const props = {
|
|
583
|
+
...defaultProps,
|
|
584
|
+
channel: CHANNELS.WEBPUSH,
|
|
585
|
+
device: MOBILE,
|
|
586
|
+
setIsFullscreenOpen,
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
const { container } = render(
|
|
590
|
+
<TestWrapper>
|
|
591
|
+
<PreviewHeader {...props} />
|
|
592
|
+
</TestWrapper>
|
|
593
|
+
);
|
|
594
|
+
|
|
595
|
+
// MOBILE device should give ACTIVE class to expander
|
|
596
|
+
const activeEl = container.querySelector('.active');
|
|
597
|
+
expect(activeEl).toBeTruthy();
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
it('should render expander icon without ACTIVE class when device is DESKTOP', () => {
|
|
601
|
+
const setIsFullscreenOpen = jest.fn();
|
|
602
|
+
const props = {
|
|
603
|
+
...defaultProps,
|
|
604
|
+
channel: CHANNELS.WEBPUSH,
|
|
605
|
+
device: DESKTOP,
|
|
606
|
+
setIsFullscreenOpen,
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
const { container } = render(
|
|
610
|
+
<TestWrapper>
|
|
611
|
+
<PreviewHeader {...props} />
|
|
612
|
+
</TestWrapper>
|
|
613
|
+
);
|
|
614
|
+
|
|
615
|
+
// DESKTOP device should NOT give ACTIVE class to expander
|
|
616
|
+
const activeEl = container.querySelector('.active');
|
|
617
|
+
expect(activeEl).toBeNull();
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
it('should render expander icon alongside device content for WEBPUSH', () => {
|
|
621
|
+
// Note: suppression of showDeviceToggle for WEBPUSH is done in UnifiedPreview (parent),
|
|
622
|
+
// not in PreviewHeader itself. PreviewHeader only adds the expander icon for WEBPUSH.
|
|
623
|
+
const setIsFullscreenOpen = jest.fn();
|
|
624
|
+
const props = {
|
|
625
|
+
...defaultProps,
|
|
626
|
+
channel: CHANNELS.WEBPUSH,
|
|
627
|
+
setIsFullscreenOpen,
|
|
628
|
+
showDeviceToggle: false,
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
const { container } = render(
|
|
632
|
+
<TestWrapper>
|
|
633
|
+
<PreviewHeader {...props} />
|
|
634
|
+
</TestWrapper>
|
|
635
|
+
);
|
|
636
|
+
|
|
637
|
+
// expander icon should be present for WEBPUSH
|
|
638
|
+
const expanderIcon = container.querySelector('[class*="expander"]') ||
|
|
639
|
+
container.querySelector('.anticon');
|
|
640
|
+
expect(expanderIcon).toBeTruthy();
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
it('should accept isFullscreenOpen and setIsFullscreenOpen as props', () => {
|
|
644
|
+
const setIsFullscreenOpen = jest.fn();
|
|
645
|
+
const props = {
|
|
646
|
+
...defaultProps,
|
|
647
|
+
channel: CHANNELS.WEBPUSH,
|
|
648
|
+
isFullscreenOpen: false,
|
|
649
|
+
setIsFullscreenOpen,
|
|
650
|
+
};
|
|
651
|
+
|
|
652
|
+
expect(() =>
|
|
653
|
+
render(
|
|
654
|
+
<TestWrapper>
|
|
655
|
+
<PreviewHeader {...props} />
|
|
656
|
+
</TestWrapper>
|
|
657
|
+
)
|
|
658
|
+
).not.toThrow();
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
it('should use default setIsFullscreenOpen when not provided', () => {
|
|
662
|
+
const props = {
|
|
663
|
+
...defaultProps,
|
|
664
|
+
channel: CHANNELS.WEBPUSH,
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
expect(() =>
|
|
668
|
+
render(
|
|
669
|
+
<TestWrapper>
|
|
670
|
+
<PreviewHeader {...props} />
|
|
671
|
+
</TestWrapper>
|
|
672
|
+
)
|
|
673
|
+
).not.toThrow();
|
|
674
|
+
});
|
|
675
|
+
});
|
|
517
676
|
});
|