@capillarytech/creatives-library 8.0.242-alpha.0 → 8.0.242-alpha.2
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 +2 -1
- package/initialReducer.js +2 -0
- package/package.json +1 -1
- package/sagas/__tests__/assetPolling.test.js +74 -3
- package/sagas/assetPolling.js +8 -1
- package/services/api.js +10 -5
- package/services/tests/api.test.js +18 -0
- package/translations/en.json +0 -1
- package/utils/common.js +5 -0
- package/utils/commonUtils.js +14 -1
- package/utils/tests/commonUtil.test.js +224 -0
- package/utils/transformTemplateConfig.js +0 -10
- package/utils/transformerUtils.js +0 -42
- package/v2Components/CapDeviceContent/index.js +61 -56
- package/v2Components/CapImageUpload/constants.js +0 -2
- package/v2Components/CapImageUpload/index.js +14 -54
- package/v2Components/CapImageUpload/index.scss +1 -4
- package/v2Components/CapImageUpload/messages.js +0 -4
- package/v2Components/CapTagList/index.js +6 -1
- package/v2Components/CapTagListWithInput/index.js +5 -1
- package/v2Components/CapTagListWithInput/messages.js +1 -1
- package/v2Components/CapWhatsappCTA/tests/index.test.js +5 -0
- package/v2Components/ErrorInfoNote/index.js +412 -72
- package/v2Components/ErrorInfoNote/messages.js +22 -0
- package/v2Components/ErrorInfoNote/style.scss +279 -2
- package/v2Components/HtmlEditor/HTMLEditor.js +217 -90
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1132 -133
- package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +17 -12
- package/v2Components/HtmlEditor/_htmlEditor.scss +15 -23
- package/v2Components/HtmlEditor/_index.lazy.scss +1 -1
- package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +13 -101
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +148 -139
- package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +2 -1
- package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
- package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +1 -0
- package/v2Components/HtmlEditor/components/EditorToolbar/index.js +1 -1
- package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +1 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +4 -7
- package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +35 -45
- package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +1 -3
- package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
- package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +7 -6
- package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +3 -6
- package/v2Components/HtmlEditor/components/PreviewPane/index.js +10 -11
- package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +87 -62
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +49 -31
- package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +254 -0
- package/v2Components/HtmlEditor/components/ValidationTabs/index.js +362 -0
- package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +51 -0
- package/v2Components/HtmlEditor/constants.js +29 -20
- package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +373 -16
- package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
- package/v2Components/HtmlEditor/hooks/useInAppContent.js +88 -146
- package/v2Components/HtmlEditor/index.js +1 -1
- package/v2Components/HtmlEditor/messages.js +95 -85
- package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +99 -101
- package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +23 -25
- package/v2Components/HtmlEditor/utils/validationAdapter.js +34 -41
- package/v2Components/MobilePushPreviewV2/index.js +32 -7
- package/v2Components/TemplatePreview/_templatePreview.scss +44 -24
- package/v2Components/TemplatePreview/index.js +47 -32
- package/v2Components/TemplatePreview/messages.js +4 -0
- package/v2Components/TestAndPreviewSlidebox/index.js +31 -25
- package/v2Containers/App/constants.js +0 -5
- package/v2Containers/BeeEditor/index.js +82 -80
- package/v2Containers/BeePopupEditor/constants.js +10 -0
- package/v2Containers/BeePopupEditor/index.js +193 -0
- package/v2Containers/BeePopupEditor/tests/index.test.js +627 -0
- package/v2Containers/Cap/tests/__snapshots__/index.test.js.snap +0 -1
- package/v2Containers/CreativesContainer/SlideBoxContent.js +148 -120
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +9 -3
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -2
- package/v2Containers/CreativesContainer/constants.js +1 -2
- package/v2Containers/CreativesContainer/index.js +173 -193
- package/v2Containers/CreativesContainer/messages.js +4 -4
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +38 -50
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +36 -0
- package/v2Containers/Email/actions.js +7 -0
- package/v2Containers/Email/constants.js +5 -1
- package/v2Containers/Email/index.js +13 -0
- package/v2Containers/Email/messages.js +32 -0
- package/v2Containers/Email/reducer.js +12 -1
- package/v2Containers/Email/sagas.js +41 -6
- package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +2 -0
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1046 -0
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +193 -7
- package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +40 -74
- package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +2 -67
- package/v2Containers/EmailWrapper/constants.js +2 -0
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +436 -67
- package/v2Containers/EmailWrapper/index.js +99 -23
- package/v2Containers/EmailWrapper/messages.js +61 -1
- package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +26 -1
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +111 -77
- package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +376 -0
- package/v2Containers/InApp/__tests__/sagas.test.js +363 -0
- package/v2Containers/InApp/actions.js +7 -0
- package/v2Containers/InApp/constants.js +20 -4
- package/v2Containers/InApp/index.js +800 -357
- package/v2Containers/InApp/index.scss +4 -3
- package/v2Containers/InApp/messages.js +7 -3
- package/v2Containers/InApp/reducer.js +21 -3
- package/v2Containers/InApp/sagas.js +29 -9
- package/v2Containers/InApp/selectors.js +25 -5
- package/v2Containers/InApp/tests/index.test.js +154 -50
- package/v2Containers/InApp/tests/reducer.test.js +34 -0
- package/v2Containers/InApp/tests/sagas.test.js +61 -9
- package/v2Containers/InApp/tests/selectors.test.js +612 -0
- package/v2Containers/InAppWrapper/components/InAppWrapperView.js +162 -0
- package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +267 -0
- package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +9 -0
- package/v2Containers/InAppWrapper/constants.js +16 -0
- package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +473 -0
- package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +198 -0
- package/v2Containers/InAppWrapper/index.js +148 -0
- package/v2Containers/InAppWrapper/messages.js +49 -0
- package/v2Containers/InappAdvance/index.js +1099 -0
- package/v2Containers/InappAdvance/index.scss +10 -0
- package/v2Containers/InappAdvance/tests/index.test.js +448 -0
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -3
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -2
- package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -25
- package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -18
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -46
- package/v2Containers/SmsTrai/Create/tests/__snapshots__/index.test.js.snap +0 -4
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -8
- package/v2Containers/TagList/index.js +67 -1
- package/v2Containers/Templates/ChannelTypeIllustration.js +1 -13
- package/v2Containers/Templates/_templates.scss +56 -200
- package/v2Containers/Templates/actions.js +1 -2
- package/v2Containers/Templates/constants.js +0 -1
- package/v2Containers/Templates/index.js +124 -277
- package/v2Containers/Templates/messages.js +4 -24
- package/v2Containers/Templates/reducer.js +0 -2
- package/v2Containers/Templates/tests/index.test.js +0 -10
- package/v2Containers/TemplatesV2/index.js +2 -3
- package/v2Containers/TemplatesV2/messages.js +0 -4
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +35 -132
- package/v2Components/CapImageUrlUpload/constants.js +0 -19
- package/v2Components/CapImageUrlUpload/index.js +0 -455
- package/v2Components/CapImageUrlUpload/index.scss +0 -35
- package/v2Components/CapImageUrlUpload/messages.js +0 -47
- package/v2Containers/WebPush/Create/components/ButtonForm.js +0 -175
- package/v2Containers/WebPush/Create/components/ButtonItem.js +0 -101
- package/v2Containers/WebPush/Create/components/ButtonList.js +0 -144
- package/v2Containers/WebPush/Create/components/_buttons.scss +0 -246
- package/v2Containers/WebPush/Create/components/tests/ButtonForm.test.js +0 -554
- package/v2Containers/WebPush/Create/components/tests/ButtonItem.test.js +0 -607
- package/v2Containers/WebPush/Create/components/tests/ButtonList.test.js +0 -633
- package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonForm.test.js.snap +0 -666
- package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonItem.test.js.snap +0 -74
- package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonList.test.js.snap +0 -80
- package/v2Containers/WebPush/Create/index.js +0 -1755
- package/v2Containers/WebPush/Create/index.scss +0 -123
- package/v2Containers/WebPush/Create/messages.js +0 -199
- package/v2Containers/WebPush/Create/preview/DevicePreviewContent.js +0 -241
- package/v2Containers/WebPush/Create/preview/NotificationContainer.js +0 -290
- package/v2Containers/WebPush/Create/preview/PreviewContent.js +0 -81
- package/v2Containers/WebPush/Create/preview/PreviewControls.js +0 -240
- package/v2Containers/WebPush/Create/preview/PreviewDisclaimer.js +0 -23
- package/v2Containers/WebPush/Create/preview/WebPushPreview.js +0 -144
- package/v2Containers/WebPush/Create/preview/assets/Light.svg +0 -53
- package/v2Containers/WebPush/Create/preview/assets/Top.svg +0 -5
- package/v2Containers/WebPush/Create/preview/assets/chrome-icon.png +0 -0
- package/v2Containers/WebPush/Create/preview/assets/edge-icon.png +0 -0
- package/v2Containers/WebPush/Create/preview/assets/firefox-icon.svg +0 -106
- package/v2Containers/WebPush/Create/preview/assets/iOS.svg +0 -26
- package/v2Containers/WebPush/Create/preview/assets/opera-icon.svg +0 -18
- package/v2Containers/WebPush/Create/preview/assets/safari-icon.svg +0 -29
- package/v2Containers/WebPush/Create/preview/components/AndroidMobileChromeHeader.js +0 -44
- package/v2Containers/WebPush/Create/preview/components/AndroidMobileExpanded.js +0 -110
- package/v2Containers/WebPush/Create/preview/components/IOSHeader.js +0 -45
- package/v2Containers/WebPush/Create/preview/components/NotificationExpandedContent.js +0 -72
- package/v2Containers/WebPush/Create/preview/components/NotificationHeader.js +0 -55
- package/v2Containers/WebPush/Create/preview/components/WindowsChromeExpanded.js +0 -70
- package/v2Containers/WebPush/Create/preview/components/tests/AndroidMobileExpanded.test.js +0 -512
- package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/AndroidMobileExpanded.test.js.snap +0 -77
- package/v2Containers/WebPush/Create/preview/config/notificationMappings.js +0 -527
- package/v2Containers/WebPush/Create/preview/constants.js +0 -162
- package/v2Containers/WebPush/Create/preview/notification-container.scss +0 -104
- package/v2Containers/WebPush/Create/preview/preview.scss +0 -409
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-chrome.scss +0 -300
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-edge.scss +0 -12
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-firefox.scss +0 -12
- package/v2Containers/WebPush/Create/preview/styles/_android-mobile-opera.scss +0 -12
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-chrome.scss +0 -303
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-edge.scss +0 -11
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-firefox.scss +0 -11
- package/v2Containers/WebPush/Create/preview/styles/_android-tablet-opera.scss +0 -11
- package/v2Containers/WebPush/Create/preview/styles/_base.scss +0 -188
- package/v2Containers/WebPush/Create/preview/styles/_ios.scss +0 -106
- package/v2Containers/WebPush/Create/preview/styles/_ipados.scss +0 -107
- package/v2Containers/WebPush/Create/preview/styles/_macos-chrome.scss +0 -75
- package/v2Containers/WebPush/Create/preview/styles/_windows-chrome.scss +0 -174
- package/v2Containers/WebPush/Create/preview/tests/DevicePreviewContent.test.js +0 -909
- package/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +0 -1077
- package/v2Containers/WebPush/Create/preview/tests/PreviewControls.test.js +0 -723
- package/v2Containers/WebPush/Create/preview/tests/WebPushPreview.test.js +0 -943
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/DevicePreviewContent.test.js.snap +0 -128
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/NotificationContainer.test.js.snap +0 -121
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/PreviewControls.test.js.snap +0 -144
- package/v2Containers/WebPush/Create/preview/tests/__snapshots__/WebPushPreview.test.js.snap +0 -127
- package/v2Containers/WebPush/Create/utils/urlValidation.js +0 -116
- package/v2Containers/WebPush/Create/utils/urlValidation.test.js +0 -449
- package/v2Containers/WebPush/actions.js +0 -60
- package/v2Containers/WebPush/constants.js +0 -108
- package/v2Containers/WebPush/index.js +0 -2
- package/v2Containers/WebPush/reducer.js +0 -104
- package/v2Containers/WebPush/sagas.js +0 -119
- package/v2Containers/WebPush/selectors.js +0 -65
- package/v2Containers/WebPush/tests/reducer.test.js +0 -863
- package/v2Containers/WebPush/tests/sagas.test.js +0 -566
- 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';
|