@capillarytech/creatives-library 8.0.316 → 8.0.317-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 (172) hide show
  1. package/constants/unified.js +15 -0
  2. package/package.json +1 -1
  3. package/services/api.js +6 -0
  4. package/services/tests/api.test.js +7 -0
  5. package/utils/common.js +6 -1
  6. package/utils/templateVarUtils.js +172 -0
  7. package/utils/tests/tagValidations.test.js +34 -0
  8. package/utils/tests/templateVarUtils.test.js +160 -0
  9. package/v2Components/CapTagList/index.js +25 -22
  10. package/v2Components/CapTagList/style.scss +48 -0
  11. package/v2Components/CapTagListWithInput/__tests__/CapTagListWithInput.test.js +63 -0
  12. package/v2Components/CapTagListWithInput/index.js +4 -0
  13. package/v2Components/CapWhatsappCTA/index.js +2 -0
  14. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -49
  15. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
  16. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +207 -21
  17. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
  18. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +85 -10
  19. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
  20. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +79 -11
  21. package/v2Components/CommonTestAndPreview/SendTestMessage.js +11 -5
  22. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +20 -1
  23. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +133 -4
  24. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +12 -0
  25. package/v2Components/CommonTestAndPreview/constants.js +38 -0
  26. package/v2Components/CommonTestAndPreview/index.js +693 -155
  27. package/v2Components/CommonTestAndPreview/messages.js +41 -3
  28. package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
  29. package/v2Components/CommonTestAndPreview/sagas.js +15 -6
  30. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +352 -0
  31. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +269 -1
  32. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
  33. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
  34. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +25 -4
  35. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
  36. package/v2Components/CommonTestAndPreview/tests/index.test.js +132 -4
  37. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
  38. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
  39. package/v2Components/FormBuilder/index.js +14 -1
  40. package/v2Components/HtmlEditor/HTMLEditor.js +6 -1
  41. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -0
  42. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +927 -2
  43. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +3 -0
  44. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +87 -0
  45. package/v2Components/SmsFallback/constants.js +73 -0
  46. package/v2Components/SmsFallback/index.js +956 -0
  47. package/v2Components/SmsFallback/index.scss +265 -0
  48. package/v2Components/SmsFallback/messages.js +78 -0
  49. package/v2Components/SmsFallback/smsFallbackUtils.js +107 -0
  50. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
  51. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
  52. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
  53. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +197 -0
  54. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +261 -0
  55. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
  56. package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
  57. package/v2Components/TestAndPreviewSlidebox/index.js +8 -1
  58. package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
  59. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
  60. package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
  61. package/v2Components/VarSegmentMessageEditor/index.js +125 -0
  62. package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
  63. package/v2Containers/BeeEditor/index.js +3 -0
  64. package/v2Containers/CommunicationFlow/CommunicationFlow.js +291 -0
  65. package/v2Containers/CommunicationFlow/CommunicationFlow.scss +25 -0
  66. package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +255 -0
  67. package/v2Containers/CommunicationFlow/constants.js +200 -0
  68. package/v2Containers/CommunicationFlow/index.js +102 -0
  69. package/v2Containers/CommunicationFlow/messages.js +346 -0
  70. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.js +522 -0
  71. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.scss +170 -0
  72. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +796 -0
  73. package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/index.js +5 -0
  74. package/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/CommunicationStrategyStep.js +95 -0
  75. package/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/Tests/CommunicationStrategyStep.test.js +133 -0
  76. package/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/index.js +5 -0
  77. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.js +289 -0
  78. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.scss +70 -0
  79. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.js +319 -0
  80. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.scss +69 -0
  81. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +616 -0
  82. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +577 -0
  83. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/deliverySettingsConfig.test.js +1111 -0
  84. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/deliverySettingsConfig.js +696 -0
  85. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/index.js +7 -0
  86. package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.js +102 -0
  87. package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.scss +36 -0
  88. package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/Tests/DynamicControlsStep.test.js +91 -0
  89. package/v2Containers/CommunicationFlow/steps/DynamicControlsStep/index.js +5 -0
  90. package/v2Containers/CommunicationFlow/steps/MessageTypeStep/MessageTypeStep.js +86 -0
  91. package/v2Containers/CommunicationFlow/steps/MessageTypeStep/Tests/MessageTypeStep.test.js +100 -0
  92. package/v2Containers/CommunicationFlow/steps/MessageTypeStep/index.js +5 -0
  93. package/v2Containers/CommunicationFlow/utils/getEnabledSteps.js +30 -0
  94. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +43 -0
  95. package/v2Containers/CreativesContainer/SlideBoxContent.js +64 -5
  96. package/v2Containers/CreativesContainer/SlideBoxFooter.js +10 -1
  97. package/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
  98. package/v2Containers/CreativesContainer/constants.js +12 -0
  99. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +67 -0
  100. package/v2Containers/CreativesContainer/index.js +289 -93
  101. package/v2Containers/CreativesContainer/index.scss +51 -1
  102. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
  103. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +104 -0
  104. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +110 -0
  105. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
  106. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +363 -0
  107. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -10
  108. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
  109. package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
  110. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
  111. package/v2Containers/Email/index.js +1 -0
  112. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +7 -1
  113. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +3 -0
  114. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +20 -2
  115. package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +16 -1
  116. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +3 -0
  117. package/v2Containers/EmailWrapper/index.js +4 -0
  118. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +1 -0
  119. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +9 -0
  120. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +19 -0
  121. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +3 -0
  122. package/v2Containers/InAppWrapper/index.js +3 -0
  123. package/v2Containers/MobilePush/Create/index.js +2 -0
  124. package/v2Containers/MobilePush/Edit/index.js +2 -0
  125. package/v2Containers/MobilepushWrapper/index.js +3 -1
  126. package/v2Containers/Rcs/constants.js +32 -1
  127. package/v2Containers/Rcs/index.js +951 -873
  128. package/v2Containers/Rcs/index.scss +85 -6
  129. package/v2Containers/Rcs/messages.js +10 -1
  130. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +205 -0
  131. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +40834 -1963
  132. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
  133. package/v2Containers/Rcs/tests/index.test.js +41 -38
  134. package/v2Containers/Rcs/tests/mockData.js +38 -0
  135. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +251 -0
  136. package/v2Containers/Rcs/tests/utils.test.js +379 -1
  137. package/v2Containers/Rcs/utils.js +358 -10
  138. package/v2Containers/Sms/Create/index.js +83 -36
  139. package/v2Containers/Sms/Edit/index.js +2 -0
  140. package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
  141. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  142. package/v2Containers/SmsTrai/Create/index.js +9 -4
  143. package/v2Containers/SmsTrai/Edit/constants.js +2 -0
  144. package/v2Containers/SmsTrai/Edit/index.js +611 -128
  145. package/v2Containers/SmsTrai/Edit/index.scss +121 -0
  146. package/v2Containers/SmsTrai/Edit/messages.js +9 -4
  147. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4327 -2374
  148. package/v2Containers/SmsWrapper/index.js +39 -8
  149. package/v2Containers/TagList/index.js +47 -2
  150. package/v2Containers/TagList/messages.js +4 -0
  151. package/v2Containers/TagList/tests/TagList.test.js +122 -20
  152. package/v2Containers/TagList/tests/mockdata.js +17 -0
  153. package/v2Containers/Templates/TemplatesActionBar.js +101 -0
  154. package/v2Containers/Templates/_templates.scss +61 -2
  155. package/v2Containers/Templates/actions.js +11 -0
  156. package/v2Containers/Templates/constants.js +2 -0
  157. package/v2Containers/Templates/index.js +90 -40
  158. package/v2Containers/Templates/sagas.js +57 -12
  159. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
  160. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1043 -1079
  161. package/v2Containers/Templates/tests/sagas.test.js +193 -12
  162. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
  163. package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
  164. package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
  165. package/v2Containers/TemplatesV2/index.js +86 -23
  166. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
  167. package/v2Containers/Viber/index.js +5 -0
  168. package/v2Containers/WebPush/Create/hooks/useTagManagement.js +0 -2
  169. package/v2Containers/WebPush/Create/index.js +9 -1
  170. package/v2Containers/Whatsapp/index.js +8 -20
  171. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +598 -34
  172. package/v2Containers/Zalo/index.js +2 -0
