@capillarytech/creatives-library 9.0.15-alpha.3 → 9.0.16-beta.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 (146) hide show
  1. package/AppRoot.js +80 -0
  2. package/app.js +5 -3
  3. package/constants/unified.js +0 -29
  4. package/entry.js +67 -1
  5. package/global-styles.js +1 -1
  6. package/mfe-exposed-components.js +2 -4
  7. package/package.json +2 -2
  8. package/services/api.js +1 -1
  9. package/services/tests/api.test.js +20 -35
  10. package/styles/containers/layout/_layoutPage.scss +8 -6
  11. package/utils/commonUtils.js +1 -19
  12. package/utils/getDataLayer.js +15 -0
  13. package/utils/gtmTrackers/gtmEvents/creativeDetails.js +2 -1
  14. package/utils/mfeDetect.js +1 -0
  15. package/v2Components/CapActionButton/constants.js +1 -8
  16. package/v2Components/CapActionButton/index.js +144 -202
  17. package/v2Components/CapActionButton/index.scss +14 -232
  18. package/v2Components/CapActionButton/messages.js +3 -19
  19. package/v2Components/CapActionButton/tests/index.test.js +18 -42
  20. package/v2Components/CapImageUpload/index.js +2 -2
  21. package/v2Components/CapTagList/index.js +0 -10
  22. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +48 -70
  23. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
  24. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -214
  25. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
  26. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +9 -83
  27. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
  28. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -60
  29. package/v2Components/CommonTestAndPreview/SendTestMessage.js +5 -10
  30. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +22 -202
  31. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +80 -381
  32. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +8 -155
  33. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +0 -11
  34. package/v2Components/CommonTestAndPreview/constants.js +2 -38
  35. package/v2Components/CommonTestAndPreview/index.js +222 -761
  36. package/v2Components/CommonTestAndPreview/messages.js +3 -45
  37. package/v2Components/CommonTestAndPreview/sagas.js +6 -25
  38. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +0 -1
  39. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +267 -284
  40. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +65 -231
  41. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +5 -118
  42. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -341
  43. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +1 -8
  44. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +13 -34
  45. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +283 -281
  46. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
  47. package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -133
  48. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +24 -31
  49. package/v2Components/NavigationBar/index.js +9 -7
  50. package/v2Components/NavigationBar/mfeModuleHeader.config.js +16 -0
  51. package/v2Components/Pagination/_pagination.scss +2 -0
  52. package/v2Components/TemplatePreview/_templatePreview.scss +25 -45
  53. package/v2Components/TemplatePreview/index.js +32 -147
  54. package/v2Components/TemplatePreview/tests/index.test.js +0 -142
  55. package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +6 -6
  56. package/v2Components/TestAndPreviewSlidebox/index.js +1 -13
  57. package/v2Components/TestAndPreviewSlidebox/sagas.js +4 -11
  58. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +1 -3
  59. package/v2Containers/Cap/constants.js +1 -0
  60. package/v2Containers/Cap/index.js +47 -21
  61. package/v2Containers/Cap/sagas.js +19 -0
  62. package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +1 -4
  63. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +0 -4
  64. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +20 -20
  65. package/v2Containers/CreativesContainer/SlideBoxContent.js +7 -37
  66. package/v2Containers/CreativesContainer/SlideBoxFooter.js +5 -14
  67. package/v2Containers/CreativesContainer/SlideBoxHeader.js +5 -36
  68. package/v2Containers/CreativesContainer/constants.js +0 -11
  69. package/v2Containers/CreativesContainer/index.js +104 -323
  70. package/v2Containers/CreativesContainer/index.scss +1 -83
  71. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -79
  72. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
  73. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
  74. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +90 -333
  75. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +15 -20
  76. package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
  77. package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
  78. package/v2Containers/Rcs/constants.js +11 -131
  79. package/v2Containers/Rcs/index.js +829 -2682
  80. package/v2Containers/Rcs/index.scss +8 -443
  81. package/v2Containers/Rcs/messages.js +6 -45
  82. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +39409 -74176
  83. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +74 -10
  84. package/v2Containers/Rcs/tests/index.test.js +40 -151
  85. package/v2Containers/Rcs/tests/mockData.js +0 -38
  86. package/v2Containers/Rcs/tests/utils.test.js +30 -643
  87. package/v2Containers/Rcs/utils.js +12 -466
  88. package/v2Containers/Sms/Create/index.js +48 -115
  89. package/v2Containers/SmsTrai/Create/index.js +4 -9
  90. package/v2Containers/SmsTrai/Edit/constants.js +0 -2
  91. package/v2Containers/SmsTrai/Edit/index.js +169 -678
  92. package/v2Containers/SmsTrai/Edit/messages.js +4 -14
  93. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2854 -5455
  94. package/v2Containers/SmsWrapper/index.js +8 -37
  95. package/v2Containers/TagList/index.js +0 -6
  96. package/v2Containers/Templates/_templates.scss +11 -196
  97. package/v2Containers/Templates/actions.js +0 -11
  98. package/v2Containers/Templates/constants.js +0 -2
  99. package/v2Containers/Templates/index.js +60 -131
  100. package/v2Containers/Templates/sagas.js +13 -57
  101. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1015 -1060
  102. package/v2Containers/Templates/tests/sagas.test.js +16 -199
  103. package/v2Containers/TemplatesV2/TemplatesV2.style.js +10 -75
  104. package/v2Containers/TemplatesV2/index.js +43 -86
  105. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
  106. package/v2Containers/Whatsapp/index.js +25 -4
  107. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -578
  108. package/utils/rcsPayloadUtils.js +0 -92
  109. package/utils/templateVarUtils.js +0 -201
  110. package/utils/tests/rcsPayloadUtils.test.js +0 -226
  111. package/utils/tests/templateVarUtils.test.js +0 -204
  112. package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
  113. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
  114. package/v2Components/CommonTestAndPreview/utils.js +0 -84
  115. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -91
  116. package/v2Components/SmsFallback/constants.js +0 -94
  117. package/v2Components/SmsFallback/index.js +0 -958
  118. package/v2Components/SmsFallback/index.scss +0 -266
  119. package/v2Components/SmsFallback/messages.js +0 -78
  120. package/v2Components/SmsFallback/smsFallbackUtils.js +0 -120
  121. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
  122. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
  123. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
  124. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -208
  125. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -309
  126. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
  127. package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
  128. package/v2Components/TemplatePreview/constants.js +0 -2
  129. package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
  130. package/v2Components/VarSegmentMessageEditor/index.js +0 -125
  131. package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
  132. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -17
  133. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -79
  134. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
  135. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
  136. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
  137. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -227
  138. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -318
  139. package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
  140. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
  141. package/v2Containers/SmsTrai/Edit/index.scss +0 -126
  142. package/v2Containers/Templates/TemplatesActionBar.js +0 -101
  143. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
  144. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
  145. package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
  146. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +0 -131
