cap-creatives-ui 8.0.280 → 8.0.321

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 (247) hide show
  1. package/.github/workflows/pr-title-check.yml +88 -0
  2. package/app/constants/unified.js +21 -1
  3. package/app/containers/App/constants.js +0 -1
  4. package/app/containers/Login/test/index.test.js +123 -0
  5. package/app/containers/Login/test/selectors.test.js +165 -0
  6. package/app/initialState.js +0 -2
  7. package/app/services/api.js +6 -0
  8. package/app/services/tests/api.test.js +7 -0
  9. package/app/services/tests/getSchema.test.js +95 -0
  10. package/app/utils/common.js +23 -9
  11. package/app/utils/commonUtils.js +64 -93
  12. package/app/utils/tagValidations.js +83 -219
  13. package/app/utils/templateVarUtils.js +172 -0
  14. package/app/utils/tests/common.test.js +265 -323
  15. package/app/utils/tests/commonUtil.test.js +461 -118
  16. package/app/utils/tests/commonUtils.test.js +581 -0
  17. package/app/utils/tests/messageUtils.test.js +95 -0
  18. package/app/utils/tests/smsCharCount.test.js +304 -0
  19. package/app/utils/tests/smsCharCountV2.test.js +213 -10
  20. package/app/utils/tests/tagValidations.test.js +474 -357
  21. package/app/utils/tests/templateVarUtils.test.js +160 -0
  22. package/app/v2Components/CapDeviceContent/index.js +10 -7
  23. package/app/v2Components/CapTagList/index.js +32 -24
  24. package/app/v2Components/CapTagList/style.scss +48 -0
  25. package/app/v2Components/CapTagListWithInput/__tests__/CapTagListWithInput.test.js +63 -0
  26. package/app/v2Components/CapTagListWithInput/index.js +8 -0
  27. package/app/v2Components/CapWhatsappCTA/index.js +2 -0
  28. package/app/v2Components/CapWhatsappCarouselButton/index.js +32 -14
  29. package/app/v2Components/CapWhatsappCarouselButton/tests/index.test.js +120 -2
  30. package/app/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -49
  31. package/app/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +39 -0
  32. package/app/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +606 -0
  33. package/app/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.scss +36 -0
  34. package/app/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +79 -0
  35. package/app/v2Components/CommonTestAndPreview/DeliverySettings/index.js +314 -0
  36. package/app/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +141 -0
  37. package/app/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +156 -0
  38. package/app/v2Components/CommonTestAndPreview/SendTestMessage.js +57 -1
  39. package/app/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +20 -1
  40. package/app/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +133 -4
  41. package/app/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +210 -4
  42. package/app/v2Components/CommonTestAndPreview/actions.js +20 -0
  43. package/app/v2Components/CommonTestAndPreview/constants.js +57 -1
  44. package/app/v2Components/CommonTestAndPreview/index.js +878 -156
  45. package/app/v2Components/CommonTestAndPreview/messages.js +41 -3
  46. package/app/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
  47. package/app/v2Components/CommonTestAndPreview/reducer.js +47 -0
  48. package/app/v2Components/CommonTestAndPreview/sagas.js +75 -5
  49. package/app/v2Components/CommonTestAndPreview/selectors.js +51 -0
  50. package/app/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +352 -0
  51. package/app/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +1156 -0
  52. package/app/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +334 -0
  53. package/app/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +576 -0
  54. package/app/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +156 -0
  55. package/app/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
  56. package/app/v2Components/CommonTestAndPreview/tests/actions.test.js +50 -0
  57. package/app/v2Components/CommonTestAndPreview/tests/constants.test.js +18 -7
  58. package/app/v2Components/CommonTestAndPreview/tests/index.test.js +914 -5
  59. package/app/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
  60. package/app/v2Components/CommonTestAndPreview/tests/reducer.test.js +118 -0
  61. package/app/v2Components/CommonTestAndPreview/tests/sagas.test.js +146 -378
  62. package/app/v2Components/CommonTestAndPreview/tests/selectors.test.js +146 -0
  63. package/app/v2Components/ErrorInfoNote/index.js +24 -26
  64. package/app/v2Components/FormBuilder/index.js +182 -204
  65. package/app/v2Components/FormBuilder/messages.js +4 -8
  66. package/app/v2Components/HtmlEditor/HTMLEditor.js +7 -6
  67. package/app/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -1
  68. package/app/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +928 -17
  69. package/app/v2Components/HtmlEditor/components/CodeEditorPane/index.js +4 -2
  70. package/app/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +452 -3
  71. package/app/v2Components/HtmlEditor/hooks/useValidation.js +12 -9
  72. package/app/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +132 -0
  73. package/app/v2Components/HtmlEditor/utils/htmlValidator.js +4 -2
  74. package/app/v2Components/SmsFallback/SmsFallbackLocalSelector.js +87 -0
  75. package/app/v2Components/SmsFallback/constants.js +73 -0
  76. package/app/v2Components/SmsFallback/index.js +956 -0
  77. package/app/v2Components/SmsFallback/index.scss +265 -0
  78. package/app/v2Components/SmsFallback/messages.js +78 -0
  79. package/app/v2Components/SmsFallback/smsFallbackUtils.js +107 -0
  80. package/app/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
  81. package/app/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
  82. package/app/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
  83. package/app/v2Components/SmsFallback/tests/smsFallbackUi.test.js +197 -0
  84. package/app/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +261 -0
  85. package/app/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
  86. package/app/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
  87. package/app/v2Components/TestAndPreviewSlidebox/index.js +22 -1
  88. package/app/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
  89. package/app/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
  90. package/app/v2Components/VarSegmentMessageEditor/constants.js +2 -0
  91. package/app/v2Components/VarSegmentMessageEditor/index.js +125 -0
  92. package/app/v2Components/VarSegmentMessageEditor/index.scss +46 -0
  93. package/app/v2Containers/BeeEditor/index.js +3 -0
  94. package/app/v2Containers/BeePopupEditor/index.js +9 -2
  95. package/app/v2Containers/Cap/mockData.js +0 -14
  96. package/app/v2Containers/Cap/reducer.js +3 -55
  97. package/app/v2Containers/Cap/tests/reducer.test.js +0 -102
  98. package/app/v2Containers/CommunicationFlow/CommunicationFlow.js +291 -0
  99. package/app/v2Containers/CommunicationFlow/CommunicationFlow.scss +25 -0
  100. package/app/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +255 -0
  101. package/app/v2Containers/CommunicationFlow/constants.js +200 -0
  102. package/app/v2Containers/CommunicationFlow/index.js +102 -0
  103. package/app/v2Containers/CommunicationFlow/messages.js +346 -0
  104. package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.js +522 -0
  105. package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.scss +170 -0
  106. package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +796 -0
  107. package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/index.js +5 -0
  108. package/app/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/CommunicationStrategyStep.js +95 -0
  109. package/app/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/Tests/CommunicationStrategyStep.test.js +133 -0
  110. package/app/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/index.js +5 -0
  111. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.js +289 -0
  112. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.scss +70 -0
  113. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.js +319 -0
  114. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.scss +69 -0
  115. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +616 -0
  116. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +577 -0
  117. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/deliverySettingsConfig.test.js +1111 -0
  118. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/deliverySettingsConfig.js +696 -0
  119. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/index.js +7 -0
  120. package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.js +102 -0
  121. package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.scss +36 -0
  122. package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/Tests/DynamicControlsStep.test.js +91 -0
  123. package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/index.js +5 -0
  124. package/app/v2Containers/CommunicationFlow/steps/MessageTypeStep/MessageTypeStep.js +86 -0
  125. package/app/v2Containers/CommunicationFlow/steps/MessageTypeStep/Tests/MessageTypeStep.test.js +100 -0
  126. package/app/v2Containers/CommunicationFlow/steps/MessageTypeStep/index.js +5 -0
  127. package/app/v2Containers/CommunicationFlow/utils/getEnabledSteps.js +30 -0
  128. package/app/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +43 -0
  129. package/app/v2Containers/CreativesContainer/SlideBoxContent.js +127 -11
  130. package/app/v2Containers/CreativesContainer/SlideBoxFooter.js +62 -9
  131. package/app/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
  132. package/app/v2Containers/CreativesContainer/constants.js +24 -0
  133. package/app/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +67 -0
  134. package/app/v2Containers/CreativesContainer/index.js +346 -71
  135. package/app/v2Containers/CreativesContainer/index.scss +51 -1
  136. package/app/v2Containers/CreativesContainer/messages.js +12 -0
  137. package/app/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
  138. package/app/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +69 -1
  139. package/app/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +443 -0
  140. package/app/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +110 -0
  141. package/app/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +147 -4
  142. package/app/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +363 -0
  143. package/app/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +57 -10
  144. package/app/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
  145. package/app/v2Containers/CreativesContainer/tests/index.test.js +71 -9
  146. package/app/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
  147. package/app/v2Containers/Email/index.js +2 -5
  148. package/app/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +58 -77
  149. package/app/v2Containers/EmailWrapper/components/EmailWrapperView.js +3 -0
  150. package/app/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +158 -89
  151. package/app/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +16 -1
  152. package/app/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +17 -12
  153. package/app/v2Containers/EmailWrapper/index.js +4 -0
  154. package/app/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +1 -0
  155. package/app/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +133 -0
  156. package/app/v2Containers/FTP/index.js +2 -51
  157. package/app/v2Containers/FTP/messages.js +0 -4
  158. package/app/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +110 -155
  159. package/app/v2Containers/InApp/index.js +297 -118
  160. package/app/v2Containers/InApp/tests/index.test.js +17 -6
  161. package/app/v2Containers/InApp/tests/mockData.js +1 -1
  162. package/app/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +19 -0
  163. package/app/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +3 -0
  164. package/app/v2Containers/InAppWrapper/index.js +3 -0
  165. package/app/v2Containers/InappAdvance/index.js +5 -104
  166. package/app/v2Containers/InappAdvance/tests/index.test.js +2 -0
  167. package/app/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +24 -3
  168. package/app/v2Containers/Line/Container/Text/index.js +0 -1
  169. package/app/v2Containers/MobilePush/Create/index.js +105 -28
  170. package/app/v2Containers/MobilePush/Create/messages.js +4 -0
  171. package/app/v2Containers/MobilePush/Edit/index.js +250 -68
  172. package/app/v2Containers/MobilePush/Edit/messages.js +4 -0
  173. package/app/v2Containers/MobilePushNew/components/PlatformContentFields.js +36 -12
  174. package/app/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +68 -27
  175. package/app/v2Containers/MobilePushNew/index.js +78 -35
  176. package/app/v2Containers/MobilePushNew/messages.js +8 -0
  177. package/app/v2Containers/MobilepushWrapper/index.js +11 -1
  178. package/app/v2Containers/Rcs/constants.js +32 -1
  179. package/app/v2Containers/Rcs/index.js +963 -916
  180. package/app/v2Containers/Rcs/index.scss +85 -6
  181. package/app/v2Containers/Rcs/messages.js +10 -1
  182. package/app/v2Containers/Rcs/rcsLibraryHydrationUtils.js +205 -0
  183. package/app/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +41136 -1566
  184. package/app/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
  185. package/app/v2Containers/Rcs/tests/index.test.js +41 -38
  186. package/app/v2Containers/Rcs/tests/mockData.js +38 -0
  187. package/app/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +251 -0
  188. package/app/v2Containers/Rcs/tests/utils.test.js +379 -1
  189. package/app/v2Containers/Rcs/utils.js +358 -10
  190. package/app/v2Containers/Sms/Create/index.js +122 -39
  191. package/app/v2Containers/Sms/Create/messages.js +4 -0
  192. package/app/v2Containers/Sms/Edit/index.js +37 -3
  193. package/app/v2Containers/Sms/commonMethods.js +3 -6
  194. package/app/v2Containers/Sms/smsFormDataHelpers.js +67 -0
  195. package/app/v2Containers/Sms/tests/commonMethods.test.js +122 -0
  196. package/app/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  197. package/app/v2Containers/SmsTrai/Create/index.js +9 -4
  198. package/app/v2Containers/SmsTrai/Create/index.scss +1 -1
  199. package/app/v2Containers/SmsTrai/Edit/constants.js +2 -0
  200. package/app/v2Containers/SmsTrai/Edit/index.js +667 -160
  201. package/app/v2Containers/SmsTrai/Edit/index.scss +121 -0
  202. package/app/v2Containers/SmsTrai/Edit/messages.js +9 -4
  203. package/app/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4590 -2436
  204. package/app/v2Containers/SmsWrapper/index.js +41 -8
  205. package/app/v2Containers/TagList/index.js +63 -2
  206. package/app/v2Containers/TagList/messages.js +8 -0
  207. package/app/v2Containers/TagList/tests/TagList.test.js +122 -20
  208. package/app/v2Containers/TagList/tests/mockdata.js +17 -0
  209. package/app/v2Containers/Templates/TemplatesActionBar.js +101 -0
  210. package/app/v2Containers/Templates/_templates.scss +61 -2
  211. package/app/v2Containers/Templates/actions.js +11 -0
  212. package/app/v2Containers/Templates/constants.js +2 -0
  213. package/app/v2Containers/Templates/index.js +90 -40
  214. package/app/v2Containers/Templates/reducer.js +3 -1
  215. package/app/v2Containers/Templates/sagas.js +57 -12
  216. package/app/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
  217. package/app/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1043 -1079
  218. package/app/v2Containers/Templates/tests/reducer.test.js +12 -0
  219. package/app/v2Containers/Templates/tests/sagas.test.js +193 -12
  220. package/app/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
  221. package/app/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
  222. package/app/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
  223. package/app/v2Containers/TemplatesV2/index.js +147 -49
  224. package/app/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
  225. package/app/v2Containers/Viber/index.js +9 -10
  226. package/app/v2Containers/Viber/index.scss +1 -1
  227. package/app/v2Containers/WebPush/Create/components/BrandIconSection.test.js +264 -0
  228. package/app/v2Containers/WebPush/Create/components/MessageSection.js +78 -19
  229. package/app/v2Containers/WebPush/Create/components/MessageSection.test.js +82 -0
  230. package/app/v2Containers/WebPush/Create/components/__snapshots__/BrandIconSection.test.js.snap +187 -0
  231. package/app/v2Containers/WebPush/Create/components/__snapshots__/MessageSection.test.js.snap +25 -17
  232. package/app/v2Containers/WebPush/Create/hooks/useAiraTriggerPosition.js +80 -0
  233. package/app/v2Containers/WebPush/Create/hooks/useAiraTriggerPosition.test.js +210 -0
  234. package/app/v2Containers/WebPush/Create/hooks/useTagManagement.js +1 -5
  235. package/app/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -7
  236. package/app/v2Containers/WebPush/Create/index.js +36 -6
  237. package/app/v2Containers/WebPush/Create/index.scss +5 -0
  238. package/app/v2Containers/WebPush/Create/messages.js +8 -1
  239. package/app/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +269 -0
  240. package/app/v2Containers/WebPush/Create/utils/validation.js +31 -15
  241. package/app/v2Containers/WebPush/Create/utils/validation.test.js +72 -24
  242. package/app/v2Containers/Whatsapp/index.js +28 -53
  243. package/app/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +26939 -3982
  244. package/app/v2Containers/Whatsapp/tests/index.test.js +172 -0
  245. package/app/v2Containers/Zalo/index.js +5 -11
  246. package/package.json +2 -2
  247. package/version +9 -0
