@capillarytech/creatives-library 8.0.345-alpha.14 → 8.0.345-alpha.15

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 (129) hide show
  1. package/constants/unified.js +29 -0
  2. package/package.json +1 -1
  3. package/services/tests/api.test.js +13 -0
  4. package/utils/commonUtils.js +19 -1
  5. package/utils/rcsPayloadUtils.js +92 -0
  6. package/utils/templateVarUtils.js +201 -0
  7. package/utils/tests/templateVarUtils.test.js +204 -0
  8. package/v2Components/CapActionButton/constants.js +7 -0
  9. package/v2Components/CapActionButton/index.js +167 -109
  10. package/v2Components/CapActionButton/index.scss +157 -6
  11. package/v2Components/CapActionButton/messages.js +19 -3
  12. package/v2Components/CapActionButton/tests/index.test.js +41 -17
  13. package/v2Components/CapTagList/index.js +10 -0
  14. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -49
  15. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
  16. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +207 -21
  17. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
  18. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +85 -10
  19. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
  20. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +79 -11
  21. package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
  22. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +160 -15
  23. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js.rej +18 -0
  24. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +341 -76
  25. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +133 -4
  26. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
  27. package/v2Components/CommonTestAndPreview/constants.js +38 -2
  28. package/v2Components/CommonTestAndPreview/index.js +676 -186
  29. package/v2Components/CommonTestAndPreview/messages.js +49 -3
  30. package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
  31. package/v2Components/CommonTestAndPreview/sagas.js +15 -6
  32. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +308 -284
  33. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +231 -65
  34. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
  35. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
  36. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
  37. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +34 -13
  38. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
  39. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
  40. package/v2Components/CommonTestAndPreview/tests/index.test.js +132 -4
  41. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
  42. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
  43. package/v2Components/FormBuilder/index.js +8 -10
  44. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +87 -0
  45. package/v2Components/SmsFallback/constants.js +73 -0
  46. package/v2Components/SmsFallback/index.js +955 -0
  47. package/v2Components/SmsFallback/index.scss +265 -0
  48. package/v2Components/SmsFallback/messages.js +78 -0
  49. package/v2Components/SmsFallback/smsFallbackUtils.js +118 -0
  50. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
  51. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
  52. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
  53. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +197 -0
  54. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +277 -0
  55. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
  56. package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
  57. package/v2Components/TemplatePreview/_templatePreview.scss +33 -23
  58. package/v2Components/TemplatePreview/constants.js +2 -0
  59. package/v2Components/TemplatePreview/index.js +143 -28
  60. package/v2Components/TemplatePreview/tests/index.test.js +142 -0
  61. package/v2Components/TestAndPreviewSlidebox/index.js +13 -1
  62. package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
  63. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
  64. package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
  65. package/v2Components/VarSegmentMessageEditor/index.js +125 -0
  66. package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
  67. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +43 -0
  68. package/v2Containers/CreativesContainer/SlideBoxContent.js +36 -4
  69. package/v2Containers/CreativesContainer/SlideBoxFooter.js +10 -1
  70. package/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
  71. package/v2Containers/CreativesContainer/constants.js +9 -0
  72. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +67 -0
  73. package/v2Containers/CreativesContainer/index.js +300 -103
  74. package/v2Containers/CreativesContainer/index.scss +51 -1
  75. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
  76. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +78 -34
  77. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +79 -16
  78. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
  79. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +357 -98
  80. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
  81. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
  82. package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
  83. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
  84. package/v2Containers/Email/reducer.js +3 -11
  85. package/v2Containers/Email/sagas.js +5 -9
  86. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +0 -4
  87. package/v2Containers/Email/tests/sagas.test.js +3 -21
  88. package/v2Containers/Rcs/constants.js +119 -8
  89. package/v2Containers/Rcs/index.js +2379 -807
  90. package/v2Containers/Rcs/index.js.rej +1336 -0
  91. package/v2Containers/Rcs/index.scss +276 -6
  92. package/v2Containers/Rcs/index.scss.rej +74 -0
  93. package/v2Containers/Rcs/messages.js +38 -3
  94. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +225 -0
  95. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +98018 -70073
  96. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
  97. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap.rej +128 -0
  98. package/v2Containers/Rcs/tests/index.test.js +152 -121
  99. package/v2Containers/Rcs/tests/mockData.js +38 -0
  100. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +318 -0
  101. package/v2Containers/Rcs/tests/utils.test.js +646 -30
  102. package/v2Containers/Rcs/utils.js +478 -11
  103. package/v2Containers/Sms/Create/index.js +100 -40
  104. package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
  105. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  106. package/v2Containers/SmsTrai/Create/index.js +9 -4
  107. package/v2Containers/SmsTrai/Edit/constants.js +2 -0
  108. package/v2Containers/SmsTrai/Edit/index.js +636 -130
  109. package/v2Containers/SmsTrai/Edit/index.scss +121 -0
  110. package/v2Containers/SmsTrai/Edit/messages.js +14 -4
  111. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4328 -2375
  112. package/v2Containers/SmsWrapper/index.js +37 -8
  113. package/v2Containers/TagList/index.js +6 -0
  114. package/v2Containers/Templates/TemplatesActionBar.js +101 -0
  115. package/v2Containers/Templates/_templates.scss +163 -2
  116. package/v2Containers/Templates/actions.js +11 -0
  117. package/v2Containers/Templates/constants.js +2 -0
  118. package/v2Containers/Templates/index.js +119 -54
  119. package/v2Containers/Templates/sagas.js +57 -12
  120. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
  121. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1043 -1079
  122. package/v2Containers/Templates/tests/sagas.test.js +193 -123
  123. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
  124. package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
  125. package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
  126. package/v2Containers/TemplatesV2/index.js +86 -23
  127. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
  128. package/v2Containers/Whatsapp/index.js +3 -20
  129. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
