@capillarytech/creatives-library 8.0.242-alpha.0 → 8.0.242-alpha.10

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 (216) hide show
  1. package/assets/Android.png +0 -0
  2. package/assets/iOS.png +0 -0
  3. package/constants/unified.js +2 -1
  4. package/initialReducer.js +2 -0
  5. package/package.json +1 -1
  6. package/sagas/__tests__/assetPolling.test.js +74 -3
  7. package/sagas/assetPolling.js +8 -1
  8. package/services/api.js +10 -5
  9. package/services/tests/api.test.js +18 -0
  10. package/translations/en.json +0 -1
  11. package/utils/common.js +5 -0
  12. package/utils/commonUtils.js +14 -1
  13. package/utils/tests/commonUtil.test.js +224 -0
  14. package/utils/transformTemplateConfig.js +0 -10
  15. package/utils/transformerUtils.js +0 -42
  16. package/v2Components/CapDeviceContent/index.js +61 -56
  17. package/v2Components/CapImageUpload/constants.js +0 -2
  18. package/v2Components/CapImageUpload/index.js +14 -54
  19. package/v2Components/CapImageUpload/index.scss +1 -4
  20. package/v2Components/CapImageUpload/messages.js +0 -4
  21. package/v2Components/CapTagList/index.js +6 -1
  22. package/v2Components/CapTagListWithInput/index.js +5 -1
  23. package/v2Components/CapTagListWithInput/messages.js +1 -1
  24. package/v2Components/CapWhatsappCTA/tests/index.test.js +5 -0
  25. package/v2Components/ErrorInfoNote/index.js +412 -72
  26. package/v2Components/ErrorInfoNote/messages.js +22 -0
  27. package/v2Components/ErrorInfoNote/style.scss +279 -2
  28. package/v2Components/HtmlEditor/HTMLEditor.js +220 -91
  29. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1132 -133
  30. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +17 -12
  31. package/v2Components/HtmlEditor/_htmlEditor.scss +107 -45
  32. package/v2Components/HtmlEditor/_index.lazy.scss +1 -1
  33. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +13 -101
  34. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +148 -139
  35. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +2 -1
  36. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
  37. package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +9 -0
  38. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +1 -1
  39. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +22 -0
  40. package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +4 -7
  41. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +35 -45
  42. package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +1 -3
  43. package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
  44. package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +7 -6
  45. package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +3 -6
  46. package/v2Components/HtmlEditor/components/PreviewPane/index.js +10 -11
  47. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  48. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +70 -72
  49. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +49 -31
  50. package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +254 -0
  51. package/v2Components/HtmlEditor/components/ValidationTabs/index.js +362 -0
  52. package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +51 -0
  53. package/v2Components/HtmlEditor/constants.js +29 -20
  54. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +373 -16
  55. package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
  56. package/v2Components/HtmlEditor/hooks/useInAppContent.js +88 -146
  57. package/v2Components/HtmlEditor/index.js +1 -1
  58. package/v2Components/HtmlEditor/messages.js +95 -85
  59. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +99 -101
  60. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +23 -25
  61. package/v2Components/HtmlEditor/utils/validationAdapter.js +34 -41
  62. package/v2Components/MobilePushPreviewV2/index.js +32 -7
  63. package/v2Components/TemplatePreview/_templatePreview.scss +44 -24
  64. package/v2Components/TemplatePreview/index.js +47 -32
  65. package/v2Components/TemplatePreview/messages.js +4 -0
  66. package/v2Components/TestAndPreviewSlidebox/index.js +31 -25
  67. package/v2Containers/App/constants.js +0 -5
  68. package/v2Containers/BeeEditor/index.js +82 -80
  69. package/v2Containers/BeePopupEditor/constants.js +10 -0
  70. package/v2Containers/BeePopupEditor/index.js +193 -0
  71. package/v2Containers/BeePopupEditor/tests/index.test.js +627 -0
  72. package/v2Containers/Cap/tests/__snapshots__/index.test.js.snap +0 -1
  73. package/v2Containers/CreativesContainer/SlideBoxContent.js +148 -120
  74. package/v2Containers/CreativesContainer/SlideBoxFooter.js +9 -3
  75. package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -2
  76. package/v2Containers/CreativesContainer/constants.js +1 -2
  77. package/v2Containers/CreativesContainer/index.js +173 -193
  78. package/v2Containers/CreativesContainer/messages.js +4 -4
  79. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +38 -50
  80. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +36 -0
  81. package/v2Containers/Email/actions.js +7 -0
  82. package/v2Containers/Email/constants.js +5 -1
  83. package/v2Containers/Email/index.js +13 -0
  84. package/v2Containers/Email/messages.js +32 -0
  85. package/v2Containers/Email/reducer.js +12 -1
  86. package/v2Containers/Email/sagas.js +41 -6
  87. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +2 -0
  88. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1046 -0
  89. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +193 -7
  90. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +40 -74
  91. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +2 -67
  92. package/v2Containers/EmailWrapper/constants.js +2 -0
  93. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +436 -67
  94. package/v2Containers/EmailWrapper/index.js +99 -23
  95. package/v2Containers/EmailWrapper/messages.js +61 -1
  96. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +111 -77
  97. package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +376 -0
  98. package/v2Containers/InApp/__tests__/sagas.test.js +363 -0
  99. package/v2Containers/InApp/actions.js +7 -0
  100. package/v2Containers/InApp/constants.js +20 -4
  101. package/v2Containers/InApp/index.js +801 -357
  102. package/v2Containers/InApp/index.scss +4 -3
  103. package/v2Containers/InApp/messages.js +7 -3
  104. package/v2Containers/InApp/reducer.js +21 -3
  105. package/v2Containers/InApp/sagas.js +29 -9
  106. package/v2Containers/InApp/selectors.js +25 -5
  107. package/v2Containers/InApp/tests/index.test.js +154 -50
  108. package/v2Containers/InApp/tests/reducer.test.js +34 -0
  109. package/v2Containers/InApp/tests/sagas.test.js +61 -9
  110. package/v2Containers/InApp/tests/selectors.test.js +612 -0
  111. package/v2Containers/InAppWrapper/components/InAppWrapperView.js +162 -0
  112. package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +267 -0
  113. package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +9 -0
  114. package/v2Containers/InAppWrapper/constants.js +16 -0
  115. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +473 -0
  116. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +198 -0
  117. package/v2Containers/InAppWrapper/index.js +148 -0
  118. package/v2Containers/InAppWrapper/messages.js +49 -0
  119. package/v2Containers/InappAdvance/index.js +1099 -0
  120. package/v2Containers/InappAdvance/index.scss +10 -0
  121. package/v2Containers/InappAdvance/tests/index.test.js +448 -0
  122. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -3
  123. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -2
  124. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -25
  125. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -18
  126. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -46
  127. package/v2Containers/SmsTrai/Create/tests/__snapshots__/index.test.js.snap +0 -4
  128. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -8
  129. package/v2Containers/TagList/index.js +67 -1
  130. package/v2Containers/Templates/ChannelTypeIllustration.js +1 -13
  131. package/v2Containers/Templates/_templates.scss +56 -200
  132. package/v2Containers/Templates/actions.js +1 -2
  133. package/v2Containers/Templates/constants.js +0 -1
  134. package/v2Containers/Templates/index.js +124 -277
  135. package/v2Containers/Templates/messages.js +4 -24
  136. package/v2Containers/Templates/reducer.js +0 -2
  137. package/v2Containers/Templates/tests/index.test.js +0 -10
  138. package/v2Containers/TemplatesV2/index.js +2 -3
  139. package/v2Containers/TemplatesV2/messages.js +0 -4
  140. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +35 -132
  141. package/v2Components/CapImageUrlUpload/constants.js +0 -19
  142. package/v2Components/CapImageUrlUpload/index.js +0 -455
  143. package/v2Components/CapImageUrlUpload/index.scss +0 -35
  144. package/v2Components/CapImageUrlUpload/messages.js +0 -47
  145. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +0 -214
  146. package/v2Containers/WebPush/Create/components/ButtonForm.js +0 -175
  147. package/v2Containers/WebPush/Create/components/ButtonItem.js +0 -101
  148. package/v2Containers/WebPush/Create/components/ButtonList.js +0 -144
  149. package/v2Containers/WebPush/Create/components/_buttons.scss +0 -246
  150. package/v2Containers/WebPush/Create/components/tests/ButtonForm.test.js +0 -554
  151. package/v2Containers/WebPush/Create/components/tests/ButtonItem.test.js +0 -607
  152. package/v2Containers/WebPush/Create/components/tests/ButtonList.test.js +0 -633
  153. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonForm.test.js.snap +0 -666
  154. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonItem.test.js.snap +0 -74
  155. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonList.test.js.snap +0 -80
  156. package/v2Containers/WebPush/Create/index.js +0 -1755
  157. package/v2Containers/WebPush/Create/index.scss +0 -123
  158. package/v2Containers/WebPush/Create/messages.js +0 -199
  159. package/v2Containers/WebPush/Create/preview/DevicePreviewContent.js +0 -241
  160. package/v2Containers/WebPush/Create/preview/NotificationContainer.js +0 -290
  161. package/v2Containers/WebPush/Create/preview/PreviewContent.js +0 -81
  162. package/v2Containers/WebPush/Create/preview/PreviewControls.js +0 -240
  163. package/v2Containers/WebPush/Create/preview/PreviewDisclaimer.js +0 -23
  164. package/v2Containers/WebPush/Create/preview/WebPushPreview.js +0 -144
  165. package/v2Containers/WebPush/Create/preview/assets/Light.svg +0 -53
  166. package/v2Containers/WebPush/Create/preview/assets/Top.svg +0 -5
  167. package/v2Containers/WebPush/Create/preview/assets/chrome-icon.png +0 -0
  168. package/v2Containers/WebPush/Create/preview/assets/edge-icon.png +0 -0
  169. package/v2Containers/WebPush/Create/preview/assets/firefox-icon.svg +0 -106
  170. package/v2Containers/WebPush/Create/preview/assets/iOS.svg +0 -26
  171. package/v2Containers/WebPush/Create/preview/assets/opera-icon.svg +0 -18
  172. package/v2Containers/WebPush/Create/preview/assets/safari-icon.svg +0 -29
  173. package/v2Containers/WebPush/Create/preview/components/AndroidMobileChromeHeader.js +0 -44
  174. package/v2Containers/WebPush/Create/preview/components/AndroidMobileExpanded.js +0 -110
  175. package/v2Containers/WebPush/Create/preview/components/IOSHeader.js +0 -45
  176. package/v2Containers/WebPush/Create/preview/components/NotificationExpandedContent.js +0 -72
  177. package/v2Containers/WebPush/Create/preview/components/NotificationHeader.js +0 -55
  178. package/v2Containers/WebPush/Create/preview/components/WindowsChromeExpanded.js +0 -70
  179. package/v2Containers/WebPush/Create/preview/components/tests/AndroidMobileExpanded.test.js +0 -512
  180. package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/AndroidMobileExpanded.test.js.snap +0 -77
  181. package/v2Containers/WebPush/Create/preview/config/notificationMappings.js +0 -527
  182. package/v2Containers/WebPush/Create/preview/constants.js +0 -162
  183. package/v2Containers/WebPush/Create/preview/notification-container.scss +0 -104
  184. package/v2Containers/WebPush/Create/preview/preview.scss +0 -409
  185. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-chrome.scss +0 -300
  186. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-edge.scss +0 -12
  187. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-firefox.scss +0 -12
  188. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-opera.scss +0 -12
  189. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-chrome.scss +0 -303
  190. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-edge.scss +0 -11
  191. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-firefox.scss +0 -11
  192. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-opera.scss +0 -11
  193. package/v2Containers/WebPush/Create/preview/styles/_base.scss +0 -188
  194. package/v2Containers/WebPush/Create/preview/styles/_ios.scss +0 -106
  195. package/v2Containers/WebPush/Create/preview/styles/_ipados.scss +0 -107
  196. package/v2Containers/WebPush/Create/preview/styles/_macos-chrome.scss +0 -75
  197. package/v2Containers/WebPush/Create/preview/styles/_windows-chrome.scss +0 -174
  198. package/v2Containers/WebPush/Create/preview/tests/DevicePreviewContent.test.js +0 -909
  199. package/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +0 -1077
  200. package/v2Containers/WebPush/Create/preview/tests/PreviewControls.test.js +0 -723
  201. package/v2Containers/WebPush/Create/preview/tests/WebPushPreview.test.js +0 -943
  202. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/DevicePreviewContent.test.js.snap +0 -128
  203. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/NotificationContainer.test.js.snap +0 -121
  204. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/PreviewControls.test.js.snap +0 -144
  205. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/WebPushPreview.test.js.snap +0 -127
  206. package/v2Containers/WebPush/Create/utils/urlValidation.js +0 -116
  207. package/v2Containers/WebPush/Create/utils/urlValidation.test.js +0 -449
  208. package/v2Containers/WebPush/actions.js +0 -60
  209. package/v2Containers/WebPush/constants.js +0 -108
  210. package/v2Containers/WebPush/index.js +0 -2
  211. package/v2Containers/WebPush/reducer.js +0 -104
  212. package/v2Containers/WebPush/sagas.js +0 -119
  213. package/v2Containers/WebPush/selectors.js +0 -65
  214. package/v2Containers/WebPush/tests/reducer.test.js +0 -863
  215. package/v2Containers/WebPush/tests/sagas.test.js +0 -566
  216. package/v2Containers/WebPush/tests/selectors.test.js +0 -960
