@capillarytech/creatives-library 9.0.70-alpha.sprint-cap-213712.13 → 9.0.71

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.
Files changed (46) hide show
  1. package/constants/unified.js +0 -1
  2. package/package.json +1 -1
  3. package/utils/common.js +0 -6
  4. package/v2Components/FormBuilder/Classic.js +3 -0
  5. package/v2Containers/Cap/tests/index.test.js +1 -0
  6. package/v2Containers/CreativesContainer/SlideBoxContent.js +39 -30
  7. package/v2Containers/CreativesContainer/SlideBoxFooter.js +6 -2
  8. package/v2Containers/CreativesContainer/constants.js +1 -0
  9. package/v2Containers/CreativesContainer/index.js +28 -3
  10. package/v2Containers/CreativesContainer/messages.js +4 -0
  11. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +40 -12
  12. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +10 -0
  13. package/v2Containers/CreativesContainer/tests/switchToEditAfterCreate.test.js +152 -0
  14. package/v2Containers/Email/index.js +41 -6
  15. package/v2Containers/Email/tests/index.test.js +123 -0
  16. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +32 -12
  17. package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +34 -0
  18. package/v2Containers/InApp/index.js +19 -23
  19. package/v2Containers/InApp/tests/index.test.js +52 -9
  20. package/v2Containers/InappAdvance/index.js +19 -24
  21. package/v2Containers/InappAdvance/tests/index.test.js +39 -15
  22. package/v2Containers/Line/Container/index.js +18 -8
  23. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +129 -108
  24. package/v2Containers/Line/Container/tests/index.test.js +68 -0
  25. package/v2Containers/MobilePush/Create/index.js +24 -5
  26. package/v2Containers/MobilePush/Create/test/stayOpenOnSave.test.js +80 -0
  27. package/v2Containers/MobilePush/Edit/index.js +15 -9
  28. package/v2Containers/MobilePush/Edit/test/stayOpenOnSave.test.js +60 -0
  29. package/v2Containers/MobilePushNew/index.js +41 -7
  30. package/v2Containers/MobilePushNew/tests/index.test.js +61 -0
  31. package/v2Containers/Sms/Create/index.js +32 -14
  32. package/v2Containers/Sms/Create/test/stayOpenOnSave.test.js +92 -0
  33. package/v2Containers/Sms/Edit/index.js +22 -14
  34. package/v2Containers/Sms/Edit/tests/index.test.js +66 -0
  35. package/v2Containers/Templates/constants.js +1 -0
  36. package/v2Containers/Templates/index.js +48 -31
  37. package/v2Containers/Templates/messages.js +4 -0
  38. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +128 -4
  39. package/v2Containers/Templates/tests/archival.test.js +221 -0
  40. package/v2Containers/Viber/index.js +15 -6
  41. package/v2Containers/Viber/tests/index.test.js +97 -28
  42. package/v2Containers/WebPush/Create/components/FormActions.js +1 -1
  43. package/v2Containers/WebPush/Create/components/FormActions.test.js +3 -3
  44. package/v2Containers/WebPush/Create/index.js +32 -6
  45. package/v2Containers/WebPush/Create/messages.js +0 -4
  46. package/v2Containers/WebPush/Create/tests/stayOpenOnSave.test.js +268 -0
@@ -9072,14 +9072,14 @@ new message content.",
9072
9072
  />
9073
9073
  </DefaultLoadingIcon>
9074
9074
  <FormattedMessage
9075
- defaultMessage="Done"
9076
- id="creatives.containersV2.LineContainer.doneButtonLabel"
9075
+ defaultMessage="Save"
9076
+ id="creatives.containersV2.save"
9077
9077
  key=".2"
9078
9078
  style={Object {}}
9079
9079
  values={Object {}}
9080
9080
  >
9081
9081
  <span>
9082
- Done
9082
+ Save
9083
9083
  </span>
