@capillarytech/creatives-library 8.0.263 → 8.0.265

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 (280) hide show
  1. package/assets/Android.png +0 -0
  2. package/assets/iOS.png +0 -0
  3. package/constants/unified.js +1 -3
  4. package/initialReducer.js +0 -2
  5. package/package.json +1 -1
  6. package/services/api.js +0 -15
  7. package/services/tests/api.test.js +0 -34
  8. package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +35 -17
  9. package/tests/integration/TemplateCreation/api-response.js +1 -31
  10. package/tests/integration/TemplateCreation/msw-handler.js +0 -2
  11. package/utils/common.js +0 -11
  12. package/utils/commonUtils.js +5 -28
  13. package/utils/tests/commonUtil.test.js +0 -224
  14. package/utils/tests/transformerUtils.test.js +0 -297
  15. package/utils/transformTemplateConfig.js +10 -0
  16. package/utils/transformerUtils.js +0 -40
  17. package/v2Components/CapDeviceContent/index.js +56 -61
  18. package/v2Components/CapImageUpload/constants.js +0 -2
  19. package/v2Components/CapImageUpload/index.js +16 -65
  20. package/v2Components/CapImageUpload/index.scss +1 -4
  21. package/v2Components/CapImageUpload/messages.js +1 -5
  22. package/v2Components/CapTagList/index.js +1 -6
  23. package/v2Components/CapTagListWithInput/index.js +1 -5
  24. package/v2Components/CapTagListWithInput/messages.js +1 -1
  25. package/v2Components/CapWhatsappCTA/tests/index.test.js +0 -5
  26. package/v2Components/ErrorInfoNote/index.js +72 -402
  27. package/v2Components/ErrorInfoNote/messages.js +6 -32
  28. package/v2Components/ErrorInfoNote/style.scss +6 -278
  29. package/v2Components/FormBuilder/tests/index.test.js +4 -13
  30. package/v2Components/HtmlEditor/HTMLEditor.js +99 -418
  31. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +133 -1882
  32. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +16 -27
  33. package/v2Components/HtmlEditor/_htmlEditor.scss +45 -108
  34. package/v2Components/HtmlEditor/_index.lazy.scss +1 -0
  35. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +102 -23
  36. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +140 -148
  37. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +1 -2
  38. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
  39. package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +1 -9
  40. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +6 -31
  41. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +0 -22
  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 +10 -7
  48. package/v2Components/HtmlEditor/components/PreviewPane/index.js +43 -22
  49. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  50. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +152 -0
  51. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +0 -18
  52. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +31 -36
  53. package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +34 -46
  54. package/v2Components/HtmlEditor/components/ValidationPanel/index.js +46 -52
  55. package/v2Components/HtmlEditor/constants.js +20 -45
  56. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +16 -373
  57. package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +16 -351
  58. package/v2Components/HtmlEditor/hooks/useEditorContent.js +2 -5
  59. package/v2Components/HtmlEditor/hooks/useInAppContent.js +146 -88
  60. package/v2Components/HtmlEditor/hooks/useValidation.js +56 -213
  61. package/v2Components/HtmlEditor/index.js +1 -1
  62. package/v2Components/HtmlEditor/messages.js +94 -102
  63. package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +45 -214
  64. package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +0 -134
  65. package/v2Components/HtmlEditor/utils/contentSanitizer.js +41 -40
  66. package/v2Components/HtmlEditor/utils/htmlValidator.js +72 -71
  67. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +124 -158
  68. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +25 -23
  69. package/v2Components/HtmlEditor/utils/validationAdapter.js +41 -66
  70. package/v2Components/MobilePushPreviewV2/index.js +7 -33
  71. package/v2Components/TemplatePreview/_templatePreview.scss +24 -55
  72. package/v2Components/TemplatePreview/index.js +32 -47
  73. package/v2Components/TemplatePreview/messages.js +0 -4
  74. package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +0 -1
  75. package/v2Containers/App/constants.js +0 -5
  76. package/v2Containers/BeeEditor/index.js +90 -172
  77. package/v2Containers/CreativesContainer/SlideBoxContent.js +53 -184
  78. package/v2Containers/CreativesContainer/SlideBoxFooter.js +13 -163
  79. package/v2Containers/CreativesContainer/SlideBoxHeader.js +1 -3
  80. package/v2Containers/CreativesContainer/constants.js +0 -4
  81. package/v2Containers/CreativesContainer/index.js +46 -408
  82. package/v2Containers/CreativesContainer/messages.js +0 -12
  83. package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +0 -210
  84. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +2 -11
  85. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +50 -342
  86. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +0 -103
  87. package/v2Containers/Email/actions.js +0 -7
  88. package/v2Containers/Email/constants.js +1 -5
  89. package/v2Containers/Email/index.js +36 -237
  90. package/v2Containers/Email/messages.js +0 -32
  91. package/v2Containers/Email/reducer.js +1 -12
  92. package/v2Containers/Email/sagas.js +7 -61
  93. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +0 -2
  94. package/v2Containers/Email/tests/reducer.test.js +0 -46
  95. package/v2Containers/Email/tests/sagas.test.js +29 -320
  96. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +21 -211
  97. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +74 -40
  98. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +67 -2
  99. package/v2Containers/EmailWrapper/constants.js +0 -2
  100. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +77 -629
  101. package/v2Containers/EmailWrapper/index.js +23 -103
  102. package/v2Containers/EmailWrapper/messages.js +1 -65
  103. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +214 -0
  104. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +77 -594
  105. package/v2Containers/InApp/actions.js +0 -7
  106. package/v2Containers/InApp/constants.js +4 -20
  107. package/v2Containers/InApp/index.js +359 -802
  108. package/v2Containers/InApp/index.scss +3 -4
  109. package/v2Containers/InApp/messages.js +3 -7
  110. package/v2Containers/InApp/reducer.js +3 -21
  111. package/v2Containers/InApp/sagas.js +9 -29
  112. package/v2Containers/InApp/selectors.js +5 -25
  113. package/v2Containers/InApp/tests/index.test.js +50 -154
  114. package/v2Containers/InApp/tests/reducer.test.js +0 -34
  115. package/v2Containers/InApp/tests/sagas.test.js +9 -61
  116. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +0 -3
  117. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +0 -2
  118. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +0 -2
  119. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +0 -9
  120. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +0 -12
  121. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +0 -4
  122. package/v2Containers/TagList/index.js +19 -62
  123. package/v2Containers/Templates/ChannelTypeIllustration.js +1 -13
  124. package/v2Containers/Templates/_templates.scss +1 -265
  125. package/v2Containers/Templates/actions.js +1 -2
  126. package/v2Containers/Templates/constants.js +0 -1
  127. package/v2Containers/Templates/index.js +38 -363
  128. package/v2Containers/Templates/messages.js +0 -28
  129. package/v2Containers/Templates/reducer.js +0 -2
  130. package/v2Containers/Templates/tests/index.test.js +0 -10
  131. package/v2Containers/TemplatesV2/TemplatesV2.style.js +2 -4
  132. package/v2Containers/TemplatesV2/index.js +7 -15
  133. package/v2Containers/TemplatesV2/messages.js +0 -4
  134. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +0 -34
  135. package/utils/imageUrlUpload.js +0 -141
  136. package/v2Components/CapImageUrlUpload/constants.js +0 -26
  137. package/v2Components/CapImageUrlUpload/index.js +0 -365
  138. package/v2Components/CapImageUrlUpload/index.scss +0 -35
  139. package/v2Components/CapImageUrlUpload/messages.js +0 -47
  140. package/v2Components/ErrorInfoNote/constants.js +0 -1
  141. package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +0 -870
  142. package/v2Components/HtmlEditor/components/ValidationPanel/constants.js +0 -6
  143. package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +0 -281
  144. package/v2Components/HtmlEditor/components/ValidationTabs/index.js +0 -295
  145. package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +0 -51
  146. package/v2Components/HtmlEditor/utils/validationConstants.js +0 -38
  147. package/v2Components/MobilePushPreviewV2/constants.js +0 -6
  148. package/v2Containers/BeePopupEditor/_beePopupEditor.scss +0 -14
  149. package/v2Containers/BeePopupEditor/constants.js +0 -10
  150. package/v2Containers/BeePopupEditor/index.js +0 -194
  151. package/v2Containers/BeePopupEditor/tests/index.test.js +0 -627
  152. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +0 -1246
  153. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +0 -2472
  154. package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +0 -520
  155. package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +0 -956
  156. package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +0 -376
  157. package/v2Containers/InApp/__tests__/sagas.test.js +0 -363
  158. package/v2Containers/InApp/tests/selectors.test.js +0 -612
  159. package/v2Containers/InAppWrapper/components/InAppWrapperView.js +0 -151
  160. package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +0 -267
  161. package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +0 -23
  162. package/v2Containers/InAppWrapper/constants.js +0 -16
  163. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +0 -473
  164. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +0 -198
  165. package/v2Containers/InAppWrapper/index.js +0 -148
  166. package/v2Containers/InAppWrapper/messages.js +0 -49
  167. package/v2Containers/InappAdvance/index.js +0 -1099
  168. package/v2Containers/InappAdvance/index.scss +0 -10
  169. package/v2Containers/InappAdvance/tests/index.test.js +0 -448
  170. package/v2Containers/WebPush/Create/components/BrandIconSection.js +0 -108
  171. package/v2Containers/WebPush/Create/components/ButtonForm.js +0 -172
  172. package/v2Containers/WebPush/Create/components/ButtonItem.js +0 -101
  173. package/v2Containers/WebPush/Create/components/ButtonList.js +0 -145
  174. package/v2Containers/WebPush/Create/components/ButtonsLinksSection.js +0 -164
  175. package/v2Containers/WebPush/Create/components/ButtonsLinksSection.test.js +0 -463
  176. package/v2Containers/WebPush/Create/components/FormActions.js +0 -54
  177. package/v2Containers/WebPush/Create/components/FormActions.test.js +0 -163
  178. package/v2Containers/WebPush/Create/components/MediaSection.js +0 -142
  179. package/v2Containers/WebPush/Create/components/MediaSection.test.js +0 -341
  180. package/v2Containers/WebPush/Create/components/MessageSection.js +0 -103
  181. package/v2Containers/WebPush/Create/components/MessageSection.test.js +0 -268
  182. package/v2Containers/WebPush/Create/components/NotificationTitleSection.js +0 -87
  183. package/v2Containers/WebPush/Create/components/NotificationTitleSection.test.js +0 -210
  184. package/v2Containers/WebPush/Create/components/TemplateNameSection.js +0 -54
  185. package/v2Containers/WebPush/Create/components/TemplateNameSection.test.js +0 -143
  186. package/v2Containers/WebPush/Create/components/__snapshots__/ButtonsLinksSection.test.js.snap +0 -86
  187. package/v2Containers/WebPush/Create/components/__snapshots__/FormActions.test.js.snap +0 -16
  188. package/v2Containers/WebPush/Create/components/__snapshots__/MediaSection.test.js.snap +0 -41
  189. package/v2Containers/WebPush/Create/components/__snapshots__/MessageSection.test.js.snap +0 -54
  190. package/v2Containers/WebPush/Create/components/__snapshots__/NotificationTitleSection.test.js.snap +0 -37
  191. package/v2Containers/WebPush/Create/components/__snapshots__/TemplateNameSection.test.js.snap +0 -21
  192. package/v2Containers/WebPush/Create/components/_buttons.scss +0 -246
  193. package/v2Containers/WebPush/Create/components/tests/ButtonForm.test.js +0 -554
  194. package/v2Containers/WebPush/Create/components/tests/ButtonItem.test.js +0 -607
  195. package/v2Containers/WebPush/Create/components/tests/ButtonList.test.js +0 -633
  196. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonForm.test.js.snap +0 -666
  197. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonItem.test.js.snap +0 -74
  198. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonList.test.js.snap +0 -78
  199. package/v2Containers/WebPush/Create/hooks/useButtonManagement.js +0 -138
  200. package/v2Containers/WebPush/Create/hooks/useButtonManagement.test.js +0 -406
  201. package/v2Containers/WebPush/Create/hooks/useCharacterCount.js +0 -30
  202. package/v2Containers/WebPush/Create/hooks/useCharacterCount.test.js +0 -151
  203. package/v2Containers/WebPush/Create/hooks/useImageUpload.js +0 -104
  204. package/v2Containers/WebPush/Create/hooks/useImageUpload.test.js +0 -538
  205. package/v2Containers/WebPush/Create/hooks/useTagManagement.js +0 -122
  206. package/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -633
  207. package/v2Containers/WebPush/Create/index.js +0 -1148
  208. package/v2Containers/WebPush/Create/index.scss +0 -134
  209. package/v2Containers/WebPush/Create/messages.js +0 -211
  210. package/v2Containers/WebPush/Create/preview/DevicePreviewContent.js +0 -228
  211. package/v2Containers/WebPush/Create/preview/NotificationContainer.js +0 -294
  212. package/v2Containers/WebPush/Create/preview/PreviewContent.js +0 -90
  213. package/v2Containers/WebPush/Create/preview/PreviewControls.js +0 -305
  214. package/v2Containers/WebPush/Create/preview/PreviewDisclaimer.js +0 -25
  215. package/v2Containers/WebPush/Create/preview/WebPushPreview.js +0 -155
  216. package/v2Containers/WebPush/Create/preview/assets/Light.svg +0 -53
  217. package/v2Containers/WebPush/Create/preview/assets/Top.svg +0 -5
  218. package/v2Containers/WebPush/Create/preview/assets/android-arrow-down.svg +0 -9
  219. package/v2Containers/WebPush/Create/preview/assets/android-arrow-up.svg +0 -9
  220. package/v2Containers/WebPush/Create/preview/assets/chrome-icon.png +0 -0
  221. package/v2Containers/WebPush/Create/preview/assets/edge-icon.png +0 -0
  222. package/v2Containers/WebPush/Create/preview/assets/firefox-icon.svg +0 -106
  223. package/v2Containers/WebPush/Create/preview/assets/iOS.svg +0 -26
  224. package/v2Containers/WebPush/Create/preview/assets/macos-arrow-down-icon.svg +0 -9
  225. package/v2Containers/WebPush/Create/preview/assets/macos-triple-dot-icon.svg +0 -9
  226. package/v2Containers/WebPush/Create/preview/assets/opera-icon.svg +0 -18
  227. package/v2Containers/WebPush/Create/preview/assets/safari-icon.svg +0 -29
  228. package/v2Containers/WebPush/Create/preview/assets/windows-close-icon.svg +0 -9
  229. package/v2Containers/WebPush/Create/preview/assets/windows-triple-dot-icon.svg +0 -9
  230. package/v2Containers/WebPush/Create/preview/components/AndroidMobileChromeHeader.js +0 -51
  231. package/v2Containers/WebPush/Create/preview/components/AndroidMobileExpanded.js +0 -145
  232. package/v2Containers/WebPush/Create/preview/components/IOSHeader.js +0 -45
  233. package/v2Containers/WebPush/Create/preview/components/NotificationExpandedContent.js +0 -68
  234. package/v2Containers/WebPush/Create/preview/components/NotificationHeader.js +0 -61
  235. package/v2Containers/WebPush/Create/preview/components/WindowsChromeExpanded.js +0 -99
  236. package/v2Containers/WebPush/Create/preview/components/tests/AndroidMobileExpanded.test.js +0 -733
  237. package/v2Containers/WebPush/Create/preview/components/tests/WindowsChromeExpanded.test.js +0 -571
  238. package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/AndroidMobileExpanded.test.js.snap +0 -85
  239. package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/WindowsChromeExpanded.test.js.snap +0 -81
  240. package/v2Containers/WebPush/Create/preview/config/notificationMappings.js +0 -50
  241. package/v2Containers/WebPush/Create/preview/constants.js +0 -637
  242. package/v2Containers/WebPush/Create/preview/notification-container.scss +0 -79
  243. package/v2Containers/WebPush/Create/preview/preview.scss +0 -358
  244. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-chrome.scss +0 -370
  245. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-edge.scss +0 -12
  246. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-firefox.scss +0 -12
  247. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-opera.scss +0 -12
  248. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-chrome.scss +0 -47
  249. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-edge.scss +0 -11
  250. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-firefox.scss +0 -11
  251. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-opera.scss +0 -11
  252. package/v2Containers/WebPush/Create/preview/styles/_base.scss +0 -207
  253. package/v2Containers/WebPush/Create/preview/styles/_ios.scss +0 -153
  254. package/v2Containers/WebPush/Create/preview/styles/_ipados.scss +0 -107
  255. package/v2Containers/WebPush/Create/preview/styles/_macos-chrome.scss +0 -101
  256. package/v2Containers/WebPush/Create/preview/styles/_windows-chrome.scss +0 -229
  257. package/v2Containers/WebPush/Create/preview/tests/DevicePreviewContent.test.js +0 -909
  258. package/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +0 -1081
  259. package/v2Containers/WebPush/Create/preview/tests/PreviewControls.test.js +0 -723
  260. package/v2Containers/WebPush/Create/preview/tests/WebPushPreview.test.js +0 -1327
  261. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/DevicePreviewContent.test.js.snap +0 -131
  262. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/NotificationContainer.test.js.snap +0 -112
  263. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/PreviewControls.test.js.snap +0 -144
  264. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/WebPushPreview.test.js.snap +0 -129
  265. package/v2Containers/WebPush/Create/utils/payloadBuilder.js +0 -96
  266. package/v2Containers/WebPush/Create/utils/payloadBuilder.test.js +0 -396
  267. package/v2Containers/WebPush/Create/utils/previewUtils.js +0 -89
  268. package/v2Containers/WebPush/Create/utils/urlValidation.js +0 -115
  269. package/v2Containers/WebPush/Create/utils/urlValidation.test.js +0 -449
  270. package/v2Containers/WebPush/Create/utils/validation.js +0 -75
  271. package/v2Containers/WebPush/Create/utils/validation.test.js +0 -283
  272. package/v2Containers/WebPush/actions.js +0 -60
  273. package/v2Containers/WebPush/constants.js +0 -132
  274. package/v2Containers/WebPush/index.js +0 -2
  275. package/v2Containers/WebPush/reducer.js +0 -104
  276. package/v2Containers/WebPush/sagas.js +0 -119
  277. package/v2Containers/WebPush/selectors.js +0 -65
  278. package/v2Containers/WebPush/tests/reducer.test.js +0 -863
  279. package/v2Containers/WebPush/tests/sagas.test.js +0 -566
  280. package/v2Containers/WebPush/tests/selectors.test.js +0 -960
