@capillarytech/creatives-library 9.0.28 → 9.0.29-alpha.1
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.
- package/constants/unified.js +29 -0
- package/global-styles.js +1 -1
- package/package.json +1 -1
- package/services/tests/api.test.js +35 -20
- package/utils/commonUtils.js +22 -1
- package/utils/rcsPayloadUtils.js +102 -0
- package/utils/templateVarUtils.js +198 -0
- package/utils/tests/rcsPayloadUtils.test.js +295 -0
- package/utils/tests/templateVarUtils.test.js +204 -0
- package/v2Components/CapActionButton/constants.js +21 -1
- package/v2Components/CapActionButton/index.js +260 -144
- package/v2Components/CapActionButton/index.scss +245 -14
- package/v2Components/CapActionButton/messages.js +32 -3
- package/v2Components/CapActionButton/tests/index.test.js +74 -19
- package/v2Components/CapImageUpload/index.js +2 -2
- package/v2Components/CapTagList/index.js +9 -0
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -48
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +214 -21
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +83 -9
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +58 -11
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +186 -22
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +381 -80
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +155 -8
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
- package/v2Components/CommonTestAndPreview/constants.js +35 -2
- package/v2Components/CommonTestAndPreview/index.js +770 -231
- package/v2Components/CommonTestAndPreview/messages.js +45 -3
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
- package/v2Components/CommonTestAndPreview/sagas.js +25 -6
- package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +1 -0
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +412 -257
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +337 -63
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
- package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +108 -15
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
- package/v2Components/CommonTestAndPreview/tests/index.test.js +133 -4
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +31 -24
- package/v2Components/CommonTestAndPreview/tests/utils.test.js +151 -0
- package/v2Components/CommonTestAndPreview/utils.js +84 -0
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +91 -0
- package/v2Components/SmsFallback/constants.js +94 -0
- package/v2Components/SmsFallback/index.js +958 -0
- package/v2Components/SmsFallback/index.scss +266 -0
- package/v2Components/SmsFallback/messages.js +78 -0
- package/v2Components/SmsFallback/smsFallbackUtils.js +120 -0
- package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
- package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
- package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
- package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +208 -0
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +309 -0
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +471 -0
- package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
- package/v2Components/TemplatePreview/_templatePreview.scss +45 -25
- package/v2Components/TemplatePreview/constants.js +2 -0
- package/v2Components/TemplatePreview/index.js +147 -32
- package/v2Components/TemplatePreview/tests/index.test.js +142 -0
- package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +6 -6
- package/v2Components/TestAndPreviewSlidebox/index.js +13 -1
- package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
- package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
- package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
- package/v2Components/VarSegmentMessageEditor/index.js +125 -0
- package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +4 -1
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +4 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +20 -20
- package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +27 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +37 -4
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +14 -5
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +36 -5
- package/v2Containers/CreativesContainer/constants.js +11 -0
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +77 -0
- package/v2Containers/CreativesContainer/index.js +328 -106
- package/v2Containers/CreativesContainer/index.scss +102 -1
- package/v2Containers/CreativesContainer/tests/CreativesSlideBoxWrapper.test.js +58 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.smsDltPreview.test.js +73 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +37 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +103 -34
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +193 -15
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +88 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +549 -57
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
- package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
- package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
- package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
- package/v2Containers/Email/index.js +3 -63
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +4 -9
- package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
- package/v2Containers/Rcs/carouselUtils.js +224 -0
- package/v2Containers/Rcs/components/CarouselCard.js +317 -0
- package/v2Containers/Rcs/components/CarouselCardButtons.js +113 -0
- package/v2Containers/Rcs/components/CarouselCardMedia.js +136 -0
- package/v2Containers/Rcs/components/CarouselCharacterCount.js +31 -0
- package/v2Containers/Rcs/components/CarouselDimensionSelection.js +80 -0
- package/v2Containers/Rcs/constants.js +132 -16
- package/v2Containers/Rcs/index.js +1808 -949
- package/v2Containers/Rcs/index.scss +443 -8
- package/v2Containers/Rcs/messages.js +45 -22
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +484 -0
- package/v2Containers/Rcs/tests/CarouselCard.test.js +464 -0
- package/v2Containers/Rcs/tests/CarouselCardButtons.test.js +211 -0
- package/v2Containers/Rcs/tests/CarouselCardMedia.test.js +160 -0
- package/v2Containers/Rcs/tests/CarouselCharacterCount.test.js +50 -0
- package/v2Containers/Rcs/tests/CarouselDimensionSelection.test.js +119 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +74176 -39409
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +10 -74
- package/v2Containers/Rcs/tests/carouselUtils.test.js +916 -0
- package/v2Containers/Rcs/tests/index.test.js +219 -40
- package/v2Containers/Rcs/tests/mockData.js +38 -0
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +873 -0
- package/v2Containers/Rcs/tests/utils.test.js +682 -30
- package/v2Containers/Rcs/utils.js +514 -12
- package/v2Containers/Sms/Create/index.js +115 -48
- package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
- package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
- package/v2Containers/SmsTrai/Create/index.js +9 -4
- package/v2Containers/SmsTrai/Edit/constants.js +2 -0
- package/v2Containers/SmsTrai/Edit/index.js +678 -169
- package/v2Containers/SmsTrai/Edit/index.scss +126 -0
- package/v2Containers/SmsTrai/Edit/messages.js +14 -4
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +5615 -3014
- package/v2Containers/SmsWrapper/index.js +37 -8
- package/v2Containers/TagList/index.js +5 -0
- package/v2Containers/Templates/TemplatesActionBar.js +101 -0
- package/v2Containers/Templates/_templates.scss +196 -12
- package/v2Containers/Templates/actions.js +11 -0
- package/v2Containers/Templates/constants.js +2 -0
- package/v2Containers/Templates/index.js +131 -59
- package/v2Containers/Templates/sagas.js +57 -13
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +129 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1060 -1015
- package/v2Containers/Templates/tests/sagas.test.js +199 -16
- package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
- package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
- package/v2Containers/TemplatesV2/index.js +88 -25
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
- package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
- package/v2Containers/Whatsapp/index.js +3 -20
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
@import '~@capillarytech/cap-ui-library/styles/_variables';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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
|
+
}
|
|
5
18
|
}
|
|
6
19
|
|
|
7
20
|
.cap-rcs-saved-cta {
|
|
@@ -10,13 +23,36 @@
|
|
|
10
23
|
padding: 0.625rem;
|
|
11
24
|
border-radius: $CAP_SPACE_04;
|
|
12
25
|
margin-bottom: $CAP_SPACE_12;
|
|
26
|
+
flex-wrap: nowrap;
|
|
13
27
|
|
|
14
|
-
|
|
15
|
-
|
|
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;
|
|
16
35
|
}
|
|
17
36
|
|
|
18
|
-
|
|
19
|
-
|
|
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
|
+
}
|
|
20
56
|
}
|
|
21
57
|
|
|
22
58
|
.url {
|
|
@@ -37,20 +73,202 @@
|
|
|
37
73
|
font-weight: 500;
|
|
38
74
|
}
|
|
39
75
|
|
|
40
|
-
.rcs-saved-cta-
|
|
41
|
-
|
|
42
|
-
|
|
76
|
+
.rcs-saved-cta-action-icons {
|
|
77
|
+
display: flex;
|
|
78
|
+
align-items: center;
|
|
79
|
+
gap: $CAP_SPACE_08;
|
|
80
|
+
flex-shrink: 0;
|
|
43
81
|
}
|
|
44
82
|
|
|
83
|
+
.rcs-saved-cta-edit-icon,
|
|
45
84
|
.rcs-saved-cta-delete-icon {
|
|
46
|
-
|
|
47
|
-
|
|
85
|
+
cursor: pointer;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Dynamic-URL tag controls: keep them from stretching the flex row's
|
|
89
|
+
// height/width — same treatment as the edit/delete icons above.
|
|
90
|
+
.rcs-cta-tag-revert {
|
|
91
|
+
display: flex;
|
|
92
|
+
align-items: center;
|
|
93
|
+
flex-shrink: 0;
|
|
94
|
+
cursor: pointer;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.rcs-cta-taglist {
|
|
98
|
+
flex-shrink: 0;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Button text / URL: count via CapInput `suffix` (antd affix, right-aligned inside field).
|
|
103
|
+
// Phone: react-phone-input-2 — overlay count + padding on .form-control
|
|
104
|
+
// Selector needs `.cap-heading.h5` too — CapHeading's own `.cap-heading.h5` rule (2 classes)
|
|
105
|
+
// otherwise beats a single-class `.rcs-cta-inner-char-count` selector and forces dark text,
|
|
106
|
+
// regardless of source order (light-grey placeholder look per Figma).
|
|
107
|
+
.cap-heading.h5.rcs-cta-inner-char-count {
|
|
108
|
+
font-size: 0.75rem;
|
|
109
|
+
line-height: 1.25rem;
|
|
110
|
+
color: $FONT_COLOR_03;
|
|
111
|
+
white-space: nowrap;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Button text: match the phone field's full-width treatment (Figma reference).
|
|
115
|
+
// `className` on CapInput lands on componentWithLabelHOC's OUTER wrapper div
|
|
116
|
+
// (confirmed via rendered DOM), not on the `.ant-input-affix-wrapper` itself —
|
|
117
|
+
// force the wrapper full-width directly rather than relying on descendant rules.
|
|
118
|
+
.rcs-cta-button-text {
|
|
119
|
+
width: 100%;
|
|
120
|
+
|
|
121
|
+
.ant-input-affix-wrapper {
|
|
122
|
+
width: 100%;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.rcs-cta-input-with-inner-count {
|
|
127
|
+
position: relative;
|
|
128
|
+
width: 100%;
|
|
129
|
+
|
|
130
|
+
&--phone .rcs-cta-inner-char-count {
|
|
131
|
+
position: absolute;
|
|
132
|
+
right: 0.6875rem;
|
|
133
|
+
top: 50%;
|
|
134
|
+
transform: translateY(-50%);
|
|
135
|
+
z-index: 2;
|
|
136
|
+
pointer-events: none;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// RCS phone field: Figma — one light border, 4px radius, ~antd Input large height, full width, count inside right
|
|
141
|
+
.rcs-button-cta-create-container .rcs-cta-input-with-inner-count--phone {
|
|
142
|
+
.react-tel-input.rcs-cta-phone-input {
|
|
143
|
+
width: 100%;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.rcs-cta-phone-input .form-control {
|
|
147
|
+
width: 100% !important;
|
|
148
|
+
max-width: none;
|
|
149
|
+
height: 2.5rem;
|
|
150
|
+
min-height: 2.5rem;
|
|
151
|
+
padding-top: 0;
|
|
152
|
+
padding-bottom: 0;
|
|
153
|
+
padding-left: 3rem;
|
|
154
|
+
padding-right: 3.5rem;
|
|
155
|
+
margin: 0;
|
|
156
|
+
font-size: 0.875rem;
|
|
157
|
+
line-height: 2.5rem;
|
|
158
|
+
color: $CAP_G01;
|
|
159
|
+
background: $CAP_WHITE;
|
|
160
|
+
border: 1px solid $CAP_G06;
|
|
161
|
+
border-radius: 0.25rem;
|
|
162
|
+
box-shadow: none;
|
|
163
|
+
|
|
164
|
+
&::placeholder {
|
|
165
|
+
color: $CAP_G05;
|
|
48
166
|
}
|
|
167
|
+
|
|
168
|
+
&:hover {
|
|
169
|
+
border-color: $CAP_G11;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
&:focus {
|
|
173
|
+
border-color: $CAP_G01;
|
|
174
|
+
box-shadow: none;
|
|
175
|
+
outline: none;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// The flag-dropdown sits on top of .form-control's left edge, so hovering the
|
|
180
|
+
// flag itself doesn't trigger `.form-control:hover` — bind the border-color
|
|
181
|
+
// change to the whole wrapper so the border reacts consistently everywhere.
|
|
182
|
+
.react-tel-input.rcs-cta-phone-input:hover .form-control {
|
|
183
|
+
border-color: $CAP_G11;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.react-tel-input.rcs-cta-phone-input:focus-within .form-control {
|
|
187
|
+
border-color: $CAP_G01;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.rcs-cta-phone-input .flag-dropdown {
|
|
191
|
+
// Raw px, not rem: must exactly match .form-control's 1px border width.
|
|
192
|
+
// At this app's 14px root font-size, 0.0625rem resolved to 0.875px — the
|
|
193
|
+
// sub-pixel gap left the outer border looking "cut off" right where the
|
|
194
|
+
// flag-dropdown's opaque background sat, most visible on hover when the
|
|
195
|
+
// border color changed and made the gap's anti-aliasing stand out.
|
|
196
|
+
top: 1px;
|
|
197
|
+
bottom: 1px;
|
|
198
|
+
left: 1px;
|
|
199
|
+
padding: 0;
|
|
200
|
+
background: $CAP_WHITE;
|
|
201
|
+
border: none;
|
|
202
|
+
border-radius: 0.1875rem 0 0 0.1875rem; // inset from outer 4px radius
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.rcs-cta-phone-input .flag-dropdown.open {
|
|
206
|
+
border-radius: 0.1875rem 0 0 0;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.rcs-cta-phone-input .selected-flag {
|
|
210
|
+
width: 2.75rem;
|
|
211
|
+
padding: 0 0 0 0.5rem;
|
|
212
|
+
background: transparent;
|
|
213
|
+
border-radius: 0.1875rem 0 0 0.1875rem;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.rcs-cta-phone-input .selected-flag:hover,
|
|
217
|
+
.rcs-cta-phone-input .selected-flag:focus {
|
|
218
|
+
background: $CAP_G09;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.rcs-cta-phone-input .selected-flag .arrow {
|
|
222
|
+
left: 1.375rem;
|
|
223
|
+
border-top-color: $CAP_G04;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// URL CTA: URL type (narrow) + URL field (~1:3) per Figma
|
|
228
|
+
.rcs-cta-url-fields-row {
|
|
229
|
+
.rcs-cta-url-type-col .ant-select {
|
|
230
|
+
width: 100%;
|
|
231
|
+
}
|
|
49
232
|
}
|
|
50
233
|
|
|
51
|
-
.
|
|
52
|
-
|
|
53
|
-
|
|
234
|
+
// RCS “create CTA” card: 0.5rem padding + gaps (Figma-tight; fixes excess space above first label)
|
|
235
|
+
.rcs-button-cta-create-container.cap-row-v2 {
|
|
236
|
+
display: flex;
|
|
237
|
+
flex-direction: column;
|
|
238
|
+
// Cross-axis (=width, since this is a column-flex row) defaults to `align-items: flex-start`
|
|
239
|
+
// via antd's `.ant-row-top`, which lets child Cols shrink to content instead of filling the
|
|
240
|
+
// row — same issue CapRow's own `.cap-row-vertical` guards against. Force stretch here too,
|
|
241
|
+
// otherwise fields with no explicit width of their own (e.g. Button text) render too narrow.
|
|
242
|
+
align-items: stretch;
|
|
243
|
+
flex-wrap: nowrap;
|
|
244
|
+
width: 100%;
|
|
245
|
+
border: solid 1px $CAP_G06;
|
|
246
|
+
padding: 1.25rem;
|
|
247
|
+
border-radius: 0.25rem; // 4px — match .cap-rcs-saved-cta
|
|
248
|
+
|
|
249
|
+
// Stack “Button type” (radios) above “Button text”
|
|
250
|
+
.rcs-button-cta-create.cap-row-v2 {
|
|
251
|
+
display: flex;
|
|
252
|
+
flex-direction: column;
|
|
253
|
+
align-items: stretch;
|
|
254
|
+
flex-wrap: nowrap;
|
|
255
|
+
margin: 0;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Space after each field label (CapHeading margins are often zeroed by the library)
|
|
259
|
+
.cta-label {
|
|
260
|
+
margin-bottom: 0;
|
|
261
|
+
margin-top: 0.75rem;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.cta-label + * {
|
|
265
|
+
margin-top: 0.5rem;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// “Button type” heading has no .cta-label — keep same label→control gap as other fields
|
|
269
|
+
.rcs-button-cta-create > .ant-col:first-of-type .cap-rcs-cta-type-radio {
|
|
270
|
+
margin-top: 0.5rem;
|
|
271
|
+
}
|
|
54
272
|
}
|
|
55
273
|
|
|
56
274
|
.disabled {
|
|
@@ -65,3 +283,16 @@
|
|
|
65
283
|
.button-disabled-tooltip-wrapper {
|
|
66
284
|
display: inline-block;
|
|
67
285
|
}
|
|
286
|
+
|
|
287
|
+
// Space above Save/Delete row: kept on this stable outer wrapper (not on
|
|
288
|
+
// .button-disabled-tooltip-wrapper) since that inner CapRow's own display
|
|
289
|
+
// (flex vs inline-block) flips depending on whether CapTooltip renders its
|
|
290
|
+
// extra trigger span (title set vs empty), which made the gap disappear
|
|
291
|
+
// whenever the Save button was enabled.
|
|
292
|
+
.rcs-cta-save-delete-btn {
|
|
293
|
+
margin-top: 1.25rem;
|
|
294
|
+
|
|
295
|
+
.rcs-cta-delete-btn {
|
|
296
|
+
margin-left: 0.75rem;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
@@ -12,7 +12,7 @@ export default defineMessages({
|
|
|
12
12
|
},
|
|
13
13
|
ctaQr: {
|
|
14
14
|
id: `${prefix}.ctaQr`,
|
|
15
|
-
defaultMessage: 'Quick
|
|
15
|
+
defaultMessage: 'Quick reply',
|
|
16
16
|
},
|
|
17
17
|
ctaPhoneNo: {
|
|
18
18
|
id: `${prefix}.ctaPhoneNo`,
|
|
@@ -47,9 +47,17 @@ 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
|
+
},
|
|
50
54
|
ctaType: {
|
|
51
55
|
id: `${prefix}.ctaType`,
|
|
52
|
-
defaultMessage: '
|
|
56
|
+
defaultMessage: 'Button type',
|
|
57
|
+
},
|
|
58
|
+
ctaUrlRadio: {
|
|
59
|
+
id: `${prefix}.ctaUrlRadio`,
|
|
60
|
+
defaultMessage: 'URL',
|
|
53
61
|
},
|
|
54
62
|
templateButtonTextPlaceholder: {
|
|
55
63
|
id: `${prefix}.templateButtonTextPlaceholder`,
|
|
@@ -145,7 +153,15 @@ export default defineMessages({
|
|
|
145
153
|
},
|
|
146
154
|
ctaWebsiteType: {
|
|
147
155
|
id: `${prefix}.ctaWebsiteType`,
|
|
148
|
-
defaultMessage: 'URL
|
|
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',
|
|
149
165
|
},
|
|
150
166
|
ctaWebsiteTypeStatic: {
|
|
151
167
|
id: `${prefix}.ctaWebsiteTypeStatic`,
|
|
@@ -183,4 +199,17 @@ export default defineMessages({
|
|
|
183
199
|
id: `${prefix}.addLabels`,
|
|
184
200
|
defaultMessage: 'Add Labels',
|
|
185
201
|
},
|
|
202
|
+
ctaDynamicUrlTooltip: {
|
|
203
|
+
id: `${prefix}.ctaDynamicUrlTooltip`,
|
|
204
|
+
defaultMessage:
|
|
205
|
+
'Only one variable can be added to a URL. No need to add {one} to the end of the URL',
|
|
206
|
+
},
|
|
207
|
+
ctaTagListLabel: {
|
|
208
|
+
id: `${prefix}.ctaTagListLabel`,
|
|
209
|
+
defaultMessage: 'Add URL label',
|
|
210
|
+
},
|
|
211
|
+
ctaTagListRevert: {
|
|
212
|
+
id: `${prefix}.ctaTagListRevert`,
|
|
213
|
+
defaultMessage: 'Reset website URL label to default value',
|
|
214
|
+
},
|
|
186
215
|
});
|
|
@@ -4,8 +4,10 @@ import '@testing-library/jest-dom';
|
|
|
4
4
|
import { render, screen, fireEvent } from '../../../utils/test-utils';
|
|
5
5
|
import { waitFor } from '@testing-library/react';
|
|
6
6
|
import { CapActionButton } from '../index';
|
|
7
|
-
import {
|
|
8
|
-
|
|
7
|
+
import {
|
|
8
|
+
BTN_MAX_LENGTH, PHONE_NUMBER_MAX_LENGTH, URL_MAX_LENGTH, RCS_CTA_URL_TYPE, DYNAMIC_URL_SUFFIX,
|
|
9
|
+
} from '../constants';
|
|
10
|
+
import { RCS_BUTTON_TYPES, HOST_ICS } from '../../../v2Containers/Rcs/constants';
|
|
9
11
|
|
|
10
12
|
const updateHandler = jest.fn();
|
|
11
13
|
const deleteHandler = jest.fn();
|
|
@@ -13,6 +15,7 @@ const initializeComponent = (
|
|
|
13
15
|
data,
|
|
14
16
|
isEditFlow = false,
|
|
15
17
|
maxButtons = 3,
|
|
18
|
+
host = '',
|
|
16
19
|
) => {
|
|
17
20
|
// Normalize legacy test data shape to match component props
|
|
18
21
|
const normalizeType = (legacyType) => {
|
|
@@ -26,6 +29,7 @@ const initializeComponent = (
|
|
|
26
29
|
text: d.displayText ?? d.text ?? '',
|
|
27
30
|
phoneNumber: d.phoneNumber ?? '',
|
|
28
31
|
url: d.url ?? '',
|
|
32
|
+
urlType: d.urlType,
|
|
29
33
|
postback: d.postback ?? '',
|
|
30
34
|
isSaved: d.isSaved ?? false,
|
|
31
35
|
}));
|
|
@@ -38,6 +42,7 @@ const initializeComponent = (
|
|
|
38
42
|
isEditFlow={isEditFlow}
|
|
39
43
|
maxButtons={maxButtons}
|
|
40
44
|
isFullMode={true}
|
|
45
|
+
host={host}
|
|
41
46
|
/>,
|
|
42
47
|
);
|
|
43
48
|
};
|
|
@@ -60,7 +65,7 @@ describe('CapActionButton', () => {
|
|
|
60
65
|
isSaved: false,
|
|
61
66
|
},
|
|
62
67
|
]);
|
|
63
|
-
expect(screen.getByText('
|
|
68
|
+
expect(screen.getByText('Button type')).toBeInTheDocument();
|
|
64
69
|
expect(screen.getByText('Button text')).toBeInTheDocument();
|
|
65
70
|
expect(screen.getAllByText('Phone number')[1]).toBeInTheDocument();
|
|
66
71
|
expect(screen.getByRole('button', { name: /save/i })).toBeDisabled();
|
|
@@ -123,7 +128,7 @@ describe('CapActionButton', () => {
|
|
|
123
128
|
id: 1,
|
|
124
129
|
},
|
|
125
130
|
};
|
|
126
|
-
const urlInput = await screen.getByPlaceholderText('Enter
|
|
131
|
+
const urlInput = await screen.getByPlaceholderText('Enter URL');
|
|
127
132
|
fireEvent.change(urlInput, urlEvent);
|
|
128
133
|
expect(updateHandler).toHaveBeenCalledWith(
|
|
129
134
|
{
|
|
@@ -163,6 +168,27 @@ describe('CapActionButton', () => {
|
|
|
163
168
|
);
|
|
164
169
|
});
|
|
165
170
|
|
|
171
|
+
it('should show delete for quick reply index 0 for non-ICS hosts but hide it for ICS (stop button)', () => {
|
|
172
|
+
const button = {
|
|
173
|
+
index: 0,
|
|
174
|
+
type: RCS_BUTTON_TYPES.QUICK_REPLY,
|
|
175
|
+
text: 'Reply',
|
|
176
|
+
phoneNumber: '',
|
|
177
|
+
url: '',
|
|
178
|
+
postback: 'Reply',
|
|
179
|
+
isSaved: false,
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// Non-ICS (Infobip/others): delete should be available
|
|
183
|
+
const { unmount } = initializeComponent([button], false, 3, 'rcsinfobipbulk');
|
|
184
|
+
expect(screen.getByRole('button', { name: /delete/i })).toBeInTheDocument();
|
|
185
|
+
unmount();
|
|
186
|
+
|
|
187
|
+
// ICS: index 0 is reserved; delete should be hidden
|
|
188
|
+
initializeComponent([button], false, 3, HOST_ICS);
|
|
189
|
+
expect(screen.queryByRole('button', { name: /delete/i })).not.toBeInTheDocument();
|
|
190
|
+
});
|
|
191
|
+
|
|
166
192
|
it('should respect character length limits', async () => {
|
|
167
193
|
const initialData = {
|
|
168
194
|
index: 0,
|
|
@@ -201,7 +227,7 @@ describe('CapActionButton', () => {
|
|
|
201
227
|
isSaved: false,
|
|
202
228
|
};
|
|
203
229
|
initializeComponent([initialData]);
|
|
204
|
-
const urlInput = await screen.getByPlaceholderText('Enter
|
|
230
|
+
const urlInput = await screen.getByPlaceholderText('Enter URL');
|
|
205
231
|
const longUrl = 'https://example.com/' + 'a'.repeat(URL_MAX_LENGTH + 1);
|
|
206
232
|
fireEvent.change(urlInput, { target: { value: longUrl, id: 0 } });
|
|
207
233
|
expect(urlInput.value.length).toBeLessThanOrEqual(URL_MAX_LENGTH);
|
|
@@ -248,7 +274,8 @@ describe('CapActionButton', () => {
|
|
|
248
274
|
{ index: 0, ctaType: RCS_BUTTON_TYPES.QUICK_REPLY, displayText: 'stop', phoneNumber: '', url: '', postback: 'stop', isSaved: true },
|
|
249
275
|
{ index: 1, ctaType: RCS_BUTTON_TYPES.QUICK_REPLY, displayText: 'Saved', phoneNumber: '', url: '', postback: 'Saved', isSaved: true },
|
|
250
276
|
];
|
|
251
|
-
|
|
277
|
+
// Use ICS host so index 0 (STOP) stays non-deletable and we only get delete icon for index 1
|
|
278
|
+
const { container } = initializeComponent(suggestions, false, 3, HOST_ICS);
|
|
252
279
|
const deleteIcons = container.querySelectorAll('.rcs-saved-cta-delete-icon');
|
|
253
280
|
expect(deleteIcons.length).toBeGreaterThan(0);
|
|
254
281
|
fireEvent.click(deleteIcons[0]);
|
|
@@ -306,7 +333,8 @@ describe('CapActionButton', () => {
|
|
|
306
333
|
postback: 'stop',
|
|
307
334
|
isSaved: true,
|
|
308
335
|
};
|
|
309
|
-
|
|
336
|
+
// Only ICS treats index 0 as a mandatory STOP quick reply
|
|
337
|
+
const { container } = initializeComponent([stopButton], false, 3, HOST_ICS);
|
|
310
338
|
expect(container.querySelector('.rcs-saved-cta-delete-icon')).not.toBeInTheDocument();
|
|
311
339
|
});
|
|
312
340
|
|
|
@@ -427,7 +455,7 @@ describe('CapActionButton', () => {
|
|
|
427
455
|
isSaved: false,
|
|
428
456
|
};
|
|
429
457
|
initializeComponent([button]);
|
|
430
|
-
expect(screen.getByPlaceholderText(/enter
|
|
458
|
+
expect(screen.getByPlaceholderText(/enter url/i)).toBeInTheDocument();
|
|
431
459
|
});
|
|
432
460
|
|
|
433
461
|
it('should update both displayText and postback when button text changes (updateDisplayAndPostback)', () => {
|
|
@@ -507,7 +535,7 @@ describe('CapActionButton', () => {
|
|
|
507
535
|
isSaved: false,
|
|
508
536
|
};
|
|
509
537
|
initializeComponent([initial]);
|
|
510
|
-
const urlInput = screen.getByPlaceholderText('Enter
|
|
538
|
+
const urlInput = screen.getByPlaceholderText('Enter URL');
|
|
511
539
|
fireEvent.change(urlInput, { target: { value: 'http://localhost:3030/creatives/ui/v2', id: 1 } });
|
|
512
540
|
expect(screen.getByText(/url is not valid/i)).toBeInTheDocument();
|
|
513
541
|
});
|
|
@@ -526,6 +554,34 @@ describe('CapActionButton', () => {
|
|
|
526
554
|
expect(screen.getByRole('button', { name: /save/i })).toBeDisabled();
|
|
527
555
|
});
|
|
528
556
|
|
|
557
|
+
it('should disable save for a dynamic-URL CTA still holding the unresolved {{1}} placeholder', () => {
|
|
558
|
+
const initial = {
|
|
559
|
+
index: 0,
|
|
560
|
+
type: RCS_BUTTON_TYPES.CTA,
|
|
561
|
+
text: 'Visit here',
|
|
562
|
+
url: `https://example.com${DYNAMIC_URL_SUFFIX}`,
|
|
563
|
+
urlType: RCS_CTA_URL_TYPE.DYNAMIC,
|
|
564
|
+
postback: 'Visit here',
|
|
565
|
+
isSaved: false,
|
|
566
|
+
};
|
|
567
|
+
initializeComponent([initial]);
|
|
568
|
+
expect(screen.getByRole('button', { name: /save/i })).toBeDisabled();
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
it('should enable save for a dynamic-URL CTA once the placeholder has been replaced with a real tag', () => {
|
|
572
|
+
const initial = {
|
|
573
|
+
index: 0,
|
|
574
|
+
type: RCS_BUTTON_TYPES.CTA,
|
|
575
|
+
text: 'Visit here',
|
|
576
|
+
url: 'https://example.com{{first_name}}',
|
|
577
|
+
urlType: RCS_CTA_URL_TYPE.DYNAMIC,
|
|
578
|
+
postback: 'Visit here',
|
|
579
|
+
isSaved: false,
|
|
580
|
+
};
|
|
581
|
+
initializeComponent([initial]);
|
|
582
|
+
expect(screen.getByRole('button', { name: /save/i })).not.toBeDisabled();
|
|
583
|
+
});
|
|
584
|
+
|
|
529
585
|
it('should render edit and delete icons in edit mode (renderedContent)', () => {
|
|
530
586
|
const button = {
|
|
531
587
|
index: 1,
|
|
@@ -607,12 +663,12 @@ describe('CapActionButton', () => {
|
|
|
607
663
|
);
|
|
608
664
|
};
|
|
609
665
|
render(<StatefulWrapper />);
|
|
610
|
-
let urlInput = screen.getByPlaceholderText('Enter
|
|
666
|
+
let urlInput = screen.getByPlaceholderText('Enter URL');
|
|
611
667
|
// Enter invalid URL
|
|
612
668
|
fireEvent.change(urlInput, { target: { value: 'badurl', id: 0 } });
|
|
613
669
|
expect(await screen.findByText(/url is not valid/i)).toBeInTheDocument();
|
|
614
670
|
// Re-grab input after re-render and enter valid URL
|
|
615
|
-
urlInput = screen.getByPlaceholderText('Enter
|
|
671
|
+
urlInput = screen.getByPlaceholderText('Enter URL');
|
|
616
672
|
fireEvent.change(urlInput, { target: { value: 'https://good.com', id: 0 } });
|
|
617
673
|
await waitFor(() => expect(screen.queryByText(/url is not valid/i)).not.toBeInTheDocument());
|
|
618
674
|
});
|
|
@@ -867,15 +923,14 @@ describe('CapActionButton function logic', () => {
|
|
|
867
923
|
expect(renderCtaOptions(label, tooltipLabel, false)).toBe('Test');
|
|
868
924
|
});
|
|
869
925
|
|
|
870
|
-
it('
|
|
871
|
-
const
|
|
872
|
-
const
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
</CapHeading>
|
|
926
|
+
it('inner char count uses current/max format', () => {
|
|
927
|
+
const formatMessage = (msg, values) => `${values.current}/${values.max}`;
|
|
928
|
+
const renderInnerCharCount = (len, max) => (
|
|
929
|
+
<span className="rcs-cta-inner-char-count">
|
|
930
|
+
{formatMessage({}, { current: len, max })}
|
|
931
|
+
</span>
|
|
877
932
|
);
|
|
878
|
-
const result =
|
|
933
|
+
const result = renderInnerCharCount(5, 25);
|
|
879
934
|
expect(result.props.children).toBe('5/25');
|
|
880
935
|
});
|
|
881
936
|
});
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import React, { useState, useEffect, useCallback } from 'react';
|
|
8
|
+
import React, { useState, useEffect, useCallback, useMemo } 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 = LabelHOC(ImageComponent);
|
|
95
|
+
const WithLabel = useMemo(() => LabelHOC(ImageComponent), [ImageComponent]);
|
|
96
96
|
|
|
97
97
|
const uploadImages = useCallback((e, { files }) => {
|
|
98
98
|
if (e) {
|
|
@@ -346,6 +346,8 @@ 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, getPopupContainer,
|
|
349
|
+
overlayStyle,
|
|
350
|
+
overlayClassName,
|
|
349
351
|
} = this.props;
|
|
350
352
|
const {formatMessage} = intl;
|
|
351
353
|
const {
|
|
@@ -440,6 +442,9 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
|
|
|
440
442
|
classNames={{
|
|
441
443
|
container: 'tag-list-popover-container'
|
|
442
444
|
}}
|
|
445
|
+
overlayStyle={overlayStyle}
|
|
446
|
+
overlayClassName={overlayClassName}
|
|
447
|
+
getPopupContainer={getPopupContainer}
|
|
443
448
|
>
|
|
444
449
|
<CapTooltip
|
|
445
450
|
title={
|
|
@@ -511,6 +516,10 @@ CapTagList.propTypes = {
|
|
|
511
516
|
disableTooltipMsg: PropTypes.string,
|
|
512
517
|
fetchingSchemaError: PropTypes.bool,
|
|
513
518
|
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,
|
|
514
523
|
};
|
|
515
524
|
|
|
516
525
|
CapTagList.defaultValue = {
|