9084
9084
  </FormattedMessage>
9085
9085
  </button>
@@ -12368,7 +12368,24 @@ new message content.",
12368
12368
  },
12369
12369
  }
12370
12370
  }
12371
- getFormSubscriptionData={[MockFunction]}
12371
+ getFormSubscriptionData={
12372
+ [MockFunction] {
12373
+ "calls": Array [
12374
+ Array [
12375
+ Object {
12376
+ "keepSlideBoxOpen": true,
12377
+ "validity": true,
12378
+ },
12379
+ ],
12380
+ ],
12381
+ "results": Array [
12382
+ Object {
12383
+ "type": "return",
12384
+ "value": undefined,
12385
+ },
12386
+ ],
12387
+ }
12388
+ }
12372
12389
  handleClose={[MockFunction]}
12373
12390
  intl={
12374
12391
  Object {
@@ -13215,19 +13232,7 @@ new message content.",
13215
13232
  "weCRMtemplates": Array [],
13216
13233
  }
13217
13234
  }
13218
- onCreateComplete={
13219
- [MockFunction] {
13220
- "calls": Array [
13221
- Array [],
13222
- ],
13223
- "results": Array [
13224
- Object {
13225
- "type": "return",
13226
- "value": undefined,
13227
- },
13228
- ],
13229
- }
13230
- }
13235
+ onCreateComplete={[MockFunction]}
13231
13236
  onShowTemplates={[MockFunction]}
13232
13237
  params={
13233
13238
  Object {
@@ -18352,14 +18357,14 @@ new message content.",
18352
18357
  />
18353
18358
  </DefaultLoadingIcon>
18354
18359
  <FormattedMessage
18355
- defaultMessage="Done"
18356
- id="creatives.containersV2.LineContainer.doneButtonLabel"
18360
+ defaultMessage="Save"
18361
+ id="creatives.containersV2.save"
18357
18362
  key=".2"
18358
18363
  style={Object {}}
18359
18364
  values={Object {}}
18360
18365
  >
18361
18366
  <span>
18362
- Done
18367
+ Save
18363
18368
  </span>
18364
18369
  </FormattedMessage>
18365
18370
  </button>
@@ -21656,7 +21661,24 @@ new message content.",
21656
21661
  },
21657
21662
  }
21658
21663
  }
21659
- getFormSubscriptionData={[MockFunction]}
21664
+ getFormSubscriptionData={
21665
+ [MockFunction] {
21666
+ "calls": Array [
21667
+ Array [
21668
+ Object {
21669
+ "keepSlideBoxOpen": true,
21670
+ "validity": true,
21671
+ },
21672
+ ],
21673
+ ],
21674
+ "results": Array [
21675
+ Object {
21676
+ "type": "return",
21677
+ "value": undefined,
21678
+ },
21679
+ ],
21680
+ }
21681
+ }
21660
21682
  handleClose={[MockFunction]}
21661
21683
  intl={
21662
21684
  Object {
@@ -22563,19 +22585,7 @@ new message content.",
22563
22585
  "weCRMtemplates": Array [],
22564
22586
  }
22565
22587
  }
22566
- onCreateComplete={
22567
- [MockFunction] {
22568
- "calls": Array [
22569
- Array [],
22570
- ],
22571
- "results": Array [
22572
- Object {
22573
- "type": "return",
22574
- "value": undefined,
22575
- },
22576
- ],
22577
- }
22578
- }
22588
+ onCreateComplete={[MockFunction]}
22579
22589
  onShowTemplates={[MockFunction]}
22580
22590
  params={
22581
22591
  Object {
@@ -31248,7 +31258,24 @@ new message content.",
31248
31258
  },
31249
31259
  }
31250
31260
  }
