@capillarytech/creatives-library 8.0.358 → 8.0.359-alpha.0

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 (36) hide show
  1. package/index.html +0 -1
  2. package/package.json +1 -1
  3. package/utils/cdnTransformation.js +3 -75
  4. package/utils/tests/cdnTransformation.test.js +0 -127
  5. package/v2Components/CommonTestAndPreview/UnifiedPreview/PreviewHeader.js +0 -16
  6. package/v2Components/CommonTestAndPreview/UnifiedPreview/ViberPreviewContent.js +132 -14
  7. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +163 -54
  8. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +6 -52
  9. package/v2Components/CommonTestAndPreview/constants.js +0 -2
  10. package/v2Components/CommonTestAndPreview/index.js +231 -77
  11. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/PreviewHeader.test.js +0 -163
  12. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/ViberPreviewContent.test.js +364 -0
  13. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +0 -255
  14. package/v2Components/CommonTestAndPreview/tests/constants.test.js +1 -2
  15. package/v2Components/CommonTestAndPreview/tests/index.test.js +0 -194
  16. package/v2Components/FormBuilder/index.js +52 -162
  17. package/v2Components/TestAndPreviewSlidebox/index.js +2 -2
  18. package/v2Containers/App/constants.js +0 -3
  19. package/v2Containers/CreativesContainer/index.js +24 -60
  20. package/v2Containers/Templates/_templates.scss +77 -0
  21. package/v2Containers/Templates/index.js +92 -82
  22. package/v2Containers/Templates/sagas.js +1 -6
  23. package/v2Containers/Templates/tests/sagas.test.js +6 -23
  24. package/v2Containers/Viber/constants.js +19 -0
  25. package/v2Containers/Viber/index.js +714 -47
  26. package/v2Containers/Viber/index.scss +148 -0
  27. package/v2Containers/Viber/messages.js +116 -0
  28. package/v2Containers/Viber/tests/index.test.js +80 -0
  29. package/v2Containers/WebPush/Create/index.js +8 -91
  30. package/v2Containers/WebPush/Create/index.scss +0 -7
  31. package/v2Components/CommonTestAndPreview/UnifiedPreview/WebPushPreviewContent.js +0 -169
  32. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/WebPushPreviewContent.test.js +0 -522
  33. package/v2Containers/App/tests/constants.test.js +0 -61
  34. package/v2Containers/Templates/tests/webpush.test.js +0 -375
  35. package/v2Containers/WebPush/Create/tests/getTemplateContent.test.js +0 -348
  36. package/v2Containers/WebPush/Create/tests/testAndPreviewIntegration.test.js +0 -325
@@ -81,8 +81,7 @@ import {
81
81
  IMAGE,
82
82
  VIDEO,
83
83
  URL,
84
- CHANNELS_USING_ANDROID_PREVIEW_DEVICE,
85
- DAYS,
84
+ CHANNELS_USING_ANDROID_PREVIEW_DEVICE
86
85
  } from './constants';
87
86
 
88
87
  // Import utilities
