@capillarytech/creatives-library 8.0.213 → 8.0.214-beta.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/HOW_BEE_EDITOR_WORKS.md +375 -0
- package/constants/unified.js +1 -0
- package/package.json +1 -1
- package/services/api.js +5 -0
- package/utils/common.js +6 -1
- package/v2Components/CapTagList/index.js +2 -1
- package/v2Components/CapTagListWithInput/index.js +5 -1
- package/v2Components/CapTagListWithInput/messages.js +1 -1
- package/v2Components/ErrorInfoNote/style.scss +1 -1
- package/v2Components/HtmlEditor/HTMLEditor.js +86 -14
- package/v2Components/HtmlEditor/_htmlEditor.scss +4 -4
- package/v2Components/HtmlEditor/_index.lazy.scss +1 -1
- package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +107 -96
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +68 -92
- package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
- package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
- package/v2Containers/CreativesContainer/SlideBoxContent.js +85 -35
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +9 -3
- package/v2Containers/CreativesContainer/index.js +107 -35
- package/v2Containers/CreativesContainer/messages.js +4 -0
- package/v2Containers/Email/actions.js +7 -0
- package/v2Containers/Email/constants.js +5 -1
- package/v2Containers/Email/index.js +13 -0
- package/v2Containers/Email/messages.js +32 -0
- package/v2Containers/Email/reducer.js +12 -1
- package/v2Containers/Email/sagas.js +17 -0
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1005 -0
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +193 -7
- package/v2Containers/EmailWrapper/constants.js +2 -0
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +470 -71
- package/v2Containers/EmailWrapper/index.js +102 -23
- package/v2Containers/EmailWrapper/messages.js +61 -1
- package/v2Containers/EmailWrapper/tests/EmailHTMLEditor.test.js +177 -0
- package/v2Containers/EmailWrapper/tests/EmailHTMLEditorValidation.test.js +90 -0
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +49 -49
- package/v2Containers/TagList/index.js +2 -0
- package/v2Containers/Templates/index.js +5 -0
|
@@ -55,11 +55,11 @@ describe('useEmailWrapper', () => {
|
|
|
55
55
|
mockStopTimer.mockClear();
|
|
56
56
|
|
|
57
57
|
isEmpty.mockImplementation(val => {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
if (val === null || val === undefined) return true;
|
|
59
|
+
if (typeof val === 'object') return Object.keys(val).length === 0;
|
|
60
|
+
if (typeof val === 'string') return val.trim().length === 0;
|
|
61
|
+
return !val;
|
|
62
|
+
});
|
|
63
63
|
|
|
64
64
|
mockProps = {
|
|
65
65
|
...EmailWrapperMockData,
|
|
@@ -224,7 +224,7 @@ describe('useEmailWrapper', () => {
|
|
|
224
224
|
const mockReader = {
|
|
225
225
|
onload: null,
|
|
226
226
|
onerror: null,
|
|
227
|
-
readAsText: jest.fn(function() {
|
|
227
|
+
readAsText: jest.fn(function () {
|
|
228
228
|
this.result = mockHtmlContent;
|
|
229
229
|
if (this.onload) {
|
|
230
230
|
this.onload();
|
|
@@ -294,7 +294,7 @@ describe('useEmailWrapper', () => {
|
|
|
294
294
|
});
|
|
295
295
|
|
|
296
296
|
expect(mockCapNotificationError).toHaveBeenCalledWith(expect.objectContaining({
|
|
297
|
-
|
|
297
|
+
key: "email-upload-error",
|
|
298
298
|
}));
|
|
299
299
|
expect(mockProps.templatesActions.handleZipUpload).not.toHaveBeenCalled();
|
|
300
300
|
});
|
|
@@ -309,14 +309,14 @@ describe('useEmailWrapper', () => {
|
|
|
309
309
|
result.current.useFileUpload({ file: null });
|
|
310
310
|
});
|
|
311
311
|
expect(mockCapNotificationError).toHaveBeenCalledTimes(1);
|
|
312
|
-
expect(mockCapNotificationError).toHaveBeenCalledWith(expect.objectContaining({ key: "email-upload-error"}));
|
|
312
|
+
expect(mockCapNotificationError).toHaveBeenCalledWith(expect.objectContaining({ key: "email-upload-error" }));
|
|
313
313
|
|
|
314
314
|
mockCapNotificationError.mockClear();
|
|
315
315
|
reactAct(() => {
|
|
316
|
-
|
|
316
|
+
result.current.useFileUpload({ file: { name: 'test.zip', size: 100 } });
|
|
317
317
|
});
|
|
318
318
|
expect(mockCapNotificationError).toHaveBeenCalledTimes(1);
|
|
319
|
-
expect(mockCapNotificationError).toHaveBeenCalledWith(expect.objectContaining({ key: "email-upload-error"}));
|
|
319
|
+
expect(mockCapNotificationError).toHaveBeenCalledWith(expect.objectContaining({ key: "email-upload-error" }));
|
|
320
320
|
|
|
321
321
|
expect(mockProps.templatesActions.handleZipUpload).not.toHaveBeenCalled();
|
|
322
322
|
expect(mockProps.templatesActions.handleHtmlUpload).not.toHaveBeenCalled();
|
|
@@ -397,7 +397,7 @@ describe('useEmailWrapper', () => {
|
|
|
397
397
|
// selectedCreateMode is initially '' from hook's useState
|
|
398
398
|
};
|
|
399
399
|
const { result, rerender } = renderHook((props) => useEmailWrapper(props), {
|
|
400
|
-
|
|
400
|
+
initialProps: initialProps
|
|
401
401
|
});
|
|
402
402
|
|
|
403
403
|
// 4. Simulate user selecting a template ID via the exposed callback (useEditor)
|
|
@@ -448,11 +448,11 @@ describe('useEmailWrapper', () => {
|
|
|
448
448
|
|
|
449
449
|
it('sets selectedCreateMode in useEffect when in UPLOAD mode and CREATE_TEMPLATE_CONTENT step with EmailLayout', async () => {
|
|
450
450
|
const initialRenderProps = {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
451
|
+
...mockProps,
|
|
452
|
+
step: STEPS.MODE_SELECTION,
|
|
453
|
+
emailCreateMode: EMAIL_CREATE_MODES.UPLOAD,
|
|
454
|
+
EmailLayout: null,
|
|
455
|
+
};
|
|
456
456
|
|
|
457
457
|
const { result, rerender } = renderHook((props) => useEmailWrapper(props), {
|
|
458
458
|
initialProps: initialRenderProps
|
|
@@ -467,11 +467,11 @@ describe('useEmailWrapper', () => {
|
|
|
467
467
|
|
|
468
468
|
// Reset isEmpty to default before setting the specific mock for the effect
|
|
469
469
|
isEmpty.mockImplementation((val) => {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
470
|
+
if (val === null || val === undefined) return true;
|
|
471
|
+
if (typeof val === 'object') return Object.keys(val).length === 0;
|
|
472
|
+
if (typeof val === 'string') return val.trim().length === 0;
|
|
473
|
+
return !val;
|
|
474
|
+
});
|
|
475
475
|
// Mock isEmpty specifically for the EmailLayout check inside the useEffect
|
|
476
476
|
isEmpty.mockImplementationOnce(() => false); // Mock !isEmpty(EmailLayout) to be true
|
|
477
477
|
|
|
@@ -479,16 +479,16 @@ describe('useEmailWrapper', () => {
|
|
|
479
479
|
|
|
480
480
|
// Wait for the effect to run, set selectedCreateMode, and isShowEmailCreate to update
|
|
481
481
|
await waitFor(() => {
|
|
482
|
-
|
|
482
|
+
expect(result.current.isShowEmailCreate).toBe(true);
|
|
483
483
|
});
|
|
484
484
|
|
|
485
485
|
// Restore default isEmpty behavior after the specific mock is used
|
|
486
486
|
isEmpty.mockImplementation((val) => {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
487
|
+
if (val === null || val === undefined) return true;
|
|
488
|
+
if (typeof val === 'object') return Object.keys(val).length === 0;
|
|
489
|
+
if (typeof val === 'string') return val.trim().length === 0;
|
|
490
|
+
return !val;
|
|
491
|
+
});
|
|
492
492
|
isEmpty.mockClear(); // Clear any remaining mock state if needed
|
|
493
493
|
|
|
494
494
|
expect(mockProps.templatesActions.setEdmTemplate).not.toHaveBeenCalled();
|
|
@@ -533,11 +533,11 @@ describe('useEmailWrapper', () => {
|
|
|
533
533
|
|
|
534
534
|
// Reset isEmpty to default before setting the specific mock for the effect
|
|
535
535
|
isEmpty.mockImplementation((val) => {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
536
|
+
if (val === null || val === undefined) return true;
|
|
537
|
+
if (typeof val === 'object') return Object.keys(val).length === 0;
|
|
538
|
+
if (typeof val === 'string') return val.trim().length === 0;
|
|
539
|
+
return !val;
|
|
540
|
+
});
|
|
541
541
|
// Mock isEmpty specifically for the EmailLayout check inside the useEffect
|
|
542
542
|
isEmpty.mockImplementationOnce(() => false); // Mock !isEmpty(EmailLayout) to be true
|
|
543
543
|
|
|
@@ -545,16 +545,16 @@ describe('useEmailWrapper', () => {
|
|
|
545
545
|
|
|
546
546
|
// Wait for the effect to run, set selectedCreateMode, and isShowEmailCreate to update
|
|
547
547
|
await waitFor(() => {
|
|
548
|
-
|
|
548
|
+
expect(result.current.isShowEmailCreate).toBe(true);
|
|
549
549
|
});
|
|
550
550
|
|
|
551
551
|
// Restore default isEmpty behavior after the specific mock is used
|
|
552
552
|
isEmpty.mockImplementation((val) => {
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
553
|
+
if (val === null || val === undefined) return true;
|
|
554
|
+
if (typeof val === 'object') return Object.keys(val).length === 0;
|
|
555
|
+
if (typeof val === 'string') return val.trim().length === 0;
|
|
556
|
+
return !val;
|
|
557
|
+
});
|
|
558
558
|
isEmpty.mockClear(); // Clear any remaining mock state if needed
|
|
559
559
|
|
|
560
560
|
// --- Case 3: EDITOR mode selected, Template becomes available -> true (via effect) ---
|
|
@@ -563,11 +563,11 @@ describe('useEmailWrapper', () => {
|
|
|
563
563
|
find.mockReturnValue(mockTemplate); // Ensure find is mocked for this case
|
|
564
564
|
|
|
565
565
|
const editorPropsInitial = {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
566
|
+
...mockProps,
|
|
567
|
+
step: STEPS.TEMPLATE_SELECTION,
|
|
568
|
+
emailCreateMode: EMAIL_CREATE_MODES.EDITOR,
|
|
569
|
+
CmsTemplates: [mockTemplate],
|
|
570
|
+
SelectedEdmDefaultTemplate: null,
|
|
571
571
|
};
|
|
572
572
|
rerender(editorPropsInitial);
|
|
573
573
|
|
|
@@ -579,12 +579,12 @@ describe('useEmailWrapper', () => {
|
|
|
579
579
|
expect(result.current.modeContent).toEqual({ id: templateId });
|
|
580
580
|
|
|
581
581
|
const editorPropsFinal = {
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
582
|
+
...mockProps, // Use fresh props base
|
|
583
|
+
step: STEPS.CREATE_TEMPLATE_CONTENT, // Move to the create step
|
|
584
|
+
emailCreateMode: EMAIL_CREATE_MODES.EDITOR,
|
|
585
|
+
CmsTemplates: [mockTemplate],
|
|
586
|
+
SelectedEdmDefaultTemplate: null, // Template not yet selected in props
|
|
587
|
+
// modeContent state ({id: templateId}) should persist internally from the reactAct call
|
|
588
588
|
};
|
|
589
589
|
|
|
590
590
|
// Mock isEmpty specifically for the check within the useEffect triggered by rerender
|
|
@@ -372,6 +372,7 @@ export class TagList extends React.Component { // eslint-disable-line react/pref
|
|
|
372
372
|
channel={this.props.channel}
|
|
373
373
|
disabled={this.props.disabled}
|
|
374
374
|
fetchingSchemaError={this?.state?.tagsError}
|
|
375
|
+
popoverPlacement={this.props.popoverPlacement}
|
|
375
376
|
/>
|
|
376
377
|
</div>
|
|
377
378
|
);
|
|
@@ -402,6 +403,7 @@ TagList.propTypes = {
|
|
|
402
403
|
disabled: PropTypes.bool,
|
|
403
404
|
fetchingSchemaError: PropTypes.bool,
|
|
404
405
|
eventContextTags: PropTypes.array,
|
|
406
|
+
popoverPlacement: PropTypes.string,
|
|
405
407
|
intl: PropTypes.shape({
|
|
406
408
|
formatMessage: PropTypes.func.isRequired,
|
|
407
409
|
locale: PropTypes.string,
|
|
@@ -931,6 +931,11 @@ export class Templates extends React.Component { // eslint-disable-line react/pr
|
|
|
931
931
|
const zaloSelectedTemplateData = this.selectTemplate(parseInt(creativesParams._id, 10)) || {};
|
|
932
932
|
const { name = '' } = zaloSelectedTemplateData;
|
|
933
933
|
creativesParams.name = name
|
|
934
|
+
} else if (this.state.channel?.toLowerCase() === EMAIL_LOWERCASE) {
|
|
935
|
+
const emailSelectedTemplateData = this.selectTemplate(creativesParams._id) || {};
|
|
936
|
+
const activeTab = get(emailSelectedTemplateData, 'versions.base.activeTab', 'en');
|
|
937
|
+
const isDragDrop = get(emailSelectedTemplateData, `versions.base.${activeTab}.is_drag_drop`, false);
|
|
938
|
+
creativesParams.is_drag_drop = isDragDrop;
|
|
934
939
|
}
|
|
935
940
|
}
|
|
936
941
|
creativesParams.type = this.state.channel.toUpperCase();
|