31251
- getFormSubscriptionData={[MockFunction]}
31261
+ getFormSubscriptionData={
31262
+ [MockFunction] {
31263
+ "calls": Array [
31264
+ Array [
31265
+ Object {
31266
+ "keepSlideBoxOpen": true,
31267
+ "validity": true,
31268
+ },
31269
+ ],
31270
+ ],
31271
+ "results": Array [
31272
+ Object {
31273
+ "type": "return",
31274
+ "value": undefined,
31275
+ },
31276
+ ],
31277
+ }
31278
+ }
31252
31279
  handleClose={[MockFunction]}
31253
31280
  intl={
31254
31281
  Object {
@@ -32215,19 +32242,7 @@ new message content.",
32215
32242
  "weCRMtemplates": Array [],
32216
32243
  }
32217
32244
  }
32218
- onCreateComplete={
32219
- [MockFunction] {
32220
- "calls": Array [
32221
- Array [],
32222
- ],
32223
- "results": Array [
32224
- Object {
32225
- "type": "return",
32226
- "value": undefined,
32227
- },
32228
- ],
32229
- }
32230
- }
32245
+ onCreateComplete={[MockFunction]}
32231
32246
  onShowTemplates={[MockFunction]}
32232
32247
  params={
32233
32248
  Object {
@@ -43056,6 +43071,12 @@ new message content.",
43056
43071
  getFormSubscriptionData={
43057
43072
  [MockFunction] {
43058
43073
  "calls": Array [
43074
+ Array [
43075
+ Object {
43076
+ "keepSlideBoxOpen": true,
43077
+ "validity": true,
43078
+ },
43079
+ ],
43059
43080
  Array [
43060
43081
  Object {
43061
43082
  "_id": "629f7ecf134738019b2e332e",
@@ -43093,6 +43114,10 @@ new message content.",
43093
43114
  "type": "return",
43094
43115
  "value": undefined,
43095
43116
  },
43117
+ Object {
43118
+ "type": "return",
43119
+ "value": undefined,
43120
+ },
43096
43121
  ],
43097
43122
  }
43098
43123
  }
@@ -44062,19 +44087,7 @@ new message content.",
44062
44087
  "weCRMtemplates": Array [],
44063
44088
  }
44064
44089
  }
44065
- onCreateComplete={
44066
- [MockFunction] {
44067
- "calls": Array [
44068
- Array [],
44069
- ],
44070
- "results": Array [
44071
- Object {
44072
- "type": "return",
44073
- "value": undefined,
44074
- },
44075
- ],
44076
- }
44077
- }
44090
+ onCreateComplete={[MockFunction]}
44078
44091
  onShowTemplates={[MockFunction]}
44079
44092
  params={
44080
44093
  Object {
@@ -59351,14 +59364,14 @@ new message content.",
59351
59364
  />
59352
59365
  </DefaultLoadingIcon>
59353
59366
  <FormattedMessage
59354
- defaultMessage="Done"
59355
- id="creatives.containersV2.LineContainer.doneButtonLabel"
59367
+ defaultMessage="Save"
59368
+ id="creatives.containersV2.save"
59356
59369
  key=".2"
59357
59370
  style={Object {}}
59358
59371
  values={Object {}}
59359
59372
  >
59360
59373
  <span>
59361
- Done
59374
+ Save
59362
59375
  </span>
59363
59376
  </FormattedMessage>
59364
59377
  </button>
@@ -62244,7 +62257,24 @@ new message content.",
62244
62257
  },
62245
62258
  }
62246
62259
  }
62247
- getFormSubscriptionData={[MockFunction]}
62260
+ getFormSubscriptionData={
62261
+ [MockFunction] {
62262
+ "calls": Array [
62263
+ Array [
62264
+ Object {
62265
+ "keepSlideBoxOpen": true,
62266
+ "validity": true,
62267
+ },
62268
+ ],
62269
+ ],
62270
+ "results": Array [
62271
+ Object {
62272
+ "type": "return",
62273
+ "value": undefined,
62274
+ },
62275
+ ],
62276
+ }
62277
+ }
62248
62278
  handleClose={[MockFunction]}