@@ -10,12 +10,14 @@ import { isTraiDLTEnable } from '../../utils/common';
10
10
  import SmsEdit from '../Sms/Edit';
11
11
  import SmsTraiCreate from '../SmsTrai/Create';
12
12
  import SmsTraiEdit from '../SmsTrai/Edit';
13
+
13
14
  const SmsWrapper = (props) => {
14
15
  const {
15
16
  isCreateSms,
16
17
  isEditSms,
17
18
  setIsLoadingContent,
18
19
  location,
20
+ route: routeFromProps,
19
21
  isGetFormData,
20
22
  getFormSubscriptionData,
21
23
  isFullMode,
@@ -30,6 +32,7 @@ const SmsWrapper = (props) => {
30
32
  smsRegister,
31
33
  onShowTemplates,
32
34
  eventContextTags,
35
+ waitEventContextTags,
33
36
  showLiquidErrorInFooter,
34
37
  getLiquidTags,
35
38
  showTestAndPreviewSlidebox,
@@ -37,15 +40,33 @@ const SmsWrapper = (props) => {
37
40
  handleCloseTestAndPreview,
38
41
  isTestAndPreviewMode,
39
42
  onValidationFail,
43
+ embeddedSmsFallback = false,
44
+ onEmbeddedSmsFooterValidity,
45
+ forceFullTagContext = false,
40
46
  } = props;
41
47
 
48
+ /** FormBuilder / SMS Create assume `location.query`; connected-router shapes may omit it. */
49
+ const smsLocation = (() => {
50
+ const loc = location || {};
51
+ const q = loc.query;
52
+ if (q && typeof q === 'object') {
53
+ return { ...loc, query: { ...q } };
54
+ }
55
+ return {
56
+ pathname: loc.pathname || '/sms/create',
57
+ search: loc.search || '',
58
+ query: { type: 'embedded', module: 'library' },
59
+ };
60
+ })();
61
+
42
62
  const smsProps = {
43
63
  onCreateComplete,
44
64
  setIsLoadingContent,
45
- location,
46
- route: { name: 'sms' },
65
+ location: smsLocation,
66
+ route: routeFromProps || { name: 'sms' },
47
67
  isGetFormData,
48
68
  getFormSubscriptionData,
69
+ templateData,
49
70
  getDefaultTags,
50
71
  isFullMode,
51
72
  forwardedTags,
@@ -53,6 +74,7 @@ const SmsWrapper = (props) => {
53
74
  onPreviewContentClicked,
54
75
  onTestContentClicked,
55
76
  eventContextTags,
77
+ waitEventContextTags,
56
78
  showLiquidErrorInFooter,
57
79
  getLiquidTags,
58
80
  showTestAndPreviewSlidebox,
@@ -60,24 +82,33 @@ const SmsWrapper = (props) => {
60
82
  handleCloseTestAndPreview,
61
83
  isTestAndPreviewMode,
62
84
  onValidationFail,
85
+ forceFullTagContext,
86
+ embeddedSmsFallback,
87
+ onEmbeddedSmsFooterValidity,
88
+ ...(embeddedSmsFallback
89
+ ? {
90
+ tagListGetPopupContainer: () => document.body,
91
+ tagListPopoverOverlayStyle: { zIndex: 10020 },
92
+ tagListPopoverOverlayClassName: 'sms-fallback-taglist-popover rcs-sms-fallback-taglist-popover',
93
+ }
94
+ : {}),
63
95
  };
64
- const isTraiDlt = isTraiDLTEnable(isFullMode, smsRegister);
96
+ const useTraiSmsFlow = isTraiDLTEnable(isFullMode, smsRegister);
65
97
  return <>
66
98
  {
67
- isCreateSms && (isTraiDlt ?
99
+ isCreateSms && (useTraiSmsFlow ?
68
100
  <SmsTraiCreate
69
101
  isComponent
70
102
  {...smsProps}
71
103
  onShowTemplates={onShowTemplates}
72
104
  /> :
73
105
  <SmsCreate
74
- isComponent {
75
- ...smsProps
76
- }
106
+ isComponent
107
+ {...smsProps}
77
108
  />
78
109
  )
79
110
  }
80
- {isEditSms && (isTraiDlt ?
111
+ {isEditSms && (useTraiSmsFlow ?
81
112
  <SmsTraiEdit
82
113
  {...smsProps}
83
114
  params={{id: templateData._id}}
@@ -167,7 +167,7 @@ export class TagList extends React.Component { // eslint-disable-line react/pref
167
167
  let injectedTags = {};
168
168
  const eventContextTagsObj = {};
169
169
 
170
- const {selectedOfferDetails, eventContextTags } = props;
170
+ const {selectedOfferDetails, eventContextTags, waitEventContextTags } = props;
171
171
  if (props.injectedTags && !_.isEmpty(props.injectedTags)) {
172
172
  const formattedInjectedTags = handleInjectedData(
173
173
  props.injectedTags,
@@ -219,6 +219,43 @@ export class TagList extends React.Component { // eslint-disable-line react/pref
219
219
  };
220
220
  });
221
221
  }
222
+ // Wait event context tags should be displayed in the Add Labels when node is next to Event based wait node.
223
+ if (waitEventContextTags && Object.keys(waitEventContextTags)?.length) {
224
+
225
+ Object.keys(waitEventContextTags).forEach((blockId) => {
226
+ const WAIT_EVENT_HEADER_MSG_LABEL = `${waitEventContextTags[blockId].eventName} (${waitEventContextTags[blockId].blockName})`;
227
+ eventContextTagsObj[blockId] = {
228
+ "name": WAIT_EVENT_HEADER_MSG_LABEL,
229
+ "desc": WAIT_EVENT_HEADER_MSG_LABEL,
230
+ "resolved": true,
231
+ 'tag-header': true,
232
+ "subtags": {},
233
+ };
234
+
235
+ waitEventContextTags?.[blockId]?.tags?.forEach((tag) => {
236
+ const {
237
+ tagName, label, profileId, profileName, blockName, eventName
238
+ } = tag || {};
239
+ if (!profileId || !tagName || !label || !profileName) return;
240
+ // Initializing the tags profile if it doesn't exist
241
+ if (!eventContextTagsObj?.[blockId]?.subtags?.[profileId]) {
242
+ eventContextTagsObj[blockId].subtags[profileId] = {
243
+ "name": profileName,
244
+ "desc": profileName,
245
+ "resolved": true,
246
+ 'tag-header': true,
247
+ "subtags": {},
248
+ };
249
+ }
250
+ // Adding the current tag to the profile group
251
+ eventContextTagsObj[blockId].subtags[profileId].subtags[tagName] = {
252
+ name: label,
253
+ desc: label,
254
+ resolved: true,
255
+ };
256
+ });
257
+ });
258
+ }
222
259
  this.setState({tags: _.merge( {}, tags, injectedTags, eventContextTagsObj )});
223
260
  }
224
261
 
@@ -406,7 +443,7 @@ export class TagList extends React.Component { // eslint-disable-line react/pref
406
443
  isDisabled = true;
407
444
  tooltipMsg = intl.formatMessage(messages.personalizationNotSupportedAnonymous);
408
445
  }
409
-
446
+
410
447
  return (
411
448
  <div className={this.props.className ? this.props.className : ''}>
412
449
  <CapTagList
@@ -427,6 +464,9 @@ export class TagList extends React.Component { // eslint-disable-line react/pref
427
464
  disableTooltipMsg={tooltipMsg}
428
465
  fetchingSchemaError={this?.state?.tagsError}
429
466
  popoverPlacement={this.props.popoverPlacement}
467
+ overlayStyle={this.props.popoverOverlayStyle}
468
+ overlayClassName={this.props.popoverOverlayClassName}
469
+ getPopupContainer={this.props.getPopupContainer}
430
470
  />
431
471
  </div>
432
472
  );
@@ -437,6 +477,7 @@ TagList.defaultProps = {
437
477
  isNewVersionFlow: false,
438
478
  userLocale: 'en',
439
479
  eventContextTags: [],
480
+ waitEventContextTags: {},
440
481
  };
441
482
 
442
483
  TagList.propTypes = {
@@ -457,10 +498,14 @@ TagList.propTypes = {
457
498
  disabled: PropTypes.bool,
458
499
  fetchingSchemaError: PropTypes.bool,
459
500
  eventContextTags: PropTypes.array,
501
+ waitEventContextTags: PropTypes.object,
460
502
  popoverPlacement: PropTypes.string,
461
503
  // message to show when Add Label button is disabled (e.g. personalization restriction)
462
504
  disableTooltipMsg: PropTypes.string,
463
505
  restrictPersonalization: PropTypes.bool,
506
+ popoverOverlayStyle: PropTypes.object,
507
+ popoverOverlayClassName: PropTypes.string,
508
+ getPopupContainer: PropTypes.func,
464
509
  intl: PropTypes.shape({
465
510
  formatMessage: PropTypes.func.isRequired,
466
511
  locale: PropTypes.string,
@@ -19,4 +19,8 @@ export default defineMessages({
19
19
  id: `${scope}.personalizationNotSupportedAnonymous`,
20
20
  defaultMessage: 'Personalization tags are not supported for anonymous customers',
21
21
  },
22
+ waitEvent: {
23
+ id: `${scope}.waitEvent`,
24
+ defaultMessage: 'Wait Event',
25
+ },
22
26
  });
@@ -5,28 +5,34 @@ import { initialReducer } from '../../../initialReducer';
5
5
  import { injectIntl } from "react-intl";
6
6
  import { fireEvent } from "@testing-library/react";
7
7
  import { TagList } from '../index';
8
- import { TagListData, eventContextTags } from './mockdata';
8
+ import { TagListData, eventContextTags, waitEventContextTags } from './mockdata';
9
+ import { OfferTag, badgesTags, offer } from '../../../utils/tests/common.mockdata';
10
+ import * as commonUtils from "../../../utils/common";
9
11
  import { Provider } from 'react-redux';
10
12
  import { screen, render } from '../../../utils/test-utils';
11
13
  import history from '../../../utils/history';
12
14
  const { getByText, queryByText } = screen;
13
15
 
16
+ const buildProps = (props = {}) => ({
17
+ ...TagListData,
18
+ onTagSelect: jest.fn(),
19
+ ...props,
20
+ });
14
21
 
15
- const initializeTagList = (props) => {
22
+ const initializeTagList = (props = {}) => {
16
23
  const store = configureStore({}, initialReducer, history);
17
24
  const Component = injectIntl(TagList);
18
-
19
- const propsObj = {
20
- ...TagListData,
21
- onTagSelect: jest.fn(),
22
- ...props,
25
+ const propsObj = buildProps(props);
26
+ return {
27
+ ...render(
28
+ <Provider store={store}>
29
+ <Component {...propsObj} />
30
+ </Provider>
31
+ ),
32
+ store,
33
+ Component,
34
+ propsObj,
23
35
  };
24
-
25
- return render(
26
- <Provider store={store}>
27
- <Component {...propsObj} />
28
- </Provider>
29
- );
30
36
  };
31
37
 
32
38
  const addLabelBtnAssertion = () => {
@@ -41,19 +47,115 @@ describe("TagList test : UNIT", () => {
41
47
  addLabelBtnAssertion();
42
48
  });
43
49
 
44
- it('should render event context tags correctly from generateTags and show tags under profile', () => {
45
- initializeTagList({eventContextTags});
50
+ it('should render event context tag section from generateTags', () => {
51
+ initializeTagList({ eventContextTags, moduleFilterEnabled: false });
46
52
  addLabelBtnAssertion();
47
53
  const EVENT_CONTEXT_TAG_HEADER = getByText(/Entry event/i);
48
54
  expect(EVENT_CONTEXT_TAG_HEADER).toBeInTheDocument();
49
- fireEvent.click(EVENT_CONTEXT_TAG_HEADER);
50
- // Customer profile tags
51
- const CUSTOMER_PROFILE = getByText(/Current Customer/i);
52
- fireEvent.click(CUSTOMER_PROFILE);
53
- expect(getByText(/lifetimePurchases/i)).toBeInTheDocument();
54
55
 
55
56
  // Behavioural event profile tags should not be visible as label and profile name is not present
56
57
  const BEHAVIOURAL_EVENT_PROFILE = queryByText(/Behavioural event/i);
57
58
  expect(BEHAVIOURAL_EVENT_PROFILE).not.toBeInTheDocument();
58
59
  });
60
+
61
+ it('should render wait event context section when waitEventContextTags is provided', () => {
62
+ initializeTagList({ waitEventContextTags, moduleFilterEnabled: false });
63
+ addLabelBtnAssertion();
64
+ const WAIT_EVENT_HEADER = getByText(/Order Placed \(Wait Block\)/i);
65
+ expect(WAIT_EVENT_HEADER).toBeInTheDocument();
66
+ });
67
+
68
+ it('should merge empty waitEventContextTags with entry event tags', () => {
69
+ initializeTagList({ eventContextTags, waitEventContextTags: {}, moduleFilterEnabled: false });
70
+ addLabelBtnAssertion();
71
+ expect(getByText(/Entry event/i)).toBeInTheDocument();
72
+ });
73
+
74
+ it('calls parent onContextChange with Outbound when tags and injectedTags are empty on mount', () => {
75
+ const onContextChange = jest.fn();
76
+ initializeTagList({
77
+ tags: [],
78
+ injectedTags: {},
79
+ onContextChange,
80
+ onTagSelect: jest.fn(),
81
+ });
82
+ expect(onContextChange).toHaveBeenCalledWith('Outbound');
83
+ });
84
+
85
+ it('applies fetchingSchemaError from props via componentWillReceiveProps', () => {
86
+ const { rerender, Component, propsObj, store } = initializeTagList({ fetchingSchemaError: false });
87
+ addLabelBtnAssertion();
88
+ rerender(
89
+ <Provider store={store}>
90
+ <Component {...propsObj} fetchingSchemaError />
91
+ </Provider>
92
+ );
93
+ expect(screen.getByText(/add label/i)).toBeInTheDocument();
94
+ });
95
+
96
+ it('disables Add label when restrictPersonalization is true', () => {
97
+ initializeTagList({
98
+ restrictPersonalization: true,
99
+ disabled: false,
100
+ moduleFilterEnabled: false,
101
+ });
102
+ const btn = screen.getByText(/add label/i).closest('button');
103
+ expect(btn).toBeDisabled();
104
+ });
105
+
106
+ it('calls transformCouponTags when selectedOfferDetails and coupon tags are present', () => {
107
+ const spy = jest.spyOn(TagList.prototype, 'transformCouponTags');
108
+ initializeTagList({
109
+ tags: OfferTag,
110
+ injectedTags: {},
111
+ selectedOfferDetails: [{ id: 'c1', couponName: 'Promo Coupon', couponSeriesId: 'c1' }],
112
+ moduleFilterEnabled: false,
113
+ });
114
+ expect(spy).toHaveBeenCalled();
115
+ spy.mockRestore();
116
+ });
117
+
118
+ it('calls transformBadgeTags from common when badge offer and Badge tags are present', () => {
119
+ const spy = jest.spyOn(commonUtils, 'transformBadgeTags');
120
+ initializeTagList({
121
+ tags: badgesTags,
122
+ injectedTags: {},
123
+ selectedOfferDetails: offer,
124
+ moduleFilterEnabled: false,
125
+ });
126
+ expect(spy).toHaveBeenCalled();
127
+ spy.mockRestore();
128
+ });
129
+
130
+ it('unmounts without throwing', () => {
131
+ const { unmount } = initializeTagList();
132
+ expect(() => unmount()).not.toThrow();
133
+ });
134
+
135
+ it('regenerates tags when props.tags change (componentDidUpdate)', () => {
136
+ const { rerender, Component, store } = initializeTagList({ tags: TagListData.tags });
137
+ const extra = [
138
+ ...TagListData.tags,
139
+ {
140
+ _id: 'extra-tag',
141
+ type: 'TAG',
142
+ definition: {
143
+ label: { en: 'Extra' },
144
+ value: 'extra_value',
145
+ subtags: [],
146
+ 'tag-header': false,
147
+ supportedModules: [{ context: 'default', layout: 'sms', mandatory: false }],
148
+ },
149
+ scope: { tag: 'STANDARD', orgId: -1, verticals: [] },
150
+ isActive: true,
151
+ },
152
+ ];
153
+ rerender(
154
+ <Provider store={store}>
155
+ <Component {...buildProps({ tags: extra })} />
156
+ </Provider>
157
+ );
158
+ addLabelBtnAssertion();
159
+ expect(screen.getByText(/add label/i)).toBeInTheDocument();
160
+ });
59
161
  });
@@ -149,3 +149,20 @@ export const eventContextTags = [
149
149
  "isDynamicFact": false
150
150
  }
151
151
  ];
152
+
153
+ export const waitEventContextTags = {
154
+ block1: {
155
+ eventName: 'Order Placed',
156
+ blockName: 'Wait Block',
157
+ tags: [
158
+ {
159
+ tagName: 'waitEvent.orderId',
160
+ label: 'Order ID',
161
+ profileId: 'ORDER_PROFILE',
162
+ profileName: 'Order Profile',
163
+ blockName: 'Wait Block',
164
+ eventName: 'Order Placed',
165
+ },
166
+ ],
167
+ },
168
+ };
@@ -0,0 +1,101 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import CapInput from '@capillarytech/cap-ui-library/CapInput';
4
+ import CapButton from '@capillarytech/cap-ui-library/CapButton';
5
+
6
+ /**
7
+ * Reusable action bar for template pickers.
8
+ * Layout and spacing live in `_templates.scss` (`.action-container`, `.action-container__toolbar-row`).
9
+ * Default search field width: `.action-container__toolbar-row .search-text` (13.125rem).
10
+ * Pass `searchInputClassName` / `searchInputStyle` to override (defaults match Templates list: 210px per master).
11
+ */
12
+ const TemplatesActionBar = ({
13
+ searchValue,
14
+ onSearchChange,
15
+ onSearch,
16
+ onClear,
17
+ searchPlaceholder,
18
+ searchInputClassName,
19
+ searchInputStyle,
20
+ ctaLabel,
21
+ onCtaClick,
22
+ ctaDisabled,
23
+ ctaClassName,
24
+ ctaNode,
25
+ children,
26
+ showCta = true,
27
+ }) => (
28
+ <div className="action-container">
29
+ <div className="action-container__toolbar-row">
30
+ {searchPlaceholder && (
31
+ <CapInput.Search
32
+ className={['search-text', searchInputClassName].filter(Boolean).join(' ')}
33
+ placeholder={searchPlaceholder}
34
+ value={searchValue}
35
+ onChange={onSearchChange}
36
+ /* antd `Input` (used by CapInput.Search) has no `onSearch`; only `Input.Search` does. */
37
+ onPressEnter={(e) => {
38
+ if (onSearch) {
39
+ const v = e?.target && 'value' in e.target ? e.target.value : searchValue;
40
+ onSearch(v);
41
+ }
42
+ }}
43
+ onSearch={onSearch}
44
+ onClear={onClear}
45
+ onScroll={(e) => e.stopPropagation()}
46
+ />
47
+ )}
48
+ {children}
49
+ </div>
50
+ {showCta && (
51
+ <div>
52
+ {ctaNode || (
53
+ <CapButton
54
+ className={ctaClassName}
55
+ type="primary"
56
+ disabled={ctaDisabled}
57
+ onClick={onCtaClick}
58
+ >
59
+ {ctaLabel}
60
+ </CapButton>
61
+ )}
62
+ </div>
63
+ )}
64
+ </div>
65
+ );
66
+
67
+ TemplatesActionBar.propTypes = {
68
+ searchValue: PropTypes.string,
69
+ onSearchChange: PropTypes.func,
70
+ onSearch: PropTypes.func,
71
+ onClear: PropTypes.func,
72
+ searchPlaceholder: PropTypes.string,
73
+ searchInputClassName: PropTypes.string,
74
+ searchInputStyle: PropTypes.object,
75
+ ctaLabel: PropTypes.node,
76
+ onCtaClick: PropTypes.func,
77
+ ctaDisabled: PropTypes.bool,
78
+ ctaClassName: PropTypes.string,
79
+ ctaNode: PropTypes.node,
80
+ children: PropTypes.node,
81
+ showCta: PropTypes.bool,
82
+ };
83
+
84
+ TemplatesActionBar.defaultProps = {
85
+ searchValue: '',
86
+ onSearchChange: () => {},
87
+ onSearch: () => {},
88
+ onClear: () => {},
89
+ searchPlaceholder: '',
90
+ searchInputClassName: '',
91
+ searchInputStyle: { width: '210px' },
92
+ ctaLabel: null,
93
+ onCtaClick: () => {},
94
+ ctaDisabled: false,
95
+ ctaClassName: '',
96
+ ctaNode: null,
97
+ children: null,
98
+ };
99
+
100
+ export default TemplatesActionBar;
101
+
@@ -659,11 +659,27 @@
659
659
  }
660
660
 
661
661
  .action-container{
662
- margin-top: 8px;
663
- margin-bottom: 16px;
662
+ margin-top: $CAP_SPACE_08;
663
+ margin-bottom: $CAP_SPACE_16;
664
664
  display: flex;
665
665
  justify-content: space-between;
666
666
  align-items: center;
667
+
668
+ &__toolbar-row {
669
+ display: flex;
670
+ align-items: center;
671
+ gap: 0.75rem;
672
+ }
673
+
674
+ &__toolbar-row .search-text {
675
+ width: 13.125rem;
676
+ }
677
+
678
+ &__create-row {
679
+ display: flex;
680
+ justify-content: space-between;
681
+ align-items: center;
682
+ }
667
683
  }
668
684
 
669
685
  .popover-action-container:hover{
@@ -1101,4 +1117,47 @@
1101
1117
  .inapp-illustration-parent {
1102
1118
  height: "calc(100vh - 325px)";
1103
1119
  overflow: 'auto';
1120
+ }
1121
+
1122
+ /* Local SMS / slidebox: viewport-based .v2-pagination-container-half height leaves empty space below and clips cards */
1123
+ .creatives-templates-container--local-sms.library-mode {
1124
+ .creatives-templates-list.library-mode > .cap-row:first-of-type > div {
1125
+ display: flex;
1126
+ flex-direction: column;
1127
+ flex: 1 1 auto;
1128
+ min-height: 0;
1129
+ overflow: hidden;
1130
+ }
1131
+
1132
+ .creatives-templates-list.library-mode > .cap-row:first-of-type > div > div:first-child {
1133
+ display: flex;
1134
+ flex-direction: column;
1135
+ flex: 1 1 auto;
1136
+ min-height: 0;
1137
+ overflow: hidden;
1138
+ }
1139
+
1140
+ /* Block below search/filter: grid + skeletons — must grow to use space above footer */
1141
+ .creatives-templates-list.library-mode > .cap-row:first-of-type > div > div:first-child > div:nth-child(2) {
1142
+ flex: 1 1 auto;
1143
+ min-height: 0;
1144
+ display: flex;
1145
+ flex-direction: column;
1146
+ overflow: hidden;
1147
+ }
1148
+
1149
+ /*
1150
+ * Scroll needs a definite height. Pure flex + height:auto + max-height:100% often won’t bound (no % base), so no scrollbar.
1151
+ * Use a taller slice than global 100vh-20rem so the grid uses space under search; still overflow-y:auto for long lists.
1152
+ */
1153
+ .v2-pagination-container,
1154
+ .v2-pagination-container-half {
1155
+ flex: 0 1 auto;
1156
+ min-height: 0;
1157
+ height: calc(100vh - 12rem);
1158
+ max-height: calc(100vh - 12rem);
1159
+ overflow-y: auto;
1160
+ overflow-x: hidden;
1161
+ -webkit-overflow-scrolling: touch;
1162
+ }
1104
1163
  }
@@ -15,6 +15,17 @@ export function getAllTemplates(channel, queryParams, intlCopyOf = '') {
15
15
  };
16
16
  }
17
17
 
18
+
19
+ export function getLocalSmsTemplates(queryParams, intlCopyOf = '', onSuccess, onFailure) {
20
+ return {
21
+ type: types.GET_LOCAL_SMS_TEMPLATES_REQUEST,
22
+ queryParams,
23
+ intlCopyOf,
24
+ onSuccess,
25
+ onFailure,
26
+ };
27
+ }
28
+
18
29
  export function resetTemplate() {
19
30
  return {
20
31
  type: types.RESET_TEMPLATE,
@@ -10,6 +10,8 @@ export const GET_ALL_TEMPLATES_REQUEST = 'app/v2Containers/Templates/GET_ALL_TEM
10
10
  export const GET_ALL_TEMPLATES_SUCCESS = 'app/v2Containers/Templates/GET_ALL_TEMPLATES_SUCCESS';
11
11
  export const GET_ALL_TEMPLATES_FAILURE = 'app/v2Containers/Templates/GET_ALL_TEMPLATES_FAILURE';
12
12
 
13
+ export const GET_LOCAL_SMS_TEMPLATES_REQUEST = 'app/v2Containers/Templates/GET_LOCAL_SMS_TEMPLATES_REQUEST';
14
+
13
15
  export const DELETE_TEMPLATE_REQUEST = 'app/v2Containers/Templates/DELETE_TEMPLATE_REQUEST';
14
16
  export const DELETE_RCS_TEMPLATE_REQUEST = 'app/v2Containers/Templates/DELETE_RCS_TEMPLATE_REQUEST';
15
17
  export const DELETE_TEMPLATE_SUCCESS = 'app/v2Containers/Templates/DELETE_TEMPLATE_SUCCESS';