@capillarytech/creatives-library 8.0.242-alpha.1 → 8.0.242-alpha.11

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 (255) hide show
  1. package/assets/Android.png +0 -0
  2. package/assets/iOS.png +0 -0
  3. package/config/app.js +1 -1
  4. package/constants/unified.js +2 -2
  5. package/initialReducer.js +0 -2
  6. package/package.json +1 -1
  7. package/services/api.js +5 -10
  8. package/services/tests/api.test.js +0 -18
  9. package/translations/en.json +4 -3
  10. package/utils/common.js +6 -5
  11. package/utils/commonUtils.js +1 -14
  12. package/utils/imageUrlUpload.js +141 -0
  13. package/utils/tests/commonUtil.test.js +0 -224
  14. package/utils/transformTemplateConfig.js +10 -0
  15. package/v2Components/CapDeviceContent/index.js +56 -61
  16. package/v2Components/CapImageUpload/constants.js +2 -0
  17. package/v2Components/CapImageUpload/index.js +65 -16
  18. package/v2Components/CapImageUpload/index.scss +4 -1
  19. package/v2Components/CapImageUpload/messages.js +5 -1
  20. package/v2Components/CapImageUrlUpload/constants.js +26 -0
  21. package/v2Components/CapImageUrlUpload/index.js +365 -0
  22. package/v2Components/CapImageUrlUpload/index.scss +35 -0
  23. package/v2Components/CapImageUrlUpload/messages.js +47 -0
  24. package/v2Components/CapTagList/index.js +1 -6
  25. package/v2Components/CapTagListWithInput/index.js +1 -5
  26. package/v2Components/CapTagListWithInput/messages.js +1 -1
  27. package/v2Components/CapWhatsappCTA/tests/index.test.js +0 -5
  28. package/v2Components/ErrorInfoNote/index.js +72 -412
  29. package/v2Components/ErrorInfoNote/messages.js +0 -22
  30. package/v2Components/ErrorInfoNote/style.scss +2 -279
  31. package/v2Components/HtmlEditor/HTMLEditor.js +89 -210
  32. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +133 -1132
  33. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +12 -17
  34. package/v2Components/HtmlEditor/_htmlEditor.scss +23 -8
  35. package/v2Components/HtmlEditor/_index.lazy.scss +1 -1
  36. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +101 -13
  37. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +139 -148
  38. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +1 -2
  39. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
  40. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +1 -1
  41. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +0 -1
  42. package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +7 -4
  43. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +45 -35
  44. package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +3 -1
  45. package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
  46. package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +6 -7
  47. package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +6 -3
  48. package/v2Components/HtmlEditor/components/PreviewPane/index.js +11 -10
  49. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  50. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +62 -87
  51. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +31 -49
  52. package/v2Components/HtmlEditor/constants.js +20 -29
  53. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +16 -373
  54. package/v2Components/HtmlEditor/hooks/useEditorContent.js +2 -5
  55. package/v2Components/HtmlEditor/hooks/useInAppContent.js +146 -88
  56. package/v2Components/HtmlEditor/index.js +1 -1
  57. package/v2Components/HtmlEditor/messages.js +85 -95
  58. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +101 -99
  59. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +25 -23
  60. package/v2Components/HtmlEditor/utils/validationAdapter.js +41 -34
  61. package/v2Components/MobilePushPreviewV2/index.js +7 -32
  62. package/v2Components/TemplatePreview/_templatePreview.scss +24 -44
  63. package/v2Components/TemplatePreview/index.js +32 -47
  64. package/v2Components/TemplatePreview/messages.js +0 -4
  65. package/v2Components/TestAndPreviewSlidebox/index.js +25 -31
  66. package/v2Containers/App/constants.js +5 -0
  67. package/v2Containers/BeeEditor/index.js +80 -82
  68. package/v2Containers/Cap/tests/__snapshots__/index.test.js.snap +4 -3
  69. package/v2Containers/CreativesContainer/SlideBoxContent.js +118 -148
  70. package/v2Containers/CreativesContainer/SlideBoxFooter.js +3 -9
  71. package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -2
  72. package/v2Containers/CreativesContainer/constants.js +2 -1
  73. package/v2Containers/CreativesContainer/index.js +41 -173
  74. package/v2Containers/CreativesContainer/messages.js +4 -4
  75. package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +210 -0
  76. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +354 -38
  77. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +0 -36
  78. package/v2Containers/Email/actions.js +0 -7
  79. package/v2Containers/Email/constants.js +1 -5
  80. package/v2Containers/Email/index.js +0 -13
  81. package/v2Containers/Email/messages.js +0 -32
  82. package/v2Containers/Email/reducer.js +1 -12
  83. package/v2Containers/Email/sagas.js +6 -41
  84. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +0 -2
  85. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +7 -193
  86. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +74 -40
  87. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +67 -2
  88. package/v2Containers/EmailWrapper/constants.js +0 -2
  89. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +67 -436
  90. package/v2Containers/EmailWrapper/index.js +23 -99
  91. package/v2Containers/EmailWrapper/messages.js +1 -61
  92. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +1 -26
  93. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +77 -111
  94. package/v2Containers/InApp/actions.js +0 -7
  95. package/v2Containers/InApp/constants.js +4 -20
  96. package/v2Containers/InApp/index.js +357 -800
  97. package/v2Containers/InApp/index.scss +3 -4
  98. package/v2Containers/InApp/messages.js +3 -7
  99. package/v2Containers/InApp/reducer.js +3 -21
  100. package/v2Containers/InApp/sagas.js +9 -29
  101. package/v2Containers/InApp/selectors.js +5 -25
  102. package/v2Containers/InApp/tests/index.test.js +50 -154
  103. package/v2Containers/InApp/tests/reducer.test.js +0 -34
  104. package/v2Containers/InApp/tests/sagas.test.js +9 -61
  105. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +12 -12
  106. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +8 -8
  107. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +100 -77
  108. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +72 -63
  109. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +184 -150
  110. package/v2Containers/SmsTrai/Create/tests/__snapshots__/index.test.js.snap +16 -12
  111. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +32 -28
  112. package/v2Containers/TagList/index.js +1 -67
  113. package/v2Containers/Templates/ChannelTypeIllustration.js +13 -1
  114. package/v2Containers/Templates/_templates.scss +202 -56
  115. package/v2Containers/Templates/actions.js +2 -1
  116. package/v2Containers/Templates/constants.js +1 -0
  117. package/v2Containers/Templates/index.js +278 -128
  118. package/v2Containers/Templates/messages.js +24 -4
  119. package/v2Containers/Templates/reducer.js +2 -0
  120. package/v2Containers/Templates/tests/index.test.js +10 -0
  121. package/v2Containers/TemplatesV2/index.js +8 -1
  122. package/v2Containers/TemplatesV2/messages.js +4 -0
  123. package/v2Containers/WebPush/Create/components/BrandIconSection.js +108 -0
  124. package/v2Containers/WebPush/Create/components/ButtonForm.js +172 -0
  125. package/v2Containers/WebPush/Create/components/ButtonItem.js +101 -0
  126. package/v2Containers/WebPush/Create/components/ButtonList.js +145 -0
  127. package/v2Containers/WebPush/Create/components/ButtonsLinksSection.js +164 -0
  128. package/v2Containers/WebPush/Create/components/ButtonsLinksSection.test.js +463 -0
  129. package/v2Containers/WebPush/Create/components/FormActions.js +54 -0
  130. package/v2Containers/WebPush/Create/components/FormActions.test.js +163 -0
  131. package/v2Containers/WebPush/Create/components/MediaSection.js +142 -0
  132. package/v2Containers/WebPush/Create/components/MediaSection.test.js +341 -0
  133. package/v2Containers/WebPush/Create/components/MessageSection.js +103 -0
  134. package/v2Containers/WebPush/Create/components/MessageSection.test.js +268 -0
  135. package/v2Containers/WebPush/Create/components/NotificationTitleSection.js +87 -0
  136. package/v2Containers/WebPush/Create/components/NotificationTitleSection.test.js +210 -0
  137. package/v2Containers/WebPush/Create/components/TemplateNameSection.js +54 -0
  138. package/v2Containers/WebPush/Create/components/TemplateNameSection.test.js +143 -0
  139. package/v2Containers/WebPush/Create/components/__snapshots__/ButtonsLinksSection.test.js.snap +86 -0
  140. package/v2Containers/WebPush/Create/components/__snapshots__/FormActions.test.js.snap +16 -0
  141. package/v2Containers/WebPush/Create/components/__snapshots__/MediaSection.test.js.snap +41 -0
  142. package/v2Containers/WebPush/Create/components/__snapshots__/MessageSection.test.js.snap +54 -0
  143. package/v2Containers/WebPush/Create/components/__snapshots__/NotificationTitleSection.test.js.snap +37 -0
  144. package/v2Containers/WebPush/Create/components/__snapshots__/TemplateNameSection.test.js.snap +21 -0
  145. package/v2Containers/WebPush/Create/components/_buttons.scss +246 -0
  146. package/v2Containers/WebPush/Create/components/tests/ButtonForm.test.js +554 -0
  147. package/v2Containers/WebPush/Create/components/tests/ButtonItem.test.js +607 -0
  148. package/v2Containers/WebPush/Create/components/tests/ButtonList.test.js +633 -0
  149. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonForm.test.js.snap +666 -0
  150. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonItem.test.js.snap +74 -0
  151. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonList.test.js.snap +78 -0
  152. package/v2Containers/WebPush/Create/hooks/useButtonManagement.js +138 -0
  153. package/v2Containers/WebPush/Create/hooks/useButtonManagement.test.js +406 -0
  154. package/v2Containers/WebPush/Create/hooks/useCharacterCount.js +30 -0
  155. package/v2Containers/WebPush/Create/hooks/useCharacterCount.test.js +151 -0
  156. package/v2Containers/WebPush/Create/hooks/useImageUpload.js +104 -0
  157. package/v2Containers/WebPush/Create/hooks/useImageUpload.test.js +538 -0
  158. package/v2Containers/WebPush/Create/hooks/useTagManagement.js +122 -0
  159. package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +633 -0
  160. package/v2Containers/WebPush/Create/index.js +1056 -0
  161. package/v2Containers/WebPush/Create/index.scss +134 -0
  162. package/v2Containers/WebPush/Create/messages.js +203 -0
  163. package/v2Containers/WebPush/Create/preview/DevicePreviewContent.js +228 -0
  164. package/v2Containers/WebPush/Create/preview/NotificationContainer.js +294 -0
  165. package/v2Containers/WebPush/Create/preview/PreviewContent.js +90 -0
  166. package/v2Containers/WebPush/Create/preview/PreviewControls.js +305 -0
  167. package/v2Containers/WebPush/Create/preview/PreviewDisclaimer.js +23 -0
  168. package/v2Containers/WebPush/Create/preview/WebPushPreview.js +150 -0
  169. package/v2Containers/WebPush/Create/preview/assets/Light.svg +53 -0
  170. package/v2Containers/WebPush/Create/preview/assets/Top.svg +5 -0
  171. package/v2Containers/WebPush/Create/preview/assets/android-arrow-down.svg +9 -0
  172. package/v2Containers/WebPush/Create/preview/assets/android-arrow-up.svg +9 -0
  173. package/v2Containers/WebPush/Create/preview/assets/chrome-icon.png +0 -0
  174. package/v2Containers/WebPush/Create/preview/assets/edge-icon.png +0 -0
  175. package/v2Containers/WebPush/Create/preview/assets/firefox-icon.svg +106 -0
  176. package/v2Containers/WebPush/Create/preview/assets/iOS.svg +26 -0
  177. package/v2Containers/WebPush/Create/preview/assets/macos-arrow-down-icon.svg +9 -0
  178. package/v2Containers/WebPush/Create/preview/assets/macos-triple-dot-icon.svg +9 -0
  179. package/v2Containers/WebPush/Create/preview/assets/opera-icon.svg +18 -0
  180. package/v2Containers/WebPush/Create/preview/assets/safari-icon.svg +29 -0
  181. package/v2Containers/WebPush/Create/preview/assets/windows-close-icon.svg +9 -0
  182. package/v2Containers/WebPush/Create/preview/assets/windows-triple-dot-icon.svg +9 -0
  183. package/v2Containers/WebPush/Create/preview/components/AndroidMobileChromeHeader.js +47 -0
  184. package/v2Containers/WebPush/Create/preview/components/AndroidMobileExpanded.js +141 -0
  185. package/v2Containers/WebPush/Create/preview/components/IOSHeader.js +45 -0
  186. package/v2Containers/WebPush/Create/preview/components/NotificationExpandedContent.js +68 -0
  187. package/v2Containers/WebPush/Create/preview/components/NotificationHeader.js +61 -0
  188. package/v2Containers/WebPush/Create/preview/components/WindowsChromeExpanded.js +99 -0
  189. package/v2Containers/WebPush/Create/preview/components/tests/AndroidMobileExpanded.test.js +733 -0
  190. package/v2Containers/WebPush/Create/preview/components/tests/WindowsChromeExpanded.test.js +571 -0
  191. package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/AndroidMobileExpanded.test.js.snap +81 -0
  192. package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/WindowsChromeExpanded.test.js.snap +81 -0
  193. package/v2Containers/WebPush/Create/preview/config/notificationMappings.js +50 -0
  194. package/v2Containers/WebPush/Create/preview/constants.js +637 -0
  195. package/v2Containers/WebPush/Create/preview/notification-container.scss +79 -0
  196. package/v2Containers/WebPush/Create/preview/preview.scss +351 -0
  197. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-chrome.scss +370 -0
  198. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-edge.scss +12 -0
  199. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-firefox.scss +12 -0
  200. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-opera.scss +12 -0
  201. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-chrome.scss +47 -0
  202. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-edge.scss +11 -0
  203. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-firefox.scss +11 -0
  204. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-opera.scss +11 -0
  205. package/v2Containers/WebPush/Create/preview/styles/_base.scss +207 -0
  206. package/v2Containers/WebPush/Create/preview/styles/_ios.scss +153 -0
  207. package/v2Containers/WebPush/Create/preview/styles/_ipados.scss +107 -0
  208. package/v2Containers/WebPush/Create/preview/styles/_macos-chrome.scss +101 -0
  209. package/v2Containers/WebPush/Create/preview/styles/_windows-chrome.scss +229 -0
  210. package/v2Containers/WebPush/Create/preview/tests/DevicePreviewContent.test.js +909 -0
  211. package/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +1081 -0
  212. package/v2Containers/WebPush/Create/preview/tests/PreviewControls.test.js +723 -0
  213. package/v2Containers/WebPush/Create/preview/tests/WebPushPreview.test.js +943 -0
  214. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/DevicePreviewContent.test.js.snap +131 -0
  215. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/NotificationContainer.test.js.snap +112 -0
  216. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/PreviewControls.test.js.snap +144 -0
  217. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/WebPushPreview.test.js.snap +129 -0
  218. package/v2Containers/WebPush/Create/utils/payloadBuilder.js +94 -0
  219. package/v2Containers/WebPush/Create/utils/payloadBuilder.test.js +390 -0
  220. package/v2Containers/WebPush/Create/utils/previewUtils.js +89 -0
  221. package/v2Containers/WebPush/Create/utils/urlValidation.js +115 -0
  222. package/v2Containers/WebPush/Create/utils/urlValidation.test.js +449 -0
  223. package/v2Containers/WebPush/Create/utils/validation.js +75 -0
  224. package/v2Containers/WebPush/Create/utils/validation.test.js +283 -0
  225. package/v2Containers/WebPush/actions.js +60 -0
  226. package/v2Containers/WebPush/constants.js +128 -0
  227. package/v2Containers/WebPush/index.js +2 -0
  228. package/v2Containers/WebPush/reducer.js +104 -0
  229. package/v2Containers/WebPush/sagas.js +119 -0
  230. package/v2Containers/WebPush/selectors.js +65 -0
  231. package/v2Containers/WebPush/tests/reducer.test.js +863 -0
  232. package/v2Containers/WebPush/tests/sagas.test.js +566 -0
  233. package/v2Containers/WebPush/tests/selectors.test.js +843 -0
  234. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +528 -431
  235. package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +0 -254
  236. package/v2Components/HtmlEditor/components/ValidationTabs/index.js +0 -362
  237. package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +0 -51
  238. package/v2Containers/BeePopupEditor/constants.js +0 -10
  239. package/v2Containers/BeePopupEditor/index.js +0 -193
  240. package/v2Containers/BeePopupEditor/tests/index.test.js +0 -627
  241. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +0 -1045
  242. package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +0 -376
  243. package/v2Containers/InApp/__tests__/sagas.test.js +0 -363
  244. package/v2Containers/InApp/tests/selectors.test.js +0 -612
  245. package/v2Containers/InAppWrapper/components/InAppWrapperView.js +0 -162
  246. package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +0 -267
  247. package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +0 -9
  248. package/v2Containers/InAppWrapper/constants.js +0 -16
  249. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +0 -473
  250. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +0 -198
  251. package/v2Containers/InAppWrapper/index.js +0 -148
  252. package/v2Containers/InAppWrapper/messages.js +0 -49
  253. package/v2Containers/InappAdvance/index.js +0 -1099
  254. package/v2Containers/InappAdvance/index.scss +0 -10
  255. package/v2Containers/InappAdvance/tests/index.test.js +0 -448
