@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
@@ -123,151 +123,3 @@
123
123
  .test-and-preview-button {
124
124
  margin-left: 6.25rem;
125
125
  }
126
-
127
- .viber-remove-row-btn {
128
- margin-top: $CAP_SPACE_24;
129
- }
130
-
131
- .viber-add-row-btn {
132
- margin-top: $CAP_SPACE_08;
133
- color: $FONT_COLOR_05;
134
- background: transparent;
135
- border: none;
136
- box-shadow: none;
137
-
138
- &:hover,
139
- &:focus,
140
- &:active {
141
- color: $FONT_COLOR_05;
142
- background: transparent;
143
- border: none;
144
- }
145
- }
146
-
147
- .viber-carousel-section {
148
- margin-top: $CAP_SPACE_16;
149
- }
150
-
151
- .viber-carousel-card {
152
- border: 1px solid $CAP_G06;
153
- border-radius: $CAP_SPACE_04;
154
- padding: $CAP_SPACE_16;
155
- margin-bottom: $CAP_SPACE_16;
156
- }
157
-
158
- .viber-carousel-card-selector {
159
- margin-bottom: $CAP_SPACE_16;
160
- border-bottom: 1px solid $CAP_G07;
161
- }
162
-
163
- .viber-carousel-card-selector-btn {
164
- min-width: 2.5rem;
165
- padding: $CAP_SPACE_12 $CAP_SPACE_20;
166
- border: none;
167
- background: transparent;
168
- color: $FONT_COLOR_04;
169
- font-size: $CAP_SPACE_40;
170
- cursor: pointer;
171
- border-bottom: 3px solid transparent;
172
- }
173
-
174
- .viber-carousel-card-selector-btn.active {
175
- color: $FONT_COLOR_01;
176
- border-bottom-color: currentColor;
177
- }
178
-
179
- .viber-carousel-add-btn {
180
- border-left: 1px solid $CAP_G07;
181
- margin-left: $CAP_SPACE_16;
182
- padding-left: $CAP_SPACE_24;
183
- padding-right: $CAP_SPACE_24;
184
- }
185
-
186
- .viber-carousel-image-recommendation {
187
- display: block;
188
- margin: $CAP_SPACE_08 0 $CAP_SPACE_16 0;
189
- color: $FONT_COLOR_03;
190
- }
191
-
192
- .viber-carousel-card-title-header {
193
- align-items: center;
194
- }
195
-
196
- .viber-carousel-button {
197
- border-top: 1px dashed $CAP_G06;
198
- margin-top: $CAP_SPACE_12;
199
- padding-top: $CAP_SPACE_12;
200
- }
201
-
202
- .viber-carousel-button-label {
203
- margin-top: $CAP_SPACE_16;
204
- }
205
-
206
- .viber-carousel-button-title-header {
207
- align-items: center;
208
- }
209
-
210
- .viber-carousel-url-type-select {
211
- .ant-select-selection-selected-value {
212
- max-width: none;
213
- overflow: visible;
214
- text-overflow: unset;
215
- }
216
-
217
- .ant-select-selection__rendered {
218
- margin-right: $CAP_SPACE_24;
219
- }
220
- }
221
-
222
- .viber-carousel-url-type-dropdown.ant-select-dropdown {
223
- min-width: 12rem;
224
- }
225
-
226
- .viber-carousel-url-type-dropdown .ant-select-dropdown-menu-item {
227
- white-space: normal;
228
- word-break: break-word;
229
- height: auto;
230
- line-height: 1.25rem;
231
- padding-top: $CAP_SPACE_06;
232
- padding-bottom: $CAP_SPACE_06;
233
- }
234
-
235
- .viber-carousel-saved-button {
236
- display: flex;
237
- align-items: center;
238
- border: 1px solid $CAP_G06;
239
- border-radius: $CAP_SPACE_04;
240
- padding: $CAP_SPACE_12 $CAP_SPACE_16;
241
- margin-top: $CAP_SPACE_12;
242
- }
243
-
244
- .viber-carousel-saved-button-icon {
245
- margin-right: $CAP_SPACE_12;
246
- color: $FONT_COLOR_01;
247
- }
248
-
249
- .viber-carousel-saved-button-text {
250
- flex: 1;
251
- margin-right: $CAP_SPACE_12;
252
- }
253
-
254
- .viber-carousel-delete-icon-btn {
255
- margin-left: $CAP_SPACE_08;
256
- }
257
-
258
- .viber-carousel-tab-label-disabled {
259
- color: $FONT_COLOR_04;
260
- cursor: not-allowed;
261
- }
262
-
263
- .viber-carousel-field-length-top {
264
- display: block;
265
- text-align: right;
266
- margin-top: $CAP_SPACE_04;
267
- }
268
-
269
- .viber-form-error {
270
- color: $CAP_RED;
271
- display: block;
272
- margin-top: $CAP_SPACE_08;
273
- }
@@ -154,10 +154,6 @@ export default defineMessages({
154
154
  id: `${scope}.mediaVideo`,
155
155
  defaultMessage: 'Video',
156
156
  },
157
- mediaCarousel: {
158
- id: `${scope}.mediaCarousel`,
159
- defaultMessage: 'Carousel',
160
- },
161
157
  videoErrorMessage: {
162
158
  id: `${scope}.videoErrorMessage`,
163
159
  defaultMessage: 'Please upload the video with allowed file extension, size, dimension and aspect ratio',
@@ -214,118 +210,6 @@ export default defineMessages({
214
210
  id: `${scope}.cancel`,
215
211
  defaultMessage: 'Cancel',
216
212
  },
217
- carouselCardsLabel: {
218
- id: `${scope}.carouselCardsLabel`,
219
- defaultMessage: 'Carousel cards',
220
- },
221
- carouselCardHeading: {
222
- id: `${scope}.carouselCardHeading`,
223
- defaultMessage: 'Card {index}',
224
- },
225
- carouselCardTextLabel: {
226
- id: `${scope}.carouselCardTextLabel`,
227
- defaultMessage: 'Title',
228
- },
229
- carouselCardTextPlaceholder: {
230
- id: `${scope}.carouselCardTextPlaceholder`,
231
- defaultMessage: 'Enter title',
232
- },
233
- carouselMediaUrlLabel: {
234
- id: `${scope}.carouselMediaUrlLabel`,
235
- defaultMessage: 'Card media URL',
236
- },
237
- carouselMediaUrlPlaceholder: {
238
- id: `${scope}.carouselMediaUrlPlaceholder`,
239
- defaultMessage: 'https://example.com/image.jpg',
240
- },
241
- carouselButtonTitleLabel: {
242
- id: `${scope}.carouselButtonTitleLabel`,
243
- defaultMessage: 'Button title',
244
- },
245
- carouselButtonTitlePlaceholder: {
246
- id: `${scope}.carouselButtonTitlePlaceholder`,
247
- defaultMessage: 'Enter button title',
248
- },
249
- carouselButtonActionLabel: {
250
- id: `${scope}.carouselButtonActionLabel`,
251
- defaultMessage: 'Button action URL',
252
- },
253
- carouselButtonUrlTypeLabel: {
254
- id: `${scope}.carouselButtonUrlTypeLabel`,
255
- defaultMessage: 'URL type',
256
- },
257
- carouselUrlTypeStatic: {
258
- id: `${scope}.carouselUrlTypeStatic`,
259
- defaultMessage: 'Static',
260
- },
261
- carouselUrlTypeDynamic: {
262
- id: `${scope}.carouselUrlTypeDynamic`,
263
- defaultMessage: 'Dynamic',
264
- },
265
- carouselButtonActionPlaceholder: {
266
- id: `${scope}.carouselButtonActionPlaceholder`,
267
- defaultMessage: 'https://example.com/action',
268
- },
269
- addCarouselCard: {
270
- id: `${scope}.addCarouselCard`,
271
- defaultMessage: 'Add card',
272
- },
273
- removeCarouselCard: {
274
- id: `${scope}.removeCarouselCard`,
275
- defaultMessage: 'Remove card',
276
- },
277
- addCarouselButton: {
278
- id: `${scope}.addCarouselButton`,
279
- defaultMessage: 'Add button',
280
- },
281
- removeCarouselButton: {
282
- id: `${scope}.removeCarouselButton`,
283
- defaultMessage: 'Remove button',
284
- },
285
- carouselCardsLimitError: {
286
- id: `${scope}.carouselCardsLimitError`,
287
- defaultMessage: 'Carousel message requires 2 to 5 cards',
288
- },
289
- carouselCardError: {
290
- id: `${scope}.carouselCardError`,
291
- defaultMessage: 'Each card needs text and a valid media URL',
292
- },
293
- carouselButtonError: {
294
- id: `${scope}.carouselButtonError`,
295
- defaultMessage: 'Each button needs title and a valid action URL',
296
- },
297
- characterLimitExceededError: {
298
- id: `${scope}.characterLimitExceededError`,
299
- defaultMessage: 'Character limit exceeded',
300
- },
301
- textCannotBeEmptyError: {
302
- id: `${scope}.textCannotBeEmptyError`,
303
- defaultMessage: "Text can't be empty",
304
- },
305
- urlCannotBeEmptyError: {
306
- id: `${scope}.urlCannotBeEmptyError`,
307
- defaultMessage: "URL can't be empty",
308
- },
309
- carouselButtonUrlMaxLengthError: {
310
- id: `${scope}.carouselButtonUrlMaxLengthError`,
311
- defaultMessage: 'URL can not exceed 1000 characters',
312
- },
313
- carouselFirstButtonTitleMaxLengthError: {
314
- id: `${scope}.carouselFirstButtonTitleMaxLengthError`,
315
- defaultMessage: 'Button text should not exceed 10',
316
- },
317
- carouselSecondButtonTitleMaxLengthError: {
318
- id: `${scope}.carouselSecondButtonTitleMaxLengthError`,
319
- defaultMessage: 'Button text should not exceed 12',
320
- },
321
- carouselCardTitleMinLengthError: {
322
- id: `${scope}.carouselCardTitleMinLengthError`,
323
- defaultMessage: 'Minimum 2 characters required',
324
- },
325
- carouselCardTitleMaxLengthError: {
326
- id: `${scope}.carouselCardTitleMaxLengthError`,
327
- defaultMessage: 'Title can not be more than 38 characters',
328
- },
329
213
  assetIdMissingError: {
330
214
  id: `${scope}.assetIdMissingError`,
331
215
  defaultMessage: 'Asset upload initiated but no asset ID was returned from the server. Unable to track processing status.',
@@ -383,84 +383,4 @@ describe('Test Viber container', () => {
383
383
  const doneBtn = screen.getByRole('button', { name: /done/i });
384
384
  expect(doneBtn).toBeEnabled();
385
385
  });
386
-
387
- it('does not show empty URL error on focus or while typing in carousel button action', async () => {
388
- renderComponent({
389
- actions: mockActions,
390
- globalActions: mockGlobalActions,
391
- templateData: { mode: 'create' },
392
- viber: {
393
- uploadedAssetData: {},
394
- createTemplateInProgress: false,
395
- },
396
- location: {
397
- pathname: '/sms/edit',
398
- query: { type: false, module: 'default' },
399
- search: '',
400
- },
401
- isFullMode: true,
402
- handleClose: jest.fn(),
403
- });
404
-
405
- fireEvent.click(screen.getByRole('radio', { name: /carousel/i }));
406
- const actionInput = await screen.findByPlaceholderText('https://example.com/action');
407
-
408
- fireEvent.focus(actionInput);
409
- expect(screen.queryByText("URL can't be empty")).not.toBeInTheDocument();
410
-
411
- fireEvent.change(actionInput, { target: { value: 'https://' } });
412
- expect(screen.queryByText("URL can't be empty")).not.toBeInTheDocument();
413
- });
414
-
415
- it('shows empty URL error on blur when carousel button action is left empty', async () => {
416
- renderComponent({
417
- actions: mockActions,
418
- globalActions: mockGlobalActions,
419
- templateData: { mode: 'create' },
420
- viber: {
421
- uploadedAssetData: {},
422
- createTemplateInProgress: false,
423
- },
424
- location: {
425
- pathname: '/sms/edit',
426
- query: { type: false, module: 'default' },
427
- search: '',
428
- },
429
- isFullMode: true,
430
- handleClose: jest.fn(),
431
- });
432
-
433
- fireEvent.click(screen.getByRole('radio', { name: /carousel/i }));
434
- const actionInput = await screen.findByPlaceholderText('https://example.com/action');
435
-
436
- fireEvent.focus(actionInput);
437
- fireEvent.blur(actionInput);
438
-
439
- expect(await screen.findByText("URL can't be empty")).toBeInTheDocument();
440
- });
441
-
442
- it('shows empty URL error on save when carousel button action is empty', async () => {
443
- renderComponent({
444
- actions: mockActions,
445
- globalActions: mockGlobalActions,
446
- templateData: { mode: 'create' },
447
- viber: {
448
- uploadedAssetData: {},
449
- createTemplateInProgress: false,
450
- },
451
- location: {
452
- pathname: '/sms/edit',
453
- query: { type: false, module: 'default' },
454
- search: '',
455
- },
456
- isFullMode: true,
457
- handleClose: jest.fn(),
458
- });
459
-
460
- fireEvent.click(screen.getByRole('radio', { name: /carousel/i }));
461
- await screen.findByPlaceholderText('https://example.com/action');
462
- fireEvent.click(screen.getByRole('button', { name: /save/i }));
463
-
464
- expect(await screen.findByText("URL can't be empty")).toBeInTheDocument();
465
- });
466
386
  });
@@ -45,6 +45,8 @@ import {
45
45
  ACTION_TYPES,
46
46
  NOTIFICATION_TITLE_MAX_LENGTH,
47
47
  MESSAGE_MAX_LENGTH,
48
+ EXTERNAL_URL,
49
+ SITE_URL,
48
50
  } from '../constants';
49
51
  import * as actions from '../actions';
50
52
  import {
@@ -73,6 +75,9 @@ import {
73
75
  import './index.scss';
74
76
  import { WEBPUSH } from '../../CreativesContainer/constants';
75
77
  import { isAiContentBotDisabled } from '../../../utils/common';
78
+ import TestAndPreviewSlidebox from '../../../v2Components/TestAndPreviewSlidebox';
79
+ import creativesMessages from '../../CreativesContainer/messages';
80
+ import CapButton from '@capillarytech/cap-ui-library/CapButton';
76
81
 
77
82
  // Memoized TagList wrapper components for better performance
78
83
  const MemoizedTagList = memo(({
@@ -182,6 +187,7 @@ const WebPushCreate = ({
182
187
  const [redirectUrlError, setRedirectUrlError] = useState('');
183
188
  const [activeUploadField, setActiveUploadField] = useState(null);
184
189
  const [templateIdError, setTemplateIdError] = useState('');
190
+ const [showTestAndPreviewSlidebox, setShowTestAndPreviewSlidebox] = useState(false);
185
191
 
186
192
  // Refs
187
193
  const titleCountRef = useRef(null);
@@ -569,6 +575,67 @@ const WebPushCreate = ({
569
575
  return !(templateNameInvalid || titleValidation || messageValidation);
570
576
  };
571
577
 
578
+ const getTemplateContent = useCallback(() => {
579
+ let cta = null;
580
+ if (onClickBehaviour === ON_CLICK_BEHAVIOUR_OPTIONS.REDIRECT_TO_URL && redirectUrl) {
581
+ cta = { type: EXTERNAL_URL, actionLink: redirectUrl.trim() };
582
+ } else if (onClickBehaviour === ON_CLICK_BEHAVIOUR_OPTIONS.SITE_URL && websiteLink) {
583
+ cta = { type: SITE_URL, actionLink: websiteLink };
584
+ }
585
+
586
+ const hasImage = mediaType === WEBPUSH_MEDIA_TYPES.IMAGE && imageSrc;
587
+ const hasCtas = buttons && buttons.length > 0;
588
+ let expandableDetails = null;
589
+ if (hasImage || hasCtas) {
590
+ expandableDetails = {
591
+ media: hasImage ? [{ url: imageSrc, type: WEBPUSH_MEDIA_TYPES.IMAGE }] : [],
592
+ ctas: hasCtas ? buttons.map((btn) => ({
593
+ type: EXTERNAL_URL,
594
+ action: '',
595
+ title: btn.text || '',
596
+ actionLink: btn.url || '',
597
+ })) : [],
598
+ };
599
+ }
600
+
601
+ const iconImageUrl = brandIconOption !== BRAND_ICON_OPTIONS.DONT_SHOW && brandIconSrc ? brandIconSrc : undefined;
602
+
603
+ return {
604
+ channel: WEBPUSH,
605
+ accountId,
606
+ content: {
607
+ title: notificationTitle || '',
608
+ message: message || '',
609
+ ...(iconImageUrl ? { iconImageUrl } : {}),
610
+ ...(cta ? { cta } : {}),
611
+ ...(expandableDetails ? { expandableDetails } : {}),
612
+ },
613
+ messageSubject: templateName || notificationTitle || '',
614
+ offers: [],
615
+ };
616
+ }, [
617
+ notificationTitle,
618
+ message,
619
+ accountId,
620
+ mediaType,
621
+ imageSrc,
622
+ brandIconOption,
623
+ brandIconSrc,
624
+ buttons,
625
+ onClickBehaviour,
626
+ redirectUrl,
627
+ websiteLink,
628
+ templateName,
629
+ ]);
630
+
631
+ const handleTestAndPreview = useCallback(() => {
632
+ setShowTestAndPreviewSlidebox(true);
633
+ }, []);
634
+
635
+ const handleCloseTestAndPreview = useCallback(() => {
636
+ setShowTestAndPreviewSlidebox(false);
637
+ }, []);
638
+
572
639
  const isFormValid = () => {
573
640
  const templateNameInvalid = isFullMode && validateTemplateName(templateName);
574
641
  const titleValidation = validateTitle(notificationTitle);
@@ -1040,14 +1107,23 @@ const WebPushCreate = ({
1040
1107
  formatMessage={formatMessage}
1041
1108
  messages={messages}
1042
1109
  />
1043
- <FormActions
1044
- onSave={handleSave}
1045
- isSaveDisabled={isSaveDisabled}
1046
- errorText={errorText}
1047
- accountErrorText={accountErrorText}
1048
- formatMessage={formatMessage}
1049
- messages={messages}
1050
- />
1110
+ <CapRow className="webpush-test-preview-action">
1111
+ <FormActions
1112
+ onSave={handleSave}
1113
+ isSaveDisabled={isSaveDisabled}
1114
+ errorText={errorText}
1115
+ accountErrorText={accountErrorText}
1116
+ formatMessage={formatMessage}
1117
+ messages={messages}
1118
+ />
1119
+ <CapButton
1120
+ type="secondary"
1121
+ onClick={handleTestAndPreview}
1122
+ className="webpush-test-preview-btn"
1123
+ >
1124
+ {formatMessage(creativesMessages.testAndPreview)}
1125
+ </CapButton>
1126
+ </CapRow>
1051
1127
  </CapColumn>
1052
1128
  <CapColumn className="preview-section" span={10}>
1053
1129
  <WebPushPreview
@@ -1059,6 +1135,13 @@ const WebPushCreate = ({
1059
1135
  buttons={buttons}
1060
1136
  />
1061
1137
  </CapColumn>
1138
+ <TestAndPreviewSlidebox
1139
+ show={showTestAndPreviewSlidebox}
1140
+ onClose={handleCloseTestAndPreview}
1141
+ content={getTemplateContent()}
1142
+ currentChannel={WEBPUSH}
1143
+ accountId={accountId || null}
1144
+ />
1062
1145
  </CapRow>
1063
1146
  );
1064
1147
  };
@@ -135,5 +135,12 @@
135
135
  font-family: 'Roboto', open-sans, sans-serif;
136
136
  }
137
137
  }
138
+
139
+ .webpush-test-preview-action {
140
+ display: flex;
141
+ .webpush-test-preview-btn {
142
+ margin-left: $CAP_SPACE_12;
143
+ }
144
+ }
138
145
  }
139
146