@capillarytech/creatives-library 9.0.69 → 9.0.70-alpha.sprint-cap-modalintact.12
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/FormBuilder/Classic.js +3 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +37 -29
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +6 -2
- package/v2Containers/CreativesContainer/index.js +26 -1
- package/v2Containers/CreativesContainer/messages.js +4 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +40 -12
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +10 -0
- package/v2Containers/CreativesContainer/tests/switchToEditAfterCreate.test.js +152 -0
- package/v2Containers/Email/index.js +41 -6
- package/v2Containers/Email/tests/index.test.js +123 -0
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +32 -12
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +34 -0
- package/v2Containers/InApp/index.js +19 -23
- package/v2Containers/InApp/tests/index.test.js +52 -9
- package/v2Containers/InappAdvance/index.js +19 -24
- package/v2Containers/InappAdvance/tests/index.test.js +39 -15
- package/v2Containers/Line/Container/index.js +18 -8
- package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +129 -108
- package/v2Containers/Line/Container/tests/index.test.js +68 -0
- package/v2Containers/MobilePush/Create/index.js +24 -5
- package/v2Containers/MobilePush/Create/test/stayOpenOnSave.test.js +80 -0
- package/v2Containers/MobilePush/Edit/index.js +15 -9
- package/v2Containers/MobilePush/Edit/test/stayOpenOnSave.test.js +60 -0
- package/v2Containers/MobilePushNew/index.js +41 -7
- package/v2Containers/MobilePushNew/tests/index.test.js +61 -0
- package/v2Containers/Sms/Create/index.js +31 -13
- package/v2Containers/Sms/Create/test/stayOpenOnSave.test.js +92 -0
- package/v2Containers/Sms/Edit/index.js +20 -12
- package/v2Containers/Sms/Edit/tests/index.test.js +66 -0
- package/v2Containers/Templates/index.js +17 -4
- package/v2Containers/Viber/index.js +15 -6
- package/v2Containers/Viber/tests/index.test.js +97 -28
- package/v2Containers/WebPush/Create/components/FormActions.js +1 -1
- package/v2Containers/WebPush/Create/components/FormActions.test.js +3 -3
- package/v2Containers/WebPush/Create/index.js +32 -6
- package/v2Containers/WebPush/Create/messages.js +0 -4
- package/v2Containers/WebPush/Create/tests/stayOpenOnSave.test.js +268 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useState, useEffect, useRef } from "react";
|
|
2
2
|
import isEmpty from 'lodash/isEmpty';
|
|
3
3
|
import get from 'lodash/get';
|
|
4
|
+
import isPlainObject from 'lodash/isPlainObject';
|
|
4
5
|
import { bindActionCreators } from "redux";
|
|
5
6
|
import { createStructuredSelector } from "reselect";
|
|
6
7
|
import { injectIntl, FormattedMessage } from "react-intl";
|
|
@@ -62,7 +63,6 @@ export const InappAdvanced = (props) => {
|
|
|
62
63
|
intl,
|
|
63
64
|
actions,
|
|
64
65
|
isFullMode,
|
|
65
|
-
onCreateComplete,
|
|
66
66
|
params,
|
|
67
67
|
templateData = {},
|
|
68
68
|
editData = {},
|
|
@@ -102,6 +102,9 @@ export const InappAdvanced = (props) => {
|
|
|
102
102
|
const [tags, updateTags] = useState([]);
|
|
103
103
|
//for edit only
|
|
104
104
|
const [isEditFlow, setEditFlow] = useState(false);
|
|
105
|
+
// Full mode: id of the template created in this session — the form stays as is and later saves update it
|
|
106
|
+
const [savedTemplateId, setSavedTemplateId] = useState('');
|
|
107
|
+
const isUpdate = isEditFlow || !!savedTemplateId;
|
|
105
108
|
const [errorMessage, setErrorMessage] = useState({
|
|
106
109
|
STANDARD_ERROR_MSG: {
|
|
107
110
|
ANDROID: [],
|
|
@@ -545,7 +548,7 @@ export const InappAdvanced = (props) => {
|
|
|
545
548
|
const actionCallback = ({ errorMessage: errorMsg }) => {
|
|
546
549
|
if (!errorMsg) {
|
|
547
550
|
CapNotification.success({
|
|
548
|
-
message:
|
|
551
|
+
message: isUpdate ? formatMessage(messages.inAppEditNotification, {
|
|
549
552
|
name: templateName,
|
|
550
553
|
}) : formatMessage(messages.inAppCreateNotification, {
|
|
551
554
|
name: templateName,
|
|
@@ -648,10 +651,15 @@ export const InappAdvanced = (props) => {
|
|
|
648
651
|
const payload = createPayload(latestHtmlValues);
|
|
649
652
|
actions.createInAppTemplate(payload, (resp, errorMsg) => {
|
|
650
653
|
actionCallback({ resp, errorMessage: errorMsg });
|
|
654
|
+
setSpin(false);
|
|
651
655
|
if (!errorMsg) {
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
656
|
+
// Full mode stays open and continues in place as an edit.
|
|
657
|
+
const savedResponse = resp?.templateId;
|
|
658
|
+
const newTemplateId = (isPlainObject(savedResponse) ? savedResponse._id : savedResponse) || '';
|
|
659
|
+
setSavedTemplateId(newTemplateId);
|
|
660
|
+
getFormData({
|
|
661
|
+
_id: newTemplateId, validity: true, keepSlideBoxOpen: true, continuedAsEdit: true,
|
|
662
|
+
});
|
|
655
663
|
}
|
|
656
664
|
});
|
|
657
665
|
};
|
|
@@ -664,14 +672,13 @@ export const InappAdvanced = (props) => {
|
|
|
664
672
|
actions.editTemplate(
|
|
665
673
|
{
|
|
666
674
|
...payload,
|
|
667
|
-
_id: params?.id,
|
|
675
|
+
_id: params?.id || savedTemplateId,
|
|
668
676
|
},
|
|
669
677
|
(resp, errorMsg) => {
|
|
670
678
|
actionCallback({ resp, errorMessage: errorMsg });
|
|
679
|
+
setSpin(false);
|
|
671
680
|
if (!errorMsg) {
|
|
672
|
-
|
|
673
|
-
} else {
|
|
674
|
-
setSpin(false);
|
|
681
|
+
getFormData({ validity: true, keepSlideBoxOpen: true }); // stays open; host refreshes its list
|
|
675
682
|
}
|
|
676
683
|
},
|
|
677
684
|
);
|
|
@@ -846,7 +853,7 @@ export const InappAdvanced = (props) => {
|
|
|
846
853
|
|
|
847
854
|
const onDoneCallback = async () => {
|
|
848
855
|
if (isFullMode) {
|
|
849
|
-
if (
|
|
856
|
+
if (isUpdate) {
|
|
850
857
|
await onEditInApp();
|
|
851
858
|
return;
|
|
852
859
|
}
|
|
@@ -950,20 +957,8 @@ export const InappAdvanced = (props) => {
|
|
|
950
957
|
disabled={isDisableDone()}
|
|
951
958
|
className="inapp-create-btn"
|
|
952
959
|
>
|
|
953
|
-
{
|
|
954
|
-
|
|
955
|
-
return isFullMode ? (
|
|
956
|
-
<FormattedMessage {...messages.update} />
|
|
957
|
-
) : (
|
|
958
|
-
<FormattedMessage {...globalMessages.done} />
|
|
959
|
-
);
|
|
960
|
-
}
|
|
961
|
-
return isFullMode ? (
|
|
962
|
-
<FormattedMessage {...messages.create} />
|
|
963
|
-
) : (
|
|
964
|
-
<FormattedMessage {...globalMessages.done} />
|
|
965
|
-
);
|
|
966
|
-
})()}
|
|
960
|
+
{/* Full mode saves without closing, so the button reads "Save" for create and edit */}
|
|
961
|
+
<FormattedMessage {...(isFullMode ? globalMessages.save : globalMessages.done)} />
|
|
967
962
|
</CapButton>
|
|
968
963
|
</>
|
|
969
964
|
}
|
|
@@ -6,7 +6,7 @@ import { configureStore } from '@capillarytech/vulcan-react-sdk/utils';
|
|
|
6
6
|
import history from '../../../utils/history';
|
|
7
7
|
import { initialReducer } from '../../../initialReducer';
|
|
8
8
|
import { InappAdvanced } from '../index';
|
|
9
|
-
import { render, screen, fireEvent, waitFor } from '../../../utils/test-utils';
|
|
9
|
+
import { render, screen, fireEvent, waitFor, act } from '../../../utils/test-utils';
|
|
10
10
|
|
|
11
11
|
// Mock child components that are complex or not under test
|
|
12
12
|
jest.mock('../../BeePopupEditor', () => ({
|
|
@@ -113,7 +113,7 @@ describe('InappAdvanced Component', () => {
|
|
|
113
113
|
it('should render without crashing', () => {
|
|
114
114
|
renderComponent(defaultProps);
|
|
115
115
|
|
|
116
|
-
expect(screen.getByText('
|
|
116
|
+
expect(screen.getByText('Save')).toBeInTheDocument();
|
|
117
117
|
});
|
|
118
118
|
|
|
119
119
|
it('should call getBeePopupBuilderToken on mount', () => {
|
|
@@ -129,7 +129,7 @@ describe('InappAdvanced Component', () => {
|
|
|
129
129
|
});
|
|
130
130
|
|
|
131
131
|
// The spinner should be present but we can't easily test for it without mocking
|
|
132
|
-
expect(screen.getByText('
|
|
132
|
+
expect(screen.getByText('Save')).toBeInTheDocument();
|
|
133
133
|
});
|
|
134
134
|
});
|
|
135
135
|
|
|
@@ -238,16 +238,16 @@ describe('InappAdvanced Component', () => {
|
|
|
238
238
|
});
|
|
239
239
|
|
|
240
240
|
describe('Action Buttons', () => {
|
|
241
|
-
it('should render
|
|
241
|
+
it('should render Save button in full mode for new templates', () => {
|
|
242
242
|
renderComponent({
|
|
243
243
|
...defaultProps,
|
|
244
244
|
isFullMode: true,
|
|
245
245
|
});
|
|
246
246
|
|
|
247
|
-
expect(screen.getByText('
|
|
247
|
+
expect(screen.getByText('Save')).toBeInTheDocument();
|
|
248
248
|
});
|
|
249
249
|
|
|
250
|
-
it('should render
|
|
250
|
+
it('should render Save button in edit mode', () => {
|
|
251
251
|
renderComponent({
|
|
252
252
|
...defaultProps,
|
|
253
253
|
isFullMode: true,
|
|
@@ -265,7 +265,7 @@ describe('InappAdvanced Component', () => {
|
|
|
265
265
|
},
|
|
266
266
|
});
|
|
267
267
|
|
|
268
|
-
expect(screen.getByText('
|
|
268
|
+
expect(screen.getByText('Save')).toBeInTheDocument();
|
|
269
269
|
});
|
|
270
270
|
|
|
271
271
|
it('should render Done button in library mode', () => {
|
|
@@ -277,13 +277,13 @@ describe('InappAdvanced Component', () => {
|
|
|
277
277
|
expect(screen.getByText('Done')).toBeInTheDocument();
|
|
278
278
|
});
|
|
279
279
|
|
|
280
|
-
it('should call appropriate action when
|
|
280
|
+
it('should call appropriate action when Save button is clicked', async () => {
|
|
281
281
|
renderComponent({
|
|
282
282
|
...defaultProps,
|
|
283
283
|
isFullMode: true,
|
|
284
284
|
});
|
|
285
285
|
|
|
286
|
-
const createButton = screen.getByText('
|
|
286
|
+
const createButton = screen.getByText('Save');
|
|
287
287
|
fireEvent.click(createButton);
|
|
288
288
|
|
|
289
289
|
// Wait for async operations to complete
|
|
@@ -325,7 +325,7 @@ describe('InappAdvanced Component', () => {
|
|
|
325
325
|
});
|
|
326
326
|
|
|
327
327
|
// Template name should be used in the payload creation
|
|
328
|
-
expect(screen.getByText('
|
|
328
|
+
expect(screen.getByText('Save')).toBeInTheDocument();
|
|
329
329
|
});
|
|
330
330
|
|
|
331
331
|
it('should handle setTemplateName callback', () => {
|
|
@@ -348,7 +348,7 @@ describe('InappAdvanced Component', () => {
|
|
|
348
348
|
});
|
|
349
349
|
|
|
350
350
|
// Component should render without crashing
|
|
351
|
-
expect(screen.getByText('
|
|
351
|
+
expect(screen.getByText('Save')).toBeInTheDocument();
|
|
352
352
|
});
|
|
353
353
|
|
|
354
354
|
it('should use account data in payload creation', () => {
|
|
@@ -363,7 +363,7 @@ describe('InappAdvanced Component', () => {
|
|
|
363
363
|
});
|
|
364
364
|
|
|
365
365
|
// Account data should be used when creating the payload
|
|
366
|
-
expect(screen.getByText('
|
|
366
|
+
expect(screen.getByText('Save')).toBeInTheDocument();
|
|
367
367
|
});
|
|
368
368
|
});
|
|
369
369
|
|
|
@@ -396,7 +396,7 @@ describe('InappAdvanced Component', () => {
|
|
|
396
396
|
});
|
|
397
397
|
|
|
398
398
|
// Should show Update button in edit flow
|
|
399
|
-
expect(screen.getByText('
|
|
399
|
+
expect(screen.getByText('Save')).toBeInTheDocument();
|
|
400
400
|
});
|
|
401
401
|
});
|
|
402
402
|
|
|
@@ -427,7 +427,7 @@ describe('InappAdvanced Component', () => {
|
|
|
427
427
|
beePopupBuilderToken: undefined,
|
|
428
428
|
});
|
|
429
429
|
|
|
430
|
-
expect(screen.getByText('
|
|
430
|
+
expect(screen.getByText('Save')).toBeInTheDocument();
|
|
431
431
|
});
|
|
432
432
|
});
|
|
433
433
|
|
|
@@ -447,4 +447,28 @@ describe('InappAdvanced Component', () => {
|
|
|
447
447
|
expect(container.querySelector('.inapp-footer-lib')).toBeInTheDocument();
|
|
448
448
|
});
|
|
449
449
|
});
|
|
450
|
-
|
|
450
|
+
describe('Full mode keeps the slidebox open after saving', () => {
|
|
451
|
+
it('create: stays open (header switches) and the next save updates the created template', async () => {
|
|
452
|
+
renderComponent({ ...defaultProps, isFullMode: true, params: {} });
|
|
453
|
+
|
|
454
|
+
fireEvent.click(screen.getByText('Save'));
|
|
455
|
+
await waitFor(() => expect(mockActions.createInAppTemplate).toHaveBeenCalledTimes(1), { timeout: 3000 });
|
|
456
|
+
const onCreated = mockActions.createInAppTemplate.mock.calls[0][1];
|
|
457
|
+
act(() => onCreated({ templateId: { _id: 'ia-1' } }));
|
|
458
|
+
|
|
459
|
+
expect(defaultProps.getFormData).toHaveBeenCalledWith({
|
|
460
|
+
_id: 'ia-1', validity: true, keepSlideBoxOpen: true, continuedAsEdit: true,
|
|
461
|
+
});
|
|
462
|
+
expect(defaultProps.onCreateComplete).not.toHaveBeenCalled();
|
|
463
|
+
|
|
464
|
+
fireEvent.click(screen.getByText('Save'));
|
|
465
|
+
await waitFor(() => expect(mockActions.editTemplate).toHaveBeenCalledTimes(1), { timeout: 3000 });
|
|
466
|
+
expect(mockActions.createInAppTemplate).toHaveBeenCalledTimes(1);
|
|
467
|
+
expect(mockActions.editTemplate.mock.calls[0][0]._id).toBe('ia-1');
|
|
468
|
+
|
|
469
|
+
act(() => mockActions.editTemplate.mock.calls[0][1]({ templateId: { _id: 'ia-1' } }));
|
|
470
|
+
expect(defaultProps.getFormData).toHaveBeenLastCalledWith({ validity: true, keepSlideBoxOpen: true });
|
|
471
|
+
expect(defaultProps.onCreateComplete).not.toHaveBeenCalled();
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
});
|
|
@@ -4,6 +4,7 @@ import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
|
|
|
4
4
|
import { createStructuredSelector } from 'reselect';
|
|
5
5
|
import get from 'lodash/get';
|
|
6
6
|
import isEmpty from 'lodash/isEmpty';
|
|
7
|
+
import isPlainObject from 'lodash/isPlainObject';
|
|
7
8
|
import style from './style';
|
|
8
9
|
import withStyles from '../../../hoc/withStyles';
|
|
9
10
|
import './_lineCreate.scss';
|
|
@@ -22,6 +23,7 @@ import { CREATE, EDIT, TRACK_CREATE_LINE, TRACK_EDIT_LINE } from '../../App/cons
|
|
|
22
23
|
|
|
23
24
|
import LineWrapper from './Wrapper';
|
|
24
25
|
import messages from './messages';
|
|
26
|
+
import globalMessages from '../../Cap/messages';
|
|
25
27
|
import { TEXT, IMAGE, IMAGE_MAP, IMAGE_CAROUSEL, MAX_LINE_WRAPPER_LIMIT, MAX_LINE_WRAPPER_LIMIT_FULL_MODE, DEFAULT_VIDEO_URI, VIDEO, TEMPLATE } from './constants';
|
|
26
28
|
import { FONT_COLOR_05 } from '@capillarytech/cap-ui-library/styled/variables';
|
|
27
29
|
|
|
@@ -50,7 +52,6 @@ export const LineContainer = ({
|
|
|
50
52
|
Line,
|
|
51
53
|
className,
|
|
52
54
|
params,
|
|
53
|
-
onCreateComplete,
|
|
54
55
|
intl: { formatMessage },
|
|
55
56
|
getFormSubscriptionData,
|
|
56
57
|
templateData,
|
|
@@ -67,6 +68,8 @@ export const LineContainer = ({
|
|
|
67
68
|
const initialWrapperContent = [{ type: TEXT, isError: true, isInit: true }];
|
|
68
69
|
const [lineWrapperContents, updateLineWrapperContent] = useState(initialWrapperContent);
|
|
69
70
|
const [isEditFlow, checkEditFlow] = useState(false);
|
|
71
|
+
// Full mode: id of the template created in this session — the form stays as is and later saves update it
|
|
72
|
+
const [savedTemplateId, setSavedTemplateId] = useState('');
|
|
70
73
|
const [isEditLoading, updateEditLoading] = useState(false);
|
|
71
74
|
const [accountSelectFlow, updateAccountSelectFlow] = useState(false);
|
|
72
75
|
const [uploadedAssetList, updateUploadedAssetList] = useState({});
|
|
@@ -653,7 +656,13 @@ export const LineContainer = ({
|
|
|
653
656
|
mode: CREATE,
|
|
654
657
|
timeTaken,
|
|
655
658
|
}, formatFSPayload(lineWrapperContents)));
|
|
656
|
-
|
|
659
|
+
// Full mode stays open and continues in place as an edit.
|
|
660
|
+
const savedResponse = resp?.templateId;
|
|
661
|
+
const newTemplateId = (isPlainObject(savedResponse) ? savedResponse._id : savedResponse) || '';
|
|
662
|
+
setSavedTemplateId(newTemplateId);
|
|
663
|
+
getFormSubscriptionData({
|
|
664
|
+
_id: newTemplateId, validity: true, keepSlideBoxOpen: true, continuedAsEdit: true,
|
|
665
|
+
});
|
|
657
666
|
}
|
|
658
667
|
}
|
|
659
668
|
);
|
|
@@ -701,7 +710,7 @@ export const LineContainer = ({
|
|
|
701
710
|
actions.editTemplate(
|
|
702
711
|
{
|
|
703
712
|
...formatSubmitPayload(lineWrapperContents),
|
|
704
|
-
_id: params
|
|
713
|
+
_id: params?.id || savedTemplateId,
|
|
705
714
|
},
|
|
706
715
|
(resp, errorMessage) => {
|
|
707
716
|
editCallback(errorMessage);
|
|
@@ -713,20 +722,20 @@ export const LineContainer = ({
|
|
|
713
722
|
label: getLineType(lineWrapperContents),
|
|
714
723
|
});
|
|
715
724
|
gtmPush('creativeDetails', Object.assign({
|
|
716
|
-
id: resp
|
|
725
|
+
id: resp?.templateId?._id || resp?.templateId,
|
|
717
726
|
channel: LINE,
|
|
718
727
|
mode: EDIT,
|
|
719
728
|
timeTaken,
|
|
720
729
|
}, formatFSPayload(lineWrapperContents)));
|
|
721
|
-
|
|
722
|
-
|
|
730
|
+
|
|
731
|
+
getFormSubscriptionData({ validity: true, keepSlideBoxOpen: true }); // stays open; host refreshes its list
|
|
723
732
|
}
|
|
724
733
|
});
|
|
725
734
|
}
|
|
726
735
|
|
|
727
736
|
const onDoneCallback = () => {
|
|
728
737
|
if (isFullMode) {
|
|
729
|
-
if (isEditFlow) {
|
|
738
|
+
if (isEditFlow || savedTemplateId) {
|
|
730
739
|
return onEditLine;
|
|
731
740
|
}
|
|
732
741
|
return onCreateLine;
|
|
@@ -845,7 +854,8 @@ export const LineContainer = ({
|
|
|
845
854
|
disabled={isDoneDisabled()}
|
|
846
855
|
className="create-msg"
|
|
847
856
|
>
|
|
848
|
-
|
|
857
|
+
{/* Full mode saves without closing, so the button reads "Save" for create and edit */}
|
|
858
|
+
<FormattedMessage {...(isFullMode ? globalMessages.save : messages.doneButtonLabel)} />
|
|
849
859
|
</CapButton>
|
|
850
860
|
{
|
|
851
861
|
!isFullMode
|