@@ -0,0 +1,362 @@
1
+ /**
2
+ * ValidationTabs Component
3
+ *
4
+ * Displays validation errors in a tabbed interface with 3 categories:
5
+ * - HTML issues: General HTML/CSS validation errors and warnings
6
+ * - Label issues: Tag syntax errors (open/close tags, attributes, brackets)
7
+ * - Liquid issues: Liquid expression errors (only when liquid feature enabled)
8
+ */
9
+
10
+ import React, { useState, useMemo } from 'react';
11
+ import PropTypes from 'prop-types';
12
+ import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
13
+
14
+ // Cap UI Library
15
+ import CapRow from '@capillarytech/cap-ui-library/CapRow';
16
+ import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
17
+ import CapTab from '@capillarytech/cap-ui-library/CapTab';
18
+ import CapTooltip from '@capillarytech/cap-ui-library/CapTooltip';
19
+
20
+ // Messages
21
+ import messages from './messages';
22
+
23
+ // Styles
24
+ import './_validationTabs.scss';
25
+
26
+ // Constants for issue sources
27
+ const ISSUE_SOURCES = {
28
+ HTMLHINT: 'htmlhint',
29
+ CSS_VALIDATOR: 'css-validator',
30
+ CUSTOM: 'custom',
31
+ SECURITY: 'security',
32
+ LIQUID: 'liquid-validator',
33
+ };
34
+
35
+ // Label issue patterns - syntax errors related to tags
36
+ const LABEL_ISSUE_PATTERNS = [
37
+ 'tag must be paired',
38
+ 'open tag match failed',
39
+ 'closed tag match failed',
40
+ 'unclosed',
41
+ 'missing required',
42
+ 'tag-pair',
43
+ 'attr-value-not-empty',
44
+ 'attr-no-duplication',
45
+ 'tag-self-close',
46
+ 'spec-char-escape',
47
+ 'tagname-lowercase',
48
+ 'attr-lowercase',
49
+ 'id-unique',
50
+ 'src-not-empty',
51
+ 'alt-require',
52
+ ];
53
+
54
+ /**
55
+ * Categorize issues into HTML, Label, and Liquid categories
56
+ */
57
+ const categorizeIssues = (allIssues) => {
58
+ const htmlIssues = [];
59
+ const labelIssues = [];
60
+ const liquidIssues = [];
61
+
62
+ allIssues.forEach((issue) => {
63
+ const { source, rule, message } = issue;
64
+ const messageLower = (message || '').toLowerCase();
65
+ const ruleLower = (rule || '').toLowerCase();
66
+
67
+ // Check if it's a Liquid issue - ONLY by source, not by message content
68
+ // This prevents false positives where HTML errors mention liquid syntax
69
+ if (source === ISSUE_SOURCES.LIQUID) {
70
+ liquidIssues.push(issue);
71
+ return;
72
+ }
73
+
74
+ // Check if it's a Label (tag syntax) issue
75
+ const isLabelIssue = LABEL_ISSUE_PATTERNS.some(
76
+ (pattern) => messageLower.includes(pattern.toLowerCase())
77
+ || ruleLower.includes(pattern.toLowerCase()),
78
+ );
79
+
80
+ if (isLabelIssue) {
81
+ labelIssues.push(issue);
82
+ return;
83
+ }
84
+
85
+ // Default to HTML issues
86
+ htmlIssues.push(issue);
87
+ });
88
+
89
+ return { htmlIssues, labelIssues, liquidIssues };
90
+ };
91
+
92
+ /**
93
+ * Get icon based on severity
94
+ */
95
+ const getSeverityIcon = (severity) => {
96
+ if (severity === 'warning') {
97
+ return <CapIcon type="alert-warning" className="validation-tabs__icon validation-tabs__icon--warning" />;
98
+ }
99
+ return <CapIcon type="warning-circle" className="validation-tabs__icon validation-tabs__icon--error" />;
100
+ };
101
+
102
+ /**
103
+ * ValidationTabContent - Renders the content for each tab
104
+ */
105
+ const ValidationTabContent = ({
106
+ issues,
107
+ onErrorClick,
108
+ intl,
109
+ }) => {
110
+ if (!issues || issues.length === 0) {
111
+ return null;
112
+ }
113
+
114
+ const handleNavigateClick = (issue, e) => {
115
+ e.stopPropagation();
116
+ if (onErrorClick && issue.line) {
117
+ onErrorClick({
118
+ line: issue.line,
119
+ column: issue.column || 1,
120
+ message: issue.message,
121
+ severity: issue.severity,
122
+ });
123
+ }
124
+ };
125
+
126
+ return (
127
+ <div className="validation-tabs__content">
128
+ {issues.map((issue, index) => {
129
+ const {
130
+ severity, message, line, column, rule,
131
+ } = issue;
132
+ const key = `${message}-${line}-${column}-${index}`;
133
+
134
+ return (
135
+ <div
136
+ key={key}
137
+ className={`validation-tabs__item validation-tabs__item--${severity || 'error'}`}
138
+ >
139
+ <div className="validation-tabs__item-icon">
140
+ {getSeverityIcon(severity)}
141
+ </div>
142
+ <div className="validation-tabs__item-content">
143
+ <span className="validation-tabs__item-message">
144
+ {message}
145
+ </span>
146
+ {line && (
147
+ <span className="validation-tabs__item-location">
148
+ <FormattedMessage
149
+ {...messages.lineChar}
150
+ values={{ line, char: column || 1 }}
151
+ />
152
+ </span>
153
+ )}
154
+ </div>
155
+ {line && (
156
+ <CapTooltip title={`Line ${line}, Char ${column || 1}`}>
157
+ <button
158
+ type="button"
159
+ className="validation-tabs__item-navigate"
160
+ onClick={(e) => handleNavigateClick(issue, e)}
161
+ aria-label={`Line ${line}, Char ${column || 1}`}
162
+ >
163
+ <CapIcon type="redirection" />
164
+ </button>
165
+ </CapTooltip>
166
+ )}
167
+ </div>
168
+ );
169
+ })}
170
+ </div>
171
+ );
172
+ };
173
+
174
+ ValidationTabContent.propTypes = {
175
+ issues: PropTypes.array,
176
+ onErrorClick: PropTypes.func,
177
+ intl: intlShape.isRequired,
178
+ };
179
+
180
+ ValidationTabContent.defaultProps = {
181
+ issues: [],
182
+ onErrorClick: null,
183
+ };
184
+
185
+ /**
186
+ * ValidationTabs Component
187
+ */
188
+ const ValidationTabs = ({
189
+ intl,
190
+ validation,
191
+ onErrorClick,
192
+ onClose,
193
+ isLiquidEnabled,
194
+ className,
195
+ }) => {
196
+ const [activeKey, setActiveKey] = useState(null);
197
+
198
+ // Categorize issues
199
+ const { htmlIssues, labelIssues, liquidIssues } = useMemo(() => {
200
+ if (!validation) {
201
+ return { htmlIssues: [], labelIssues: [], liquidIssues: [] };
202
+ }
203
+
204
+ // Get all issues from validation
205
+ const allIssues = validation.getAllIssues ? validation.getAllIssues() : [];
206
+ const categorized = categorizeIssues(allIssues);
207
+ return categorized;
208
+ }, [validation]);
209
+
210
+ // Calculate counts
211
+ const htmlCount = htmlIssues.length;
212
+ const labelCount = labelIssues.length;
213
+ const liquidCount = liquidIssues.length;
214
+ const totalCount = htmlCount + labelCount + (isLiquidEnabled ? liquidCount : 0);
215
+
216
+ // Set default active key when issues change
217
+ useMemo(() => {
218
+ if (htmlCount > 0 && !activeKey) {
219
+ setActiveKey('html');
220
+ } else if (labelCount > 0 && !activeKey) {
221
+ setActiveKey('label');
222
+ } else if (liquidCount > 0 && isLiquidEnabled && !activeKey) {
223
+ setActiveKey('liquid');
224
+ }
225
+ }, [htmlCount, labelCount, liquidCount, isLiquidEnabled, activeKey]);
226
+
227
+ // Don't render if no issues
228
+ if (totalCount === 0) {
229
+ return null;
230
+ }
231
+
232
+ // Build tab panes (CapTab uses 'panes' with 'tab' and 'content' properties)
233
+ const tabPanes = [];
234
+
235
+ if (htmlCount > 0) {
236
+ tabPanes.push({
237
+ key: 'html',
238
+ tab: (
239
+ <CapTooltip title={`${intl.formatMessage(messages.htmlIssues)} (${htmlCount})`}>
240
+ <span className="validation-tabs__tab-label">
241
+ <FormattedMessage {...messages.htmlIssues} />
242
+ <span className="validation-tabs__tab-count">
243
+ (
244
+ {htmlCount}
245
+ )
246
+ </span>
247
+ </span>
248
+ </CapTooltip>
249
+ ),
250
+ content: (
251
+ <ValidationTabContent
252
+ issues={htmlIssues}
253
+ onErrorClick={onErrorClick}
254
+ intl={intl}
255
+ />
256
+ ),
257
+ });
258
+ }
259
+
260
+ if (labelCount > 0) {
261
+ tabPanes.push({
262
+ key: 'label',
263
+ tab: (
264
+ <CapTooltip title={`${intl.formatMessage(messages.labelIssues)} (${labelCount})`}>
265
+ <span className="validation-tabs__tab-label">
266
+ <FormattedMessage {...messages.labelIssues} />
267
+ <span className="validation-tabs__tab-count">
268
+ (
269
+ {labelCount}
270
+ )
271
+ </span>
272
+ </span>
273
+ </CapTooltip>
274
+ ),
275
+ content: (
276
+ <ValidationTabContent
277
+ issues={labelIssues}
278
+ onErrorClick={onErrorClick}
279
+ intl={intl}
280
+ />
281
+ ),
282
+ });
283
+ }
284
+
285
+ if (isLiquidEnabled && liquidCount > 0) {
286
+ tabPanes.push({
287
+ key: 'liquid',
288
+ tab: (
289
+ <CapTooltip title={`${intl.formatMessage(messages.liquidIssues)} (${liquidCount})`}>
290
+ <span className="validation-tabs__tab-label">
291
+ <FormattedMessage {...messages.liquidIssues} />
292
+ <span className="validation-tabs__tab-count">
293
+ (
294
+ {liquidCount}
295
+ )
296
+ </span>
297
+ </span>
298
+ </CapTooltip>
299
+ ),
300
+ content: (
301
+ <ValidationTabContent
302
+ issues={liquidIssues}
303
+ onErrorClick={onErrorClick}
304
+ intl={intl}
305
+ />
306
+ ),
307
+ });
308
+ }
309
+
310
+ // Handle close
311
+ const handleClose = () => {
312
+ if (onClose) {
313
+ onClose();
314
+ }
315
+ };
316
+
317
+ return (
318
+ <div className={`validation-tabs ${className || ''}`}>
319
+ <CapRow className="validation-tabs__header">
320
+ <CapTab
321
+ activeKey={activeKey || (tabPanes[0]?.key)}
322
+ onChange={setActiveKey}
323
+ panes={tabPanes}
324
+ className="validation-tabs__tabs"
325
+ />
326
+ <CapRow className="validation-tabs__actions">
327
+ <CapTooltip title={intl.formatMessage(messages.closePanel)}>
328
+ <button
329
+ type="button"
330
+ className="validation-tabs__close"
331
+ onClick={handleClose}
332
+ aria-label={intl.formatMessage(messages.closePanel)}
333
+ >
334
+ <CapIcon type="close" />
335
+ </button>
336
+ </CapTooltip>
337
+ </CapRow>
338
+ </CapRow>
339
+ </div>
340
+ );
341
+ };
342
+
343
+ ValidationTabs.propTypes = {
344
+ intl: intlShape.isRequired,
345
+ validation: PropTypes.shape({
346
+ getAllIssues: PropTypes.func,
347
+ }),
348
+ onErrorClick: PropTypes.func,
349
+ onClose: PropTypes.func,
350
+ isLiquidEnabled: PropTypes.bool,
351
+ className: PropTypes.string,
352
+ };
353
+
354
+ ValidationTabs.defaultProps = {
355
+ validation: null,
356
+ onErrorClick: null,
357
+ onClose: null,
358
+ isLiquidEnabled: false,
359
+ className: '',
360
+ };
361
+
362
+ export default injectIntl(ValidationTabs);
@@ -0,0 +1,51 @@
1
+ /**
2
+ * ValidationTabs Messages
3
+ *
4
+ * Internationalization messages for the ValidationTabs component
5
+ */
6
+
7
+ import { defineMessages } from 'react-intl';
8
+
9
+ const scope = 'app.components.HtmlEditor.ValidationTabs';
10
+
11
+ export default defineMessages({
12
+ // Tab labels
13
+ htmlIssues: {
14
+ id: `${scope}.htmlIssues`,
15
+ defaultMessage: 'HTML issues',
16
+ },
17
+ labelIssues: {
18
+ id: `${scope}.labelIssues`,
19
+ defaultMessage: 'Label issues',
20
+ },
21
+ liquidIssues: {
22
+ id: `${scope}.liquidIssues`,
23
+ defaultMessage: 'Liquid issues',
24
+ },
25
+
26
+ // Error item labels
27
+ syntaxError: {
28
+ id: `${scope}.syntaxError`,
29
+ defaultMessage: 'Syntax Error',
30
+ },
31
+ lineChar: {
32
+ id: `${scope}.lineChar`,
33
+ defaultMessage: 'Line {line}, Char {char}.',
34
+ },
35
+
36
+ // Tooltips
37
+ navigateToError: {
38
+ id: `${scope}.navigateToError`,
39
+ defaultMessage: 'Go to error location',
40
+ },
41
+ closePanel: {
42
+ id: `${scope}.closePanel`,
43
+ defaultMessage: 'Close validation panel',
44
+ },
45
+
46
+ // Liquid documentation
47
+ liquidDocumentation: {
48
+ id: `${scope}.liquidDocumentation`,
49
+ defaultMessage: 'Liquid documentation',
50
+ },
51
+ });
@@ -7,26 +7,26 @@
7
7
  // HTML Editor Variants