62249
62279
  intl={
62250
62280
  Object {
@@ -62633,19 +62663,7 @@ new message content.",
62633
62663
  "weCRMtemplates": Array [],
62634
62664
  }
62635
62665
  }
62636
- onCreateComplete={
62637
- [MockFunction] {
62638
- "calls": Array [
62639
- Array [],
62640
- ],
62641
- "results": Array [
62642
- Object {
62643
- "type": "return",
62644
- "value": undefined,
62645
- },
62646
- ],
62647
- }
62648
- }
62666
+ onCreateComplete={[MockFunction]}
62649
62667
  onShowTemplates={[MockFunction]}
62650
62668
  params={
62651
62669
  Object {
@@ -67532,14 +67550,14 @@ new message content.",
67532
67550
  />
67533
67551
  </DefaultLoadingIcon>
67534
67552
  <FormattedMessage
67535
- defaultMessage="Done"
67536
- id="creatives.containersV2.LineContainer.doneButtonLabel"
67553
+ defaultMessage="Save"
67554
+ id="creatives.containersV2.save"
67537
67555
  key=".2"
67538
67556
  style={Object {}}
67539
67557
  values={Object {}}
67540
67558
  >
67541
67559
  <span>
67542
- Done
67560
+ Save
67543
67561
  </span>
67544
67562
  </FormattedMessage>
67545
67563
  </button>
@@ -70433,7 +70451,24 @@ new message content.",
70433
70451
  },
70434
70452
  }
70435
70453
  }
70436
- getFormSubscriptionData={[MockFunction]}
70454
+ getFormSubscriptionData={
70455
+ [MockFunction] {
70456
+ "calls": Array [
70457
+ Array [
70458
+ Object {
70459
+ "keepSlideBoxOpen": true,
70460
+ "validity": true,
70461
+ },
70462
+ ],
70463
+ ],
70464
+ "results": Array [
70465
+ Object {
70466
+ "type": "return",
70467
+ "value": undefined,
70468
+ },
70469
+ ],
70470
+ }
70471
+ }
70437
70472
  handleClose={[MockFunction]}
70438
70473
  intl={
70439
70474
  Object {
@@ -70882,19 +70917,7 @@ new message content.",
70882
70917
  "weCRMtemplates": Array [],
70883
70918
  }
70884
70919
  }
70885
- onCreateComplete={
70886
- [MockFunction] {
70887
- "calls": Array [
70888
- Array [],
70889
- ],
70890
- "results": Array [
70891
- Object {
70892
- "type": "return",
70893
- "value": undefined,
70894
- },
70895
- ],
70896
- }
70897
- }
70920
+ onCreateComplete={[MockFunction]}
70898
70921
  onShowTemplates={[MockFunction]}
