@capillarytech/creatives-library 9.0.73-alpha.sprint-testpreview.22 → 9.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/v2Components/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 +36 -75
- 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 +92 -97
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +24 -0
- package/v2Components/CommonTestAndPreview/tests/utils.test.js +0 -66
- package/v2Components/CommonTestAndPreview/utils.js +0 -14
- 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 +37 -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 +134 -14
- package/v2Components/FormBuilder/Functional/FormBuilderShell.js +9 -0
- package/v2Components/FormBuilder/Functional/layout/TabsContainer.js +11 -12
- package/v2Components/FormBuilder/Functional/tests/mpush.singlePlatform.test.js +144 -0
- package/v2Components/FormBuilder/Functional/tests/tabsContainer.test.js +17 -5
- package/v2Components/FormBuilder/_formBuilder.scss +21 -0
- package/v2Components/FormBuilder/messages.js +12 -0
- package/v2Components/FormBuilder/tests/mpush.characterization.test.js +45 -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/SlideBoxContent.js +0 -2
- package/v2Containers/CreativesContainer/index.js +7 -1
- 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 +172 -8
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +264 -5
- package/v2Containers/EmailWrapper/components/_emailHTMLEditor.scss +29 -0
- package/v2Containers/EmailWrapper/constants.js +5 -0
- package/v2Containers/EmailWrapper/messages.js +24 -0
- package/v2Containers/MobilePush/Create/constants.js +1 -0
- package/v2Containers/MobilePush/Create/index.js +2 -2
- package/v2Containers/MobilePush/Create/test/contentValidity.test.js +58 -0
- package/v2Containers/MobilePush/Edit/index.js +2 -2
- package/v2Containers/MobilePush/Edit/test/contentValidity.test.js +17 -0
- package/v2Containers/MobilePush/commonMethods.js +7 -8
- package/v2Containers/Rcs/index.js +1 -27
- package/v2Containers/TagList/index.js +7 -0
- package/v2Containers/TagList/tests/TagList.test.js +42 -0
- package/v2Containers/Templates/index.js +4 -9
- package/v2Containers/Templates/tests/index.test.js +35 -107
- package/v2Containers/Viber/index.scss +31 -0
- package/v2Containers/Whatsapp/index.js +1 -18
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +2 -2
- package/v2Containers/Zalo/index.js +0 -18
- package/v2Containers/Rcs/tests/testAndPreviewFromListing.test.js +0 -132
- package/v2Containers/Whatsapp/tests/testAndPreviewFromListing.test.js +0 -91
- package/v2Containers/Zalo/tests/testAndPreviewFromListing.test.js +0 -98
|
@@ -47,16 +47,28 @@ describe('TabsContainer', () => {
|
|
|
47
47
|
expect(c2.textContent).toBe('');
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
it('skips isSupported:false panes
|
|
50
|
+
it('skips isSupported:false panes but keeps each visible pane bound to its own tab', () => {
|
|
51
51
|
const container = buildContainer();
|
|
52
52
|
container.panes[0].isSupported = false;
|
|
53
|
+
[0, 1].forEach((activeTabIndex) => {
|
|
54
|
+
const { container: dom, unmount } = render(
|
|
55
|
+
<TabsContainer container={container} renderContext={buildContext({ activeTabIndex })} />,
|
|
56
|
+
);
|
|
57
|
+
// only the iOS pane renders, bound to tab 1 (key 'kI'), and it is ACTIVE even
|
|
58
|
+
// when the active index still points at the hidden Android tab
|
|
59
|
+
expect(dom.querySelectorAll('.ant-tabs-tab').length).toBe(1);
|
|
60
|
+
expect(dom.querySelector('.ant-tabs-tab-active').getAttribute('data-node-key')).toBe('kI');
|
|
61
|
+
unmount();
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('renders no tabs when every pane is hidden', () => {
|
|
66
|
+
const container = buildContainer();
|
|
67
|
+
container.panes.forEach((pane) => { pane.isSupported = false; }); // eslint-disable-line no-param-reassign
|
|
53
68
|
const { container: dom } = render(
|
|
54
69
|
<TabsContainer container={container} renderContext={buildContext()} />,
|
|
55
70
|
);
|
|
56
|
-
|
|
57
|
-
// it must also be ACTIVE, proving the pane key and activeKey shift together
|
|
58
|
-
expect(dom.querySelectorAll('.ant-tabs-tab').length).toBe(1);
|
|
59
|
-
expect(dom.querySelectorAll('.ant-tabs-tab-active').length).toBe(1);
|
|
71
|
+
expect(dom.querySelectorAll('.ant-tabs-tab').length).toBe(0);
|
|
60
72
|
});
|
|
61
73
|
|
|
62
74
|
it('renders the tab-header sections for every locale (Classic has no locale gating)', () => {
|
|
@@ -136,4 +136,25 @@
|
|
|
136
136
|
}
|
|
137
137
|
.error-boundary{
|
|
138
138
|
border: $CAP_SPACE_02 solid $CAP_RED;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Suffix controls (character counter, Remove link) rendered alongside EMAIL's
|
|
142
|
+
// Subject/Pre-header inputs by HighFreqTagInput (Classic.js).
|
|
143
|
+
.high-freq-tag-input-counter {
|
|
144
|
+
white-space: nowrap;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.high-freq-tag-input-remove-link {
|
|
148
|
+
white-space: nowrap;
|
|
149
|
+
cursor: pointer;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.high-freq-tag-input-prefix {
|
|
153
|
+
padding-right: $CAP_SPACE_08;
|
|
154
|
+
margin-right: $CAP_SPACE_08;
|
|
155
|
+
border-right: 1px solid $CAP_COLOR_16;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.high-freq-tag-input-prefix-label {
|
|
159
|
+
font-weight: 600;
|
|
139
160
|
}
|
|
@@ -98,4 +98,16 @@ export default defineMessages({
|
|
|
98
98
|
id: `creatives.componentsV2.FormBuilder.personalizationTagsErrorMessage`,
|
|
99
99
|
defaultMessage: 'Personalization tags are not supported for anonymous customers, please remove the tags.',
|
|
100
100
|
},
|
|
101
|
+
addPreheader: {
|
|
102
|
+
id: 'creatives.componentsV2.FormBuilder.addPreheader',
|
|
103
|
+
defaultMessage: '+ Pre-header',
|
|
104
|
+
},
|
|
105
|
+
removePreheader: {
|
|
106
|
+
id: 'creatives.componentsV2.FormBuilder.removePreheader',
|
|
107
|
+
defaultMessage: 'Remove',
|
|
108
|
+
},
|
|
109
|
+
preheaderInfoTooltip: {
|
|
110
|
+
id: 'creatives.componentsV2.FormBuilder.preheaderInfoTooltip',
|
|
111
|
+
defaultMessage: 'Shows next to the subject line in the inbox, use it to add context and improve open rates. Keep it to 40–85 characters.',
|
|
112
|
+
},
|
|
101
113
|
});
|
|
@@ -457,3 +457,48 @@ describe('MPUSH characterization — imageSchema', () => {
|
|
|
457
457
|
expect(props.onSubmit).not.toHaveBeenCalled();
|
|
458
458
|
});
|
|
459
459
|
});
|
|
460
|
+
|
|
461
|
+
describe('MPUSH — single-platform accounts', () => {
|
|
462
|
+
const tab0 = { 'message-title': 'A title', 'message-editor': 'A body', base: true };
|
|
463
|
+
const tab1 = { 'message-title2': 'I title', 'message-editor2': 'I body' };
|
|
464
|
+
const renderEdit = async (hiddenPane, currentTab) => {
|
|
465
|
+
const { schema } = buildMpushSchema();
|
|
466
|
+
schema.containers[0].panes[hiddenPane].isSupported = false;
|
|
467
|
+
const base = buildProps({ schema, isEdit: true, formData: {} });
|
|
468
|
+
const { rerenderWith } = renderMpush(base);
|
|
469
|
+
rerenderWith({ ...base, currentTab });
|
|
470
|
+
rerenderWith({
|
|
471
|
+
...base, currentTab, formData: { 'template-name': 'T', 0: { ...tab0 }, 1: { ...tab1 }, base: tab0 },
|
|
472
|
+
});
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
it('iOS only (Android hidden): edit shows the saved iOS content', async () => {
|
|
476
|
+
await renderEdit(0, 2);
|
|
477
|
+
await waitFor(() => {
|
|
478
|
+
expect(document.getElementById('message-title2')).toHaveValue('I title');
|
|
479
|
+
expect(document.getElementById('message-editor2')).toHaveValue('I body');
|
|
480
|
+
});
|
|
481
|
+
expect(screen.queryByText('Android')).toBeNull();
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
it('Android only (iOS hidden): edit shows the saved Android content', async () => {
|
|
485
|
+
await renderEdit(1, 1);
|
|
486
|
+
await waitFor(() => {
|
|
487
|
+
expect(titleInput()).toHaveValue('A title');
|
|
488
|
+
expect(editorInput()).toHaveValue('A body');
|
|
489
|
+
});
|
|
490
|
+
expect(screen.queryByText('iOS')).toBeNull();
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
it('both platforms hidden: renders no tabs and leaves currentTab alone', () => {
|
|
494
|
+
const { schema } = buildMpushSchema();
|
|
495
|
+
schema.containers[0].panes[0].isSupported = false;
|
|
496
|
+
schema.containers[0].panes[1].isSupported = false;
|
|
497
|
+
const base = buildProps({ schema, formData: _.cloneDeep(seedTabs) });
|
|
498
|
+
const { rerenderWith } = renderMpush(base);
|
|
499
|
+
rerenderWith({ ...base, currentTab: 2 });
|
|
500
|
+
|
|
501
|
+
expect(screen.queryByText('Android')).toBeNull();
|
|
502
|
+
expect(screen.queryByText('iOS')).toBeNull();
|
|
503
|
+
});
|
|
504
|
+
});
|
|
@@ -479,7 +479,7 @@ describe('handleSave — CCS flow', () => {
|
|
|
479
479
|
initialData: {
|
|
480
480
|
contentItems: [{
|
|
481
481
|
channel: 'EMAIL',
|
|
482
|
-
templateData: { emailSubject: 'Order Confirmed', emailBody: '<html>Hi</html>' },
|
|
482
|
+
templateData: { emailSubject: 'Order Confirmed', emailBody: '<html>Hi</html>', emailPreHeader: 'Your order is on its way' },
|
|
483
483
|
}],
|
|
484
484
|
},
|
|
485
485
|
onSave,
|
|
@@ -497,6 +497,7 @@ describe('handleSave — CCS flow', () => {
|
|
|
497
497
|
channel: 'EMAIL',
|
|
498
498
|
messageSubject: 'Order Confirmed',
|
|
499
499
|
messageBody: '<html>Hi</html>',
|
|
500
|
+
messagePreHeader: 'Your order is on its way',
|
|
500
501
|
},
|
|
501
502
|
}),
|
|
502
503
|
}),
|
|
@@ -10,20 +10,20 @@ describe('CCS_CONTENT_TRANSFORMS', () => {
|
|
|
10
10
|
it('prefers emailBody over emailHtml when both are present', () => {
|
|
11
11
|
expect(
|
|
12
12
|
CCS_CONTENT_TRANSFORMS.EMAIL({
|
|
13
|
-
emailSubject: 'Subject', emailBody: '<p>Body</p>', emailHtml: '<p>Html</p>',
|
|
13
|
+
emailSubject: 'Subject', emailBody: '<p>Body</p>', emailHtml: '<p>Html</p>', emailPreHeader: 'Preheader',
|
|
14
14
|
}),
|
|
15
|
-
).toEqual({ messageSubject: 'Subject', messageBody: '<p>Body</p>' });
|
|
15
|
+
).toEqual({ messageSubject: 'Subject', messageBody: '<p>Body</p>', messagePreHeader: 'Preheader' });
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
it('falls back to emailHtml when emailBody is absent', () => {
|
|
19
19
|
expect(
|
|
20
20
|
CCS_CONTENT_TRANSFORMS.EMAIL({ emailSubject: 'Subject', emailHtml: '<p>Html</p>' }),
|
|
21
|
-
).toEqual({ messageSubject: 'Subject', messageBody: '<p>Html</p>' });
|
|
21
|
+
).toEqual({ messageSubject: 'Subject', messageBody: '<p>Html</p>', messagePreHeader: undefined });
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
it('defaults templateData to {} when called with no argument', () => {
|
|
25
25
|
expect(CCS_CONTENT_TRANSFORMS.EMAIL()).toEqual({
|
|
26
|
-
messageSubject: undefined, messageBody: undefined,
|
|
26
|
+
messageSubject: undefined, messageBody: undefined, messagePreHeader: undefined,
|
|
27
27
|
});
|
|
28
28
|
});
|
|
29
29
|
});
|
|
@@ -65,6 +65,7 @@ export const CCS_CONTENT_TRANSFORMS = {
|
|
|
65
65
|
EMAIL: (templateData = {}) => ({
|
|
66
66
|
messageSubject: templateData.emailSubject,
|
|
67
67
|
messageBody: templateData.emailBody || templateData.emailHtml,
|
|
68
|
+
messagePreHeader: templateData.emailPreHeader,
|
|
68
69
|
}),
|
|
69
70
|
SMS: (templateData = {}) => ({
|
|
70
71
|
message: templateData.messageBody || '',
|
|
@@ -101,7 +101,7 @@ describe('mapCommDefinitionToStepData', () => {
|
|
|
101
101
|
singleChannelStrategy: {
|
|
102
102
|
variant: {
|
|
103
103
|
channel: 'EMAIL',
|
|
104
|
-
emailMessageContent: { messageSubject: 'Subject', messageBody: '<p>Body</p>' },
|
|
104
|
+
emailMessageContent: { messageSubject: 'Subject', messageBody: '<p>Body</p>', messagePreHeader: 'Preheader' },
|
|
105
105
|
},
|
|
106
106
|
},
|
|
107
107
|
};
|
|
@@ -111,6 +111,7 @@ describe('mapCommDefinitionToStepData', () => {
|
|
|
111
111
|
expect(result.contentItems[0].templateData).toEqual({
|
|
112
112
|
emailSubject: 'Subject',
|
|
113
113
|
emailBody: '<p>Body</p>',
|
|
114
|
+
emailPreHeader: 'Preheader',
|
|
114
115
|
channel: 'EMAIL',
|
|
115
116
|
});
|
|
116
117
|
});
|
|
@@ -27,6 +27,7 @@ const CCS_CONTENT_REVERSE_TRANSFORMS = {
|
|
|
27
27
|
EMAIL: (payload = {}) => ({
|
|
28
28
|
emailSubject: payload.messageSubject,
|
|
29
29
|
emailBody: payload.messageBody,
|
|
30
|
+
emailPreHeader: payload.messagePreHeader,
|
|
30
31
|
}),
|
|
31
32
|
SMS: (payload = {}) => ({
|
|
32
33
|
messageBody: payload.message || '',
|
|
@@ -653,7 +653,6 @@ export function SlideBoxContent(props) {
|
|
|
653
653
|
showTestAndPreviewSlidebox={showTestAndPreviewSlidebox}
|
|
654
654
|
handleTestAndPreview={handleTestAndPreview}
|
|
655
655
|
handleCloseTestAndPreview={handleCloseTestAndPreview}
|
|
656
|
-
handleClose={handleClose}
|
|
657
656
|
onContentValidityChange={onContentValidityChange}
|
|
658
657
|
/>
|
|
659
658
|
}
|
|
@@ -1158,7 +1157,6 @@ export function SlideBoxContent(props) {
|
|
|
1158
1157
|
handleTestAndPreview={handleTestAndPreview}
|
|
1159
1158
|
handleCloseTestAndPreview={handleCloseTestAndPreview}
|
|
1160
1159
|
isTestAndPreviewMode={isTestAndPreviewMode}
|
|
1161
|
-
handleClose={handleClose}
|
|
1162
1160
|
params={{
|
|
1163
1161
|
id: templateData._id,
|
|
1164
1162
|
}}
|
|
@@ -503,6 +503,7 @@ export class Creatives extends React.Component {
|
|
|
503
503
|
case constants.EMAIL: {
|
|
504
504
|
const formData = {
|
|
505
505
|
subject: templateData.emailSubject,
|
|
506
|
+
preheader: templateData.emailPreHeader,
|
|
506
507
|
html_content: templateData.emailBody,
|
|
507
508
|
is_drag_drop: templateData.is_drag_drop,
|
|
508
509
|
lang_id: templateData.lang_id,
|
|
@@ -1092,10 +1093,15 @@ export class Creatives extends React.Component {
|
|
|
1092
1093
|
}
|
|
1093
1094
|
const newHtmlContent = await updateImagesInHtml(html_content);
|
|
1094
1095
|
templateData = {
|
|
1095
|
-
...templateData,
|
|
1096
|
+
...templateData,
|
|
1097
|
+
...emailBase,
|
|
1098
|
+
emailBody: newHtmlContent,
|
|
1099
|
+
emailSubject: emailBase?.subject || '',
|
|
1100
|
+
emailPreHeader: emailBase?.preheader || '',
|
|
1096
1101
|
};
|
|
1097
1102
|
delete templateData.html_content;
|
|
1098
1103
|
delete templateData.subject;
|
|
1104
|
+
delete templateData.preheader;
|
|
1099
1105
|
}
|
|
1100
1106
|
break;
|
|
1101
1107
|
case constants.MOBILE_PUSH:
|
|
@@ -376,4 +376,85 @@ describe('Test SlideBoxContent container', () => {
|
|
|
376
376
|
expect(getCreativesData).toHaveBeenCalledTimes(1);
|
|
377
377
|
expect(getCreativesData).toHaveBeenCalledWith({ channel: 'EMAIL'});
|
|
378
378
|
});
|
|
379
|
+
|
|
380
|
+
it('EMAIL getFormData maps preheader to emailPreHeader for campaigns, alongside subject/body', async () => {
|
|
381
|
+
renderFunction('EMAIL', 'editTemplate', emailTemplates, emailEditTemplateData);
|
|
382
|
+
renderedComponent.instance().onEditTemplate();
|
|
383
|
+
|
|
384
|
+
const mockValue = {
|
|
385
|
+
validity: true,
|
|
386
|
+
value: {
|
|
387
|
+
name: 'email_creative_name',
|
|
388
|
+
type: 'EMAIL',
|
|
389
|
+
base: {
|
|
390
|
+
subject: 'Test subject',
|
|
391
|
+
preheader: 'Test preheader',
|
|
392
|
+
html_content: '<p>hello</p>',
|
|
393
|
+
},
|
|
394
|
+
},
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
renderedComponent.instance().getFormData(mockValue);
|
|
398
|
+
await tick();
|
|
399
|
+
|
|
400
|
+
expect(getCreativesData).toHaveBeenCalledTimes(1);
|
|
401
|
+
const sentData = getCreativesData.mock.calls[0][0];
|
|
402
|
+
expect(sentData.emailSubject).toBe('Test subject');
|
|
403
|
+
expect(sentData.emailPreHeader).toBe('Test preheader');
|
|
404
|
+
expect(sentData.emailBody).toBe('<p>hello</p>');
|
|
405
|
+
expect(sentData.subject).toBeUndefined();
|
|
406
|
+
expect(sentData.preheader).toBeUndefined();
|
|
407
|
+
expect(sentData.html_content).toBeUndefined();
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('EMAIL getFormData maps emailPreHeader to an empty string when not present', async () => {
|
|
411
|
+
renderFunction('EMAIL', 'editTemplate', emailTemplates, emailEditTemplateData);
|
|
412
|
+
renderedComponent.instance().onEditTemplate();
|
|
413
|
+
|
|
414
|
+
const mockValue = {
|
|
415
|
+
validity: true,
|
|
416
|
+
value: {
|
|
417
|
+
name: 'email_creative_name',
|
|
418
|
+
type: 'EMAIL',
|
|
419
|
+
base: {
|
|
420
|
+
subject: 'Test subject',
|
|
421
|
+
html_content: '<p>hello</p>',
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
renderedComponent.instance().getFormData(mockValue);
|
|
427
|
+
await tick();
|
|
428
|
+
|
|
429
|
+
expect(getCreativesData).toHaveBeenCalledTimes(1);
|
|
430
|
+
const sentData = getCreativesData.mock.calls[0][0];
|
|
431
|
+
expect(sentData.emailPreHeader).toBe('');
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it('EMAIL getTemplateData maps emailPreHeader back to preheader when reopening a saved campaign message', () => {
|
|
435
|
+
renderedComponent = shallowWithIntl(
|
|
436
|
+
<Creatives
|
|
437
|
+
loyaltyMetaData={loyaltyMetaData}
|
|
438
|
+
Templates={emailTemplates}
|
|
439
|
+
channel="EMAIL"
|
|
440
|
+
slidBoxContent="editTemplate"
|
|
441
|
+
templateData={{
|
|
442
|
+
channel: 'EMAIL',
|
|
443
|
+
emailSubject: 'Reopened subject',
|
|
444
|
+
emailPreHeader: 'Reopened preheader',
|
|
445
|
+
emailBody: '<p>hello</p>',
|
|
446
|
+
}}
|
|
447
|
+
handleCloseCreatives={handleCloseCreatives}
|
|
448
|
+
getCreativesData={getCreativesData}
|
|
449
|
+
isFullMode={false}
|
|
450
|
+
templateActions={{
|
|
451
|
+
getCdnTransformationConfig,
|
|
452
|
+
}}
|
|
453
|
+
/>,
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
const { templateData } = renderedComponent.instance().state;
|
|
457
|
+
expect(templateData.base.subject).toBe('Reopened subject');
|
|
458
|
+
expect(templateData.base.preheader).toBe('Reopened preheader');
|
|
459
|
+
});
|
|
379
460
|
});
|
|
@@ -119,6 +119,10 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
119
119
|
"template-subject": {
|
|
120
120
|
onTagSelect: this.onTagSelect,
|
|
121
121
|
},
|
|
122
|
+
// unified pre-header field with input and tag selection
|
|
123
|
+
"template-preheader": {
|
|
124
|
+
onTagSelect: this.onTagSelect,
|
|
125
|
+
},
|
|
122
126
|
"template-version": {
|
|
123
127
|
onSelect: this.handleVersionSelect,
|
|
124
128
|
addVersion: this.addVersion,
|
|
@@ -645,6 +649,14 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
645
649
|
formData['template-subject'] = subjectFromEditData;
|
|
646
650
|
}
|
|
647
651
|
}
|
|
652
|
+
|
|
653
|
+
// Ensure template-preheader is preserved at the top level
|
|
654
|
+
if ([undefined, ''].includes(formData['template-preheader'])) {
|
|
655
|
+
const preheaderFromEditData = _.get(nextProps, 'Email.templateDetails.versions.base.preheader', '');
|
|
656
|
+
if (preheaderFromEditData) {
|
|
657
|
+
formData['template-preheader'] = preheaderFromEditData;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
648
660
|
|
|
649
661
|
if (langIndex !== undefined && langIndex !== -1 && temp && temp.panes) {
|
|
650
662
|
_.forEach(temp.panes, (pane, index) => {
|
|
@@ -903,11 +915,11 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
903
915
|
}
|
|
904
916
|
|
|
905
917
|
onTagSelect = (data, currentTab, sourceVal) => {
|
|
906
|
-
// If tag is selected from Subject's TagList
|
|
907
|
-
if (sourceVal && (sourceVal.id === 'title-tagList' || sourceVal.id === 'template-subject')) {
|
|
918
|
+
// If tag is selected from Subject's TagList, unified subject field, or the pre-header field, insert into the relevant input
|
|
919
|
+
if (sourceVal && (sourceVal.id === 'title-tagList' || sourceVal.id === 'template-subject' || sourceVal.id === 'template-preheader')) {
|
|
908
920
|
const tagToInsert = `{{${data}}}`;
|
|
909
921
|
const formData = _.cloneDeep(this.state.formData);
|
|
910
|
-
const fieldId = 'template-subject';
|
|
922
|
+
const fieldId = sourceVal.id === 'template-preheader' ? 'template-preheader' : 'template-subject';
|
|
911
923
|
const input = document.getElementById(fieldId) || document.querySelector(`#${fieldId} input`);
|
|
912
924
|
let subjectValue = formData[fieldId] || '';
|
|
913
925
|
try {
|
|
@@ -1184,6 +1196,8 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
1184
1196
|
formData['template-name'] = editData.name;
|
|
1185
1197
|
const subject = _.get(editData, 'versions.base.subject', '');
|
|
1186
1198
|
formData['template-subject'] = subject;
|
|
1199
|
+
const preheader = _.get(editData, 'versions.base.preheader', '');
|
|
1200
|
+
formData['template-preheader'] = preheader;
|
|
1187
1201
|
formData.base = editData.versions.base;
|
|
1188
1202
|
|
|
1189
1203
|
_.forEach(editData.versions.history, (data1, index) => {
|
|
@@ -1686,6 +1700,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
1686
1700
|
newFormData.base = _.cloneDeep(tmpData);
|
|
1687
1701
|
newFormData.secondary_templates.push({template_data: tmpData});
|
|
1688
1702
|
newFormData.base.subject = formData['template-subject'];
|
|
1703
|
+
newFormData.base.preheader = formData['template-preheader'] || '';
|
|
1689
1704
|
return newFormData;
|
|
1690
1705
|
}
|
|
1691
1706
|
|
|
@@ -2531,7 +2546,14 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
2531
2546
|
newSchema.standalone.sections = [];
|
|
2532
2547
|
if (this.props.location.query.type === 'embedded' && (this.props.location.query.module === "loyalty" || this.props.location.query.module === "dvs" || this.props.location.query.module === "coupon_expiry" || this.props.location.query.module === "timeline" ||
|
|
2533
2548
|
this.props.location.query.module === "library")) {
|
|
2534
|
-
|
|
2549
|
+
// Keep only Subject/Pre-header (matched by field id; template-name stays hidden).
|
|
2550
|
+
const standAloneFieldIdsToKeep = ['template-subject', 'template-preheader'];
|
|
2551
|
+
_.forEach(_.get(schema, 'standalone.sections', []), (section) => {
|
|
2552
|
+
const hasKeepableField = _.some(section.inputFields, (inputField) => _.some(inputField.cols, (col) => standAloneFieldIdsToKeep.includes(col.id)));
|
|
2553
|
+
if (hasKeepableField) {
|
|
2554
|
+
newSchema.standalone.sections.push(_.cloneDeep(section));
|
|
2555
|
+
}
|
|
2556
|
+
});
|
|
2535
2557
|
}
|
|
2536
2558
|
|
|
2537
2559
|
_.forEach(newSchema.containers, (container) => {
|
|
@@ -2590,6 +2612,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
2590
2612
|
if ((this.props.location.query.isLanguageSupport === undefined || this.props.location.query.isLanguageSupport === 'false')) {
|
|
2591
2613
|
// If formData['template-subject'] is empty, assign an empty string; otherwise, retain its current value or use data.base.subject.
|
|
2592
2614
|
formData['template-subject'] = formData?.['template-subject'] === '' ? '' : (data?.base?.subject || '');
|
|
2615
|
+
formData['template-preheader'] = formData?.['template-preheader'] === '' ? '' : (data?.base?.preheader || '');
|
|
2593
2616
|
const baseLanguage = this.props.currentOrgDetails.basic_details.base_language;
|
|
2594
2617
|
let languageIndex = _.findIndex(this.supportedLanguages, {iso_code: baseLanguage});
|
|
2595
2618
|
|
|
@@ -2923,12 +2946,16 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
2923
2946
|
if (index === "template-subject" && obj?.versions?.history?.[0]) {
|
|
2924
2947
|
obj.versions.history[0].subject = newdata;
|
|
2925
2948
|
}
|
|
2949
|
+
if (index === "template-preheader" && obj?.versions?.history?.[0]) {
|
|
2950
|
+
obj.versions.history[0].preheader = newdata;
|
|
2951
|
+
}
|
|
2926
2952
|
});
|
|
2927
2953
|
//const data = formData[`${this.state.currentTab - 1}`];
|
|
2928
2954
|
obj.name = formData['template-name'];
|
|
2929
2955
|
obj.versions.base = formData.base;
|
|
2930
2956
|
obj.versions.base.tabKey = baseKey;
|
|
2931
2957
|
obj.versions.base.subject = formData['template-subject'];
|
|
2958
|
+
obj.versions.base.preheader = formData['template-preheader'] || '';
|
|
2932
2959
|
obj.versions.base.activeTab = formData?.base?.activeTab || baseLanguage;
|
|
2933
2960
|
_.forEach(obj.versions.base.selectedLanguages, (language) => {
|
|
2934
2961
|
if (obj.versions.base[language].is_drag_drop) {
|
|
@@ -42,6 +42,36 @@ export const response = {
|
|
|
42
42
|
},
|
|
43
43
|
],
|
|
44
44
|
},
|
|
45
|
+
{
|
|
46
|
+
type: "multicols",
|
|
47
|
+
inputFields: [
|
|
48
|
+
{
|
|
49
|
+
cols: [
|
|
50
|
+
{
|
|
51
|
+
id: "template-preheader",
|
|
52
|
+
placeholder: "e.g. Get 20% off your next order",
|
|
53
|
+
type: "cap-tag-list-with-input",
|
|
54
|
+
metaType: "text",
|
|
55
|
+
datatype: "string",
|
|
56
|
+
required: false,
|
|
57
|
+
label: "Pre-header",
|
|
58
|
+
styling: "semantic",
|
|
59
|
+
order: 1,
|
|
60
|
+
width: 16,
|
|
61
|
+
customComponent: true,
|
|
62
|
+
standalone: true,
|
|
63
|
+
onlyDisplay: false,
|
|
64
|
+
maxLength: 85,
|
|
65
|
+
errorMessage: "Unsubscribe tag is not allowed in Pre-header.",
|
|
66
|
+
supportedEvents: [
|
|
67
|
+
"onChange",
|
|
68
|
+
"onTagSelect",
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
},
|
|
45
75
|
{
|
|
46
76
|
type: "multicols",
|
|
47
77
|
inputFields: [
|
|
@@ -154,6 +154,14 @@ export default defineMessages({
|
|
|
154
154
|
id: 'creatives.containersV2.Email.emailSubjectNotEmplty',
|
|
155
155
|
defaultMessage: 'Email subject cannot be empty.',
|
|
156
156
|
},
|
|
157
|
+
"Enter Pre-header Text": {
|
|
158
|
+
id: 'creatives.containersV2.Email.emailPreheader',
|
|
159
|
+
defaultMessage: 'Enter pre-header text',
|
|
160
|
+
},
|
|
161
|
+
"Pre-header": {
|
|
162
|
+
id: 'creatives.containersV2.Email.Preheader',
|
|
163
|
+
defaultMessage: 'Pre-header',
|
|
164
|
+
},
|
|
157
165
|
"Enter template name": {
|
|
158
166
|
id: 'creatives.containersV2.Email.templateNamePlaceholder',
|
|
159
167
|
defaultMessage: 'Enter template name',
|
|
@@ -377,6 +377,27 @@ describe('Email container', () => {
|
|
|
377
377
|
const addLangCol = cols.find((c) => c.id === 'add-language-button');
|
|
378
378
|
expect(addLangCol.colStyle.display).toBe('');
|
|
379
379
|
});
|
|
380
|
+
|
|
381
|
+
const getStandaloneFieldIds = (schema) => _.flatMap(
|
|
382
|
+
schema.standalone.sections,
|
|
383
|
+
(section) => _.flatMap(section.inputFields, (inputField) => _.map(inputField.cols, (col) => col.id)),
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
it('keeps both template-subject and template-preheader as standalone fields for embedded/library module', () => {
|
|
387
|
+
const wrapper = renderEmail({ location: { query: { type: 'embedded', module: 'library' } } });
|
|
388
|
+
|
|
389
|
+
const result = wrapper.instance().removeStandAlone(_.cloneDeep(emailDefinition));
|
|
390
|
+
|
|
391
|
+
expect(getStandaloneFieldIds(result)).toEqual(['template-subject', 'template-preheader']);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it('drops template-name from standalone fields for embedded/library module (unchanged prior behavior)', () => {
|
|
395
|
+
const wrapper = renderEmail({ location: { query: { type: 'embedded', module: 'library' } } });
|
|
396
|
+
|
|
397
|
+
const result = wrapper.instance().removeStandAlone(_.cloneDeep(emailDefinition));
|
|
398
|
+
|
|
399
|
+
expect(getStandaloneFieldIds(result)).not.toContain('template-name');
|
|
400
|
+
});
|
|
380
401
|
});
|
|
381
402
|
|
|
382
403
|
describe('handleEdmSave (mode === "switchEditor")', () => {
|
|
@@ -717,4 +738,90 @@ describe('Email container', () => {
|
|
|
717
738
|
expect(wrapper.state('savedTemplate')).toBeNull();
|
|
718
739
|
});
|
|
719
740
|
});
|
|
741
|
+
|
|
742
|
+
describe('pre-header field', () => {
|
|
743
|
+
describe('onTagSelect', () => {
|
|
744
|
+
it('appends the tag to template-preheader when a tag is selected from the pre-header field', () => {
|
|
745
|
+
const wrapper = renderEmail();
|
|
746
|
+
wrapper.setState({ formData: { 'template-preheader': 'Hello ' } });
|
|
747
|
+
|
|
748
|
+
wrapper.instance().onTagSelect('first_name', 1, { id: 'template-preheader' });
|
|
749
|
+
|
|
750
|
+
expect(wrapper.state('formData')['template-preheader']).toBe('Hello {{first_name}}');
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
it('does not touch template-subject when the tag is selected from the pre-header field', () => {
|
|
754
|
+
const wrapper = renderEmail();
|
|
755
|
+
wrapper.setState({ formData: { 'template-preheader': '', 'template-subject': 'Original subject' } });
|
|
756
|
+
|
|
757
|
+
wrapper.instance().onTagSelect('loyalty_points', 1, { id: 'template-preheader' });
|
|
758
|
+
|
|
759
|
+
expect(wrapper.state('formData')['template-subject']).toBe('Original subject');
|
|
760
|
+
expect(wrapper.state('formData')['template-preheader']).toBe('{{loyalty_points}}');
|
|
761
|
+
});
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
describe('setEditData', () => {
|
|
765
|
+
it('populates formData["template-preheader"] from versions.base.preheader', () => {
|
|
766
|
+
const wrapper = renderEmail();
|
|
767
|
+
wrapper.setState({ schema: { containers: [] } });
|
|
768
|
+
const instance = wrapper.instance();
|
|
769
|
+
const editData = {
|
|
770
|
+
name: 'My Template',
|
|
771
|
+
versions: {
|
|
772
|
+
base: {
|
|
773
|
+
subject: 'My subject',
|
|
774
|
+
preheader: 'My preheader text',
|
|
775
|
+
activeTab: 'en',
|
|
776
|
+
selectedLanguages: ['en'],
|
|
777
|
+
en: {},
|
|
778
|
+
},
|
|
779
|
+
history: [],
|
|
780
|
+
},
|
|
781
|
+
};
|
|
782
|
+
|
|
783
|
+
instance.setEditData(editData);
|
|
784
|
+
|
|
785
|
+
expect(wrapper.state('formData')['template-preheader']).toBe('My preheader text');
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
it('defaults to an empty string when versions.base.preheader is absent', () => {
|
|
789
|
+
const wrapper = renderEmail();
|
|
790
|
+
wrapper.setState({ schema: { containers: [] } });
|
|
791
|
+
const instance = wrapper.instance();
|
|
792
|
+
const editData = {
|
|
793
|
+
name: 'My Template',
|
|
794
|
+
versions: {
|
|
795
|
+
base: {
|
|
796
|
+
subject: 'My subject',
|
|
797
|
+
activeTab: 'en',
|
|
798
|
+
selectedLanguages: ['en'],
|
|
799
|
+
en: {},
|
|
800
|
+
},
|
|
801
|
+
history: [],
|
|
802
|
+
},
|
|
803
|
+
};
|
|
804
|
+
|
|
805
|
+
instance.setEditData(editData);
|
|
806
|
+
|
|
807
|
+
expect(wrapper.state('formData')['template-preheader']).toBe('');
|
|
808
|
+
});
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
describe('transformFormData', () => {
|
|
812
|
+
it('carries template-preheader into the returned base.preheader', () => {
|
|
813
|
+
const wrapper = renderEmail();
|
|
814
|
+
wrapper.setState({
|
|
815
|
+
formData: {
|
|
816
|
+
'template-preheader': 'Preview snippet text',
|
|
817
|
+
base: { activeTab: 'en', en: { 'template-content': '<p>hi</p>' } },
|
|
818
|
+
},
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
const result = wrapper.instance().transformFormData(wrapper.state('formData'));
|
|
822
|
+
|
|
823
|
+
expect(result.base.preheader).toBe('Preview snippet text');
|
|
824
|
+
});
|
|
825
|
+
});
|
|
826
|
+
});
|
|
720
827
|
});
|