@@ -12,14 +12,14 @@
12
12
  * Note: Uses injectIntl with forwardRef to provide direct access to CodeEditorPane via ref
13
13
  */
14
14
 
15
- import React, {
16
- useRef, useCallback, useMemo, useState, useEffect, useImperativeHandle, forwardRef,
17
- } from 'react';
15
+ import React, { useRef, useCallback, useMemo, useState } from 'react';
18
16
  import PropTypes from 'prop-types';
17
+ import { Layout } from 'antd'; // Fallback - no Cap UI equivalent
19
18
  import { injectIntl, intlShape } from 'react-intl';
20
19
 
21
20
  // Cap UI Components (First Preference)
22
21
  import CapRow from '@capillarytech/cap-ui-library/CapRow';
22
+ import CapColumn from '@capillarytech/cap-ui-library/CapColumn';
23
23
  import CapSpin from '@capillarytech/cap-ui-library/CapSpin';
24
24
  import CapNotification from '@capillarytech/cap-ui-library/CapNotification';
25
25
  import CapModal from '@capillarytech/cap-ui-library/CapModal';
@@ -30,6 +30,7 @@ import DeviceToggle from './components/DeviceToggle';
30
30
  import SplitContainer from './components/SplitContainer';
31
31
  import CodeEditorPane from './components/CodeEditorPane';