70899
70922
  params={
70900
70923
  Object {
@@ -78921,6 +78944,12 @@ new message content.",
78921
78944
  getFormSubscriptionData={
78922
78945
  [MockFunction] {
78923
78946
  "calls": Array [
78947
+ Array [
78948
+ Object {
78949
+ "keepSlideBoxOpen": true,
78950
+ "validity": true,
78951
+ },
78952
+ ],
78924
78953
  Array [
78925
78954
  Object {
78926
78955
  "_id": "62a810e4d4715c768d6ee9cf",
@@ -79036,6 +79065,10 @@ new message content.",
79036
79065
  "type": "return",
79037
79066
  "value": undefined,
79038
79067
  },
79068
+ Object {
79069
+ "type": "return",
79070
+ "value": undefined,
79071
+ },
79039
79072
  ],
79040
79073
  }
79041
79074
  }
@@ -79487,19 +79520,7 @@ new message content.",
79487
79520
  "weCRMtemplates": Array [],
79488
79521
  }
79489
79522
  }
79490
- onCreateComplete={
79491
- [MockFunction] {
79492
- "calls": Array [
79493
- Array [],
79494
- ],
79495
- "results": Array [
79496
- Object {
79497
- "type": "return",
79498
- "value": undefined,
79499
- },
79500
- ],
79501
- }
79502
- }
79523
+ onCreateComplete={[MockFunction]}
79503
79524
  onShowTemplates={[MockFunction]}
79504
79525
  params={
79505
79526
  Object {
@@ -108,3 +108,71 @@ describe('line wrapper test/>', () => {
108
108
  expect(renderedComponent).toMatchSnapshot();
109
109
  });
110
110
  });
111
+
112
+ describe('LineContainer — full mode keeps the slidebox open after saving', () => {
113
+ // eslint-disable-next-line global-require
114
+ const { act } = require('react-dom/test-utils');
115
+ const renderLine = (overrides = {}) => {
116
+ const props = {
117
+ intl: { formatMessage: jest.fn() },
118
+ isFullMode: true,
119
+ Line: mockData.Line1,
120
+ onShowTemplates: jest.fn(),
121
+ handleClose: jest.fn(),
122
+ onCreateComplete: jest.fn(),
123
+ getFormSubscriptionData: jest.fn(),
124
+ Templates: mockData.Templates1,
125
+ lineData: mockData.Templates1,
126
+ templateData: mockData.templateData1,
127
+ params: {},
128
+ templateActions: { getWeCrmAccounts: jest.fn(), getAllTemplates: jest.fn(), setChannelAccount: jest.fn() },
129
+ actions: {
130
+ getTemplateDetails: jest.fn(),
131
+ createTemplate: jest.fn(),
132
+ clearEditResponse: jest.fn(),
133
+ editTemplate: jest.fn(),
134
+ },
135
+ ...overrides,
136
+ };
137
+ const wrapper = mountWithIntl(
138
+ <Provider store={store}>
139
+ <LineContainer {...props} />
140
+ </Provider>,
141
+ );
142
+ // Invoke the main button's handler directly (its enabled state depends on the mocked Wrapper's content)
143
+ const clickSave = () => {
144
+ wrapper.update();
145
+ act(() => { wrapper.find('CapButton.create-msg').prop('onClick')(); });
146
+ };
147
+ return { props, wrapper, clickSave };
148
+ };
149
+
150
+ it('create: stays open (header switches) and the next save updates the created template', () => {
151
+ const { props, clickSave } = renderLine();
152
+
153
+ clickSave();
154
+ expect(props.actions.createTemplate).toHaveBeenCalledTimes(1);
155
+ act(() => { props.actions.createTemplate.mock.calls[0][1]({ templateId: 'ln-1' }); });
156
+
157
+ expect(props.getFormSubscriptionData).toHaveBeenCalledWith({
158
+ _id: 'ln-1', validity: true, keepSlideBoxOpen: true, continuedAsEdit: true,
159
+ });
160
+ expect(props.onCreateComplete).not.toHaveBeenCalled();
161
+
162
+ clickSave();
163
+ expect(props.actions.createTemplate).toHaveBeenCalledTimes(1);
164
+ expect(props.actions.editTemplate).toHaveBeenCalledTimes(1);
165
+ expect(props.actions.editTemplate.mock.calls[0][0]._id).toBe('ln-1');
166
+ });
167
+
168
+ it('edit: stays open after a successful update', () => {
169
+ const { props, clickSave } = renderLine({ params: mockData.params1 });
170
+
171
+ clickSave();
172
+ expect(props.actions.editTemplate).toHaveBeenCalledTimes(1);
173
+ act(() => { props.actions.editTemplate.mock.calls[0][1]({ templateId: { _id: 'ln-9' } }); });
174
+
175
+ expect(props.getFormSubscriptionData).toHaveBeenCalledWith({ validity: true, keepSlideBoxOpen: true });
176
+ expect(props.onCreateComplete).not.toHaveBeenCalled();
177
+ });
178
+ });
@@ -9,7 +9,7 @@ import PropTypes, { object } from 'prop-types';
9
9
  import React from 'react';
10
10
  import { bindActionCreators } from 'redux';
11
11
  import { notification } from 'antd';
12
- import {get, set, filter, isEmpty, map, forEach, uniqueId, cloneDeep, isEqual, findIndex} from 'lodash';
12
+ import {get, set, filter, isEmpty, map, forEach, uniqueId, cloneDeep, isEqual, findIndex, isPlainObject} from 'lodash';
13
13
  import { createStructuredSelector } from 'reselect';
14
14
  import { intlShape, FormattedMessage } from 'react-intl';
15
15
  import styled from 'styled-components';
@@ -26,9 +26,10 @@ import { gtmPush } from '../../../utils/gtmTrackers';
26
26
 
27
27
  import { makeSelectTemplates } from '../../Templates/selectors';
28
28
  import messages from './messages';
29
+ import editMessages from '../Edit/messages';
29
30
  import getEventsMap from '../eventsMap';
30
31
  import { GA } from '@capillarytech/cap-ui-utils';
31
- import { CREATE, TRACK_CREATE_MPUSH } from '../../App/constants';
32
+ import { CREATE, EDIT, TRACK_CREATE_MPUSH } from '../../App/constants';
32
33
  import { MOBILE_PUSH } from '../../CreativesContainer/constants';
33
34
  import { getContent, getMobilePushEmbeddedContentValidity } from '../commonMethods';
34
35
  import { getCdnUrl } from '../../../utils/cdnTransformation';
@@ -53,6 +54,8 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
53
54
  formData: {},
54
55
  tabCount: 1,
55
56
  currentTab: 1,
57
+ // Full mode: id of the template created in this session; later saves update it
58
+ savedTemplateId: '',
56
59
  loading: false,
57
60
  isFormValid: true,
58
61
  tabKey: '',
@@ -162,7 +165,8 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
162
165
  }