@@ -0,0 +1,264 @@
1
+ import React from 'react';
2
+ import CapHeading from '@capillarytech/cap-ui-library/CapHeading';
3
+ import CapRadioGroup from '@capillarytech/cap-ui-library/CapRadioGroup';
4
+ import CapDivider from '@capillarytech/cap-ui-library/CapDivider';
5
+ import { mountWithIntl, shallowWithIntl } from '../../../../helpers/intl-enzym-test-helpers';
6
+ import { BrandIconSection } from './BrandIconSection';
7
+ import CapImageUpload from '../../../../v2Components/CapImageUpload';
8
+ import { BRAND_ICON_OPTIONS } from '../../constants';
9
+
10
+ describe('BrandIconSection', () => {
11
+ const mockFormatMessage = jest.fn((msg) => msg?.defaultMessage || msg?.id || '');
12
+
13
+ const mockMessages = {
14
+ brandIconLogo: { id: 'app.webpush.brandIconLogo', defaultMessage: 'Brand Icon/Logo' },
15
+ dontShow: { id: 'app.webpush.dontShow', defaultMessage: "Don't show" },
16
+ uploadImage: { id: 'app.webpush.uploadImage', defaultMessage: 'Upload image' },
17
+ };
18
+
19
+ const mockBrandIconUpload = {
20
+ imageSrc: '',
21
+ uploadAsset: jest.fn(),
22
+ setUpdateImageSrc: jest.fn(),
23
+ updateOnReUpload: jest.fn(),
24
+ };
25
+
26
+ const defaultProps = {
27
+ brandIconOption: BRAND_ICON_OPTIONS.DONT_SHOW,
28
+ onBrandIconChange: jest.fn(),
29
+ brandIconUpload: mockBrandIconUpload,
30
+ isLocked: false,
31
+ isAnyUploadActive: false,
32
+ formatMessage: mockFormatMessage,
33
+ messages: mockMessages,
34
+ webPush: {},
35
+ isFullMode: true,
36
+ };
37
+
38
+ beforeEach(() => {
39
+ jest.clearAllMocks();
40
+ });
41
+
42
+ describe('brandIconOptions array', () => {
43
+ it('builds exactly two options: DONT_SHOW and UPLOAD_IMAGE', () => {
44
+ const wrapper = mountWithIntl(<BrandIconSection {...defaultProps} />);
45
+ const radioGroup = wrapper.find(CapRadioGroup);
46
+ const options = radioGroup.prop('options');
47
+ expect(options).toHaveLength(2);
48
+ expect(options[0].value).toBe(BRAND_ICON_OPTIONS.DONT_SHOW);
49
+ expect(options[1].value).toBe(BRAND_ICON_OPTIONS.UPLOAD_IMAGE);
50
+ });
51
+
52
+ it('does NOT include ADD_IMAGE_URL option (commented-out feature)', () => {
53
+ const wrapper = mountWithIntl(<BrandIconSection {...defaultProps} />);
54
+ const options = wrapper.find(CapRadioGroup).prop('options');
55
+ const hasAddImageUrl = options.some((opt) => opt.value === BRAND_ICON_OPTIONS.ADD_IMAGE_URL);
56
+ expect(hasAddImageUrl).toBe(false);
57
+ });
58
+
59
+ it('calls formatMessage for each option label', () => {
60
+ mountWithIntl(<BrandIconSection {...defaultProps} />);
61
+ expect(mockFormatMessage).toHaveBeenCalledWith(mockMessages.dontShow);
62
+ expect(mockFormatMessage).toHaveBeenCalledWith(mockMessages.uploadImage);
63
+ });
64
+ });
65
+
66
+ describe('CapRadioGroup rendering', () => {
67
+ it('passes brandIconOption as value to CapRadioGroup', () => {
68
+ const wrapper = mountWithIntl(
69
+ <BrandIconSection {...defaultProps} brandIconOption={BRAND_ICON_OPTIONS.UPLOAD_IMAGE} />
70
+ );
71
+ expect(wrapper.find(CapRadioGroup).prop('value')).toBe(BRAND_ICON_OPTIONS.UPLOAD_IMAGE);
72
+ });
73
+
74
+ it('passes onBrandIconChange as onChange to CapRadioGroup', () => {
75
+ const onBrandIconChange = jest.fn();
76
+ const wrapper = mountWithIntl(
77
+ <BrandIconSection {...defaultProps} onBrandIconChange={onBrandIconChange} />
78
+ );
79
+ expect(wrapper.find(CapRadioGroup).prop('onChange')).toBe(onBrandIconChange);
80
+ });
81
+
82
+ it('disables CapRadioGroup when isAnyUploadActive is true', () => {
83
+ const wrapper = mountWithIntl(
84
+ <BrandIconSection {...defaultProps} isAnyUploadActive={true} />
85
+ );
86
+ expect(wrapper.find(CapRadioGroup).prop('disabled')).toBe(true);
87
+ });
88
+
89
+ it('does not disable CapRadioGroup when isAnyUploadActive is false', () => {
90
+ const wrapper = mountWithIntl(
91
+ <BrandIconSection {...defaultProps} isAnyUploadActive={false} />
92
+ );
93
+ expect(wrapper.find(CapRadioGroup).prop('disabled')).toBe(false);
94
+ });
95
+ });
96
+
97
+ describe('conditional upload section (brandIconOption === UPLOAD_IMAGE)', () => {
98
+ it('does NOT render CapImageUpload when brandIconOption is DONT_SHOW', () => {
99
+ const wrapper = mountWithIntl(
100
+ <BrandIconSection {...defaultProps} brandIconOption={BRAND_ICON_OPTIONS.DONT_SHOW} />
101
+ );
102
+ expect(wrapper.find(CapImageUpload).exists()).toBe(false);
103
+ });
104
+
105
+ it('renders CapImageUpload when brandIconOption is UPLOAD_IMAGE', () => {
106
+ const wrapper = mountWithIntl(
107
+ <BrandIconSection {...defaultProps} brandIconOption={BRAND_ICON_OPTIONS.UPLOAD_IMAGE} />
108
+ );
109
+ expect(wrapper.find(CapImageUpload).exists()).toBe(true);
110
+ });
111
+
112
+ it('renders the upload section row with correct className', () => {
113
+ const wrapper = mountWithIntl(
114
+ <BrandIconSection {...defaultProps} brandIconOption={BRAND_ICON_OPTIONS.UPLOAD_IMAGE} />
115
+ );
116
+ expect(wrapper.find('.webpush-brand-icon-upload-section').exists()).toBe(true);
117
+ });
118
+
119
+ it('passes brandIconUpload.imageSrc to CapImageUpload as imageSrc', () => {
120
+ const wrapper = mountWithIntl(
121
+ <BrandIconSection
122
+ {...defaultProps}
123
+ brandIconOption={BRAND_ICON_OPTIONS.UPLOAD_IMAGE}
124
+ brandIconUpload={{ ...mockBrandIconUpload, imageSrc: 'https://example.com/icon.png' }}
125
+ />
126
+ );
127
+ expect(wrapper.find(CapImageUpload).prop('imageSrc')).toBe('https://example.com/icon.png');
128
+ });
129
+
130
+ it('passes brandIconUpload.uploadAsset to CapImageUpload as uploadAsset', () => {
131
+ const uploadAsset = jest.fn();
132
+ const wrapper = mountWithIntl(
133
+ <BrandIconSection
134
+ {...defaultProps}
135
+ brandIconOption={BRAND_ICON_OPTIONS.UPLOAD_IMAGE}
136
+ brandIconUpload={{ ...mockBrandIconUpload, uploadAsset }}
137
+ />
138
+ );
139
+ expect(wrapper.find(CapImageUpload).prop('uploadAsset')).toBe(uploadAsset);
140
+ });
141
+
142
+ it('passes isFullMode to CapImageUpload', () => {
143
+ const wrapper = mountWithIntl(
144
+ <BrandIconSection
145
+ {...defaultProps}
146
+ brandIconOption={BRAND_ICON_OPTIONS.UPLOAD_IMAGE}
147
+ isFullMode={false}
148
+ />
149
+ );
150
+ expect(wrapper.find(CapImageUpload).prop('isFullMode')).toBe(false);
151
+ });
152
+ });
153
+
154
+ describe('isLocked behavior', () => {
155
+ it('applies pointer-events: none and opacity: 0.5 to upload row when isLocked is true', () => {
156
+ const wrapper = mountWithIntl(
157
+ <BrandIconSection
158
+ {...defaultProps}
159
+ brandIconOption={BRAND_ICON_OPTIONS.UPLOAD_IMAGE}
160
+ isLocked={true}
161
+ />
162
+ );
163
+ const uploadRow = wrapper.find('.webpush-brand-icon-upload-section').first();
164
+ expect(uploadRow.prop('style')).toEqual({ pointerEvents: 'none', opacity: 0.5 });
165
+ });
166
+
167
+ it('sets aria-disabled on upload row when isLocked is true', () => {
168
+ const wrapper = mountWithIntl(
169
+ <BrandIconSection
170
+ {...defaultProps}
171
+ brandIconOption={BRAND_ICON_OPTIONS.UPLOAD_IMAGE}
172
+ isLocked={true}
173
+ />
174
+ );
175
+ const uploadRow = wrapper.find('.webpush-brand-icon-upload-section').first();
176
+ expect(uploadRow.prop('aria-disabled')).toBe(true);
177
+ });
178
+
179
+ it('does NOT apply lock styles to upload row when isLocked is false', () => {
180
+ const wrapper = mountWithIntl(
181
+ <BrandIconSection
182
+ {...defaultProps}
183
+ brandIconOption={BRAND_ICON_OPTIONS.UPLOAD_IMAGE}
184
+ isLocked={false}
185
+ />
186
+ );
187
+ const uploadRow = wrapper.find('.webpush-brand-icon-upload-section').first();
188
+ expect(uploadRow.prop('style')).toBeUndefined();
189
+ });
190
+
191
+ it('passes disabled=true to CapImageUpload when isLocked is true', () => {
192
+ const wrapper = mountWithIntl(
193
+ <BrandIconSection
194
+ {...defaultProps}
195
+ brandIconOption={BRAND_ICON_OPTIONS.UPLOAD_IMAGE}
196
+ isLocked={true}
197
+ />
198
+ );
199
+ expect(wrapper.find(CapImageUpload).prop('disabled')).toBe(true);
200
+ });
201
+
202
+ it('passes disabled=false to CapImageUpload when isLocked is false', () => {
203
+ const wrapper = mountWithIntl(
204
+ <BrandIconSection
205
+ {...defaultProps}
206
+ brandIconOption={BRAND_ICON_OPTIONS.UPLOAD_IMAGE}
207
+ isLocked={false}
208
+ />
209
+ );
210
+ expect(wrapper.find(CapImageUpload).prop('disabled')).toBe(false);
211
+ });
212
+ });
213
+
214
+ describe('structural rendering', () => {
215
+ it('always renders CapDivider regardless of brandIconOption', () => {
216
+ const wrapperDontShow = mountWithIntl(
217
+ <BrandIconSection {...defaultProps} brandIconOption={BRAND_ICON_OPTIONS.DONT_SHOW} />
218
+ );
219
+ expect(wrapperDontShow.find(CapDivider).exists()).toBe(true);
220
+
221
+ const wrapperUpload = mountWithIntl(
222
+ <BrandIconSection {...defaultProps} brandIconOption={BRAND_ICON_OPTIONS.UPLOAD_IMAGE} />
223
+ );
224
+ expect(wrapperUpload.find(CapDivider).exists()).toBe(true);
225
+ });
226
+
227
+ it('renders the brand icon heading row with className creatives-webpush-brand-icon', () => {
228
+ const wrapper = mountWithIntl(<BrandIconSection {...defaultProps} />);
229
+ expect(wrapper.find('.creatives-webpush-brand-icon').exists()).toBe(true);
230
+ });
231
+
232
+ it('renders CapHeading with type h4', () => {
233
+ const wrapper = mountWithIntl(<BrandIconSection {...defaultProps} />);
234
+ expect(wrapper.find(CapHeading).prop('type')).toBe('h4');
235
+ });
236
+ });
237
+
238
+ describe('snapshot', () => {
239
+ it('matches snapshot for DONT_SHOW state', () => {
240
+ const wrapper = shallowWithIntl(
241
+ <BrandIconSection {...defaultProps} brandIconOption={BRAND_ICON_OPTIONS.DONT_SHOW} />
242
+ );
243
+ expect(wrapper).toMatchSnapshot();
244
+ });
245
+
246
+ it('matches snapshot for UPLOAD_IMAGE state', () => {
247
+ const wrapper = shallowWithIntl(
248
+ <BrandIconSection {...defaultProps} brandIconOption={BRAND_ICON_OPTIONS.UPLOAD_IMAGE} />
249
+ );
250
+ expect(wrapper).toMatchSnapshot();
251
+ });
252
+
253
+ it('matches snapshot for locked UPLOAD_IMAGE state', () => {
254
+ const wrapper = shallowWithIntl(
255
+ <BrandIconSection
256
+ {...defaultProps}
257
+ brandIconOption={BRAND_ICON_OPTIONS.UPLOAD_IMAGE}
258
+ isLocked={true}
259
+ />
260
+ );
261
+ expect(wrapper).toMatchSnapshot();
262
+ });
263
+ });
264
+ });
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useRef } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { FormattedMessage } from 'react-intl';
4
4
  import CapInput from '@capillarytech/cap-ui-library/CapInput';
