@capillarytech/creatives-library 8.0.345-alpha.11 → 8.0.345-alpha.12

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 (126) hide show
  1. package/constants/unified.js +29 -0
  2. package/package.json +1 -1
  3. package/services/tests/api.test.js +13 -0
  4. package/utils/commonUtils.js +19 -1
  5. package/utils/rcsPayloadUtils.js +92 -0
  6. package/utils/templateVarUtils.js +201 -0
  7. package/utils/tests/templateVarUtils.test.js +204 -0
  8. package/v2Components/CapActionButton/constants.js +7 -0
  9. package/v2Components/CapActionButton/index.js +167 -109
  10. package/v2Components/CapActionButton/index.scss +157 -6
  11. package/v2Components/CapActionButton/messages.js +19 -3
  12. package/v2Components/CapActionButton/tests/index.test.js +41 -17
  13. package/v2Components/CapTagList/index.js +10 -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 +10 -5
  22. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +160 -15
  23. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js.rej +18 -0
  24. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +341 -76
  25. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +133 -4
  26. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
  27. package/v2Components/CommonTestAndPreview/constants.js +38 -2
  28. package/v2Components/CommonTestAndPreview/index.js +676 -186
  29. package/v2Components/CommonTestAndPreview/messages.js +49 -3
  30. package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
  31. package/v2Components/CommonTestAndPreview/sagas.js +15 -6
  32. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +308 -284
  33. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +231 -65
  34. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
  35. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
  36. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
  37. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +34 -13
  38. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
  39. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
  40. package/v2Components/CommonTestAndPreview/tests/index.test.js +132 -4
  41. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
  42. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
  43. package/v2Components/FormBuilder/index.js +8 -10
  44. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +87 -0
  45. package/v2Components/SmsFallback/constants.js +73 -0
  46. package/v2Components/SmsFallback/index.js +955 -0
  47. package/v2Components/SmsFallback/index.scss +265 -0
  48. package/v2Components/SmsFallback/messages.js +78 -0
  49. package/v2Components/SmsFallback/smsFallbackUtils.js +118 -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 +277 -0
  55. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
  56. package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
  57. package/v2Components/TemplatePreview/_templatePreview.scss +33 -23
  58. package/v2Components/TemplatePreview/constants.js +2 -0
  59. package/v2Components/TemplatePreview/index.js +143 -28
  60. package/v2Components/TemplatePreview/tests/index.test.js +142 -0
  61. package/v2Components/TestAndPreviewSlidebox/index.js +13 -1
  62. package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
  63. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
  64. package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
  65. package/v2Components/VarSegmentMessageEditor/index.js +125 -0
  66. package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
  67. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +43 -0
  68. package/v2Containers/CreativesContainer/SlideBoxContent.js +37 -24
  69. package/v2Containers/CreativesContainer/SlideBoxFooter.js +10 -1
  70. package/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
  71. package/v2Containers/CreativesContainer/constants.js +9 -0
  72. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +67 -0
  73. package/v2Containers/CreativesContainer/index.js +301 -107
  74. package/v2Containers/CreativesContainer/index.scss +51 -1
  75. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
  76. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +78 -34
  77. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +79 -16
  78. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
  79. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +357 -98
  80. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
  81. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
  82. package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
  83. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
  84. package/v2Containers/Email/index.js +2 -46
  85. package/v2Containers/Rcs/constants.js +119 -8
  86. package/v2Containers/Rcs/index.js +2370 -807
  87. package/v2Containers/Rcs/index.js.rej +1336 -0
  88. package/v2Containers/Rcs/index.scss +276 -6
  89. package/v2Containers/Rcs/index.scss.rej +74 -0
  90. package/v2Containers/Rcs/messages.js +38 -3
  91. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +225 -0
  92. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +98302 -70345
  93. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
  94. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap.rej +128 -0
  95. package/v2Containers/Rcs/tests/index.test.js +152 -121
  96. package/v2Containers/Rcs/tests/mockData.js +38 -0
  97. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +318 -0
  98. package/v2Containers/Rcs/tests/utils.test.js +646 -30
  99. package/v2Containers/Rcs/utils.js +478 -11
  100. package/v2Containers/Sms/Create/index.js +100 -40
  101. package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
  102. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  103. package/v2Containers/SmsTrai/Create/index.js +9 -4
  104. package/v2Containers/SmsTrai/Edit/constants.js +2 -0
  105. package/v2Containers/SmsTrai/Edit/index.js +636 -130
  106. package/v2Containers/SmsTrai/Edit/index.scss +121 -0
  107. package/v2Containers/SmsTrai/Edit/messages.js +14 -4
  108. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4328 -2375
  109. package/v2Containers/SmsWrapper/index.js +37 -8
  110. package/v2Containers/TagList/index.js +6 -0
  111. package/v2Containers/Templates/TemplatesActionBar.js +101 -0
  112. package/v2Containers/Templates/_templates.scss +163 -2
  113. package/v2Containers/Templates/actions.js +11 -0
  114. package/v2Containers/Templates/constants.js +2 -0
  115. package/v2Containers/Templates/index.js +119 -54
  116. package/v2Containers/Templates/sagas.js +57 -12
  117. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
  118. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1043 -1079
  119. package/v2Containers/Templates/tests/sagas.test.js +193 -123
  120. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
  121. package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
  122. package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
  123. package/v2Containers/TemplatesV2/index.js +86 -23
  124. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
  125. package/v2Containers/Whatsapp/index.js +3 -20
  126. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