163
166
  }
164
167
  } else if (nextProps.isGetFormData && this.props.isGetFormData !== nextProps.isGetFormData && this.props.isFullMode && !this.props.Create.createTemplateInProgress) {
165
- this.startValidation();
168
+ // Re-arm per Save click (saves only on startValidation false → true).
169
+ this.setState({ startValidation: false }, this.startValidation);
166
170
  }
167
171
  if (nextProps.Create.uploadedAssetData) {
168
172
  const formData = cloneDeep(this.state.formData);
@@ -1592,6 +1596,11 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
1592
1596
  },
1593
1597
  } = obj || {};
1594
1598
  const label = this.props.location.pathname.indexOf('image') >= 0 ? 'image' : 'text';
1599
+ // Full mode: a template created earlier in this session is updated (same API, with its _id)
1600
+ const { savedTemplateId } = this.state;
1601
+ if (savedTemplateId) {
1602
+ obj._id = savedTemplateId;
1603
+ }
1595
1604
  this.props.actions.createTemplate(obj, (response) => {
1596
1605
  const timeTaken = GA.timeTracker.stopTimer(TRACK_CREATE_MPUSH, {
1597
1606
  category: 'Creatives',
@@ -1604,14 +1613,24 @@ export class Create extends React.Component { // eslint-disable-line react/prefe
1604
1613
  channel: MOBILE_PUSH,
1605
1614
  timeTaken,
1606
1615
  content,
1607
- mode: CREATE,
1616
+ mode: savedTemplateId ? EDIT : CREATE,
1608
1617
  imageAdded: definitionMode === 'image',
1609
1618
  });
1610
1619
 
1620
+ this.props.actions.clearCreateResponse();
1621
+ if (this.props.isFullMode) {
1622
+ // Stay open: next save updates this template.
1623
+ const savedResponse = response?.templateId;
1624
+ const newTemplateId = (isPlainObject(savedResponse) ? savedResponse._id : savedResponse) || savedTemplateId;
1625
+ this.setState({ savedTemplateId: newTemplateId });
1626
+ const message = this.props.intl.formatMessage(savedTemplateId ? editMessages['MOBILEPUSH Template Edited Successfully'] : messages.messageCreateSuccess);
1627
+ CapNotification.success({message, key: 'createMpushSuccess'});
1628
+ this.props.getFormLibraryData({ _id: newTemplateId, validity: true, keepSlideBoxOpen: true });
1629
+ return;
1630
+ }
1611
1631
  this.discardValues();
1612
1632
  const message = this.props.intl.formatMessage(messages.messageCreateSuccess);
1613
1633
  CapNotification.success({message, key: 'createMpushSuccess'});
1614
- this.props.actions.clearCreateResponse();
1615
1634
  this.props.getFormLibraryData({validity: true});
1616
1635
  });
1617
1636
  };