8
8
  export const HTML_EDITOR_VARIANTS = {
9
9
  EMAIL: 'email',
10
- INAPP: 'inapp'
10
+ INAPP: 'inapp',
11
11
  };
12
12
 
13
13
  // Device Types (for InApp variant)
14
14
  export const DEVICE_TYPES = {
15
15
  ANDROID: 'android',
16
- IOS: 'ios'
16
+ IOS: 'ios',
17
17
  };
18
18
 
19
19
  // Editor languages
20
20
  export const EDITOR_LANGUAGES = {
21
21
  HTML: 'html',
22
22
  CSS: 'css',
23
- JAVASCRIPT: 'javascript'
23
+ JAVASCRIPT: 'javascript',
24
24
  };
25
25
 
26
26
  // Preview modes
27
27
  export const PREVIEW_MODES = {
28
28
  DESKTOP: 'desktop',
29
- MOBILE: 'mobile'
29
+ MOBILE: 'mobile',
30
30
  };
31
31
 
32
32
  // Device specifications for mobile preview
@@ -38,7 +38,7 @@ export const DEVICE_PRESETS = {
38
38
  devicePixelRatio: 2.75,
39
39
  userAgent: 'Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36',
40
40
  platform: 'android',
41
- model: 'Pixel 7'
41
+ model: 'Pixel 7',
42
42
  },