@@ -0,0 +1,258 @@
1
+ import {
2
+ CAP_SPACE_32,
3
+ CAP_SPACE_56,
4
+ CAP_SPACE_64,
5
+ } from '@capillarytech/cap-ui-library/styled/variables';
6
+ import {
7
+ isDeepEmpty,
8
+ getSlideBoxWrapperMarginFromLiquidErrors,
9
+ computeLiquidFooterUpdateFromFormBuilder,
10
+ } from '../embeddedSlideboxUtils';
11
+ import { ANDROID, IOS, MOBILE_PUSH, LIQUID_ERROR_MSG, STANDARD_ERROR_MSG } from '../constants';
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // isDeepEmpty
15
+ // ---------------------------------------------------------------------------
16
+
17
+ describe('isDeepEmpty', () => {
18
+ it('returns true for null', () => {
19
+ expect(isDeepEmpty(null)).toBe(true);
20
+ });
21
+
22
+ it('returns true for undefined', () => {
23
+ expect(isDeepEmpty(undefined)).toBe(true);
24
+ });
25
+
26
+ it('returns true for empty string', () => {
27
+ expect(isDeepEmpty('')).toBe(true);
28
+ });
29
+
30
+ it('returns false for non-empty string', () => {
31
+ expect(isDeepEmpty('hello')).toBe(false);
32
+ });
33
+
34
+ it('returns true for empty array', () => {
35
+ expect(isDeepEmpty([])).toBe(true);
36
+ });
37
+
38
+ it('returns false for non-empty array', () => {
39
+ expect(isDeepEmpty(['item'])).toBe(false);
40
+ });
41
+
42
+ it('returns true for empty object', () => {
43
+ expect(isDeepEmpty({})).toBe(true);
44
+ });
45
+
46
+ it('returns true for object where all values are deep-empty', () => {
47
+ expect(isDeepEmpty({ a: null, b: '', c: [] })).toBe(true);
48
+ });
49
+
50
+ it('returns false for object with at least one non-empty value', () => {
51
+ expect(isDeepEmpty({ a: null, b: 'something' })).toBe(false);
52
+ });
53
+
54
+ it('returns true for nested all-empty object', () => {
55
+ expect(isDeepEmpty({ a: { b: null, c: [] } })).toBe(true);
56
+ });
57
+
58
+ it('returns false for nested object with a non-empty leaf', () => {
59
+ expect(isDeepEmpty({ a: { b: 'value' } })).toBe(false);
60
+ });
61
+
62
+ it('returns false for a number (non-null, non-string, non-array, non-object)', () => {
63
+ expect(isDeepEmpty(0)).toBe(false);
64
+ expect(isDeepEmpty(42)).toBe(false);
65
+ });
66
+
67
+ it('returns false for a boolean false', () => {
68
+ expect(isDeepEmpty(false)).toBe(false);
69
+ });
70
+ });
71
+
72
+ // ---------------------------------------------------------------------------
73
+ // getSlideBoxWrapperMarginFromLiquidErrors
74
+ // ---------------------------------------------------------------------------
75
+
76
+ describe('getSlideBoxWrapperMarginFromLiquidErrors', () => {
77
+ it('returns 0 when liquidErrorMessage is null', () => {
78
+ expect(getSlideBoxWrapperMarginFromLiquidErrors(null)).toBe(0);
79
+ });
80
+
81
+ it('returns 0 when liquidErrorMessage is undefined', () => {
82
+ expect(getSlideBoxWrapperMarginFromLiquidErrors(undefined)).toBe(0);
83
+ });
84
+
85
+ it('returns 0 when both error arrays are empty', () => {
86
+ expect(getSlideBoxWrapperMarginFromLiquidErrors({
87
+ STANDARD_ERROR_MSG: [],
88
+ LIQUID_ERROR_MSG: [],
89
+ })).toBe(0);
90
+ });
91
+
92
+ it('returns 0 when neither error array is present', () => {
93
+ expect(getSlideBoxWrapperMarginFromLiquidErrors({})).toBe(0);
94
+ });
95
+
96
+ it('returns CAP_SPACE_64 when both STANDARD and LIQUID errors are present', () => {
97
+ expect(getSlideBoxWrapperMarginFromLiquidErrors({
98
+ STANDARD_ERROR_MSG: ['std error'],
99
+ LIQUID_ERROR_MSG: ['liquid error'],
100
+ })).toBe(CAP_SPACE_64);
101
+ });
102
+
103
+ it('returns CAP_SPACE_56 when only LIQUID errors are present', () => {
104
+ expect(getSlideBoxWrapperMarginFromLiquidErrors({
105
+ STANDARD_ERROR_MSG: [],
106
+ LIQUID_ERROR_MSG: ['liquid error'],
107
+ })).toBe(CAP_SPACE_56);
108
+ });
109
+
110
+ it('returns CAP_SPACE_32 when only STANDARD errors are present', () => {
111
+ expect(getSlideBoxWrapperMarginFromLiquidErrors({
112
+ STANDARD_ERROR_MSG: ['std error'],
113
+ LIQUID_ERROR_MSG: [],
114
+ })).toBe(CAP_SPACE_32);
115
+ });
116
+
117
+ it('returns CAP_SPACE_56 when LIQUID has multiple errors and STANDARD is absent', () => {
118
+ expect(getSlideBoxWrapperMarginFromLiquidErrors({
119
+ LIQUID_ERROR_MSG: ['err1', 'err2'],
120
+ })).toBe(CAP_SPACE_56);
121
+ });
122
+
123
+ it('returns CAP_SPACE_32 when STANDARD has multiple errors and LIQUID is absent', () => {
124
+ expect(getSlideBoxWrapperMarginFromLiquidErrors({
125
+ STANDARD_ERROR_MSG: ['err1', 'err2'],
126
+ })).toBe(CAP_SPACE_32);
127
+ });
128
+ });
129
+
130
+ // ---------------------------------------------------------------------------
131
+ // computeLiquidFooterUpdateFromFormBuilder
132
+ // ---------------------------------------------------------------------------
133
+
134
+ describe('computeLiquidFooterUpdateFromFormBuilder', () => {
135
+ const noErrors = { [LIQUID_ERROR_MSG]: [], [STANDARD_ERROR_MSG]: [] };
136
+ const liquidOnly = { [LIQUID_ERROR_MSG]: ['liquid err'], [STANDARD_ERROR_MSG]: [] };
137
+ const standardOnly = { [LIQUID_ERROR_MSG]: [], [STANDARD_ERROR_MSG]: ['std err'] };
138
+ const bothErrors = { [LIQUID_ERROR_MSG]: ['l'], [STANDARD_ERROR_MSG]: ['s'] };
139
+
140
+ describe('normal (non-null) returns', () => {
141
+ it('returns isLiquidValidationError=false when no errors', () => {
142
+ const result = computeLiquidFooterUpdateFromFormBuilder(noErrors, 1, {
143
+ previousIsLiquidValidationError: false,
144
+ currentChannelUpper: 'SMS',
145
+ });
146
+ expect(result).not.toBeNull();
147
+ expect(result.isLiquidValidationError).toBe(false);
148
+ expect(result.liquidErrorMessage).toEqual(noErrors);
149
+ });
150
+
151
+ it('sets isLiquidValidationError=true when liquid errors present', () => {
152
+ const result = computeLiquidFooterUpdateFromFormBuilder(liquidOnly, 1, {
153
+ previousIsLiquidValidationError: false,
154
+ currentChannelUpper: 'SMS',
155
+ });
156
+ expect(result.isLiquidValidationError).toBe(true);
157
+ });
158
+
159
+ it('sets isLiquidValidationError=true when standard errors present', () => {
160
+ const result = computeLiquidFooterUpdateFromFormBuilder(standardOnly, 1, {
161
+ previousIsLiquidValidationError: false,
162
+ currentChannelUpper: 'SMS',
163
+ });
164
+ expect(result.isLiquidValidationError).toBe(true);
165
+ });
166
+
167
+ it('sets isLiquidValidationError=true when both errors present', () => {
168
+ const result = computeLiquidFooterUpdateFromFormBuilder(bothErrors, 1, {
169
+ previousIsLiquidValidationError: false,
170
+ currentChannelUpper: 'SMS',
171
+ });
172
+ expect(result.isLiquidValidationError).toBe(true);
173
+ });
174
+
175
+ it('maps tab 1 to ANDROID', () => {
176
+ const result = computeLiquidFooterUpdateFromFormBuilder(noErrors, 1, {
177
+ previousIsLiquidValidationError: false,
178
+ currentChannelUpper: 'SMS',
179
+ });
180
+ expect(result.activeFormBuilderTab).toBe(ANDROID);
181
+ });
182
+
183
+ it('maps tab 2 to IOS', () => {
184
+ const result = computeLiquidFooterUpdateFromFormBuilder(noErrors, 2, {
185
+ previousIsLiquidValidationError: false,
186
+ currentChannelUpper: 'SMS',
187
+ });
188
+ expect(result.activeFormBuilderTab).toBe(IOS);
189
+ });
190
+
191
+ it('maps tab 3 (or any other tab) to null', () => {
192
+ const result = computeLiquidFooterUpdateFromFormBuilder(noErrors, 3, {
193
+ previousIsLiquidValidationError: false,
194
+ currentChannelUpper: 'SMS',
195
+ });
196
+ expect(result.activeFormBuilderTab).toBeNull();
197
+ });
198
+
199
+ it('maps undefined tab to null for activeFormBuilderTab', () => {
200
+ const result = computeLiquidFooterUpdateFromFormBuilder(noErrors, undefined, {
201
+ previousIsLiquidValidationError: false,
202
+ currentChannelUpper: 'SMS',
203
+ });
204
+ expect(result.activeFormBuilderTab).toBeNull();
205
+ });
206
+
207
+ it('does NOT return null for Mobile Push when errors ARE present', () => {
208
+ const result = computeLiquidFooterUpdateFromFormBuilder(liquidOnly, 1, {
209
+ previousIsLiquidValidationError: true,
210
+ currentChannelUpper: MOBILE_PUSH,
211
+ });
212
+ expect(result).not.toBeNull();
213
+ expect(result.isLiquidValidationError).toBe(true);
214
+ });
215
+
216
+ it('does NOT return null for non-Mobile Push channel even with previousIsLiquidValidationError', () => {
217
+ const result = computeLiquidFooterUpdateFromFormBuilder(noErrors, 1, {
218
+ previousIsLiquidValidationError: true,
219
+ currentChannelUpper: 'SMS',
220
+ });
221
+ expect(result).not.toBeNull();
222
+ expect(result.isLiquidValidationError).toBe(false);
223
+ });
224
+
225
+ it('works without options argument (uses defaults)', () => {
226
+ const result = computeLiquidFooterUpdateFromFormBuilder(noErrors, 1);
227
+ expect(result).not.toBeNull();
228
+ expect(result.isLiquidValidationError).toBe(false);
229
+ });
230
+ });
231
+
232
+ describe('Mobile Push OLD clear (returns null)', () => {
233
+ it('returns null when no errors and previousIsLiquidValidationError is true for MOBILEPUSH', () => {
234
+ const result = computeLiquidFooterUpdateFromFormBuilder(noErrors, 1, {
235
+ previousIsLiquidValidationError: true,
236
+ currentChannelUpper: MOBILE_PUSH,
237
+ });
238
+ expect(result).toBeNull();
239
+ });
240
+
241
+ it('does NOT return null when previousIsLiquidValidationError is false for MOBILEPUSH', () => {
242
+ const result = computeLiquidFooterUpdateFromFormBuilder(noErrors, 1, {
243
+ previousIsLiquidValidationError: false,
244
+ currentChannelUpper: MOBILE_PUSH,
245
+ });
246
+ expect(result).not.toBeNull();
247
+ });
248
+
249
+ it('does NOT return null when only standard errors cleared but liquid error remains for MOBILEPUSH', () => {
250
+ const result = computeLiquidFooterUpdateFromFormBuilder(liquidOnly, 1, {
251
+ previousIsLiquidValidationError: true,
252
+ currentChannelUpper: MOBILE_PUSH,
253
+ });
254
+ // hasLiquid = true → condition !hasLiquid is false → does not return null
255
+ expect(result).not.toBeNull();
256
+ });
257
+ });
258
+ });
@@ -173,19 +173,35 @@ describe('Test SlideBoxContent container', () => {
173
173
  expect(getCreativesData).toHaveBeenCalledWith({ channel: 'RCS' });
174
174
  });