@@ -0,0 +1,80 @@
1
+ import React from 'react';
2
+ import { shallow } from 'enzyme';
3
+ import { Create } from '../index';
4
+
5
+ jest.mock('@capillarytech/cap-ui-library', () => ({
6
+ ...jest.requireActual('@capillarytech/cap-ui-library'),
7
+ CapNotification: { success: jest.fn(), error: jest.fn() },
8
+ }));
9
+
10
+ const baseProps = (overrides = {}) => ({
11
+ actions: {
12
+ createTemplate: jest.fn((obj, callback) => callback({ templateId: { _id: 'mp-1', name: 'Push' } })),
13
+ clearCreateResponse: jest.fn(),
14
+ },
15
+ globalActions: { fetchSchemaForEntity: jest.fn(), addMessageToQueue: jest.fn() },
16
+ Templates: { selectedWeChatAccount: { id: 'acc-1' } },
17
+ Create: {},
18
+ params: {},
19
+ location: { query: { type: 'library', module: 'default' }, pathname: '/mobilepush/create/text' },
20
+ route: { name: 'mobilepush' },
21
+ router: { push: jest.fn() },
22
+ metaEntities: {},
23
+ intl: { formatMessage: (m) => (m && (m.defaultMessage || m.id)) || '' },
24
+ isFullMode: true,
25
+ getFormLibraryData: jest.fn(),
26
+ onValidationFail: jest.fn(),
27
+ ...overrides,
28
+ });
29
+
30
+ const render = (overrides) => {
31
+ const props = baseProps(overrides);
32
+ const wrapper = shallow(<Create {...props} />);
33
+ const instance = wrapper.instance();
34
+ instance.getTransformedData = () => ({ name: 'Push', definition: { mode: 'text' } });
35
+ instance.discardValues = jest.fn();
36
+ return { props, wrapper, instance };
37
+ };
38
+
39
+ describe('MobilePush/Create — full mode keeps the slidebox open after saving', () => {
40
+ it('remembers the created template, keeps the form and asks the host to stay open', () => {
41
+ const { props, wrapper, instance } = render();
42
+
43
+ instance.saveFormData({});
44
+
45
+ expect(props.actions.createTemplate.mock.calls[0][0]._id).toBeUndefined();
46
+ expect(wrapper.state('savedTemplateId')).toBe('mp-1');
47
+ expect(instance.discardValues).not.toHaveBeenCalled();
48
+ expect(props.getFormLibraryData).toHaveBeenCalledWith({ _id: 'mp-1', validity: true, keepSlideBoxOpen: true });
49
+ });
50
+
51
+ it('updates the same template on the next save (plain id response too)', () => {
52
+ const { props, instance } = render();
53
+ props.actions.createTemplate.mockImplementation((obj, callback) => callback({ templateId: 'mp-1' }));
54
+
55
+ instance.saveFormData({});
56
+ instance.saveFormData({});
57
+
58
+ expect(props.actions.createTemplate).toHaveBeenCalledTimes(2);
59
+ expect(props.actions.createTemplate.mock.calls[1][0]._id).toBe('mp-1');
60
+ });
61
+
62
+ it('re-arms validation on each Save click (false, then true)', () => {
63
+ const { wrapper } = render({ isGetFormData: false });
64
+ wrapper.setState({ startValidation: true }); // left set by the previous save
65
+
66
+ wrapper.setProps({ isGetFormData: true });
67
+
68
+ expect(wrapper.state('startValidation')).toBe(true);
69
+ });
70
+
71
+ it('library mode is unchanged: form is discarded and the host is told as before', () => {
72
+ const { props, wrapper, instance } = render({ isFullMode: false });
73
+
74
+ instance.saveFormData({});
75
+
76
+ expect(instance.discardValues).toHaveBeenCalled();
77
+ expect(wrapper.state('savedTemplateId')).toBe('');
78
+ expect(props.getFormLibraryData).toHaveBeenCalledWith({ validity: true });
79
+ });
80
+ });
@@ -185,7 +185,8 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
185
185
  nextProps.getFormLibraryData(this.getFormData());