32
32
  import PreviewPane from './components/PreviewPane';
33
+ import ValidationErrorDisplay from './components/ValidationErrorDisplay';
33
34
  import { EditorProvider } from './components/common/EditorContext';
34
35
 
35
36
  // Hooks and utilities
@@ -39,11 +40,7 @@ import { useLayoutState } from './hooks/useLayoutState';
39
40
  import { useValidation } from './hooks/useValidation';
40
41
 
41
42
  // Constants
42
- import {
43
- HTML_EDITOR_VARIANTS, DEVICE_TYPES, DEFAULT_HTML_CONTENT, TAG, EMBEDDED, DEFAULT, FULL, ALL, SMS, EMAIL,
44
- BLOCKING_ERROR_RULE_IDS,
45
- VALIDATION_SEVERITY,
46
- } from './constants';
43
+ import { HTML_EDITOR_VARIANTS, DEVICE_TYPES, DEFAULT_HTML_CONTENT } from './constants';
47
44
 
48
45
  // Styles
49
46
  import './_htmlEditor.scss';
@@ -51,33 +48,8 @@ import './components/FullscreenModal/_fullscreenModal.scss';
51
48
 
52
49
  // Messages
53
50
  import messages from './messages';
54
- import { ISSUE_SOURCES } from './utils/validationConstants';
55
-
56
- /** Check if an issue is a blocking error (Errors tab). Non-blocking = Warnings. */
57
- const isBlockingError = (issue) => {
58
- const { rule, source, severity } = issue || {};
59
- if (rule === 'liquid-api-validation' || rule === 'standard-api-validation') return true;
60
- if (source === ISSUE_SOURCES.LIQUID && severity === VALIDATION_SEVERITY.ERROR) return true;
61
- if (BLOCKING_ERROR_RULE_IDS.includes(rule)) return true;
62
- return false;
63
- };
64
-
65
- /** Count issues as Errors (blocking) and Warnings (non-blocking). */
66
- const countIssuesBySeverity = (allIssues) => {
67
- let errors = 0;
68
- let warnings = 0;
69
- (allIssues || []).forEach((issue) => {
70
- if (isBlockingError(issue)) errors += 1;
71
- else warnings += 1;
72
- });
73
- return {
74
- errors,
75
- warnings,
76
- total: (allIssues || []).length,
77
- };
78
- };
79
51
 