175
175
 
176
- it('RCS getFormData does not attach smsFallBackContent to templateData', async () => {
177
- renderFunction('RCS', 'editTemplate', rcsTemplates, rcsEditTemplateData);
176
+ it('RCS getFormData mirrors smsFallBackContent onto templateData for library round-trip', async () => {
177
+ renderedComponent = shallowWithIntl(
178
+ <Creatives
179
+ loyaltyMetaData={loyaltyMetaData}
180
+ Templates={rcsTemplates}
181
+ channel="RCS"
182
+ slidBoxContent="editTemplate"
183
+ templateData={rcsEditTemplateData}
184
+ handleCloseCreatives={handleCloseCreatives}
185
+ getCreativesData={getCreativesData}
186
+ isFullMode={false}
187
+ templateActions={{
188
+ getCdnTransformationConfig,
189
+ }}
190
+ />,
191
+ );
178
192
  renderedComponent.instance().onEditTemplate();
179
193
 
180
194
  const mockValue = {
195
+ validity: true,
181
196
  value: {
182
197
  name: 'rcs_creative_name',
198
+ type: 'RCS',
183
199
  versions: {
184
200
  base: {
185
201
  content: {
186
202
  RCS: {
187
203
  rcsContent: { contentType: 'RICHCARD', cardType: 'STANDALONE', cardSettings: {}, cardContent: [{}] },
188
- smsFallBackContent: { message: 'should-not-be-copied' },
204
+ smsFallBackContent: { message: 'fallback-body' },
189
205
  },
190
206
  },
191
207
  },
@@ -193,16 +209,62 @@ describe('Test SlideBoxContent container', () => {
193
209
  },
194
210
  };
195
211
 
196
- renderedComponent
197
- .find('CapSlideBox')
198
- .props()
199
- .content.props.getFormData(mockValue);
212
+ // Call getFormData on the container instance — shallow CapSlideBox `content` shape can differ across wrappers
213
+ renderedComponent.instance().getFormData(mockValue);
200
214
  await tick();
201
215
 
202
216
  const instance = renderedComponent.instance();
203
217
  expect(instance.state.templateData).toBeDefined();
204
- // sms fallback content should not be set on templateData per current logic
205
- expect(instance.state.templateData.smsFallBackContent).toBeUndefined();
218
+ expect(instance.state.templateData.smsFallBackContent).toEqual({ message: 'fallback-body' });
219
+ });
220
+
221
+ it('RCS getFormData mirrors rcsCardVarMapped onto templateData for campaign reopen', async () => {
222
+ renderedComponent = shallowWithIntl(
223
+ <Creatives
224
+ loyaltyMetaData={loyaltyMetaData}
225
+ Templates={rcsTemplates}
226
+ channel="RCS"
227
+ slidBoxContent="editTemplate"
228
+ templateData={rcsEditTemplateData}
229
+ handleCloseCreatives={handleCloseCreatives}
230
+ getCreativesData={getCreativesData}
231
+ isFullMode={false}
232
+ templateActions={{
233
+ getCdnTransformationConfig,
234
+ }}
235
+ />,
236
+ );
237
+ renderedComponent.instance().onEditTemplate();
238
+
239
+ const cardVarMapped = { 1: '[Name]', user_name: '[Name]' };
240
+ const mockValue = {
241
+ validity: true,
242
+ value: {
243
+ name: 'rcs_creative_name',
244
+ type: 'RCS',
245
+ versions: {
246
+ base: {
247
+ content: {
248
+ RCS: {
249
+ rcsContent: {
250
+ contentType: 'RICHCARD',
251
+ cardType: 'STANDALONE',
252
+ cardSettings: {},
253
+ cardContent: [{ title: 'Hi {{user_name}}', cardVarMapped }],
254
+ },
255
+ smsFallBackContent: {},
256
+ },
257
+ },
258
+ },
259
+ },
260
+ },
261
+ };
262
+
263
+ renderedComponent.instance().getFormData(mockValue);
264
+ await tick();
265
+
266
+ const instance = renderedComponent.instance();
267
+ expect(instance.state.templateData.rcsCardVarMapped).toEqual(cardVarMapped);
206
268
  });
207
269
 
208
270
  it('Text getCreatives data for rcs, data from creatives done to campaigns', async () => {
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Covers local-templates flag resolution: nested `localTemplatesConfig.useLocalTemplates`
3
+ * vs top-level `useLocalTemplates` (embedded consumers / SlideBoxContent).
4
+ */
5
+ import React from 'react';
6
+ import { shallowWithIntl } from '../../../helpers/intl-enzym-test-helpers';
7
+ import { Creatives } from '../index';
8
+ import mockdata from '../../mockdata';
9
+
10
+ const { smsTemplates, loyaltyMetaData } = mockdata;
11
+
12
+ jest.mock('../../../v2Components/FormBuilder', () => ({
13
+ __esModule: true,
14
+ default: (props) => (
15
+ <div className="FormBuilder-mock" {...props}>
16
+ FormBuilder
17
+ </div>
18
+ ),
19
+ }));
20
+
21
+ const baseProps = {
22
+ loyaltyMetaData,
23
+ Templates: smsTemplates,
24
+ channel: 'SMS',
25
+ creativesMode: 'create',
26
+ templateData: null,
27
+ isFullMode: true,
28
+ handleCloseCreatives: jest.fn(),
29
+ getCreativesData: jest.fn(),
30
+ templateActions: {
31
+ getCdnTransformationConfig: jest.fn(),
32
+ resetTemplateStoreData: jest.fn(),
33
+ },
34
+ globalActions: {
35
+ clearMetaEntities: jest.fn(),
36
+ },
37
+ };
38
+
39
+ describe('CreativesContainer useLocalTemplates prop resolution', () => {
40
+ beforeEach(() => {
41
+ jest.clearAllMocks();
42
+ });
43
+
44
+ it('initial slidebox is templates when only top-level useLocalTemplates is true', () => {
45
+ const wrapper = shallowWithIntl(
46
+ <Creatives
47
+ {...baseProps}
48
+ useLocalTemplates
49
+ />,
50
+ );
51
+ expect(wrapper.instance().state.slidBoxContent).toBe('templates');
52
+ });
53
+
54
+ it('initial slidebox is templates when localTemplatesConfig.useLocalTemplates is true', () => {
55
+ const wrapper = shallowWithIntl(
56
+ <Creatives
57
+ {...baseProps}
58
+ localTemplatesConfig={{ useLocalTemplates: true }}
59
+ />,
60
+ );
61
+ expect(wrapper.instance().state.slidBoxContent).toBe('templates');
62
+ });
63
+
64
+ it('does not reset template store on unmount when embedded and top-level useLocalTemplates is true', () => {
65
+ const wrapper = shallowWithIntl(
66
+ <Creatives
67
+ {...baseProps}
68
+ location={{ query: { type: 'embedded' } }}
69
+ useLocalTemplates
70
+ />,
71
+ );
72
+ wrapper.unmount();
73
+ expect(baseProps.templateActions.resetTemplateStoreData).not.toHaveBeenCalled();
74
+ expect(baseProps.globalActions.clearMetaEntities).toHaveBeenCalled();
75
+ });
76
+
77
+ it('resets template store on unmount when embedded and local templates are not used', () => {
78
+ const wrapper = shallowWithIntl(
79
+ <Creatives
80
+ {...baseProps}
81
+ location={{ query: { type: 'embedded' } }}
82
+ useLocalTemplates={false}
83
+ />,
84
+ );
85
+ wrapper.unmount();
86
+ expect(baseProps.templateActions.resetTemplateStoreData).toHaveBeenCalled();
87
+ expect(baseProps.globalActions.clearMetaEntities).toHaveBeenCalled();
88
+ });
89
+
90
+ it('prefers nested localTemplatesConfig.useLocalTemplates=false over top-level true (not local list mode)', () => {
91
+ const wrapper = shallowWithIntl(
92
+ <Creatives
93
+ {...baseProps}
94
+ localTemplatesConfig={{ useLocalTemplates: false }}
95
+ useLocalTemplates
96
+ />,
97
+ );
98
+ expect(wrapper.instance().state.slidBoxContent).toBe('createTemplate');
99
+ });
100
+
101
+ it('uses top-level useLocalTemplates when localTemplatesConfig omits the flag', () => {
102
+ const wrapper = shallowWithIntl(
103
+ <Creatives
104
+ {...baseProps}
105
+ localTemplatesConfig={{}}
106
+ useLocalTemplates
107
+ />,
108
+ );
109
+ expect(wrapper.instance().state.slidBoxContent).toBe('templates');
110
+ });
111
+
112
+ it('resets template store when nested useLocalTemplates is false even if top-level is true', () => {
113
+ const wrapper = shallowWithIntl(
114
+ <Creatives
115
+ {...baseProps}
116
+ location={{ query: { type: 'embedded' } }}
117
+ localTemplatesConfig={{ useLocalTemplates: false }}
118
+ useLocalTemplates
119
+ />,
120
+ );
121
+ wrapper.unmount();
122
+ expect(baseProps.templateActions.resetTemplateStoreData).toHaveBeenCalled();
123
+ expect(baseProps.globalActions.clearMetaEntities).toHaveBeenCalled();
124
+ });
125
+ });
@@ -32,7 +32,7 @@ import * as creativesContainerActions from '../CreativesContainer/actions';
32
32
  import withCreatives from '../../hoc/withCreatives';
33
33
  import { EMAIL } from '../CreativesContainer/constants';
34
34
  import { GA } from '@capillarytech/cap-ui-utils';
35
- import { TRACK_CREATE_EMAIL, TRACK_EDIT_EMAIL, BEE_PLUGIN, CREATE, EDIT, LOYALTY } from '../App/constants';
35
+ import { TRACK_CREATE_EMAIL, TRACK_EDIT_EMAIL, BEE_PLUGIN, CREATE, EDIT } from '../App/constants';
36
36
  import { FONT_COLOR_05 } from '@capillarytech/cap-ui-library/styled/variables';
37
37
  import { gtmPush } from '../../utils/gtmTrackers';
38
38
  const {CapCustomCardList} = CapCustomCard;
@@ -98,8 +98,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
98
98
  this.edmEvent = undefined;
99
99
  // When schema is set after CmsSettings (e.g. library create BEE), allow BEE init to run once
100
100
  this.schemaJustFilledForBee = false;
101
- // Guard: prevent startTemplateCreation being called twice (componentDidMount + componentWillReceiveProps)
102
- this.templateCreationStarted = false;
103
101
  this.supportedLanguages = this.getSupportedLanguages(props);
104
102
  this.map = {
105
103
  "template-name": {
@@ -226,7 +224,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
226
224
  || _.get(this.props.Templates.BEETemplate, 'versions.base.drag_drop_id')
227
225
  || _.get(this.props.Templates.BEETemplate, 'versions.base.id')
228
226
  || this.props.Templates.BEETemplate?._id;
229
- console.log('[Rafeeq manzoor] comes here 1');
230
227
  this.props.actions.getCmsSetting(BEE_PLUGIN, dragDropId, 'open', undefined, isBEESupport, isBEEAppEnable);
231
228
  } else if (this.props.location.query.module !== "library" || (this.props.location.query.module === "library" && !this.props.templateData)) {
232
229
  // Extract drag_drop_id - check multiple possible paths
@@ -237,7 +234,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
237
234
  || _.get(this.props.Templates.BEETemplate, 'versions.base.drag_drop_id')
238
235
  || _.get(this.props.Templates.BEETemplate, 'versions.base.id')
239
236
  || this.props.Templates.BEETemplate?._id;
240
- console.log('[Rafeeq manzoor] comes here 2');
241
237
  this.props.actions.getCmsSetting(BEE_PLUGIN, dragDropId, 'create', undefined, isBEESupport, isBEEAppEnable);
242
238
  }
243
239
  }
@@ -290,10 +286,8 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
290
286
 
291
287
  if (this.props.params.id) {
292
288
  const activeTabForLang = beeTemplate.versions?.base?.activeTab || 'en';
293
- console.log('[Rafeeq manzoor] comes here 3');
294
289
  this.props.actions.getCmsSetting(BEE_PLUGIN, dragDropId, 'open', activeTabForLang, isBEESupport, isBEEAppEnable);
295
290
  } else {
296
- console.log('[Rafeeq manzoor] comes here 4');
297
291
  this.props.actions.getCmsSetting(BEE_PLUGIN, dragDropId, 'create', undefined, isBEESupport, isBEEAppEnable);
298
292
  }
299
293
  }
@@ -304,24 +298,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
304
298
  if (!this.props.params?.id && this.props.Templates?.selectedEmailLayout) {
305
299
  this.setState((prev) => ({ loadingStatus: Math.max(prev.loadingStatus, 2) }));
306
300
  }
307
-
308
- // For loyalty library mode: componentWillReceiveProps may never fire if Redux state is
309
- // already stable after mount (schema cached, BEETemplate already empty). Call
310
- // startTemplateCreation directly here so the BEE editor always initialises.
311
- if (
312
- !this.templateCreationStarted
313
- && this.state.isEdit
314
- && this.props.moduleType === LOYALTY
315
- && this.props.location.query.module === 'library'
316
- && !_.isEmpty(this.props.templateData)
317
- && !this.props.params.id
318
- && !this.props.isGetFormData
319
- && _.isEmpty(_.get(this, `state.formData['template-subject']`))
320
- ) {
321
- this.templateCreationStarted = true;
322
- console.log('[componentDidMount] loyalty library - calling startTemplateCreation directly', this.props.templateData);
323
- this.startTemplateCreation(this.props.templateData);
324
- }
325
301
  }
326
302
 
327
303
 
@@ -355,7 +331,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
355
331
  this.getFormData();
356
332
  }
357
333
  }
358
- console.log('[Rafeeq manzoor] nextProps', nextProps, _.isEmpty(this.state.formData));
359
334
  if (this.state.languageDataSet && nextProps.Templates.selectedEmailLayout && nextProps.Templates.selectedEmailLayout !== '' && !_.isEqual(this.props.Templates.selectedEmailLayout, nextProps.Templates.selectedEmailLayout )) {
360
335
  this.setNewLanguageContent(nextProps.Templates.selectedEmailLayout);
361
336
  }
@@ -384,7 +359,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
384
359
  }, 0);
385
360
  }
