@capillarytech/creatives-library 8.0.345-alpha.14 → 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/tests/api.test.js +13 -0
- 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/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 +10 -1
- 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 -103
- package/v2Containers/CreativesContainer/index.scss +51 -1
- 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 -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/reducer.js +3 -11
- package/v2Containers/Email/sagas.js +5 -9
- package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +0 -4
- package/v2Containers/Email/tests/sagas.test.js +3 -21
- 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/TemplatesActionBar.js +101 -0
- package/v2Containers/Templates/_templates.scss +163 -2
- package/v2Containers/Templates/actions.js +11 -0
- package/v2Containers/Templates/constants.js +2 -0
- package/v2Containers/Templates/index.js +119 -54
- package/v2Containers/Templates/sagas.js +57 -12
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1043 -1079
- package/v2Containers/Templates/tests/sagas.test.js +193 -123
- 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
|
@@ -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
|
});
|
|
@@ -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,
|
|
@@ -18,11 +18,17 @@
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
&__summary-entry {
|
|
21
|
+
display: flex;
|
|
22
|
+
align-items: baseline;
|
|
23
|
+
gap: 0;
|
|
21
24
|
margin-right: $CAP_SPACE_18;
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
&__summary-key
|
|
25
|
-
|
|
27
|
+
&__summary-key,
|
|
28
|
+
&__summary-value {
|
|
29
|
+
line-height: 1.4;
|
|
30
|
+
margin-top: 0;
|
|
31
|
+
margin-bottom: 0;
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
&__edit-icon {
|