@capillarytech/creatives-library 8.0.207 → 8.0.209

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 (77) hide show
  1. package/assets/Android.png +0 -0
  2. package/assets/iOS.png +0 -0
  3. package/package.json +16 -2
  4. package/v2Components/HtmlEditor/HTMLEditor.js +508 -0
  5. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1809 -0
  6. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +532 -0
  7. package/v2Components/HtmlEditor/_htmlEditor.scss +304 -0
  8. package/v2Components/HtmlEditor/_index.lazy.scss +26 -0
  9. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +376 -0
  10. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +331 -0
  11. package/v2Components/HtmlEditor/components/DeviceToggle/__tests__/index.test.js +314 -0
  12. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +244 -0
  13. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +111 -0
  14. package/v2Components/HtmlEditor/components/EditorToolbar/PreviewModeGroup.js +72 -0
  15. package/v2Components/HtmlEditor/components/EditorToolbar/__tests__/PreviewModeGroup.test.js +1594 -0
  16. package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +113 -0
  17. package/v2Components/HtmlEditor/components/EditorToolbar/_previewModeGroup.scss +82 -0
  18. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +115 -0
  19. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +57 -0
  20. package/v2Components/HtmlEditor/components/InAppPreviewPane/ContentOverlay.js +90 -0
  21. package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +60 -0
  22. package/v2Components/HtmlEditor/components/InAppPreviewPane/LayoutSelector.js +58 -0
  23. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/ContentOverlay.test.js +389 -0
  24. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +424 -0
  25. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/LayoutSelector.test.js +248 -0
  26. package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +253 -0
  27. package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +104 -0
  28. package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +179 -0
  29. package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +220 -0
  30. package/v2Components/HtmlEditor/components/PreviewPane/index.js +229 -0
  31. package/v2Components/HtmlEditor/components/SplitContainer/SplitContainer.js +276 -0
  32. package/v2Components/HtmlEditor/components/SplitContainer/__tests__/SplitContainer.test.js +295 -0
  33. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +257 -0
  34. package/v2Components/HtmlEditor/components/SplitContainer/index.js +7 -0
  35. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +152 -0
  36. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +31 -0
  37. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +70 -0
  38. package/v2Components/HtmlEditor/components/ValidationPanel/__tests__/index.test.js +98 -0
  39. package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +311 -0
  40. package/v2Components/HtmlEditor/components/ValidationPanel/index.js +297 -0
  41. package/v2Components/HtmlEditor/components/ValidationPanel/messages.js +57 -0
  42. package/v2Components/HtmlEditor/components/common/EditorContext.js +84 -0
  43. package/v2Components/HtmlEditor/components/common/__tests__/EditorContext.test.js +660 -0
  44. package/v2Components/HtmlEditor/constants.js +241 -0
  45. package/v2Components/HtmlEditor/hooks/__tests__/useEditorContent.test.js +450 -0
  46. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +785 -0
  47. package/v2Components/HtmlEditor/hooks/__tests__/useLayoutState.test.js +580 -0
  48. package/v2Components/HtmlEditor/hooks/__tests__/useValidation.enhanced.test.js +768 -0
  49. package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +590 -0
  50. package/v2Components/HtmlEditor/hooks/useEditorContent.js +274 -0
  51. package/v2Components/HtmlEditor/hooks/useInAppContent.js +407 -0
  52. package/v2Components/HtmlEditor/hooks/useLayoutState.js +247 -0
  53. package/v2Components/HtmlEditor/hooks/useValidation.js +325 -0
  54. package/v2Components/HtmlEditor/index.js +29 -0
  55. package/v2Components/HtmlEditor/index.lazy.js +114 -0
  56. package/v2Components/HtmlEditor/messages.js +389 -0
  57. package/v2Components/HtmlEditor/utils/__tests__/contentSanitizer.test.js +741 -0
  58. package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +1042 -0
  59. package/v2Components/HtmlEditor/utils/__tests__/liquidTemplateSupport.test.js +515 -0
  60. package/v2Components/HtmlEditor/utils/__tests__/properSyntaxHighlighting.test.js +473 -0
  61. package/v2Components/HtmlEditor/utils/__tests__/simplePerformance.test.js +1109 -0
  62. package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +240 -0
  63. package/v2Components/HtmlEditor/utils/contentSanitizer.js +433 -0
  64. package/v2Components/HtmlEditor/utils/htmlValidator.js +508 -0
  65. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +524 -0
  66. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +163 -0
  67. package/v2Components/HtmlEditor/utils/simplePerformance.js +145 -0
  68. package/v2Components/HtmlEditor/utils/validationAdapter.js +130 -0
  69. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +200 -0
  70. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +545 -0
  71. package/v2Containers/EmailWrapper/index.js +8 -1
  72. package/v2Containers/Templates/constants.js +8 -0
  73. package/v2Containers/Templates/index.js +56 -28
  74. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +5 -14
  75. package/v2Containers/Whatsapp/constants.js +26 -2
  76. package/v2Containers/Whatsapp/index.js +4 -1
  77. package/v2Containers/Whatsapp/tests/index.test.js +460 -18