386
361
  if (nextProps.currentOrgDetails && nextProps.currentOrgDetails.basic_details && !_.isEmpty(nextProps.currentOrgDetails.basic_details) && _.isEmpty(this.state.formData)) {
387
- console.log('[Rafeeq manzoor] if condition');
388
362
  const formData = this.initFormData(nextProps);
389
363
  formData['template-version-options'] = [
390
364
  {
@@ -478,10 +452,8 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
478
452
  // Extract langId from active tab
479
453
  const activeTabForLang = beeTemplate.versions?.base?.activeTab || 'en';
480
454
  if (hasParamsId) {
481
- console.log('[Rafeeq manzoor] comes here 5');
482
455
  this.props.actions.getCmsSetting(BEE_PLUGIN, dragDropId, 'open', activeTabForLang, isBEESupport, isBEEAppEnable);
483
456
  } else if (nextProps.location.query.module !== "library" || (nextProps.location.query.module === "library" && !nextProps.templateData)) {
484
- console.log('[Rafeeq manzoor] comes here 6');
485
457
  this.props.actions.getCmsSetting(BEE_PLUGIN, dragDropId, 'create', undefined, isBEESupport, isBEEAppEnable);
486
458
  }
487
459
  }
@@ -498,12 +470,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
498
470
  }
499
471
  });