@@ -8,7 +8,55 @@ import CapError from '@capillarytech/cap-ui-library/CapError';
8
8
  import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
9
9
  import CapEmojiPicker from '@capillarytech/cap-ui-library/CapEmojiPicker';
10
10
  import CapDivider from '@capillarytech/cap-ui-library/CapDivider';
11
+ import CapAskAira from '@capillarytech/cap-ui-library/CapAskAira';
11
12
  import { MESSAGE_MAX_LENGTH, SHOW_CHARACTER_COUNT } from '../../constants';
13
+ import { useAiraTriggerPosition } from '../hooks/useAiraTriggerPosition';
14
+
15
+ const InputWrapper = ({
16
+ children,
17
+ inputRef,
18
+ error,
19
+ value,
20
+ onChange,
21
+ isAiContentBotDisabled,
22
+ }) => {
23
+ const wrapperRef = useRef(null);
24
+ const airaRootStyle = useAiraTriggerPosition({ wrapperRef, inputRef, error });
25
+
26
+ return (
27
+ <div className="webpush-message-input-wrapper" ref={wrapperRef}>
28
+ {children}
29
+ {!isAiContentBotDisabled && (
30
+ <CapAskAira.ContentGenerationBot
31
+ text={value || ''}
32
+ setText={(text) => onChange(text)}
33
+ iconPlacement="float-br"
34
+ iconSize="1.6rem"
35
+ rootStyle={airaRootStyle}
36
+ />
37
+ )}
38
+ </div>
39
+ );
40
+ };
41
+
42
+ InputWrapper.propTypes = {
43
+ children: PropTypes.node.isRequired,
44
+ inputRef: PropTypes.oneOfType([
45
+ PropTypes.shape({ current: PropTypes.any }),
46
+ PropTypes.func,
47
+ ]),
48
+ error: PropTypes.string,
49
+ value: PropTypes.string,
50
+ onChange: PropTypes.func.isRequired,
51
+ isAiContentBotDisabled: PropTypes.bool,
52
+ };
53
+
54
+ InputWrapper.defaultProps = {
55
+ inputRef: null,
56
+ error: '',
57
+ value: '',
58
+ isAiContentBotDisabled: false,
59
+ };
12
60
 
