@capillarytech/creatives-library 8.0.353-alpha.6 → 8.0.353

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/constants/unified.js +0 -29
  2. package/package.json +1 -1
  3. package/services/tests/api.test.js +20 -35
  4. package/utils/commonUtils.js +1 -19
  5. package/v2Components/CapActionButton/constants.js +0 -7
  6. package/v2Components/CapActionButton/index.js +108 -166
  7. package/v2Components/CapActionButton/index.scss +6 -157
  8. package/v2Components/CapActionButton/messages.js +3 -19
  9. package/v2Components/CapActionButton/tests/index.test.js +17 -41
  10. package/v2Components/CapTagList/index.js +0 -10
  11. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +49 -72
  12. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
  13. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -213
  14. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
  15. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +10 -85
  16. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
  17. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -79
  18. package/v2Components/CommonTestAndPreview/SendTestMessage.js +5 -10
  19. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +15 -157
  20. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +76 -346
  21. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +4 -133
  22. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +0 -11
  23. package/v2Components/CommonTestAndPreview/constants.js +2 -38
  24. package/v2Components/CommonTestAndPreview/index.js +186 -691
  25. package/v2Components/CommonTestAndPreview/messages.js +3 -45
  26. package/v2Components/CommonTestAndPreview/sagas.js +6 -25
  27. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +284 -308
  28. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +65 -231
  29. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +5 -118
  30. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -341
  31. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +1 -8
  32. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +13 -34
  33. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +283 -281
  34. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
  35. package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -132
  36. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +26 -36
  37. package/v2Components/FormBuilder/index.js +168 -63
  38. package/v2Components/TemplatePreview/_templatePreview.scss +23 -38
  39. package/v2Components/TemplatePreview/index.js +31 -143
  40. package/v2Components/TemplatePreview/tests/index.test.js +0 -142
  41. package/v2Components/TestAndPreviewSlidebox/index.js +1 -13
  42. package/v2Components/TestAndPreviewSlidebox/sagas.js +4 -11
  43. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +1 -3
  44. package/v2Containers/CreativesContainer/SlideBoxContent.js +4 -36
  45. package/v2Containers/CreativesContainer/SlideBoxFooter.js +1 -10
  46. package/v2Containers/CreativesContainer/SlideBoxHeader.js +4 -29
  47. package/v2Containers/CreativesContainer/constants.js +0 -9
  48. package/v2Containers/CreativesContainer/index.js +163 -346
  49. package/v2Containers/CreativesContainer/index.scss +1 -51
  50. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -78
  51. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
  52. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
  53. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +98 -357
  54. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +15 -20
  55. package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
  56. package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
  57. package/v2Containers/Rcs/constants.js +10 -119
  58. package/v2Containers/Rcs/index.js +818 -2450
  59. package/v2Containers/Rcs/index.scss +8 -280
  60. package/v2Containers/Rcs/messages.js +3 -34
  61. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +70073 -98018
  62. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +5 -0
  63. package/v2Containers/Rcs/tests/index.test.js +121 -152
  64. package/v2Containers/Rcs/tests/mockData.js +0 -38
  65. package/v2Containers/Rcs/tests/utils.test.js +30 -646
  66. package/v2Containers/Rcs/utils.js +11 -478
  67. package/v2Containers/Sms/Create/index.js +40 -106
  68. package/v2Containers/SmsTrai/Create/index.js +4 -9
  69. package/v2Containers/SmsTrai/Edit/constants.js +0 -2
  70. package/v2Containers/SmsTrai/Edit/index.js +130 -640
  71. package/v2Containers/SmsTrai/Edit/messages.js +4 -14
  72. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2296 -4249
  73. package/v2Containers/SmsWrapper/index.js +8 -37
  74. package/v2Containers/TagList/index.js +0 -6
  75. package/v2Containers/Templates/_templates.scss +9 -166
  76. package/v2Containers/Templates/actions.js +0 -11
  77. package/v2Containers/Templates/constants.js +0 -2
  78. package/v2Containers/Templates/index.js +52 -120
  79. package/v2Containers/Templates/sagas.js +12 -56
  80. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1017 -1062
  81. package/v2Containers/Templates/tests/sagas.test.js +16 -199
  82. package/v2Containers/TemplatesV2/TemplatesV2.style.js +1 -72
  83. package/v2Containers/TemplatesV2/index.js +23 -86
  84. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
  85. package/v2Containers/Whatsapp/index.js +20 -3
  86. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -578
  87. package/utils/rcsPayloadUtils.js +0 -92
  88. package/utils/templateVarUtils.js +0 -201
  89. package/utils/tests/rcsPayloadUtils.test.js +0 -226
  90. package/utils/tests/templateVarUtils.test.js +0 -204
  91. package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
  92. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
  93. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -91
  94. package/v2Components/SmsFallback/constants.js +0 -73
  95. package/v2Components/SmsFallback/index.js +0 -956
  96. package/v2Components/SmsFallback/index.scss +0 -265
  97. package/v2Components/SmsFallback/messages.js +0 -78
  98. package/v2Components/SmsFallback/smsFallbackUtils.js +0 -119
  99. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
  100. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
  101. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
  102. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -223
  103. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -309
  104. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
  105. package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
  106. package/v2Components/TemplatePreview/constants.js +0 -2
  107. package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
  108. package/v2Components/VarSegmentMessageEditor/index.js +0 -125
  109. package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
  110. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -43
  111. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -79
  112. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
  113. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
  114. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
  115. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -225
  116. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -318
  117. package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
  118. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
  119. package/v2Containers/SmsTrai/Edit/index.scss +0 -121
  120. package/v2Containers/Templates/TemplatesActionBar.js +0 -101
  121. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
  122. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
  123. package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
  124. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +0 -131