500
472
  }
501
- console.log('[Rafeeq manzoor] here we have data 1', this.state.isEdit, nextProps.location.query.module);
502
- console.log('[Rafeeq manzoor] here we have data 2', nextProps.templateData, !_.isEmpty(nextProps.templateData), this.props.params.id, nextProps.isGetFormData);
503
- console.log('[Rafeeq manzoor] here we have data 3', _.isEmpty(_.get(this, `state.formData['template-subject']`)));
504
-
505
- if (!this.templateCreationStarted && this.state.isEdit && nextProps.moduleType === LOYALTY && nextProps.location.query.module === "library" && !_.isEmpty(nextProps.templateData) && !this.props.params.id && !nextProps.isGetFormData && _.isEmpty(_.get(this, `state.formData['template-subject']`))) {
506
- this.templateCreationStarted = true;
473
+ if (this.state.isEdit && nextProps.location.query.module === "library" && !_.isEmpty(nextProps.templateData) && !this.props.params.id && !nextProps.isGetFormData && _.isEmpty(_.get(this, `state.formData['template-subject']`))) {
507
474
  this.startTemplateCreation(nextProps.templateData);
508
475
  }
509
476
  if (nextProps.location.query.module === 'library' && nextProps.isGetFormData && (this.props.isGetFormData !== nextProps.isGetFormData)) {
@@ -1271,7 +1238,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
1271
1238
  // formData.base.activeTab = language;
1272
1239
  // }
1273
1240
  if (language && editData.versions.base[language].is_drag_drop && isBEEAppEnable) {
1274
- console.log('[Rafeeq manzoor] comes here 7');
1275
1241
  this.props.actions.getCmsSetting(BEE_PLUGIN, editData._id, 'open', language, isBEESupport, isBEEAppEnable);
1276
1242
  }
1277
1243
  });