186
186
  }
187
187
  } else if (nextProps.isGetFormData && this.props.isGetFormData !== nextProps.isGetFormData && this.props.isFullMode && !this.props.Create.createTemplateInProgress) {
188
- this.startValidation();
188
+ // Re-arm per Save click (saves only on startValidation false → true).
189
+ this.setState({ startValidation: false }, this.startValidation);
189
190
  }
190
191
  let selectedWeChatAccount = {};
191
192
  const queryType = String(get(this.props, 'location.query.type', ''))?.toLowerCase();
@@ -281,6 +282,10 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
281
282
  if (nextProps.Edit.editTemplateError && !_.isEqual(nextProps.Edit.editTemplateError, this.props.Edit.editTemplateError)) {
282
283
  const message = getMessageObject('error', (nextProps.Edit.editTemplateErrorMessage && nextProps.Edit.editTemplateErrorMessage !== '') ? nextProps.Edit.editTemplateErrorMessage : this.props.intl.formatMessage(messages.somethingWentWrong), true);
283
284
  this.props.globalActions.addMessageToQueue(message);
285
+ if (this.props.isFullMode && this.props.onValidationFail) {
286
+ // Slidebox stays open: reset the host's Save trigger so the user can retry
287
+ this.props.onValidationFail();
288
+ }
284
289
  }
285
290
  }
286
291
  componentWillUnmount() {
@@ -435,19 +440,20 @@ export class Edit extends React.Component { // eslint-disable-line react/prefer-
435
440
 
436
441
  }
437
442
 
438
- this.resetSchema();
439
- this.setState({
440
- formData: {},
441
- tabCount: 1,
442
- currentTab: 1,
443
- editData: {},
444
- });
445
443
  const message = this.props.intl.formatMessage(messages['MOBILEPUSH Template Edited Successfully']);
446
444
  CapNotification.success({message, key: 'editMpushSuccess'});
447
445
  this.props.actions.clearEditResponse();
448
446
  if (this.props.isFullMode) {
449
- this.props.getFormLibraryData({validity: true});
447
+ // Stay open with the form as is (editData keeps the _id, so later saves update again); host refreshes its list
448
+ this.props.getFormLibraryData({ validity: true, keepSlideBoxOpen: true });
450
449
  } else {
450
+ this.resetSchema();
451
+ this.setState({
452
+ formData: {},
453
+ tabCount: 1,
454
+ currentTab: 1,
455
+ editData: {},
456
+ });
451
457
  const module = this.props.location.query.module ? this.props.location.query.module : 'default';
452
458
  const type = this.props.location.query.type;
453
459
  this.props.router.push({