@@ -1,253 +0,0 @@
1
- import {
2
- getSmsMessageFromFormData,
3
- getSmsEmbeddedFooterValidity,
4
- } from '../smsFormDataHelpers';
5
-
6
- // ---------------------------------------------------------------------------
7
- // getSmsMessageFromFormData
8
- // ---------------------------------------------------------------------------
9
-
10
- describe('getSmsMessageFromFormData', () => {
11
- describe('null / invalid formData guard', () => {
12
- it('returns empty string when formData is null', () => {
13
- expect(getSmsMessageFromFormData(null, 1)).toBe('');
14
- });
15
-
16
- it('returns empty string when formData is undefined', () => {
17
- expect(getSmsMessageFromFormData(undefined, 1)).toBe('');
18
- });
19
-
20
- it('returns empty string when formData is a string (not an object)', () => {
21
- expect(getSmsMessageFromFormData('bad', 1)).toBe('');
22
- });
23
-
24
- it('returns empty string when formData is a number', () => {
25
- expect(getSmsMessageFromFormData(42, 1)).toBe('');
26
- });
27
- });
28
-
29
- describe('currentTab normalisation', () => {
30
- it('defaults to tab 1 when currentTab is null', () => {
31
- const formData = { 0: { 'sms-editor': 'hello' } };
32
- expect(getSmsMessageFromFormData(formData, null)).toBe('hello');
33
- });
34
-
35
- it('defaults to tab 1 when currentTab is undefined', () => {
36
- const formData = { 0: { 'sms-editor': 'hello' } };
37
- expect(getSmsMessageFromFormData(formData, undefined)).toBe('hello');
38
- });
39
-
40
- it('defaults to tab 1 when currentTab is 0', () => {
41
- const formData = { 0: { 'sms-editor': 'hello' } };
42
- expect(getSmsMessageFromFormData(formData, 0)).toBe('hello');
43
- });
44
-
45
- it('defaults to tab 1 when currentTab is negative', () => {
46
- const formData = { 0: { 'sms-editor': 'hello' } };
47
- expect(getSmsMessageFromFormData(formData, -5)).toBe('hello');
48
- });
49
- });
50
-
51
- describe('versioned key lookup', () => {
52
- it('uses "sms-editor" key for tab 1', () => {
53
- const formData = { 0: { 'sms-editor': 'tab1 msg' } };
54
- expect(getSmsMessageFromFormData(formData, 1)).toBe('tab1 msg');
55
- });
56
-
57
- it('uses "sms-editor2" versioned key for tab 2', () => {
58
- const formData = { 1: { 'sms-editor2': 'tab2 msg', 'sms-editor': 'fallback' } };
59
- expect(getSmsMessageFromFormData(formData, 2)).toBe('tab2 msg');
60
- });
61
-
62
- it('uses "sms-editor3" versioned key for tab 3', () => {
63
- const formData = { 2: { 'sms-editor3': 'tab3 msg' } };
64
- expect(getSmsMessageFromFormData(formData, 3)).toBe('tab3 msg');
65
- });
66
-
67
- it('returns empty string when versioned key exists but is null (cleared version)', () => {
68
- const formData = { 1: { 'sms-editor2': null } };
69
- expect(getSmsMessageFromFormData(formData, 2)).toBe('');
70
- });
71
-
72
- it('returns empty string when versioned key exists but is empty string', () => {
73
- const formData = { 1: { 'sms-editor2': '' } };
74
- expect(getSmsMessageFromFormData(formData, 2)).toBe('');
75
- });
76
-
77
- it('coerces a numeric value in versioned key to string', () => {
78
- const formData = { 1: { 'sms-editor2': 12345 } };
79
- expect(getSmsMessageFromFormData(formData, 2)).toBe('12345');
80
- });
81
- });
82
-
83
- describe('"sms-editor" flat key fallback (versioned key absent)', () => {
84
- it('falls back to "sms-editor" when versioned key is absent for tab 2', () => {
85
- const formData = { 1: { 'sms-editor': 'flat fallback' } };
86
- expect(getSmsMessageFromFormData(formData, 2)).toBe('flat fallback');
87
- });
88
- });
89
-
90
- describe('activeTab nested fallback', () => {
91
- it('falls back to activeTab["sms-editor"] when neither versioned key nor flat "sms-editor" is present', () => {
92
- // The versioned key must be absent entirely for the activeTab fallback to be reached.
93
- // (If 'sms-editor' exists but is null, the function commits to '' without falling through.)
94
- const formData = {
95
- 0: {
96
- activeTab: 'variant1',
97
- variant1: { 'sms-editor': 'variant msg' },
98
- },
99
- };
100
- expect(getSmsMessageFromFormData(formData, 1)).toBe('variant msg');
101
- });
102
-
103
- it('falls back to base["sms-editor"] when activeTab entry lacks "sms-editor" and flat key is absent', () => {
104
- const formData = {
105
- 0: {
106
- activeTab: 'variant1',
107
- variant1: {},
108
- base: { 'sms-editor': 'base msg' },
109
- },
110
- };
111
- expect(getSmsMessageFromFormData(formData, 1)).toBe('base msg');
112
- });
113
-
114
- it('defaults activeTab to "base" when activeTab prop is not set and flat key is absent', () => {
115
- const formData = {
116
- 0: {
117
- base: { 'sms-editor': 'default base msg' },
118
- },
119
- };
120
- expect(getSmsMessageFromFormData(formData, 1)).toBe('default base msg');
121
- });
122
- });
123
-
124
- describe('root formData.base fallback', () => {
125
- it('falls back to formData.base["sms-editor"] when tab slot is missing', () => {
126
- const formData = {
127
- base: { 'sms-editor': 'root base msg' },
128
- };
129
- expect(getSmsMessageFromFormData(formData, 1)).toBe('root base msg');
130
- });
131
-
132
- it('returns empty string when even root base has no "sms-editor"', () => {
133
- const formData = { base: {} };
134
- expect(getSmsMessageFromFormData(formData, 1)).toBe('');
135
- });
136
-
137
- it('returns empty string when formData has no relevant keys at all', () => {
138
- expect(getSmsMessageFromFormData({}, 1)).toBe('');
139
- });
140
- });
141
- });
142
-
143
- // ---------------------------------------------------------------------------
144
- // getSmsEmbeddedFooterValidity
145
- // ---------------------------------------------------------------------------
146
-
147
- describe('getSmsEmbeddedFooterValidity', () => {
148
- describe('isTemplateNameEmpty', () => {
149
- it('is true when template-name is absent', () => {
150
- const formData = { 0: { 'sms-editor': 'msg' } };
151
- const { isTemplateNameEmpty } = getSmsEmbeddedFooterValidity(formData, 1);
152
- expect(isTemplateNameEmpty).toBe(true);
153
- });
154
-
155
- it('is true when template-name is empty string', () => {
156
- const formData = { 'template-name': '', 0: { 'sms-editor': 'msg' } };
157
- const { isTemplateNameEmpty } = getSmsEmbeddedFooterValidity(formData, 1);
158
- expect(isTemplateNameEmpty).toBe(true);
159
- });
160
-
161
- it('is true when template-name is whitespace only', () => {
162
- const formData = { 'template-name': ' ', 0: { 'sms-editor': 'msg' } };
163
- const { isTemplateNameEmpty } = getSmsEmbeddedFooterValidity(formData, 1);
164
- expect(isTemplateNameEmpty).toBe(true);
165
- });
166
-
167
- it('is false when template-name is a non-empty string', () => {
168
- const formData = { 'template-name': 'My Template', 0: { 'sms-editor': 'msg' } };
169
- const { isTemplateNameEmpty } = getSmsEmbeddedFooterValidity(formData, 1);
170
- expect(isTemplateNameEmpty).toBe(false);
171
- });
172
- });
173
-
174
- describe('isMessageEmpty — single tab', () => {
175
- it('is true when message is empty string', () => {
176
- const formData = { 'template-name': 'T', 0: { 'sms-editor': '' } };
177
- const { isMessageEmpty } = getSmsEmbeddedFooterValidity(formData, 1);
178
- expect(isMessageEmpty).toBe(true);
179
- });
180
-
181
- it('is true when message is whitespace only', () => {
182
- const formData = { 'template-name': 'T', 0: { 'sms-editor': ' ' } };
183
- const { isMessageEmpty } = getSmsEmbeddedFooterValidity(formData, 1);
184
- expect(isMessageEmpty).toBe(true);
185
- });
186
-
187
- it('is true when no sms-editor key at all', () => {
188
- const formData = { 'template-name': 'T', 0: {} };
189
- const { isMessageEmpty } = getSmsEmbeddedFooterValidity(formData, 1);
190
- expect(isMessageEmpty).toBe(true);
191
- });
192
-
193
- it('is false when message is non-empty', () => {
194
- const formData = { 'template-name': 'T', 0: { 'sms-editor': 'Hello world' } };
195
- const { isMessageEmpty } = getSmsEmbeddedFooterValidity(formData, 1);
196
- expect(isMessageEmpty).toBe(false);
197
- });
198
- });
199
-
200
- describe('isMessageEmpty — multiple tabs', () => {
201
- it('is false when all tabs have non-empty messages', () => {
202
- const formData = {
203
- 'template-name': 'T',
204
- 0: { 'sms-editor': 'Tab 1 message' },
205
- 1: { 'sms-editor2': 'Tab 2 message' },
206
- 2: { 'sms-editor3': 'Tab 3 message' },
207
- };
208
- const { isMessageEmpty } = getSmsEmbeddedFooterValidity(formData, 3);
209
- expect(isMessageEmpty).toBe(false);
210
- });
211
-
212
- it('is true when the second tab has an empty message', () => {
213
- const formData = {
214
- 'template-name': 'T',
215
- 0: { 'sms-editor': 'Tab 1 message' },
216
- 1: { 'sms-editor2': '' },
217
- };
218
- const { isMessageEmpty } = getSmsEmbeddedFooterValidity(formData, 2);
219
- expect(isMessageEmpty).toBe(true);
220
- });
221
-
222
- it('is true when the third tab has no message', () => {
223
- const formData = {
224
- 'template-name': 'T',
225
- 0: { 'sms-editor': 'msg1' },
226
- 1: { 'sms-editor2': 'msg2' },
227
- 2: {},
228
- };
229
- const { isMessageEmpty } = getSmsEmbeddedFooterValidity(formData, 3);
230
- expect(isMessageEmpty).toBe(true);
231
- });
232
-
233
- it('treats tabCount=1 as single-tab check even when not explicitly provided', () => {
234
- const formData = { 'template-name': 'T', 0: { 'sms-editor': 'msg' } };
235
- const { isMessageEmpty } = getSmsEmbeddedFooterValidity(formData);
236
- expect(isMessageEmpty).toBe(false);
237
- });
238
- });
239
-
240
- describe('both fields together', () => {
241
- it('returns both empty when template name absent and message empty', () => {
242
- const formData = { 0: { 'sms-editor': '' } };
243
- const result = getSmsEmbeddedFooterValidity(formData, 1);
244
- expect(result).toEqual({ isTemplateNameEmpty: true, isMessageEmpty: true });
245
- });
246
-
247
- it('returns both non-empty when template name and message are present', () => {
248
- const formData = { 'template-name': 'My Template', 0: { 'sms-editor': 'Hello!' } };
249
- const result = getSmsEmbeddedFooterValidity(formData, 1);
250
- expect(result).toEqual({ isTemplateNameEmpty: false, isMessageEmpty: false });
251
- });
252
- });
253
- });
@@ -1,121 +0,0 @@
1
- @import '~@capillarytech/cap-ui-library/styles/_variables';
2
-
3
- .sms-trai-segmented-editor {
4
- .ant-input {
5
- margin-bottom: 0.125rem;
6
- }
7
- }
8
-
9
- /*
10
- * DLT SMS fallback edit only: mirror RCS rich-card message box (`.cap-rcs-creatives` + default
11
- * VarSegment `rcs-edit-template-message-input` / `rcs-edit-template-message-split`).
12
- */
13
- .sms-trai-edit-rcs-fallback {
14
- width: 100%;
15
-
16
- .rcs_text_area_wrapper {
17
- position: relative;
18
- width: 100%;
19
- }
20
-
21
- .rcs_text_area_wrapper .rcs-edit-template-message-input {
22
- background-color: $CAP_G10;
23
- padding: $CAP_SPACE_12 $CAP_SPACE_16;
24
- }
25
-
26
- .rcs_text_area_wrapper .rcs-edit-template-message-split {
27
- margin-bottom: $CAP_SPACE_08;
28
- overflow: hidden;
29
- text-overflow: ellipsis;
30
- color: $FONT_COLOR_03;
31
- font-weight: 500;
32
- }
33
-
34
- .rcs_text_area_wrapper .rcs-edit-template-message-input .ant-input,
35
- .rcs_text_area_wrapper .rcs-edit-template-message-input textarea.ant-input {
36
- width: 100%;
37
- box-sizing: border-box;
38
- min-height: 2.5rem;
39
- padding-top: $CAP_SPACE_08;
40
- padding-bottom: $CAP_SPACE_08;
41
- border-color: $CAP_G07;
42
- box-shadow: none;
43
- overflow: hidden;
44
- }
45
-
46
- .rcs_text_area_wrapper .rcs-edit-template-message-input .ant-input:focus,
47
- .rcs_text_area_wrapper .rcs-edit-template-message-input .ant-input:active,
48
- .rcs_text_area_wrapper .rcs-edit-template-message-input textarea.ant-input:focus,
49
- .rcs_text_area_wrapper .rcs-edit-template-message-input textarea.ant-input:active {
50
- border-color: $CAP_G07;
51
- box-shadow: none;
52
- outline: none;
53
- }
54
-
55
- /*
56
- * DLT per-slot hint: long selector (under CapSpin + message row) for specificity without !important.
57
- */
58
- .rcs_text_area_wrapper .rcs-edit-template-message-input .var-segment-message-editor__var-slot {
59
- display: flex;
60
- flex-direction: column;
61
- align-items: stretch;
62
- }
63
-
64
- .rcs_text_area_wrapper
65
- .rcs-edit-template-message-input
66
- .var-segment-message-editor__var-slot
67
- span.sms-trai-rcs-fallback-var-hint {
68
- display: block;
69
- box-sizing: border-box;
70
- flex-shrink: 0;
71
- margin-top: $CAP_SPACE_04;
72
- margin-left: auto;
73
- margin-right: 0;
74
- width: fit-content;
75
- max-width: 100%;
76
- text-align: right;
77
- color: $FONT_COLOR_02;
78
- font-weight: 400;
79
- font-size: 0.75rem;
80
- line-height: 1rem;
81
- opacity: 0.92;
82
- }
83
-
84
- /* Footer “1 SMS (n characters)” — match RCS message typography, not muted gray */
85
- .rcs-character-count,
86
- .rcs-character-count--compact {
87
- color: $FONT_COLOR_03;
88
- font-weight: 500;
89
- }
90
- }
91
-
92
- .rcs-character-count {
93
- display: block;
94
- margin-top: $CAP_SPACE_16;
95
- margin-bottom: $CAP_SPACE_04;
96
- text-align: right;
97
- color: $FONT_COLOR_02;
98
- width: 100%;
99
- }
100
-
101
- .rcs-character-count--compact {
102
- font-size: 0.75rem;
103
- line-height: 1rem;
104
- font-weight: 400;
105
- }
106
-
107
- /* Non–RCS fallback: segmented editor shell + character count row (was inline styles on CapRow) */
108
- .sms-trai-editor-segment-row {
109
- background-color: $CAP_G10;
110
- padding: $CAP_SPACE_16;
111
- }
112
-
113
- .sms-trai-length-row {
114
- display: flex;
115
- justify-content: flex-end;
116
- margin-top: $CAP_SPACE_04;
117
- }
118
-
119
- .sms-trai-edit-bottom-spacer {
120
- margin-bottom: 6.25rem;
121
- }
@@ -1,101 +0,0 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
- import CapInput from '@capillarytech/cap-ui-library/CapInput';
4
- import CapButton from '@capillarytech/cap-ui-library/CapButton';
5
-
6
- /**
7
- * Reusable action bar for template pickers.
8
- * Layout and spacing live in `_templates.scss` (`.action-container`, `.action-container__toolbar-row`).
9
- * Default search field width: `.action-container__toolbar-row .search-text` (13.125rem).
10
- * Pass `searchInputClassName` / `searchInputStyle` to override (defaults match Templates list: 210px per master).
11
- */
12
- const TemplatesActionBar = ({
13
- searchValue,
14
- onSearchChange,
15
- onSearch,
16
- onClear,
17
- searchPlaceholder,
18
- searchInputClassName,
19
- searchInputStyle,
20
- ctaLabel,
21
- onCtaClick,
22
- ctaDisabled,
23
- ctaClassName,
24
- ctaNode,
25
- children,
26
- showCta = true,
27
- }) => (
28
- <div className="action-container">
29
- <div className="action-container__toolbar-row">
30
- {searchPlaceholder && (
31
- <CapInput.Search
32
- className={['search-text', searchInputClassName].filter(Boolean).join(' ')}
33
- placeholder={searchPlaceholder}
34
- value={searchValue}
35
- onChange={onSearchChange}
36
- /* antd `Input` (used by CapInput.Search) has no `onSearch`; only `Input.Search` does. */
37
- onPressEnter={(e) => {
38
- if (onSearch) {
39
- const v = e?.target && 'value' in e.target ? e.target.value : searchValue;
40
- onSearch(v);
41
- }
42
- }}
43
- onSearch={onSearch}
44
- onClear={onClear}
45
- onScroll={(e) => e.stopPropagation()}
46
- />
47
- )}
48
- {children}
49
- </div>
50
- {showCta && (
51
- <div>
52
- {ctaNode || (
53
- <CapButton
54
- className={ctaClassName}
55
- type="primary"
56
- disabled={ctaDisabled}
57
- onClick={onCtaClick}
58
- >
59
- {ctaLabel}
60
- </CapButton>
61
- )}
62
- </div>
63
- )}
64
- </div>
65
- );
66
-
67
- TemplatesActionBar.propTypes = {
68
- searchValue: PropTypes.string,
69
- onSearchChange: PropTypes.func,
70
- onSearch: PropTypes.func,
71
- onClear: PropTypes.func,
72
- searchPlaceholder: PropTypes.string,
73
- searchInputClassName: PropTypes.string,
74
- searchInputStyle: PropTypes.object,
75
- ctaLabel: PropTypes.node,
76
- onCtaClick: PropTypes.func,
77
- ctaDisabled: PropTypes.bool,
78
- ctaClassName: PropTypes.string,
79
- ctaNode: PropTypes.node,
80
- children: PropTypes.node,
81
- showCta: PropTypes.bool,
82
- };
83
-
84
- TemplatesActionBar.defaultProps = {
85
- searchValue: '',
86
- onSearchChange: () => {},
87
- onSearch: () => {},
88
- onClear: () => {},
89
- searchPlaceholder: '',
90
- searchInputClassName: '',
91
- searchInputStyle: { width: '210px' },
92
- ctaLabel: null,
93
- onCtaClick: () => {},
94
- ctaDisabled: false,
95
- ctaClassName: '',
96
- ctaNode: null,
97
- children: null,
98
- };
99
-
100
- export default TemplatesActionBar;
101
-
@@ -1,120 +0,0 @@
1
- /**
2
- * @jest-environment jsdom
3
- */
4
- import React from 'react';
5
- import { render, screen, fireEvent } from '@testing-library/react';
6
- import '@testing-library/jest-dom';
7
- import TemplatesActionBar from '../TemplatesActionBar';
8
-
9
- jest.mock('@capillarytech/cap-ui-library/CapInput', () => {
10
- const React = require('react');
11
- function Search(props) {
12
- return React.createElement('input', {
13
- 'data-testid': 'cap-input-search',
14
- value: props?.value,
15
- placeholder: props?.placeholder,
16
- onChange: props?.onChange,
17
- onKeyDown: (e) => {
18
- if (e.key === 'Enter' && props.onPressEnter) {
19
- props.onPressEnter(e);
20
- }
21
- },
22
- });
23
- }
24
- function CapInput() {
25
- return null;
26
- }
27
- CapInput.Search = Search;
28
- return { __esModule: true, default: CapInput };
29
- });
30
-
31
- jest.mock('@capillarytech/cap-ui-library/CapButton', () => {
32
- const React = require('react');
33
- return function CapButton(props) {
34
- return React.createElement('button', {
35
- type: 'button',
36
- 'data-testid': 'cta',
37
- onClick: props?.onClick,
38
- disabled: props?.disabled,
39
- }, props?.children);
40
- };
41
- });
42
-
43
- describe('TemplatesActionBar', () => {
44
- it('renders search when searchPlaceholder is set', () => {
45
- render(
46
- <TemplatesActionBar
47
- searchPlaceholder="Find templates"
48
- searchValue="hi"
49
- ctaLabel="Create"
50
- />,
51
- );
52
- expect(screen.getByTestId('cap-input-search')).toHaveAttribute('placeholder', 'Find templates');
53
- });
54
-
55
- it('omits search when searchPlaceholder is empty', () => {
56
- const { container } = render(
57
- <TemplatesActionBar searchPlaceholder="" ctaLabel="Go" />,
58
- );
59
- expect(container.querySelector('[data-testid="cap-input-search"]')).toBeNull();
60
- });
61
-
62
- it('fires onSearchChange and onCtaClick', () => {
63
- const onSearchChange = jest.fn();
64
- const onCtaClick = jest.fn();
65
- render(
66
- <TemplatesActionBar
67
- searchPlaceholder="S"
68
- onSearchChange={onSearchChange}
69
- ctaLabel="New"
70
- onCtaClick={onCtaClick}
71
- />,
72
- );
73
- fireEvent.change(screen.getByTestId('cap-input-search'), { target: { value: 'x' } });
74
- fireEvent.click(screen.getByTestId('cta'));
75
- expect(onSearchChange).toHaveBeenCalled();
76
- expect(onCtaClick).toHaveBeenCalled();
77
- });
78
-
79
- it('fires onSearch when Enter is pressed (antd Input has no native onSearch)', () => {
80
- const onSearch = jest.fn();
81
- render(
82
- <TemplatesActionBar
83
- searchPlaceholder="S"
84
- searchValue="query"
85
- onSearch={onSearch}
86
- ctaLabel="New"
87
- />,
88
- );
89
- const input = screen.getByTestId('cap-input-search');
90
- fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' });
91
- expect(onSearch).toHaveBeenCalledWith('query');
92
- });
93
-
94
- it('renders ctaNode instead of default button when provided', () => {
95
- render(
96
- <TemplatesActionBar
97
- searchPlaceholder="S"
98
- ctaNode={<span data-testid="custom-cta">Custom</span>}
99
- />,
100
- );
101
- expect(screen.getByTestId('custom-cta')).toBeInTheDocument();
102
- expect(screen.queryByTestId('cta')).toBeNull();
103
- });
104
-
105
- it('hides CTA area when showCta is false', () => {
106
- const { container } = render(
107
- <TemplatesActionBar searchPlaceholder="S" showCta={false} ctaLabel="X" />,
108
- );
109
- expect(container.querySelector('[data-testid="cta"]')).toBeNull();
110
- });
111
-
112
- it('renders children in toolbar row', () => {
113
- render(
114
- <TemplatesActionBar searchPlaceholder="S" ctaLabel="C">
115
- <span data-testid="child">extra</span>
116
- </TemplatesActionBar>,
117
- );
118
- expect(screen.getByTestId('child')).toBeInTheDocument();
119
- });
120
- });