@capillarytech/creatives-library 8.0.345-alpha.13 → 8.0.345-alpha.15
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/package.json +1 -1
- package/services/api.js +0 -20
- package/services/tests/api.test.js +13 -59
- package/utils/commonUtils.js +19 -1
- package/utils/rcsPayloadUtils.js +92 -0
- package/utils/templateVarUtils.js +201 -0
- package/utils/tests/templateVarUtils.test.js +204 -0
- package/v2Components/CapActionButton/constants.js +7 -0
- package/v2Components/CapActionButton/index.js +167 -109
- package/v2Components/CapActionButton/index.scss +157 -6
- package/v2Components/CapActionButton/messages.js +19 -3
- package/v2Components/CapActionButton/tests/index.test.js +41 -17
- package/v2Components/CapCustomSkeleton/index.js +1 -1
- package/v2Components/CapCustomSkeleton/tests/__snapshots__/index.test.js.snap +12 -12
- package/v2Components/CapTagList/index.js +10 -0
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -49
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +207 -21
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +85 -10
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +79 -11
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +160 -15
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js.rej +18 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +341 -76
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +133 -4
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
- package/v2Components/CommonTestAndPreview/constants.js +38 -2
- package/v2Components/CommonTestAndPreview/index.js +676 -186
- package/v2Components/CommonTestAndPreview/messages.js +49 -3
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
- package/v2Components/CommonTestAndPreview/sagas.js +15 -6
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +308 -284
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +231 -65
- 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 +34 -13
- 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 +132 -4
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
- package/v2Components/FormBuilder/index.js +8 -10
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +87 -0
- package/v2Components/SmsFallback/constants.js +73 -0
- package/v2Components/SmsFallback/index.js +955 -0
- package/v2Components/SmsFallback/index.scss +265 -0
- package/v2Components/SmsFallback/messages.js +78 -0
- package/v2Components/SmsFallback/smsFallbackUtils.js +118 -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 +197 -0
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +277 -0
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
- package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
- package/v2Components/TemplatePreview/_templatePreview.scss +33 -23
- package/v2Components/TemplatePreview/constants.js +2 -0
- package/v2Components/TemplatePreview/index.js +143 -28
- package/v2Components/TemplatePreview/tests/index.test.js +142 -0
- 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/CreativesContainer/CreativesSlideBoxWrapper.js +43 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +36 -4
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +11 -4
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
- package/v2Containers/CreativesContainer/constants.js +9 -0
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +67 -0
- package/v2Containers/CreativesContainer/index.js +300 -108
- package/v2Containers/CreativesContainer/index.scss +51 -1
- package/v2Containers/CreativesContainer/messages.js +0 -4
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +78 -34
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +79 -16
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +357 -98
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -18
- 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/Rcs/constants.js +119 -8
- package/v2Containers/Rcs/index.js +2379 -807
- package/v2Containers/Rcs/index.js.rej +1336 -0
- package/v2Containers/Rcs/index.scss +276 -6
- package/v2Containers/Rcs/index.scss.rej +74 -0
- package/v2Containers/Rcs/messages.js +38 -3
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +225 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +98018 -70073
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap.rej +128 -0
- package/v2Containers/Rcs/tests/index.test.js +152 -121
- package/v2Containers/Rcs/tests/mockData.js +38 -0
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +318 -0
- package/v2Containers/Rcs/tests/utils.test.js +646 -30
- package/v2Containers/Rcs/utils.js +478 -11
- package/v2Containers/Sms/Create/index.js +100 -40
- 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 +636 -130
- package/v2Containers/SmsTrai/Edit/index.scss +121 -0
- package/v2Containers/SmsTrai/Edit/messages.js +14 -4
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4328 -2375
- package/v2Containers/SmsWrapper/index.js +37 -8
- package/v2Containers/TagList/index.js +6 -0
- package/v2Containers/Templates/ChannelTypeIllustration.js +6 -23
- package/v2Containers/Templates/TemplatesActionBar.js +101 -0
- package/v2Containers/Templates/_templates.scss +181 -126
- package/v2Containers/Templates/actions.js +11 -36
- package/v2Containers/Templates/constants.js +2 -23
- package/v2Containers/Templates/index.js +142 -333
- package/v2Containers/Templates/messages.js +0 -68
- package/v2Containers/Templates/reducer.js +0 -68
- package/v2Containers/Templates/sagas.js +55 -98
- package/v2Containers/Templates/selectors.js +0 -12
- package/v2Containers/Templates/tests/ChannelTypeIllustration.test.js +0 -12
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1042 -1256
- package/v2Containers/Templates/tests/index.test.js +0 -6
- package/v2Containers/Templates/tests/reducer.test.js +0 -178
- package/v2Containers/Templates/tests/sagas.test.js +200 -436
- package/v2Containers/Templates/tests/selector.test.js +0 -32
- 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 +86 -23
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
- package/v2Containers/Whatsapp/index.js +3 -20
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
- package/v2Containers/Assets/images/archive_Empty_Illustration.svg +0 -9
|
@@ -1,14 +1,27 @@
|
|
|
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 {
|
|
8
21
|
position: relative;
|
|
9
22
|
border: solid $CAP_SPACE_01 $CAP_G06;
|
|
10
23
|
padding: 0.625rem;
|
|
11
|
-
border-radius:
|
|
24
|
+
border-radius: 0.25rem; // 4px
|
|
12
25
|
margin-bottom: $CAP_SPACE_12;
|
|
13
26
|
|
|
14
27
|
div:not(:last-child) {
|
|
@@ -39,9 +52,139 @@
|
|
|
39
52
|
}
|
|
40
53
|
}
|
|
41
54
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
55
|
+
// Button text / URL: count via CapInput `suffix` (antd affix, right-aligned inside field).
|
|
56
|
+
// Phone: react-phone-input-2 — overlay count + padding on .form-control
|
|
57
|
+
.rcs-cta-inner-char-count {
|
|
58
|
+
font-size: 0.75rem;
|
|
59
|
+
line-height: 1.25rem;
|
|
60
|
+
color: $FONT_COLOR_03;
|
|
61
|
+
white-space: nowrap;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.rcs-cta-input-with-inner-count {
|
|
65
|
+
position: relative;
|
|
66
|
+
width: 100%;
|
|
67
|
+
|
|
68
|
+
&--phone .rcs-cta-inner-char-count {
|
|
69
|
+
position: absolute;
|
|
70
|
+
right: 0.6875rem;
|
|
71
|
+
top: 50%;
|
|
72
|
+
transform: translateY(-50%);
|
|
73
|
+
z-index: 2;
|
|
74
|
+
pointer-events: none;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// RCS phone field: Figma — one light border, 4px radius, ~antd Input large height, full width, count inside right
|
|
79
|
+
.rcs-button-cta-create-container .rcs-cta-input-with-inner-count--phone {
|
|
80
|
+
.react-tel-input.rcs-cta-phone-input {
|
|
81
|
+
width: 100%;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.rcs-cta-phone-input .form-control {
|
|
85
|
+
width: 100% !important;
|
|
86
|
+
max-width: none;
|
|
87
|
+
height: 2.5rem;
|
|
88
|
+
min-height: 2.5rem;
|
|
89
|
+
padding-top: 0;
|
|
90
|
+
padding-bottom: 0;
|
|
91
|
+
padding-left: 3rem;
|
|
92
|
+
padding-right: 3.5rem;
|
|
93
|
+
margin: 0;
|
|
94
|
+
font-size: 0.875rem;
|
|
95
|
+
line-height: 2.5rem;
|
|
96
|
+
color: $CAP_G01;
|
|
97
|
+
background: $CAP_WHITE;
|
|
98
|
+
border: 1px solid $CAP_G06;
|
|
99
|
+
border-radius: 0.25rem;
|
|
100
|
+
box-shadow: none;
|
|
101
|
+
|
|
102
|
+
&::placeholder {
|
|
103
|
+
color: $CAP_G05;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
&:hover {
|
|
107
|
+
border-color: $CAP_G11;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
&:focus {
|
|
111
|
+
border-color: $CAP_G01;
|
|
112
|
+
box-shadow: none;
|
|
113
|
+
outline: none;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.rcs-cta-phone-input .flag-dropdown {
|
|
118
|
+
top: 1px;
|
|
119
|
+
bottom: 1px;
|
|
120
|
+
left: 1px;
|
|
121
|
+
padding: 0;
|
|
122
|
+
background: $CAP_WHITE;
|
|
123
|
+
border: none;
|
|
124
|
+
border-radius: 0.1875rem 0 0 0.1875rem; // inset from outer 4px radius
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.rcs-cta-phone-input .flag-dropdown.open {
|
|
128
|
+
border-radius: 0.1875rem 0 0 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.rcs-cta-phone-input .selected-flag {
|
|
132
|
+
width: 2.75rem;
|
|
133
|
+
padding: 0 0 0 0.5rem;
|
|
134
|
+
background: transparent;
|
|
135
|
+
border-radius: 0.1875rem 0 0 0.1875rem;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.rcs-cta-phone-input .selected-flag:hover,
|
|
139
|
+
.rcs-cta-phone-input .selected-flag:focus {
|
|
140
|
+
background: $CAP_G09;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.rcs-cta-phone-input .selected-flag .arrow {
|
|
144
|
+
left: 1.375rem;
|
|
145
|
+
border-top-color: $CAP_G04;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// URL CTA: URL type (narrow) + URL field (~1:3) per Figma
|
|
150
|
+
.rcs-cta-url-fields-row {
|
|
151
|
+
.rcs-cta-url-type-col .ant-select {
|
|
152
|
+
width: 100%;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// RCS “create CTA” card: 0.5rem padding + gaps (Figma-tight; fixes excess space above first label)
|
|
157
|
+
.rcs-button-cta-create-container.cap-row-v2 {
|
|
158
|
+
display: flex;
|
|
159
|
+
flex-direction: column;
|
|
160
|
+
flex-wrap: nowrap;
|
|
161
|
+
width: 100%;
|
|
162
|
+
border: solid 1px $CAP_G06;
|
|
163
|
+
padding: 1.25rem;
|
|
164
|
+
border-radius: 0.25rem; // 4px — match .cap-rcs-saved-cta
|
|
165
|
+
|
|
166
|
+
// Stack “Button type” (radios) above “Button text”
|
|
167
|
+
.rcs-button-cta-create.cap-row-v2 {
|
|
168
|
+
display: flex;
|
|
169
|
+
flex-direction: column;
|
|
170
|
+
flex-wrap: nowrap;
|
|
171
|
+
margin: 0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Space after each field label (CapHeading margins are often zeroed by the library)
|
|
175
|
+
.cta-label {
|
|
176
|
+
margin-bottom: 0;
|
|
177
|
+
margin-top: 0.75rem;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.cta-label + * {
|
|
181
|
+
margin-top: 0.5rem;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// “Button type” heading has no .cta-label — keep same label→control gap as other fields
|
|
185
|
+
.rcs-button-cta-create > .ant-col:first-of-type .cap-rcs-cta-type-radio {
|
|
186
|
+
margin-top: 0.5rem;
|
|
187
|
+
}
|
|
45
188
|
}
|
|
46
189
|
|
|
47
190
|
.disabled {
|
|
@@ -55,4 +198,12 @@
|
|
|
55
198
|
|
|
56
199
|
.button-disabled-tooltip-wrapper {
|
|
57
200
|
display: inline-block;
|
|
201
|
+
margin-top: 1.25rem;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Space between Save and Delete on RCS / carousel CTA rows
|
|
205
|
+
.rcs-cta-save-delete-btn {
|
|
206
|
+
.rcs-cta-delete-btn {
|
|
207
|
+
margin-left: 0.75rem;
|
|
208
|
+
}
|
|
58
209
|
}
|
|
@@ -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`,
|
|
@@ -4,7 +4,7 @@ import '@testing-library/jest-dom';
|
|
|
4
4
|
import { render, screen, fireEvent } from '../../../utils/test-utils';
|
|
5
5
|
import { CapActionButton } from '../index';
|
|
6
6
|
import { BTN_MAX_LENGTH, PHONE_NUMBER_MAX_LENGTH, URL_MAX_LENGTH } from '../constants';
|
|
7
|
-
import { RCS_BUTTON_TYPES } from '../../../v2Containers/Rcs/constants';
|
|
7
|
+
import { RCS_BUTTON_TYPES, HOST_ICS } from '../../../v2Containers/Rcs/constants';
|
|
8
8
|
|
|
9
9
|
const updateHandler = jest.fn();
|
|
10
10
|
const deleteHandler = jest.fn();
|
|
@@ -12,6 +12,7 @@ const initializeComponent = (
|
|
|
12
12
|
data,
|
|
13
13
|
isEditFlow = false,
|
|
14
14
|
maxButtons = 3,
|
|
15
|
+
host = '',
|
|
15
16
|
) => {
|
|
16
17
|
// Normalize legacy test data shape to match component props
|
|
17
18
|
const normalizeType = (legacyType) => {
|
|
@@ -37,6 +38,7 @@ const initializeComponent = (
|
|
|
37
38
|
isEditFlow={isEditFlow}
|
|
38
39
|
maxButtons={maxButtons}
|
|
39
40
|
isFullMode={true}
|
|
41
|
+
host={host}
|
|
40
42
|
/>,
|
|
41
43
|
);
|
|
42
44
|
};
|
|
@@ -59,7 +61,7 @@ describe('CapActionButton', () => {
|
|
|
59
61
|
isSaved: false,
|
|
60
62
|
},
|
|
61
63
|
]);
|
|
62
|
-
expect(screen.getByText('
|
|
64
|
+
expect(screen.getByText('Button type')).toBeInTheDocument();
|
|
63
65
|
expect(screen.getByText('Button text')).toBeInTheDocument();
|
|
64
66
|
expect(screen.getAllByText('Phone number')[1]).toBeInTheDocument();
|
|
65
67
|
expect(screen.getByRole('button', { name: /save/i })).toBeDisabled();
|
|
@@ -122,7 +124,7 @@ describe('CapActionButton', () => {
|
|
|
122
124
|
id: 1,
|
|
123
125
|
},
|
|
124
126
|
};
|
|
125
|
-
const urlInput = await screen.getByPlaceholderText('Enter
|
|
127
|
+
const urlInput = await screen.getByPlaceholderText('Enter URL');
|
|
126
128
|
fireEvent.change(urlInput, urlEvent);
|
|
127
129
|
expect(updateHandler).toHaveBeenCalledWith(
|
|
128
130
|
{
|
|
@@ -162,6 +164,27 @@ describe('CapActionButton', () => {
|
|
|
162
164
|
);
|
|
163
165
|
});
|
|
164
166
|
|
|
167
|
+
it('should show delete for quick reply index 0 for non-ICS hosts but hide it for ICS (stop button)', () => {
|
|
168
|
+
const button = {
|
|
169
|
+
index: 0,
|
|
170
|
+
type: RCS_BUTTON_TYPES.QUICK_REPLY,
|
|
171
|
+
text: 'Reply',
|
|
172
|
+
phoneNumber: '',
|
|
173
|
+
url: '',
|
|
174
|
+
postback: 'Reply',
|
|
175
|
+
isSaved: false,
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// Non-ICS (Infobip/others): delete should be available
|
|
179
|
+
const { unmount } = initializeComponent([button], false, 3, 'rcsinfobipbulk');
|
|
180
|
+
expect(screen.getByRole('button', { name: /delete/i })).toBeInTheDocument();
|
|
181
|
+
unmount();
|
|
182
|
+
|
|
183
|
+
// ICS: index 0 is reserved; delete should be hidden
|
|
184
|
+
initializeComponent([button], false, 3, HOST_ICS);
|
|
185
|
+
expect(screen.queryByRole('button', { name: /delete/i })).not.toBeInTheDocument();
|
|
186
|
+
});
|
|
187
|
+
|
|
165
188
|
it('should respect character length limits', async () => {
|
|
166
189
|
const initialData = {
|
|
167
190
|
index: 0,
|
|
@@ -198,7 +221,7 @@ describe('CapActionButton', () => {
|
|
|
198
221
|
isSaved: false,
|
|
199
222
|
};
|
|
200
223
|
initializeComponent([initialData]);
|
|
201
|
-
const urlInput = await screen.getByPlaceholderText('Enter
|
|
224
|
+
const urlInput = await screen.getByPlaceholderText('Enter URL');
|
|
202
225
|
const longUrl = 'https://example.com/' + 'a'.repeat(URL_MAX_LENGTH + 1);
|
|
203
226
|
fireEvent.change(urlInput, { target: { value: longUrl, id: 0 } });
|
|
204
227
|
expect(urlInput.value.length).toBeLessThanOrEqual(URL_MAX_LENGTH);
|
|
@@ -245,7 +268,8 @@ describe('CapActionButton', () => {
|
|
|
245
268
|
{ index: 0, ctaType: RCS_BUTTON_TYPES.QUICK_REPLY, displayText: 'stop', phoneNumber: '', url: '', postback: 'stop', isSaved: true },
|
|
246
269
|
{ index: 1, ctaType: RCS_BUTTON_TYPES.QUICK_REPLY, displayText: 'Saved', phoneNumber: '', url: '', postback: 'Saved', isSaved: true },
|
|
247
270
|
];
|
|
248
|
-
|
|
271
|
+
// Use ICS host so index 0 (STOP) stays non-deletable and we only get delete icon for index 1
|
|
272
|
+
const { container } = initializeComponent(suggestions, false, 3, HOST_ICS);
|
|
249
273
|
const deleteIcons = container.querySelectorAll('.rcs-saved-cta-delete-icon');
|
|
250
274
|
expect(deleteIcons.length).toBeGreaterThan(0);
|
|
251
275
|
fireEvent.click(deleteIcons[0]);
|
|
@@ -303,7 +327,8 @@ describe('CapActionButton', () => {
|
|
|
303
327
|
postback: 'stop',
|
|
304
328
|
isSaved: true,
|
|
305
329
|
};
|
|
306
|
-
|
|
330
|
+
// Only ICS treats index 0 as a mandatory STOP quick reply
|
|
331
|
+
const { container } = initializeComponent([stopButton], false, 3, HOST_ICS);
|
|
307
332
|
expect(container.querySelector('.rcs-saved-cta-delete-icon')).not.toBeInTheDocument();
|
|
308
333
|
});
|
|
309
334
|
|
|
@@ -424,7 +449,7 @@ describe('CapActionButton', () => {
|
|
|
424
449
|
isSaved: false,
|
|
425
450
|
};
|
|
426
451
|
initializeComponent([button]);
|
|
427
|
-
expect(screen.getByPlaceholderText(/enter
|
|
452
|
+
expect(screen.getByPlaceholderText(/enter url/i)).toBeInTheDocument();
|
|
428
453
|
});
|
|
429
454
|
|
|
430
455
|
it('should update both displayText and postback when button text changes (updateDisplayAndPostback)', () => {
|
|
@@ -504,7 +529,7 @@ describe('CapActionButton', () => {
|
|
|
504
529
|
isSaved: false,
|
|
505
530
|
};
|
|
506
531
|
initializeComponent([initial]);
|
|
507
|
-
const urlInput = screen.getByPlaceholderText('Enter
|
|
532
|
+
const urlInput = screen.getByPlaceholderText('Enter URL');
|
|
508
533
|
fireEvent.change(urlInput, { target: { value: 'http://localhost:3030/creatives/ui/v2', id: 1 } });
|
|
509
534
|
expect(screen.getByText(/url is not valid/i)).toBeInTheDocument();
|
|
510
535
|
});
|
|
@@ -590,7 +615,7 @@ describe('CapActionButton', () => {
|
|
|
590
615
|
isSaved: false,
|
|
591
616
|
};
|
|
592
617
|
initializeComponent([initial]);
|
|
593
|
-
const urlInput = screen.getByPlaceholderText('Enter
|
|
618
|
+
const urlInput = screen.getByPlaceholderText('Enter URL');
|
|
594
619
|
// Enter invalid URL
|
|
595
620
|
fireEvent.change(urlInput, { target: { value: 'badurl', id: 0 } });
|
|
596
621
|
expect(screen.getByText(/url is not valid/i)).toBeInTheDocument();
|
|
@@ -849,15 +874,14 @@ describe('CapActionButton function logic', () => {
|
|
|
849
874
|
expect(renderCtaOptions(label, tooltipLabel, false)).toBe('Test');
|
|
850
875
|
});
|
|
851
876
|
|
|
852
|
-
it('
|
|
853
|
-
const
|
|
854
|
-
const
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
</CapHeading>
|
|
877
|
+
it('inner char count uses current/max format', () => {
|
|
878
|
+
const formatMessage = (msg, values) => `${values.current}/${values.max}`;
|
|
879
|
+
const renderInnerCharCount = (len, max) => (
|
|
880
|
+
<span className="rcs-cta-inner-char-count">
|
|
881
|
+
{formatMessage({}, { current: len, max })}
|
|
882
|
+
</span>
|
|
859
883
|
);
|
|
860
|
-
const result =
|
|
884
|
+
const result = renderInnerCharCount(5, 25);
|
|
861
885
|
expect(result.props.children).toBe('5/25');
|
|
862
886
|
});
|
|
863
887
|
});
|
|
@@ -20,7 +20,7 @@ exports[`CapCustomSkeleton test renders correct CapCustomSkeleton component 1`]
|
|
|
20
20
|
>
|
|
21
21
|
<CapColumn
|
|
22
22
|
key="0"
|
|
23
|
-
lg={
|
|
23
|
+
lg={6}
|
|
24
24
|
md={8}
|
|
25
25
|
sm={12}
|
|
26
26
|
xs={24}
|
|
@@ -40,7 +40,7 @@ exports[`CapCustomSkeleton test renders correct CapCustomSkeleton component 1`]
|
|
|
40
40
|
</CapColumn>
|
|
41
41
|
<CapColumn
|
|
42
42
|
key="1"
|
|
43
|
-
lg={
|
|
43
|
+
lg={6}
|
|
44
44
|
md={8}
|
|
45
45
|
sm={12}
|
|
46
46
|
xs={24}
|
|
@@ -60,7 +60,7 @@ exports[`CapCustomSkeleton test renders correct CapCustomSkeleton component 1`]
|
|
|
60
60
|
</CapColumn>
|
|
61
61
|
<CapColumn
|
|
62
62
|
key="2"
|
|
63
|
-
lg={
|
|
63
|
+
lg={6}
|
|
64
64
|
md={8}
|
|
65
65
|
sm={12}
|
|
66
66
|
xs={24}
|
|
@@ -80,7 +80,7 @@ exports[`CapCustomSkeleton test renders correct CapCustomSkeleton component 1`]
|
|
|
80
80
|
</CapColumn>
|
|
81
81
|
<CapColumn
|
|
82
82
|
key="3"
|
|
83
|
-
lg={
|
|
83
|
+
lg={6}
|
|
84
84
|
md={8}
|
|
85
85
|
sm={12}
|
|
86
86
|
xs={24}
|
|
@@ -100,7 +100,7 @@ exports[`CapCustomSkeleton test renders correct CapCustomSkeleton component 1`]
|
|
|
100
100
|
</CapColumn>
|
|
101
101
|
<CapColumn
|
|
102
102
|
key="4"
|
|
103
|
-
lg={
|
|
103
|
+
lg={6}
|
|
104
104
|
md={8}
|
|
105
105
|
sm={12}
|
|
106
106
|
xs={24}
|
|
@@ -120,7 +120,7 @@ exports[`CapCustomSkeleton test renders correct CapCustomSkeleton component 1`]
|
|
|
120
120
|
</CapColumn>
|
|
121
121
|
<CapColumn
|
|
122
122
|
key="5"
|
|
123
|
-
lg={
|
|
123
|
+
lg={6}
|
|
124
124
|
md={8}
|
|
125
125
|
sm={12}
|
|
126
126
|
xs={24}
|
|
@@ -140,7 +140,7 @@ exports[`CapCustomSkeleton test renders correct CapCustomSkeleton component 1`]
|
|
|
140
140
|
</CapColumn>
|
|
141
141
|
<CapColumn
|
|
142
142
|
key="6"
|
|
143
|
-
lg={
|
|
143
|
+
lg={6}
|
|
144
144
|
md={8}
|
|
145
145
|
sm={12}
|
|
146
146
|
xs={24}
|
|
@@ -160,7 +160,7 @@ exports[`CapCustomSkeleton test renders correct CapCustomSkeleton component 1`]
|
|
|
160
160
|
</CapColumn>
|
|
161
161
|
<CapColumn
|
|
162
162
|
key="7"
|
|
163
|
-
lg={
|
|
163
|
+
lg={6}
|
|
164
164
|
md={8}
|
|
165
165
|
sm={12}
|
|
166
166
|
xs={24}
|
|
@@ -180,7 +180,7 @@ exports[`CapCustomSkeleton test renders correct CapCustomSkeleton component 1`]
|
|
|
180
180
|
</CapColumn>
|
|
181
181
|
<CapColumn
|
|
182
182
|
key="8"
|
|
183
|
-
lg={
|
|
183
|
+
lg={6}
|
|
184
184
|
md={8}
|
|
185
185
|
sm={12}
|
|
186
186
|
xs={24}
|
|
@@ -200,7 +200,7 @@ exports[`CapCustomSkeleton test renders correct CapCustomSkeleton component 1`]
|
|
|
200
200
|
</CapColumn>
|
|
201
201
|
<CapColumn
|
|
202
202
|
key="9"
|
|
203
|
-
lg={
|
|
203
|
+
lg={6}
|
|
204
204
|
md={8}
|
|
205
205
|
sm={12}
|
|
206
206
|
xs={24}
|
|
@@ -220,7 +220,7 @@ exports[`CapCustomSkeleton test renders correct CapCustomSkeleton component 1`]
|
|
|
220
220
|
</CapColumn>
|
|
221
221
|
<CapColumn
|
|
222
222
|
key="10"
|
|
223
|
-
lg={
|
|
223
|
+
lg={6}
|
|
224
224
|
md={8}
|
|
225
225
|
sm={12}
|
|
226
226
|
xs={24}
|
|
@@ -240,7 +240,7 @@ exports[`CapCustomSkeleton test renders correct CapCustomSkeleton component 1`]
|
|
|
240
240
|
</CapColumn>
|
|
241
241
|
<CapColumn
|
|
242
242
|
key="11"
|
|
243
|
-
lg={
|
|
243
|
+
lg={6}
|
|
244
244
|
md={8}
|
|
245
245
|
sm={12}
|
|
246
246
|
xs={24}
|
|
@@ -386,6 +386,9 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
|
|
|
386
386
|
render() {
|
|
387
387
|
const {
|
|
388
388
|
hidePopover = false, intl = {}, moduleFilterEnabled, label, modalProps, channel, fetchingSchemaError = false,
|
|
389
|
+
overlayStyle,
|
|
390
|
+
overlayClassName,
|
|
391
|
+
getPopupContainer,
|
|
389
392
|
} = this.props;
|
|
390
393
|
const {formatMessage} = intl;
|
|
391
394
|
const {
|
|
@@ -478,6 +481,9 @@ class CapTagList extends React.Component { // eslint-disable-line react/prefer-s
|
|
|
478
481
|
content={contentSection}
|
|
479
482
|
trigger="click"
|
|
480
483
|
placement={this.props.popoverPlacement || (channel === EMAIL.toUpperCase() ? "leftTop" : "rightTop")}
|
|
484
|
+
overlayStyle={overlayStyle}
|
|
485
|
+
overlayClassName={overlayClassName}
|
|
486
|
+
getPopupContainer={getPopupContainer}
|
|
481
487
|
>
|
|
482
488
|
<CapTooltip
|
|
483
489
|
title={
|
|
@@ -549,6 +555,10 @@ CapTagList.propTypes = {
|
|
|
549
555
|
disableTooltipMsg: PropTypes.string,
|
|
550
556
|
fetchingSchemaError: PropTypes.bool,
|
|
551
557
|
popoverPlacement: PropTypes.string,
|
|
558
|
+
overlayStyle: PropTypes.object,
|
|
559
|
+
overlayClassName: PropTypes.string,
|
|
560
|
+
/** e.g. () => document.body — avoids overflow/stacking issues inside slideboxes */
|
|
561
|
+
getPopupContainer: PropTypes.func,
|
|
552
562
|
};
|
|
553
563
|
|
|
554
564
|
CapTagList.defaultValue = {
|
|
@@ -8,6 +8,7 @@ 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';
|
|
11
12
|
|
|
12
13
|
const CustomValuesEditor = ({
|
|
13
14
|
isExtractingTags,
|
|
@@ -16,15 +17,16 @@ const CustomValuesEditor = ({
|
|
|
16
17
|
setShowJSON,
|
|
17
18
|
customValues,
|
|
18
19
|
handleJSONTextChange,
|
|
19
|
-
|
|
20
|
-
requiredTags,
|
|
21
|
-
optionalTags,
|
|
20
|
+
sections,
|
|
22
21
|
handleCustomValueChange,
|
|
23
22
|
handleDiscardCustomValues,
|
|
24
23
|
handleUpdatePreview,
|
|
25
24
|
isUpdatingPreview,
|
|
26
25
|
formatMessage,
|
|
27
26
|
}) => {
|
|
27
|
+
/** Same as SMS Test & Preview: show token path from extract-tags (fullPath or name). */
|
|
28
|
+
const getPersonalizationTagColumnLabel = (tagNode) => tagNode?.fullPath ?? tagNode?.name ?? '';
|
|
29
|
+
|
|
28
30
|
if (isExtractingTags) {
|
|
29
31
|
return (
|
|
30
32
|
<CapRow className="loading-container">
|
|
@@ -77,52 +79,68 @@ const CustomValuesEditor = ({
|
|
|
77
79
|
</CapRow>
|
|
78
80
|
) : (
|
|
79
81
|
<>
|
|
80
|
-
{
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
<CapLabel type="label31" className="header-cell">
|
|
87
|
-
<FormattedMessage {...messages.customValues} />
|
|
82
|
+
{(sections || []).filter((tagsSection) =>
|
|
83
|
+
(tagsSection?.requiredTags?.length || 0) + (tagsSection?.optionalTags?.length || 0) > 0).map((section) => (
|
|
84
|
+
<React.Fragment key={section.key || section.title?.id || section.title || CUSTOM_VALUES_EDITOR_SECTION_FALLBACK_KEY}>
|
|
85
|
+
{section.title ? (
|
|
86
|
+
<CapLabel type="label2" className="tags-section-title">
|
|
87
|
+
{typeof section.title === 'string' ? section.title : <FormattedMessage {...section.title} />}
|
|
88
88
|
</CapLabel>
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
<CapRow
|
|
92
|
-
<
|
|
93
|
-
{
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
type="text"
|
|
99
|
-
isRequired
|
|
100
|
-
className="tag-input-field"
|
|
101
|
-
value={customValues[tag.fullPath] || ''}
|
|
102
|
-
onChange={(e) => handleCustomValueChange(tag.fullPath, e.target.value)}
|
|
103
|
-
placeholder={formatMessage(messages.enterValue)}
|
|
104
|
-
size="small"
|
|
105
|
-
/>
|
|
106
|
-
</CapRow>
|
|
89
|
+
) : null}
|
|
90
|
+
<CapRow className="values-table">
|
|
91
|
+
<CapRow className="table-header">
|
|
92
|
+
<CapLabel type="label31" className="header-cell">
|
|
93
|
+
<FormattedMessage {...messages.personalizationTags} />
|
|
94
|
+
</CapLabel>
|
|
95
|
+
<CapLabel type="label31" className="header-cell">
|
|
96
|
+
<FormattedMessage {...messages.customValues} />
|
|
97
|
+
</CapLabel>
|
|
107
98
|
</CapRow>
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
<CapRow className="
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
99
|
+
{(section?.requiredTags || []).map((tag, tagIndex) => {
|
|
100
|
+
const personalizationTagColumnText = getPersonalizationTagColumnLabel(tag);
|
|
101
|
+
return (
|
|
102
|
+
<CapRow key={tag?.fullPath ?? `required-${tagIndex}`} className="value-row">
|
|
103
|
+
<CapRow className="tag-name">
|
|
104
|
+
{personalizationTagColumnText}
|
|
105
|
+
<span className="required-tag-indicator">*</span>
|
|
106
|
+
</CapRow>
|
|
107
|
+
<CapRow className="tag-input">
|
|
108
|
+
<CapInput
|
|
109
|
+
type="text"
|
|
110
|
+
isRequired
|
|
111
|
+
className="tag-input-field"
|
|
112
|
+
value={customValues?.[tag?.fullPath] ?? ''}
|
|
113
|
+
onChange={(e) => handleCustomValueChange(tag?.fullPath, e.target.value)}
|
|
114
|
+
placeholder={formatMessage(messages.enterValue)}
|
|
115
|
+
size="small"
|
|
116
|
+
/>
|
|
117
|
+
</CapRow>
|
|
121
118
|
</CapRow>
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
119
|
+
);
|
|
120
|
+
})}
|
|
121
|
+
{(section?.optionalTags || []).map((tag, tagIndex) => {
|
|
122
|
+
const personalizationTagColumnText = getPersonalizationTagColumnLabel(tag);
|
|
123
|
+
return (
|
|
124
|
+
<CapRow key={tag?.fullPath ?? `optional-${tagIndex}`} className="value-row">
|
|
125
|
+
<CapRow className="tag-name">
|
|
126
|
+
{personalizationTagColumnText}
|
|
127
|
+
</CapRow>
|
|
128
|
+
<CapRow className="tag-input">
|
|
129
|
+
<CapInput
|
|
130
|
+
type="text"
|
|
131
|
+
className="tag-input-field"
|
|
132
|
+
value={customValues?.[tag?.fullPath] ?? ''}
|
|
133
|
+
onChange={(e) => handleCustomValueChange(tag?.fullPath, e.target.value)}
|
|
134
|
+
placeholder={formatMessage(messages.enterValue)}
|
|
135
|
+
size="small"
|
|
136
|
+
/>
|
|
137
|
+
</CapRow>
|
|
138
|
+
</CapRow>
|
|
139
|
+
);
|
|
140
|
+
})}
|
|
141
|
+
</CapRow>
|
|
142
|
+
</React.Fragment>
|
|
143
|
+
))}
|
|
126
144
|
</>
|
|
127
145
|
)}
|
|
128
146
|
<CapRow className="editor-actions">
|
|
@@ -156,9 +174,12 @@ CustomValuesEditor.propTypes = {
|
|
|
156
174
|
setShowJSON: PropTypes.func.isRequired,
|
|
157
175
|
customValues: PropTypes.object.isRequired,
|
|
158
176
|
handleJSONTextChange: PropTypes.func.isRequired,
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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,
|
|
162
183
|
handleCustomValueChange: PropTypes.func.isRequired,
|
|
163
184
|
handleDiscardCustomValues: PropTypes.func.isRequired,
|
|
164
185
|
handleUpdatePreview: PropTypes.func.isRequired,
|