@@ -10,7 +10,7 @@ export default css`
10
10
 
11
11
  .component-wrapper {
12
12
  ${(props) => props.isFullMode ? `
13
- max-width: 1140px;
13
+ max-width: 71.25rem;
14
14
  margin: 0 auto;
15
15
  width: 100%;
16
16
  padding: ${CAP_SPACE_24} 0;
@@ -23,6 +23,77 @@ export default css`
23
23
  height: calc(100vh - 11.25rem);
24
24
  } `}
25
25
  }
26
+
27
+ /* SMS fallback / local list: single pane skips .cap-tab-v2, so flex-fill the grid instead of viewport-fixed pagination height */
28
+ .creatives-templates-container--local-sms.library-mode {
29
+ display: flex;
30
+ flex-direction: column;
31
+ flex: 1;
32
+ min-height: 0;
33
+ height: 100%;
34
+
35
+ .component-wrapper {
36
+ display: flex;
37
+ flex-direction: column;
38
+ flex: 1;
39
+ min-height: 0;
40
+ height: 100%;
41
+ }
42
+
43
+ .templates-v2-local-sms-pane {
44
+ display: flex;
45
+ flex-direction: column;
46
+ flex: 1;
47
+ min-height: 0;
48
+ overflow: hidden;
49
+ height: 100%;
50
+ }
51
+
52
+ .templates-v2-local-sms-pane .creatives-templates-list.library-mode {
53
+ display: flex;
54
+ flex-direction: column;
55
+ flex: 1;
56
+ min-height: 0;
57
+ overflow: hidden;
58
+ height: 100%;
59
+ }
60
+
61
+ .templates-v2-local-sms-pane .creatives-templates-list.library-mode > .cap-row:first-of-type {
62
+ flex: 1;
63
+ min-height: 0;
64
+ display: flex;
65
+ flex-direction: column;
66
+ overflow: hidden;
67
+ }
68
+
69
+ .templates-v2-local-sms-pane .creatives-templates-list.library-mode > .cap-row:first-of-type > div {
70
+ flex: 1;
71
+ min-height: 0;
72
+ display: flex;
73
+ flex-direction: column;
74
+ overflow: hidden;
75
+ }
76
+
77
+ .templates-v2-local-sms-pane .creatives-templates-list.library-mode > .cap-row:first-of-type > div > div:first-child {
78
+ flex: 1;
79
+ min-height: 0;
80
+ display: flex;
81
+ flex-direction: column;
82
+ overflow: hidden;
83
+ }
84
+
85
+ .templates-v2-local-sms-pane .v2-pagination-container,
86
+ .templates-v2-local-sms-pane .v2-pagination-container-half {
87
+ /* Match _templates local-SMS: bounded height so overflow-y scroll works (flex+100% alone often doesn’t) */
88
+ flex: 0 1 auto;
89
+ min-height: 0;
90
+ height: calc(100vh - 12rem);
91
+ max-height: calc(100vh - 12rem);
92
+ overflow-y: auto;
93
+ overflow-x: hidden;
94
+ -webkit-overflow-scrolling: touch;
95
+ }
96
+ }
26
97
  `;
27
98
 
28
99
  export const CapTabStyle = css`