43
43
  IPHONE: {
44
44
  key: 'iphone',
@@ -47,15 +47,15 @@ export const DEVICE_PRESETS = {
47
47
  devicePixelRatio: 3,
48
48
  userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)',
49
49
  platform: 'ios',
50
- model: 'iPhone 15'
51
- }
50
+ model: 'iPhone 15',
51
+ },
52
52
  };
53
53
 
54
54
  // Validation severity levels
55
55
  export const VALIDATION_SEVERITY = {
56
56
  ERROR: 'error',
57
57
  WARNING: 'warning',
58
- INFO: 'info'
58
+ INFO: 'info',
59
59
  };
60
60
 
61
61
  // Validation types
@@ -63,14 +63,14 @@ export const VALIDATION_TYPES = {
63
63
  HTML: 'html',
64
64
  CSS: 'css',
65
65
  JAVASCRIPT: 'javascript',
66
- SECURITY: 'security'
66
+ SECURITY: 'security',
67
67
  };
68
68
 
69
69
  // Error types for preview
70
70
  export const PREVIEW_ERROR_TYPES = {
71
71
  RUNTIME: 'runtime',
72
72
  RENDER: 'render',
73
- SECURITY: 'security'
73
+ SECURITY: 'security',
74
74
  };
75
75
 