@@ -189,6 +188,8 @@ const CommonTestAndPreview = (props) => {
189
188
  const [previewDevice, setPreviewDevice] = useState(initialDevice);
190
189
  // Track if a preview call has been made (to know when to use previewDataHtml vs raw content)
191
190
  const [hasPreviewCallBeenMade, setHasPreviewCallBeenMade] = useState(false);
191
+ // Viber: tag values applied to preview only after explicit Update preview / Discard (not while typing)
192
+ const [viberPreviewTagValues, setViberPreviewTagValues] = useState(null);
192
193
  const [previewDataHtml, setPreviewDataHtml] = useState(() => {
193
194
  // Initialize preview data based on channel
194
195
  if (channel === CHANNELS.EMAIL && formData) {
@@ -456,6 +457,144 @@ const CommonTestAndPreview = (props) => {
456
457
  return resolvedText;
457
458
  };
458
459
 
460
+ const getViberMergedFormData = useCallback((formDataOverride) => {
461
+ const formDataObj = formDataOverride && typeof formDataOverride === 'object'
462
+ ? formDataOverride
463
+ : (formData && typeof formData === 'object' ? formData : {});
464
+ let contentObj = {};
465
+ if (content && typeof content === 'object') {
466
+ contentObj = content;
467
+ } else if (typeof content === 'string' && content.trim()) {
468
+ try {
469
+ contentObj = JSON.parse(content);
470
+ } catch (e) {
471
+ contentObj = {};
472
+ }
473
+ }
474
+ const previewCards = formDataObj.viberPreviewContent?.cards
475
+ ?? formDataObj.cards
476
+ ?? contentObj.viberPreviewContent?.cards
477
+ ?? contentObj.cards;
478
+ const mergedPreview = {
479
+ ...(contentObj.viberPreviewContent || {}),
480
+ ...(formDataObj.viberPreviewContent || {}),
481
+ ...(Array.isArray(previewCards) && previewCards.length ? { cards: previewCards } : {}),
482
+ };
483
+ return {
484
+ ...contentObj,
485
+ ...formDataObj,
486
+ ...(Array.isArray(previewCards) && previewCards.length ? { cards: previewCards } : {}),
487
+ ...(Object.keys(mergedPreview).length ? { viberPreviewContent: mergedPreview } : {}),
488
+ };
489
+ }, [formData, content]);
490
+
491
+ const getViberCarouselCardsFromFormData = (formDataObj) => {
492
+ const previewCards = formDataObj?.viberPreviewContent?.cards;
493
+ if (Array.isArray(previewCards) && previewCards.length) {
494
+ return previewCards;
495
+ }
496
+ const rootCards = formDataObj?.cards;
497
+ if (Array.isArray(rootCards) && rootCards.length) {
498
+ return rootCards;
499
+ }
500
+ return [];
501
+ };
502
+
503
+ const getViberTagExtractionContent = (formDataObj, contentStr = '') => {
504
+ const messageText = formDataObj?.messageContent
505
+ || formDataObj?.viberPreviewContent?.messageContent
506
+ || contentStr
507
+ || '';
508
+ const cardFieldTexts = getViberCarouselCardsFromFormData(formDataObj).flatMap((card) => {
509
+ const fields = [card?.text || ''];
510
+ (card?.buttons || []).forEach((button) => {
511
+ fields.push(button?.title || '', button?.action || '');
512
+ });
513
+ return fields;
514
+ });
515
+ return [messageText, ...cardFieldTexts]
516
+ .filter((value) => typeof value === 'string' && value.trim())
517
+ .join(' ');
518
+ };
519
+
520
+ const resolveViberCarouselCards = (cards, tagValues) => {
521
+ if (!Array.isArray(cards) || !tagValues || !Object.keys(tagValues).length) {
522
+ return cards;
523
+ }
524
+ return cards.map((card) => ({
525
+ ...card,
526
+ text: resolveTagsInText(card?.text || '', tagValues),
527
+ buttons: (card?.buttons || []).map((button) => ({
528
+ ...button,
529
+ title: resolveTagsInText(button?.title || '', tagValues),
530
+ action: resolveTagsInText(button?.action || '', tagValues),
531
+ })),
532
+ }));
533
+ };
534
+
535
+ const applyViberCustomValuesToContent = (contentObj, tagValues) => {
536
+ if (!contentObj || typeof contentObj !== 'object' || !tagValues || !Object.keys(tagValues).length) {
537
+ return contentObj;
538
+ }
539
+ const result = { ...contentObj };
540
+ if (typeof result.messageContent === 'string') {
541
+ result.messageContent = resolveTagsInText(result.messageContent, tagValues);
542
+ }
543
+ if (Array.isArray(result.cards)) {
544
+ result.cards = resolveViberCarouselCards(result.cards, tagValues);
545
+ }
546
+ if (result.viberPreviewContent && typeof result.viberPreviewContent === 'object') {
547
+ const preview = result.viberPreviewContent;
548
+ result.viberPreviewContent = {
549
+ ...preview,
550
+ messageContent: resolveTagsInText(preview.messageContent || '', tagValues),
551
+ cards: resolveViberCarouselCards(preview.cards, tagValues),
552
+ };
553
+ }
554
+ return result;
555
+ };
556
+
557
+ const mergeViberPreviewWithResolved = (base, resolved) => {
558
+ if (!resolved || typeof resolved !== 'object') {
559
+ return base && typeof base === 'object' ? base : {};
560
+ }
561
+ const baseObj = base && typeof base === 'object' ? base : {};
562
+ const basePreview = baseObj.viberPreviewContent || {};
563
+ const resolvedPreview = resolved.viberPreviewContent || {};
564
+ const baseCards = basePreview.cards || baseObj.cards || [];
565
+ const resolvedCards = resolvedPreview.cards || resolved.cards || [];
566
+ const mergedCards = baseCards.length
567
+ ? baseCards.map((card, index) => {
568
+ const resolvedCard = resolvedCards[index];
569
+ if (!resolvedCard) {
570
+ return card;
571
+ }
572
+ return {
573
+ ...card,
574
+ ...(resolvedCard.text != null ? { text: resolvedCard.text } : {}),
575
+ ...(resolvedCard.mediaUrl != null ? { mediaUrl: resolvedCard.mediaUrl } : {}),
576
+ buttons: (card.buttons || []).map((button, buttonIndex) => ({
577
+ ...button,
578
+ ...(resolvedCard.buttons?.[buttonIndex] || {}),
579
+ })),
580
+ };
581
+ })
582
+ : resolvedCards;
583
+ const resolvedMessage = resolved.messageContent ?? resolvedPreview.messageContent;
584
+
585
+ return {
586
+ ...baseObj,
587
+ ...resolved,
588
+ viberPreviewContent: {
589
+ ...basePreview,
590
+ ...resolvedPreview,
591
+ type: resolvedPreview.type || basePreview.type || baseObj.type,
592
+ cards: mergedCards.length ? mergedCards : (resolvedPreview.cards || basePreview.cards),
593
+ ...(resolvedMessage != null ? { messageContent: resolvedMessage } : {}),
594
+ },
595
+ };
596
+ };
597
+
459
598
  /**
460
599
  * Common handler for saving test customers (both new and existing)
461
600
  */
@@ -611,12 +750,6 @@ const CommonTestAndPreview = (props) => {
611
750
  messageBody: contentStr,
612
751
  };
613
752
 
614
- case CHANNELS.WEBPUSH:
615
- return {
616
- ...basePayload,
617
- messageBody: contentStr,
618
- };
619
-
620
753
  default:
621
754
  return basePayload;
622
755
  }
@@ -663,17 +796,13 @@ const CommonTestAndPreview = (props) => {
663
796
  templateContent: formDataObj?.bodyText || contentStr,
664
797
  };
665
798
 
666
- case CHANNELS.VIBER:
667
- return {
668
- templateSubject: formDataObj?.messageTitle || '',
669
- templateContent: contentStr,
670
- };
671
-
672
- case CHANNELS.WEBPUSH:
799
+ case CHANNELS.VIBER: {
800
+ const viberFormData = getViberMergedFormData(formDataObj);
673
801
  return {
674
- templateSubject: formDataObj?.content?.title || '',
675
- templateContent: contentStr,
802
+ templateSubject: viberFormData?.messageTitle || '',
803
+ templateContent: getViberTagExtractionContent(viberFormData, contentStr),
676
804
  };
805
+ }
677
806
 
678
807
  case CHANNELS.ZALO: {
679
808
  // For Zalo, extract content from templateListParams array
@@ -1562,11 +1691,16 @@ const CommonTestAndPreview = (props) => {
1562
1691
  // 1. formDataObj from getTemplateContent (contains both viberPreviewContent and payload fields)
1563
1692
  // 2. formDataObj[0] (array format - legacy)
1564
1693
  // 3. contentStr (direct string - legacy)
1694
+ // Merge content prop so listing / preview-only flows include carousel card tags
1695
+ const viberFormData = getViberMergedFormData(formDataObj);
1696
+ formDataObj = viberFormData;
1565
1697
 
1566
1698
  let messageText = '';
1567
1699
  let imageData = null;
1568
1700
  let videoData = null;
1569
1701
  let buttonData = null;
1702
+ let cardsData = [];
1703
+ let messageType = '';
1570
1704
  let accountId = null;
1571
1705
  let accountDetails = null;
1572
1706
  let scenarioKey = '';
@@ -1581,6 +1715,8 @@ const CommonTestAndPreview = (props) => {
1581
1715
  imageData = formDataObj.image || null;
1582
1716
  videoData = formDataObj.video || null;
1583
1717
  buttonData = formDataObj.button || null;
1718
+ cardsData = formDataObj.cards || [];
1719
+ messageType = formDataObj.type || '';
1584
1720
  accountId = formDataObj.accountId || null;
1585
1721
  accountDetails = formDataObj.accountDetails || null;
1586
1722
  scenarioKey = formDataObj.scenarioKey || VIBER_API_SCENARIO_KEY;
@@ -1607,6 +1743,8 @@ const CommonTestAndPreview = (props) => {
1607
1743
  url: formDataObj?.buttonURL || '',
1608
1744
  };
1609
1745
  }
1746
+ cardsData = viberPreview.cards || formDataObj?.cards || [];
1747
+ messageType = viberPreview.type || formDataObj?.type || '';
1610
1748
  // Extract account info from parent formDataObj if available
1611
1749
  accountId = formDataObj.accountId || null;
1612
1750
  accountDetails = formDataObj.accountDetails || null;
@@ -1649,6 +1787,10 @@ const CommonTestAndPreview = (props) => {
1649
1787
  text: messageText,
1650
1788
  };
1651
1789
 
1790
+ if (messageType === CHANNELS.VIBER) {
1791
+ messageType = '';
1792
+ }
1793
+
1652
1794
  // Add image if present
1653
1795
  if (imageData && imageData.url) {
1654
1796
  viberContent.image = {
@@ -1677,9 +1819,34 @@ const CommonTestAndPreview = (props) => {
1677
1819
  }
1678
1820
  }
1679
1821
 
1680
- // Resolve tags in text if custom values are provided
1681
- if (customValuesObj && Object.keys(customValuesObj).length > 0 && viberContent.text) {
1682
- viberContent.text = resolveTagsInText(viberContent.text, customValuesObj);
1822
+ if (messageType === MEDIA_TYPE_CAROUSEL || (Array.isArray(cardsData) && cardsData.length)) {
1823
+ viberContent.type = MEDIA_TYPE_CAROUSEL;
1824
+ viberContent.cards = (cardsData || []).map((card) => ({
1825
+ text: card?.text || '',
1826
+ mediaUrl: card?.mediaUrl || '',
1827
+ buttons: (card?.buttons || []).map((button) => ({
1828
+ title: button?.title || '',
1829
+ action: button?.action || '',
1830
+ })),
1831
+ }));
1832
+ delete viberContent.button;
1833
+ }
1834
+
1835
+ // Resolve tags in text/cards if custom values are provided
1836
+ if (customValuesObj && Object.keys(customValuesObj).length > 0) {
1837
+ if (viberContent.text) {
1838
+ viberContent.text = resolveTagsInText(viberContent.text, customValuesObj);
1839
+ }
1840
+ if (Array.isArray(viberContent.cards)) {
1841
+ viberContent.cards = resolveViberCarouselCards(viberContent.cards, customValuesObj);
1842
+ }
1843
+ if (viberContent.button) {
1844
+ viberContent.button = {
1845
+ ...viberContent.button,
1846
+ text: resolveTagsInText(viberContent.button.text || '', customValuesObj),
1847
+ url: resolveTagsInText(viberContent.button.url || '', customValuesObj),
1848
+ };
1849
+ }
1683
1850
  }
1684
1851
 
1685
1852
  // Build messageBody JSON string
@@ -1814,42 +1981,6 @@ const CommonTestAndPreview = (props) => {
1814
1981
  };
1815
1982
  }
1816
1983
 
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
-
1853
1984
  default:
1854
1985
  return basePayload;
1855
1986
  }
@@ -1881,7 +2012,7 @@ const CommonTestAndPreview = (props) => {
1881
2012
  contentObj = hasPreviewCallBeenMade && previewDataHtml?.resolvedBody
1882
2013
  ? previewDataHtml.resolvedBody
1883
2014
  : getCurrentContent || '';
1884
- } else if ([CHANNELS.WHATSAPP, CHANNELS.WEBPUSH].includes(channel)) {
2015
+ } else if (channel === CHANNELS.WHATSAPP) {
1885
2016
  // For WhatsApp, content is an object with templateMsg, media, CTA, etc.
1886
2017
  // Content comes from WhatsApp component state, passed via content prop
1887
2018
  let resolvedContent = null;
@@ -2059,33 +2190,39 @@ const CommonTestAndPreview = (props) => {
2059
2190
  // Only use previewDataHtml if preview call was made
2060
2191
  if (hasPreviewCallBeenMade && previewDataHtml?.resolvedBody) {
2061
2192
  resolvedContent = previewDataHtml.resolvedBody;
2193
+ if (typeof resolvedContent === 'string') {
2194
+ try {
2195
+ resolvedContent = JSON.parse(resolvedContent);
2196
+ } catch (e) {
2197
+ resolvedContent = null;
2198
+ }
2199
+ }
2062
2200
  }
2063
2201
 
2064
- // Parse content if it's a string
2065
- let parsedViberContent = content;
2066
- if (typeof content === 'string') {
2067
- try {
2068
- parsedViberContent = JSON.parse(content);
2069
- } catch (e) {
2070
- parsedViberContent = {};
2071
- }
2202
+ // Parse content if it's a string, then merge with formData for carousel + tags in all entry flows
2203
+ let parsedViberContent = getViberMergedFormData();
2204
+ if (!parsedViberContent || typeof parsedViberContent !== 'object') {
2205
+ parsedViberContent = {};
2072
2206
  }
2073
- // Use resolvedContent if available (from preview call), otherwise use raw content
2207
+ // Merge template preview state with API resolved body so carousel type/cards survive slim responses
2074
2208
  if (resolvedContent && typeof resolvedContent === 'object') {
2075
- contentObj = { ...resolvedContent };
2076
- // Merge in accountName and brandName from raw content if not in resolvedContent
2077
- if (parsedViberContent?.accountName && !contentObj.accountName) {
2078
- contentObj.accountName = parsedViberContent.accountName;
2209
+ const base = parsedViberContent && typeof parsedViberContent === 'object' ? parsedViberContent : {};
2210
+ contentObj = mergeViberPreviewWithResolved(base, resolvedContent);
2211
+ if (base.accountName && !contentObj.accountName) {
2212
+ contentObj.accountName = base.accountName;
2079
2213
  }
2080
- if (parsedViberContent?.brandName && !contentObj.brandName) {
2081
- contentObj.brandName = parsedViberContent.brandName;
2214
+ if (base.brandName && !contentObj.brandName) {
2215
+ contentObj.brandName = base.brandName;
2082
2216
  }
2083
2217
  } else {
2084
- // Use raw content if no preview call was made or resolvedContent is not available
2085
- contentObj = {...parsedViberContent, messageContent: resolvedContent};
2086
- if (resolvedContent) {
2087
- contentObj.viberPreviewContent = {...parsedViberContent?.viberPreviewContent, messageContent: resolvedContent};
2088
- }
2218
+ contentObj = parsedViberContent && typeof parsedViberContent === 'object' ? parsedViberContent : {};
2219
+ }
2220
+ if (
2221
+ hasPreviewCallBeenMade
2222
+ && viberPreviewTagValues
2223
+ && Object.keys(viberPreviewTagValues).length > 0
2224
+ ) {
2225
+ contentObj = applyViberCustomValuesToContent(contentObj, viberPreviewTagValues);
2089
2226
  }
2090
2227
  } else if (channel === CHANNELS.ZALO) {
2091
2228
  // For Zalo, content is an object with templatePreviewUrl, templateStatus, etc.
@@ -2341,6 +2478,8 @@ const CommonTestAndPreview = (props) => {
2341
2478
  setShowJSON(false);
2342
2479
  setTagsExtracted(false);
2343
2480
  setPreviewDevice(DESKTOP);
2481
+ setHasPreviewCallBeenMade(false);
2482
+ setViberPreviewTagValues(null);
2344
2483
  setSelectedTestEntities([]);
2345
2484
  actions.clearPrefilledValues();
2346
2485
  } else {
@@ -2470,6 +2609,11 @@ const CommonTestAndPreview = (props) => {
2470
2609
 
2471
2610
  setCustomValues(updatedValues);
2472
2611
 
2612
+ // Viber: do not auto-resolve tags in preview; user must click Update preview
2613
+ if (channel === CHANNELS.VIBER) {
2614
+ return;
2615
+ }
2616
+
2473
2617
  // Update preview with prefilled values (this is a valid preview call trigger)
2474
2618
  const payload = preparePreviewPayload(
2475
2619
  channel,
@@ -2547,6 +2691,7 @@ const CommonTestAndPreview = (props) => {
2547
2691
  setPreviewDevice(DESKTOP);
2548
2692
  setPreviewDataHtml('');
2549
2693
  setHasPreviewCallBeenMade(false); // Reset preview call flag
2694
+ setViberPreviewTagValues(null);
2550
2695
  setSelectedTestEntities([]);
2551
2696
  setBeeContent('');
2552
2697
  previousBeeContentRef.current = '';
@@ -2588,6 +2733,7 @@ const CommonTestAndPreview = (props) => {
2588
2733
  const handleClearSelection = () => {
2589
2734
  setSelectedCustomer(null);
2590
2735
  setHasPreviewCallBeenMade(false); // Reset flag when customer is cleared
2736
+ setViberPreviewTagValues(null);
2591
2737
 
2592
2738
  // Clear all preview errors when customer is cleared
2593
2739
  actions.clearPreviewErrors();
@@ -2647,6 +2793,9 @@ const CommonTestAndPreview = (props) => {
2647
2793
  emptyValues,
2648
2794
  selectedCustomer
2649
2795
  );
2796
+ if (channel === CHANNELS.VIBER) {
2797
+ setViberPreviewTagValues({ ...emptyValues });
2798
+ }
2650
2799
  actions.updatePreviewRequested(payload);
2651
2800
  setHasPreviewCallBeenMade(true); // Mark that preview call was made
2652
2801
  };
@@ -2664,6 +2813,9 @@ const CommonTestAndPreview = (props) => {
2664
2813
  customValues,
2665
2814
  selectedCustomer
2666
2815
  );
2816
+ if (channel === CHANNELS.VIBER) {
2817
+ setViberPreviewTagValues({ ...customValues });
2818
+ }
2667
2819
  await actions.updatePreviewRequested(payload);
2668
2820
  setHasPreviewCallBeenMade(true); // Mark that preview call was made
2669
2821
  } catch (error) {
@@ -2685,6 +2837,8 @@ const CommonTestAndPreview = (props) => {
2685
2837
  const activeTab = currentTabData?.activeTab;
2686
2838
  const templateContent = currentTabData?.[activeTab]?.['template-content'];
2687
2839
  contentToExtract = templateContent || contentToExtract;
2840
+ } else if (channel === CHANNELS.VIBER) {
2841
+ contentToExtract = getViberTagExtractionContent(getViberMergedFormData(), contentToExtract);
2688
2842
  }
2689
2843
 
2690
2844
  // Check for personalization tags (excluding unsubscribe)
@@ -514,167 +514,4 @@ 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 without 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
- // Expander icon no longer adds ACTIVE class based on device
596
- const activeEl = container.querySelector('.active');
597
- expect(activeEl).toBeNull();
598
- // But the expander icon itself should still be present
599
- const expanderIcon = container.querySelector('[class*="expander"]') ||
600
- container.querySelector('.anticon');
601
- expect(expanderIcon).toBeTruthy();
602
- });
603
-
604
- it('should render expander icon without ACTIVE class when device is DESKTOP', () => {
605
- const setIsFullscreenOpen = jest.fn();
606
- const props = {
607
- ...defaultProps,
608
- channel: CHANNELS.WEBPUSH,
609
- device: DESKTOP,
610
- setIsFullscreenOpen,
611
- };
612
-
613
- const { container } = render(
614
- <TestWrapper>
615
- <PreviewHeader {...props} />
616
- </TestWrapper>
617
- );
618
-
619
- // Expander icon does not have ACTIVE class for any device
620
- const activeEl = container.querySelector('.active');
621
- expect(activeEl).toBeNull();
622
- });
623
-
624
- it('should render expander icon alongside device content for WEBPUSH', () => {
625
- // Note: suppression of showDeviceToggle for WEBPUSH is done in UnifiedPreview (parent),
626
- // not in PreviewHeader itself. PreviewHeader only adds the expander icon for WEBPUSH.
627
- const setIsFullscreenOpen = jest.fn();
628
- const props = {
629
- ...defaultProps,
630
- channel: CHANNELS.WEBPUSH,
631
- setIsFullscreenOpen,
632
- showDeviceToggle: false,
633
- };
634
-
635
- const { container } = render(
636
- <TestWrapper>
637
- <PreviewHeader {...props} />
638
- </TestWrapper>
639
- );
640
-
641
- // expander icon should be present for WEBPUSH
642
- const expanderIcon = container.querySelector('[class*="expander"]') ||
643
- container.querySelector('.anticon');
644
- expect(expanderIcon).toBeTruthy();
645
- });
646
-
647
- it('should accept isFullscreenOpen and setIsFullscreenOpen as props', () => {
648
- const setIsFullscreenOpen = jest.fn();
649
- const props = {
650
- ...defaultProps,
651
- channel: CHANNELS.WEBPUSH,
652
- isFullscreenOpen: false,
653
- setIsFullscreenOpen,
654
- };
655
-
656
- expect(() =>
657
- render(
658
- <TestWrapper>
659
- <PreviewHeader {...props} />
660
- </TestWrapper>
661
- )
662
- ).not.toThrow();
663
- });
664
-
665
- it('should use default setIsFullscreenOpen when not provided', () => {
666
- const props = {
667
- ...defaultProps,
668
- channel: CHANNELS.WEBPUSH,
669
- };
670
-
671
- expect(() =>
672
- render(
673
- <TestWrapper>
674
- <PreviewHeader {...props} />
675
- </TestWrapper>
676
- )
677
- ).not.toThrow();
678
- });
679
- });
680
517
  });