@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.
- package/constants/unified.js +29 -0
- package/package.json +1 -1
- package/services/tests/api.test.js +13 -0
- package/utils/commonUtils.js +19 -1
- package/utils/rcsPayloadUtils.js +92 -0
- package/utils/templateVarUtils.js +201 -0
- package/utils/tests/templateVarUtils.test.js +204 -0
- package/v2Components/CapActionButton/constants.js +7 -0
- package/v2Components/CapActionButton/index.js +167 -109
- package/v2Components/CapActionButton/index.scss +157 -6
- package/v2Components/CapActionButton/messages.js +19 -3
- package/v2Components/CapActionButton/tests/index.test.js +41 -17
- package/v2Components/CapTagList/index.js +10 -0
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -49
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +207 -21
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +85 -10
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +79 -11
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +160 -15
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js.rej +18 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +341 -76
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +133 -4
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
- package/v2Components/CommonTestAndPreview/constants.js +38 -2
- package/v2Components/CommonTestAndPreview/index.js +676 -186
- package/v2Components/CommonTestAndPreview/messages.js +49 -3
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
- package/v2Components/CommonTestAndPreview/sagas.js +15 -6
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +308 -284
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +231 -65
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
- package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +34 -13
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
- package/v2Components/CommonTestAndPreview/tests/index.test.js +132 -4
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +2 -2
- package/v2Components/FormBuilder/index.js +8 -10
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +87 -0
- package/v2Components/SmsFallback/constants.js +73 -0
- package/v2Components/SmsFallback/index.js +955 -0
- package/v2Components/SmsFallback/index.scss +265 -0
- package/v2Components/SmsFallback/messages.js +78 -0
- package/v2Components/SmsFallback/smsFallbackUtils.js +118 -0
- package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
- package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
- package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
- package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +197 -0
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +277 -0
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
- package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
- package/v2Components/TemplatePreview/_templatePreview.scss +33 -23
- package/v2Components/TemplatePreview/constants.js +2 -0
- package/v2Components/TemplatePreview/index.js +143 -28
- package/v2Components/TemplatePreview/tests/index.test.js +142 -0
- package/v2Components/TestAndPreviewSlidebox/index.js +13 -1
- package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
- package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
- package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
- package/v2Components/VarSegmentMessageEditor/index.js +125 -0
- package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
- package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +43 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +36 -4
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +10 -1
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
- package/v2Containers/CreativesContainer/constants.js +9 -0
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +67 -0
- package/v2Containers/CreativesContainer/index.js +300 -103
- package/v2Containers/CreativesContainer/index.scss +51 -1
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +78 -34
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +79 -16
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +8 -0
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +357 -98
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
- package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
- package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
- package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
- package/v2Containers/Email/reducer.js +3 -11
- package/v2Containers/Email/sagas.js +5 -9
- package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +0 -4
- package/v2Containers/Email/tests/sagas.test.js +3 -21
- package/v2Containers/Rcs/constants.js +119 -8
- package/v2Containers/Rcs/index.js +2379 -807
- package/v2Containers/Rcs/index.js.rej +1336 -0
- package/v2Containers/Rcs/index.scss +276 -6
- package/v2Containers/Rcs/index.scss.rej +74 -0
- package/v2Containers/Rcs/messages.js +38 -3
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +225 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +98018 -70073
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap.rej +128 -0
- package/v2Containers/Rcs/tests/index.test.js +152 -121
- package/v2Containers/Rcs/tests/mockData.js +38 -0
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +318 -0
- package/v2Containers/Rcs/tests/utils.test.js +646 -30
- package/v2Containers/Rcs/utils.js +478 -11
- package/v2Containers/Sms/Create/index.js +100 -40
- package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
- package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
- package/v2Containers/SmsTrai/Create/index.js +9 -4
- package/v2Containers/SmsTrai/Edit/constants.js +2 -0
- package/v2Containers/SmsTrai/Edit/index.js +636 -130
- package/v2Containers/SmsTrai/Edit/index.scss +121 -0
- package/v2Containers/SmsTrai/Edit/messages.js +14 -4
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4328 -2375
- package/v2Containers/SmsWrapper/index.js +37 -8
- package/v2Containers/TagList/index.js +6 -0
- package/v2Containers/Templates/TemplatesActionBar.js +101 -0
- package/v2Containers/Templates/_templates.scss +163 -2
- package/v2Containers/Templates/actions.js +11 -0
- package/v2Containers/Templates/constants.js +2 -0
- package/v2Containers/Templates/index.js +119 -54
- package/v2Containers/Templates/sagas.js +57 -12
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1043 -1079
- package/v2Containers/Templates/tests/sagas.test.js +193 -123
- package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
- package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
- package/v2Containers/TemplatesV2/index.js +86 -23
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
- package/v2Containers/Whatsapp/index.js +3 -20
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
|
@@ -9,7 +9,16 @@ import { render, screen } from '@testing-library/react';
|
|
|
9
9
|
import '@testing-library/jest-dom';
|
|
10
10
|
import { injectIntl, IntlProvider } from 'react-intl';
|
|
11
11
|
import UnifiedPreview from '../../UnifiedPreview';
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
CHANNELS,
|
|
14
|
+
DESKTOP,
|
|
15
|
+
TABLET,
|
|
16
|
+
MOBILE,
|
|
17
|
+
ANDROID,
|
|
18
|
+
IOS,
|
|
19
|
+
PREVIEW_TAB_RCS,
|
|
20
|
+
PREVIEW_TAB_SMS_FALLBACK,
|
|
21
|
+
} from '../../constants';
|
|
13
22
|
import messages from '../../messages';
|
|
14
23
|
|
|
15
24
|
// Convert messages object to format expected by IntlProvider
|
|
@@ -547,6 +556,195 @@ describe('UnifiedPreview', () => {
|
|
|
547
556
|
|
|
548
557
|
expect(screen.getByTestId('rcs-sender-id')).toHaveTextContent('RCS_SENDER');
|
|
549
558
|
});
|
|
559
|
+
|
|
560
|
+
describe('RCS SMS fallback — Test & Preview tabs', () => {
|
|
561
|
+
it('without SMS fallback selected, shows only RCS preview (no RCS+SMS tab layout)', () => {
|
|
562
|
+
const props = {
|
|
563
|
+
...defaultProps,
|
|
564
|
+
channel: CHANNELS.RCS,
|
|
565
|
+
content: { rcsTitle: 'Hello RCS' },
|
|
566
|
+
smsFallbackContent: undefined,
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
const { container } = render(
|
|
570
|
+
<TestWrapper>
|
|
571
|
+
<ComponentToRender {...props} />
|
|
572
|
+
</TestWrapper>
|
|
573
|
+
);
|
|
574
|
+
|
|
575
|
+
expect(container.querySelector('.unified-preview-rcs-tabs')).toBeNull();
|
|
576
|
+
expect(screen.getByTestId('rcs-preview')).toBeInTheDocument();
|
|
577
|
+
expect(screen.queryByTestId('sms-preview')).not.toBeInTheDocument();
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
it('with SMS fallback template body, shows RCS and Fallback SMS tabs', () => {
|
|
581
|
+
const props = {
|
|
582
|
+
...defaultProps,
|
|
583
|
+
channel: CHANNELS.RCS,
|
|
584
|
+
content: { rcsTitle: 'Hello RCS' },
|
|
585
|
+
smsFallbackContent: {
|
|
586
|
+
content: 'SMS fallback body',
|
|
587
|
+
templateContent: 'SMS fallback body',
|
|
588
|
+
},
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
const { container } = render(
|
|
592
|
+
<TestWrapper>
|
|
593
|
+
<ComponentToRender {...props} />
|
|
594
|
+
</TestWrapper>
|
|
595
|
+
);
|
|
596
|
+
|
|
597
|
+
expect(container.querySelector('.unified-preview-rcs-tabs')).toBeInTheDocument();
|
|
598
|
+
expect(
|
|
599
|
+
screen.getByRole('tab', { name: messages.rcsTab.defaultMessage })
|
|
600
|
+
).toBeInTheDocument();
|
|
601
|
+
expect(
|
|
602
|
+
screen.getByRole('tab', { name: messages.smsFallbackTab.defaultMessage })
|
|
603
|
+
).toBeInTheDocument();
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
it('on SMS fallback tab, renders SMS preview with resolved fallback text when smsFallbackResolvedText is set', () => {
|
|
607
|
+
const props = {
|
|
608
|
+
...defaultProps,
|
|
609
|
+
channel: CHANNELS.RCS,
|
|
610
|
+
content: { rcsTitle: 'Hello RCS' },
|
|
611
|
+
smsFallbackContent: {
|
|
612
|
+
content: '{{var}}',
|
|
613
|
+
templateContent: '{{var}}',
|
|
614
|
+
},
|
|
615
|
+
smsFallbackResolvedText: 'Resolved SMS for preview',
|
|
616
|
+
activePreviewTab: PREVIEW_TAB_SMS_FALLBACK,
|
|
617
|
+
onPreviewTabChange: jest.fn(),
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
render(
|
|
621
|
+
<TestWrapper>
|
|
622
|
+
<ComponentToRender {...props} />
|
|
623
|
+
</TestWrapper>
|
|
624
|
+
);
|
|
625
|
+
|
|
626
|
+
expect(screen.getByTestId('sms-content')).toHaveTextContent('Resolved SMS for preview');
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
it('on RCS tab (default), renders RCS preview when dual tabs are shown', () => {
|
|
630
|
+
const props = {
|
|
631
|
+
...defaultProps,
|
|
632
|
+
channel: CHANNELS.RCS,
|
|
633
|
+
content: { rcsTitle: 'Only RCS pane' },
|
|
634
|
+
smsFallbackContent: { content: 'SMS', templateContent: 'SMS' },
|
|
635
|
+
activePreviewTab: PREVIEW_TAB_RCS,
|
|
636
|
+
onPreviewTabChange: jest.fn(),
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
render(
|
|
640
|
+
<TestWrapper>
|
|
641
|
+
<ComponentToRender {...props} />
|
|
642
|
+
</TestWrapper>
|
|
643
|
+
);
|
|
644
|
+
|
|
645
|
+
expect(screen.getByTestId('rcs-preview')).toBeInTheDocument();
|
|
646
|
+
expect(screen.getByTestId('rcs-content')).toHaveTextContent(/Only RCS pane/);
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
it('on SMS fallback tab, shows raw template when no varmap and no resolved text', () => {
|
|
650
|
+
const props = {
|
|
651
|
+
...defaultProps,
|
|
652
|
+
channel: CHANNELS.RCS,
|
|
653
|
+
content: { rcsTitle: 'Hello RCS' },
|
|
654
|
+
smsFallbackContent: {
|
|
655
|
+
content: 'Hello {{name}}',
|
|
656
|
+
templateContent: 'Hello {{name}}',
|
|
657
|
+
// no rcsSmsFallbackVarMapped
|
|
658
|
+
},
|
|
659
|
+
smsFallbackResolvedText: undefined,
|
|
660
|
+
activePreviewTab: PREVIEW_TAB_SMS_FALLBACK,
|
|
661
|
+
onPreviewTabChange: jest.fn(),
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
render(
|
|
665
|
+
<TestWrapper>
|
|
666
|
+
<ComponentToRender {...props} />
|
|
667
|
+
</TestWrapper>
|
|
668
|
+
);
|
|
669
|
+
|
|
670
|
+
// rawFallbackTemplate is shown directly — {{tags}} remain visible
|
|
671
|
+
expect(screen.getByTestId('sms-content')).toHaveTextContent('Hello {{name}}');
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
it('on SMS fallback tab, treats empty resolved text as absent and shows raw template', () => {
|
|
675
|
+
const props = {
|
|
676
|
+
...defaultProps,
|
|
677
|
+
channel: CHANNELS.RCS,
|
|
678
|
+
content: { rcsTitle: 'Hello RCS' },
|
|
679
|
+
smsFallbackContent: {
|
|
680
|
+
content: 'Raw {{var}} template',
|
|
681
|
+
templateContent: 'Raw {{var}} template',
|
|
682
|
+
},
|
|
683
|
+
smsFallbackResolvedText: '',
|
|
684
|
+
activePreviewTab: PREVIEW_TAB_SMS_FALLBACK,
|
|
685
|
+
onPreviewTabChange: jest.fn(),
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
render(
|
|
689
|
+
<TestWrapper>
|
|
690
|
+
<ComponentToRender {...props} />
|
|
691
|
+
</TestWrapper>
|
|
692
|
+
);
|
|
693
|
+
|
|
694
|
+
expect(screen.getByTestId('sms-content')).toHaveTextContent('Raw {{var}} template');
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
it('on SMS fallback tab, applies varmap slot substitution when varmap entries exist and no resolved text', () => {
|
|
698
|
+
// getFallbackResolvedContent key format: `${fullToken}_${segmentIndex}`
|
|
699
|
+
// 'Hello {{name}}' → segments ['Hello ', '{{name}}'], so {{name}} is at index 1 → key '{{name}}_1'
|
|
700
|
+
const props = {
|
|
701
|
+
...defaultProps,
|
|
702
|
+
channel: CHANNELS.RCS,
|
|
703
|
+
content: { rcsTitle: 'Hello RCS' },
|
|
704
|
+
smsFallbackContent: {
|
|
705
|
+
content: 'Hello {{name}}',
|
|
706
|
+
templateContent: 'Hello {{name}}',
|
|
707
|
+
rcsSmsFallbackVarMapped: { '{{name}}_1': 'World' },
|
|
708
|
+
},
|
|
709
|
+
smsFallbackResolvedText: undefined,
|
|
710
|
+
activePreviewTab: PREVIEW_TAB_SMS_FALLBACK,
|
|
711
|
+
onPreviewTabChange: jest.fn(),
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
render(
|
|
715
|
+
<TestWrapper>
|
|
716
|
+
<ComponentToRender {...props} />
|
|
717
|
+
</TestWrapper>
|
|
718
|
+
);
|
|
719
|
+
|
|
720
|
+
expect(screen.getByTestId('sms-content')).toHaveTextContent('Hello World');
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
it('on SMS fallback tab, resolved text takes priority over varmap entries', () => {
|
|
724
|
+
const props = {
|
|
725
|
+
...defaultProps,
|
|
726
|
+
channel: CHANNELS.RCS,
|
|
727
|
+
content: { rcsTitle: 'Hello RCS' },
|
|
728
|
+
smsFallbackContent: {
|
|
729
|
+
content: 'Hello {{name}}',
|
|
730
|
+
templateContent: 'Hello {{name}}',
|
|
731
|
+
rcsSmsFallbackVarMapped: { '{{name}}_1': 'World' },
|
|
732
|
+
},
|
|
733
|
+
smsFallbackResolvedText: 'Hello John',
|
|
734
|
+
activePreviewTab: PREVIEW_TAB_SMS_FALLBACK,
|
|
735
|
+
onPreviewTabChange: jest.fn(),
|
|
736
|
+
};
|
|
737
|
+
|
|
738
|
+
render(
|
|
739
|
+
<TestWrapper>
|
|
740
|
+
<ComponentToRender {...props} />
|
|
741
|
+
</TestWrapper>
|
|
742
|
+
);
|
|
743
|
+
|
|
744
|
+
// resolvedText wins over varmap substitution
|
|
745
|
+
expect(screen.getByTestId('sms-content')).toHaveTextContent('Hello John');
|
|
746
|
+
});
|
|
747
|
+
});
|
|
550
748
|
});
|
|
551
749
|
|
|
552
750
|
describe('Channel Routing - INAPP', () => {
|
|
@@ -30,23 +30,28 @@ jest.mock('@capillarytech/cap-ui-library/CapNotification', () => ({
|
|
|
30
30
|
}));
|
|
31
31
|
|
|
32
32
|
// Mock child components - must use React.createElement to avoid hoisting issues
|
|
33
|
+
let lastLeftPanelContentProps = null;
|
|
33
34
|
jest.mock('../LeftPanelContent', () => {
|
|
34
35
|
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
|
|
35
36
|
const ReactLib = require('react');
|
|
36
37
|
return {
|
|
37
38
|
__esModule: true,
|
|
38
|
-
default: function MockLeftPanelContent() {
|
|
39
|
-
|
|
39
|
+
default: function MockLeftPanelContent(props) {
|
|
40
|
+
lastLeftPanelContentProps = props;
|
|
41
|
+
const editorEl = props.renderCustomValuesEditor ? props.renderCustomValuesEditor() : null;
|
|
42
|
+
return ReactLib.createElement('div', { 'data-testid': 'left-panel' }, 'Left Panel', editorEl);
|
|
40
43
|
},
|
|
41
44
|
};
|
|
42
45
|
});
|
|
43
46
|
|
|
47
|
+
let lastCustomValuesEditorProps = null;
|
|
44
48
|
jest.mock('../CustomValuesEditor', () => {
|
|
45
49
|
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
|
|
46
50
|
const ReactLib = require('react');
|
|
47
51
|
return {
|
|
48
52
|
__esModule: true,
|
|
49
|
-
default: function MockCustomValuesEditor() {
|
|
53
|
+
default: function MockCustomValuesEditor(props) {
|
|
54
|
+
lastCustomValuesEditorProps = props;
|
|
50
55
|
return ReactLib.createElement('div', { 'data-testid': 'custom-values-editor' }, 'Custom Values Editor');
|
|
51
56
|
},
|
|
52
57
|
};
|
|
@@ -191,6 +196,8 @@ describe('CommonTestAndPreview', () => {
|
|
|
191
196
|
beforeEach(() => {
|
|
192
197
|
jest.clearAllMocks();
|
|
193
198
|
lastSendTestMessageProps = null;
|
|
199
|
+
lastLeftPanelContentProps = null;
|
|
200
|
+
lastCustomValuesEditorProps = null;
|
|
194
201
|
// Reset all mock function implementations
|
|
195
202
|
Object.values(mockActions).forEach((mockFn) => {
|
|
196
203
|
if (jest.isMockFunction(mockFn)) {
|
|
@@ -245,6 +252,24 @@ describe('CommonTestAndPreview', () => {
|
|
|
245
252
|
});
|
|
246
253
|
});
|
|
247
254
|
|
|
255
|
+
it('should call getSenderDetailsRequested for RCS and SMS when channel is RCS', async () => {
|
|
256
|
+
render(
|
|
257
|
+
<TestWrapper>
|
|
258
|
+
<CommonTestAndPreview {...defaultProps} channel={CHANNELS.RCS} />
|
|
259
|
+
</TestWrapper>
|
|
260
|
+
);
|
|
261
|
+
await waitFor(() => {
|
|
262
|
+
expect(mockActions.getSenderDetailsRequested).toHaveBeenCalledWith({
|
|
263
|
+
channel: CHANNELS.RCS,
|
|
264
|
+
orgUnitId: -1,
|
|
265
|
+
});
|
|
266
|
+
expect(mockActions.getSenderDetailsRequested).toHaveBeenCalledWith({
|
|
267
|
+
channel: CHANNELS.SMS,
|
|
268
|
+
orgUnitId: -1,
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
|
|
248
273
|
it('should not call getSenderDetailsRequested when channel is INAPP', async () => {
|
|
249
274
|
render(
|
|
250
275
|
<TestWrapper>
|
|
@@ -303,7 +328,7 @@ describe('CommonTestAndPreview', () => {
|
|
|
303
328
|
});
|
|
304
329
|
expect(lastSendTestMessageProps).toBeDefined();
|
|
305
330
|
expect(lastSendTestMessageProps.deliverySettings).toBeDefined();
|
|
306
|
-
expect(lastSendTestMessageProps.
|
|
331
|
+
expect(lastSendTestMessageProps.senderDetailsByChannel).toEqual(senderDetailsByChannel);
|
|
307
332
|
expect(lastSendTestMessageProps.wecrmAccounts).toEqual([]);
|
|
308
333
|
expect(typeof lastSendTestMessageProps.onSaveDeliverySettings).toBe('function');
|
|
309
334
|
expect(lastSendTestMessageProps.isLoadingSenderDetails).toBe(false);
|
|
@@ -3281,4 +3306,107 @@ describe('CommonTestAndPreview', () => {
|
|
|
3281
3306
|
});
|
|
3282
3307
|
});
|
|
3283
3308
|
});
|
|
3309
|
+
|
|
3310
|
+
describe('SMS DLT and mustache tag discrimination (smsTemplateHasMustacheTags / buildSyntheticSmsMustacheTags)', () => {
|
|
3311
|
+
it('should return no tags for SMS content that contains only DLT {#var#} tokens', async () => {
|
|
3312
|
+
render(
|
|
3313
|
+
<TestWrapper>
|
|
3314
|
+
<CommonTestAndPreview
|
|
3315
|
+
{...defaultProps}
|
|
3316
|
+
channel={CHANNELS.SMS}
|
|
3317
|
+
formData={{ 0: { 'sms-editor': 'Order {#orderId#} is confirmed' } }}
|
|
3318
|
+
extractedTags={[]}
|
|
3319
|
+
/>
|
|
3320
|
+
</TestWrapper>
|
|
3321
|
+
);
|
|
3322
|
+
|
|
3323
|
+
await waitFor(() => expect(lastLeftPanelContentProps).not.toBeNull());
|
|
3324
|
+
|
|
3325
|
+
// smsTemplateHasMustacheTags returns false for DLT-only → extractedTags is []
|
|
3326
|
+
expect(lastLeftPanelContentProps.extractedTags).toEqual([]);
|
|
3327
|
+
});
|
|
3328
|
+
|
|
3329
|
+
it('should build synthetic tags from {{mustache}} tokens, excluding DLT {#var#} tokens', async () => {
|
|
3330
|
+
render(
|
|
3331
|
+
<TestWrapper>
|
|
3332
|
+
<CommonTestAndPreview
|
|
3333
|
+
{...defaultProps}
|
|
3334
|
+
channel={CHANNELS.SMS}
|
|
3335
|
+
formData={{ 0: { 'sms-editor': 'Hi {{name}}, order {#orderId#} shipped' } }}
|
|
3336
|
+
extractedTags={[]}
|
|
3337
|
+
/>
|
|
3338
|
+
</TestWrapper>
|
|
3339
|
+
);
|
|
3340
|
+
|
|
3341
|
+
await waitFor(() => expect(lastLeftPanelContentProps).not.toBeNull());
|
|
3342
|
+
|
|
3343
|
+
const tags = lastLeftPanelContentProps.extractedTags;
|
|
3344
|
+
// Only {{name}} should become a tag — {#orderId#} must be excluded
|
|
3345
|
+
expect(tags).toHaveLength(1);
|
|
3346
|
+
expect(tags[0].name).toBe('name');
|
|
3347
|
+
});
|
|
3348
|
+
|
|
3349
|
+
it('should use API-extracted tags when present instead of building synthetic ones', async () => {
|
|
3350
|
+
const apiTags = [{ name: 'firstName', metaData: { userDriven: true }, children: [] }];
|
|
3351
|
+
render(
|
|
3352
|
+
<TestWrapper>
|
|
3353
|
+
<CommonTestAndPreview
|
|
3354
|
+
{...defaultProps}
|
|
3355
|
+
channel={CHANNELS.SMS}
|
|
3356
|
+
formData={{ 0: { 'sms-editor': 'Hi {{firstName}}' } }}
|
|
3357
|
+
extractedTags={apiTags}
|
|
3358
|
+
/>
|
|
3359
|
+
</TestWrapper>
|
|
3360
|
+
);
|
|
3361
|
+
|
|
3362
|
+
await waitFor(() => expect(lastLeftPanelContentProps).not.toBeNull());
|
|
3363
|
+
|
|
3364
|
+
// API tags take priority over buildSyntheticSmsMustacheTags
|
|
3365
|
+
expect(lastLeftPanelContentProps.extractedTags).toEqual(apiTags);
|
|
3366
|
+
});
|
|
3367
|
+
});
|
|
3368
|
+
|
|
3369
|
+
describe('handleDiscardCustomValues — preview reset', () => {
|
|
3370
|
+
it('should call updatePreviewRequested when handleDiscardCustomValues is invoked', async () => {
|
|
3371
|
+
render(
|
|
3372
|
+
<TestWrapper>
|
|
3373
|
+
<CommonTestAndPreview
|
|
3374
|
+
{...defaultProps}
|
|
3375
|
+
channel={CHANNELS.SMS}
|
|
3376
|
+
formData={{ 0: { 'sms-editor': 'Hi {{name}}' } }}
|
|
3377
|
+
/>
|
|
3378
|
+
</TestWrapper>
|
|
3379
|
+
);
|
|
3380
|
+
|
|
3381
|
+
await waitFor(() => expect(lastCustomValuesEditorProps).not.toBeNull());
|
|
3382
|
+
|
|
3383
|
+
lastCustomValuesEditorProps.handleDiscardCustomValues();
|
|
3384
|
+
|
|
3385
|
+
expect(mockActions.updatePreviewRequested).toHaveBeenCalledTimes(1);
|
|
3386
|
+
});
|
|
3387
|
+
|
|
3388
|
+
it('should call updatePreviewRequested with RCS channel when handleDiscardCustomValues is invoked for RCS with fallback', async () => {
|
|
3389
|
+
render(
|
|
3390
|
+
<TestWrapper>
|
|
3391
|
+
<CommonTestAndPreview
|
|
3392
|
+
{...defaultProps}
|
|
3393
|
+
channel={CHANNELS.RCS}
|
|
3394
|
+
smsFallbackContent={{
|
|
3395
|
+
templateContent: 'Fallback {{name}}',
|
|
3396
|
+
content: 'Fallback {{name}}',
|
|
3397
|
+
}}
|
|
3398
|
+
formData={{}}
|
|
3399
|
+
/>
|
|
3400
|
+
</TestWrapper>
|
|
3401
|
+
);
|
|
3402
|
+
|
|
3403
|
+
await waitFor(() => expect(lastCustomValuesEditorProps).not.toBeNull());
|
|
3404
|
+
|
|
3405
|
+
lastCustomValuesEditorProps.handleDiscardCustomValues();
|
|
3406
|
+
|
|
3407
|
+
// updatePreviewRequested is called; syncSmsFallbackPreview (which hits Api directly) is NOT called
|
|
3408
|
+
expect(mockActions.updatePreviewRequested).toHaveBeenCalledTimes(1);
|
|
3409
|
+
});
|
|
3410
|
+
});
|
|
3284
3411
|
});
|
|
3412
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {
|
|
2
|
+
normalizePreviewApiPayload,
|
|
3
|
+
extractPreviewFromLiquidResponse,
|
|
4
|
+
getSmsFallbackTextForTagExtraction,
|
|
5
|
+
} from '../previewApiUtils';
|
|
6
|
+
import { RCS_SMS_FALLBACK_VAR_MAPPED_PROP } from '../constants';
|
|
7
|
+
|
|
8
|
+
describe('previewApiUtils', () => {
|
|
9
|
+
describe('normalizePreviewApiPayload', () => {
|
|
10
|
+
it('maps messageBody to resolvedBody when resolvedBody is missing', () => {
|
|
11
|
+
expect(normalizePreviewApiPayload({ messageBody: 'Hello' }).resolvedBody).toBe('Hello');
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe('extractPreviewFromLiquidResponse', () => {
|
|
16
|
+
it('accepts nested data shape', () => {
|
|
17
|
+
const r = { data: { resolvedBody: 'ok' } };
|
|
18
|
+
expect(extractPreviewFromLiquidResponse(r)).toEqual({ resolvedBody: 'ok' });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('accepts top-level preview (typical SMS)', () => {
|
|
22
|
+
const r = { messageBody: 'sms text' };
|
|
23
|
+
expect(extractPreviewFromLiquidResponse(r)).toMatchObject({
|
|
24
|
+
messageBody: 'sms text',
|
|
25
|
+
resolvedBody: 'sms text',
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('returns null when errors array is non-empty', () => {
|
|
30
|
+
expect(extractPreviewFromLiquidResponse({ errors: [{ code: 1 }] })).toBeNull();
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('getSmsFallbackTextForTagExtraction', () => {
|
|
35
|
+
it('returns empty string when context is missing or has no template body', () => {
|
|
36
|
+
expect(getSmsFallbackTextForTagExtraction(null)).toBe('');
|
|
37
|
+
expect(getSmsFallbackTextForTagExtraction({})).toBe('');
|
|
38
|
+
expect(getSmsFallbackTextForTagExtraction({ templateContent: '', content: '' })).toBe('');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('returns raw template when rcsSmsFallbackVarMapped is absent or empty', () => {
|
|
42
|
+
const raw = '{{optout}} {{fullname}} test SMS';
|
|
43
|
+
expect(getSmsFallbackTextForTagExtraction({ templateContent: raw })).toBe(raw);
|
|
44
|
+
expect(getSmsFallbackTextForTagExtraction({
|
|
45
|
+
content: raw,
|
|
46
|
+
[RCS_SMS_FALLBACK_VAR_MAPPED_PROP]: {},
|
|
47
|
+
})).toBe(raw);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('prefers templateContent over content when both are set', () => {
|
|
51
|
+
expect(getSmsFallbackTextForTagExtraction({
|
|
52
|
+
templateContent: 'A',
|
|
53
|
+
content: 'B',
|
|
54
|
+
})).toBe('A');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('applies VarSegment slot map so preview / meta payloads are not stale vs raw template', () => {
|
|
58
|
+
const rawTemplateWithSlots = '{{optout}} tail';
|
|
59
|
+
const rcsSmsFallbackVarMappedSlots = { '{{optout}}_0': 'STOP' };
|
|
60
|
+
const resolvedFallbackText = getSmsFallbackTextForTagExtraction({
|
|
61
|
+
templateContent: rawTemplateWithSlots,
|
|
62
|
+
[RCS_SMS_FALLBACK_VAR_MAPPED_PROP]: rcsSmsFallbackVarMappedSlots,
|
|
63
|
+
});
|
|
64
|
+
expect(resolvedFallbackText).toBe('STOP tail');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
-
call, put, takeLatest, all,
|
|
8
|
+
call, put, takeLatest, takeEvery, all,
|
|
9
9
|
} from 'redux-saga/effects';
|
|
10
10
|
import {
|
|
11
11
|
searchCustomersSaga,
|
|
@@ -1333,7 +1333,7 @@ describe('CommonTestAndPreview Sagas', () => {
|
|
|
1333
1333
|
it('should watch for GET_SENDER_DETAILS_REQUESTED', () => {
|
|
1334
1334
|
const generator = watchGetSenderDetails();
|
|
1335
1335
|
expect(generator.next().value).toEqual(
|
|
1336
|
-
|
|
1336
|
+
takeEvery('app/CommonTestAndPreview/GET_SENDER_DETAILS_REQUESTED', getSenderDetailsSaga)
|
|
1337
1337
|
);
|
|
1338
1338
|
expect(generator.next().done).toBe(true);
|
|
1339
1339
|
});
|
|
@@ -37,6 +37,7 @@ import { createStructuredSelector } from 'reselect';
|
|
|
37
37
|
import { CAP_SPACE_12, CAP_SPACE_08, FONT_COLOR_05, FONT_COLOR_04 } from '@capillarytech/cap-ui-library/styled/variables';
|
|
38
38
|
import UnifiedPreview from '../CommonTestAndPreview/UnifiedPreview';
|
|
39
39
|
import { ANDROID } from '../CommonTestAndPreview/constants';
|
|
40
|
+
import TemplatePreview from '../TemplatePreview';
|
|
40
41
|
import TagList from '../../v2Containers/TagList';
|
|
41
42
|
import CapTagListWithInput from '../CapTagListWithInput';
|
|
42
43
|
import SlideBox from '../SlideBox';
|
|
@@ -2990,7 +2991,6 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
2990
2991
|
selectedOfferDetails={this.props.selectedOfferDetails}
|
|
2991
2992
|
eventContextTags={this.props?.eventContextTags}
|
|
2992
2993
|
restrictPersonalization={this.props.restrictPersonalization}
|
|
2993
|
-
waitEventContextTags={this.props?.waitEventContextTags}
|
|
2994
2994
|
/>
|
|
2995
2995
|
</CapColumn>
|
|
2996
2996
|
);
|
|
@@ -3020,7 +3020,6 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3020
3020
|
userLocale={this.props.userLocale}
|
|
3021
3021
|
selectedOfferDetails={this.props.selectedOfferDetails}
|
|
3022
3022
|
eventContextTags={this.props?.eventContextTags}
|
|
3023
|
-
waitEventContextTags={this.props?.waitEventContextTags}
|
|
3024
3023
|
moduleFilterEnabled={this.props.location && this.props.location.query && this.props.location.query.type !== 'embedded'}
|
|
3025
3024
|
containerStyle={val.style || {}}
|
|
3026
3025
|
inputProps={val.inputProps || {}}
|
|
@@ -3660,7 +3659,9 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3660
3659
|
channel={channel}
|
|
3661
3660
|
eventContextTags={this.props?.eventContextTags}
|
|
3662
3661
|
restrictPersonalization={this.props.restrictPersonalization}
|
|
3663
|
-
|
|
3662
|
+
getPopupContainer={this.props.tagListGetPopupContainer}
|
|
3663
|
+
popoverOverlayStyle={this.props.tagListPopoverOverlayStyle}
|
|
3664
|
+
popoverOverlayClassName={this.props.tagListPopoverOverlayClassName}
|
|
3664
3665
|
/>
|
|
3665
3666
|
</CapColumn>
|
|
3666
3667
|
);
|
|
@@ -3707,7 +3708,6 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
3707
3708
|
userLocale={this.state.translationLang}
|
|
3708
3709
|
selectedOfferDetails={this.props.selectedOfferDetails}
|
|
3709
3710
|
eventContextTags={this.props?.eventContextTags}
|
|
3710
|
-
waitEventContextTags={this.props?.waitEventContextTags}
|
|
3711
3711
|
moduleFilterEnabled={moduleFilterEnabledForCapTagList}
|
|
3712
3712
|
containerStyle={val.style || {}}
|
|
3713
3713
|
inputProps={val.inputProps || {}}
|
|
@@ -4002,9 +4002,6 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
|
|
|
4002
4002
|
onContextChange={this.props.onContextChange}
|
|
4003
4003
|
moduleFilterEnabled={isModuleFilterEnabled}
|
|
4004
4004
|
eventContextTags={this.props?.eventContextTags}
|
|
4005
|
-
waitEventContextTags={this.props?.waitEventContextTags}
|
|
4006
|
-
isGetBeeData={this.props?.isGetBeeData}
|
|
4007
|
-
getBEEData={this.props?.getBEEData}
|
|
4008
4005
|
/>
|
|
4009
4006
|
</CapColumn>
|
|
4010
4007
|
);
|
|
@@ -4307,7 +4304,6 @@ FormBuilder.defaultProps = {
|
|
|
4307
4304
|
userLocale: localStorage.getItem('jlocale') || 'en',
|
|
4308
4305
|
showLiquidErrorInFooter: () => {},
|
|
4309
4306
|
metaDataStatus: "",
|
|
4310
|
-
waitEventContextTags: {},
|
|
4311
4307
|
isTestAndPreviewMode: false, // Default to false to maintain existing behavior
|
|
4312
4308
|
};
|
|
4313
4309
|
|
|
@@ -4356,13 +4352,15 @@ FormBuilder.propTypes = {
|
|
|
4356
4352
|
type: PropTypes.string.isRequired,
|
|
4357
4353
|
isEmailLoading: PropTypes.bool.isRequired,
|
|
4358
4354
|
moduleType: PropTypes.string.isRequired,
|
|
4359
|
-
showLiquidErrorInFooter: PropTypes.
|
|
4355
|
+
showLiquidErrorInFooter: PropTypes.func.isRequired,
|
|
4360
4356
|
eventContextTags: PropTypes.array.isRequired,
|
|
4361
|
-
waitEventContextTags: PropTypes.object,
|
|
4362
4357
|
forwardedTags: PropTypes.object.isRequired,
|
|
4363
4358
|
isLoyaltyModule: PropTypes.bool.isRequired,
|
|
4364
4359
|
isTestAndPreviewMode: PropTypes.bool, // Add new prop type
|
|
4365
4360
|
restrictPersonalization: PropTypes.bool,
|
|
4361
|
+
tagListGetPopupContainer: PropTypes.func,
|
|
4362
|
+
tagListPopoverOverlayStyle: PropTypes.object,
|
|
4363
|
+
tagListPopoverOverlayClassName: PropTypes.string,
|
|
4366
4364
|
};
|
|
4367
4365
|
|
|
4368
4366
|
const mapStateToProps = createStructuredSelector({
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import CreativesContainer from '../../v2Containers/CreativesContainer';
|
|
4
|
+
import CapPageSpinner from '../CapPageSpinner';
|
|
5
|
+
import {
|
|
6
|
+
EMBEDDED_SMS_CREATIVES_LOCATION,
|
|
7
|
+
SMS_FALLBACK_CHANNEL_KEY,
|
|
8
|
+
SMS_FALLBACK_CREATIVE_EDITOR,
|
|
9
|
+
SMS_FALLBACK_ENABLE_NEW_CHANNELS,
|
|
10
|
+
} from './constants';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Reuse the exact embedded CreativesContainer SMS flow (same as normal SMS create).
|
|
14
|
+
* Avoid overriding Templates with local config so "Create new" follows built-in createTemplate path.
|
|
15
|
+
*/
|
|
16
|
+
export function SmsFallbackLocalSelector({
|
|
17
|
+
hidden,
|
|
18
|
+
fetchDetailsLoading,
|
|
19
|
+
templateList,
|
|
20
|
+
channelsToHide,
|
|
21
|
+
smsRegister,
|
|
22
|
+
onCloseCreatives,
|
|
23
|
+
onSelectTemplate,
|
|
24
|
+
filterContent,
|
|
25
|
+
location,
|
|
26
|
+
/** Required when user completes embedded SMS create/edit — `CreativesContainer` calls this on save (see `processCentralCommsMetaId`). */
|
|
27
|
+
getCreativesData,
|
|
28
|
+
}) {
|
|
29
|
+
const rootClassName = [
|
|
30
|
+
'sms-fallback-selector',
|
|
31
|
+
hidden ? 'sms-fallback-selector--visually-hidden' : '',
|
|
32
|
+
]
|
|
33
|
+
.filter(Boolean)
|
|
34
|
+
.join(' ');
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div className={rootClassName}>
|
|
38
|
+
{fetchDetailsLoading && (
|
|
39
|
+
<div className="sms-fallback-selector__loading" aria-busy="true" aria-live="polite">
|
|
40
|
+
<CapPageSpinner spinning />
|
|
41
|
+
</div>
|
|
42
|
+
)}
|
|
43
|
+
<CreativesContainer
|
|
44
|
+
creativesMode="create"
|
|
45
|
+
location={location || EMBEDDED_SMS_CREATIVES_LOCATION}
|
|
46
|
+
templateData={null}
|
|
47
|
+
handleCloseCreatives={onCloseCreatives}
|
|
48
|
+
isFullMode={false}
|
|
49
|
+
smsRegister={smsRegister}
|
|
50
|
+
editor={SMS_FALLBACK_CREATIVE_EDITOR}
|
|
51
|
+
enableNewChannels={SMS_FALLBACK_ENABLE_NEW_CHANNELS}
|
|
52
|
+
channel={SMS_FALLBACK_CHANNEL_KEY}
|
|
53
|
+
channelsToHide={channelsToHide}
|
|
54
|
+
selectedBadges={[]}
|
|
55
|
+
localTemplatesConfig={{
|
|
56
|
+
useLocalTemplates: true,
|
|
57
|
+
localTemplates: templateList.templates,
|
|
58
|
+
localTemplatesLoading: templateList.loading,
|
|
59
|
+
localTemplatesFilterContent: filterContent,
|
|
60
|
+
localTemplatesOnPageChange: templateList.loadMore,
|
|
61
|
+
localTemplatesUseSkeleton: true,
|
|
62
|
+
}}
|
|
63
|
+
onSelectTemplate={onSelectTemplate}
|
|
64
|
+
getCreativesData={getCreativesData}
|
|
65
|
+
/>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
SmsFallbackLocalSelector.propTypes = {
|
|
71
|
+
hidden: PropTypes.bool,
|
|
72
|
+
fetchDetailsLoading: PropTypes.bool,
|
|
73
|
+
templateList: PropTypes.shape({
|
|
74
|
+
templates: PropTypes.array,
|
|
75
|
+
loading: PropTypes.bool,
|
|
76
|
+
loadMore: PropTypes.func,
|
|
77
|
+
}).isRequired,
|
|
78
|
+
channelsToHide: PropTypes.arrayOf(PropTypes.string),
|
|
79
|
+
smsRegister: PropTypes.any,
|
|
80
|
+
onCloseCreatives: PropTypes.func.isRequired,
|
|
81
|
+
onSelectTemplate: PropTypes.func.isRequired,
|
|
82
|
+
filterContent: PropTypes.node,
|
|
83
|
+
location: PropTypes.object,
|
|
84
|
+
getCreativesData: PropTypes.func.isRequired,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export default SmsFallbackLocalSelector;
|