@capillarytech/creatives-library 8.0.274 → 8.0.276-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/package.json +1 -1
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +5 -2
- package/v2Containers/Email/index.js +40 -6
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +4 -3
- package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +137 -0
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +1 -1
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +2 -4
- package/v2Containers/MobilePush/Create/index.js +1 -1
- package/v2Containers/MobilePush/Edit/index.js +3 -6
package/package.json
CHANGED
|
@@ -8,6 +8,7 @@ import messages from './messages';
|
|
|
8
8
|
import ErrorInfoNote from '../../v2Components/ErrorInfoNote';
|
|
9
9
|
import { PREVIEW } from './constants';
|
|
10
10
|
import { EMAIL_CREATE_MODES } from '../EmailWrapper/constants';
|
|
11
|
+
import { hasSupportCKEditor } from '../../utils/common';
|
|
11
12
|
|
|
12
13
|
function getFullModeSaveBtn(slidBoxContent, isCreatingTemplate) {
|
|
13
14
|
if (isCreatingTemplate) {
|
|
@@ -72,8 +73,10 @@ function SlideBoxFooter(props) {
|
|
|
72
73
|
const isBEEEditor = selectedEmailCreateMode === EMAIL_CREATE_MODES.DRAG_DROP
|
|
73
74
|
|| (emailCreateMode === EMAIL_CREATE_MODES.EDITOR && !isHTMLEditorMode)
|
|
74
75
|
|| (isEditMode && !isHtmlEditorValidationStateActive);
|
|
76
|
+
const isSupportCKEditor = hasSupportCKEditor();
|
|
75
77
|
// Only check validation for HTML Editor mode, not for BEE/DragDrop editor
|
|
76
|
-
|
|
78
|
+
// In upload mode the legacy Email (CK) component does not report validation state, so do not disable buttons
|
|
79
|
+
const shouldCheckValidation = isEmailChannel && htmlEditorValidationState && isHTMLEditorMode && !isBEEEditor && !isSupportCKEditor;
|
|
77
80
|
const isContentEmpty = shouldCheckValidation ? (htmlEditorValidationState?.isContentEmpty ?? true) : false;
|
|
78
81
|
// Check if validation has completed
|
|
79
82
|
const validationComplete = shouldCheckValidation ? (htmlEditorValidationState?.validationComplete ?? false) : true;
|
|
@@ -123,7 +126,7 @@ function SlideBoxFooter(props) {
|
|
|
123
126
|
const isBEEEditorMode = isBEEEditorModeInEdit || isBEEEditorModeInCreate;
|
|
124
127
|
const hasBEEEditorErrors = isEmailChannel && isBEEEditorMode && (hasStandardErrors || hasLiquidErrors) && (!htmlEditorValidationState || !htmlEditorHasErrors);
|
|
125
128
|
|
|
126
|
-
const shouldShowErrorInfoNote = hasBEEEditorErrors;
|
|
129
|
+
const shouldShowErrorInfoNote = hasBEEEditorErrors || isSupportCKEditor;
|
|
127
130
|
return (
|
|
128
131
|
<div className="template-footer-width">
|
|
129
132
|
{shouldShowErrorInfoNote && (
|
|
@@ -235,7 +235,12 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
235
235
|
this.props.actions.getCmsSetting(BEE_PLUGIN, dragDropId, 'create', undefined, isBEESupport, isBEEAppEnable);
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
|
-
|
|
238
|
+
const hasUploadedContent = !this.props.params?.id && this.props.Templates?.selectedEmailLayout && !_.isEmpty(formData);
|
|
239
|
+
this.setState({
|
|
240
|
+
content: (this.props.Templates.selectedEmailLayout ? this.props.Templates.selectedEmailLayout : ''),
|
|
241
|
+
formData,
|
|
242
|
+
...(hasUploadedContent ? { loadingStatus: 2 } : {}),
|
|
243
|
+
});
|
|
239
244
|
|
|
240
245
|
// setTimeout(() => {
|
|
241
246
|
// // this.getFormData();
|
|
@@ -286,6 +291,12 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
286
291
|
}
|
|
287
292
|
}
|
|
288
293
|
}
|
|
294
|
+
|
|
295
|
+
// When SUPPORT_CK_EDITOR: after zip/HTML upload we show with selectedEmailLayout. Ensure spinner stops
|
|
296
|
+
// even if formData was empty on first mount (e.g. currentOrgDetails not yet available).
|
|
297
|
+
if (!this.props.params?.id && this.props.Templates?.selectedEmailLayout) {
|
|
298
|
+
this.setState((prev) => ({ loadingStatus: Math.max(prev.loadingStatus, 2) }));
|
|
299
|
+
}
|
|
289
300
|
}
|
|
290
301
|
|
|
291
302
|
|
|
@@ -313,11 +324,10 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
313
324
|
// }
|
|
314
325
|
const {isFullMode, isGetFormData} = nextProps;
|
|
315
326
|
if (isFullMode && isGetFormData && !_.isEqual(isGetFormData, this.props.isGetFormData) && !this.state.isDragDrop) {
|
|
316
|
-
//
|
|
317
|
-
//
|
|
318
|
-
// Don't start validation if we're in Test & Preview mode
|
|
327
|
+
// Only for CK editor: when Done is clicked in full mode, trigger the same chain as library
|
|
328
|
+
// so that FormBuilder validation and saveFormData run reliably (getFormData -> saveValidationData -> startValidation).
|
|
319
329
|
if (!nextProps.isTestAndPreviewMode) {
|
|
320
|
-
this.
|
|
330
|
+
this.getFormData();
|
|
321
331
|
}
|
|
322
332
|
}
|
|
323
333
|
if (this.state.languageDataSet && nextProps.Templates.selectedEmailLayout && nextProps.Templates.selectedEmailLayout !== '' && !_.isEqual(this.props.Templates.selectedEmailLayout, nextProps.Templates.selectedEmailLayout )) {
|
|
@@ -1561,7 +1571,12 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
1561
1571
|
newFormData[0] = baseData;
|
|
1562
1572
|
newFormData[0].base = true;
|
|
1563
1573
|
}
|
|
1564
|
-
|
|
1574
|
+
// Don't clear selectedEmailLayout when we're in create mode with uploaded content -
|
|
1575
|
+
// EmailWrapper uses it to keep isShowEmailCreate true. Clearing it would unmount Email and show wrong UI.
|
|
1576
|
+
const isCreateWithUpload = !props.params?.id && props.Templates?.selectedEmailLayout;
|
|
1577
|
+
if (!isCreateWithUpload) {
|
|
1578
|
+
this.props.templatesActions.resetUploadData();
|
|
1579
|
+
}
|
|
1565
1580
|
return newFormData;
|
|
1566
1581
|
}
|
|
1567
1582
|
|
|
@@ -2902,6 +2917,25 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
|
|
|
2902
2917
|
if (!isLoading) {
|
|
2903
2918
|
isLoading = this.state.loadingStatus < 2;
|
|
2904
2919
|
}
|
|
2920
|
+
|
|
2921
|
+
// When SUPPORT_CK_EDITOR: after zip/HTML upload we show Email component with selectedEmailLayout.
|
|
2922
|
+
// Don't show spinner once we have uploaded content in formData (create mode, no params.id).
|
|
2923
|
+
if (isLoading && !this.props.params?.id && this.props.Templates?.selectedEmailLayout) {
|
|
2924
|
+
const content = this.state.formData?.[0]?.['template-content']
|
|
2925
|
+
|| _.get(this.state.formData, 'base.template-content')
|
|
2926
|
+
|| (this.state.formData?.base && this.state.formData.base[this.state.formData.base?.activeTab]?.['template-content']);
|
|
2927
|
+
if (content) {
|
|
2928
|
+
isLoading = false;
|
|
2929
|
+
}
|
|
2930
|
+
if (typeof window !== 'undefined' && this.state.loadingStatus < 2) {
|
|
2931
|
+
console.log('[UPLOAD-DEBUG] Email isEmailLoading (upload path)', {
|
|
2932
|
+
loadingStatus: this.state.loadingStatus,
|
|
2933
|
+
hasContentInFormData: !!content,
|
|
2934
|
+
isLoadingResult: isLoading,
|
|
2935
|
+
hasSelectedEmailLayout: !!this.props.Templates?.selectedEmailLayout,
|
|
2936
|
+
});
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2905
2939
|
// if (isLoading) {
|
|
2906
2940
|
// this.props.creativesContainerActions.hideCreativesContanerLoader();
|
|
2907
2941
|
// }
|
|
@@ -198,12 +198,13 @@ const EmailWrapperView = ({
|
|
|
198
198
|
const isEditModeForEditor = hasParamsIdForEditor;
|
|
199
199
|
const isBEEFromProps = emailProps?.editor === 'BEE' && emailProps?.selectedEditorMode === null;
|
|
200
200
|
const isDragDropFromCreateMode = emailCreateMode === EMAIL_CREATE_MODES.DRAG_DROP;
|
|
201
|
-
const isExplicitlyBEEEditor = isBEEFromProps || isDragDropFromCreateMode;
|
|
201
|
+
const isExplicitlyBEEEditor = isBEEFromProps || isDragDropFromCreateMode || (emailProps?.editor === "BEE" && !isFullMode);
|
|
202
202
|
let isHTMLEditorMode = false;
|
|
203
203
|
|
|
204
204
|
if (supportCKEditor) {
|
|
205
|
-
//
|
|
206
|
-
|
|
205
|
+
// UPLOAD mode should use Email component (CKEditor), NOT HTML editor
|
|
206
|
+
// This ensures consistency between create and edit flows when SUPPORT_CK_EDITOR is enabled
|
|
207
|
+
isHTMLEditorMode = false; // Always use Email component (CKEditor) in legacy flow
|
|
207
208
|
} else if (isEditModeForEditor) {
|
|
208
209
|
isHTMLEditorMode = !isExplicitlyBEEEditor;
|
|
209
210
|
} else {
|
|
@@ -517,4 +517,141 @@ describe('EmailWrapperView', () => {
|
|
|
517
517
|
// Should handle gracefully if ref methods don't exist
|
|
518
518
|
});
|
|
519
519
|
});
|
|
520
|
+
|
|
521
|
+
describe('Issue 1: Beefree template should open in Beefree editor in Edit mode', () => {
|
|
522
|
+
beforeEach(() => {
|
|
523
|
+
const { hasSupportCKEditor } = require('../../../../utils/common');
|
|
524
|
+
hasSupportCKEditor.mockReturnValue(false); // New flow
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
it('should render Email component (not HTML editor) when editing Beefree template', () => {
|
|
528
|
+
const emailPropsWithBEE = {
|
|
529
|
+
...defaultProps.emailProps,
|
|
530
|
+
editor: 'BEE',
|
|
531
|
+
selectedEditorMode: null,
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
renderWithIntl({
|
|
535
|
+
step: STEPS.CREATE_TEMPLATE_CONTENT,
|
|
536
|
+
isShowEmailCreate: true,
|
|
537
|
+
emailProps: emailPropsWithBEE,
|
|
538
|
+
emailCreateMode: EMAIL_CREATE_MODES.DRAG_DROP,
|
|
539
|
+
params: { id: 'beefree-template-1' },
|
|
540
|
+
location: { query: {}, pathname: '/email/edit/beefree-template-1' },
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
// Should render Email component (BEE editor), not HTML editor
|
|
544
|
+
expect(screen.queryByTestId('email-html-editor')).not.toBeInTheDocument();
|
|
545
|
+
// Email component would be rendered (we're using EmailWithoutSaga which doesn't have a testid)
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
it('should render HTML editor when editing non-Beefree template', () => {
|
|
549
|
+
const emailPropsWithHTML = {
|
|
550
|
+
...defaultProps.emailProps,
|
|
551
|
+
editor: 'HTML',
|
|
552
|
+
selectedEditorMode: EMAIL_CREATE_MODES.HTML_EDITOR,
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
renderWithIntl({
|
|
556
|
+
step: STEPS.CREATE_TEMPLATE_CONTENT,
|
|
557
|
+
isShowEmailCreate: true,
|
|
558
|
+
emailProps: emailPropsWithHTML,
|
|
559
|
+
emailCreateMode: EMAIL_CREATE_MODES.HTML_EDITOR,
|
|
560
|
+
params: { id: 'html-template-1' },
|
|
561
|
+
location: { query: {}, pathname: '/email/edit/html-template-1' },
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
// Should render HTML editor for non-Beefree template
|
|
565
|
+
expect(screen.getByTestId('email-html-editor')).toBeInTheDocument();
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
it('should render Email component when emailCreateMode is DRAG_DROP in create mode', () => {
|
|
569
|
+
const emailPropsWithBEE = {
|
|
570
|
+
...defaultProps.emailProps,
|
|
571
|
+
editor: 'BEE',
|
|
572
|
+
selectedEditorMode: null,
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
renderWithIntl({
|
|
576
|
+
step: STEPS.CREATE_TEMPLATE_CONTENT,
|
|
577
|
+
isShowEmailCreate: true,
|
|
578
|
+
emailProps: emailPropsWithBEE,
|
|
579
|
+
emailCreateMode: EMAIL_CREATE_MODES.DRAG_DROP,
|
|
580
|
+
params: {},
|
|
581
|
+
location: { query: {}, pathname: '/email/create' },
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
// Should render Email component (BEE editor), not HTML editor
|
|
585
|
+
expect(screen.queryByTestId('email-html-editor')).not.toBeInTheDocument();
|
|
586
|
+
});
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
describe('Issue 2: Upload Zip should open in CKEditor when SUPPORT_CK_EDITOR is enabled', () => {
|
|
590
|
+
beforeEach(() => {
|
|
591
|
+
const { hasSupportCKEditor } = require('../../../../utils/common');
|
|
592
|
+
hasSupportCKEditor.mockReturnValue(true); // Legacy flow
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
it('should render Email component (CKEditor) for UPLOAD mode in create flow', () => {
|
|
596
|
+
const emailPropsForCKEditor = {
|
|
597
|
+
...defaultProps.emailProps,
|
|
598
|
+
editor: undefined, // Default CKEditor
|
|
599
|
+
selectedEditorMode: null,
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
renderWithIntl({
|
|
603
|
+
step: STEPS.CREATE_TEMPLATE_CONTENT,
|
|
604
|
+
isShowEmailCreate: true,
|
|
605
|
+
emailProps: emailPropsForCKEditor,
|
|
606
|
+
emailCreateMode: EMAIL_CREATE_MODES.UPLOAD,
|
|
607
|
+
EmailLayout: { html: '<p>Uploaded content</p>' },
|
|
608
|
+
params: {},
|
|
609
|
+
location: { query: {}, pathname: '/email/create' },
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
// Should render Email component (CKEditor), NOT HTML editor
|
|
613
|
+
expect(screen.queryByTestId('email-html-editor')).not.toBeInTheDocument();
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
it('should render Email component (CKEditor) for UPLOAD mode in edit flow', () => {
|
|
617
|
+
const emailPropsForCKEditor = {
|
|
618
|
+
...defaultProps.emailProps,
|
|
619
|
+
editor: undefined,
|
|
620
|
+
selectedEditorMode: null,
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
renderWithIntl({
|
|
624
|
+
step: STEPS.CREATE_TEMPLATE_CONTENT,
|
|
625
|
+
isShowEmailCreate: true,
|
|
626
|
+
emailProps: emailPropsForCKEditor,
|
|
627
|
+
emailCreateMode: EMAIL_CREATE_MODES.UPLOAD,
|
|
628
|
+
EmailLayout: { html: '<p>Edited uploaded content</p>' },
|
|
629
|
+
params: { id: 'uploaded-template-1' },
|
|
630
|
+
location: { query: {}, pathname: '/email/edit/uploaded-template-1' },
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
// Should render Email component (CKEditor), NOT HTML editor
|
|
634
|
+
expect(screen.queryByTestId('email-html-editor')).not.toBeInTheDocument();
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
it('should NOT render HTML editor for EDITOR mode when SUPPORT_CK_EDITOR is enabled', () => {
|
|
638
|
+
const emailPropsForCKEditor = {
|
|
639
|
+
...defaultProps.emailProps,
|
|
640
|
+
editor: undefined,
|
|
641
|
+
selectedEditorMode: null,
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
renderWithIntl({
|
|
645
|
+
step: STEPS.CREATE_TEMPLATE_CONTENT,
|
|
646
|
+
isShowEmailCreate: true,
|
|
647
|
+
emailProps: emailPropsForCKEditor,
|
|
648
|
+
emailCreateMode: EMAIL_CREATE_MODES.EDITOR,
|
|
649
|
+
params: {},
|
|
650
|
+
location: { query: {}, pathname: '/email/create' },
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
// Should render Email component (CKEditor), NOT HTML editor
|
|
654
|
+
expect(screen.queryByTestId('email-html-editor')).not.toBeInTheDocument();
|
|
655
|
+
});
|
|
656
|
+
});
|
|
520
657
|
});
|
|
@@ -643,7 +643,7 @@ const useEmailWrapper = ({
|
|
|
643
643
|
// If template was created in BEE AND BEE is enabled → open in BEE editor
|
|
644
644
|
// Otherwise → open in HTML editor (fallback)
|
|
645
645
|
// IMPORTANT: When supportCKEditor is false, default to HTML editor unless explicitly BEE
|
|
646
|
-
if (isDragDrop && isBeeEnabled) {
|
|
646
|
+
if ((isDragDrop && isBeeEnabled) || (!isFullMode && isBeeEnabled)) {
|
|
647
647
|
editorType = 'BEE';
|
|
648
648
|
selectedEditorMode = null; // BEE uses existing flow
|
|
649
649
|
} else {
|
|
@@ -732,8 +732,7 @@ describe('useEmailWrapper', () => {
|
|
|
732
732
|
|
|
733
733
|
await waitFor(() => {
|
|
734
734
|
const emailProps = result.current.emailProps;
|
|
735
|
-
expect(emailProps.editor).toBe('
|
|
736
|
-
expect(emailProps.selectedEditorMode).toBe(EMAIL_CREATE_MODES.HTML_EDITOR);
|
|
735
|
+
expect(emailProps.editor).toBe('BEE');
|
|
737
736
|
});
|
|
738
737
|
});
|
|
739
738
|
|
|
@@ -762,8 +761,7 @@ describe('useEmailWrapper', () => {
|
|
|
762
761
|
|
|
763
762
|
await waitFor(() => {
|
|
764
763
|
const emailProps = result.current.emailProps;
|
|
765
|
-
expect(emailProps.editor).toBe('
|
|
766
|
-
expect(emailProps.selectedEditorMode).toBe(EMAIL_CREATE_MODES.HTML_EDITOR);
|
|
764
|
+
expect(emailProps.editor).toBe('BEE');
|
|
767
765
|
});
|
|
768
766
|
});
|
|
769
767
|
});
|
|
@@ -114,7 +114,7 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
|
|
|
114
114
|
if (nextProps.iosCtasData && isEmpty(this.state.templateCta)) {
|
|
115
115
|
this.setState({showIosCtaTable: true});
|
|
116
116
|
}
|
|
117
|
-
if (nextProps.metaEntities && nextProps.metaEntities.layouts && nextProps.metaEntities.layouts.length > 0 && isEmpty(this.state.schema)
|
|
117
|
+
if (nextProps.metaEntities && nextProps.metaEntities.layouts && nextProps.metaEntities.layouts.length > 0 && isEmpty(this.state.schema)) {
|
|
118
118
|
const schema = this.props.params.mode === "text" ? nextProps.metaEntities.layouts[0].definition.textSchema : nextProps.metaEntities.layouts[0].definition.imageSchema;
|
|
119
119
|
const isAndroidSupported = get(this, "props.Templates.selectedWeChatAccount.configs.android") === '1';
|
|
120
120
|
const isIosSupported = get(this, "props.Templates.selectedWeChatAccount.configs.ios") === '1';
|
|
@@ -168,7 +168,7 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
|
|
|
168
168
|
this.props.actions.getMobilepushTemplatesList('mobilepush', params);
|
|
169
169
|
}
|
|
170
170
|
if (nextProps.metaEntities && nextProps.metaEntities.layouts && nextProps.metaEntities.layouts.length > 0 && _.isEmpty(this.state.fullSchema) && _.isEmpty(this.state.formData)) {
|
|
171
|
-
this.setState({
|
|
171
|
+
this.setState({fullSchema: nextProps.metaEntities.layouts[0].definition, schema: (nextProps.location.query.module === 'loyalty') ? nextProps.metaEntities.layouts[0].definition.textSchema : {}}, () => {
|
|
172
172
|
this.handleEditSchemaOnPropsChange(nextProps, selectedWeChatAccount);
|
|
173
173
|
const templateId = get(this, "props.params.id");
|
|
174
174
|
if (nextProps.location.query.module !== 'loyalty' && templateId && templateId !== 'temp') {
|
|
@@ -388,11 +388,11 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
|
|
|
388
388
|
}
|
|
389
389
|
});
|
|
390
390
|
} else {
|
|
391
|
-
selectedAccount = accounts?.[0];
|
|
391
|
+
selectedAccount = accounts?.length > 0 ? accounts[0] : {};
|
|
392
392
|
formData['mobilepush-accounts'] = selectedAccount?.id;
|
|
393
393
|
}
|
|
394
394
|
this.props.actions.setWeChatAccount(selectedAccount);
|
|
395
|
-
this.setState({
|
|
395
|
+
this.setState({formData, accountsOptions: accounts?.length > 0 ? accounts.map((acc) => ({key: acc.id, label: acc.name, value: acc.id})) : []});
|
|
396
396
|
};
|
|
397
397
|
|
|
398
398
|
createDefinition = (account) => ({
|
|
@@ -2018,9 +2018,6 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
|
|
|
2018
2018
|
if (this.props.supportedTags) {
|
|
2019
2019
|
tags = this.props.supportedTags;
|
|
2020
2020
|
}
|
|
2021
|
-
if (this.props.supportedTags) {
|
|
2022
|
-
tags = this.props.supportedTags;
|
|
2023
|
-
}
|
|
2024
2021
|
return (
|
|
2025
2022
|
<div className="creatives-mobilepush-edit mobilepush-wrapper">
|
|
2026
2023
|
<CapSpin spinning={spinning}>
|