@capillarytech/creatives-library 8.0.113 → 8.0.114-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/containers/App/test/saga.test.js +1 -1
- package/containers/Assets/Gallery/tests/__snapshots__/reducer.test.js.snap +1 -1
- package/containers/Assets/Gallery/tests/actions.test.js +2 -3
- package/containers/Assets/Gallery/tests/reducer.test.js +7 -7
- package/containers/Assets/Gallery/tests/saga.test.js +9 -9
- package/containers/Dashboard/test/saga.test.js +1 -1
- package/containers/Ebill/test/saga.test.js +1 -1
- package/containers/Email/test/saga.test.js +33 -33
- package/containers/LanguageProvider/tests/actions.test.js +1 -1
- package/containers/LanguageProvider/tests/reducer.test.js +2 -2
- package/containers/LanguageProvider/tests/selectors.test.js +1 -1
- package/containers/Line/Create/tests/saga.test.js +2 -9
- package/containers/Line/Edit/test/saga.test.js +10 -14
- package/containers/MobilePush/Create/test/saga.test.js +2 -2
- package/containers/MobilePush/Edit/tests/saga.test.js +14 -14
- package/containers/Sms/Create/test/saga.test.js +4 -5
- package/containers/Sms/Edit/test/saga.test.js +1 -1
- package/containers/Templates/test/saga.test.js +14 -17
- package/containers/WeChat/MapTemplates/test/saga.test.js +9 -13
- package/containers/WeChat/RichmediaTemplates/Create/test/saga.test.js +1 -1
- package/containers/WeChat/RichmediaTemplates/Edit/test/saga.test.js +1 -1
- package/package.json +1 -1
- package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +8 -9
- package/utils/commonUtils.js +389 -3
- package/utils/tagValidations.js +20 -5
- package/utils/tests/authWrapper.test.js +2 -2
- package/utils/tests/cdnTransformation.test.js +16 -15
- package/utils/tests/commonUtil.test.js +324 -178
- package/v2Components/CapVideoUpload/tests/index.test.js +1 -1
- package/v2Components/CapWhatsappCTA/tests/index.test.js +1 -2
- package/v2Components/ErrorInfoNote/ErrorTypeRenderer.js +130 -0
- package/v2Components/ErrorInfoNote/ErrorTypeRenderer.test.js +146 -0
- package/v2Components/ErrorInfoNote/index.js +114 -46
- package/v2Components/ErrorInfoNote/messages.js +25 -0
- package/v2Components/ErrorInfoNote/style.scss +14 -1
- package/v2Components/ErrorInfoNote/utils.js +28 -0
- package/v2Components/ErrorInfoNote/utils.test.js +93 -0
- package/v2Components/FormBuilder/index.js +200 -127
- package/v2Components/FormBuilder/messages.js +1 -1
- package/v2Components/MarketingObjective/test/index.test.js +1 -1
- package/v2Components/NavigationBar/tests/saga.test.js +2 -3
- package/v2Containers/Assets/Gallery/tests/__snapshots__/reducer.test.js.snap +1 -1
- package/v2Containers/Assets/Gallery/tests/actions.test.js +2 -3
- package/v2Containers/Assets/Gallery/tests/reducer.test.js +7 -7
- package/v2Containers/Assets/Gallery/tests/saga.test.js +2 -2
- package/v2Containers/BeeEditor/test/saga.test.js +1 -1
- package/v2Containers/CallTask/test/saga.test.js +1 -1
- package/v2Containers/Cap/reducer.js +4 -4
- package/v2Containers/Cap/tests/actions.test.js +1 -1
- package/v2Containers/Cap/tests/reducer.test.js +11 -11
- package/v2Containers/Cap/tests/saga.test.js +1 -1
- package/v2Containers/Cap/tests/selectors.test.js +3 -3
- package/v2Containers/CapFacebookPreview/tests/saga.test.js +1 -1
- package/v2Containers/CreativesContainer/SlideBoxContent.js +23 -3
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +3 -1
- package/v2Containers/CreativesContainer/constants.js +2 -1
- package/v2Containers/CreativesContainer/index.js +37 -10
- package/v2Containers/CreativesContainer/messages.js +4 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +21 -3
- package/v2Containers/Ebill/index.js +3 -3
- package/v2Containers/Ebill/test/saga.test.js +1 -1
- package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +4 -4
- package/v2Containers/Email/tests/actions.test.js +1 -1
- package/v2Containers/Email/tests/reducer.test.js +2 -2
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +1 -1
- package/v2Containers/FTP/test/saga.test.js +1 -1
- package/v2Containers/Facebook/test/saga.test.js +7 -7
- package/v2Containers/InApp/index.js +127 -49
- package/v2Containers/InApp/tests/action.test.js +7 -7
- package/v2Containers/InApp/tests/index.test.js +2 -4
- package/v2Containers/InApp/tests/reducer.test.js +175 -89
- package/v2Containers/InApp/tests/sagas.test.js +1 -1
- package/v2Containers/InApp/utils.js +37 -0
- package/v2Containers/LanguageProvider/tests/actions.test.js +1 -1
- package/v2Containers/LanguageProvider/tests/reducer.test.js +3 -3
- package/v2Containers/LanguageProvider/tests/saga.test.js +2 -2
- package/v2Containers/LanguageProvider/tests/selectors.test.js +1 -1
- package/v2Containers/Line/Container/ImageCarousel/tests/utils.test.js +3 -3
- package/v2Containers/Line/Container/Sticker/tests/utils.test.js +6 -6
- package/v2Containers/MobilePush/Create/index.js +24 -20
- package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
- package/v2Containers/MobilePush/Edit/index.js +4 -1
- package/v2Containers/MobilePush/Edit/test/saga.test.js +14 -14
- package/v2Containers/MobilepushWrapper/index.js +2 -0
- package/v2Containers/Rcs/tests/saga.test.js +1 -1
- package/v2Containers/Sms/Create/index.js +14 -2
- package/v2Containers/Sms/Create/test/saga.test.js +1 -1
- package/v2Containers/Sms/Edit/index.js +2 -0
- package/v2Containers/Sms/Edit/test/saga.test.js +5 -5
- package/v2Containers/SmsTrai/Create/tests/saga.test.js +1 -1
- package/v2Containers/SmsTrai/Create/tests/selectors.test.js +1 -1
- package/v2Containers/SmsWrapper/index.js +2 -0
- package/v2Containers/TagList/tests/TagList.test.js +1 -3
- package/v2Containers/TagList/tests/utils.test.js +3 -3
- package/v2Containers/Templates/tests/actions.test.js +1 -1
- package/v2Containers/Templates/tests/reducer.test.js +8 -8
- package/v2Containers/Templates/tests/sagas.test.js +2 -4
- package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -13
- package/v2Containers/WeChat/RichmediaTemplates/Create/test/saga.test.js +1 -1
- package/v2Containers/WeChat/RichmediaTemplates/Edit/test/saga.test.js +1 -1
- package/v2Containers/Whatsapp/tests/__snapshots__/utils.test.js.snap +9 -9
- package/v2Containers/Whatsapp/tests/actions.test.js +3 -3
- package/v2Containers/Whatsapp/tests/reducer.test.js +32 -36
- package/v2Containers/Whatsapp/tests/utils.test.js +19 -19
- package/v2Containers/Zalo/tests/actions.test.js +3 -3
- package/v2Containers/Zalo/tests/reducer.test.js +72 -42
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// app/v2Components/ErrorInfoNote/ErrorTypeRenderer.js
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import CapDivider from "@capillarytech/cap-ui-library/CapDivider";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Renders error sections for either generic (non-platform) or platform-specific (Android/iOS) errors.
|
|
8
|
+
* - If `genericErrors` is provided, renders standard and liquid errors (if present) without platform labels.
|
|
9
|
+
* - If `androidErrors`/`iosErrors` are provided, renders merged errors for each platform with platform labels and dividers.
|
|
10
|
+
*/
|
|
11
|
+
const ErrorTypeRenderer = ({
|
|
12
|
+
genericErrors,
|
|
13
|
+
androidErrors,
|
|
14
|
+
iosErrors,
|
|
15
|
+
ErrorSectionComponent,
|
|
16
|
+
}) => {
|
|
17
|
+
// Render generic (non-platform) errors
|
|
18
|
+
if (genericErrors) {
|
|
19
|
+
const hasStandard = genericErrors.standard?.errorsToShow?.length > 0;
|
|
20
|
+
const hasLiquid = genericErrors.liquid?.errorsToShow?.length > 0;
|
|
21
|
+
if (!hasStandard && !hasLiquid) return null;
|
|
22
|
+
return (
|
|
23
|
+
<div className="error-container">
|
|
24
|
+
{hasStandard && (
|
|
25
|
+
<ErrorSectionComponent
|
|
26
|
+
title={genericErrors.standard.title}
|
|
27
|
+
errors={genericErrors.standard.errorsToShow}
|
|
28
|
+
liquidError={false}
|
|
29
|
+
platformLabel={null}
|
|
30
|
+
/>
|
|
31
|
+
)}
|
|
32
|
+
{hasStandard && hasLiquid && <CapDivider className="liquid-divider" />}
|
|
33
|
+
{hasLiquid && (
|
|
34
|
+
<ErrorSectionComponent
|
|
35
|
+
title={genericErrors.liquid.title}
|
|
36
|
+
errors={genericErrors.liquid.errorsToShow}
|
|
37
|
+
liquidError
|
|
38
|
+
platformLabel={null}
|
|
39
|
+
/>
|
|
40
|
+
)}
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Render platform-specific errors (Android/iOS)
|
|
46
|
+
const hasAndroid = (androidErrors?.standard?.errorsToShow?.length || 0) > 0
|
|
47
|
+
|| (androidErrors?.liquid?.errorsToShow?.length || 0) > 0;
|
|
48
|
+
const hasIos = (iosErrors?.standard?.errorsToShow?.length || 0) > 0
|
|
49
|
+
|| (iosErrors?.liquid?.errorsToShow?.length || 0) > 0;
|
|
50
|
+
|
|
51
|
+
if (!hasAndroid && !hasIos) return null;
|
|
52
|
+
|
|
53
|
+
const showPlatformLabels = !!androidErrors?.standard?.platformLabel || !!iosErrors?.standard?.platformLabel;
|
|
54
|
+
const showTitle = hasAndroid && hasIos && showPlatformLabels;
|
|
55
|
+
return (
|
|
56
|
+
<div className="error-container">
|
|
57
|
+
{hasAndroid && (
|
|
58
|
+
<ErrorSectionComponent
|
|
59
|
+
title={showTitle && androidErrors.liquid?.title}
|
|
60
|
+
errors={[
|
|
61
|
+
...(androidErrors.standard?.errorsToShow || []),
|
|
62
|
+
...(androidErrors.liquid?.errorsToShow || []),
|
|
63
|
+
]}
|
|
64
|
+
liquidError={!!androidErrors.liquid?.errorsToShow?.length}
|
|
65
|
+
platformLabel={showPlatformLabels ? 'ANDROID' : null}
|
|
66
|
+
/>
|
|
67
|
+
)}
|
|
68
|
+
{hasAndroid && hasIos && showPlatformLabels && (
|
|
69
|
+
<CapDivider className="platform-divider" />
|
|
70
|
+
)}
|
|
71
|
+
{hasIos && (
|
|
72
|
+
<ErrorSectionComponent
|
|
73
|
+
title={!showTitle && iosErrors.liquid?.title}
|
|
74
|
+
errors={[
|
|
75
|
+
...(iosErrors.standard?.errorsToShow || []),
|
|
76
|
+
...(iosErrors.liquid?.errorsToShow || []),
|
|
77
|
+
]}
|
|
78
|
+
liquidError={!!iosErrors.liquid?.errorsToShow?.length}
|
|
79
|
+
platformLabel={showPlatformLabels ? 'IOS' : null}
|
|
80
|
+
/>
|
|
81
|
+
)}
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
ErrorTypeRenderer.propTypes = {
|
|
87
|
+
genericErrors: PropTypes.shape({
|
|
88
|
+
standard: PropTypes.shape({
|
|
89
|
+
errorsToShow: PropTypes.array.isRequired,
|
|
90
|
+
title: PropTypes.node.isRequired,
|
|
91
|
+
}),
|
|
92
|
+
liquid: PropTypes.shape({
|
|
93
|
+
errorsToShow: PropTypes.array.isRequired,
|
|
94
|
+
title: PropTypes.node.isRequired,
|
|
95
|
+
}),
|
|
96
|
+
}),
|
|
97
|
+
androidErrors: PropTypes.shape({
|
|
98
|
+
standard: PropTypes.shape({
|
|
99
|
+
errorsToShow: PropTypes.array.isRequired,
|
|
100
|
+
title: PropTypes.node.isRequired,
|
|
101
|
+
platformLabel: PropTypes.string,
|
|
102
|
+
}),
|
|
103
|
+
liquid: PropTypes.shape({
|
|
104
|
+
errorsToShow: PropTypes.array.isRequired,
|
|
105
|
+
title: PropTypes.node.isRequired,
|
|
106
|
+
platformLabel: PropTypes.string,
|
|
107
|
+
}),
|
|
108
|
+
}),
|
|
109
|
+
iosErrors: PropTypes.shape({
|
|
110
|
+
standard: PropTypes.shape({
|
|
111
|
+
errorsToShow: PropTypes.array.isRequired,
|
|
112
|
+
title: PropTypes.node.isRequired,
|
|
113
|
+
platformLabel: PropTypes.string,
|
|
114
|
+
}),
|
|
115
|
+
liquid: PropTypes.shape({
|
|
116
|
+
errorsToShow: PropTypes.array.isRequired,
|
|
117
|
+
title: PropTypes.node.isRequired,
|
|
118
|
+
platformLabel: PropTypes.string,
|
|
119
|
+
}),
|
|
120
|
+
}),
|
|
121
|
+
ErrorSectionComponent: PropTypes.elementType.isRequired,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
ErrorTypeRenderer.defaultProps = {
|
|
125
|
+
genericErrors: null,
|
|
126
|
+
androidErrors: null,
|
|
127
|
+
iosErrors: null,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export default ErrorTypeRenderer;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import ErrorTypeRenderer from './ErrorTypeRenderer';
|
|
4
|
+
|
|
5
|
+
describe('ErrorTypeRenderer', () => { const ErrorSectionComponent = jest.fn(({
|
|
6
|
+
title,
|
|
7
|
+
errors,
|
|
8
|
+
liquidError,
|
|
9
|
+
platformLabel,
|
|
10
|
+
}) => (
|
|
11
|
+
<div data-testid="error-section">
|
|
12
|
+
<span>{title}</span>
|
|
13
|
+
<span>{errors && errors.join(',')}</span>
|
|
14
|
+
<span>{liquidError ? 'liquid' : 'standard'}</span>
|
|
15
|
+
<span>{platformLabel}</span>
|
|
16
|
+
</div>
|
|
17
|
+
));
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
ErrorSectionComponent.mockClear();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('renders nothing if all errors are empty', () => {
|
|
24
|
+
const { container } = render(
|
|
25
|
+
<ErrorTypeRenderer
|
|
26
|
+
genericErrors={{ standard: { errorsToShow: [], title: 'Standard' }, liquid: { errorsToShow: [], title: 'Liquid' } }}
|
|
27
|
+
ErrorSectionComponent={ErrorSectionComponent}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
expect(container.firstChild).toBeNull();
|
|
31
|
+
expect(ErrorSectionComponent).not.toHaveBeenCalled();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('renders generic standard errors only', () => {
|
|
35
|
+
render(
|
|
36
|
+
<ErrorTypeRenderer
|
|
37
|
+
genericErrors={{ standard: { errorsToShow: ['err1', 'err2'], title: 'Standard' }, liquid: { errorsToShow: [], title: 'Liquid' } }}
|
|
38
|
+
ErrorSectionComponent={ErrorSectionComponent}
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
expect(ErrorSectionComponent).toHaveBeenCalledWith(
|
|
42
|
+
expect.objectContaining({
|
|
43
|
+
title: 'Standard',
|
|
44
|
+
errors: ['err1', 'err2'],
|
|
45
|
+
liquidError: false,
|
|
46
|
+
platformLabel: null,
|
|
47
|
+
}),
|
|
48
|
+
expect.anything()
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('renders generic liquid errors only', () => {
|
|
53
|
+
render(
|
|
54
|
+
<ErrorTypeRenderer
|
|
55
|
+
genericErrors={{ standard: { errorsToShow: [], title: 'Standard' }, liquid: { errorsToShow: ['l1'], title: 'Liquid' } }}
|
|
56
|
+
ErrorSectionComponent={ErrorSectionComponent}
|
|
57
|
+
/>
|
|
58
|
+
);
|
|
59
|
+
expect(ErrorSectionComponent).toHaveBeenCalledWith(
|
|
60
|
+
expect.objectContaining({
|
|
61
|
+
title: 'Liquid',
|
|
62
|
+
errors: ['l1'],
|
|
63
|
+
liquidError: true,
|
|
64
|
+
platformLabel: null,
|
|
65
|
+
}),
|
|
66
|
+
expect.anything()
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('renders both generic standard and liquid errors', () => {
|
|
71
|
+
render(
|
|
72
|
+
<ErrorTypeRenderer
|
|
73
|
+
genericErrors={{ standard: { errorsToShow: ['err1'], title: 'Standard' }, liquid: { errorsToShow: ['l1'], title: 'Liquid' } }}
|
|
74
|
+
ErrorSectionComponent={ErrorSectionComponent}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
expect(ErrorSectionComponent).toHaveBeenCalledWith(
|
|
78
|
+
expect.objectContaining({
|
|
79
|
+
title: 'Standard',
|
|
80
|
+
errors: ['err1'],
|
|
81
|
+
liquidError: false,
|
|
82
|
+
platformLabel: null,
|
|
83
|
+
}),
|
|
84
|
+
expect.anything()
|
|
85
|
+
);
|
|
86
|
+
expect(ErrorSectionComponent).toHaveBeenCalledWith(
|
|
87
|
+
expect.objectContaining({
|
|
88
|
+
title: 'Liquid',
|
|
89
|
+
errors: ['l1'],
|
|
90
|
+
liquidError: true,
|
|
91
|
+
platformLabel: null,
|
|
92
|
+
}),
|
|
93
|
+
expect.anything()
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('renders merged android errors with platform label', () => {
|
|
98
|
+
render(
|
|
99
|
+
<ErrorTypeRenderer
|
|
100
|
+
androidErrors={{
|
|
101
|
+
standard: { errorsToShow: ['a1'], title: 'Android Standard', platformLabel: 'ANDROID' },
|
|
102
|
+
liquid: { errorsToShow: ['al1'], title: 'Android Liquid', platformLabel: 'ANDROID' },
|
|
103
|
+
}}
|
|
104
|
+
iosErrors={{
|
|
105
|
+
standard: { errorsToShow: [], title: 'iOS Standard', platformLabel: 'IOS' },
|
|
106
|
+
liquid: { errorsToShow: [], title: 'iOS Liquid', platformLabel: 'IOS' },
|
|
107
|
+
}}
|
|
108
|
+
ErrorSectionComponent={ErrorSectionComponent}
|
|
109
|
+
/>
|
|
110
|
+
);
|
|
111
|
+
expect(ErrorSectionComponent).toHaveBeenCalledWith(
|
|
112
|
+
expect.objectContaining({
|
|
113
|
+
title: false,
|
|
114
|
+
errors: ['a1', 'al1'],
|
|
115
|
+
liquidError: true,
|
|
116
|
+
platformLabel: 'ANDROID',
|
|
117
|
+
}),
|
|
118
|
+
expect.anything()
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('renders merged ios errors with platform label', () => {
|
|
123
|
+
render(
|
|
124
|
+
<ErrorTypeRenderer
|
|
125
|
+
androidErrors={{
|
|
126
|
+
standard: { errorsToShow: [], title: 'Android Standard', platformLabel: 'ANDROID' },
|
|
127
|
+
liquid: { errorsToShow: [], title: 'Android Liquid', platformLabel: 'ANDROID' },
|
|
128
|
+
}}
|
|
129
|
+
iosErrors={{
|
|
130
|
+
standard: { errorsToShow: ['i1'], title: 'iOS Standard', platformLabel: 'IOS' },
|
|
131
|
+
liquid: { errorsToShow: ['il1'], title: 'iOS Liquid', platformLabel: 'IOS' },
|
|
132
|
+
}}
|
|
133
|
+
ErrorSectionComponent={ErrorSectionComponent}
|
|
134
|
+
/>
|
|
135
|
+
);
|
|
136
|
+
expect(ErrorSectionComponent).toHaveBeenCalledWith(
|
|
137
|
+
expect.objectContaining({
|
|
138
|
+
title: 'iOS Liquid',
|
|
139
|
+
errors: ['i1', 'il1'],
|
|
140
|
+
liquidError: true,
|
|
141
|
+
platformLabel: 'IOS',
|
|
142
|
+
}),
|
|
143
|
+
expect.anything()
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
@@ -9,19 +9,25 @@ import {
|
|
|
9
9
|
FormattedMessage,
|
|
10
10
|
FormattedNumber,
|
|
11
11
|
injectIntl,
|
|
12
|
-
intlShape,
|
|
13
12
|
} from "react-intl";
|
|
14
13
|
import "./style.scss";
|
|
15
14
|
import messages from "./messages";
|
|
15
|
+
import { processErrors } from "./utils";
|
|
16
|
+
import ErrorTypeRenderer from "./ErrorTypeRenderer";
|
|
17
|
+
import { ANDROID, GENERIC, IOS } from "../../v2Containers/CreativesContainer/constants";
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
const ErrorSection = ({
|
|
20
|
+
title,
|
|
21
|
+
errors,
|
|
22
|
+
liquidError,
|
|
23
|
+
platformLabel,
|
|
24
|
+
}) => (
|
|
25
|
+
<>
|
|
26
|
+
{title && (
|
|
23
27
|
<CapRow
|
|
24
|
-
className={`error-header ${
|
|
28
|
+
className={`error-header ${
|
|
29
|
+
!liquidError ? "standard-error-header" : ""
|
|
30
|
+
}`}
|
|
25
31
|
>
|
|
26
32
|
<>
|
|
27
33
|
<CapIcon size="s" type="warning" className="warning-icon" />
|
|
@@ -32,8 +38,7 @@ export const ErrorInfoNote = (props) => {
|
|
|
32
38
|
<CapButton
|
|
33
39
|
type="flat"
|
|
34
40
|
className="add-btn"
|
|
35
|
-
onClick={() =>
|
|
36
|
-
window.open(`https://docs.capillarytech.com/docs/liquid-language-in-messages`, "_blank")
|
|
41
|
+
onClick={() => window.open("https://docs.capillarytech.com/docs/liquid-language-in-messages", "_blank")
|
|
37
42
|
}
|
|
38
43
|
>
|
|
39
44
|
<FormattedMessage {...messages.liquidDoc} />
|
|
@@ -42,48 +47,111 @@ export const ErrorInfoNote = (props) => {
|
|
|
42
47
|
)}
|
|
43
48
|
</>
|
|
44
49
|
</CapRow>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
)}
|
|
51
|
+
{platformLabel && (
|
|
52
|
+
<CapRow className="error-header-sub-title">
|
|
53
|
+
<CapLabel type="label2">{platformLabel}</CapLabel>
|
|
54
|
+
</CapRow>
|
|
55
|
+
)}
|
|
56
|
+
<CapList
|
|
57
|
+
className="error-list"
|
|
58
|
+
size="small"
|
|
59
|
+
dataSource={errors}
|
|
60
|
+
renderItem={(error, index) => (
|
|
61
|
+
<CapList.Item>
|
|
62
|
+
<CapLabel type="label2" className="cap-list-v2-error-item">
|
|
63
|
+
<CapLabel type="label2">
|
|
64
|
+
<FormattedNumber value={index + 1} />.
|
|
54
65
|
</CapLabel>
|
|
55
|
-
|
|
56
|
-
|
|
66
|
+
<CapLabel type="label2">{error}</CapLabel>
|
|
67
|
+
</CapLabel>
|
|
68
|
+
</CapList.Item>
|
|
69
|
+
)}
|
|
70
|
+
/>
|
|
71
|
+
</>
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
ErrorSection.propTypes = {
|
|
75
|
+
title: PropTypes.node.isRequired,
|
|
76
|
+
errors: PropTypes.array,
|
|
77
|
+
liquidError: PropTypes.bool,
|
|
78
|
+
platformLabel: PropTypes.string,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
ErrorSection.defaultProps = {
|
|
82
|
+
errors: [],
|
|
83
|
+
liquidError: false,
|
|
84
|
+
platformLabel: null,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const ErrorInfoNote = (props) => {
|
|
88
|
+
const { errorMessages } = props;
|
|
89
|
+
|
|
90
|
+
const {
|
|
91
|
+
LIQUID_ERROR_MSG: rawLiquidErrors = [],
|
|
92
|
+
STANDARD_ERROR_MSG: rawStandardErrors = [],
|
|
93
|
+
} = errorMessages || {};
|
|
94
|
+
|
|
95
|
+
// Detect if platform-specific (ANDROID/IOS) or GENERIC
|
|
96
|
+
const isObject = typeof rawStandardErrors === 'object' && rawStandardErrors !== null;
|
|
97
|
+
const isNotArray = !Array.isArray(rawStandardErrors);
|
|
98
|
+
const hasPlatformKeys = isObject && isNotArray && [ANDROID, IOS, GENERIC].some((key) => key in rawStandardErrors);
|
|
99
|
+
|
|
100
|
+
if (hasPlatformKeys) {
|
|
101
|
+
// Platform-specific
|
|
102
|
+
const androidErrors = {
|
|
103
|
+
standard: processErrors(rawStandardErrors, 'standard', 'ANDROID', messages),
|
|
104
|
+
liquid: processErrors(rawLiquidErrors, 'liquid', 'ANDROID', messages),
|
|
105
|
+
};
|
|
106
|
+
const iosErrors = {
|
|
107
|
+
standard: processErrors(rawStandardErrors, 'standard', 'IOS', messages),
|
|
108
|
+
liquid: processErrors(rawLiquidErrors, 'liquid', 'IOS', messages),
|
|
109
|
+
};
|
|
110
|
+
return (
|
|
111
|
+
<ErrorTypeRenderer
|
|
112
|
+
androidErrors={androidErrors}
|
|
113
|
+
iosErrors={iosErrors}
|
|
114
|
+
ErrorSectionComponent={ErrorSection}
|
|
57
115
|
/>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
// GENERIC (not platform-specific)
|
|
119
|
+
const genericStandard = processErrors(rawStandardErrors, 'standard', null, messages);
|
|
120
|
+
const genericLiquid = processErrors(rawLiquidErrors, 'liquid', null, messages);
|
|
61
121
|
return (
|
|
62
|
-
<
|
|
63
|
-
{
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
title={
|
|
67
|
-
<FormattedMessage {...messages.standardErrorHeader} />
|
|
68
|
-
}
|
|
69
|
-
errors={STANDARD_ERROR_MSG}
|
|
70
|
-
/>
|
|
71
|
-
</>
|
|
72
|
-
)}
|
|
73
|
-
{liquidErrors > 0 && (
|
|
74
|
-
<ErrorSection
|
|
75
|
-
title={<FormattedMessage {...messages.dynamicErrorHeader} />}
|
|
76
|
-
errors={LIQUID_ERROR_MSG}
|
|
77
|
-
liquidError={true}
|
|
78
|
-
/>
|
|
79
|
-
)}
|
|
80
|
-
</div>
|
|
122
|
+
<ErrorTypeRenderer
|
|
123
|
+
genericErrors={{ standard: genericStandard, liquid: genericLiquid }}
|
|
124
|
+
ErrorSectionComponent={ErrorSection}
|
|
125
|
+
/>
|
|
81
126
|
);
|
|
82
127
|
};
|
|
128
|
+
|
|
129
|
+
ErrorInfoNote.defaultProps = {
|
|
130
|
+
errorMessages: {
|
|
131
|
+
LIQUID_ERROR_MSG: [],
|
|
132
|
+
STANDARD_ERROR_MSG: [],
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
|
|
83
136
|
ErrorInfoNote.propTypes = {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
137
|
+
errorMessages: PropTypes.shape({
|
|
138
|
+
LIQUID_ERROR_MSG: PropTypes.oneOfType([
|
|
139
|
+
PropTypes.array,
|
|
140
|
+
PropTypes.shape({
|
|
141
|
+
ANDROID: PropTypes.array,
|
|
142
|
+
IOS: PropTypes.array,
|
|
143
|
+
GENERIC: PropTypes.array,
|
|
144
|
+
}),
|
|
145
|
+
]),
|
|
146
|
+
STANDARD_ERROR_MSG: PropTypes.oneOfType([
|
|
147
|
+
PropTypes.array,
|
|
148
|
+
PropTypes.shape({
|
|
149
|
+
ANDROID: PropTypes.array,
|
|
150
|
+
IOS: PropTypes.array,
|
|
151
|
+
GENERIC: PropTypes.array,
|
|
152
|
+
}),
|
|
153
|
+
]),
|
|
154
|
+
}),
|
|
87
155
|
};
|
|
88
156
|
|
|
89
157
|
export default injectIntl(ErrorInfoNote);
|
|
@@ -26,4 +26,29 @@ export default defineMessages({
|
|
|
26
26
|
defaultMessage:
|
|
27
27
|
"Aira can make mistakes. Please verify the suggestions before applying them"
|
|
28
28
|
},
|
|
29
|
+
liquidDocLink: {
|
|
30
|
+
id: `${scope}.liquidDocLink`,
|
|
31
|
+
defaultMessage:
|
|
32
|
+
"https://docs.capillarytech.com/docs/liquid-language-in-messages"
|
|
33
|
+
},
|
|
34
|
+
androidDynamicErrorHeader: {
|
|
35
|
+
id: `${scope}.androidDynamicErrorHeader`,
|
|
36
|
+
defaultMessage:
|
|
37
|
+
"Errors found in Android Dynamic Tags"
|
|
38
|
+
},
|
|
39
|
+
iosDynamicErrorHeader: {
|
|
40
|
+
id: `${scope}.iosDynamicErrorHeader`,
|
|
41
|
+
defaultMessage:
|
|
42
|
+
"Errors found in iOS Dynamic Tags"
|
|
43
|
+
},
|
|
44
|
+
androidStandardErrorHeader: {
|
|
45
|
+
id: `${scope}.androidStandardErrorHeader`,
|
|
46
|
+
defaultMessage:
|
|
47
|
+
"Errors found in Android Standard Tags"
|
|
48
|
+
},
|
|
49
|
+
iosStandardErrorHeader: {
|
|
50
|
+
id: `${scope}.iosStandardErrorHeader`,
|
|
51
|
+
defaultMessage:
|
|
52
|
+
"Errors found in iOS Standard Tags"
|
|
53
|
+
},
|
|
29
54
|
});
|
|
@@ -58,15 +58,28 @@
|
|
|
58
58
|
gap: $CAP_SPACE_04;
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
+
|
|
61
62
|
.liquid-divider {
|
|
62
|
-
&.ant-divider.cap-divider-v2{
|
|
63
|
+
&.ant-divider.cap-divider-v2 {
|
|
63
64
|
background: $FONT_COLOR_04;
|
|
64
65
|
}
|
|
65
66
|
margin: $CAP_SPACE_08 0;
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
.platform-divider {
|
|
70
|
+
&.ant-divider.cap-divider-v2 {
|
|
71
|
+
background: $FONT_COLOR_04;
|
|
72
|
+
margin: $CAP_SPACE_12 0;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
68
76
|
.aria-error-footer {
|
|
69
77
|
display: flex;
|
|
70
78
|
align-items: center;
|
|
71
79
|
gap: $CAP_SPACE_04;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.error-header-sub-title {
|
|
83
|
+
margin-left: $CAP_SPACE_04;
|
|
84
|
+
font-weight: 500;
|
|
72
85
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { FormattedMessage } from 'react-intl';
|
|
3
|
+
import { ANDROID, GENERIC, IOS } from '../../v2Containers/CreativesContainer/constants';
|
|
4
|
+
|
|
5
|
+
export const processErrors = (rawErrorData, errorType, platform, messages) => {
|
|
6
|
+
let errorsToShow = [];
|
|
7
|
+
let titleMsg = messages.standardErrorHeader;
|
|
8
|
+
let platformLabel = null;
|
|
9
|
+
|
|
10
|
+
const isObject = typeof rawErrorData === 'object' && rawErrorData !== null;
|
|
11
|
+
const isNotArray = !Array.isArray(rawErrorData);
|
|
12
|
+
const hasPlatformKeys = isObject && isNotArray && [ANDROID, IOS, GENERIC].some((key) => key in rawErrorData);
|
|
13
|
+
if (hasPlatformKeys) {
|
|
14
|
+
if (errorType === "liquid") {
|
|
15
|
+
titleMsg = messages.dynamicErrorHeader;
|
|
16
|
+
}
|
|
17
|
+
errorsToShow = rawErrorData[platform] || [];
|
|
18
|
+
platformLabel = platform; // Only set platform label for new structure
|
|
19
|
+
} else if (Array.isArray(rawErrorData)) {
|
|
20
|
+
errorsToShow = rawErrorData;
|
|
21
|
+
if (errorType === "liquid") {
|
|
22
|
+
titleMsg = messages.dynamicErrorHeader;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const title = <FormattedMessage {...titleMsg} />;
|
|
27
|
+
return { errorsToShow, title, platformLabel };
|
|
28
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { processErrors } from './utils';
|
|
2
|
+
|
|
3
|
+
const makeMessages = (overrides = {}) => ({
|
|
4
|
+
standardErrorHeader: { id: 'standard', defaultMessage: 'Standard' },
|
|
5
|
+
dynamicErrorHeader: { id: 'liquid', defaultMessage: 'Liquid' },
|
|
6
|
+
...overrides,
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
describe('processErrors', () => {
|
|
10
|
+
it('returns empty errors if platform is not specified and input has GENERIC key', () => {
|
|
11
|
+
const messages = makeMessages();
|
|
12
|
+
const raw = { ANDROID: ['a1'], IOS: ['i1'], GENERIC: ['g1', 'g2'] };
|
|
13
|
+
const result = processErrors(raw, 'standard', undefined, messages);
|
|
14
|
+
expect(result.errorsToShow).toEqual([]);
|
|
15
|
+
expect(result.platformLabel).toBeUndefined();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('returns empty errors for object input with missing keys', () => {
|
|
19
|
+
const messages = makeMessages();
|
|
20
|
+
const raw = { ANDROID: [], IOS: [], GENERIC: [] };
|
|
21
|
+
const result = processErrors(raw, 'standard', 'ANDROID', messages);
|
|
22
|
+
expect(result.errorsToShow).toEqual([]);
|
|
23
|
+
expect(result.platformLabel).toBe('ANDROID');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('returns empty errors for null/undefined/empty input', () => {
|
|
27
|
+
const messages = makeMessages();
|
|
28
|
+
expect(processErrors(null, 'standard', undefined, messages).errorsToShow).toEqual([]);
|
|
29
|
+
expect(processErrors(undefined, 'standard', undefined, messages).errorsToShow).toEqual([]);
|
|
30
|
+
expect(processErrors([], 'standard', undefined, messages).errorsToShow).toEqual([]);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('returns empty errors for object input with only GENERIC key', () => {
|
|
34
|
+
const messages = makeMessages();
|
|
35
|
+
const raw = { GENERIC: ['g1', 'g2'] };
|
|
36
|
+
const result = processErrors(raw, 'standard', undefined, messages);
|
|
37
|
+
expect(result.errorsToShow).toEqual([]);
|
|
38
|
+
expect(result.platformLabel).toBeUndefined();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('handles object input with only ANDROID/IOS keys', () => {
|
|
42
|
+
const messages = makeMessages();
|
|
43
|
+
const raw = { ANDROID: ['a1'], IOS: ['i1'] };
|
|
44
|
+
const result = processErrors(raw, 'standard', 'ANDROID', messages);
|
|
45
|
+
expect(result.errorsToShow).toEqual(['a1']);
|
|
46
|
+
expect(result.platformLabel).toBe('ANDROID');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('handles object input with no errors for currentTab', () => {
|
|
50
|
+
const messages = makeMessages();
|
|
51
|
+
const raw = { ANDROID: [], IOS: ['i1'] };
|
|
52
|
+
const result = processErrors(raw, 'standard', 'ANDROID', messages);
|
|
53
|
+
expect(result.errorsToShow).toEqual([]);
|
|
54
|
+
expect(result.platformLabel).toBe('ANDROID');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('returns undefined platformLabel for object input with no GENERIC key and no platform', () => {
|
|
58
|
+
const messages = makeMessages();
|
|
59
|
+
const raw = { ANDROID: ['a1'], IOS: ['i1'] };
|
|
60
|
+
const result = processErrors(raw, 'standard', undefined, messages);
|
|
61
|
+
expect(result.errorsToShow).toEqual([]);
|
|
62
|
+
expect(result.platformLabel).toBeUndefined();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('returns correct title and errors for array input with errorType liquid', () => {
|
|
66
|
+
const messages = makeMessages();
|
|
67
|
+
const result = processErrors(['liquidErr1', 'liquidErr2'], 'liquid', undefined, messages);
|
|
68
|
+
expect(result.errorsToShow).toEqual(['liquidErr1', 'liquidErr2']);
|
|
69
|
+
expect(result.title.props.id).toBe('liquid');
|
|
70
|
+
expect(result.title.props.defaultMessage).toBe('Liquid');
|
|
71
|
+
expect(result.platformLabel).toBeNull();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('returns correct title and errors for object input with errorType liquid and platform', () => {
|
|
75
|
+
const messages = makeMessages();
|
|
76
|
+
const raw = { ANDROID: ['l1', 'l2'], IOS: ['l3'], GENERIC: ['lg1'] };
|
|
77
|
+
const result = processErrors(raw, 'liquid', 'ANDROID', messages);
|
|
78
|
+
expect(result.errorsToShow).toEqual(['l1', 'l2']);
|
|
79
|
+
expect(result.title.props.id).toBe('liquid');
|
|
80
|
+
expect(result.title.props.defaultMessage).toBe('Liquid');
|
|
81
|
+
expect(result.platformLabel).toBe('ANDROID');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('returns empty errors for object input with errorType liquid and no platform', () => {
|
|
85
|
+
const messages = makeMessages();
|
|
86
|
+
const raw = { ANDROID: ['l1'], IOS: ['l2'], GENERIC: ['lg1', 'lg2'] };
|
|
87
|
+
const result = processErrors(raw, 'liquid', undefined, messages);
|
|
88
|
+
expect(result.errorsToShow).toEqual([]);
|
|
89
|
+
expect(result.title.props.id).toBe('liquid');
|
|
90
|
+
expect(result.title.props.defaultMessage).toBe('Liquid');
|
|
91
|
+
expect(result.platformLabel).toBeUndefined();
|
|
92
|
+
});
|
|
93
|
+
});
|