@@ -1,20 +1,7 @@
1
1
  @import '~@capillarytech/cap-ui-library/styles/_variables';
2
2
 
3
- // Horizontal RCS button type radios (Phone number | URL | Quick reply)
4
- .cap-rcs-cta-type-radio.cap-radio-group-v2 {
5
- display: flex;
6
- flex-wrap: wrap;
7
- align-items: center;
8
- margin-top: 0.5rem;
9
-
10
- .ant-radio-wrapper {
11
- margin-right: $CAP_SPACE_24;
12
- margin-bottom: 0;
13
-
14
- &:last-child {
15
- margin-right: 0;
16
- }
17
- }
3
+ #rcs-cta-type{
4
+ width: 90%;
18
5
  }
19
6
 
20
7
  .cap-rcs-saved-cta {
@@ -23,36 +10,13 @@
23
10
  padding: 0.625rem;
24
11
  border-radius: $CAP_SPACE_04;
25
12
  margin-bottom: $CAP_SPACE_12;
26
- flex-wrap: nowrap;
27
13
 
28
- // Icon/text columns keep a fixed span; this column takes whatever space
29
- // is left (flex: 1, set via CapColumn's `flex` prop) instead of a fixed
30
- // span that has to add up to exactly 24 alongside its siblings — with a
31
- // fixed span, the gutter/margin between columns had nowhere to come from
32
- // and pushed this column's content past the card's right border.
33
- .rcs-saved-cta-value-column {
34
- min-width: 0;
14
+ div:not(:last-child) {
15
+ margin-right: 0.5625rem;
35
16
  }
36
17
 
37
- // Value text and edit/delete icons share one flex row, right-aligned as a
38
- // unit — keeps the value flush against the icons regardless of its length,
39
- // instead of the old fixed-span column + absolutely-positioned icon combo
40
- // (which drifted out of alignment with variable-length URLs/numbers).
41
- .rcs-saved-cta-value-actions {
42
- display: flex;
43
- align-items: center;
44
- justify-content: flex-end;
45
- gap: $CAP_SPACE_08;
46
- width: 100%;
47
- min-width: 0;
48
-
49
- .url-type {
50
- flex-shrink: 0;
51
- }
52
-
53
- .url, .phone {
54
- min-width: 0;
55
- }
18
+ .rcs-url-preview-wrapper {
19
+ max-width: 5.714rem;
56
20
  }
57
21
 
58
22
  .url {
@@ -73,189 +37,20 @@
73
37
  font-weight: 500;
74
38
  }
75
39
 
76
- .rcs-saved-cta-action-icons {
77
- display: flex;
78
- align-items: center;
79
- gap: $CAP_SPACE_08;
80
- flex-shrink: 0;
40
+ .rcs-saved-cta-edit-icon {
41
+ position: absolute;
42
+ right: 0.4375rem;
81
43
  }
82
44
 
83
- .rcs-saved-cta-edit-icon,
84
45
  .rcs-saved-cta-delete-icon {
85
- cursor: pointer;
86
- }
87
- }
88
-
89
- // Button text / URL: count via CapInput `suffix` (antd affix, right-aligned inside field).
90
- // Phone: react-phone-input-2 — overlay count + padding on .form-control
91
- // Selector needs `.cap-heading.h5` too — CapHeading's own `.cap-heading.h5` rule (2 classes)
92
- // otherwise beats a single-class `.rcs-cta-inner-char-count` selector and forces dark text,
93
- // regardless of source order (light-grey placeholder look per Figma).
94
- .cap-heading.h5.rcs-cta-inner-char-count {
95
- font-size: 0.75rem;
96
- line-height: 1.25rem;
97
- color: $FONT_COLOR_03;
98
- white-space: nowrap;
99
- }
100
-
101
- // Button text: match the phone field's full-width treatment (Figma reference).
102
- // `className` on CapInput lands on componentWithLabelHOC's OUTER wrapper div
103
- // (confirmed via rendered DOM), not on the `.ant-input-affix-wrapper` itself —
104
- // force the wrapper full-width directly rather than relying on descendant rules.
105
- .rcs-cta-button-text {
106
- width: 100%;
107
-
108
- .ant-input-affix-wrapper {
109
- width: 100%;
110
- }
111
- }
112
-
113
- .rcs-cta-input-with-inner-count {
114
- position: relative;
115
- width: 100%;
116
-
117
- &--phone .rcs-cta-inner-char-count {
118
- position: absolute;
119
- right: 0.6875rem;
120
- top: 50%;
121
- transform: translateY(-50%);
122
- z-index: 2;
123
- pointer-events: none;
124
- }
125
- }
126
-
127
- // RCS phone field: Figma — one light border, 4px radius, ~antd Input large height, full width, count inside right
128
- .rcs-button-cta-create-container .rcs-cta-input-with-inner-count--phone {
129
- .react-tel-input.rcs-cta-phone-input {
130
- width: 100%;
131
- }
132
-
133
- .rcs-cta-phone-input .form-control {
134
- width: 100% !important;
135
- max-width: none;
136
- height: 2.5rem;
137
- min-height: 2.5rem;
138
- padding-top: 0;
139
- padding-bottom: 0;
140
- padding-left: 3rem;
141
- padding-right: 3.5rem;
142
- margin: 0;
143
- font-size: 0.875rem;
144
- line-height: 2.5rem;
145
- color: $CAP_G01;
146
- background: $CAP_WHITE;
147
- border: 1px solid $CAP_G06;
148
- border-radius: 0.25rem;
149
- box-shadow: none;
150
-
151
- &::placeholder {
152
- color: $CAP_G05;
153
- }
154
-
155
- &:hover {
156
- border-color: $CAP_G11;
157
- }
158
-
159
- &:focus {
160
- border-color: $CAP_G01;
161
- box-shadow: none;
162
- outline: none;
46
+ position: absolute;
47
+ right: $CAP_SPACE_02;
163
48
  }
164
- }
165
-
166
- // The flag-dropdown sits on top of .form-control's left edge, so hovering the
167
- // flag itself doesn't trigger `.form-control:hover` — bind the border-color
168
- // change to the whole wrapper so the border reacts consistently everywhere.
169
- .react-tel-input.rcs-cta-phone-input:hover .form-control {
170
- border-color: $CAP_G11;
171
- }
172
-
173
- .react-tel-input.rcs-cta-phone-input:focus-within .form-control {
174
- border-color: $CAP_G01;
175
- }
176
-
177
- .rcs-cta-phone-input .flag-dropdown {
178
- // Raw px, not rem: must exactly match .form-control's 1px border width.
179
- // At this app's 14px root font-size, 0.0625rem resolved to 0.875px — the
180
- // sub-pixel gap left the outer border looking "cut off" right where the
181
- // flag-dropdown's opaque background sat, most visible on hover when the
182
- // border color changed and made the gap's anti-aliasing stand out.
183
- top: 1px;
184
- bottom: 1px;
185
- left: 1px;
186
- padding: 0;
187
- background: $CAP_WHITE;
188
- border: none;
189
- border-radius: 0.1875rem 0 0 0.1875rem; // inset from outer 4px radius
190
- }
191
-
192
- .rcs-cta-phone-input .flag-dropdown.open {
193
- border-radius: 0.1875rem 0 0 0;
194
- }
195
-
196
- .rcs-cta-phone-input .selected-flag {
197
- width: 2.75rem;
198
- padding: 0 0 0 0.5rem;
199
- background: transparent;
200
- border-radius: 0.1875rem 0 0 0.1875rem;
201
- }
202
-
203
- .rcs-cta-phone-input .selected-flag:hover,
204
- .rcs-cta-phone-input .selected-flag:focus {
205
- background: $CAP_G09;
206
- }
207
-
208
- .rcs-cta-phone-input .selected-flag .arrow {
209
- left: 1.375rem;
210
- border-top-color: $CAP_G04;
211
- }
212
49
  }
213
50
 
214
- // URL CTA: URL type (narrow) + URL field (~1:3) per Figma
215
- .rcs-cta-url-fields-row {
216
- .rcs-cta-url-type-col .ant-select {
217
- width: 100%;
218
- }
219
- }
220
-
221
- // RCS “create CTA” card: 0.5rem padding + gaps (Figma-tight; fixes excess space above first label)
222
- .rcs-button-cta-create-container.cap-row-v2 {
223
- display: flex;
224
- flex-direction: column;
225
- // Cross-axis (=width, since this is a column-flex row) defaults to `align-items: flex-start`
226
- // via antd's `.ant-row-top`, which lets child Cols shrink to content instead of filling the
227
- // row — same issue CapRow's own `.cap-row-vertical` guards against. Force stretch here too,
228
- // otherwise fields with no explicit width of their own (e.g. Button text) render too narrow.
229
- align-items: stretch;
230
- flex-wrap: nowrap;
231
- width: 100%;
232
- border: solid 1px $CAP_G06;
233
- padding: 1.25rem;
234
- border-radius: 0.25rem; // 4px — match .cap-rcs-saved-cta
235
-
236
- // Stack “Button type” (radios) above “Button text”
237
- .rcs-button-cta-create.cap-row-v2 {
238
- display: flex;
239
- flex-direction: column;
240
- align-items: stretch;
241
- flex-wrap: nowrap;
242
- margin: 0;
243
- }
244
-
245
- // Space after each field label (CapHeading margins are often zeroed by the library)
246
- .cta-label {
247
- margin-bottom: 0;
248
- margin-top: 0.75rem;
249
- }
250
-
251
- .cta-label + * {
252
- margin-top: 0.5rem;
253
- }
254
-
255
- // “Button type” heading has no .cta-label — keep same label→control gap as other fields
256
- .rcs-button-cta-create > .ant-col:first-of-type .cap-rcs-cta-type-radio {
257
- margin-top: 0.5rem;
258
- }
51
+ .rcs-button-cta-create-container {
52
+ border: solid 1px $CAP_G06;
53
+ padding: 10px;
259
54
  }
260
55
 
261
56
  .disabled {
@@ -270,16 +65,3 @@
270
65
  .button-disabled-tooltip-wrapper {
271
66
  display: inline-block;
272
67
  }
273
-
274
- // Space above Save/Delete row: kept on this stable outer wrapper (not on
275
- // .button-disabled-tooltip-wrapper) since that inner CapRow's own display
276
- // (flex vs inline-block) flips depending on whether CapTooltip renders its
277
- // extra trigger span (title set vs empty), which made the gap disappear
278
- // whenever the Save button was enabled.
279
- .rcs-cta-save-delete-btn {
280
- margin-top: 1.25rem;
281
-
282
- .rcs-cta-delete-btn {
283
- margin-left: 0.75rem;
284
- }
285
- }
@@ -12,7 +12,7 @@ export default defineMessages({
12
12
  },
13
13
  ctaQr: {
14
14
  id: `${prefix}.ctaQr`,
15
- defaultMessage: 'Quick reply',
15
+ defaultMessage: 'Quick Reply',
16
16
  },
17
17
  ctaPhoneNo: {
18
18
  id: `${prefix}.ctaPhoneNo`,
@@ -47,17 +47,9 @@ export default defineMessages({
47
47
  id: `${prefix}.templateMessageLength`,
48
48
  defaultMessage: 'Characters count: {currentLength}/{maxLength}',
49
49
  },
50
- ctaFieldCharCountInline: {
51
- id: `${prefix}.ctaFieldCharCountInline`,
52
- defaultMessage: '{current}/{max}',
53
- },
54
50
  ctaType: {
55
51
  id: `${prefix}.ctaType`,
56
- defaultMessage: 'Button type',
57
- },
58
- ctaUrlRadio: {
59
- id: `${prefix}.ctaUrlRadio`,
60
- defaultMessage: 'URL',
52
+ defaultMessage: 'Type of action',
61
53
  },
62
54
  templateButtonTextPlaceholder: {
63
55
  id: `${prefix}.templateButtonTextPlaceholder`,
@@ -153,15 +145,7 @@ export default defineMessages({
153
145
  },
154
146
  ctaWebsiteType: {
155
147
  id: `${prefix}.ctaWebsiteType`,
156
- defaultMessage: 'URL type',
157
- },
158
- ctaUrlField: {
159
- id: `${prefix}.ctaUrlField`,
160
- defaultMessage: 'URL',
161
- },
162
- ctaEnterUrlPlaceholder: {
163
- id: `${prefix}.ctaEnterUrlPlaceholder`,
164
- defaultMessage: 'Enter URL',
148
+ defaultMessage: 'URL Type',
165
149
  },
166
150
  ctaWebsiteTypeStatic: {
167
151
  id: `${prefix}.ctaWebsiteTypeStatic`,
@@ -5,7 +5,7 @@ import { render, screen, fireEvent } from '../../../utils/test-utils';
5
5
  import { waitFor } from '@testing-library/react';
6
6
  import { CapActionButton } from '../index';
7
7
  import { BTN_MAX_LENGTH, PHONE_NUMBER_MAX_LENGTH, URL_MAX_LENGTH } from '../constants';
8
- import { RCS_BUTTON_TYPES, HOST_ICS } from '../../../v2Containers/Rcs/constants';
8
+ import { RCS_BUTTON_TYPES } from '../../../v2Containers/Rcs/constants';
9
9
 
10
10
  const updateHandler = jest.fn();
11
11
  const deleteHandler = jest.fn();
@@ -13,7 +13,6 @@ const initializeComponent = (
13
13
  data,
14
14
  isEditFlow = false,
15
15
  maxButtons = 3,
16
- host = '',
17
16
  ) => {
18
17
  // Normalize legacy test data shape to match component props
19
18
  const normalizeType = (legacyType) => {
@@ -39,7 +38,6 @@ const initializeComponent = (
39
38
  isEditFlow={isEditFlow}
40
39
  maxButtons={maxButtons}
41
40
  isFullMode={true}
42
- host={host}
43
41
  />,
44
42
  );
45
43
  };
@@ -62,7 +60,7 @@ describe('CapActionButton', () => {
62
60
  isSaved: false,
63
61
  },
64
62
  ]);
65
- expect(screen.getByText('Button type')).toBeInTheDocument();
63
+ expect(screen.getByText('Type of action')).toBeInTheDocument();
66
64
  expect(screen.getByText('Button text')).toBeInTheDocument();
67
65
  expect(screen.getAllByText('Phone number')[1]).toBeInTheDocument();
68
66
  expect(screen.getByRole('button', { name: /save/i })).toBeDisabled();
@@ -125,7 +123,7 @@ describe('CapActionButton', () => {
125
123
  id: 1,
126
124
  },
127
125
  };
128
- const urlInput = await screen.getByPlaceholderText('Enter URL');
126
+ const urlInput = await screen.getByPlaceholderText('Enter website URL');
129
127
  fireEvent.change(urlInput, urlEvent);
130
128
  expect(updateHandler).toHaveBeenCalledWith(
131
129
  {
@@ -165,27 +163,6 @@ describe('CapActionButton', () => {
165
163
  );
166
164
  });
167
165
 
168
- it('should show delete for quick reply index 0 for non-ICS hosts but hide it for ICS (stop button)', () => {
169
- const button = {
170
- index: 0,
171
- type: RCS_BUTTON_TYPES.QUICK_REPLY,
172
- text: 'Reply',
173
- phoneNumber: '',
174
- url: '',
175
- postback: 'Reply',
176
- isSaved: false,
177
- };
178
-
179
- // Non-ICS (Infobip/others): delete should be available
180
- const { unmount } = initializeComponent([button], false, 3, 'rcsinfobipbulk');
181
- expect(screen.getByRole('button', { name: /delete/i })).toBeInTheDocument();
182
- unmount();
183
-
184
- // ICS: index 0 is reserved; delete should be hidden
185
- initializeComponent([button], false, 3, HOST_ICS);
186
- expect(screen.queryByRole('button', { name: /delete/i })).not.toBeInTheDocument();
187
- });
188
-
189
166
  it('should respect character length limits', async () => {
190
167
  const initialData = {
191
168
  index: 0,
@@ -224,7 +201,7 @@ describe('CapActionButton', () => {
224
201
  isSaved: false,
225
202
  };
226
203
  initializeComponent([initialData]);
227
- const urlInput = await screen.getByPlaceholderText('Enter URL');
204
+ const urlInput = await screen.getByPlaceholderText('Enter website URL');
228
205
  const longUrl = 'https://example.com/' + 'a'.repeat(URL_MAX_LENGTH + 1);
229
206
  fireEvent.change(urlInput, { target: { value: longUrl, id: 0 } });
230
207
  expect(urlInput.value.length).toBeLessThanOrEqual(URL_MAX_LENGTH);
@@ -271,8 +248,7 @@ describe('CapActionButton', () => {
271
248
  { index: 0, ctaType: RCS_BUTTON_TYPES.QUICK_REPLY, displayText: 'stop', phoneNumber: '', url: '', postback: 'stop', isSaved: true },
272
249
  { index: 1, ctaType: RCS_BUTTON_TYPES.QUICK_REPLY, displayText: 'Saved', phoneNumber: '', url: '', postback: 'Saved', isSaved: true },
273
250
  ];
274
- // Use ICS host so index 0 (STOP) stays non-deletable and we only get delete icon for index 1
275
- const { container } = initializeComponent(suggestions, false, 3, HOST_ICS);
251
+ const { container } = initializeComponent(suggestions);
276
252
  const deleteIcons = container.querySelectorAll('.rcs-saved-cta-delete-icon');
277
253
  expect(deleteIcons.length).toBeGreaterThan(0);
278
254
  fireEvent.click(deleteIcons[0]);
@@ -330,8 +306,7 @@ describe('CapActionButton', () => {
330
306
  postback: 'stop',
331
307
  isSaved: true,
332
308
  };
333
- // Only ICS treats index 0 as a mandatory STOP quick reply
334
- const { container } = initializeComponent([stopButton], false, 3, HOST_ICS);
309
+ const { container } = initializeComponent([stopButton]);
335
310
  expect(container.querySelector('.rcs-saved-cta-delete-icon')).not.toBeInTheDocument();
336
311
  });
337
312
 
@@ -452,7 +427,7 @@ describe('CapActionButton', () => {
452
427
  isSaved: false,
453
428
  };
454
429
  initializeComponent([button]);
455
- expect(screen.getByPlaceholderText(/enter url/i)).toBeInTheDocument();
430
+ expect(screen.getByPlaceholderText(/enter website url/i)).toBeInTheDocument();
456
431
  });
457
432
 
458
433
  it('should update both displayText and postback when button text changes (updateDisplayAndPostback)', () => {
@@ -532,7 +507,7 @@ describe('CapActionButton', () => {
532
507
  isSaved: false,
533
508
  };
534
509
  initializeComponent([initial]);
535
- const urlInput = screen.getByPlaceholderText('Enter URL');
510
+ const urlInput = screen.getByPlaceholderText('Enter website URL');
536
511
  fireEvent.change(urlInput, { target: { value: 'http://localhost:3030/creatives/ui/v2', id: 1 } });
537
512
  expect(screen.getByText(/url is not valid/i)).toBeInTheDocument();
538
513
  });
@@ -632,12 +607,12 @@ describe('CapActionButton', () => {
632
607
  );
633
608
  };
634
609
  render(<StatefulWrapper />);
635
- let urlInput = screen.getByPlaceholderText('Enter URL');
610
+ let urlInput = screen.getByPlaceholderText('Enter website URL');
636
611
  // Enter invalid URL
637
612
  fireEvent.change(urlInput, { target: { value: 'badurl', id: 0 } });
638
613
  expect(await screen.findByText(/url is not valid/i)).toBeInTheDocument();
639
614
  // Re-grab input after re-render and enter valid URL
640
- urlInput = screen.getByPlaceholderText('Enter URL');
615
+ urlInput = screen.getByPlaceholderText('Enter website URL');
641
616
  fireEvent.change(urlInput, { target: { value: 'https://good.com', id: 0 } });
642
617
  await waitFor(() => expect(screen.queryByText(/url is not valid/i)).not.toBeInTheDocument());
643
618
  });
@@ -892,14 +867,15 @@ describe('CapActionButton function logic', () => {
892
867
  expect(renderCtaOptions(label, tooltipLabel, false)).toBe('Test');
893
868
  });
894
869
 
895
- it('inner char count uses current/max format', () => {
896
- const formatMessage = (msg, values) => `${values.current}/${values.max}`;
897
- const renderInnerCharCount = (len, max) => (
898
- <span className="rcs-cta-inner-char-count">
899
- {formatMessage({}, { current: len, max })}
900
- </span>
870
+ it('renderLength returns correct CapHeading', () => {
871
+ const CapHeading = ({ type, className, children }) => <div className={className}>{children}</div>;
872
+ const formatMessage = (msg, values) => `${values.currentLength}/${values.maxLength}`;
873
+ const renderLength = (len, max) => (
874
+ <CapHeading type="label1" className="rcs-render-btn-length">
875
+ {formatMessage({}, { currentLength: len, maxLength: max })}
876
+ </CapHeading>
901
877
  );
902
- const result = renderInnerCharCount(5, 25);
878
+ const result = renderLength(5, 25);
903
879
  expect(result.props.children).toBe('5/25');
904
880
  });
905
881
  });
@@ -5,7 +5,7 @@
5
5
  *
6
6
  */
7
7
 
8
- import React, { useState, useEffect, useCallback, useMemo } from 'react';
8
+ import React, { useState, useEffect, useCallback } from 'react';
9
9
  import PropTypes from "prop-types";
10
10
 
11
11
  import LabelHOC from '@capillarytech/cap-ui-library/assets/HOCs/ComponentWithLabelHOC';
@@ -92,7 +92,7 @@ function CapImageUpload(props) {
92
92
  [imageSrc],
93
93
  );
94
94
 
95
- const WithLabel = useMemo(() => LabelHOC(ImageComponent), [ImageComponent]);
95
+ const WithLabel = LabelHOC(ImageComponent);
96
96
 
97
97
  const uploadImages = useCallback((e, { files }) => {
98
98
  if (e) {
@@ -346,9 +346,6 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
346
346
  render() {
347
347
  const {
348
348
  hidePopover = false, intl = {}, moduleFilterEnabled, label, modalProps, channel, fetchingSchemaError = false,
349
- overlayStyle,
350
- overlayClassName,
351
- getPopupContainer,
352
349
  } = this.props;
353
350
  const {formatMessage} = intl;
354
351
  const {
@@ -442,9 +439,6 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
442
439
  classNames={{
443
440
  container: 'tag-list-popover-container'
444
441
  }}
445
- overlayStyle={overlayStyle}
446
- overlayClassName={overlayClassName}
447
- getPopupContainer={getPopupContainer}
448
442
  >
449
443
  <CapTooltip
450
444
  title={
@@ -516,10 +510,6 @@ CapTagList.propTypes = {
516
510
  disableTooltipMsg: PropTypes.string,
517
511
  fetchingSchemaError: PropTypes.bool,
518
512
  popoverPlacement: PropTypes.string,
519
- overlayStyle: PropTypes.object,
520
- overlayClassName: PropTypes.string,
521
- /** e.g. () => document.body — avoids overflow/stacking issues inside slideboxes */
522
- getPopupContainer: PropTypes.func,
523
513
  };
524
514
 
525
515
  CapTagList.defaultValue = {
@@ -8,8 +8,6 @@ import CapButton from '@capillarytech/cap-ui-library/CapButton';
8
8
  import CapInput from '@capillarytech/cap-ui-library/CapInput';
9
9
  import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
10
10
  import messages from './messages';
11
- import { CUSTOM_VALUES_EDITOR_SECTION_FALLBACK_KEY } from './constants';
12
- import { getPersonalizationTagColumnLabel } from './utils';
13
11
 
14
12
  const CustomValuesEditor = ({
15
13
  isExtractingTags,
@@ -18,7 +16,9 @@ const CustomValuesEditor = ({
18
16
  setShowJSON,
19
17
  customValues,
20
18
  handleJSONTextChange,
21
- sections,
19
+ extractedTags,
20
+ requiredTags,
21
+ optionalTags,
22
22
  handleCustomValueChange,
23
23
  handleDiscardCustomValues,
24
24
  handleUpdatePreview,
@@ -77,70 +77,51 @@ const CustomValuesEditor = ({
77
77
  </CapRow>
78
78
  ) : (
79
79
  <>
80
- {(sections || []).filter((tagsSection) =>
81
- (tagsSection?.requiredTags?.length || 0) + (tagsSection?.optionalTags?.length || 0) > 0).map((section) => (
82
- <React.Fragment key={section.key || section.title?.id || section.title || CUSTOM_VALUES_EDITOR_SECTION_FALLBACK_KEY}>
83
- {section.title ? (
84
- <CapLabel type="label2" className="tags-section-title">
85
- {typeof section.title === 'string' ? section.title : <FormattedMessage {...section.title} />}
80
+ {extractedTags?.length > 0 && (
81
+ <CapRow className="values-table">
82
+ <CapRow className="table-header" align='middle' justify='space-between'>
83
+ <CapLabel type="label31" className="header-cell">
84
+ <FormattedMessage {...messages.personalizationTags} />
86
85
  </CapLabel>
87
- ) : null}
88
- <CapRow className="values-table">
89
- <CapRow type="flex" className="table-header">
90
- <CapLabel type="label31" className="header-cell">
91
- <FormattedMessage {...messages.personalizationTags} />
92
- </CapLabel>
93
- <CapLabel type="label31" className="header-cell">
94
- <FormattedMessage {...messages.customValues} />
95
- </CapLabel>
96
- </CapRow>
97
- {(section?.requiredTags || []).map((tag, tagIndex) => {
98
- const personalizationTagColumnText = getPersonalizationTagColumnLabel(tag);
99
- const tagKey = tag?.fullPath ?? `required-${tagIndex}`;
100
- return (
101
- <CapRow key={tagKey} type="flex" noWrap className="value-row">
102
- <CapRow type="flex" className="tag-name">
103
- {personalizationTagColumnText}
104
- <span className="required-tag-indicator" aria-hidden="true">*</span>
105
- </CapRow>
106
- <CapRow type="flex" className="tag-input">
107
- <CapInput
108
- type="text"
109
- isRequired
110
- className="tag-input-field"
111
- value={customValues?.[tagKey] ?? ''}
112
- onChange={(e) => handleCustomValueChange(tagKey, e.target.value)}
113
- placeholder={formatMessage(messages.enterValue)}
114
- size="small"
115
- />
116
- </CapRow>
86
+ <CapLabel type="label31" className="header-cell">
87
+ <FormattedMessage {...messages.customValues} />
88
+ </CapLabel>
89
+ </CapRow>
90
+ {requiredTags.map((tag) => (
91
+ <CapRow key={tag.fullPath} className="value-row" align='middle' justify='start'>
92
+ <CapRow className="tag-name" align='top' justify='start'>
93
+ {`${tag.fullPath}*`}
117
94
  </CapRow>
118
- );
119
- })}
120
- {(section?.optionalTags || []).map((tag, tagIndex) => {
121
- const personalizationTagColumnText = getPersonalizationTagColumnLabel(tag);
122
- const tagKey = tag?.fullPath ?? `optional-${tagIndex}`;
123
- return (
124
- <CapRow key={tagKey} type="flex" noWrap className="value-row">
125
- <CapRow type="flex" className="tag-name">
126
- {personalizationTagColumnText}
127
- </CapRow>
128
- <CapRow type="flex" className="tag-input">
129
- <CapInput
130
- type="text"
131
- className="tag-input-field"
132
- value={customValues?.[tagKey] ?? ''}
133
- onChange={(e) => handleCustomValueChange(tagKey, e.target.value)}
134
- placeholder={formatMessage(messages.enterValue)}
135
- size="small"
136
- />
137
- </CapRow>
95
+ <CapRow className="tag-input">
96
+ <CapInput
97
+ type="text"
98
+ isRequired
99
+ className="tag-input-field"
100
+ value={customValues[tag.fullPath] || ''}
101
+ onChange={(e) => handleCustomValueChange(tag.fullPath, e.target.value)}
102
+ placeholder={formatMessage(messages.enterValue)}
103
+ size="small"
104
+ />
138
105
  </CapRow>
139
- );
140
- })}
141
- </CapRow>
142
- </React.Fragment>
143
- ))}
106
+ </CapRow>
107
+ ))}
108
+ {optionalTags?.map((tag) => (
109
+ <CapRow key={tag.fullPath} className="value-row" align='middle' justify='start'>
110
+ <CapRow className="tag-name" justify='start'>{tag.fullPath}</CapRow>
111
+ <CapRow className="tag-input">
112
+ <CapInput
113
+ type="text"
114
+ className="tag-input-field"
115
+ value={customValues[tag.fullPath] || ''}
116
+ onChange={(e) => handleCustomValueChange(tag.fullPath, e.target.value)}
117
+ placeholder={formatMessage(messages.enterValue)}
118
+ size="small"
119
+ />
120
+ </CapRow>
121
+ </CapRow>
122
+ ))}
123
+ </CapRow>
124
+ )}
144
125
  </>
145
126
  )}
146
127
  <CapRow type='flex' align='middle' justify='space-between' className="editor-actions">
@@ -174,12 +155,9 @@ CustomValuesEditor.propTypes = {
174
155
  setShowJSON: PropTypes.func.isRequired,
175
156
  customValues: PropTypes.object.isRequired,
176
157
  handleJSONTextChange: PropTypes.func.isRequired,
177
- sections: PropTypes.arrayOf(PropTypes.shape({
178
- key: PropTypes.string,
179
- title: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
180
- requiredTags: PropTypes.array,
181
- optionalTags: PropTypes.array,
182
- })).isRequired,
158
+ extractedTags: PropTypes.array.isRequired,
159
+ requiredTags: PropTypes.array.isRequired,
160
+ optionalTags: PropTypes.array.isRequired,
183
161
  handleCustomValueChange: PropTypes.func.isRequired,
184
162
  handleDiscardCustomValues: PropTypes.func.isRequired,
185
163
  handleUpdatePreview: PropTypes.func.isRequired,