@@ -1495,7 +1461,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
1495
1461
  const currentTab = this.state.currentTab - 1;
1496
1462
  const baseLanguage = this.props.currentOrgDetails.basic_details.base_language ? this.props.currentOrgDetails.basic_details.base_language : 'en';
1497
1463
  if (formData[currentTab][baseLanguage].is_drag_drop && isBEEAppEnable) {
1498
- console.log('[Rafeeq manzoor] comes here 8');
1499
1464
  this.props.actions.getCmsSetting(BEE_PLUGIN, formData[currentTab][baseLanguage].drag_drop_id, 'duplicate', baseLanguage, isEdmSupport, isBEEAppEnable);
1500
1465
  }
1501
1466
  });
@@ -1674,9 +1639,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
1674
1639
  if (content.id) {
1675
1640
  tmpData.id = content.id;
1676
1641
  }
1677
- if (content.drag_drop_id) {
1678
- tmpData.drag_drop_id = content.drag_drop_id;
1679
- }
1680
1642
  }
1681
1643
  newFormData.base = _.cloneDeep(tmpData);
1682
1644
  newFormData.secondary_templates.push({template_data: tmpData});
@@ -2553,7 +2515,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
2553
2515
  const isBEEAppEnable = this.checkBeeEditorAllowedForLibrary();