@@ -0,0 +1,538 @@
1
+ import { renderHook } from '@testing-library/react';
2
+ import { act } from 'react';
3
+ import { useImageUpload } from './useImageUpload';
4
+ import { IMAGE_UPLOAD_METHODS } from '../../constants';
5
+
6
+ describe('useImageUpload', () => {
7
+ const mockClearWebPushAsset = jest.fn();
8
+ const mockUploadWebPushAsset = jest.fn();
9
+
10
+ const mockWebPushActions = {
11
+ clearWebPushAsset: mockClearWebPushAsset,
12
+ uploadWebPushAsset: mockUploadWebPushAsset,
13
+ };
14
+
15
+ const defaultWebPush = {};
16
+
17
+ beforeEach(() => {
18
+ jest.clearAllMocks();
19
+ });
20
+
21
+ describe('Initialization', () => {
22
+ it('should initialize with default upload method', () => {
23
+ const { result } = renderHook(() =>
24
+ useImageUpload({
25
+ webPush: defaultWebPush,
26
+ webPushActions: mockWebPushActions,
27
+ index: 0,
28
+ })
29
+ );
30
+
31
+ expect(result.current.uploadMethod).toBe(IMAGE_UPLOAD_METHODS.UPLOAD_IMAGE);
32
+ expect(result.current.imageSrc).toBe('');
33
+ expect(result.current.imageUrl).toBe('');
34
+ expect(result.current.isValidating).toBe(false);
35
+ expect(result.current.isUploading).toBe(false);
36
+ });
37
+
38
+ it('should initialize with custom upload method', () => {
39
+ const { result } = renderHook(() =>
40
+ useImageUpload({
41
+ webPush: defaultWebPush,
42
+ webPushActions: mockWebPushActions,
43
+ index: 0,
44
+ initialUploadMethod: IMAGE_UPLOAD_METHODS.ADD_IMAGE_URL,
45
+ })
46
+ );
47
+
48
+ expect(result.current.uploadMethod).toBe(IMAGE_UPLOAD_METHODS.ADD_IMAGE_URL);
49
+ });
50
+ });
51
+
52
+ describe('Upload Method Changes', () => {
53
+ it('should change upload method', () => {
54
+ const { result } = renderHook(() =>
55
+ useImageUpload({
56
+ webPush: defaultWebPush,
57
+ webPushActions: mockWebPushActions,
58
+ index: 0,
59
+ })
60
+ );
61
+
62
+ const event = {
63
+ target: {
64
+ value: IMAGE_UPLOAD_METHODS.ADD_IMAGE_URL,
65
+ },
66
+ };
67
+
68
+ act(() => {
69
+ result.current.handleUploadMethodChange(event);
70
+ });
71
+
72
+ expect(result.current.uploadMethod).toBe(IMAGE_UPLOAD_METHODS.ADD_IMAGE_URL);
73
+ expect(result.current.imageSrc).toBe('');
74
+ expect(result.current.imageUrl).toBe('');
75
+ });
76
+
77
+ it('should clear imageSrc and imageUrl when upload method changes', () => {
78
+ const { result } = renderHook(() =>
79
+ useImageUpload({
80
+ webPush: defaultWebPush,
81
+ webPushActions: mockWebPushActions,
82
+ index: 0,
83
+ })
84
+ );
85
+
86
+ act(() => {
87
+ result.current.setImageSrc('path/to/image.jpg');
88
+ result.current.setImageUrl('https://example.com/image.jpg');
89
+ });
90
+
91
+ const event = {
92
+ target: {
93
+ value: IMAGE_UPLOAD_METHODS.ADD_IMAGE_URL,
94
+ },
95
+ };
96
+
97
+ act(() => {
98
+ result.current.handleUploadMethodChange(event);
99
+ });
100
+
101
+ expect(result.current.imageSrc).toBe('');
102
+ expect(result.current.imageUrl).toBe('');
103
+ });
104
+ });
105
+
106
+ describe('Image Source Management', () => {
107
+ it('should set image source', () => {
108
+ const { result } = renderHook(() =>
109
+ useImageUpload({
110
+ webPush: defaultWebPush,
111
+ webPushActions: mockWebPushActions,
112
+ index: 0,
113
+ })
114
+ );
115
+
116
+ act(() => {
117
+ result.current.setImageSrc('path/to/image.jpg');
118
+ });
119
+
120
+ expect(result.current.imageSrc).toBe('path/to/image.jpg');
121
+ });
122
+
123
+ it('should clear asset when setting image source', () => {
124
+ const { result } = renderHook(() =>
125
+ useImageUpload({
126
+ webPush: defaultWebPush,
127
+ webPushActions: mockWebPushActions,
128
+ index: 0,
129
+ })
130
+ );
131
+
132
+ act(() => {
133
+ result.current.setUpdateImageSrc('path/to/image.jpg');
134
+ });
135
+
136
+ expect(mockClearWebPushAsset).toHaveBeenCalledWith(0);
137
+ expect(result.current.imageSrc).toBe('path/to/image.jpg');
138
+ });
139
+
140
+ it('should use correct index when clearing asset', () => {
141
+ const { result } = renderHook(() =>
142
+ useImageUpload({
143
+ webPush: defaultWebPush,
144
+ webPushActions: mockWebPushActions,
145
+ index: 1,
146
+ })
147
+ );
148
+
149
+ act(() => {
150
+ result.current.setUpdateImageSrc('path/to/image.jpg');
151
+ });
152
+
153
+ expect(mockClearWebPushAsset).toHaveBeenCalledWith(1);
154
+ });
155
+
156
+ it('should update on re-upload', () => {
157
+ const { result } = renderHook(() =>
158
+ useImageUpload({
159
+ webPush: defaultWebPush,
160
+ webPushActions: mockWebPushActions,
161
+ index: 0,
162
+ })
163
+ );
164
+
165
+ act(() => {
166
+ result.current.setImageSrc('path/to/image.jpg');
167
+ });
168
+
169
+ act(() => {
170
+ result.current.updateOnReUpload();
171
+ });
172
+
173
+ expect(result.current.imageSrc).toBe('');
174
+ });
175
+ });
176
+
177
+ describe('Image URL Management', () => {
178
+ it('should set image URL', () => {
179
+ const { result } = renderHook(() =>
180
+ useImageUpload({
181
+ webPush: defaultWebPush,
182
+ webPushActions: mockWebPushActions,
183
+ index: 0,
184
+ })
185
+ );
186
+
187
+ act(() => {
188
+ result.current.setImageUrl('https://example.com/image.jpg');
189
+ });
190
+
191
+ expect(result.current.imageUrl).toBe('https://example.com/image.jpg');
192
+ });
193
+
194
+ it('should clear imageSrc when URL changes', () => {
195
+ const { result } = renderHook(() =>
196
+ useImageUpload({
197
+ webPush: defaultWebPush,
198
+ webPushActions: mockWebPushActions,
199
+ index: 0,
200
+ })
201
+ );
202
+
203
+ act(() => {
204
+ result.current.setImageSrc('path/to/image.jpg');
205
+ });
206
+
207
+ act(() => {
208
+ result.current.handleUrlChange('https://example.com/image.jpg');
209
+ });
210
+
211
+ expect(result.current.imageSrc).toBe('');
212
+ });
213
+
214
+ it('should not clear imageSrc when URL is the same', () => {
215
+ const { result } = renderHook(() =>
216
+ useImageUpload({
217
+ webPush: defaultWebPush,
218
+ webPushActions: mockWebPushActions,
219
+ index: 0,
220
+ })
221
+ );
222
+
223
+ // First set imageSrc to a non-empty value
224
+ act(() => {
225
+ result.current.setImageSrc('path/to/image.jpg');
226
+ });
227
+
228
+ // Then set imageUrl
229
+ act(() => {
230
+ result.current.setImageUrl('https://example.com/image.jpg');
231
+ });
232
+
233
+ // Capture the non-empty imageSrc
234
+ const nonEmptyImageSrc = result.current.imageSrc;
235
+ expect(nonEmptyImageSrc).toBe('path/to/image.jpg');
236
+ expect(nonEmptyImageSrc).not.toBe('');
237
+
238
+ // Call handleUrlChange with the same URL
239
+ act(() => {
240
+ result.current.handleUrlChange('https://example.com/image.jpg');
241
+ });
242
+
243
+ // Verify imageSrc is preserved (not cleared)
244
+ expect(result.current.imageSrc).toBe(nonEmptyImageSrc);
245
+ expect(result.current.imageSrc).toBe('path/to/image.jpg');
246
+ });
247
+ });
248
+
249
+ describe('Asset Upload', () => {
250
+ it('should call uploadWebPushAsset with correct parameters', () => {
251
+ const { result } = renderHook(() =>
252
+ useImageUpload({
253
+ webPush: defaultWebPush,
254
+ webPushActions: mockWebPushActions,
255
+ index: 0,
256
+ })
257
+ );
258
+
259
+ const file = new File(['test'], 'test.jpg', { type: 'image/jpeg' });
260
+ const type = 'image';
261
+ const fileParams = { width: 100, height: 100 };
262
+
263
+ act(() => {
264
+ result.current.uploadAsset(file, type, fileParams);
265
+ });
266
+
267
+ expect(mockUploadWebPushAsset).toHaveBeenCalledWith(file, type, fileParams, 0);
268
+ });
269
+
270
+ it('should use correct index for upload', () => {
271
+ const { result } = renderHook(() =>
272
+ useImageUpload({
273
+ webPush: defaultWebPush,
274
+ webPushActions: mockWebPushActions,
275
+ index: 1,
276
+ })
277
+ );
278
+
279
+ const file = new File(['test'], 'test.jpg', { type: 'image/jpeg' });
280
+
281
+ act(() => {
282
+ result.current.uploadAsset(file, 'image', {});
283
+ });
284
+
285
+ expect(mockUploadWebPushAsset).toHaveBeenCalledWith(file, 'image', {}, 1);
286
+ });
287
+ });
288
+
289
+ describe('Validation and Upload State', () => {
290
+ it('should update validation state', () => {
291
+ const { result } = renderHook(() =>
292
+ useImageUpload({
293
+ webPush: defaultWebPush,
294
+ webPushActions: mockWebPushActions,
295
+ index: 0,
296
+ })
297
+ );
298
+
299
+ act(() => {
300
+ result.current.handleValidationStateChange(true);
301
+ });
302
+
303
+ expect(result.current.isValidating).toBe(true);
304
+
305
+ act(() => {
306
+ result.current.handleValidationStateChange(false);
307
+ });
308
+
309
+ expect(result.current.isValidating).toBe(false);
310
+ });
311
+
312
+ it('should update upload state', () => {
313
+ const { result } = renderHook(() =>
314
+ useImageUpload({
315
+ webPush: defaultWebPush,
316
+ webPushActions: mockWebPushActions,
317
+ index: 0,
318
+ })
319
+ );
320
+
321
+ act(() => {
322
+ result.current.handleUploadStateChange(true);
323
+ });
324
+
325
+ expect(result.current.isUploading).toBe(true);
326
+
327
+ act(() => {
328
+ result.current.handleUploadStateChange(false);
329
+ });
330
+
331
+ expect(result.current.isUploading).toBe(false);
332
+ });
333
+ });
334
+
335
+ describe('Restoring Image from Uploaded Asset', () => {
336
+ it('should restore image from uploadedAssetData0', () => {
337
+ const webPush = {
338
+ uploadedAssetData0: {
339
+ metaInfo: {
340
+ secure_file_path: 'secure/path/to/image.jpg',
341
+ },
342
+ },
343
+ };
344
+
345
+ const { result } = renderHook(() =>
346
+ useImageUpload({
347
+ webPush,
348
+ webPushActions: mockWebPushActions,
349
+ index: 0,
350
+ })
351
+ );
352
+
353
+ expect(result.current.imageSrc).toBe('secure/path/to/image.jpg');
354
+ });
355
+
356
+ it('should restore image from uploadedAssetData for index 0', () => {
357
+ const webPush = {
358
+ uploadedAssetData: {
359
+ metaInfo: {
360
+ secure_file_path: 'secure/path/to/image.jpg',
361
+ },
362
+ },
363
+ };
364
+
365
+ const { result } = renderHook(() =>
366
+ useImageUpload({
367
+ webPush,
368
+ webPushActions: mockWebPushActions,
369
+ index: 0,
370
+ })
371
+ );
372
+
373
+ expect(result.current.imageSrc).toBe('secure/path/to/image.jpg');
374
+ });
375
+
376
+ it('should restore image from uploadedAssetData{index} for non-zero index', () => {
377
+ const webPush = {
378
+ uploadedAssetData1: {
379
+ metaInfo: {
380
+ secure_file_path: 'secure/path/to/brand-icon.jpg',
381
+ },
382
+ },
383
+ };
384
+
385
+ const { result } = renderHook(() =>
386
+ useImageUpload({
387
+ webPush,
388
+ webPushActions: mockWebPushActions,
389
+ index: 1,
390
+ })
391
+ );
392
+
393
+ expect(result.current.imageSrc).toBe('secure/path/to/brand-icon.jpg');
394
+ });
395
+
396
+ it('should not restore image when secure_file_path is missing', () => {
397
+ const webPush = {
398
+ uploadedAssetData0: {
399
+ metaInfo: {},
400
+ },
401
+ };
402
+
403
+ const { result } = renderHook(() =>
404
+ useImageUpload({
405
+ webPush,
406
+ webPushActions: mockWebPushActions,
407
+ index: 0,
408
+ })
409
+ );
410
+
411
+ expect(result.current.imageSrc).toBe('');
412
+ });
413
+
414
+ it('should not restore image when metaInfo is missing', () => {
415
+ const webPush = {
416
+ uploadedAssetData0: {},
417
+ };
418
+
419
+ const { result } = renderHook(() =>
420
+ useImageUpload({
421
+ webPush,
422
+ webPushActions: mockWebPushActions,
423
+ index: 0,
424
+ })
425
+ );
426
+
427
+ expect(result.current.imageSrc).toBe('');
428
+ });
429
+
430
+ it('should not restore image when uploadedAssetData is missing', () => {
431
+ const { result } = renderHook(() =>
432
+ useImageUpload({
433
+ webPush: {},
434
+ webPushActions: mockWebPushActions,
435
+ index: 0,
436
+ })
437
+ );
438
+
439
+ expect(result.current.imageSrc).toBe('');
440
+ });
441
+
442
+ it('should update image when webPush changes', () => {
443
+ const { result, rerender } = renderHook(
444
+ ({ webPush }) =>
445
+ useImageUpload({
446
+ webPush,
447
+ webPushActions: mockWebPushActions,
448
+ index: 0,
449
+ }),
450
+ {
451
+ initialProps: {
452
+ webPush: {},
453
+ },
454
+ }
455
+ );
456
+
457
+ expect(result.current.imageSrc).toBe('');
458
+
459
+ const newWebPush = {
460
+ uploadedAssetData0: {
461
+ metaInfo: {
462
+ secure_file_path: 'new/path/to/image.jpg',
463
+ },
464
+ },
465
+ };
466
+
467
+ rerender({ webPush: newWebPush });
468
+
469
+ expect(result.current.imageSrc).toBe('new/path/to/image.jpg');
470
+ });
471
+
472
+ it('should preserve image when webPush asset data is removed (edit mode compatibility)', () => {
473
+ const webPush = {
474
+ uploadedAssetData0: {
475
+ metaInfo: {
476
+ secure_file_path: 'secure/path/to/image.jpg',
477
+ },
478
+ },
479
+ };
480
+
481
+ const { result, rerender } = renderHook(
482
+ ({ webPush }) =>
483
+ useImageUpload({
484
+ webPush,
485
+ webPushActions: mockWebPushActions,
486
+ index: 0,
487
+ }),
488
+ {
489
+ initialProps: {
490
+ webPush,
491
+ },
492
+ }
493
+ );
494
+
495
+ expect(result.current.imageSrc).toBe('secure/path/to/image.jpg');
496
+
497
+ // Image should persist when asset data is removed (e.g., in edit mode)
498
+ rerender({ webPush: {} });
499
+
500
+ expect(result.current.imageSrc).toBe('secure/path/to/image.jpg');
501
+ });
502
+ });
503
+
504
+ describe('Edge Cases', () => {
505
+ it('should handle empty secure_file_path', () => {
506
+ const webPush = {
507
+ uploadedAssetData0: {
508
+ metaInfo: {
509
+ secure_file_path: '',
510
+ },
511
+ },
512
+ };
513
+
514
+ const { result } = renderHook(() =>
515
+ useImageUpload({
516
+ webPush,
517
+ webPushActions: mockWebPushActions,
518
+ index: 0,
519
+ })
520
+ );
521
+
522
+ expect(result.current.imageSrc).toBe('');
523
+ });
524
+
525
+ it('should handle null webPush', () => {
526
+ const { result } = renderHook(() =>
527
+ useImageUpload({
528
+ webPush: null,
529
+ webPushActions: mockWebPushActions,
530
+ index: 0,
531
+ })
532
+ );
533
+
534
+ expect(result.current.imageSrc).toBe('');
535
+ });
536
+ });
537
+ });
538
+
@@ -0,0 +1,122 @@
1
+ import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
2
+ import get from 'lodash/get';
3
+ import {
4
+ ALL,
5
+ TAG,
6
+ EMBEDDED,
7
+ DEFAULT,
8
+ FULL,
9
+ LIBRARY,
10
+ } from '../../../Whatsapp/constants';
11
+ import { SMS } from '../../../CreativesContainer/constants';
12
+ import { EMPTY_ARRAY } from '../../constants';
13
+
14
+ /**
15
+ * Custom hook for managing tags (fetching and context changes)
16
+ * @param {Object} params - Hook parameters
17
+ * @returns {Object} Tag state and handlers
18
+ */
19
+ export const useTagManagement = ({
20
+ location,
21
+ globalActions,
22
+ metaEntities,
23
+ getDefaultTags,
24
+ supportedTags = EMPTY_ARRAY,
25
+ injectedTags,
26
+ eventContextTags = EMPTY_ARRAY,
27
+ }) => {
28
+ const [tags, setTags] = useState([]);
29
+ const tagFetchKeyRef = useRef(null);
30
+
31
+ // Memoize location-derived values to prevent unnecessary re-renders
32
+ const locationType = useMemo(() => location?.query?.type || '', [location?.query?.type]);
33
+ const locationModule = useMemo(() => location?.query?.module || '', [location?.query?.module]);
34
+
35
+ const tagFetchKey = useMemo(
36
+ () =>
37
+ JSON.stringify({
38
+ type: locationType,
39
+ module: locationModule,
40
+ defaultTags: getDefaultTags || '',
41
+ }),
42
+ [locationModule, locationType, getDefaultTags],
43
+ );
44
+
45
+ useEffect(() => {
46
+ if (!globalActions?.fetchSchemaForEntity) return;
47
+
48
+ const { type, module } = location?.query || {};
49
+ const isEmbedded = type === EMBEDDED;
50
+ const context = isEmbedded ? module : DEFAULT;
51
+ const embedded = isEmbedded ? type : FULL;
52
+ const query = {
53
+ layout: SMS,
54
+ type: TAG,
55
+ context,
56
+ embedded,
57
+ };
58
+ if (getDefaultTags) {
59
+ query.context = getDefaultTags;
60
+ }
61
+
62
+ if (tagFetchKeyRef.current === tagFetchKey) {
63
+ return;
64
+ }
65
+ tagFetchKeyRef.current = tagFetchKey;
66
+ globalActions.fetchSchemaForEntity(query);
67
+ // tagFetchKey is memoized and captures all dependencies; including globalActions/location?.query causes infinite loops
68
+ // eslint-disable-next-line react-hooks/exhaustive-deps
69
+ }, [tagFetchKey]);
70
+
71
+ // Update tags from metaEntities and supported tags
72
+ // supportedTags must be memoized by the caller
73
+ useEffect(() => {
74
+ if (!metaEntities) return;
75
+ let tagList = get(metaEntities, 'tags.standard', []);
76
+ if (locationType === EMBEDDED && locationModule === LIBRARY && !getDefaultTags) {
77
+ tagList = supportedTags;
78
+ }
79
+ setTags(tagList);
80
+ }, [metaEntities, locationModule, locationType, getDefaultTags, supportedTags]);
81
+
82
+ const handleOnTagsContextChange = useCallback(
83
+ (data) => {
84
+ if (!globalActions?.fetchSchemaForEntity) return;
85
+
86
+ const isEmbedded = locationType === EMBEDDED;
87
+ const embedded = isEmbedded ? locationType : FULL;
88
+ const normalized = (data ?? '').toLowerCase();
89
+ const context = (!normalized || normalized === ALL) ? DEFAULT : normalized;
90
+ const query = {
91
+ layout: SMS,
92
+ type: TAG,
93
+ context,
94
+ embedded,
95
+ };
96
+ if (getDefaultTags) {
97
+ query.context = getDefaultTags;
98
+ }
99
+ globalActions.fetchSchemaForEntity(query);
100
+ },
101
+ [globalActions, getDefaultTags, locationType],
102
+ );
103
+
104
+ // Memoize validation configuration
105
+ const validationConfig = useMemo(
106
+ () => ({
107
+ tagsParam: tags,
108
+ injectedTagsParams: injectedTags,
109
+ location,
110
+ tagModule: getDefaultTags,
111
+ eventContextTags,
112
+ }),
113
+ [tags, injectedTags, location, getDefaultTags, eventContextTags],
114
+ );
115
+
116
+ return {
117
+ tags,
118
+ handleOnTagsContextChange,
119
+ validationConfig,
120
+ };
121
+ };
122
+