80
- const HTMLEditor = forwardRef(({
52
+ const HTMLEditor = ({
81
53
  intl,
82
54
  variant = HTML_EDITOR_VARIANTS.EMAIL, // New prop: 'email' or 'inapp'
83
55
  layoutType, // Layout type for InApp variant
@@ -89,33 +61,17 @@ const HTMLEditor = forwardRef(({
89
61
  showFullscreenButton = true,
90
62
  autoSave = true,
91
63
  autoSaveInterval = 30000, // 30 seconds
92
- // Tag-related props - tags are fetched and managed by parent component (EmailHTMLEditor, INAPP, etc.)
93
- tags = [],
94
- injectedTags = {},
95
- location,
96
- eventContextTags = [],
97
- selectedOfferDetails = [],
98
- channel,
99
- userLocale = 'en',
100
- moduleFilterEnabled = true,
101
- onTagContextChange, // Parent component handles tag fetching
102
- onTagSelect = null,
103
- onContextChange = null,
104
- globalActions = null,
105
- isLiquidEnabled = false, // Controls Liquid tab visibility in ValidationTabs
106
- isFullMode = true, // Full mode vs library mode - controls layout and visibility
107
- onErrorAcknowledged = null, // Callback when user clicks redirection icon to acknowledge errors
108
- onValidationChange = null, // Callback when validation state changes (for parent to track errors)
109
- apiValidationErrors = null, // API validation errors from validateLiquidTemplateContent { liquidErrors: [], standardErrors: [] }
110
64
  ...props
111
- }, ref) => {
65
+ }) => {
112
66
  // Separate refs for main and modal editors to avoid conflicts
113
67
  const mainEditorRef = useRef(null);
114
68
  const modalEditorRef = useRef(null);
115
69
  const [isFullscreenModalOpen, setIsFullscreenModalOpen] = useState(false);
116
70
 
117
71
  // Get the currently active editor ref based on fullscreen state
118
- const getActiveEditorRef = useCallback(() => isFullscreenModalOpen ? modalEditorRef : mainEditorRef, [isFullscreenModalOpen]);
72
+ const getActiveEditorRef = useCallback(() => {
73
+ return isFullscreenModalOpen ? modalEditorRef : mainEditorRef;
74
+ }, [isFullscreenModalOpen]);
119
75
 
120
76
  // Initialize custom hooks for state management - always call both hooks to follow Rules of Hooks
121
77
  const isEmailVariant = variant === HTML_EDITOR_VARIANTS.EMAIL;
@@ -124,7 +80,7 @@ const HTMLEditor = forwardRef(({
124
80
  autoSave: isEmailVariant ? autoSave : false,
125
81
  autoSaveInterval,
126
82
  onSave: isEmailVariant ? onSave : null,
127
- onChange: isEmailVariant ? onContentChange : null,
83
+ onChange: isEmailVariant ? onContentChange : null
128
84
  };
129
85
 
130
86
  const emailContent = useEditorContent(
@@ -141,7 +97,7 @@ const HTMLEditor = forwardRef(({
141
97
  // Convert string content to device-specific format
142
98
  inAppInitialContent = {
143
99
  [DEVICE_TYPES.ANDROID]: initialContent,
144
- [DEVICE_TYPES.IOS]: initialContent,
100
+ [DEVICE_TYPES.IOS]: initialContent
145
101
  };
146
102
  } else {
147
103
  // Use provided device-specific content
@@ -153,7 +109,7 @@ const HTMLEditor = forwardRef(({
153
109
  autoSave: isInAppVariant ? autoSave : false,
154
110
  autoSaveInterval,
155
111
  onSave: isInAppVariant ? onSave : null,
156
- onChange: isInAppVariant ? onContentChange : null,
112
+ onChange: isInAppVariant ? onContentChange : null
157
113
  };
158
114
 
159
115
  const inAppContent = useInAppContent(inAppInitialContent, inAppOptions);
@@ -161,64 +117,6 @@ const HTMLEditor = forwardRef(({
161
117
  // Use appropriate content hook based on variant
162
118
  const content = variant === HTML_EDITOR_VARIANTS.EMAIL ? emailContent : inAppContent;
163
119
 
164
- // Update content when initialContent prop changes (for edit mode)
165
- // This ensures the editor updates when template data loads
166
- useEffect(() => {
167
- if (isEmailVariant && emailContent && initialContent !== undefined && initialContent !== null) {
168
- // Only update if content is different to avoid unnecessary updates
169
- if (emailContent.content !== initialContent) {
170
- emailContent.updateContent(initialContent, true); // immediate update
171
- }
172
- } else if (isInAppVariant && inAppContent && initialContent !== undefined && initialContent !== null) {
173
- // Handle InApp variant updates
174
- const contentToUpdate = typeof initialContent === 'string'
175
- ? { [DEVICE_TYPES.ANDROID]: initialContent, [DEVICE_TYPES.IOS]: initialContent }
176
- : initialContent;
177
- if (inAppContent.updateContent) {
178
- const currentContent = inAppContent.getDeviceContent?.(inAppContent.activeDevice);
179
- const newContent = contentToUpdate[inAppContent.activeDevice] || contentToUpdate[DEVICE_TYPES.ANDROID] || '';
180
- if (currentContent !== newContent) {
181
- inAppContent.updateContent(newContent, true);
182
- }
183
- }
184
- }
185
- }, [initialContent, isEmailVariant, isInAppVariant]);
186
- // Handle context change for tag API calls
187
- // If variant is INAPP, use SMS layout; otherwise use the channel (EMAIL)
188
- const handleContextChange = useCallback((contextData) => {
189
- // If onContextChange is provided, use it instead of making our own API call
190
- // This prevents duplicate API calls when parent component handles tag fetching
191
- if (onContextChange) {
192
- onContextChange(contextData);
193
- return;
194
- }
195
-
196
- // Only make API call if onContextChange is not provided and globalActions is available
197
- if (!globalActions || !location) {
198
- return;
199
- }
200
-
201
- const { type } = location.query || {};
202
- const tempData = (contextData || '').toLowerCase();
203
- const isEmbedded = type === EMBEDDED;
204
- const embedded = isEmbedded ? type : FULL;
205
- const context = tempData === ALL ? DEFAULT : tempData;
206
-
207
- // Determine layout: INAPP variant uses SMS, EMAIL variant uses EMAIL
208
- const layout = variant === HTML_EDITOR_VARIANTS.INAPP ? SMS : EMAIL;
209
-
210
- const query = {
211
- layout,
212
- type: TAG,
213
- context,
214
- embedded,
215
- };
216
-
217
- // Call the API via Redux action - this will trigger the saga which calls Api.fetchSchemaForEntity
218
- // The API endpoint will be: /meta/TAG?query={...}
219
- globalActions.fetchSchemaForEntity(query);
220
- }, [variant, globalActions, location, onContextChange]);
221
-
222
120
  // Destructure content properties for cleaner access throughout component
223
121
  const {
224
122
  activeDevice,
@@ -226,14 +124,14 @@ const HTMLEditor = forwardRef(({
226
124
  switchDevice,
227
125
  toggleContentSync,
228
126
  getDeviceContent,
229
- markAsSaved,
127
+ markAsSaved
230
128
  } = content || {};
231
129
 
232
130
  const layout = useLayoutState({
233
131
  splitSizes: [50, 50],
234
132
  viewMode: 'desktop',
235
133
  mobileWidth: 375,
236
- isFullscreen: false,
134
+ isFullscreen: false
237
135
  });
238
136
 
239
137
  // Get current content for validation based on variant
@@ -260,7 +158,7 @@ const HTMLEditor = forwardRef(({
260
158
  'sanitizer.productionValidHtml': messages.sanitizer.productionValidHtml,
261
159
  'sanitizer.productionSanitized': messages.sanitizer.productionSanitized,
262
160
  'sanitizer.productionInlineCss': messages.sanitizer.productionInlineCss,
263
- 'sanitizer.productionLargeContent': messages.sanitizer.productionLargeContent,
161
+ 'sanitizer.productionLargeContent': messages.sanitizer.productionLargeContent
264
162
  };
265
163
 
266
164
  const messageObj = messageMap[messageKey];
@@ -281,7 +179,7 @@ const HTMLEditor = forwardRef(({
281
179
  'validator.largeImageDetected': messages.validator.largeImageDetected,
282
180
  'validator.unclosedCssRule': messages.validator.unclosedCssRule,
283
181
  'validator.emptyCssRule': messages.validator.emptyCssRule,
284
- 'validator.cssValidationFailed': messages.validator.cssValidationFailed,
182
+ 'validator.cssValidationFailed': messages.validator.cssValidationFailed
285
183
  };
286
184
 
287
185
  const messageObj = messageMap[messageKey];
@@ -292,215 +190,57 @@ const HTMLEditor = forwardRef(({
292
190
  enableRealTime: true,
293
191
  debounceMs: 500,
294
192
  enableSanitization: true,
295
- securityLevel: 'standard',
296
- apiValidationErrors, // Pass API validation errors to merge with client-side validation
193
+ securityLevel: 'standard'
297
194
  }, formatSanitizerMessage, formatValidatorMessage);
298
195
 
299
- // Expose validation and content state via ref
300
- useImperativeHandle(ref, () => ({
301
- getValidation: () => validation,
302
- getContent: () => currentContent,
303
- isContentEmpty: () => !currentContent || currentContent.trim() === '',
304
- getIssueCounts: () => {
305
- if (!validation || typeof validation.getAllIssues !== 'function') {
306
- return { errors: 0, warnings: 0, total: 0 };
307
- }
308
- return countIssuesBySeverity(validation.getAllIssues());
309
- },
310
- getValidationState: () => ({
311
- isValidating: validation?.isValidating || false,
312
- hasErrors: validation?.hasBlockingErrors || false,
313
- issueCounts: !validation || typeof validation.getAllIssues !== 'function'
314
- ? { errors: 0, warnings: 0, total: 0 }
315
- : countIssuesBySeverity(validation.getAllIssues()),
316
- })
317
- ,
318
- }), [validation, currentContent, apiValidationErrors]); // Include apiValidationErrors so ref methods return updated counts
319
-
320
- // Use ref to store callback to avoid infinite loops (callback in deps would cause re-runs)
321
- const onValidationChangeRef = useRef(onValidationChange);
322
- useEffect(() => {
323
- onValidationChangeRef.current = onValidationChange;
324
- }, [onValidationChange]);
325
-
326
- // Track last sent validation state to prevent duplicate updates
327
- const lastSentValidationStateRef = useRef(null);
328
-
329
- // Store validation ref to access current value without triggering re-renders
330
- const validationRef = useRef(validation);
331
- validationRef.current = validation;
332
-
333
- // Extract STABLE primitive values from validation for dependency array
334
- const isValidating = validation?.isValidating;
335
- const validationTotalErrors = validation?.summary?.totalErrors || 0;
336
- const validationTotalWarnings = validation?.summary?.totalWarnings || 0;
337
- const validationLastValidated = validation?.lastValidated?.getTime?.() || null;
338
- // Only Rule Group #1 (Input & Sanitization) blocks; use for UI gating (Done/Update/Preview/Test)
339
- const validationHasBlockingErrors = validation?.hasBlockingErrors || false;
340
-
341
- // Notify parent component when validation state changes
342
- useEffect(() => {
343
- if (!onValidationChangeRef.current) {
344
- return;
345
- }
346
-
347
- // Skip if still validating (wait for validation to complete)
348
- if (isValidating) {
349
- return;
350
- }
351
-
352
- // Calculate issue counts: Errors (blocking) and Warnings (non-blocking)
353
- const calculateIssueCounts = () => {
354
- const currentValidation = validationRef.current;
355
- if (!currentValidation || typeof currentValidation.getAllIssues !== 'function') {
356
- return { errors: 0, warnings: 0, total: 0 };
357
- }
358
- return countIssuesBySeverity(currentValidation.getAllIssues());
359
- };
360
-
361
- const issueCounts = calculateIssueCounts();
362
- const isContentEmpty = !currentContent || currentContent.trim() === '';
363
-
364
- // hasErrors = only Rule Group #1 (Input & Sanitization) – gates Done/Update/Preview/Test
365
- const newState = {
366
- isContentEmpty,
367
- issueCounts,
368
- validationComplete: true,
369
- hasErrors: validationRef.current?.hasBlockingErrors ?? false,
370
- };
371
-
372
- // Only call callback if state actually changed (prevent infinite loops)
373
- const lastState = lastSentValidationStateRef.current;
374
- const hasChanged = !lastState
375
- || lastState.isContentEmpty !== newState.isContentEmpty
376
- || lastState.validationComplete !== newState.validationComplete
377
- || lastState.hasErrors !== newState.hasErrors
378
- || lastState.issueCounts?.total !== newState.issueCounts?.total
379
- || lastState.issueCounts?.errors !== newState.issueCounts?.errors
380
- || lastState.issueCounts?.warnings !== newState.issueCounts?.warnings;
381
-
382
- if (hasChanged) {
383
- lastSentValidationStateRef.current = newState;
384
- onValidationChangeRef.current(newState);
385
- }
386
- }, [isValidating, validationTotalErrors, validationTotalWarnings, validationLastValidated, validationHasBlockingErrors, currentContent, apiValidationErrors]);
196
+ // Handle label insertion at cursor position
197
+ const handleLabelInsert = useCallback((label, position) => {
198
+ // With injectIntl({ forwardRef: true }), ref points directly to CodeEditorPane
199
+ const activeEditorRef = getActiveEditorRef();
200
+ const editor = activeEditorRef.current;
387
201
 
388
- // Send initial state on mount to ensure parent has correct initial button state
389
- const hasInitializedRef = useRef(false);
390
- useEffect(() => {
391
- if (hasInitializedRef.current || !onValidationChangeRef.current) {
392
- return;
393
- }
394
- hasInitializedRef.current = true;
395
-
396
- // Send initial state with validationComplete=false to indicate validation pending
397
- const isContentEmpty = !currentContent || currentContent.trim() === '';
398
- onValidationChangeRef.current({
399
- isContentEmpty,
400
- issueCounts: { errors: 0, warnings: 0, total: 0 },
401
- validationComplete: false, // Validation hasn't run yet
402
- hasErrors: false,
403
- });
404
- }, [currentContent]); // Only depend on currentContent to run on initial content load
405
-
406
- // Force validation state recalculation when API validation errors change
407
- // This ensures that API errors are included in issue counts and displayed in ValidationErrorDisplay
408
- useEffect(() => {
409
- if (!onValidationChangeRef.current) {
202
+ if (!editor) {
203
+ CapNotification.warning({
204
+ message: intl.formatMessage(messages.labelInsertError),
205
+ description: intl.formatMessage(messages.editorNotReady),
206
+ duration: 3
207
+ });
410
208
  return;
411
209
  }
412
210
 
413
- // Skip if still validating (wait for validation to complete)
414
- if (validation?.isValidating) {
211
+ // Check if the required methods exist
212
+ if (typeof editor?.insertText !== 'function') {
213
+ CapNotification.error({
214
+ message: intl.formatMessage(messages.labelInsertError),
215
+ description: intl.formatMessage(messages.editorMethodNotAvailable),
216
+ duration: 4
217
+ });
415
218
  return;
416
219
  }
417
220
 
418
- // Recalculate issue counts (Errors + Warnings) including API errors
419
- const issueCounts = !validation || typeof validation.getAllIssues !== 'function'
420
- ? { errors: 0, warnings: 0, total: 0 }
421
- : countIssuesBySeverity(validation.getAllIssues());
422
- const isContentEmpty = !currentContent || currentContent.trim() === '';
423
-
424
- const newState = {
425
- isContentEmpty,
426
- issueCounts,
427
- validationComplete: true,
428
- hasErrors: validation?.hasBlockingErrors ?? false,
429
- };
430
-
431
- const lastState = lastSentValidationStateRef.current;
432
- const hasChanged = !lastState
433
- || lastState.hasErrors !== newState.hasErrors
434
- || lastState.issueCounts?.total !== newState.issueCounts?.total
435
- || lastState.issueCounts?.errors !== newState.issueCounts?.errors
436
- || lastState.issueCounts?.warnings !== newState.issueCounts?.warnings;
221
+ try {
222
+ // Get current cursor position or use provided position
223
+ const cursor = position !== undefined
224
+ ? position
225
+ : (typeof editor?.getCursor === 'function' ? editor.getCursor() : 0);
437
226
 
438
- if (hasChanged) {
439
- lastSentValidationStateRef.current = newState;
440
- onValidationChangeRef.current(newState);
441
- }
442
- }, [apiValidationErrors, validation, currentContent, onValidationChangeRef]);
227
+ // Insert label at cursor position
228
+ editor.insertText(label, cursor);
443
229
 
444
- // Handle label insertion at cursor position
445
- // Note: This is called for notification purposes only when tag is inserted via CodeEditorPane
446
- // The actual insertion happens in CodeEditorPane.handleTagSelect
447
- const handleLabelInsert = useCallback((label, position) => {
448
- // If position is explicitly null, it means the editor wasn't ready when tag was selected
449
- // In this case, CodeEditorPane couldn't insert the tag, so we should try here
450
- if (position === null) {
451
- // With injectIntl({ forwardRef: true }), ref points directly to CodeEditorPane
452
- const activeEditorRef = getActiveEditorRef();
453
- const editor = activeEditorRef.current;
454
-
455
- if (!editor) {
456
- CapNotification.warning({
457
- message: intl.formatMessage(messages.labelInsertError),
458
- description: intl.formatMessage(messages.editorNotReady),
459
- duration: 3,
460
- });
461
- return;
462
- }
230
+ // Focus the editor if focus method is available
231
+ editor?.focus?.();
463
232
 
464
- // Check if the required methods exist
465
- if (typeof editor?.insertText !== 'function') {
466
- CapNotification.error({
467
- message: intl.formatMessage(messages.labelInsertError),
468
- description: intl.formatMessage(messages.editorMethodNotAvailable),
469
- duration: 4,
470
- });
471
- return;
472
- }
473
-
474
- try {
475
- // Get current cursor position
476
- const cursor = typeof editor?.getCursor === 'function' ? editor.getCursor() : 0;
477
-
478
- // Insert label at cursor position
479
- editor.insertText(label, cursor);
480
-
481
- // Focus the editor if focus method is available
482
- editor?.focus?.();
483
-
484
- // Show success notification
485
- CapNotification.success({
486
- message: intl.formatMessage(messages.labelInserted),
487
- description: intl.formatMessage(messages.labelInsertedDescription, { label }),
488
- duration: 2,
489
- });
490
- } catch (error) {
491
- CapNotification.error({
492
- message: intl.formatMessage(messages.labelInsertError),
493
- description: error.message,
494
- duration: 4,
495
- });
496
- }
497
- } else {
498
- // Tag was already inserted by CodeEditorPane (position is a valid number)
499
- // Just show success notification - no need to access editor
233
+ // Show success notification
500
234
  CapNotification.success({
501
235
  message: intl.formatMessage(messages.labelInserted),
502
236
  description: intl.formatMessage(messages.labelInsertedDescription, { label }),
503
- duration: 2,
237
+ duration: 2
238
+ });
239
+ } catch (error) {
240
+ CapNotification.error({
241
+ message: intl.formatMessage(messages.labelInsertError),
242
+ description: error.message,
243
+ duration: 4
504
244
  });
505
245
  }
506
246
  }, [intl, getActiveEditorRef]);
@@ -509,23 +249,23 @@ const HTMLEditor = forwardRef(({
509
249
  const handleSave = useCallback(() => {
510
250
  try {
511
251
  const { html, css, javascript } = content?.content || {};
512
- const contentToSave = { html, css, javascript };
252
+ const currentContent = { html, css, javascript };
513
253
 
514
254
  if (onSave) {
515
- onSave(contentToSave);
255
+ onSave(currentContent);
516
256
  }
517
257
 
518
258
  markAsSaved?.();
519
259
 
520
260
  CapNotification.success({
521
261
  message: intl.formatMessage(messages.contentSaved),
522
- duration: 2,
262
+ duration: 2
523
263
  });
524
264
  } catch (error) {
525
265
  CapNotification.error({
526
266
  message: intl.formatMessage(messages.saveError),
527
267
  description: error.message,
528
- duration: 4,
268
+ duration: 4
529
269
  });
530
270
  }
531
271
  }, [content, onSave, intl, markAsSaved]);
@@ -536,27 +276,21 @@ const HTMLEditor = forwardRef(({
536
276
  const editorInstance = activeEditorRef.current;
537
277
  const { line, column = 1 } = error || {};
538
278
 
539
- // Notify parent that user acknowledged errors by clicking redirection icon
540
- // This enables the buttons even if we can't navigate to a specific line
541
- if (onErrorAcknowledged) {
542
- onErrorAcknowledged();
543
- }
544
-
545
- if (editorInstance) {
279
+ if (editorInstance && line) {
546
280
  try {
547
- // If line number exists, navigate to it; otherwise just focus the editor
548
- if (line && editorInstance?.navigateToLine) {
281
+ // Access the CodeMirror view through the exposed ref methods
282
+ if (editorInstance?.navigateToLine) {
549
283
  editorInstance.navigateToLine(line, column);
550
284
  } else {
551
- // For API errors without line numbers, just focus the editor
552
285
  editorInstance?.focus?.();
286
+ // Fallback: just focus the editor if navigation isn't available
553
287
  }
554
288
  } catch (err) {
555
289
  // Fallback: just focus the editor
556
290
  editorInstance?.focus?.();
557
291
  }
558
292
  }
559
- }, [getActiveEditorRef, onErrorAcknowledged]);
293
+ }, [getActiveEditorRef]);
560
294
 
561
295
  // Handle fullscreen modal
562
296
  const handleOpenFullscreen = useCallback(() => {
@@ -573,7 +307,6 @@ const HTMLEditor = forwardRef(({
573
307
  content,
574
308
  layout,
575
309
  validation,
576
- isLiquidEnabled,
577
310
  editorRef: getActiveEditorRef(),
578
311
  handleLabelInsert,
579
312
  handleSave,
@@ -586,14 +319,13 @@ const HTMLEditor = forwardRef(({
586
319
  switchDevice,
587
320
  toggleContentSync,
588
321
  getDeviceContent,
589
- layoutType,
590
- }),
322
+ layoutType
323
+ })
591
324
  }), [
592
325
  variant,
593
326
  content,
594
327
  layout,
595
328
  validation,
596
- isLiquidEnabled,
597
329
  getActiveEditorRef,
598
330
  handleLabelInsert,
599
331
  handleSave,
@@ -604,7 +336,7 @@ const HTMLEditor = forwardRef(({
604
336
  switchDevice,
605
337
  toggleContentSync,
606
338
  getDeviceContent,
607
- layoutType,
339
+ layoutType
608
340
  ]);
609
341
 
610
342
  // Loading state
@@ -616,14 +348,9 @@ const HTMLEditor = forwardRef(({
616
348
  );
617
349
  }
618
350
 
619
- // Add library-mode class when not in full mode
620
- // Note: isFullMode defaults to true, so library mode is when isFullMode === false
621
- const isLibraryMode = isFullMode === false;
622
- const editorClassName = `html-editor html-editor--${variant} ${isLibraryMode ? 'html-editor--library-mode' : ''} ${className}`.trim();
623
-
624
351
  return (
625
352
  <EditorProvider value={contextValue}>
626
- <div className={editorClassName} {...props}>
353
+ <div className={`html-editor html-editor--${variant} ${className}`} {...props}>
627
354
  {/* Editor Toolbar - Conditional based on variant */}
628
355
  {variant === HTML_EDITOR_VARIANTS.EMAIL ? (
629
356
  <EditorToolbar
@@ -660,23 +387,19 @@ const HTMLEditor = forwardRef(({
660
387
  ref={mainEditorRef}
661
388
  readOnly={readOnly}
662
389
  onLabelInsert={handleLabelInsert}
663
- onErrorClick={handleValidationErrorClick}
664
- tags={tags}
665
- injectedTags={injectedTags}
666
- location={location}
667
- eventContextTags={eventContextTags}
668
- selectedOfferDetails={selectedOfferDetails}
669
- channel={channel}
670
- userLocale={userLocale}
671
- moduleFilterEnabled={moduleFilterEnabled}
672
- onTagContextChange={onTagContextChange}
673
- onTagSelect={onTagSelect}
674
- onContextChange={handleContextChange}
675
390
  />
676
391
 
677
392
  {/* Preview Pane */}
678
393
  <PreviewPane />
679
394
  </SplitContainer>
395
+
396
+ {/* Validation Display - Full Width Below Split Container */}
397
+ <ValidationErrorDisplay
398
+ validation={validation}
399
+ onErrorClick={handleValidationErrorClick}
400
+ variant={variant}
401
+ className="html-editor-validation"
402
+ />
680
403
  </CapRow>
681
404
 
682
405
  {/* Fullscreen Modal */}
@@ -685,20 +408,20 @@ const HTMLEditor = forwardRef(({
685
408
  visible={isFullscreenModalOpen}
686
409
  onCancel={handleCloseFullscreen}
687
410
  footer={null}
688
- maskClosable
411
+ maskClosable={false}
689
412
  centered
690
413
  closable={false}
691
- width="93vw"
414
+ width={"90vw"}
692
415
  className="html-editor-fullscreen-modal"
693
416
  >
694
- <CapRow className={`html-editor-fullscreen html-editor-fullscreen--${variant} ${isLibraryMode ? 'html-editor-fullscreen--library-mode' : ''}`}>
417
+ <CapRow className="html-editor-fullscreen">
695
418
  {/* Editor Toolbar - Conditional based on variant */}
696
419
  {variant === HTML_EDITOR_VARIANTS.EMAIL ? (
697
420
  <EditorToolbar
698
- showFullscreenButton // Show fullscreen button in modal to allow closing
421
+ showFullscreenButton={true} // Show fullscreen button in modal to allow closing
699
422
  onLabelInsert={handleLabelInsert}
700
423
  onSave={handleSave}
701
- isFullscreenMode
424
+ isFullscreenMode={true}
702
425
  onToggleFullscreen={handleCloseFullscreen} // Close modal when clicked in fullscreen mode
703
426
  />
704
427
  ) : (
@@ -711,10 +434,10 @@ const HTMLEditor = forwardRef(({
711
434
  onKeepContentSameChange={toggleContentSync}
712
435
  />
713
436
  <EditorToolbar
714
- showFullscreenButton // Show fullscreen button in modal to allow closing
437
+ showFullscreenButton={true} // Show fullscreen button in modal to allow closing
715
438
  onLabelInsert={handleLabelInsert}
716
439
  onSave={handleSave}
717
- isFullscreenMode
440
+ isFullscreenMode={true}
718
441
  onToggleFullscreen={handleCloseFullscreen} // Close modal when clicked in fullscreen mode
719
442
  variant={variant}
720
443
  showTitle={false} // Hide title in InApp variant
@@ -729,30 +452,28 @@ const HTMLEditor = forwardRef(({
729
452
  <CodeEditorPane
730
453
  ref={modalEditorRef}
731
454
  readOnly={readOnly}
732
- isFullscreenMode
455
+ isFullscreenMode={true}
733
456
  onLabelInsert={handleLabelInsert}
734
- onErrorClick={handleValidationErrorClick}
735
- tags={tags}
736
- injectedTags={injectedTags}
737
- location={location}
738
- eventContextTags={eventContextTags}
739
- selectedOfferDetails={selectedOfferDetails}
740
- channel={channel}
741
- userLocale={userLocale}
742
- moduleFilterEnabled={moduleFilterEnabled}
743
- onTagContextChange={onTagContextChange}
744
457
  />
745
458
 
746
- {/* Preview Pane */}
747
- <PreviewPane isFullscreenMode isModalContext />
748
- </SplitContainer>
459
+ {/* Preview Pane */}
460
+ <PreviewPane isFullscreenMode={true} isModalContext={true} />
461
+ </SplitContainer>
462
+
463
+ {/* Validation Display in Modal */}
464
+ <ValidationErrorDisplay
465
+ validation={validation}
466
+ onErrorClick={handleValidationErrorClick}
467
+ variant={variant}
468
+ className="html-editor-validation"
469
+ />
749
470
  </CapRow>
750
471
  </CapRow>
751
472
  </CapModal>
752
473
  </div>
753
474
  </EditorProvider>
754
475
  );
755
- });
476
+ };
756
477
 
757
478
  HTMLEditor.propTypes = {
758
479
  intl: intlShape.isRequired,
@@ -760,7 +481,7 @@ HTMLEditor.propTypes = {
760
481
  layoutType: PropTypes.string, // Layout type for InApp variant
761
482
  initialContent: PropTypes.oneOfType([
762
483
  PropTypes.string,
763
- PropTypes.objectOf(PropTypes.string), // Per-device content for INAPP variant
484
+ PropTypes.objectOf(PropTypes.string) // Per-device content for INAPP variant
764
485
  ]),
765
486
  onSave: PropTypes.func,
766
487
  onContentChange: PropTypes.func,
@@ -768,33 +489,11 @@ HTMLEditor.propTypes = {
768
489
  readOnly: PropTypes.bool,
769
490
  showFullscreenButton: PropTypes.bool,
770
491
  autoSave: PropTypes.bool,
771
- autoSaveInterval: PropTypes.number,
772
- // Tag-related props - tags are fetched and managed by parent component
773
- tags: PropTypes.array,
774
- injectedTags: PropTypes.object,
775
- location: PropTypes.object,
776
- eventContextTags: PropTypes.array,
777
- selectedOfferDetails: PropTypes.array,
778
- channel: PropTypes.string,
779
- userLocale: PropTypes.string,
780
- moduleFilterEnabled: PropTypes.bool,
781
- onTagContextChange: PropTypes.func, // Required - parent must handle tag fetching
782
- onTagSelect: PropTypes.func,
783
- onContextChange: PropTypes.func, // Deprecated: use globalActions instead
784
- globalActions: PropTypes.object,
785
- isLiquidEnabled: PropTypes.bool, // Controls Liquid tab visibility in validation
786
- isFullMode: PropTypes.bool, // Full mode vs library mode
787
- onErrorAcknowledged: PropTypes.func, // Callback when user clicks redirection icon to acknowledge errors
788
- onValidationChange: PropTypes.func, // Callback when validation state changes
789
- apiValidationErrors: PropTypes.shape({
790
- liquidErrors: PropTypes.arrayOf(PropTypes.string),
791
- standardErrors: PropTypes.arrayOf(PropTypes.string),
792
- }), // API validation errors from validateLiquidTemplateContent
492
+ autoSaveInterval: PropTypes.number
793
493
  };
794
494
 
795
495
  HTMLEditor.defaultProps = {
796
496
  variant: HTML_EDITOR_VARIANTS.EMAIL, // Default to email variant
797
- layoutType: null,
798
497
  initialContent: null, // Will use default from useEditorContent hook
799
498
  onSave: null,
800
499
  onContentChange: null,
@@ -802,26 +501,8 @@ HTMLEditor.defaultProps = {
802
501
  readOnly: false,
803
502
  showFullscreenButton: true,
804
503
  autoSave: true,
805
- autoSaveInterval: 30000,
806
- // Tag-related defaults - tags are fetched and managed by parent component
807
- tags: [],
808
- injectedTags: {},
809
- location: null,
810
- eventContextTags: [],
811
- selectedOfferDetails: [],
812
- channel: null,
813
- userLocale: 'en',
814
- moduleFilterEnabled: true,
815
- onTagContextChange: null, // Parent component should provide this
816
- onTagSelect: null,
817
- onContextChange: null,
818
- globalActions: null, // Redux actions for API calls
819
- isLiquidEnabled: false,
820
- isFullMode: true, // Default to full mode
821
- onErrorAcknowledged: null, // Callback when user clicks redirection icon to acknowledge errors
822
- onValidationChange: null, // Callback when validation state changes
823
- apiValidationErrors: null, // API validation errors from validateLiquidTemplateContent
504
+ autoSaveInterval: 30000
824
505
  };
825
506
 
826
- // Export with injectIntl - HTMLEditor now uses forwardRef internally
827
- export default injectIntl(HTMLEditor);
507
+ // Export with forwardRef to allow direct access to CodeEditorPane via ref
508
+ export default injectIntl(HTMLEditor, { forwardRef: true });