2554
2516
  if (templates.drag_drop_id && isBEEAppEnable) {
2555
2517
  tempData.drag_drop_id = templates.drag_drop_id;
2556
- console.log('[Rafeeq manzoor] comes here 9');
2557
2518
  this.props.actions.getCmsSetting(BEE_PLUGIN, tempData._id, 'open', tempData.iso_code, isEdmSupport, isBEEAppEnable);
2558
2519
  }
2559
2520
  // formData.usingTabContainer = true;
@@ -2574,17 +2535,13 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
2574
2535
  });
2575
2536
  this.addLanguage(false, formData, false, additionalLanguages);
2576
2537
  }
2577
- console.log("[Rafeeq manzoor] comes here lib 1", formData[0])
2578
2538
  formData.base = _.cloneDeep(formData[0]);
2579
2539
  if (formData?.['template-subject'] !== '') {
2580
- console.log("[Rafeeq manzoor] comes here lib 1 if", formData)
2581
2540
  this.setState({formData, loading: false, injectedTags: data.tags, tabKey, isDragDrop: (data.base.is_drag_drop !== 0 )}, () => {
2582
2541
  // this.setState({tabKey: ''});
2583
2542
  const isBEEEnable = this.checkBeeEditorAllowedForLibrary();
2584
- console.log("[Rafeeq manzoor] comes here lib 1 isBEEEnable", isBEEEnable)
2585
2543
  _.forEach(formData[0].selectedLanguages, (language) => {
2586
2544
  if (formData[0][language].is_drag_drop && isBEEEnable) {
2587
- console.log('[Rafeeq manzoor] comes here 10');
2588
2545
  this.props.actions.getCmsSetting(BEE_PLUGIN, formData[0][language].drag_drop_id, 'open', language, isEdmSupport, isBEEEnable);
2589
2546
  }
2590
2547
  });
@@ -2927,7 +2884,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
2927
2884
  // let getQuery = '';
2928
2885
  const isEdmSupport = (this.props.location.query.isEdmSupport !== "false") || false;
2929
2886
  const isBEEAppEnable = this.checkBeeEditorAllowedForLibrary();
2930
- console.log('[Rafeeq manzoor] comes here 11');
2931
2887
  this.props.actions.getCmsSetting(BEE_PLUGIN, data._id, 'create', this.state.formData[this.state.currentTab - 1].activeTab, isEdmSupport, isBEEAppEnable);
2932
2888
  // this.props.templatesActions.setEdmTemplate(data);
2933
2889
  this.toggleEdmEmailTemplateSelection();