76
76
  // Performance thresholds
@@ -79,7 +79,7 @@ export const PERFORMANCE = {
79
79
  VALIDATION_DEBOUNCE: 500, // ms
80
80
  AUTO_SAVE_INTERVAL: 30000, // ms
81
81
  MAX_CONTENT_LENGTH: 100000, // characters
82
- PERFORMANCE_WARNING_THRESHOLD: 50000 // characters
82
+ PERFORMANCE_WARNING_THRESHOLD: 50000, // characters
83
83
  };
84
84
 
85
85
  // Layout constraints
@@ -89,7 +89,7 @@ export const LAYOUT = {
89
89
  DEFAULT_SPLIT_SIZES: [50, 50], // [left, right] percentages
90
90
  GUTTER_SIZE: 8, // pixels
91
91
  MOBILE_BREAKPOINT: 768, // pixels
92
- MOBILE_WIDTH_DEFAULT: 375 // pixels
92
+ MOBILE_WIDTH_DEFAULT: 375, // pixels
93
93
  };
94
94
 
95
95
 
@@ -105,7 +105,7 @@ export const CODEMIRROR_CONFIG = {
105
105
  FOLD_GUTTER: true,
106
106
  SEARCH_ENABLED: true,
107
107
  AUTOCOMPLETE_ENABLED: true,
108
- LINT_ENABLED: true
108
+ LINT_ENABLED: true,
109
109
  };
