@capillarytech/creatives-library 8.0.262-alpha.1 → 8.0.263
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/package.json +1 -1
- package/v2Components/ErrorInfoNote/index.js +58 -113
- package/v2Components/ErrorInfoNote/messages.js +8 -12
- package/v2Components/HtmlEditor/HTMLEditor.js +48 -182
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +11 -15
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +7 -8
- package/v2Components/HtmlEditor/_htmlEditor.scss +6 -6
- package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +1 -1
- package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +0 -1
- package/v2Components/HtmlEditor/components/EditorToolbar/index.js +27 -2
- package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +4 -4
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +17 -0
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +15 -28
- package/v2Components/HtmlEditor/components/ValidationPanel/index.js +10 -33
- package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +48 -13
- package/v2Components/HtmlEditor/components/ValidationTabs/index.js +77 -146
- package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +14 -14
- package/v2Components/HtmlEditor/constants.js +3 -0
- package/v2Components/HtmlEditor/hooks/useValidation.js +34 -13
- package/v2Components/HtmlEditor/messages.js +10 -0
- package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +38 -36
- package/v2Components/HtmlEditor/utils/validationConstants.js +3 -4
- package/v2Components/MobilePushPreviewV2/constants.js +6 -0
- package/v2Components/MobilePushPreviewV2/index.js +4 -3
- package/v2Containers/CreativesContainer/index.js +1 -3
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +6 -9
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +5 -49
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +23 -38
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +4 -2
|
@@ -465,9 +465,8 @@ describe('HTMLEditor - API Validation Errors', () => {
|
|
|
465
465
|
expect.objectContaining({
|
|
466
466
|
validationComplete: false,
|
|
467
467
|
issueCounts: expect.objectContaining({
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
liquid: 0,
|
|
468
|
+
errors: 0,
|
|
469
|
+
warnings: 0,
|
|
471
470
|
total: 0,
|
|
472
471
|
}),
|
|
473
472
|
})
|
|
@@ -704,13 +703,12 @@ describe('HTMLEditor - API Validation Errors', () => {
|
|
|
704
703
|
|
|
705
704
|
renderWithIntl({ onValidationChange });
|
|
706
705
|
|
|
707
|
-
//
|
|
706
|
+
// HTML hint issues are non-blocking (warnings)
|
|
708
707
|
expect(onValidationChange).toHaveBeenCalledWith(
|
|
709
708
|
expect.objectContaining({
|
|
710
709
|
issueCounts: expect.objectContaining({
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
liquid: 0,
|
|
710
|
+
errors: 0,
|
|
711
|
+
warnings: 1,
|
|
714
712
|
total: 1,
|
|
715
713
|
}),
|
|
716
714
|
})
|
|
@@ -742,13 +740,12 @@ describe('HTMLEditor - API Validation Errors', () => {
|
|
|
742
740
|
|
|
743
741
|
renderWithIntl({ onValidationChange });
|
|
744
742
|
|
|
745
|
-
//
|
|
743
|
+
// Label/tag-pair issues are non-blocking (warnings)
|
|
746
744
|
expect(onValidationChange).toHaveBeenCalledWith(
|
|
747
745
|
expect.objectContaining({
|
|
748
746
|
issueCounts: expect.objectContaining({
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
liquid: 0,
|
|
747
|
+
errors: 0,
|
|
748
|
+
warnings: 1,
|
|
752
749
|
total: 1,
|
|
753
750
|
}),
|
|
754
751
|
})
|
|
@@ -780,13 +777,12 @@ describe('HTMLEditor - API Validation Errors', () => {
|
|
|
780
777
|
|
|
781
778
|
renderWithIntl({ onValidationChange });
|
|
782
779
|
|
|
783
|
-
//
|
|
780
|
+
// Liquid-validator with severity error is blocking (errors)
|
|
784
781
|
expect(onValidationChange).toHaveBeenCalledWith(
|
|
785
782
|
expect.objectContaining({
|
|
786
783
|
issueCounts: expect.objectContaining({
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
liquid: 1,
|
|
784
|
+
errors: 1,
|
|
785
|
+
warnings: 0,
|
|
790
786
|
total: 1,
|
|
791
787
|
}),
|
|
792
788
|
})
|
|
@@ -3106,19 +3106,18 @@ describe('HTMLEditor', () => {
|
|
|
3106
3106
|
});
|
|
3107
3107
|
|
|
3108
3108
|
const issueCounts = ref.current.getIssueCounts();
|
|
3109
|
+
// None of the mock issues are blocking (no BLOCKING_ERROR_RULE_IDS, liquid has no severity 'error')
|
|
3109
3110
|
expect(issueCounts).toEqual({
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
liquid: 1,
|
|
3111
|
+
errors: 0,
|
|
3112
|
+
warnings: 3,
|
|
3113
3113
|
total: 3,
|
|
3114
3114
|
});
|
|
3115
3115
|
|
|
3116
3116
|
const validationState = ref.current.getValidationState();
|
|
3117
3117
|
expect(validationState.hasErrors).toBe(true);
|
|
3118
3118
|
expect(validationState.issueCounts).toEqual({
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
liquid: 1,
|
|
3119
|
+
errors: 0,
|
|
3120
|
+
warnings: 3,
|
|
3122
3121
|
total: 3,
|
|
3123
3122
|
});
|
|
3124
3123
|
});
|
|
@@ -3150,10 +3149,10 @@ describe('HTMLEditor', () => {
|
|
|
3150
3149
|
});
|
|
3151
3150
|
|
|
3152
3151
|
expect(ref.current.getIssueCounts()).toEqual({
|
|
3153
|
-
|
|
3152
|
+
errors: 0, warnings: 0, total: 0,
|
|
3154
3153
|
});
|
|
3155
3154
|
expect(ref.current.getValidationState().issueCounts).toEqual({
|
|
3156
|
-
|
|
3155
|
+
errors: 0, warnings: 0, total: 0,
|
|
3157
3156
|
});
|
|
3158
3157
|
});
|
|
3159
3158
|
});
|
|
@@ -88,12 +88,12 @@
|
|
|
88
88
|
|
|
89
89
|
&__content {
|
|
90
90
|
height: calc(95vh - 5%); // Same as editor pane - maximize to full available height
|
|
91
|
-
max-height: calc(95vh -
|
|
91
|
+
max-height: calc(95vh - 30%);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
iframe {
|
|
95
95
|
height: calc(95vh - 5%); // Same as editor pane - maximize to full available height
|
|
96
|
-
max-height: calc(95vh -
|
|
96
|
+
max-height: calc(95vh - 30%);
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
}
|
|
@@ -281,12 +281,12 @@
|
|
|
281
281
|
|
|
282
282
|
&__content {
|
|
283
283
|
height: calc(95vh - 5%); // Same as editor pane - maximize to full available height
|
|
284
|
-
max-height: calc(95vh -
|
|
284
|
+
max-height: calc(95vh - 30%);
|
|
285
285
|
}
|
|
286
286
|
|
|
287
287
|
iframe {
|
|
288
288
|
height: calc(95vh - 5%); // Same as editor pane - maximize to full available height
|
|
289
|
-
max-height: calc(95vh -
|
|
289
|
+
max-height: calc(95vh - 30%);
|
|
290
290
|
}
|
|
291
291
|
}
|
|
292
292
|
}
|
|
@@ -299,12 +299,12 @@
|
|
|
299
299
|
|
|
300
300
|
&__content {
|
|
301
301
|
height: calc(95vh - 5%); // Same as editor pane - maximize to full available height
|
|
302
|
-
max-height: calc(95vh -
|
|
302
|
+
max-height: calc(95vh - 30%);
|
|
303
303
|
}
|
|
304
304
|
|
|
305
305
|
iframe {
|
|
306
306
|
height: calc(95vh - 5%); // Same as editor pane - maximize to full available height
|
|
307
|
-
max-height: calc(95vh -
|
|
307
|
+
max-height: calc(95vh - 30%);
|
|
308
308
|
}
|
|
309
309
|
}
|
|
310
310
|
}
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
|
|
67
67
|
// Add Label button visibility: 50% opacity while typing, 100% on hover
|
|
68
68
|
.code-editor-pane__actions {
|
|
69
|
-
opacity: 0.
|
|
69
|
+
opacity: 0.8; // 80% transparent while typing
|
|
70
70
|
pointer-events: auto; // Always clickable
|
|
71
71
|
transition: opacity 0.2s ease;
|
|
72
72
|
|
|
@@ -12,17 +12,19 @@
|
|
|
12
12
|
import React from 'react';
|
|
13
13
|
import PropTypes from 'prop-types';
|
|
14
14
|
import { Layout, Typography } from 'antd';
|
|
15
|
-
import { injectIntl, intlShape } from 'react-intl';
|
|
15
|
+
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
|
|
16
16
|
|
|
17
17
|
import CapButton from '@capillarytech/cap-ui-library/CapButton';
|
|
18
18
|
import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
|
|
19
19
|
import CapRow from '@capillarytech/cap-ui-library/CapRow';
|
|
20
|
+
import CapTooltipWithInfo from '@capillarytech/cap-ui-library/CapTooltipWithInfo';
|
|
21
|
+
import { FONT_COLOR_01, CAP_SPACE_08, CAP_SPACE_02 } from '@capillarytech/cap-ui-library/styled/variables';
|
|
20
22
|
|
|
21
23
|
// Component imports
|
|
22
24
|
import { useEditorContext } from '../common/EditorContext';
|
|
23
25
|
|
|
24
26
|
// Constants
|
|
25
|
-
import { HTML_EDITOR_VARIANTS } from '../../constants';
|
|
27
|
+
import { HTML_EDITOR_VARIANTS, LIQUID_DOC_URL } from '../../constants';
|
|
26
28
|
|
|
27
29
|
// Styles
|
|
28
30
|
import './_editorToolbar.scss';
|
|
@@ -66,6 +68,29 @@ const EditorToolbar = ({
|
|
|
66
68
|
: intl.formatMessage(messages.htmlEditor)
|
|
67
69
|
}
|
|
68
70
|
</Text>
|
|
71
|
+
<CapTooltipWithInfo
|
|
72
|
+
title={(
|
|
73
|
+
<FormattedMessage
|
|
74
|
+
{...messages.htmlEditorTooltip}
|
|
75
|
+
values={{
|
|
76
|
+
docLink: (
|
|
77
|
+
<a
|
|
78
|
+
href={LIQUID_DOC_URL}
|
|
79
|
+
target="_blank"
|
|
80
|
+
rel="noopener noreferrer"
|
|
81
|
+
>
|
|
82
|
+
<FormattedMessage {...messages.viewDocumentation} />
|
|
83
|
+
</a>
|
|
84
|
+
),
|
|
85
|
+
}}
|
|
86
|
+
/>
|
|
87
|
+
)}
|
|
88
|
+
infoIconProps={{
|
|
89
|
+
style: { marginLeft: CAP_SPACE_08, color: FONT_COLOR_01 },
|
|
90
|
+
}}
|
|
91
|
+
autoAdjustOverflow
|
|
92
|
+
placement="top"
|
|
93
|
+
/>
|
|
69
94
|
</CapRow>
|
|
70
95
|
)}
|
|
71
96
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
.preview-pane {
|
|
8
8
|
height: 100%;
|
|
9
|
-
max-height:
|
|
9
|
+
max-height: 33.25rem;
|
|
10
10
|
position: relative;
|
|
11
11
|
background-color: $CAP_G09;
|
|
12
12
|
padding: 1rem;
|
|
@@ -67,8 +67,8 @@
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
&__content {
|
|
70
|
-
height: calc(100%
|
|
71
|
-
max-height:
|
|
70
|
+
height: calc(100%);
|
|
71
|
+
max-height: 34.25rem;
|
|
72
72
|
display: flex;
|
|
73
73
|
align-items: center;
|
|
74
74
|
justify-content: center;
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
background-color: $CAP_WHITE;
|
|
84
84
|
width: 100%;
|
|
85
85
|
height: 28.125rem;
|
|
86
|
-
max-height:
|
|
86
|
+
max-height: 34.25rem;
|
|
87
87
|
|
|
88
88
|
&--mobile {
|
|
89
89
|
// Width and height now controlled by React inline styles
|
package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ValidationErrorDisplay Styles
|
|
3
|
+
*
|
|
4
|
+
* When collapsed, panel sticks to footer (header bar only visible).
|
|
3
5
|
*/
|
|
4
6
|
|
|
5
7
|
@import '~@capillarytech/cap-ui-library/styles/_variables.scss';
|
|
@@ -9,6 +11,7 @@
|
|
|
9
11
|
flex-direction: column;
|
|
10
12
|
width: 100%;
|
|
11
13
|
margin-bottom: 1rem;
|
|
14
|
+
flex-shrink: 0;
|
|
12
15
|
|
|
13
16
|
// Ensure proper spacing when used in different contexts
|
|
14
17
|
&:last-child {
|
|
@@ -16,6 +19,12 @@
|
|
|
16
19
|
padding-bottom: 0.25rem;
|
|
17
20
|
}
|
|
18
21
|
|
|
22
|
+
// Collapsed: panel stays in footer as a slim bar (tabs + expand arrow only)
|
|
23
|
+
&--collapsed {
|
|
24
|
+
margin-bottom: 0;
|
|
25
|
+
padding-bottom: 0.25rem;
|
|
26
|
+
}
|
|
27
|
+
|
|
19
28
|
// Integration with ErrorInfoNote component
|
|
20
29
|
.error-info-note {
|
|
21
30
|
width: 100%;
|
|
@@ -24,9 +33,17 @@
|
|
|
24
33
|
// Responsive adjustments
|
|
25
34
|
@media (max-width: 768px) {
|
|
26
35
|
margin-bottom: 0.75rem;
|
|
36
|
+
|
|
37
|
+
&.validation-error-display--collapsed {
|
|
38
|
+
margin-bottom: 0;
|
|
39
|
+
}
|
|
27
40
|
}
|
|
28
41
|
|
|
29
42
|
@media (max-width: 576px) {
|
|
30
43
|
margin-bottom: 0.5rem;
|
|
44
|
+
|
|
45
|
+
&.validation-error-display--collapsed {
|
|
46
|
+
margin-bottom: 0;
|
|
47
|
+
}
|
|
31
48
|
}
|
|
32
49
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ValidationErrorDisplay - HTML Editor validation display
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Displays validation errors and warnings in two tabs (Errors, Warnings).
|
|
5
|
+
* Panel can be collapsed to a sticky footer bar; it does not disappear.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import React, { useState } from 'react';
|
|
@@ -17,41 +17,31 @@ import './_validationErrorDisplay.scss';
|
|
|
17
17
|
/**
|
|
18
18
|
* ValidationErrorDisplay Component
|
|
19
19
|
*
|
|
20
|
-
* Displays validation
|
|
20
|
+
* Displays validation issues in Errors and Warnings tabs; collapse toggles visibility but panel stays in footer.
|
|
21
21
|
*/
|
|
22
22
|
const ValidationErrorDisplay = ({
|
|
23
23
|
validation,
|
|
24
24
|
onErrorClick,
|
|
25
|
-
onClose,
|
|
26
|
-
isLiquidEnabled = false,
|
|
27
25
|
className = '',
|
|
28
26
|
}) => {
|
|
29
|
-
|
|
30
|
-
const [isDismissed, setIsDismissed] = useState(false);
|
|
27
|
+
const [isCollapsed, setIsCollapsed] = useState(false);
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
setIsDismissed(true);
|
|
35
|
-
if (onClose) {
|
|
36
|
-
onClose();
|
|
37
|
-
}
|
|
29
|
+
const handleToggleCollapse = () => {
|
|
30
|
+
setIsCollapsed((prev) => !prev);
|
|
38
31
|
};
|
|
39
32
|
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
}, [validation]);
|
|
33
|
+
// Expand panel when user clicks redirection (so panel does not stay stuck to footer)
|
|
34
|
+
const handleExpand = () => {
|
|
35
|
+
setIsCollapsed(false);
|
|
36
|
+
};
|
|
46
37
|
|
|
47
|
-
|
|
48
|
-
if (!hasValidationErrors(validation) || isDismissed) {
|
|
38
|
+
if (!hasValidationErrors(validation)) {
|
|
49
39
|
return null;
|
|
50
40
|
}
|
|
51
41
|
|
|
52
42
|
return (
|
|
53
43
|
<div
|
|
54
|
-
className={`validation-error-display ${className}`}
|
|
44
|
+
className={`validation-error-display ${isCollapsed ? 'validation-error-display--collapsed' : ''} ${className}`}
|
|
55
45
|
role="alert"
|
|
56
46
|
aria-live="polite"
|
|
57
47
|
aria-label="Validation errors"
|
|
@@ -59,8 +49,9 @@ const ValidationErrorDisplay = ({
|
|
|
59
49
|
<ValidationTabs
|
|
60
50
|
validation={validation}
|
|
61
51
|
onErrorClick={onErrorClick}
|
|
62
|
-
|
|
63
|
-
|
|
52
|
+
isCollapsed={isCollapsed}
|
|
53
|
+
onToggleCollapse={handleToggleCollapse}
|
|
54
|
+
onExpand={handleExpand}
|
|
64
55
|
/>
|
|
65
56
|
</div>
|
|
66
57
|
);
|
|
@@ -72,16 +63,12 @@ ValidationErrorDisplay.propTypes = {
|
|
|
72
63
|
getAllIssues: PropTypes.func,
|
|
73
64
|
}),
|
|
74
65
|
onErrorClick: PropTypes.func,
|
|
75
|
-
onClose: PropTypes.func,
|
|
76
|
-
isLiquidEnabled: PropTypes.bool,
|
|
77
66
|
className: PropTypes.string,
|
|
78
67
|
};
|
|
79
68
|
|
|
80
69
|
ValidationErrorDisplay.defaultProps = {
|
|
81
70
|
validation: null,
|
|
82
71
|
onErrorClick: null,
|
|
83
|
-
onClose: null,
|
|
84
|
-
isLiquidEnabled: false,
|
|
85
72
|
className: '',
|
|
86
73
|
};
|
|
87
74
|
|
|
@@ -27,6 +27,7 @@ import messages from './messages';
|
|
|
27
27
|
import { BLOCKING_ERROR_RULE_IDS } from '../../constants';
|
|
28
28
|
import './_validationPanel.scss';
|
|
29
29
|
import { SEVERITY } from './constants';
|
|
30
|
+
import { ISSUE_SOURCES } from '../../utils/validationConstants';
|
|
30
31
|
|
|
31
32
|
const { Panel } = Collapse;
|
|
32
33
|
/**
|
|
@@ -38,7 +39,6 @@ const ValidationPanel = ({
|
|
|
38
39
|
onErrorClick,
|
|
39
40
|
showLineNumbers = true,
|
|
40
41
|
groupBySource = false,
|
|
41
|
-
variant = 'email',
|
|
42
42
|
}) => {
|
|
43
43
|
const [activeKeys, setActiveKeys] = useState(['errors', 'warnings']);
|
|
44
44
|
|
|
@@ -73,7 +73,7 @@ const ValidationPanel = ({
|
|
|
73
73
|
return true;
|
|
74
74
|
}
|
|
75
75
|
// Client-side Liquid validation errors are blocking (genuine syntax errors)
|
|
76
|
-
if (source ===
|
|
76
|
+
if (source === ISSUE_SOURCES.LIQUID && severity === SEVERITY.ERROR) {
|
|
77
77
|
return true;
|
|
78
78
|
}
|
|
79
79
|
// Rule Group #1 errors are blocking
|
|
@@ -85,23 +85,6 @@ const ValidationPanel = ({
|
|
|
85
85
|
|
|
86
86
|
// Get icon for issue type
|
|
87
87
|
// Blocking errors use error-icon, warnings use warning
|
|
88
|
-
const getIssueIcon = (issue) => {
|
|
89
|
-
const { source, severity } = issue || {};
|
|
90
|
-
if (source === 'security') {
|
|
91
|
-
return <ShieldOutlined className="validation-issue__icon validation-issue__icon--security" />;
|
|
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" />;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// All other issues show as warnings
|
|
100
|
-
if (severity === SEVERITY.INFO) {
|
|
101
|
-
return <CapIcon type="info" className="validation-issue__icon validation-issue__icon--info" />;
|
|
102
|
-
}
|
|
103
|
-
return <CapIcon type="warning" className="validation-issue__icon validation-issue__icon--warning" />;
|
|
104
|
-
};
|
|
105
88
|
|
|
106
89
|
// Get source icon
|
|
107
90
|
const getSourceIcon = (source) => {
|
|
@@ -145,9 +128,6 @@ const ValidationPanel = ({
|
|
|
145
128
|
}
|
|
146
129
|
}}
|
|
147
130
|
>
|
|
148
|
-
<div className="validation-issue__icon">
|
|
149
|
-
{getIssueIcon(issue)}
|
|
150
|
-
</div>
|
|
151
131
|
|
|
152
132
|
<div className="validation-issue__content">
|
|
153
133
|
<div className="validation-issue__message">
|
|
@@ -184,24 +164,21 @@ const ValidationPanel = ({
|
|
|
184
164
|
);
|
|
185
165
|
|
|
186
166
|
// Render panel header with count
|
|
187
|
-
const renderPanelHeader = (title, count, severity, issues = []) =>
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
// Use blocking error icon only if there are blocking errors, otherwise use warning icon
|
|
191
|
-
const iconIssue = hasBlocking ? { rule: 'blocking', source: 'blocking' } : { severity: 'warning' };
|
|
167
|
+
const renderPanelHeader = (title, count, severity, issues = []) =>
|
|
168
|
+
// Check if any issue in this group is a blocking error
|
|
169
|
+
// Use blocking error icon only if there are blocking errors, otherwise use warning icon
|
|
192
170
|
|
|
193
|
-
|
|
171
|
+
(
|
|
194
172
|
<div className="validation-panel__header">
|
|
195
173
|
<span className="validation-panel__title">
|
|
196
|
-
{getIssueIcon(iconIssue)}
|
|
197
174
|
<FormattedMessage {...title} />
|
|
198
175
|
</span>
|
|
199
176
|
{count > 0 && (
|
|
200
177
|
<Badge count={count} style={{ backgroundColor: getSeverityColor(severity) }} />
|
|
201
178
|
)}
|
|
202
179
|
</div>
|
|
203
|
-
)
|
|
204
|
-
|
|
180
|
+
)
|
|
181
|
+
;
|
|
205
182
|
|
|
206
183
|
// Get severity color
|
|
207
184
|
const getSeverityColor = (severity) => {
|
|
@@ -270,8 +247,8 @@ const ValidationPanel = ({
|
|
|
270
247
|
: messages[key] || { id: `htmlEditor.validation.${key}`, defaultMessage: key };
|
|
271
248
|
|
|
272
249
|
const severity = isSourceGroup
|
|
273
|
-
? (issues.find((i) => i.severity ===
|
|
274
|
-
: issues.find((i) => i.severity ===
|
|
250
|
+
? (issues.find((i) => i.severity === SEVERITY.ERROR) ? SEVERITY.ERROR
|
|
251
|
+
: issues.find((i) => i.severity === SEVERITY.WARNING) ? SEVERITY.WARNING : SEVERITY.INFO)
|
|
275
252
|
: key;
|
|
276
253
|
|
|
277
254
|
return (
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
// Override CapTab styles for proper spacing
|
|
24
24
|
.cap-tab-v2 {
|
|
25
25
|
.ant-tabs-bar {
|
|
26
|
-
padding: 0.5rem
|
|
26
|
+
padding-right: 0.5rem;
|
|
27
|
+
padding-top: 0.5rem;
|
|
27
28
|
}
|
|
28
29
|
.ant-tabs-nav {
|
|
29
30
|
margin-bottom: 0;
|
|
@@ -36,27 +37,31 @@
|
|
|
36
37
|
.ant-tabs-tab {
|
|
37
38
|
padding: 0.5rem 0.75rem; // Add horizontal padding for spacing
|
|
38
39
|
margin-right: 0; // Remove margin, use padding instead
|
|
39
|
-
|
|
40
|
-
color: $
|
|
40
|
+
|
|
41
|
+
color: $CAP_G04;
|
|
41
42
|
font-size: 0.875rem;
|
|
42
43
|
font-weight: 400;
|
|
43
44
|
line-height: 1;
|
|
44
45
|
letter-spacing: 0;
|
|
45
46
|
border-radius: 0.25rem;
|
|
46
47
|
border-bottom: none; // Remove bottom border
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
margin-left:
|
|
48
|
+
|
|
49
|
+
&.ant-tabs-tab {
|
|
50
|
+
margin-left: 1rem; // Add space between tabs
|
|
51
|
+
font-weight: 400;
|
|
50
52
|
}
|
|
51
|
-
|
|
53
|
+
|
|
52
54
|
&:hover {
|
|
53
55
|
color: $CAP_COLOR_05;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
&.ant-tabs-tab-active {
|
|
59
|
+
color: $FONT_COLOR_01;
|
|
60
|
+
font-weight: 500;
|
|
61
|
+
|
|
57
62
|
.ant-tabs-tab-btn {
|
|
58
|
-
color: $
|
|
59
|
-
font-weight:
|
|
63
|
+
color: $FONT_COLOR_01;
|
|
64
|
+
font-weight: 500;
|
|
60
65
|
}
|
|
61
66
|
}
|
|
62
67
|
}
|
|
@@ -70,12 +75,20 @@
|
|
|
70
75
|
padding-bottom: 0; // No bottom padding
|
|
71
76
|
}
|
|
72
77
|
}
|
|
78
|
+
|
|
79
|
+
.ant-tabs-content {
|
|
80
|
+
margin-top: $CAP_SPACE_08;
|
|
81
|
+
}
|
|
73
82
|
}
|
|
74
83
|
|
|
75
84
|
&__tab-label {
|
|
76
85
|
display: flex;
|
|
77
86
|
align-items: center;
|
|
78
87
|
gap: 0.25rem;
|
|
88
|
+
|
|
89
|
+
&--warnings {
|
|
90
|
+
color: $CAP_G01;
|
|
91
|
+
}
|
|
79
92
|
}
|
|
80
93
|
|
|
81
94
|
&__tab-count {
|
|
@@ -89,7 +102,7 @@
|
|
|
89
102
|
padding-top: 0.5rem;
|
|
90
103
|
}
|
|
91
104
|
|
|
92
|
-
&
|
|
105
|
+
&__collapse-toggle {
|
|
93
106
|
display: flex;
|
|
94
107
|
align-items: center;
|
|
95
108
|
justify-content: center;
|
|
@@ -106,12 +119,27 @@
|
|
|
106
119
|
.cap-icon-v2 {
|
|
107
120
|
font-size: 0.875rem;
|
|
108
121
|
}
|
|
122
|
+
|
|
123
|
+
&:hover {
|
|
124
|
+
color: $CAP_COLOR_05;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// When collapsed: hide tab content so only header (tabs + arrow) sticks to footer
|
|
129
|
+
&--collapsed {
|
|
130
|
+
.ant-tabs-content-holder,
|
|
131
|
+
.ant-tabs-content,
|
|
132
|
+
.ant-tabs-tabpane {
|
|
133
|
+
display: none !important;
|
|
134
|
+
}
|
|
109
135
|
}
|
|
110
136
|
|
|
111
137
|
&__content {
|
|
112
|
-
|
|
138
|
+
height: 8rem; // Limit height for many errors
|
|
139
|
+
max-height: 12.5rem;
|
|
113
140
|
overflow-y: auto;
|
|
114
141
|
padding-bottom: 0; // Remove bottom padding completely
|
|
142
|
+
margin-left:2%;
|
|
115
143
|
|
|
116
144
|
// Custom scrollbar
|
|
117
145
|
&::-webkit-scrollbar {
|
|
@@ -135,8 +163,7 @@
|
|
|
135
163
|
&__item {
|
|
136
164
|
display: flex;
|
|
137
165
|
align-items: flex-start;
|
|
138
|
-
|
|
139
|
-
padding: 0.5rem 0.5rem; // Reduced from 0.375rem
|
|
166
|
+
padding: 0.1rem 0.5rem; // Reduced from 0.375rem
|
|
140
167
|
margin-bottom: 3px;
|
|
141
168
|
|
|
142
169
|
&:last-child {
|
|
@@ -155,6 +182,9 @@
|
|
|
155
182
|
}
|
|
156
183
|
|
|
157
184
|
&--warning {
|
|
185
|
+
.validation-tabs__icon--warning {
|
|
186
|
+
color: $CAP_G01;
|
|
187
|
+
}
|
|
158
188
|
.validation-tabs__item-message {
|
|
159
189
|
color: $CAP_G01;
|
|
160
190
|
}
|
|
@@ -176,6 +206,10 @@
|
|
|
176
206
|
&--error {
|
|
177
207
|
color: $CAP_RED;
|
|
178
208
|
}
|
|
209
|
+
|
|
210
|
+
&--warning {
|
|
211
|
+
color: $CAP_G01;
|
|
212
|
+
}
|
|
179
213
|
}
|
|
180
214
|
|
|
181
215
|
&__item-content {
|
|
@@ -232,6 +266,7 @@
|
|
|
232
266
|
.ant-tabs-tab {
|
|
233
267
|
margin-right: 1rem;
|
|
234
268
|
font-size: 0.8125rem;
|
|
269
|
+
font-weight: 400;
|
|
235
270
|
}
|
|
236
271
|
}
|
|
237
272
|
|