@capillarytech/creatives-library 9.0.70 → 9.0.71-alpha.sprint-cap-211543.17
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/constants/unified.js +0 -1
- package/package.json +1 -1
- package/utils/common.js +0 -6
- package/v2Components/CapTagListWithInput/__tests__/CapTagListWithInput.test.js +58 -1
- package/v2Components/CapTagListWithInput/index.js +64 -35
- package/v2Components/CapTagListWithInput/index.scss +9 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/EmailPreviewContent.js +8 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +7 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +1 -0
- package/v2Components/CommonTestAndPreview/actions.js +1 -1
- package/v2Components/CommonTestAndPreview/index.js +29 -13
- package/v2Components/CommonTestAndPreview/sagas.js +2 -1
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/EmailPreviewContent.test.js +27 -0
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +40 -0
- package/v2Components/CommonTestAndPreview/tests/index.test.js +95 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +24 -0
- package/v2Components/EmailMobilePreview/index.js +8 -1
- package/v2Components/EmailMobilePreview/index.scss +12 -3
- package/v2Components/EmailMobilePreview/tests/index.test.js +31 -0
- package/v2Components/EmailPreviewV2/_emailPreviewV2.scss +35 -5
- package/v2Components/EmailPreviewV2/index.js +18 -6
- package/v2Components/EmailPreviewV2/messages.js +4 -0
- package/v2Components/EmailPreviewV2/tests/index.test.js +43 -0
- package/v2Components/FormBuilder/Classic.js +113 -10
- package/v2Components/FormBuilder/_formBuilder.scss +18 -0
- package/v2Components/FormBuilder/messages.js +8 -0
- package/v2Containers/Cap/tests/index.test.js +1 -0
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +2 -1
- package/v2Containers/CommunicationFlow/Tests/constants.test.js +4 -4
- package/v2Containers/CommunicationFlow/constants.js +1 -0
- package/v2Containers/CommunicationFlow/utils/Tests/mapCommDefinitionToStepData.test.js +2 -1
- package/v2Containers/CommunicationFlow/utils/mapCommDefinitionToStepData.js +1 -0
- package/v2Containers/CreativesContainer/index.js +9 -3
- package/v2Containers/CreativesContainer/tests/index.test.js +81 -0
- package/v2Containers/Email/index.js +31 -4
- package/v2Containers/Email/initialSchema.js +30 -0
- package/v2Containers/Email/messages.js +8 -0
- package/v2Containers/Email/tests/index.test.js +107 -0
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +168 -8
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +72 -3
- package/v2Containers/EmailWrapper/components/_emailHTMLEditor.scss +26 -0
- package/v2Containers/EmailWrapper/constants.js +5 -0
- package/v2Containers/EmailWrapper/messages.js +20 -0
- package/v2Containers/TagList/index.js +7 -0
- package/v2Containers/TagList/tests/TagList.test.js +42 -0
- package/v2Containers/Templates/constants.js +1 -0
- package/v2Containers/Templates/index.js +31 -27
- package/v2Containers/Templates/messages.js +4 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +128 -4
- package/v2Containers/Templates/tests/archival.test.js +221 -0
package/constants/unified.js
CHANGED
|
@@ -59,7 +59,6 @@ export const EXTENDED_TAG = 'ExtendedTagMessage';
|
|
|
59
59
|
export const BADGES_UI_ENABLED = 'BADGES_UI_ENABLED';
|
|
60
60
|
export const JP_LOCALE_HIDE_FEATURE = 'JP_LOCALE_HIDE_FEATURE';
|
|
61
61
|
export const ENABLE_WECHAT = 'ENABLE_WECHAT';
|
|
62
|
-
export const ENABLE_CREATIVES_ARCHIVAL = 'ENABLE_CREATIVES_ARCHIVAL';
|
|
63
62
|
export const ENABLE_WEBPUSH = 'ENABLE_WEBPUSH';
|
|
64
63
|
export const ENABLE_CUSTOMER_BARCODE_TAG = 'ENABLE_CUSTOMER_BARCODE_TAG';
|
|
65
64
|
export const EMAIL_UNSUBSCRIBE_TAG_MANDATORY = 'EMAIL_UNSUBSCRIBE_TAG_MANDATORY';
|
package/package.json
CHANGED
package/utils/common.js
CHANGED
|
@@ -29,7 +29,6 @@ import {
|
|
|
29
29
|
ENABLE_NEW_FORMBUILDER_SMS,
|
|
30
30
|
ENABLE_NEW_FORMBUILDER_MPUSH,
|
|
31
31
|
SUPPORT_ENGAGEMENT_MODULE,
|
|
32
|
-
ENABLE_CREATIVES_ARCHIVAL,
|
|
33
32
|
} from '../constants/unified';
|
|
34
33
|
import { apiMessageFormatHandler } from './commonUtils';
|
|
35
34
|
|
|
@@ -121,11 +120,6 @@ export const hasWechatFeatureEnabled = Auth.hasFeatureAccess.bind(
|
|
|
121
120
|
ENABLE_WECHAT,
|
|
122
121
|
);
|
|
123
122
|
|
|
124
|
-
export const hasCreativesArchivalEnabled = Auth.hasFeatureAccess.bind(
|
|
125
|
-
null,
|
|
126
|
-
ENABLE_CREATIVES_ARCHIVAL,
|
|
127
|
-
);
|
|
128
|
-
|
|
129
123
|
export const hasWebPushFeatureEnabled = Auth.hasFeatureAccess.bind(
|
|
130
124
|
null,
|
|
131
125
|
ENABLE_WEBPUSH,
|
|
@@ -5,6 +5,7 @@ import { IntlProvider } from 'react-intl';
|
|
|
5
5
|
import CapTagListWithInput from '../index';
|
|
6
6
|
|
|
7
7
|
const capturedTagListProps = { current: null };
|
|
8
|
+
const capturedCapRowProps = { current: null };
|
|
8
9
|
|
|
9
10
|
jest.mock('../../../v2Containers/TagList', () => {
|
|
10
11
|
const React = require('react');
|
|
@@ -15,7 +16,14 @@ jest.mock('../../../v2Containers/TagList', () => {
|
|
|
15
16
|
return Mock;
|
|
16
17
|
});
|
|
17
18
|
|
|
18
|
-
jest.mock('@capillarytech/cap-ui-library/CapRow', () =>
|
|
19
|
+
jest.mock('@capillarytech/cap-ui-library/CapRow', () => {
|
|
20
|
+
const React = require('react');
|
|
21
|
+
const Mock = ({ children, ...rest }) => {
|
|
22
|
+
capturedCapRowProps.current = rest;
|
|
23
|
+
return <div>{children}</div>;
|
|
24
|
+
};
|
|
25
|
+
return Mock;
|
|
26
|
+
});
|
|
19
27
|
jest.mock('@capillarytech/cap-ui-library/CapColumn', () => ({ children }) => <div>{children}</div>);
|
|
20
28
|
jest.mock('@capillarytech/cap-ui-library/CapHeading', () => () => null);
|
|
21
29
|
jest.mock('@capillarytech/cap-ui-library/CapInput', () => () => <input data-testid="cap-input" />);
|
|
@@ -27,6 +35,7 @@ const waitMap = {
|
|
|
27
35
|
describe('CapTagListWithInput', () => {
|
|
28
36
|
beforeEach(() => {
|
|
29
37
|
capturedTagListProps.current = null;
|
|
38
|
+
capturedCapRowProps.current = null;
|
|
30
39
|
});
|
|
31
40
|
|
|
32
41
|
it('forwards waitEventContextTags to TagList', () => {
|
|
@@ -60,4 +69,52 @@ describe('CapTagListWithInput', () => {
|
|
|
60
69
|
|
|
61
70
|
expect(capturedTagListProps.current.waitEventContextTags).toEqual({});
|
|
62
71
|
});
|
|
72
|
+
|
|
73
|
+
it('forwards excludeTags to TagList', () => {
|
|
74
|
+
render(
|
|
75
|
+
<IntlProvider locale="en" messages={{}}>
|
|
76
|
+
<CapTagListWithInput
|
|
77
|
+
inputId="template-preheader"
|
|
78
|
+
inputOnChange={jest.fn()}
|
|
79
|
+
excludeTags={['unsubscribe']}
|
|
80
|
+
onTagSelect={jest.fn()}
|
|
81
|
+
onContextChange={jest.fn()}
|
|
82
|
+
/>
|
|
83
|
+
</IntlProvider>
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
expect(capturedTagListProps.current.excludeTags).toEqual(['unsubscribe']);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('uses default empty array for excludeTags when omitted', () => {
|
|
90
|
+
render(
|
|
91
|
+
<IntlProvider locale="en" messages={{}}>
|
|
92
|
+
<CapTagListWithInput
|
|
93
|
+
inputId="test-url"
|
|
94
|
+
inputOnChange={jest.fn()}
|
|
95
|
+
onTagSelect={jest.fn()}
|
|
96
|
+
onContextChange={jest.fn()}
|
|
97
|
+
/>
|
|
98
|
+
</IntlProvider>
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
expect(capturedTagListProps.current.excludeTags).toEqual([]);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('top-aligns the singleLineLayout row so an error message growing the input does not shift the Add Label button', () => {
|
|
105
|
+
render(
|
|
106
|
+
<IntlProvider locale="en" messages={{}}>
|
|
107
|
+
<CapTagListWithInput
|
|
108
|
+
inputId="template-preheader"
|
|
109
|
+
inputOnChange={jest.fn()}
|
|
110
|
+
inputErrorMessage="Unsubscribe tag is not allowed in Pre-header."
|
|
111
|
+
singleLineLayout
|
|
112
|
+
onTagSelect={jest.fn()}
|
|
113
|
+
onContextChange={jest.fn()}
|
|
114
|
+
/>
|
|
115
|
+
</IntlProvider>
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
expect(capturedCapRowProps.current.align).toBe('top');
|
|
119
|
+
});
|
|
63
120
|
});
|
|
@@ -7,6 +7,7 @@ import CapInput from '@capillarytech/cap-ui-library/CapInput';
|
|
|
7
7
|
import CapColumn from '@capillarytech/cap-ui-library/CapColumn';
|
|
8
8
|
import TagList from '../../v2Containers/TagList';
|
|
9
9
|
import messages from './messages';
|
|
10
|
+
import './index.scss';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* CapTagListWithInput - A reusable component that combines TagList and CapInput
|
|
@@ -18,6 +19,7 @@ export const CapTagListWithInput = (props) => {
|
|
|
18
19
|
// TagList props
|
|
19
20
|
tags = [],
|
|
20
21
|
injectedTags = {},
|
|
22
|
+
excludeTags = [],
|
|
21
23
|
location = {},
|
|
22
24
|
selectedOfferDetails = [],
|
|
23
25
|
onTagSelect,
|
|
@@ -51,10 +53,65 @@ export const CapTagListWithInput = (props) => {
|
|
|
51
53
|
showInput = true,
|
|
52
54
|
inputProps = {},
|
|
53
55
|
popoverPlacement,
|
|
56
|
+
// Tag-insert trigger inline with the input (not in a row above)
|
|
57
|
+
singleLineLayout = false,
|
|
58
|
+
// Content between input and tag trigger (singleLineLayout only)
|
|
59
|
+
extraContent = null,
|
|
54
60
|
} = props;
|
|
55
61
|
|
|
56
62
|
const { formatMessage } = intl;
|
|
57
63
|
|
|
64
|
+
const tagListElement = showTagList && (
|
|
65
|
+
<TagList
|
|
66
|
+
key={`${inputId}_tags`}
|
|
67
|
+
className={className}
|
|
68
|
+
moduleFilterEnabled={moduleFilterEnabled}
|
|
69
|
+
label={formatMessage(messages.addLabels)}
|
|
70
|
+
onTagSelect={onTagSelect}
|
|
71
|
+
onContextChange={onContextChange}
|
|
72
|
+
location={location}
|
|
73
|
+
tags={tags}
|
|
74
|
+
injectedTags={injectedTags}
|
|
75
|
+
excludeTags={excludeTags}
|
|
76
|
+
userLocale={userLocale}
|
|
77
|
+
selectedOfferDetails={selectedOfferDetails}
|
|
78
|
+
eventContextTags={eventContextTags}
|
|
79
|
+
waitEventContextTags={waitEventContextTags}
|
|
80
|
+
style={tagListStyle}
|
|
81
|
+
popoverPlacement={popoverPlacement}
|
|
82
|
+
restrictPersonalization={restrictPersonalization}
|
|
83
|
+
/>
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const inputElement = showInput && (
|
|
87
|
+
<CapInput
|
|
88
|
+
id={inputId}
|
|
89
|
+
onChange={inputOnChange}
|
|
90
|
+
placeholder={inputPlaceholder}
|
|
91
|
+
value={inputValue}
|
|
92
|
+
size={inputSize}
|
|
93
|
+
maxLength={inputMaxLength}
|
|
94
|
+
errorMessage={inputErrorMessage}
|
|
95
|
+
isRequired={inputRequired}
|
|
96
|
+
disabled={inputDisabled}
|
|
97
|
+
style={inputStyle}
|
|
98
|
+
{...inputProps}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
if (singleLineLayout) {
|
|
103
|
+
return (
|
|
104
|
+
<CapColumn style={containerStyle}>
|
|
105
|
+
{/* "top", not "middle": keeps the buttons in place when an error grows the input */}
|
|
106
|
+
<CapRow align="top" type="flex" noWrap gap={8} className="cap-tag-list-with-input__single-line-row">
|
|
107
|
+
<CapColumn className="cap-tag-list-with-input__input-column">{inputElement}</CapColumn>
|
|
108
|
+
{extraContent}
|
|
109
|
+
{tagListElement}
|
|
110
|
+
</CapRow>
|
|
111
|
+
</CapColumn>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
58
115
|
return (
|
|
59
116
|
<CapColumn style={containerStyle}>
|
|
60
117
|
<CapRow align="middle" type="flex" noWrap>
|
|
@@ -64,42 +121,9 @@ export const CapTagListWithInput = (props) => {
|
|
|
64
121
|
</CapHeading>
|
|
65
122
|
)}
|
|
66
123
|
|
|
67
|
-
{
|
|
68
|
-
<TagList
|
|
69
|
-
key={`${inputId}_tags`}
|
|
70
|
-
className={className}
|
|
71
|
-
moduleFilterEnabled={moduleFilterEnabled}
|
|
72
|
-
label={formatMessage(messages.addLabels)}
|
|
73
|
-
onTagSelect={onTagSelect}
|
|
74
|
-
onContextChange={onContextChange}
|
|
75
|
-
location={location}
|
|
76
|
-
tags={tags}
|
|
77
|
-
injectedTags={injectedTags}
|
|
78
|
-
userLocale={userLocale}
|
|
79
|
-
selectedOfferDetails={selectedOfferDetails}
|
|
80
|
-
eventContextTags={eventContextTags}
|
|
81
|
-
waitEventContextTags={waitEventContextTags}
|
|
82
|
-
style={tagListStyle}
|
|
83
|
-
popoverPlacement={popoverPlacement}
|
|
84
|
-
restrictPersonalization={restrictPersonalization}
|
|
85
|
-
/>
|
|
86
|
-
)}
|
|
124
|
+
{tagListElement}
|
|
87
125
|
</CapRow>
|
|
88
|
-
{
|
|
89
|
-
<CapInput
|
|
90
|
-
id={inputId}
|
|
91
|
-
onChange={inputOnChange}
|
|
92
|
-
placeholder={inputPlaceholder}
|
|
93
|
-
value={inputValue}
|
|
94
|
-
size={inputSize}
|
|
95
|
-
maxLength={inputMaxLength}
|
|
96
|
-
errorMessage={inputErrorMessage}
|
|
97
|
-
isRequired={inputRequired}
|
|
98
|
-
disabled={inputDisabled}
|
|
99
|
-
style={inputStyle}
|
|
100
|
-
{...inputProps}
|
|
101
|
-
/>
|
|
102
|
-
)}
|
|
126
|
+
{inputElement}
|
|
103
127
|
</CapColumn>
|
|
104
128
|
);
|
|
105
129
|
};
|
|
@@ -109,6 +133,7 @@ CapTagListWithInput.propTypes = {
|
|
|
109
133
|
// TagList props
|
|
110
134
|
tags: PropTypes.array,
|
|
111
135
|
injectedTags: PropTypes.object,
|
|
136
|
+
excludeTags: PropTypes.array,
|
|
112
137
|
location: PropTypes.object,
|
|
113
138
|
selectedOfferDetails: PropTypes.array,
|
|
114
139
|
onTagSelect: PropTypes.func.isRequired,
|
|
@@ -145,6 +170,8 @@ CapTagListWithInput.propTypes = {
|
|
|
145
170
|
showTagList: PropTypes.bool,
|
|
146
171
|
showInput: PropTypes.bool,
|
|
147
172
|
popoverPlacement: PropTypes.string,
|
|
173
|
+
singleLineLayout: PropTypes.bool,
|
|
174
|
+
extraContent: PropTypes.node,
|
|
148
175
|
};
|
|
149
176
|
|
|
150
177
|
CapTagListWithInput.defaultProps = {
|
|
@@ -176,6 +203,8 @@ CapTagListWithInput.defaultProps = {
|
|
|
176
203
|
showInput: true,
|
|
177
204
|
inputProps: {},
|
|
178
205
|
popoverPlacement: undefined,
|
|
206
|
+
singleLineLayout: false,
|
|
207
|
+
extraContent: null,
|
|
179
208
|
};
|
|
180
209
|
|
|
181
210
|
export default injectIntl(CapTagListWithInput);
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
const EmailPreviewContent = ({
|
|
23
23
|
content,
|
|
24
24
|
subject,
|
|
25
|
+
preheader,
|
|
25
26
|
device,
|
|
26
27
|
isUpdating,
|
|
27
28
|
error,
|
|
@@ -75,6 +76,11 @@ const EmailPreviewContent = ({
|
|
|
75
76
|
</CapRow>
|
|
76
77
|
</CapRow>
|
|
77
78
|
|
|
79
|
+
{/* Email Pre-header */}
|
|
80
|
+
{preheader && (
|
|
81
|
+
<CapRow useLegacy className="email-preheader">{preheader}</CapRow>
|
|
82
|
+
)}
|
|
83
|
+
|
|
78
84
|
{/* Email Content - Varies based on state */}
|
|
79
85
|
<CapRow useLegacy className="email-content">
|
|
80
86
|
{contentSection}
|
|
@@ -131,6 +137,7 @@ const EmailPreviewContent = ({
|
|
|
131
137
|
EmailPreviewContent.propTypes = {
|
|
132
138
|
content: PropTypes.string,
|
|
133
139
|
subject: PropTypes.string,
|
|
140
|
+
preheader: PropTypes.string,
|
|
134
141
|
device: PropTypes.oneOf([DESKTOP, MOBILE, TABLET]),
|
|
135
142
|
isUpdating: PropTypes.bool,
|
|
136
143
|
error: PropTypes.string,
|
|
@@ -140,6 +147,7 @@ EmailPreviewContent.propTypes = {
|
|
|
140
147
|
EmailPreviewContent.defaultProps = {
|
|
141
148
|
content: '',
|
|
142
149
|
subject: '',
|
|
150
|
+
preheader: '',
|
|
143
151
|
device: DESKTOP,
|
|
144
152
|
isUpdating: false,
|
|
145
153
|
error: null,
|
|
@@ -520,6 +520,13 @@
|
|
|
520
520
|
}
|
|
521
521
|
}
|
|
522
522
|
|
|
523
|
+
// Email Pre-header
|
|
524
|
+
.email-preheader {
|
|
525
|
+
padding: $CAP_SPACE_12 $CAP_SPACE_16;
|
|
526
|
+
font-size: $FONT_SIZE_M;
|
|
527
|
+
color: #8c8c8c;
|
|
528
|
+
}
|
|
529
|
+
|
|
523
530
|
// Email Content
|
|
524
531
|
.email-content {
|
|
525
532
|
border-top: $CAP_SPACE_01 solid $CAP_G12;
|
|
@@ -43,7 +43,7 @@ export const searchCustomersRequested = (query) => ({
|
|
|
43
43
|
*/
|
|
44
44
|
export const extractTagsRequested = (payload) => ({
|
|
45
45
|
type: EXTRACT_TAGS_REQUESTED,
|
|
46
|
-
payload, // { templateSubject, templateContent }
|
|
46
|
+
payload, // { templateSubject, templatePreheader, templateContent }
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
/**
|
|
@@ -947,6 +947,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
947
947
|
return {
|
|
948
948
|
...basePayload,
|
|
949
949
|
messageTitle: resolveTagsInText(formDataObj['template-subject'], customValuesObj),
|
|
950
|
+
messageSubtitle: resolveTagsInText(formDataObj['template-preheader'], customValuesObj),
|
|
950
951
|
messageBody: contentStr,
|
|
951
952
|
};
|
|
952
953
|
|
|
@@ -1069,6 +1070,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
1069
1070
|
case CHANNELS.EMAIL:
|
|
1070
1071
|
return {
|
|
1071
1072
|
templateSubject: formDataObj['template-subject'] || '',
|
|
1073
|
+
templatePreheader: formDataObj['template-preheader'] || '',
|
|
1072
1074
|
templateContent: contentStr,
|
|
1073
1075
|
};
|
|
1074
1076
|
|
|
@@ -1410,13 +1412,22 @@ const CommonTestAndPreview = (props) => {
|
|
|
1410
1412
|
|| formDataObj?.base?.['template-subject']
|
|
1411
1413
|
|| '';
|
|
1412
1414
|
|
|
1415
|
+
// Extract pre-header from formData (same shapes as subject)
|
|
1416
|
+
const preheader = formDataObj?.['template-preheader']
|
|
1417
|
+
|| formDataObj?.[0]?.['template-preheader']
|
|
1418
|
+
|| formDataObj?.base?.['template-preheader']
|
|
1419
|
+
|| '';
|
|
1420
|
+
|
|
1413
1421
|
// Extract body from previewData (resolved) or content
|
|
1414
1422
|
const messageBody = previewDataObj?.resolvedBody || contentStr || '';
|
|
1415
1423
|
|
|
1416
|
-
// Resolve tags in subject if custom values are provided
|
|
1424
|
+
// Resolve tags in subject/pre-header if custom values are provided
|
|
1417
1425
|
const resolvedSubject = customValuesObj && Object.keys(customValuesObj).length > 0
|
|
1418
1426
|
? resolveTagsInText(subject, customValuesObj)
|
|
1419
1427
|
: subject;
|
|
1428
|
+
const resolvedPreheader = customValuesObj && Object.keys(customValuesObj).length > 0
|
|
1429
|
+
? resolveTagsInText(preheader, customValuesObj)
|
|
1430
|
+
: preheader;
|
|
1420
1431
|
|
|
1421
1432
|
const emailChannelSettings = {
|
|
1422
1433
|
channel: CHANNELS.EMAIL,
|
|
@@ -1442,6 +1453,7 @@ const CommonTestAndPreview = (props) => {
|
|
|
1442
1453
|
channel: CHANNELS.EMAIL,
|
|
1443
1454
|
messageBody,
|
|
1444
1455
|
messageSubject: resolvedSubject,
|
|
1456
|
+
messagePreHeader: resolvedPreheader,
|
|
1445
1457
|
},
|
|
1446
1458
|
};
|
|
1447
1459
|
}
|
|
@@ -2587,6 +2599,10 @@ const CommonTestAndPreview = (props) => {
|
|
|
2587
2599
|
resolvedTitle: hasPreviewCallBeenMade && previewDataHtml?.resolvedTitle
|
|
2588
2600
|
? previewDataHtml.resolvedTitle
|
|
2589
2601
|
: formData?.['template-subject'] || '',
|
|
2602
|
+
// The liquid preview API returns the resolved pre-header as `resolvedSubtitle`.
|
|
2603
|
+
resolvedPreheader: hasPreviewCallBeenMade && previewDataHtml?.resolvedSubtitle
|
|
2604
|
+
? previewDataHtml.resolvedSubtitle
|
|
2605
|
+
: formData?.['template-preheader'] || '',
|
|
2590
2606
|
};
|
|
2591
2607
|
} else if (channel === CHANNELS.SMS) {
|
|
2592
2608
|
// For SMS, content is the text message (string)
|
|
@@ -2931,12 +2947,12 @@ const CommonTestAndPreview = (props) => {
|
|
|
2931
2947
|
});
|
|
2932
2948
|
|
|
2933
2949
|
// Always extract tags when content changes
|
|
2934
|
-
const { templateSubject, templateContent } = prepareTagExtractionPayload(
|
|
2950
|
+
const { templateSubject, templatePreheader, templateContent } = prepareTagExtractionPayload(
|
|
2935
2951
|
channel,
|
|
2936
2952
|
formData,
|
|
2937
2953
|
htmlFile,
|
|
2938
2954
|
);
|
|
2939
|
-
actions.extractTagsRequested({ templateSubject, templateContent });
|
|
2955
|
+
actions.extractTagsRequested({ templateSubject, templatePreheader, templateContent });
|
|
2940
2956
|
}
|
|
2941
2957
|
|
|
2942
2958
|
// Restore original handler
|
|
@@ -2951,32 +2967,32 @@ const CommonTestAndPreview = (props) => {
|
|
|
2951
2967
|
const templateContent = currentTabData?.[activeTab]?.['template-content'];
|
|
2952
2968
|
if (templateContent) {
|
|
2953
2969
|
// Extract tags when showing (but don't set previewDataHtml - use raw content initially)
|
|
2954
|
-
const { templateSubject, templateContent: extractContent } = prepareTagExtractionPayload(
|
|
2970
|
+
const { templateSubject, templatePreheader, templateContent: extractContent } = prepareTagExtractionPayload(
|
|
2955
2971
|
channel,
|
|
2956
2972
|
formData,
|
|
2957
2973
|
templateContent
|
|
2958
2974
|
);
|
|
2959
|
-
actions.extractTagsRequested({ templateSubject, templateContent: extractContent });
|
|
2975
|
+
actions.extractTagsRequested({ templateSubject, templatePreheader, templateContent: extractContent });
|
|
2960
2976
|
} else {
|
|
2961
2977
|
// Fallback to content prop if no template content
|
|
2962
|
-
const { templateSubject, templateContent: extractContent } = prepareTagExtractionPayload(
|
|
2978
|
+
const { templateSubject, templatePreheader, templateContent: extractContent } = prepareTagExtractionPayload(
|
|
2963
2979
|
channel,
|
|
2964
2980
|
formData,
|
|
2965
2981
|
getCurrentContent
|
|
2966
2982
|
);
|
|
2967
|
-
actions.extractTagsRequested({ templateSubject, templateContent: extractContent });
|
|
2983
|
+
actions.extractTagsRequested({ templateSubject, templatePreheader, templateContent: extractContent });
|
|
2968
2984
|
}
|
|
2969
2985
|
}
|
|
2970
2986
|
} else {
|
|
2971
2987
|
// For non-email channels, extract tags from content
|
|
2972
2988
|
// Use getCurrentContent for SMS (extracts from formData) or content prop for others
|
|
2973
2989
|
const contentToConvert = getCurrentContent;
|
|
2974
|
-
const { templateSubject, templateContent } = prepareTagExtractionPayload(
|
|
2990
|
+
const { templateSubject, templatePreheader, templateContent } = prepareTagExtractionPayload(
|
|
2975
2991
|
channel,
|
|
2976
2992
|
formData || {},
|
|
2977
2993
|
contentToConvert
|
|
2978
2994
|
);
|
|
2979
|
-
actions.extractTagsRequested({ templateSubject, templateContent });
|
|
2995
|
+
actions.extractTagsRequested({ templateSubject, templatePreheader, templateContent });
|
|
2980
2996
|
}
|
|
2981
2997
|
|
|
2982
2998
|
// Fetch test customers and groups if enabled
|
|
@@ -3057,12 +3073,12 @@ const CommonTestAndPreview = (props) => {
|
|
|
3057
3073
|
const handleContentUpdate = (contentToUpdate) => {
|
|
3058
3074
|
// Extract tags from content (but don't set previewDataHtml - use raw content initially)
|
|
3059
3075
|
// previewDataHtml will only be set when preview API call is made
|
|
3060
|
-
const { templateSubject, templateContent: extractContent } = prepareTagExtractionPayload(
|
|
3076
|
+
const { templateSubject, templatePreheader, templateContent: extractContent } = prepareTagExtractionPayload(
|
|
3061
3077
|
channel,
|
|
3062
3078
|
formData,
|
|
3063
3079
|
contentToUpdate
|
|
3064
3080
|
);
|
|
3065
|
-
actions.extractTagsRequested({ templateSubject, templateContent: extractContent });
|
|
3081
|
+
actions.extractTagsRequested({ templateSubject, templatePreheader, templateContent: extractContent });
|
|
3066
3082
|
};
|
|
3067
3083
|
|
|
3068
3084
|
if (isDragDrop) {
|
|
@@ -3504,12 +3520,12 @@ const CommonTestAndPreview = (props) => {
|
|
|
3504
3520
|
}
|
|
3505
3521
|
|
|
3506
3522
|
setTagsExtracted(true);
|
|
3507
|
-
const { templateSubject, templateContent } = prepareTagExtractionPayload(
|
|
3523
|
+
const { templateSubject, templatePreheader, templateContent } = prepareTagExtractionPayload(
|
|
3508
3524
|
activeChannelForActions,
|
|
3509
3525
|
formData || {},
|
|
3510
3526
|
contentToExtract
|
|
3511
3527
|
);
|
|
3512
|
-
actions.extractTagsRequested({ templateSubject, templateContent });
|
|
3528
|
+
actions.extractTagsRequested({ templateSubject, templatePreheader, templateContent });
|
|
3513
3529
|
};
|
|
3514
3530
|
|
|
3515
3531
|
/**
|
|
@@ -70,10 +70,11 @@ export function* searchCustomersSaga(action) {
|
|
|
70
70
|
// Extract Tags Saga
|
|
71
71
|
export function* extractTagsSaga(action) {
|
|
72
72
|
try {
|
|
73
|
-
const { templateSubject, templateContent } = action.payload;
|
|
73
|
+
const { templateSubject, templatePreheader, templateContent } = action.payload;
|
|
74
74
|
|
|
75
75
|
const payload = {
|
|
76
76
|
messageTitle: templateSubject || '',
|
|
77
|
+
messageSubtitle: templatePreheader || '',
|
|
77
78
|
messageBody: templateContent || '',
|
|
78
79
|
};
|
|
79
80
|
|
|
@@ -481,6 +481,33 @@ describe('EmailPreviewContent', () => {
|
|
|
481
481
|
|
|
482
482
|
expect(container.querySelector('.email-content')).toBeTruthy();
|
|
483
483
|
});
|
|
484
|
+
|
|
485
|
+
it('should not render a pre-header row when preheader is not provided', () => {
|
|
486
|
+
const { container } = render(
|
|
487
|
+
<TestWrapper>
|
|
488
|
+
<ComponentToRender {...defaultProps} />
|
|
489
|
+
</TestWrapper>
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
expect(container.querySelector('.email-preheader')).toBeNull();
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
it('should render the pre-header text when provided', () => {
|
|
496
|
+
const props = {
|
|
497
|
+
...defaultProps,
|
|
498
|
+
preheader: 'Unlock 20% off your next order',
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
const { container } = render(
|
|
502
|
+
<TestWrapper>
|
|
503
|
+
<ComponentToRender {...props} />
|
|
504
|
+
</TestWrapper>
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
const preheaderElement = container.querySelector('.email-preheader');
|
|
508
|
+
expect(preheaderElement).toBeTruthy();
|
|
509
|
+
expect(preheaderElement.textContent).toBe('Unlock 20% off your next order');
|
|
510
|
+
});
|
|
484
511
|
});
|
|
485
512
|
|
|
486
513
|
describe('Device Handling', () => {
|
|
@@ -46,6 +46,7 @@ jest.mock('../../UnifiedPreview/EmailPreviewContent', () => ({
|
|
|
46
46
|
<div data-testid="email-preview">
|
|
47
47
|
<div data-testid="email-content">{props.content}</div>
|
|
48
48
|
<div data-testid="email-subject">{props.subject}</div>
|
|
49
|
+
<div data-testid="email-preheader">{props.preheader}</div>
|
|
49
50
|
<div data-testid="email-device">{props.device}</div>
|
|
50
51
|
</div>
|
|
51
52
|
),
|
|
@@ -288,6 +289,45 @@ describe('UnifiedPreview', () => {
|
|
|
288
289
|
expect(screen.getByTestId('email-subject')).toHaveTextContent('Email Subject');
|
|
289
290
|
});
|
|
290
291
|
|
|
292
|
+
it('should pass resolvedPreheader through as the preheader prop', () => {
|
|
293
|
+
const props = {
|
|
294
|
+
...defaultProps,
|
|
295
|
+
channel: CHANNELS.EMAIL,
|
|
296
|
+
content: {
|
|
297
|
+
resolvedBody: '<html>Email Content</html>',
|
|
298
|
+
resolvedTitle: 'Email Subject',
|
|
299
|
+
resolvedPreheader: 'Unlock 20% off',
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
render(
|
|
304
|
+
<TestWrapper>
|
|
305
|
+
<ComponentToRender {...props} />
|
|
306
|
+
</TestWrapper>
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
expect(screen.getByTestId('email-preheader')).toHaveTextContent('Unlock 20% off');
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it('should default preheader to an empty string when resolvedPreheader is absent', () => {
|
|
313
|
+
const props = {
|
|
314
|
+
...defaultProps,
|
|
315
|
+
channel: CHANNELS.EMAIL,
|
|
316
|
+
content: {
|
|
317
|
+
resolvedBody: '<html>Email Content</html>',
|
|
318
|
+
resolvedTitle: 'Email Subject',
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
render(
|
|
323
|
+
<TestWrapper>
|
|
324
|
+
<ComponentToRender {...props} />
|
|
325
|
+
</TestWrapper>
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
expect(screen.getByTestId('email-preheader')).toHaveTextContent('');
|
|
329
|
+
});
|
|
330
|
+
|
|
291
331
|
it('should handle empty resolvedBody', () => {
|
|
292
332
|
const props = {
|
|
293
333
|
...defaultProps,
|