110
110
 
111
111
  // HTML validation rules (HTMLHint)
@@ -126,7 +126,7 @@ export const HTML_VALIDATION_RULES = {
126
126
  'inline-style-disabled': false,
127
127
  'inline-script-disabled': false,
128
128
  'space-tab-mixed-disabled': 'space',
129
- 'spec-char-escape': true
129
+ 'spec-char-escape': true,
130
130
  };
131
131
 
132
132
  // CSS validation rules
@@ -137,7 +137,7 @@ export const CSS_VALIDATION_RULES = {
137
137
  'declaration-colon-space-after': 'always',
138
138
  'declaration-colon-space-before': 'never',
139
139
  'block-closing-brace-newline-after': 'always',
140
- 'block-opening-brace-space-before': 'always'
140
+ 'block-opening-brace-space-before': 'always',
141
141
  };
142
142
 
143
143
  // JavaScript validation rules (ESLint-style)
@@ -148,7 +148,7 @@ export const JS_VALIDATION_RULES = {
148
148
  'no-console': 'warn',
149
149
  'semi': ['error', 'always'],
150
150
  'quotes': ['error', 'single'],
151
- 'indent': ['error', 2]
151
+ 'indent': ['error', 2],
152
152
  };
153
153
 
154
154
  // Keyboard shortcuts