@@ -10,8 +10,8 @@ import { connect } from 'react-redux';
10
10
  import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
11
11
  import { createStructuredSelector } from 'reselect';
12
12
  import { bindActionCreators, compose } from 'redux';
13
- import { CapTab, CapCustomCard, CapButton, CapHeader, CapSpin, CapIcon, CapTooltip } from '@capillarytech/cap-ui-library';
14
- import { find, get } from 'lodash';
13
+ import { CapTab, CapCustomCard, CapButton, CapHeader, CapIcon, CapSpin, CapTooltip } from '@capillarytech/cap-ui-library';
14
+ import { find, get, pick } from 'lodash';
15
15
  import Helmet from 'react-helmet';
16
16
 
17
17
  import { UserIsAuthenticated } from '../../utils/authWrapper';
@@ -36,13 +36,14 @@ import { makeSelectAuthenticated, selectCurrentOrgDetails } from "../../v2Contai
36
36
  import {
37
37
  CALL_TASK,
38
38
  COMMON_CHANNELS,
39
+ LOCAL_TEMPLATE_CONFIG_KEYS_FOR_PICK,
39
40
  LOYALTY_SUPPORTED_ACTION,
40
41
  MOBILE_PUSH,
41
42
  NORMALIZED_CHANNEL_ALIASES,
42
43
  SMS,
43
44
  } from "../CreativesContainer/constants";
44
45
 
45
- const {CapCustomCardList} = CapCustomCard;
46
+ const { CapCustomCardList } = CapCustomCard;
46
47
 
47
48
  const StyledCapTab = withStyles(CapTab, CapTabStyle);
48
49
  export class TemplatesV2 extends React.Component { // eslint-disable-line react/prefer-stateless-function
@@ -119,9 +120,9 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
119
120
  return !normalizedChannelsToHideSet.has(paneKey);
120
121
  });
121
122
 