13
61
  /**
14
62
  * MessageSection component - Message textarea with tags, emoji picker, and character count
@@ -23,6 +71,7 @@ export const MessageSection = ({
23
71
  messageCountRef,
24
72
  messageTextAreaRef,
25
73
  handleMessageTextAreaRef,
74
+ isAiContentBotDisabled,
26
75
  }) => {
27
76
  const renderCharacterCount = () => {
28
77
  if (!SHOW_CHARACTER_COUNT) return null;
@@ -50,29 +99,37 @@ export const MessageSection = ({
50
99
  <CapHeading type="h4" className="webpush-message">
51
100
  <FormattedMessage {...messages.message} />
52
101
  </CapHeading>
53
- <CapEmojiPicker.Wrapper
102
+ <InputWrapper
103
+ inputRef={messageTextAreaRef}
104
+ error={error}
54
105
  value={value}
55
106
  onChange={onChange}
56
- textAreaRef={messageTextAreaRef}
107
+ isAiContentBotDisabled={isAiContentBotDisabled}
57
108
  >
58
- <CapInput.TextArea
59
- id="webpush-message-input"
109
+ <CapEmojiPicker.Wrapper
60
110
  value={value}
61
111
  onChange={onChange}
62
- placeholder={formatMessage(messages.messagePlaceholder)}
63
- size="default"
64
- isRequired
65
- autosize={{ minRows: 3, maxRows: 5 }}
66
- setInputRef={handleMessageTextAreaRef}
67
- errorMessage={
68
- error && (
69
- <CapError className="webpush-template-message-error">
70
- {error}
71
- </CapError>
72
- )
73
- }
74
- />
75
- </CapEmojiPicker.Wrapper>
112
+ textAreaRef={messageTextAreaRef}
113
+ >
114
+ <CapInput.TextArea
115
+ id="webpush-message-input"
116
+ value={value}
117
+ onChange={onChange}
118
+ placeholder={formatMessage(messages.messagePlaceholder)}
119
+ size="default"
120
+ isRequired
121
+ autosize={{ minRows: 3, maxRows: 5 }}
122
+ setInputRef={handleMessageTextAreaRef}
123
+ errorMessage={
124
+ error && (
125
+ <CapError className="webpush-template-message-error">
126
+ {error}
127
+ </CapError>
128
+ )
129
+ }
130
+ />
131
+ </CapEmojiPicker.Wrapper>
132
+ </InputWrapper>
76
133
  {renderCharacterCount()}
77
134
  </CapRow>
78
135
  <CapDivider className="webpush-message-divider" />
@@ -90,6 +147,7 @@ MessageSection.propTypes = {
90
147
  messageCountRef: PropTypes.object,
91
148
  messageTextAreaRef: PropTypes.object,
92
149
  handleMessageTextAreaRef: PropTypes.func.isRequired,
150
+ isAiContentBotDisabled: PropTypes.bool,
93
151
  };
94
152
 
95
153
  MessageSection.defaultProps = {
@@ -97,6 +155,7 @@ MessageSection.defaultProps = {
97
155
  tagList: null,
98
156
  messageCountRef: null,
99
157
  messageTextAreaRef: null,
158
+ isAiContentBotDisabled: false,
100
159
  };
101
160
 
102
161
  export default MessageSection;
@@ -8,6 +8,7 @@ import CapError from '@capillarytech/cap-ui-library/CapError';
8
8
  import CapLabel from '@capillarytech/cap-ui-library/CapLabel';
9
9
  import CapEmojiPicker from '@capillarytech/cap-ui-library/CapEmojiPicker';
10
10
  import CapDivider from '@capillarytech/cap-ui-library/CapDivider';
11
+ import CapAskAira from '@capillarytech/cap-ui-library/CapAskAira';
11
12
  import { MESSAGE_MAX_LENGTH, SHOW_CHARACTER_COUNT } from '../../constants';
12
13
 
13
14
  describe('MessageSection', () => {
@@ -45,6 +46,7 @@ describe('MessageSection', () => {
45
46
  messageCountRef: null,
46
47
  messageTextAreaRef: null,
47
48
  handleMessageTextAreaRef: mockHandleMessageTextAreaRef,
49
+ isAiContentBotDisabled: true,
48
50
  };
49
51
 
50
52
  beforeEach(() => {
@@ -76,6 +78,32 @@ describe('MessageSection', () => {
76
78
  expect(emojiPicker.prop('value')).toBe('Test Message');
77
79
  });
78
80
 
81
+ it('should not render aiRA button when feature is disabled', () => {
82
+ const wrapper = mountWithIntl(<MessageSection {...defaultProps} />);
83
+ expect(wrapper.find(CapAskAira.ContentGenerationBot).exists()).toBe(false);
84
+ });
85
+
86
+ it('should render aiRA button when feature is enabled', () => {
87
+ const wrapper = mountWithIntl(
88
+ <MessageSection {...defaultProps} isAiContentBotDisabled={false} />
89
+ );
90
+ const airaBot = wrapper.find(CapAskAira.ContentGenerationBot);
91
+ expect(airaBot.exists()).toBe(true);
92
+ expect(airaBot.prop('text')).toBe('Test Message');
93
+ expect(airaBot.prop('rootStyle')).toMatchObject({
94
+ right: '2.5rem',
95
+ left: 'auto',
96
+ });
97
+ });
98
+
99
+ it('should render aiRA and emoji controls together', () => {
100
+ const wrapper = mountWithIntl(
101
+ <MessageSection {...defaultProps} isAiContentBotDisabled={false} />
102
+ );
103
+ expect(wrapper.find(CapAskAira.ContentGenerationBot).exists()).toBe(true);
104
+ expect(wrapper.find(CapEmojiPicker.Wrapper).exists()).toBe(true);
105
+ });
106
+
79
107
  it('should render CapInput.TextArea with correct props', () => {
80
108
  const wrapper = mountWithIntl(<MessageSection {...defaultProps} />);
81
109
  const textArea = wrapper.find(CapInput.TextArea);
@@ -145,6 +173,23 @@ describe('MessageSection', () => {
145
173
  const textArea = wrapper.find(CapInput.TextArea);
146
174
  expect(textArea.prop('errorMessage')).toBeDefined();
147
175
  });
176
+
177
+ it('should keep aiRA visible and anchored when error is present', () => {
178
+ const errorMessage = 'Message is required';
179
+ const wrapper = mountWithIntl(
180
+ <MessageSection
181
+ {...defaultProps}
182
+ error={errorMessage}
183
+ isAiContentBotDisabled={false}
184
+ />
185
+ );
186
+ const airaBot = wrapper.find(CapAskAira.ContentGenerationBot);
187
+ expect(airaBot.exists()).toBe(true);
188
+ expect(airaBot.prop('rootStyle')).toMatchObject({
189
+ right: '2.5rem',
190
+ left: 'auto',
191
+ });
192
+ });
148
193
  });
149
194
 
150
195
  describe('Character Count', () => {
@@ -216,6 +261,15 @@ describe('MessageSection', () => {
216
261
  emojiPicker.prop('onChange')('New Message');
217
262
  expect(mockOnChange).toHaveBeenCalledWith('New Message');
218
263
  });
264
+
265
+ it('should call onChange when aiRA returns generated text', () => {
266
+ const wrapper = mountWithIntl(
267
+ <MessageSection {...defaultProps} isAiContentBotDisabled={false} />
268
+ );
269
+ const airaBot = wrapper.find(CapAskAira.ContentGenerationBot);
270
+ airaBot.prop('setText')('Generated by aiRA');
271
+ expect(mockOnChange).toHaveBeenCalledWith('Generated by aiRA');
272
+ });
219
273
  });
220
274
 
221
275
  describe('Default Props', () => {
@@ -264,5 +318,33 @@ describe('MessageSection', () => {
264
318
  expect(textArea.prop('value')).toBe(longValue);
265
319
  });
266
320
  });
321
+
322
+ describe('InputWrapper', () => {
323
+ it('should render the wrapper div with correct class', () => {
324
+ const wrapper = mountWithIntl(<MessageSection {...defaultProps} />);
325
+ const inputWrapper = wrapper.find('.webpush-message-input-wrapper');
326
+ expect(inputWrapper.exists()).toBe(true);
327
+ });
328
+
329
+ it('should render children inside InputWrapper', () => {
330
+ const wrapper = mountWithIntl(<MessageSection {...defaultProps} />);
331
+ const inputWrapper = wrapper.find('.webpush-message-input-wrapper');
332
+ expect(inputWrapper.find(CapEmojiPicker.Wrapper).exists()).toBe(true);
333
+ });
334
+
335
+ it('should not render aiRA bot inside InputWrapper when disabled', () => {
336
+ const wrapper = mountWithIntl(<MessageSection {...defaultProps} isAiContentBotDisabled />);
337
+ const inputWrapper = wrapper.find('.webpush-message-input-wrapper');
338
+ expect(inputWrapper.find(CapAskAira.ContentGenerationBot).exists()).toBe(false);
339
+ });
340
+
341
+ it('should render aiRA bot inside InputWrapper when enabled', () => {
342
+ const wrapper = mountWithIntl(
343
+ <MessageSection {...defaultProps} isAiContentBotDisabled={false} />
344
+ );
345
+ const inputWrapper = wrapper.find('.webpush-message-input-wrapper');
346
+ expect(inputWrapper.find(CapAskAira.ContentGenerationBot).exists()).toBe(true);
347
+ });
348
+ });
267
349
  });
268
350