@capillarytech/creatives-library 8.0.259 → 8.0.260-alpha.0
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.
- package/assets/Android.png +0 -0
- package/assets/iOS.png +0 -0
- package/constants/unified.js +1 -2
- package/initialReducer.js +0 -2
- package/package.json +1 -1
- package/services/api.js +0 -10
- package/services/tests/api.test.js +0 -34
- package/translations/en.json +3 -4
- package/utils/common.js +0 -12
- package/utils/commonUtils.js +5 -28
- package/utils/tests/commonUtil.test.js +0 -224
- package/utils/transformTemplateConfig.js +10 -0
- package/v2Components/CapDeviceContent/index.js +56 -61
- package/v2Components/CapTagList/index.js +1 -6
- package/v2Components/CapTagListWithInput/index.js +1 -5
- package/v2Components/CapTagListWithInput/messages.js +1 -1
- package/v2Components/CapWhatsappCTA/tests/index.test.js +0 -5
- package/v2Components/ErrorInfoNote/index.js +72 -457
- package/v2Components/ErrorInfoNote/messages.js +6 -36
- package/v2Components/ErrorInfoNote/style.scss +6 -282
- package/v2Components/FormBuilder/index.js +4 -4
- package/v2Components/FormBuilder/tests/index.test.js +4 -13
- package/v2Components/HtmlEditor/HTMLEditor.js +94 -547
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +133 -1441
- package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +16 -27
- package/v2Components/HtmlEditor/_htmlEditor.scss +45 -108
- package/v2Components/HtmlEditor/_index.lazy.scss +1 -0
- package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +102 -23
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +140 -148
- package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +1 -2
- package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
- package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +0 -9
- package/v2Components/HtmlEditor/components/EditorToolbar/index.js +4 -4
- package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +0 -22
- package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +7 -4
- package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +45 -35
- package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +3 -1
- package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
- package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +6 -7
- package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +6 -3
- package/v2Components/HtmlEditor/components/PreviewPane/index.js +43 -22
- package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +152 -0
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +0 -1
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +31 -49
- package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +34 -50
- package/v2Components/HtmlEditor/components/ValidationPanel/index.js +41 -70
- package/v2Components/HtmlEditor/constants.js +20 -42
- package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +16 -373
- package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +16 -120
- package/v2Components/HtmlEditor/hooks/useEditorContent.js +2 -5
- package/v2Components/HtmlEditor/hooks/useInAppContent.js +146 -88
- package/v2Components/HtmlEditor/hooks/useValidation.js +53 -189
- package/v2Components/HtmlEditor/index.js +1 -1
- package/v2Components/HtmlEditor/messages.js +94 -92
- package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +45 -94
- package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +0 -134
- package/v2Components/HtmlEditor/utils/contentSanitizer.js +41 -40
- package/v2Components/HtmlEditor/utils/htmlValidator.js +72 -71
- package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +102 -134
- package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +25 -23
- package/v2Components/HtmlEditor/utils/validationAdapter.js +41 -66
- package/v2Components/MobilePushPreviewV2/index.js +7 -32
- package/v2Components/TemplatePreview/_templatePreview.scss +24 -55
- package/v2Components/TemplatePreview/index.js +32 -47
- package/v2Components/TemplatePreview/messages.js +0 -4
- package/v2Components/TestAndPreviewSlidebox/_testAndPreviewSlidebox.scss +0 -1
- package/v2Containers/BeeEditor/index.js +90 -172
- package/v2Containers/Cap/tests/__snapshots__/index.test.js.snap +3 -4
- package/v2Containers/CreativesContainer/SlideBoxContent.js +52 -128
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +13 -163
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +1 -2
- package/v2Containers/CreativesContainer/constants.js +0 -1
- package/v2Containers/CreativesContainer/index.js +46 -240
- package/v2Containers/CreativesContainer/messages.js +0 -8
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +2 -11
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +50 -38
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +0 -106
- package/v2Containers/Email/actions.js +0 -7
- package/v2Containers/Email/constants.js +1 -5
- package/v2Containers/Email/index.js +30 -239
- package/v2Containers/Email/messages.js +0 -32
- package/v2Containers/Email/reducer.js +1 -12
- package/v2Containers/Email/sagas.js +7 -61
- package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +0 -2
- package/v2Containers/Email/tests/reducer.test.js +0 -46
- package/v2Containers/Email/tests/sagas.test.js +29 -320
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +21 -211
- package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +74 -40
- package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +67 -2
- package/v2Containers/EmailWrapper/constants.js +0 -2
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +77 -629
- package/v2Containers/EmailWrapper/index.js +23 -103
- package/v2Containers/EmailWrapper/messages.js +1 -65
- package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +214 -0
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +77 -594
- package/v2Containers/InApp/actions.js +0 -7
- package/v2Containers/InApp/constants.js +4 -20
- package/v2Containers/InApp/index.js +360 -804
- package/v2Containers/InApp/index.scss +3 -4
- package/v2Containers/InApp/messages.js +3 -7
- package/v2Containers/InApp/reducer.js +3 -21
- package/v2Containers/InApp/sagas.js +9 -29
- package/v2Containers/InApp/selectors.js +5 -25
- package/v2Containers/InApp/tests/index.test.js +71 -152
- package/v2Containers/InApp/tests/reducer.test.js +0 -34
- package/v2Containers/InApp/tests/sagas.test.js +9 -61
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +12 -39
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +6 -10
- package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +75 -102
- package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +54 -81
- package/v2Containers/MobilePushNew/index.js +2 -3
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +178 -262
- package/v2Containers/SmsTrai/Create/tests/__snapshots__/index.test.js.snap +12 -16
- package/v2Containers/SmsTrai/Edit/index.js +1 -2
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +111 -468
- package/v2Containers/TagList/index.js +19 -62
- package/v2Containers/Templates/_templates.scss +1 -60
- package/v2Containers/Templates/index.js +4 -89
- package/v2Containers/Templates/messages.js +0 -4
- package/v2Containers/WebPush/Create/messages.js +8 -0
- package/v2Containers/WebPush/Create/preview/PreviewControls.js +2 -2
- package/v2Containers/WebPush/Create/preview/PreviewDisclaimer.js +3 -1
- package/v2Containers/WebPush/Create/preview/components/AndroidMobileChromeHeader.js +5 -1
- package/v2Containers/WebPush/Create/preview/components/AndroidMobileExpanded.js +5 -1
- package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/AndroidMobileExpanded.test.js.snap +5 -1
- package/v2Containers/WebPush/Create/preview/preview.scss +7 -0
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +734 -1306
- package/v2Components/ErrorInfoNote/constants.js +0 -1
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +0 -874
- package/v2Components/HtmlEditor/components/ValidationPanel/constants.js +0 -6
- package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +0 -255
- package/v2Components/HtmlEditor/components/ValidationTabs/index.js +0 -364
- package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +0 -51
- package/v2Components/HtmlEditor/utils/validationConstants.js +0 -40
- package/v2Containers/BeePopupEditor/_beePopupEditor.scss +0 -14
- package/v2Containers/BeePopupEditor/constants.js +0 -10
- package/v2Containers/BeePopupEditor/index.js +0 -194
- package/v2Containers/BeePopupEditor/tests/index.test.js +0 -627
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +0 -1285
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +0 -1880
- package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +0 -520
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +0 -643
- package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +0 -376
- package/v2Containers/InApp/__tests__/sagas.test.js +0 -363
- package/v2Containers/InApp/tests/selectors.test.js +0 -612
- package/v2Containers/InAppWrapper/components/InAppWrapperView.js +0 -151
- package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +0 -267
- package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +0 -23
- package/v2Containers/InAppWrapper/constants.js +0 -16
- package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +0 -473
- package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +0 -198
- package/v2Containers/InAppWrapper/index.js +0 -148
- package/v2Containers/InAppWrapper/messages.js +0 -49
- package/v2Containers/InappAdvance/index.js +0 -1099
- package/v2Containers/InappAdvance/index.scss +0 -10
- package/v2Containers/InappAdvance/tests/index.test.js +0 -448
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ValidationErrorDisplay Component Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for the ValidationErrorDisplay component that integrates with ErrorInfoNote.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { render, screen } from '@testing-library/react';
|
|
9
|
+
import '@testing-library/jest-dom';
|
|
10
|
+
import ValidationErrorDisplay from '../index';
|
|
11
|
+
|
|
12
|
+
// Mock ErrorInfoNote component
|
|
13
|
+
jest.mock('../../../../ErrorInfoNote', () => {
|
|
14
|
+
return function MockErrorInfoNote({ errorMessages }) {
|
|
15
|
+
const liquidErrors = errorMessages?.LIQUID_ERROR_MSG || [];
|
|
16
|
+
const standardErrors = errorMessages?.STANDARD_ERROR_MSG || [];
|
|
17
|
+
const hasErrors = liquidErrors.length > 0 || standardErrors.length > 0;
|
|
18
|
+
|
|
19
|
+
if (!hasErrors) return null;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div data-testid="error-info-note">
|
|
23
|
+
{liquidErrors.map((msg, index) => (
|
|
24
|
+
<div key={`liquid-${index}`} data-testid="error-message">
|
|
25
|
+
{msg}
|
|
26
|
+
</div>
|
|
27
|
+
))}
|
|
28
|
+
{standardErrors.map((msg, index) => (
|
|
29
|
+
<div key={`standard-${index}`} data-testid="error-message">
|
|
30
|
+
{msg}
|
|
31
|
+
</div>
|
|
32
|
+
))}
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('ValidationErrorDisplay', () => {
|
|
39
|
+
it('renders standard errors when validation has issues', () => {
|
|
40
|
+
const mockValidation = {
|
|
41
|
+
getAllIssues: () => [
|
|
42
|
+
{ message: 'Missing closing tag', severity: 'error', line: 5, column: 10 },
|
|
43
|
+
{ message: 'Invalid attribute', severity: 'error', line: 7 }
|
|
44
|
+
],
|
|
45
|
+
isClean: () => false,
|
|
46
|
+
isValidating: false
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
render(<ValidationErrorDisplay validation={mockValidation} />);
|
|
50
|
+
|
|
51
|
+
expect(screen.getByTestId('error-info-note')).toBeInTheDocument();
|
|
52
|
+
const errorMessages = screen.getAllByTestId('error-message');
|
|
53
|
+
expect(errorMessages).toHaveLength(2);
|
|
54
|
+
expect(errorMessages[0]).toHaveTextContent('Missing closing tag Line 5, Char 10.');
|
|
55
|
+
expect(errorMessages[1]).toHaveTextContent('Invalid attribute Line 7.');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('renders liquid errors separately', () => {
|
|
59
|
+
const mockValidation = {
|
|
60
|
+
getAllIssues: () => [
|
|
61
|
+
{ message: 'Invalid liquid syntax', severity: 'error', source: 'liquid-validator', line: 3 },
|
|
62
|
+
{ message: 'HTML error', severity: 'error', source: 'htmlhint', line: 10 }
|
|
63
|
+
],
|
|
64
|
+
isClean: () => false,
|
|
65
|
+
isValidating: false
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
render(<ValidationErrorDisplay validation={mockValidation} />);
|
|
69
|
+
|
|
70
|
+
expect(screen.getByTestId('error-info-note')).toBeInTheDocument();
|
|
71
|
+
const errorMessages = screen.getAllByTestId('error-message');
|
|
72
|
+
expect(errorMessages).toHaveLength(2);
|
|
73
|
+
// First should be liquid error, second should be standard error
|
|
74
|
+
expect(errorMessages[0]).toHaveTextContent('Invalid liquid syntax Line 3.');
|
|
75
|
+
expect(errorMessages[1]).toHaveTextContent('HTML error Line 10.');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('does not render when validation has no errors', () => {
|
|
79
|
+
const mockValidation = {
|
|
80
|
+
getAllIssues: () => [],
|
|
81
|
+
isClean: () => true,
|
|
82
|
+
isValidating: false
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const { container } = render(<ValidationErrorDisplay validation={mockValidation} />);
|
|
86
|
+
|
|
87
|
+
expect(screen.queryByTestId('error-info-note')).not.toBeInTheDocument();
|
|
88
|
+
expect(container.firstChild).toBeNull();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('does not render when validation is null', () => {
|
|
92
|
+
const { container } = render(<ValidationErrorDisplay validation={null} />);
|
|
93
|
+
|
|
94
|
+
expect(screen.queryByTestId('error-info-note')).not.toBeInTheDocument();
|
|
95
|
+
expect(container.firstChild).toBeNull();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('does not render when validation is in progress', () => {
|
|
99
|
+
const mockValidation = {
|
|
100
|
+
getAllIssues: () => [],
|
|
101
|
+
isClean: () => false,
|
|
102
|
+
isValidating: true
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const { container } = render(<ValidationErrorDisplay validation={mockValidation} />);
|
|
106
|
+
|
|
107
|
+
expect(screen.queryByTestId('error-info-note')).not.toBeInTheDocument();
|
|
108
|
+
expect(container.firstChild).toBeNull();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('applies custom className', () => {
|
|
112
|
+
const mockValidation = {
|
|
113
|
+
getAllIssues: () => [{ message: 'Error', severity: 'error' }],
|
|
114
|
+
isClean: () => false,
|
|
115
|
+
isValidating: false
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const { container } = render(
|
|
119
|
+
<ValidationErrorDisplay validation={mockValidation} className="custom-class" />
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const wrapper = container.querySelector('.validation-error-display');
|
|
123
|
+
expect(wrapper).toHaveClass('custom-class');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('handles inapp variant correctly', () => {
|
|
127
|
+
const mockValidation = {
|
|
128
|
+
getAllIssues: () => [{ message: 'Mobile error', severity: 'error' }],
|
|
129
|
+
isClean: () => false,
|
|
130
|
+
isValidating: false
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
render(<ValidationErrorDisplay validation={mockValidation} variant="inapp" />);
|
|
134
|
+
|
|
135
|
+
expect(screen.getByTestId('error-info-note')).toBeInTheDocument();
|
|
136
|
+
expect(screen.getByTestId('error-message')).toHaveTextContent('Mobile error');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('includes rule information in error messages when available', () => {
|
|
140
|
+
const mockValidation = {
|
|
141
|
+
getAllIssues: () => [
|
|
142
|
+
{ message: 'Alt attribute required', severity: 'error', rule: 'alt-require', line: 5 }
|
|
143
|
+
],
|
|
144
|
+
isClean: () => false,
|
|
145
|
+
isValidating: false
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
render(<ValidationErrorDisplay validation={mockValidation} />);
|
|
149
|
+
|
|
150
|
+
expect(screen.getByTestId('error-message')).toHaveTextContent('Alt attribute required Line 5. • alt-require');
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ValidationErrorDisplay - HTML Editor validation
|
|
2
|
+
* ValidationErrorDisplay - HTML Editor validation using ErrorInfoNote
|
|
3
3
|
*
|
|
4
|
-
* This component
|
|
5
|
-
*
|
|
4
|
+
* This component integrates the existing ErrorInfoNote component with the HTML Editor's
|
|
5
|
+
* validation system, providing a consistent error display that matches the Figma design.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import React
|
|
8
|
+
import React from 'react';
|
|
9
9
|
import PropTypes from 'prop-types';
|
|
10
10
|
|
|
11
|
-
import
|
|
12
|
-
import
|
|
11
|
+
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
12
|
+
import ErrorInfoNote from '../../../ErrorInfoNote';
|
|
13
|
+
|
|
14
|
+
import { transformValidationToErrorInfo, hasValidationErrors } from '../../utils/validationAdapter';
|
|
15
|
+
import { HTML_EDITOR_VARIANTS } from '../../constants';
|
|
13
16
|
|
|
14
17
|
// Styles
|
|
15
18
|
import './_validationErrorDisplay.scss';
|
|
@@ -17,72 +20,51 @@ import './_validationErrorDisplay.scss';
|
|
|
17
20
|
/**
|
|
18
21
|
* ValidationErrorDisplay Component
|
|
19
22
|
*
|
|
20
|
-
* Displays validation errors using the
|
|
23
|
+
* Displays validation errors using the existing ErrorInfoNote component
|
|
21
24
|
*/
|
|
22
25
|
const ValidationErrorDisplay = ({
|
|
23
26
|
validation,
|
|
27
|
+
variant = HTML_EDITOR_VARIANTS.EMAIL,
|
|
24
28
|
onErrorClick,
|
|
25
|
-
|
|
26
|
-
isLiquidEnabled = false,
|
|
27
|
-
className = '',
|
|
29
|
+
className = ''
|
|
28
30
|
}) => {
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// Handle close - dismiss temporarily
|
|
33
|
-
const handleClose = () => {
|
|
34
|
-
setIsDismissed(true);
|
|
35
|
-
if (onClose) {
|
|
36
|
-
onClose();
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
// Reset dismissed state when validation changes (new errors appear)
|
|
41
|
-
React.useEffect(() => {
|
|
42
|
-
if (hasValidationErrors(validation)) {
|
|
43
|
-
setIsDismissed(false);
|
|
44
|
-
}
|
|
45
|
-
}, [validation]);
|
|
46
|
-
|
|
47
|
-
// Don't render if no validation, no errors, or dismissed
|
|
48
|
-
if (!hasValidationErrors(validation) || isDismissed) {
|
|
31
|
+
// Don't render if no validation or no errors
|
|
32
|
+
if (!hasValidationErrors(validation)) {
|
|
49
33
|
return null;
|
|
50
34
|
}
|
|
51
35
|
|
|
36
|
+
// Transform validation data to ErrorInfoNote format
|
|
37
|
+
const errorData = transformValidationToErrorInfo(validation, variant);
|
|
38
|
+
const { errorMessages } = errorData || {};
|
|
39
|
+
|
|
40
|
+
// Handle error click if provided
|
|
41
|
+
const handleErrorClick = (error) => {
|
|
42
|
+
onErrorClick?.(error);
|
|
43
|
+
};
|
|
44
|
+
|
|
52
45
|
return (
|
|
53
|
-
<
|
|
46
|
+
<CapRow
|
|
54
47
|
className={`validation-error-display ${className}`}
|
|
55
48
|
role="alert"
|
|
56
49
|
aria-live="polite"
|
|
57
50
|
aria-label="Validation errors"
|
|
58
51
|
>
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
onErrorClick={
|
|
62
|
-
onClose={handleClose}
|
|
63
|
-
isLiquidEnabled={isLiquidEnabled}
|
|
52
|
+
<ErrorInfoNote
|
|
53
|
+
errorMessages={errorMessages}
|
|
54
|
+
onErrorClick={handleErrorClick}
|
|
64
55
|
/>
|
|
65
|
-
</
|
|
56
|
+
</CapRow>
|
|
66
57
|
);
|
|
67
58
|
};
|
|
68
59
|
|
|
69
60
|
ValidationErrorDisplay.propTypes = {
|
|
70
61
|
validation: PropTypes.shape({
|
|
71
62
|
isValidating: PropTypes.bool,
|
|
72
|
-
getAllIssues: PropTypes.func
|
|
63
|
+
getAllIssues: PropTypes.func
|
|
73
64
|
}),
|
|
65
|
+
variant: PropTypes.oneOf(Object.values(HTML_EDITOR_VARIANTS)),
|
|
74
66
|
onErrorClick: PropTypes.func,
|
|
75
|
-
|
|
76
|
-
isLiquidEnabled: PropTypes.bool,
|
|
77
|
-
className: PropTypes.string,
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
ValidationErrorDisplay.defaultProps = {
|
|
81
|
-
validation: null,
|
|
82
|
-
onErrorClick: null,
|
|
83
|
-
onClose: null,
|
|
84
|
-
isLiquidEnabled: false,
|
|
85
|
-
className: '',
|
|
67
|
+
className: PropTypes.string
|
|
86
68
|
};
|
|
87
69
|
|
|
88
70
|
export default ValidationErrorDisplay;
|
|
@@ -5,17 +5,17 @@
|
|
|
5
5
|
@import '~@capillarytech/cap-ui-library/styles/_variables.scss';
|
|
6
6
|
|
|
7
7
|
.validation-panel {
|
|
8
|
-
border: 1px solid
|
|
9
|
-
border-radius:
|
|
10
|
-
background:
|
|
8
|
+
border: 1px solid #d9d9d9;
|
|
9
|
+
border-radius: 6px;
|
|
10
|
+
background: #fff;
|
|
11
11
|
|
|
12
12
|
&--loading {
|
|
13
|
-
padding:
|
|
13
|
+
padding: 16px;
|
|
14
14
|
text-align: center;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
&--clean {
|
|
18
|
-
padding:
|
|
18
|
+
padding: 16px;
|
|
19
19
|
text-align: center;
|
|
20
20
|
background: #f6ffed;
|
|
21
21
|
border-color: #b7eb8f;
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
display: flex;
|
|
26
26
|
align-items: center;
|
|
27
27
|
justify-content: center;
|
|
28
|
-
gap:
|
|
28
|
+
gap: 8px;
|
|
29
29
|
color: #666;
|
|
30
30
|
font-size: 14px;
|
|
31
31
|
}
|
|
@@ -34,36 +34,36 @@
|
|
|
34
34
|
display: flex;
|
|
35
35
|
align-items: center;
|
|
36
36
|
justify-content: center;
|
|
37
|
-
gap:
|
|
37
|
+
gap: 8px;
|
|
38
38
|
color: #52c41a;
|
|
39
39
|
font-size: 14px;
|
|
40
|
-
font-weight:
|
|
40
|
+
font-weight: 500;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
&__summary {
|
|
44
44
|
display: flex;
|
|
45
|
-
gap:
|
|
46
|
-
padding:
|
|
47
|
-
background:
|
|
48
|
-
border-bottom: 1px solid
|
|
49
|
-
border-radius:
|
|
45
|
+
gap: 16px;
|
|
46
|
+
padding: 12px 16px;
|
|
47
|
+
background: #fafafa;
|
|
48
|
+
border-bottom: 1px solid #d9d9d9;
|
|
49
|
+
border-radius: 6px 6px 0 0;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
&__summary-item {
|
|
53
53
|
display: flex;
|
|
54
54
|
align-items: center;
|
|
55
|
-
gap:
|
|
56
|
-
font-size:
|
|
55
|
+
gap: 4px;
|
|
56
|
+
font-size: 12px;
|
|
57
57
|
color: #666;
|
|
58
58
|
|
|
59
59
|
&--security {
|
|
60
60
|
color: #ff4d4f;
|
|
61
|
-
font-weight:
|
|
61
|
+
font-weight: 500;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
span:first-of-type {
|
|
65
65
|
font-weight: 600;
|
|
66
|
-
margin-left:
|
|
66
|
+
margin-left: 2px;
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
border: none;
|
|
73
73
|
|
|
74
74
|
&:last-child {
|
|
75
|
-
border-radius: 0 0
|
|
75
|
+
border-radius: 0 0 6px 6px;
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -123,8 +123,8 @@
|
|
|
123
123
|
&__title {
|
|
124
124
|
display: flex;
|
|
125
125
|
align-items: center;
|
|
126
|
-
gap:
|
|
127
|
-
font-weight:
|
|
126
|
+
gap: 8px;
|
|
127
|
+
font-weight: 500;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
&__issues {
|
|
@@ -133,41 +133,41 @@
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
&__sanitization {
|
|
136
|
-
border-top: 1px solid
|
|
136
|
+
border-top: 1px solid #d9d9d9;
|
|
137
137
|
background: #f9f9f9;
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
&__sanitization-header {
|
|
141
141
|
display: flex;
|
|
142
142
|
align-items: center;
|
|
143
|
-
gap:
|
|
144
|
-
padding:
|
|
145
|
-
font-weight:
|
|
143
|
+
gap: 8px;
|
|
144
|
+
padding: 12px 16px;
|
|
145
|
+
font-weight: 500;
|
|
146
146
|
color: #666;
|
|
147
147
|
font-size: 13px;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
&__sanitization-list {
|
|
151
|
-
padding: 0
|
|
151
|
+
padding: 0 16px 12px;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
&__sanitization-item {
|
|
155
|
-
padding:
|
|
156
|
-
font-size:
|
|
155
|
+
padding: 4px 0;
|
|
156
|
+
font-size: 12px;
|
|
157
157
|
color: #8c8c8c;
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
.validation-issue {
|
|
162
162
|
display: flex;
|
|
163
|
-
gap:
|
|
164
|
-
padding:
|
|
163
|
+
gap: 12px;
|
|
164
|
+
padding: 12px 16px;
|
|
165
165
|
border-bottom: 1px solid #f0f0f0;
|
|
166
166
|
cursor: pointer;
|
|
167
167
|
transition: background-color 0.2s;
|
|
168
168
|
|
|
169
169
|
&:hover {
|
|
170
|
-
background:
|
|
170
|
+
background: #fafafa;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
&:last-child {
|
|
@@ -189,22 +189,6 @@
|
|
|
189
189
|
&__icon {
|
|
190
190
|
flex-shrink: 0;
|
|
191
191
|
margin-top: 2px;
|
|
192
|
-
|
|
193
|
-
&--error {
|
|
194
|
-
color: #ff4d4f;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
&--info {
|
|
198
|
-
color: #1890ff;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
&--warning {
|
|
202
|
-
color: #faad14;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
&--security {
|
|
206
|
-
color: #ff4d4f;
|
|
207
|
-
}
|
|
208
192
|
}
|
|
209
193
|
|
|
210
194
|
&__content {
|
|
@@ -216,14 +200,14 @@
|
|
|
216
200
|
font-size: 13px;
|
|
217
201
|
line-height: 1.4;
|
|
218
202
|
color: #262626;
|
|
219
|
-
margin-bottom:
|
|
203
|
+
margin-bottom: 4px;
|
|
220
204
|
word-break: break-word;
|
|
221
205
|
}
|
|
222
206
|
|
|
223
207
|
&__meta {
|
|
224
208
|
display: flex;
|
|
225
209
|
flex-wrap: wrap;
|
|
226
|
-
gap:
|
|
210
|
+
gap: 8px;
|
|
227
211
|
font-size: 11px;
|
|
228
212
|
color: #8c8c8c;
|
|
229
213
|
}
|
|
@@ -246,9 +230,9 @@
|
|
|
246
230
|
&__source {
|
|
247
231
|
display: flex;
|
|
248
232
|
align-items: center;
|
|
249
|
-
gap:
|
|
233
|
+
gap: 4px;
|
|
250
234
|
background: #f0f0f0;
|
|
251
|
-
padding:
|
|
235
|
+
padding: 2px 6px;
|
|
252
236
|
border-radius: 3px;
|
|
253
237
|
}
|
|
254
238
|
}
|
|
@@ -21,14 +21,13 @@ import ShieldOutlined from '@ant-design/icons/ShieldOutlined';
|
|
|
21
21
|
import BugOutlined from '@ant-design/icons/BugOutlined';
|
|
22
22
|
import CodeOutlined from '@ant-design/icons/CodeOutlined';
|
|
23
23
|
import EyeInvisibleOutlined from '@ant-design/icons/EyeInvisibleOutlined';
|
|
24
|
-
import
|
|
24
|
+
import CheckCircleOutlined from '@ant-design/icons/CheckCircleOutlined';
|
|
25
25
|
|
|
26
26
|
import messages from './messages';
|
|
27
|
-
import { BLOCKING_ERROR_RULE_IDS } from '../../constants';
|
|
28
27
|
import './_validationPanel.scss';
|
|
29
|
-
import { SEVERITY } from './constants';
|
|
30
28
|
|
|
31
29
|
const { Panel } = Collapse;
|
|
30
|
+
|
|
32
31
|
/**
|
|
33
32
|
* ValidationPanel Component
|
|
34
33
|
*/
|
|
@@ -38,7 +37,7 @@ const ValidationPanel = ({
|
|
|
38
37
|
onErrorClick,
|
|
39
38
|
showLineNumbers = true,
|
|
40
39
|
groupBySource = false,
|
|
41
|
-
variant = 'email'
|
|
40
|
+
variant = 'email'
|
|
42
41
|
}) => {
|
|
43
42
|
const [activeKeys, setActiveKeys] = useState(['errors', 'warnings']);
|
|
44
43
|
|
|
@@ -57,50 +56,31 @@ const ValidationPanel = ({
|
|
|
57
56
|
groups[source].push(issue);
|
|
58
57
|
return groups;
|
|
59
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
|
+
};
|
|
60
65
|
}
|
|
61
|
-
return {
|
|
62
|
-
errors: allIssues.filter((issue) => issue.severity === SEVERITY.ERROR),
|
|
63
|
-
warnings: allIssues.filter((issue) => issue.severity === SEVERITY.WARNING),
|
|
64
|
-
info: allIssues.filter((issue) => issue.severity === SEVERITY.INFO),
|
|
65
|
-
};
|
|
66
66
|
}, [validation, groupBySource]);
|
|
67
67
|
|
|
68
|
-
// Check if an issue is a blocking error (API error, Rule Group #1, or client-side Liquid validation errors)
|
|
69
|
-
const isBlockingError = (issue) => {
|
|
70
|
-
const { rule, source, severity } = issue || {};
|
|
71
|
-
// API errors are blocking
|
|
72
|
-
if (rule === 'liquid-api-validation' || rule === 'standard-api-validation') {
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
// Client-side Liquid validation errors are blocking (genuine syntax errors)
|
|
76
|
-
if (source === 'liquid-validator' && severity === 'error') {
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
79
|
-
// Rule Group #1 errors are blocking
|
|
80
|
-
if (BLOCKING_ERROR_RULE_IDS.includes(rule)) {
|
|
81
|
-
return true;
|
|
82
|
-
}
|
|
83
|
-
return false;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
68
|
// Get icon for issue type
|
|
87
|
-
|
|
88
|
-
const getIssueIcon = (issue) => {
|
|
89
|
-
const { source, severity } = issue || {};
|
|
69
|
+
const getIssueIcon = (severity, source) => {
|
|
90
70
|
if (source === 'security') {
|
|
91
|
-
return <ShieldOutlined
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Only show error icon for blocking errors (API errors or Rule Group #1)
|
|
95
|
-
if (isBlockingError(issue)) {
|
|
96
|
-
return <CapIcon type="error-icon" className="validation-issue__icon validation-issue__icon--error" />;
|
|
71
|
+
return <ShieldOutlined style={{ color: '#ff4d4f' }} />;
|
|
97
72
|
}
|
|
98
73
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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' }} />;
|
|
102
83
|
}
|
|
103
|
-
return <CapIcon type="alert-warning" className="validation-issue__icon validation-issue__icon--warning" />;
|
|
104
84
|
};
|
|
105
85
|
|
|
106
86
|
// Get source icon
|
|
@@ -126,7 +106,7 @@ const ValidationPanel = ({
|
|
|
126
106
|
line: issue.line,
|
|
127
107
|
column: issue.column || 1,
|
|
128
108
|
message: issue.message,
|
|
129
|
-
severity: issue.severity
|
|
109
|
+
severity: issue.severity
|
|
130
110
|
});
|
|
131
111
|
}
|
|
132
112
|
};
|
|
@@ -146,7 +126,7 @@ const ValidationPanel = ({
|
|
|
146
126
|
}}
|
|
147
127
|
>
|
|
148
128
|
<div className="validation-issue__icon">
|
|
149
|
-
{getIssueIcon(issue)}
|
|
129
|
+
{getIssueIcon(issue.severity, issue.source)}
|
|
150
130
|
</div>
|
|
151
131
|
|
|
152
132
|
<div className="validation-issue__content">
|
|
@@ -157,12 +137,10 @@ const ValidationPanel = ({
|
|
|
157
137
|
<div className="validation-issue__meta">
|
|
158
138
|
{showLineNumbers && issue.line && (
|
|
159
139
|
<span className="validation-issue__location">
|
|
160
|
-
<FormattedMessage
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
column: issue.column || 1,
|
|
165
|
-
}} />
|
|
140
|
+
<FormattedMessage {...messages.lineColumn} values={{
|
|
141
|
+
line: issue.line,
|
|
142
|
+
column: issue.column || 1
|
|
143
|
+
}} />
|
|
166
144
|
</span>
|
|
167
145
|
)}
|
|
168
146
|
|
|
@@ -184,24 +162,17 @@ const ValidationPanel = ({
|
|
|
184
162
|
);
|
|
185
163
|
|
|
186
164
|
// Render panel header with count
|
|
187
|
-
const renderPanelHeader = (title, count, severity
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
</span>
|
|
199
|
-
{count > 0 && (
|
|
200
|
-
<Badge count={count} style={{ backgroundColor: getSeverityColor(severity) }} />
|
|
201
|
-
)}
|
|
202
|
-
</div>
|
|
203
|
-
);
|
|
204
|
-
};
|
|
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
|
+
);
|
|
205
176
|
|
|
206
177
|
// Get severity color
|
|
207
178
|
const getSeverityColor = (severity) => {
|
|
@@ -270,14 +241,14 @@ const ValidationPanel = ({
|
|
|
270
241
|
: messages[key] || { id: `htmlEditor.validation.${key}`, defaultMessage: key };
|
|
271
242
|
|
|
272
243
|
const severity = isSourceGroup
|
|
273
|
-
? (issues.find(
|
|
274
|
-
|
|
244
|
+
? (issues.find(i => i.severity === 'error') ? 'error' :
|
|
245
|
+
issues.find(i => i.severity === 'warning') ? 'warning' : 'info')
|
|
275
246
|
: key;
|
|
276
247
|
|
|
277
248
|
return (
|
|
278
249
|
<Panel
|
|
279
250
|
key={key}
|
|
280
|
-
header={renderPanelHeader(title, issues.length, severity
|
|
251
|
+
header={renderPanelHeader(title, issues.length, severity)}
|
|
281
252
|
className={`validation-panel__panel validation-panel__panel--${severity}`}
|
|
282
253
|
>
|
|
283
254
|
<div className="validation-panel__issues">
|
|
@@ -320,7 +291,7 @@ ValidationPanel.propTypes = {
|
|
|
320
291
|
onErrorClick: PropTypes.func,
|
|
321
292
|
showLineNumbers: PropTypes.bool,
|
|
322
293
|
groupBySource: PropTypes.bool,
|
|
323
|
-
variant: PropTypes.oneOf(['email', 'inapp'])
|
|
294
|
+
variant: PropTypes.oneOf(['email', 'inapp'])
|
|
324
295
|
};
|
|
325
296
|
|
|
326
297
|
export default ValidationPanel;
|