122
- if (isFullMode) {
123
- filteredPanes.push({ content: <div></div>, tab: intl.formatMessage(messages.gallery), key: 'assets' });
124
- } else {
123
+ if (isFullMode && !normalizedChannelsToHideSet.has(normalizeChannel(ASSETS))) {
124
+ filteredPanes.push({ content: <div></div>, tab: intl.formatMessage(messages.gallery), key: ASSETS });
125
+ } else if (!isFullMode) {
125
126
  // Add special-mode panes only when not hidden (use normalized checks)
126
127
  if (!normalizedChannelsToHideSet.has(CALL_TASK.toLowerCase())) {
127
128
  filteredPanes.push({ content: <div></div>, tab: intl.formatMessage(messages.callTask), key: CALL_TASK.toLowerCase() });
@@ -222,7 +223,8 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
222
223
  this.setState({selectedChannel: nextProps.channel, panes });
223
224
  }
224
225
  }
225
- getTemplateDataForGrid = ({templates, handlers, filterContent, channel, isLoading, loadingTip}) => {
226
+
227
+ getTemplateDataForGrid = ({ templates, handlers, filterContent, channel, isLoading, loadingTip }) => {
226
228
  const currentChannel = channel.toUpperCase();
227
229
  const cardDataList = templates.map((template) => {
228
230
  const templateData =
@@ -248,7 +250,8 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
248
250
  </CapSpin>
249
251
 
250
252
  </div>);
251
- }
253
+ };
254
+
252
255
  getGalleryComponent = (location) => <Gallery location={location} isFullMode={this.props.isFullMode}/>
253
256
  getCallTaskComponent = () => (
254
257
  <CallTask
@@ -312,6 +315,29 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
312
315
  if (messageStrategy !== "X_ENGAGE" && channel === 'facebook' && !isFullMode) {
313
316
  return this.getFacebookComponent();
314
317
  }
318
+ const localConfig = this.props.localTemplatesConfig || pick(this.props, LOCAL_TEMPLATE_CONFIG_KEYS_FOR_PICK);
319
+ const useLocalTemplates = localConfig.useLocalTemplates;
320
+ if (useLocalTemplates && channel === (this.props.channel || 'sms')) {
321
+ // Reuse full Templates component (same UI as Redux flow) with local data only
322
+ const location = { pathname: `/${channel}`, search: '', query: !this.props.isFullMode ? { type: 'embedded', module: 'library' } : {} };
323
+ return (
324
+ <Templates
325
+ key={`${channel}-local`}
326
+ location={location}
327
+ route={{ name: channel }}
328
+ router={this.props.router}
329
+ isFullMode={this.props.isFullMode}
330
+ createNew={this.props.createNew}
331
+ onSelectTemplate={this.props.onSelectTemplate}
332
+ handlePeviewTemplate={this.props.handlePeviewTemplate}
333
+ messageStrategy={this.props.messageStrategy}
334
+ smsRegister={this.props.smsRegister}
335
+ hideTestAndPreviewBtn={this.props.hideTestAndPreviewBtn}
336
+ localTemplatesConfig={localConfig}
337
+ />
338
+ );
339
+ }
340
+
315
341
  const location = {pathname: `/${channel}`, search: '', query};
316
342
  switch (channel) {
317
343
  case 'call_task':
@@ -361,29 +387,55 @@ export class TemplatesV2 extends React.Component { // eslint-disable-line react/
361
387
  }
362
388
  render() {
363
389
  const { isFullMode, className, cap = {}, Global = {}} = this.props;
390
+ const useLocalTemplates = get(this.props, 'localTemplatesConfig.useLocalTemplates', false);
364
391
  const { accessiblePermissions = []} = cap.user || Global.user || {};
365
392
  let isCreativeAccessible = true;
366
393
  if (!accessiblePermissions.includes(CREATIVES_UI_VIEW)) {
367
394
  isCreativeAccessible = false;
368
395
  }
396
+ // Recompute active pane content every render so local-list mode updates
397
+ // (templates/loading/search UI) are not stuck with the initial cached pane.
398
+ const panes = this.setChannelContent(this.state.selectedChannel, this.state.panes);
399
+ const hideChannelTabsForLocalSms = useLocalTemplates && panes.length === 1;
400
+ const activeLocalPane = hideChannelTabsForLocalSms
401
+ ? (panes.find(
402
+ (p) => String(p.key).toLowerCase() === String(this.state.selectedChannel).toLowerCase(),
403
+ ) || panes[0])
404
+ : null;
369
405
  return (
370
406
  !isCreativeAccessible ? <AccessForbidden /> : (
371
- <div className={`${className} creatives-templates-container ${isFullMode ? 'fullmode' : 'library-mode'}`} data-testid="cap-wrapper">
372
- {isFullMode && <Helmet
373
- title={this.props.intl.formatMessage(messages.creatives)}
374
- meta={[
375
- { name: 'description', content: this.props.intl.formatMessage(messages.creativesDesc) },
376
- ]}
377
- />}
378
- <div className="component-wrapper">
379
- {isFullMode && <CapHeader title={<FormattedMessage {...messages.creatives}/>} description={<FormattedMessage {...messages.creativesDesc}/>}/>}
380
- <StyledCapTab
381
- panes={this.state.panes}
382
- onChange={this.channelChange}
383
- activeKey={this.state.selectedChannel}
384
- defaultActiveKey={this.state.selectedChannel}
385
- isFullMode={isFullMode}
407
+ <div
408
+ className={`${className} creatives-templates-container ${isFullMode ? 'fullmode' : 'library-mode'}${useLocalTemplates ? ' creatives-templates-container--local-sms' : ''}`}
409
+ data-testid="cap-wrapper"
410
+ >
411
+ {isFullMode && !useLocalTemplates && (
412
+ <Helmet
413
+ title={this.props.intl.formatMessage(messages.creatives)}
414
+ meta={[
415
+ { name: 'description', content: this.props.intl.formatMessage(messages.creativesDesc) },
416
+ ]}
386
417
  />
418
+ )}
419
+ <div className="component-wrapper">
420
+ {isFullMode && (
421
+ <CapHeader
422
+ title={<FormattedMessage {...messages.creatives} />}
423
+ {...(!useLocalTemplates && {
424
+ description: <FormattedMessage {...messages.creativesDesc} />,
425
+ })}
426
+ />
427
+ )}
428
+ {hideChannelTabsForLocalSms ? (
429
+ <div className="templates-v2-local-sms-pane">{activeLocalPane?.content}</div>
430
+ ) : (
431
+ <StyledCapTab
432
+ panes={panes}
433
+ onChange={this.channelChange}
434
+ activeKey={this.state.selectedChannel}
435
+ defaultActiveKey={this.state.selectedChannel}
436
+ isFullMode={isFullMode}
437
+ />
438
+ )}
387
439
  </div>
388
440
  </div>
389
441
  )
@@ -415,6 +467,17 @@ TemplatesV2.propTypes = {
415
467
  currentOrgDetails: PropTypes.object,
416
468
  restrictPersonalization: PropTypes.bool,
417
469
  isAnonymousType: PropTypes.bool,
470
+ // Optional: reuse grid UI with local template list (e.g. SMS fallback). Pass object or same keys as individual props.
471
+ localTemplatesConfig: PropTypes.shape({
472
+ useLocalTemplates: PropTypes.bool,
473
+ localTemplates: PropTypes.arrayOf(PropTypes.object),
474
+ localTemplatesLoading: PropTypes.bool,
475
+ localTemplatesLoadingTip: PropTypes.string,
476
+ localTemplatesFilterContent: PropTypes.node,
477
+ localTemplatesFooterContent: PropTypes.node,
478
+ localTemplatesOnPageChange: PropTypes.func,
479
+ localTemplatesUseSkeleton: PropTypes.bool,
480
+ }),
418
481
  };
419
482
 
420
483
  TemplatesV2.defaultProps = {
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Embedded SMS template list: localTemplatesConfig + SMS-only channel visibility (RCS SMS fallback).
3
+ */
4
+ import React from 'react';
5
+ import { injectIntl } from 'react-intl';
6
+ import '@testing-library/jest-dom';
7
+ import cloneDeep from 'lodash/cloneDeep';
8
+ import { Provider } from 'react-redux';
9
+ import { configureStore } from '@capillarytech/vulcan-react-sdk/utils';
10
+ import history from '../../../utils/history';
11
+ import { initialReducer } from '../../../initialReducer';
12
+ import { render, screen } from '../../../utils/test-utils';
13
+ import { TemplatesV2 } from '../index';
14
+ import { Templates, authData, currentOrgDetails as currentOrgDetailsMock } from './mockData';
15
+ import { CHANNELS_TO_HIDE_FOR_SMS_ONLY } from '../../../v2Components/SmsFallback/constants';
16
+
17
+ const mockTemplates = jest.fn(() => <div data-testid="templates-mock">Templates</div>);
18
+ jest.mock('v2Containers/Templates', () => ({
19
+ __esModule: true,
20
+ default: (props) => mockTemplates(props),
21
+ }));
22
+
23
+ jest.mock('../../../utils/authWrapper', () => ({
24
+ UserIsAuthenticated: jest.fn((config) => config),
25
+ }));
26
+
27
+ const ComponentToRender = injectIntl(TemplatesV2);
28
+ const renderComponent = (p) => {
29
+ const store = configureStore({}, initialReducer, history);
30
+ return render(
31
+ <Provider store={store}>
32
+ <ComponentToRender {...p} />
33
+ </Provider>,
34
+ );
35
+ };
36
+
37
+ describe('TemplatesV2 local SMS templates (embedded)', () => {
38
+ const templateActions = {
39
+ templateActions: jest.fn(),
40
+ deleteTemplate: jest.fn(),
41
+ getAccountsSettings: jest.fn(),
42
+ getAllTemplates: jest.fn(),
43
+ getCdnTransformationConfig: jest.fn(),
44
+ getDefaultBeeTemplates: jest.fn(),
45
+ getSenderDetails: jest.fn(),
46
+ getTemplateDetails: jest.fn(),
47
+ getUserList: jest.fn(),
48
+ getWeCrmAccounts: jest.fn(),
49
+ handleHtmlUpload: jest.fn(),
50
+ handleZipUpload: jest.fn(),
51
+ resetAccount: jest.fn(),
52
+ resetTemplate: jest.fn(),
53
+ resetTemplateData: jest.fn(),
54
+ resetTemplateStoreData: jest.fn(),
55
+ resetUploadData: jest.fn(),
56
+ setBEETemplate: jest.fn(),
57
+ setChannelAccount: jest.fn(),
58
+ setEdmTemplate: jest.fn(),
59
+ setFacebookAccount: jest.fn(),
60
+ setViberAccount: jest.fn(),
61
+ setWeChatAccount: jest.fn(),
62
+ };
63
+
64
+ const baseProps = {
65
+ cap: {
66
+ user: { accessiblePermissions: ['CREATIVES_UI_VIEW'] },
67
+ },
68
+ actions: { defaultAction: jest.fn(), getTemplates: jest.fn() },
69
+ Templates,
70
+ TemplatesList: Templates?.templates,
71
+ authData,
72
+ templateActions,
73
+ isFullMode: false,
74
+ className: 'embed-test',
75
+ channel: 'sms',
76
+ channelsToHide: CHANNELS_TO_HIDE_FOR_SMS_ONLY,
77
+ channelsToDisable: [],
78
+ onChannelChange: jest.fn(),
79
+ enableNewChannels: [],
80
+ /** Without JP_LOCALE_HIDE_FEATURE so SMS panes are not stripped to Email/Line/Gallery only */
81
+ currentOrgDetails: {
82
+ ...currentOrgDetailsMock,
83
+ accessibleFeatures: (currentOrgDetailsMock.accessibleFeatures || []).filter(
84
+ (f) => f !== 'JP_LOCALE_HIDE_FEATURE',
85
+ ),
86
+ },
87
+ location: {
88
+ pathname: 'v2',
89
+ basename: '/creatives/ui/',
90
+ query: {},
91
+ },
92
+ router: { push: jest.fn() },
93
+ };
94
+
95
+ beforeEach(() => {
96
+ mockTemplates.mockClear();
97
+ });
98
+
99
+ it('adds local-sms container class and single-pane layout when only SMS is visible', () => {
100
+ const p = cloneDeep(baseProps);
101
+ p.localTemplatesConfig = {
102
+ useLocalTemplates: true,
103
+ localTemplates: [],
104
+ localTemplatesLoading: false,
105
+ };
106
+ renderComponent(p);
107
+
108
+ const wrapper = screen.getByTestId('cap-wrapper');
109
+ expect(wrapper).toHaveClass('creatives-templates-container--local-sms');
110
+ expect(document.querySelector('.templates-v2-local-sms-pane')).toBeTruthy();
111
+ expect(mockTemplates).toHaveBeenCalled();
112
+ });
113
+
114
+ it('passes localTemplatesConfig into Templates for the SMS pane', () => {
115
+ const localConfig = {
116
+ useLocalTemplates: true,
117
+ localTemplates: [{ _id: '1', name: 'A' }],
118
+ localTemplatesLoading: false,
119
+ };
120
+ const p = cloneDeep(baseProps);
121
+ p.localTemplatesConfig = localConfig;
122
+ renderComponent(p);
123
+
124
+ expect(mockTemplates).toHaveBeenCalled();
125
+ const passed = mockTemplates.mock.calls.find(
126
+ (call) => call[0] && call[0].localTemplatesConfig && call[0].localTemplatesConfig.useLocalTemplates,
127
+ );
128
+ expect(passed).toBeTruthy();
129
+ expect(passed[0].localTemplatesConfig).toMatchObject(localConfig);
130
+ });
131
+ });
@@ -118,6 +118,7 @@ import { ANDROID } from '../../v2Components/CommonTestAndPreview/constants';
118
118
  import CapImageUpload from '../../v2Components/CapImageUpload';
119
119
  import TagList from '../TagList';
120
120
  import { validateTags } from '../../utils/tagValidations';
121
+ import { splitContentByOrderedVarTokens } from '../../utils/templateVarUtils';
121
122
  import { capitalizeString } from '../../utils/Formatter';
122
123
  import CapWhatsappCTA from '../../v2Components/CapWhatsappCTA';
123
124
  import {
@@ -483,28 +484,10 @@ export const Whatsapp = (props) => {
483
484
  );
484
485
  };
485
486
 
486
- const converStringToVarArr = (validVarArr, content) => {
487
- const templateVarArray = [];
488
- while (content?.length !== 0) {
489
- //converting content string to an array split at var
490
- const index = content.indexOf(validVarArr?.[0]);
491
- if (index !== -1) {
492
- templateVarArray.push(content.substring(0, index)); //push string before var
493
- templateVarArray.push(validVarArr?.[0]); //push var
494
- content = content.substring(index + validVarArr?.[0]?.length, content?.length); //remaining str
495
- validVarArr?.shift(); //remove considered var
496
- } else {
497
- templateVarArray.push(content); //remaining str
498
- break;
499
- }
500
- }
501
- return templateVarArray;
502
- }
503
-
504
487
  const computeTextMessage = (msg, varMap, regex) => {
505
488
  const validVarArr = msg?.match(regex) || [];
506
489
  //conerting msg string to variable arr
507
- const templateHeaderArray = converStringToVarArr(validVarArr, msg);
490
+ const templateHeaderArray = splitContentByOrderedVarTokens(validVarArr, msg);
508
491
  if (templateHeaderArray?.length !== 0) {
509
492
  let clonedVarMap = {};
510
493
  if (!isEmpty(varMap)) {
@@ -558,7 +541,7 @@ export const Whatsapp = (props) => {
558
541
  setUnsubscribeRequired(true);
559
542
  }
560
543
  //converting msg string to variable arr
561
- const templateMessageArray = converStringToVarArr(validVarArr, msg);
544
+ const templateMessageArray = splitContentByOrderedVarTokens(validVarArr, msg);
562
545
  updateTempMsgArray(templateMessageArray.filter((i) => i === 0 || i));
563
546
  };
564
547