@@ -0,0 +1,311 @@
1
+ /**
2
+ * ValidationPanel Styles
3
+ */
4
+
5
+ @import '~@capillarytech/cap-ui-library/styles/_variables.scss';
6
+
7
+ .validation-panel {
8
+ border: 1px solid #d9d9d9;
9
+ border-radius: 6px;
10
+ background: #fff;
11
+
12
+ &--loading {
13
+ padding: 16px;
14
+ text-align: center;
15
+ }
16
+
17
+ &--clean {
18
+ padding: 16px;
19
+ text-align: center;
20
+ background: #f6ffed;
21
+ border-color: #b7eb8f;
22
+ }
23
+
24
+ &__loading {
25
+ display: flex;
26
+ align-items: center;
27
+ justify-content: center;
28
+ gap: 8px;
29
+ color: #666;
30
+ font-size: 14px;
31
+ }
32
+
33
+ &__clean {
34
+ display: flex;
35
+ align-items: center;
36
+ justify-content: center;
37
+ gap: 8px;
38
+ color: #52c41a;
39
+ font-size: 14px;
40
+ font-weight: 500;
41
+ }
42
+
43
+ &__summary {
44
+ display: flex;
45
+ gap: 16px;
46
+ padding: 12px 16px;
47
+ background: #fafafa;
48
+ border-bottom: 1px solid #d9d9d9;
49
+ border-radius: 6px 6px 0 0;
50
+ }
51
+
52
+ &__summary-item {
53
+ display: flex;
54
+ align-items: center;
55
+ gap: 4px;
56
+ font-size: 12px;
57
+ color: #666;
58
+
59
+ &--security {
60
+ color: #ff4d4f;
61
+ font-weight: 500;
62
+ }
63
+
64
+ span:first-of-type {
65
+ font-weight: 600;
66
+ margin-left: 2px;
67
+ }
68
+ }
69
+
70
+ &__collapse {
71
+ .ant-collapse-item {
72
+ border: none;
73
+
74
+ &:last-child {
75
+ border-radius: 0 0 6px 6px;
76
+ }
77
+ }
78
+
79
+ .ant-collapse-header {
80
+ padding: 12px 16px !important;
81
+ align-items: center !important;
82
+ }
83
+
84
+ .ant-collapse-content {
85
+ border-top: 1px solid #f0f0f0;
86
+ }
87
+
88
+ .ant-collapse-content-box {
89
+ padding: 0;
90
+ }
91
+ }
92
+
93
+ &__panel {
94
+ &--error {
95
+ .ant-collapse-header {
96
+ background: #fff2f0;
97
+ border-left: 3px solid #ff4d4f;
98
+ }
99
+ }
100
+
101
+ &--warning {
102
+ .ant-collapse-header {
103
+ background: #fffbe6;
104
+ border-left: 3px solid #faad14;
105
+ }
106
+ }
107
+
108
+ &--info {
109
+ .ant-collapse-header {
110
+ background: #e6f7ff;
111
+ border-left: 3px solid #1890ff;
112
+ }
113
+ }
114
+ }
115
+
116
+ &__header {
117
+ display: flex;
118
+ align-items: center;
119
+ justify-content: space-between;
120
+ width: 100%;
121
+ }
122
+
123
+ &__title {
124
+ display: flex;
125
+ align-items: center;
126
+ gap: 8px;
127
+ font-weight: 500;
128
+ }
129
+
130
+ &__issues {
131
+ max-height: 300px;
132
+ overflow-y: auto;
133
+ }
134
+
135
+ &__sanitization {
136
+ border-top: 1px solid #d9d9d9;
137
+ background: #f9f9f9;
138
+ }
139
+
140
+ &__sanitization-header {
141
+ display: flex;
142
+ align-items: center;
143
+ gap: 8px;
144
+ padding: 12px 16px;
145
+ font-weight: 500;
146
+ color: #666;
147
+ font-size: 13px;
148
+ }
149
+
150
+ &__sanitization-list {
151
+ padding: 0 16px 12px;
152
+ }
153
+
154
+ &__sanitization-item {
155
+ padding: 4px 0;
156
+ font-size: 12px;
157
+ color: #8c8c8c;
158
+ }
159
+ }
160
+
161
+ .validation-issue {
162
+ display: flex;
163
+ gap: 12px;
164
+ padding: 12px 16px;
165
+ border-bottom: 1px solid #f0f0f0;
166
+ cursor: pointer;
167
+ transition: background-color 0.2s;
168
+
169
+ &:hover {
170
+ background: #fafafa;
171
+ }
172
+
173
+ &:last-child {
174
+ border-bottom: none;
175
+ }
176
+
177
+ &--error {
178
+ border-left: 2px solid #ff4d4f;
179
+ }
180
+
181
+ &--warning {
182
+ border-left: 2px solid #faad14;
183
+ }
184
+
185
+ &--info {
186
+ border-left: 2px solid #1890ff;
187
+ }
188
+
189
+ &__icon {
190
+ flex-shrink: 0;
191
+ margin-top: 2px;
192
+ }
193
+
194
+ &__content {
195
+ flex: 1;
196
+ min-width: 0;
197
+ }
198
+
199
+ &__message {
200
+ font-size: 13px;
201
+ line-height: 1.4;
202
+ color: #262626;
203
+ margin-bottom: 4px;
204
+ word-break: break-word;
205
+ }
206
+
207
+ &__meta {
208
+ display: flex;
209
+ flex-wrap: wrap;
210
+ gap: 8px;
211
+ font-size: 11px;
212
+ color: #8c8c8c;
213
+ }
214
+
215
+ &__location {
216
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
217
+ background: #f5f5f5;
218
+ padding: 2px 6px;
219
+ border-radius: 3px;
220
+ }
221
+
222
+ &__rule {
223
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
224
+ background: #e6f7ff;
225
+ padding: 2px 6px;
226
+ border-radius: 3px;
227
+ color: #1890ff;
228
+ }
229
+
230
+ &__source {
231
+ display: flex;
232
+ align-items: center;
233
+ gap: 4px;
234
+ background: #f0f0f0;
235
+ padding: 2px 6px;
236
+ border-radius: 3px;
237
+ }
238
+ }
239
+
240
+ // Dark theme support for editor integration
241
+ .html-editor-content .validation-panel {
242
+ border-color: #424242;
243
+ background: #2d2d2d;
244
+ color: #e0e0e0;
245
+
246
+ &__summary {
247
+ background: #3d3d3d;
248
+ border-color: #424242;
249
+ }
250
+
251
+ .validation-issue {
252
+ border-color: #424242;
253
+ color: #e0e0e0;
254
+
255
+ &:hover {
256
+ background: #3d3d3d;
257
+ }
258
+
259
+ &__message {
260
+ color: #e0e0e0;
261
+ }
262
+
263
+ &__location {
264
+ background: #424242;
265
+ color: #e0e0e0;
266
+ }
267
+
268
+ &__rule {
269
+ background: #1f1f1f;
270
+ color: #69c0ff;
271
+ }
272
+
273
+ &__source {
274
+ background: #424242;
275
+ color: #e0e0e0;
276
+ }
277
+ }
278
+
279
+ .ant-collapse-header {
280
+ background: #3d3d3d !important;
281
+ color: #e0e0e0 !important;
282
+ }
283
+
284
+ .ant-collapse-content {
285
+ background: #2d2d2d;
286
+ border-color: #424242;
287
+ }
288
+ }
289
+
290
+ // Responsive design
291
+ @media (max-width: 768px) {
292
+ .validation-panel {
293
+ &__summary {
294
+ flex-direction: column;
295
+ gap: 8px;
296
+ }
297
+
298
+ &__issues {
299
+ max-height: 200px;
300
+ }
301
+ }
302
+
303
+ .validation-issue {
304
+ padding: 8px 12px;
305
+
306
+ &__meta {
307
+ flex-direction: column;
308
+ gap: 4px;
309
+ }
310
+ }
311
+ }
@@ -0,0 +1,297 @@
1
+ /**
2
+ * ValidationPanel Component
3
+ * Displays validation errors, warnings, and security issues
4
+ */
5
+
6
+ import React, { useState, useMemo } from 'react';
7
+ import PropTypes from 'prop-types';
8
+ import { FormattedMessage } from 'react-intl';
9
+
10
+ // Import components individually to avoid undefined imports
11
+ import Badge from 'antd/lib/badge';
12
+ import Collapse from 'antd/lib/collapse';
13
+ import Empty from 'antd/lib/empty';
14
+ import Spin from 'antd/lib/spin';
15
+
16
+ // Import icons individually
17
+ import ExclamationCircleOutlined from '@ant-design/icons/ExclamationCircleOutlined';
18
+ import WarningOutlined from '@ant-design/icons/WarningOutlined';
19
+ import InfoCircleOutlined from '@ant-design/icons/InfoCircleOutlined';
20
+ import ShieldOutlined from '@ant-design/icons/ShieldOutlined';
21
+ import BugOutlined from '@ant-design/icons/BugOutlined';
22
+ import CodeOutlined from '@ant-design/icons/CodeOutlined';
23
+ import EyeInvisibleOutlined from '@ant-design/icons/EyeInvisibleOutlined';
24
+ import CheckCircleOutlined from '@ant-design/icons/CheckCircleOutlined';
25
+
26
+ import messages from './messages';
27
+ import './_validationPanel.scss';
28
+
29
+ const { Panel } = Collapse;
30
+
31
+ /**
32
+ * ValidationPanel Component
33
+ */
34
+ const ValidationPanel = ({
35
+ validation,
36
+ isVisible = true,
37
+ onErrorClick,
38
+ showLineNumbers = true,
39
+ groupBySource = false,
40
+ variant = 'email'
41
+ }) => {
42
+ const [activeKeys, setActiveKeys] = useState(['errors', 'warnings']);
43
+
44
+ // Group issues by type or source
45
+ const groupedIssues = useMemo(() => {
46
+ if (!validation) return {};
47
+
48
+ const allIssues = validation.getAllIssues();
49
+
50
+ if (groupBySource) {
51
+ return allIssues.reduce((groups, issue) => {
52
+ const source = issue.source || 'unknown';
53
+ if (!groups[source]) {
54
+ groups[source] = [];
55
+ }
56
+ groups[source].push(issue);
57
+ return groups;
58
+ }, {});
59
+ } else {
60
+ return {
61
+ errors: allIssues.filter(issue => issue.severity === 'error'),
62
+ warnings: allIssues.filter(issue => issue.severity === 'warning'),
63
+ info: allIssues.filter(issue => issue.severity === 'info')
64
+ };
65
+ }
66
+ }, [validation, groupBySource]);
67
+
68
+ // Get icon for issue type
69
+ const getIssueIcon = (severity, source) => {
70
+ if (source === 'security') {
71
+ return <ShieldOutlined style={{ color: '#ff4d4f' }} />;
72
+ }
73
+
74
+ switch (severity) {
75
+ case 'error':
76
+ return <ExclamationCircleOutlined style={{ color: '#ff4d4f' }} />;
77
+ case 'warning':
78
+ return <WarningOutlined style={{ color: '#faad14' }} />;
79
+ case 'info':
80
+ return <InfoCircleOutlined style={{ color: '#1890ff' }} />;
81
+ default:
82
+ return <BugOutlined style={{ color: '#666' }} />;
83
+ }
84
+ };
85
+
86
+ // Get source icon
87
+ const getSourceIcon = (source) => {
88
+ switch (source) {
89
+ case 'htmlhint':
90
+ return <CodeOutlined />;
91
+ case 'css-validator':
92
+ return <CodeOutlined />;
93
+ case 'security':
94
+ return <ShieldOutlined />;
95
+ case 'custom':
96
+ return <BugOutlined />;
97
+ default:
98
+ return <InfoCircleOutlined />;
99
+ }
100
+ };
101
+
102
+ // Handle issue click
103
+ const handleIssueClick = (issue) => {
104
+ if (onErrorClick && issue.line) {
105
+ onErrorClick({
106
+ line: issue.line,
107
+ column: issue.column || 1,
108
+ message: issue.message,
109
+ severity: issue.severity
110
+ });
111
+ }
112
+ };
113
+
114
+ // Render issue item
115
+ const renderIssue = (issue, index) => (
116
+ <div
117
+ key={index}
118
+ className={`validation-issue validation-issue--${issue.severity}`}
119
+ onClick={() => handleIssueClick(issue)}
120
+ role="button"
121
+ tabIndex={0}
122
+ onKeyPress={(e) => {
123
+ if (e.key === 'Enter' || e.key === ' ') {
124
+ handleIssueClick(issue);
125
+ }
126
+ }}
127
+ >
128
+ <div className="validation-issue__icon">
129
+ {getIssueIcon(issue.severity, issue.source)}
130
+ </div>
131
+
132
+ <div className="validation-issue__content">
133
+ <div className="validation-issue__message">
134
+ {issue.message}
135
+ </div>
136
+
137
+ <div className="validation-issue__meta">
138
+ {showLineNumbers && issue.line && (
139
+ <span className="validation-issue__location">
140
+ <FormattedMessage {...messages.lineColumn} values={{
141
+ line: issue.line,
142
+ column: issue.column || 1
143
+ }} />
144
+ </span>
145
+ )}
146
+
147
+ {issue.rule && (
148
+ <span className="validation-issue__rule">
149
+ {issue.rule}
150
+ </span>
151
+ )}
152
+
153
+ {issue.source && (
154
+ <span className="validation-issue__source">
155
+ {getSourceIcon(issue.source)}
156
+ {issue.source}
157
+ </span>
158
+ )}
159
+ </div>
160
+ </div>
161
+ </div>
162
+ );
163
+
164
+ // Render panel header with count
165
+ const renderPanelHeader = (title, count, severity) => (
166
+ <div className="validation-panel__header">
167
+ <span className="validation-panel__title">
168
+ {getIssueIcon(severity)}
169
+ <FormattedMessage {...title} />
170
+ </span>
171
+ {count > 0 && (
172
+ <Badge count={count} style={{ backgroundColor: getSeverityColor(severity) }} />
173
+ )}
174
+ </div>
175
+ );
176
+
177
+ // Get severity color
178
+ const getSeverityColor = (severity) => {
179
+ switch (severity) {
180
+ case 'error': return '#ff4d4f';
181
+ case 'warning': return '#faad14';
182
+ case 'info': return '#1890ff';
183
+ default: return '#666';
184
+ }
185
+ };
186
+
187
+ if (!isVisible) {
188
+ return null;
189
+ }
190
+
191
+ // Show loading state
192
+ if (validation?.isValidating) {
193
+ return (
194
+ <div className="validation-panel validation-panel--loading">
195
+ <div className="validation-panel__loading">
196
+ <Spin size="small" />
197
+ <span><FormattedMessage {...messages.validating} /></span>
198
+ </div>
199
+ </div>
200
+ );
201
+ }
202
+
203
+ // Skip clean state - don't show "No validation issues found" message
204
+
205
+ // Render validation issues
206
+ return (
207
+ <div className="validation-panel">
208
+ <div className="validation-panel__summary">
209
+ <div className="validation-panel__summary-item">
210
+ <ExclamationCircleOutlined style={{ color: '#ff4d4f' }} />
211
+ <span>{validation?.summary?.totalErrors || 0}</span>
212
+ <FormattedMessage {...messages.errors} />
213
+ </div>
214
+
215
+ <div className="validation-panel__summary-item">
216
+ <WarningOutlined style={{ color: '#faad14' }} />
217
+ <span>{validation?.summary?.totalWarnings || 0}</span>
218
+ <FormattedMessage {...messages.warnings} />
219
+ </div>
220
+
221
+ {validation?.summary?.hasSecurityIssues && (
222
+ <div className="validation-panel__summary-item validation-panel__summary-item--security">
223
+ <ShieldOutlined style={{ color: '#ff4d4f' }} />
224
+ <FormattedMessage {...messages.securityIssues} />
225
+ </div>
226
+ )}
227
+ </div>
228
+
229
+ <Collapse
230
+ activeKey={activeKeys}
231
+ onChange={setActiveKeys}
232
+ className="validation-panel__collapse"
233
+ ghost
234
+ >
235
+ {Object.entries(groupedIssues).map(([key, issues]) => {
236
+ if (!issues || issues.length === 0) return null;
237
+
238
+ const isSourceGroup = groupBySource;
239
+ const title = isSourceGroup
240
+ ? { id: `htmlEditor.validation.source.${key}`, defaultMessage: key }
241
+ : messages[key] || { id: `htmlEditor.validation.${key}`, defaultMessage: key };
242
+
243
+ const severity = isSourceGroup
244
+ ? (issues.find(i => i.severity === 'error') ? 'error' :
245
+ issues.find(i => i.severity === 'warning') ? 'warning' : 'info')
246
+ : key;
247
+
248
+ return (
249
+ <Panel
250
+ key={key}
251
+ header={renderPanelHeader(title, issues.length, severity)}
252
+ className={`validation-panel__panel validation-panel__panel--${severity}`}
253
+ >
254
+ <div className="validation-panel__issues">
255
+ {issues.length === 0 ? (
256
+ <Empty
257
+ image={Empty.PRESENTED_IMAGE_SIMPLE}
258
+ description={<FormattedMessage {...messages.noIssuesOfType} />}
259
+ />
260
+ ) : (
261
+ issues.map((issue, index) => renderIssue(issue, index))
262
+ )}
263
+ </div>
264
+ </Panel>
265
+ );
266
+ })}
267
+ </Collapse>
268
+
269
+ {validation?.sanitizationWarnings?.length > 0 && (
270
+ <div className="validation-panel__sanitization">
271
+ <div className="validation-panel__sanitization-header">
272
+ <EyeInvisibleOutlined />
273
+ <FormattedMessage {...messages.sanitizationWarnings} />
274
+ </div>
275
+ <div className="validation-panel__sanitization-list">
276
+ {validation.sanitizationWarnings.map((warning, index) => (
277
+ <div key={index} className="validation-panel__sanitization-item">
278
+ {warning.message}
279
+ </div>
280
+ ))}
281
+ </div>
282
+ </div>
283
+ )}
284
+ </div>
285
+ );
286
+ };
287
+
288
+ ValidationPanel.propTypes = {
289
+ validation: PropTypes.object,
290
+ isVisible: PropTypes.bool,
291
+ onErrorClick: PropTypes.func,
292
+ showLineNumbers: PropTypes.bool,
293
+ groupBySource: PropTypes.bool,
294
+ variant: PropTypes.oneOf(['email', 'inapp'])
295
+ };
296
+
297
+ export default ValidationPanel;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * ValidationPanel Messages
3
+ * Internationalization messages for the validation panel
4
+ */
5
+
6
+ import { defineMessages } from 'react-intl';
7
+
8
+ export default defineMessages({
9
+ errors: {
10
+ id: 'htmlEditor.validation.errors',
11
+ defaultMessage: 'Errors'
12
+ },
13
+ warnings: {
14
+ id: 'htmlEditor.validation.warnings',
15
+ defaultMessage: 'Warnings'
16
+ },
17
+ info: {
18
+ id: 'htmlEditor.validation.info',
19
+ defaultMessage: 'Info'
20
+ },
21
+ securityIssues: {
22
+ id: 'htmlEditor.validation.securityIssues',
23
+ defaultMessage: 'Security Issues'
24
+ },
25
+ noIssues: {
26
+ id: 'htmlEditor.validation.noIssues',
27
+ defaultMessage: 'No validation issues found'
28
+ },
29
+ noIssuesOfType: {
30
+ id: 'htmlEditor.validation.noIssuesOfType',
31
+ defaultMessage: 'No issues of this type'
32
+ },
33
+ validating: {
34
+ id: 'htmlEditor.validation.validating',
35
+ defaultMessage: 'Validating...'
36
+ },
37
+ lineColumn: {
38
+ id: 'htmlEditor.validation.lineColumn',
39
+ defaultMessage: 'Line {line}, Column {column}'
40
+ },
41
+ sanitizationWarnings: {
42
+ id: 'htmlEditor.validation.sanitizationWarnings',
43
+ defaultMessage: 'Content Sanitization Warnings'
44
+ },
45
+ htmlValidation: {
46
+ id: 'htmlEditor.validation.htmlValidation',
47
+ defaultMessage: 'HTML Validation'
48
+ },
49
+ cssValidation: {
50
+ id: 'htmlEditor.validation.cssValidation',
51
+ defaultMessage: 'CSS Validation'
52
+ },
53
+ securityValidation: {
54
+ id: 'htmlEditor.validation.securityValidation',
55
+ defaultMessage: 'Security Validation'
56
+ }
57
+ });