@@ -163,7 +163,7 @@ export const KEYBOARD_SHORTCUTS = {
163
163
  FIND: 'Ctrl+F',
164
164
  FIND_MAC: 'Cmd+F',
165
165
  REPLACE: 'Ctrl+H',
166
- REPLACE_MAC: 'Cmd+Option+F'
166
+ REPLACE_MAC: 'Cmd+Option+F',
167
167
  };
168
168
 
169
169
  // Feature flags
@@ -176,7 +176,7 @@ export const FEATURES = {
176
176
  KEYBOARD_SHORTCUTS: true,
177
177
  ERROR_RECOVERY: true,
178
178
  PERFORMANCE_MONITORING: true,
179
- ACCESSIBILITY_FEATURES: true
179
+ ACCESSIBILITY_FEATURES: true,
180
180
  };
181
181
 
182
182
  // Animation durations (ms)
@@ -186,7 +186,7 @@ export const ANIMATIONS = {
186
186
  SLOW: 500,
187
187
  PANEL_RESIZE: 200,
188
188
  FADE_IN: 300,
189
- SLIDE_IN: 250
189
+ SLIDE_IN: 250,
190
190
  };
191
191
 
192
192
  // Z-index layers
@@ -195,7 +195,7 @@ export const Z_INDEX = {
195
195
  OVERLAY: 1000,
196
196
  MODAL: 1050,
197
197
  FULLSCREEN: 9999,
198
- TOOLTIP: 10000
198
+ TOOLTIP: 10000,
199
199
  };
200
200
 
201
201
  // Default HTML content template
@@ -239,3 +239,12 @@ export const DEFAULT_HTML_CONTENT = `<!DOCTYPE html>
239
239
  </script>
240
240
  </body>
241
241
  </html>`;
242
+
243
+ // Constants for tag API calls
244
+ export const TAG = 'TAG';
245
+ export const EMBEDDED = 'embedded';
246
+ export const DEFAULT = 'default';
247
+ export const FULL = 'full';
248
+ export const ALL = 'all';
249
+ export const SMS = 'SMS';
250
+ export const EMAIL = 'EMAIL';