@capillarytech/creatives-library 9.0.15-alpha.3 → 9.0.16-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AppRoot.js +80 -0
- package/app.js +5 -3
- package/constants/unified.js +0 -29
- package/entry.js +67 -1
- package/global-styles.js +1 -1
- package/mfe-exposed-components.js +2 -4
- package/package.json +2 -2
- package/services/api.js +1 -1
- package/services/tests/api.test.js +20 -35
- package/styles/containers/layout/_layoutPage.scss +8 -6
- package/utils/commonUtils.js +1 -19
- package/utils/getDataLayer.js +15 -0
- package/utils/gtmTrackers/gtmEvents/creativeDetails.js +2 -1
- package/utils/mfeDetect.js +1 -0
- package/v2Components/CapActionButton/constants.js +1 -8
- package/v2Components/CapActionButton/index.js +144 -202
- package/v2Components/CapActionButton/index.scss +14 -232
- package/v2Components/CapActionButton/messages.js +3 -19
- package/v2Components/CapActionButton/tests/index.test.js +18 -42
- package/v2Components/CapImageUpload/index.js +2 -2
- package/v2Components/CapTagList/index.js +0 -10
- package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +48 -70
- package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +2 -8
- package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +21 -214
- package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +0 -16
- package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +9 -83
- package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +0 -30
- package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +11 -60
- package/v2Components/CommonTestAndPreview/SendTestMessage.js +5 -10
- package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +22 -202
- package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +80 -381
- package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +8 -155
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +0 -11
- package/v2Components/CommonTestAndPreview/constants.js +2 -38
- package/v2Components/CommonTestAndPreview/index.js +222 -761
- package/v2Components/CommonTestAndPreview/messages.js +3 -45
- package/v2Components/CommonTestAndPreview/sagas.js +6 -25
- package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +0 -1
- package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +267 -284
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +65 -231
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +5 -118
- package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +0 -341
- package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +1 -8
- package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +13 -34
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +283 -281
- package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +1 -199
- package/v2Components/CommonTestAndPreview/tests/index.test.js +4 -133
- package/v2Components/CommonTestAndPreview/tests/sagas.test.js +24 -31
- package/v2Components/NavigationBar/index.js +9 -7
- package/v2Components/NavigationBar/mfeModuleHeader.config.js +16 -0
- package/v2Components/Pagination/_pagination.scss +2 -0
- package/v2Components/TemplatePreview/_templatePreview.scss +25 -45
- package/v2Components/TemplatePreview/index.js +32 -147
- package/v2Components/TemplatePreview/tests/index.test.js +0 -142
- package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +6 -6
- package/v2Components/TestAndPreviewSlidebox/index.js +1 -13
- package/v2Components/TestAndPreviewSlidebox/sagas.js +4 -11
- package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +1 -3
- package/v2Containers/Cap/constants.js +1 -0
- package/v2Containers/Cap/index.js +47 -21
- package/v2Containers/Cap/sagas.js +19 -0
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +1 -4
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +0 -4
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +20 -20
- package/v2Containers/CreativesContainer/SlideBoxContent.js +7 -37
- package/v2Containers/CreativesContainer/SlideBoxFooter.js +5 -14
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +5 -36
- package/v2Containers/CreativesContainer/constants.js +0 -11
- package/v2Containers/CreativesContainer/index.js +104 -323
- package/v2Containers/CreativesContainer/index.scss +1 -83
- package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +34 -79
- package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +16 -79
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -8
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +90 -333
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +15 -20
- package/v2Containers/CreativesContainer/tests/index.test.js +9 -71
- package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
- package/v2Containers/Rcs/constants.js +11 -131
- package/v2Containers/Rcs/index.js +829 -2682
- package/v2Containers/Rcs/index.scss +8 -443
- package/v2Containers/Rcs/messages.js +6 -45
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +39409 -74176
- package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +74 -10
- package/v2Containers/Rcs/tests/index.test.js +40 -151
- package/v2Containers/Rcs/tests/mockData.js +0 -38
- package/v2Containers/Rcs/tests/utils.test.js +30 -643
- package/v2Containers/Rcs/utils.js +12 -466
- package/v2Containers/Sms/Create/index.js +48 -115
- package/v2Containers/SmsTrai/Create/index.js +4 -9
- package/v2Containers/SmsTrai/Edit/constants.js +0 -2
- package/v2Containers/SmsTrai/Edit/index.js +169 -678
- package/v2Containers/SmsTrai/Edit/messages.js +4 -14
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +2854 -5455
- package/v2Containers/SmsWrapper/index.js +8 -37
- package/v2Containers/TagList/index.js +0 -6
- package/v2Containers/Templates/_templates.scss +11 -196
- package/v2Containers/Templates/actions.js +0 -11
- package/v2Containers/Templates/constants.js +0 -2
- package/v2Containers/Templates/index.js +60 -131
- package/v2Containers/Templates/sagas.js +13 -57
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1015 -1060
- package/v2Containers/Templates/tests/sagas.test.js +16 -199
- package/v2Containers/TemplatesV2/TemplatesV2.style.js +10 -75
- package/v2Containers/TemplatesV2/index.js +43 -86
- package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
- package/v2Containers/Whatsapp/index.js +25 -4
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +34 -578
- package/utils/rcsPayloadUtils.js +0 -92
- package/utils/templateVarUtils.js +0 -201
- package/utils/tests/rcsPayloadUtils.test.js +0 -226
- package/utils/tests/templateVarUtils.test.js +0 -204
- package/v2Components/CommonTestAndPreview/previewApiUtils.js +0 -59
- package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +0 -67
- package/v2Components/CommonTestAndPreview/utils.js +0 -84
- package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +0 -91
- package/v2Components/SmsFallback/constants.js +0 -94
- package/v2Components/SmsFallback/index.js +0 -958
- package/v2Components/SmsFallback/index.scss +0 -266
- package/v2Components/SmsFallback/messages.js +0 -78
- package/v2Components/SmsFallback/smsFallbackUtils.js +0 -120
- package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +0 -50
- package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +0 -147
- package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +0 -304
- package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +0 -208
- package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +0 -309
- package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +0 -422
- package/v2Components/SmsFallback/useLocalTemplateList.js +0 -92
- package/v2Components/TemplatePreview/constants.js +0 -2
- package/v2Components/VarSegmentMessageEditor/constants.js +0 -2
- package/v2Components/VarSegmentMessageEditor/index.js +0 -125
- package/v2Components/VarSegmentMessageEditor/index.scss +0 -46
- package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +0 -17
- package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +0 -79
- package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +0 -90
- package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +0 -258
- package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +0 -125
- package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +0 -227
- package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +0 -318
- package/v2Containers/Sms/smsFormDataHelpers.js +0 -67
- package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +0 -253
- package/v2Containers/SmsTrai/Edit/index.scss +0 -126
- package/v2Containers/Templates/TemplatesActionBar.js +0 -101
- package/v2Containers/Templates/tests/TemplatesActionBar.test.js +0 -120
- package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +0 -180
- package/v2Containers/Templates/utils/smsTemplatesListApi.js +0 -79
- package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +0 -131
|
@@ -9,16 +9,7 @@ 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 {
|
|
13
|
-
CHANNELS,
|
|
14
|
-
DESKTOP,
|
|
15
|
-
TABLET,
|
|
16
|
-
MOBILE,
|
|
17
|
-
ANDROID,
|
|
18
|
-
IOS,
|
|
19
|
-
PREVIEW_TAB_RCS,
|
|
20
|
-
PREVIEW_TAB_SMS_FALLBACK,
|
|
21
|
-
} from '../../constants';
|
|
12
|
+
import { CHANNELS, DESKTOP, TABLET, MOBILE, ANDROID, IOS } from '../../constants';
|
|
22
13
|
import messages from '../../messages';
|
|
23
14
|
|
|
24
15
|
// Convert messages object to format expected by IntlProvider
|
|
@@ -571,195 +562,6 @@ describe('UnifiedPreview', () => {
|
|
|
571
562
|
|
|
572
563
|
expect(screen.getByTestId('rcs-sender-id')).toHaveTextContent('RCS_SENDER');
|
|
573
564
|
});
|
|
574
|
-
|
|
575
|
-
describe('RCS SMS fallback — Test & Preview tabs', () => {
|
|
576
|
-
it('without SMS fallback selected, shows only RCS preview (no RCS+SMS tab layout)', () => {
|
|
577
|
-
const props = {
|
|
578
|
-
...defaultProps,
|
|
579
|
-
channel: CHANNELS.RCS,
|
|
580
|
-
content: { rcsTitle: 'Hello RCS' },
|
|
581
|
-
smsFallbackContent: undefined,
|
|
582
|
-
};
|
|
583
|
-
|
|
584
|
-
const { container } = render(
|
|
585
|
-
<TestWrapper>
|
|
586
|
-
<ComponentToRender {...props} />
|
|
587
|
-
</TestWrapper>
|
|
588
|
-
);
|
|
589
|
-
|
|
590
|
-
expect(container.querySelector('.unified-preview-rcs-tabs')).toBeNull();
|
|
591
|
-
expect(screen.getByTestId('rcs-preview')).toBeInTheDocument();
|
|
592
|
-
expect(screen.queryByTestId('sms-preview')).not.toBeInTheDocument();
|
|
593
|
-
});
|
|
594
|
-
|
|
595
|
-
it('with SMS fallback template body, shows RCS and Fallback SMS tabs', () => {
|
|
596
|
-
const props = {
|
|
597
|
-
...defaultProps,
|
|
598
|
-
channel: CHANNELS.RCS,
|
|
599
|
-
content: { rcsTitle: 'Hello RCS' },
|
|
600
|
-
smsFallbackContent: {
|
|
601
|
-
content: 'SMS fallback body',
|
|
602
|
-
templateContent: 'SMS fallback body',
|
|
603
|
-
},
|
|
604
|
-
};
|
|
605
|
-
|
|
606
|
-
const { container } = render(
|
|
607
|
-
<TestWrapper>
|
|
608
|
-
<ComponentToRender {...props} />
|
|
609
|
-
</TestWrapper>
|
|
610
|
-
);
|
|
611
|
-
|
|
612
|
-
expect(container.querySelector('.unified-preview-rcs-tabs')).toBeInTheDocument();
|
|
613
|
-
expect(
|
|
614
|
-
screen.getByRole('tab', { name: messages.rcsTab.defaultMessage })
|
|
615
|
-
).toBeInTheDocument();
|
|
616
|
-
expect(
|
|
617
|
-
screen.getByRole('tab', { name: messages.smsFallbackTab.defaultMessage })
|
|
618
|
-
).toBeInTheDocument();
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
it('on SMS fallback tab, renders SMS preview with resolved fallback text when smsFallbackResolvedText is set', () => {
|
|
622
|
-
const props = {
|
|
623
|
-
...defaultProps,
|
|
624
|
-
channel: CHANNELS.RCS,
|
|
625
|
-
content: { rcsTitle: 'Hello RCS' },
|
|
626
|
-
smsFallbackContent: {
|
|
627
|
-
content: '{{var}}',
|
|
628
|
-
templateContent: '{{var}}',
|
|
629
|
-
},
|
|
630
|
-
smsFallbackResolvedText: 'Resolved SMS for preview',
|
|
631
|
-
activePreviewTab: PREVIEW_TAB_SMS_FALLBACK,
|
|
632
|
-
onPreviewTabChange: jest.fn(),
|
|
633
|
-
};
|
|
634
|
-
|
|
635
|
-
render(
|
|
636
|
-
<TestWrapper>
|
|
637
|
-
<ComponentToRender {...props} />
|
|
638
|
-
</TestWrapper>
|
|
639
|
-
);
|
|
640
|
-
|
|
641
|
-
expect(screen.getByTestId('sms-content')).toHaveTextContent('Resolved SMS for preview');
|
|
642
|
-
});
|
|
643
|
-
|
|
644
|
-
it('on RCS tab (default), renders RCS preview when dual tabs are shown', () => {
|
|
645
|
-
const props = {
|
|
646
|
-
...defaultProps,
|
|
647
|
-
channel: CHANNELS.RCS,
|
|
648
|
-
content: { rcsTitle: 'Only RCS pane' },
|
|
649
|
-
smsFallbackContent: { content: 'SMS', templateContent: 'SMS' },
|
|
650
|
-
activePreviewTab: PREVIEW_TAB_RCS,
|
|
651
|
-
onPreviewTabChange: jest.fn(),
|
|
652
|
-
};
|
|
653
|
-
|
|
654
|
-
render(
|
|
655
|
-
<TestWrapper>
|
|
656
|
-
<ComponentToRender {...props} />
|
|
657
|
-
</TestWrapper>
|
|
658
|
-
);
|
|
659
|
-
|
|
660
|
-
expect(screen.getByTestId('rcs-preview')).toBeInTheDocument();
|
|
661
|
-
expect(screen.getByTestId('rcs-content')).toHaveTextContent(/Only RCS pane/);
|
|
662
|
-
});
|
|
663
|
-
|
|
664
|
-
it('on SMS fallback tab, shows raw template when no varmap and no resolved text', () => {
|
|
665
|
-
const props = {
|
|
666
|
-
...defaultProps,
|
|
667
|
-
channel: CHANNELS.RCS,
|
|
668
|
-
content: { rcsTitle: 'Hello RCS' },
|
|
669
|
-
smsFallbackContent: {
|
|
670
|
-
content: 'Hello {{name}}',
|
|
671
|
-
templateContent: 'Hello {{name}}',
|
|
672
|
-
// no rcsSmsFallbackVarMapped
|
|
673
|
-
},
|
|
674
|
-
smsFallbackResolvedText: undefined,
|
|
675
|
-
activePreviewTab: PREVIEW_TAB_SMS_FALLBACK,
|
|
676
|
-
onPreviewTabChange: jest.fn(),
|
|
677
|
-
};
|
|
678
|
-
|
|
679
|
-
render(
|
|
680
|
-
<TestWrapper>
|
|
681
|
-
<ComponentToRender {...props} />
|
|
682
|
-
</TestWrapper>
|
|
683
|
-
);
|
|
684
|
-
|
|
685
|
-
// rawFallbackTemplate is shown directly — {{tags}} remain visible
|
|
686
|
-
expect(screen.getByTestId('sms-content')).toHaveTextContent('Hello {{name}}');
|
|
687
|
-
});
|
|
688
|
-
|
|
689
|
-
it('on SMS fallback tab, treats empty resolved text as absent and shows raw template', () => {
|
|
690
|
-
const props = {
|
|
691
|
-
...defaultProps,
|
|
692
|
-
channel: CHANNELS.RCS,
|
|
693
|
-
content: { rcsTitle: 'Hello RCS' },
|
|
694
|
-
smsFallbackContent: {
|
|
695
|
-
content: 'Raw {{var}} template',
|
|
696
|
-
templateContent: 'Raw {{var}} template',
|
|
697
|
-
},
|
|
698
|
-
smsFallbackResolvedText: '',
|
|
699
|
-
activePreviewTab: PREVIEW_TAB_SMS_FALLBACK,
|
|
700
|
-
onPreviewTabChange: jest.fn(),
|
|
701
|
-
};
|
|
702
|
-
|
|
703
|
-
render(
|
|
704
|
-
<TestWrapper>
|
|
705
|
-
<ComponentToRender {...props} />
|
|
706
|
-
</TestWrapper>
|
|
707
|
-
);
|
|
708
|
-
|
|
709
|
-
expect(screen.getByTestId('sms-content')).toHaveTextContent('Raw {{var}} template');
|
|
710
|
-
});
|
|
711
|
-
|
|
712
|
-
it('on SMS fallback tab, applies varmap slot substitution when varmap entries exist and no resolved text', () => {
|
|
713
|
-
// getFallbackResolvedContent key format: `${fullToken}_${segmentIndex}`
|
|
714
|
-
// 'Hello {{name}}' → segments ['Hello ', '{{name}}'], so {{name}} is at index 1 → key '{{name}}_1'
|
|
715
|
-
const props = {
|
|
716
|
-
...defaultProps,
|
|
717
|
-
channel: CHANNELS.RCS,
|
|
718
|
-
content: { rcsTitle: 'Hello RCS' },
|
|
719
|
-
smsFallbackContent: {
|
|
720
|
-
content: 'Hello {{name}}',
|
|
721
|
-
templateContent: 'Hello {{name}}',
|
|
722
|
-
rcsSmsFallbackVarMapped: { '{{name}}_1': 'World' },
|
|
723
|
-
},
|
|
724
|
-
smsFallbackResolvedText: undefined,
|
|
725
|
-
activePreviewTab: PREVIEW_TAB_SMS_FALLBACK,
|
|
726
|
-
onPreviewTabChange: jest.fn(),
|
|
727
|
-
};
|
|
728
|
-
|
|
729
|
-
render(
|
|
730
|
-
<TestWrapper>
|
|
731
|
-
<ComponentToRender {...props} />
|
|
732
|
-
</TestWrapper>
|
|
733
|
-
);
|
|
734
|
-
|
|
735
|
-
expect(screen.getByTestId('sms-content')).toHaveTextContent('Hello World');
|
|
736
|
-
});
|
|
737
|
-
|
|
738
|
-
it('on SMS fallback tab, resolved text takes priority over varmap entries', () => {
|
|
739
|
-
const props = {
|
|
740
|
-
...defaultProps,
|
|
741
|
-
channel: CHANNELS.RCS,
|
|
742
|
-
content: { rcsTitle: 'Hello RCS' },
|
|
743
|
-
smsFallbackContent: {
|
|
744
|
-
content: 'Hello {{name}}',
|
|
745
|
-
templateContent: 'Hello {{name}}',
|
|
746
|
-
rcsSmsFallbackVarMapped: { '{{name}}_1': 'World' },
|
|
747
|
-
},
|
|
748
|
-
smsFallbackResolvedText: 'Hello John',
|
|
749
|
-
activePreviewTab: PREVIEW_TAB_SMS_FALLBACK,
|
|
750
|
-
onPreviewTabChange: jest.fn(),
|
|
751
|
-
};
|
|
752
|
-
|
|
753
|
-
render(
|
|
754
|
-
<TestWrapper>
|
|
755
|
-
<ComponentToRender {...props} />
|
|
756
|
-
</TestWrapper>
|
|
757
|
-
);
|
|
758
|
-
|
|
759
|
-
// resolvedText wins over varmap substitution
|
|
760
|
-
expect(screen.getByTestId('sms-content')).toHaveTextContent('Hello John');
|
|
761
|
-
});
|
|
762
|
-
});
|
|
763
565
|
});
|
|
764
566
|
|
|
765
567
|
describe('Channel Routing - INAPP', () => {
|
|
@@ -30,28 +30,23 @@ 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;
|
|
34
33
|
jest.mock('../LeftPanelContent', () => {
|
|
35
34
|
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
|
|
36
35
|
const ReactLib = require('react');
|
|
37
36
|
return {
|
|
38
37
|
__esModule: true,
|
|
39
|
-
default: function MockLeftPanelContent(
|
|
40
|
-
|
|
41
|
-
const editorEl = props.renderCustomValuesEditor ? props.renderCustomValuesEditor() : null;
|
|
42
|
-
return ReactLib.createElement('div', { 'data-testid': 'left-panel' }, 'Left Panel', editorEl);
|
|
38
|
+
default: function MockLeftPanelContent() {
|
|
39
|
+
return ReactLib.createElement('div', { 'data-testid': 'left-panel' }, 'Left Panel');
|
|
43
40
|
},
|
|
44
41
|
};
|
|
45
42
|
});
|
|
46
43
|
|
|
47
|
-
let lastCustomValuesEditorProps = null;
|
|
48
44
|
jest.mock('../CustomValuesEditor', () => {
|
|
49
45
|
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
|
|
50
46
|
const ReactLib = require('react');
|
|
51
47
|
return {
|
|
52
48
|
__esModule: true,
|
|
53
|
-
default: function MockCustomValuesEditor(
|
|
54
|
-
lastCustomValuesEditorProps = props;
|
|
49
|
+
default: function MockCustomValuesEditor() {
|
|
55
50
|
return ReactLib.createElement('div', { 'data-testid': 'custom-values-editor' }, 'Custom Values Editor');
|
|
56
51
|
},
|
|
57
52
|
};
|
|
@@ -196,8 +191,6 @@ describe('CommonTestAndPreview', () => {
|
|
|
196
191
|
beforeEach(() => {
|
|
197
192
|
jest.clearAllMocks();
|
|
198
193
|
lastSendTestMessageProps = null;
|
|
199
|
-
lastLeftPanelContentProps = null;
|
|
200
|
-
lastCustomValuesEditorProps = null;
|
|
201
194
|
// Reset all mock function implementations
|
|
202
195
|
Object.values(mockActions).forEach((mockFn) => {
|
|
203
196
|
if (jest.isMockFunction(mockFn)) {
|
|
@@ -252,25 +245,6 @@ describe('CommonTestAndPreview', () => {
|
|
|
252
245
|
});
|
|
253
246
|
});
|
|
254
247
|
|
|
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).toHaveBeenCalledTimes(2);
|
|
263
|
-
expect(mockActions.getSenderDetailsRequested).toHaveBeenNthCalledWith(1, {
|
|
264
|
-
channel: CHANNELS.RCS,
|
|
265
|
-
orgUnitId: -1,
|
|
266
|
-
});
|
|
267
|
-
expect(mockActions.getSenderDetailsRequested).toHaveBeenNthCalledWith(2, {
|
|
268
|
-
channel: CHANNELS.SMS,
|
|
269
|
-
orgUnitId: -1,
|
|
270
|
-
});
|
|
271
|
-
});
|
|
272
|
-
});
|
|
273
|
-
|
|
274
248
|
it('should not call getSenderDetailsRequested when channel is INAPP', async () => {
|
|
275
249
|
render(
|
|
276
250
|
<TestWrapper>
|
|
@@ -329,7 +303,7 @@ describe('CommonTestAndPreview', () => {
|
|
|
329
303
|
});
|
|
330
304
|
expect(lastSendTestMessageProps).toBeDefined();
|
|
331
305
|
expect(lastSendTestMessageProps.deliverySettings).toBeDefined();
|
|
332
|
-
expect(lastSendTestMessageProps.
|
|
306
|
+
expect(lastSendTestMessageProps.senderDetailsOptions).toEqual(senderDetailsByChannel[CHANNELS.SMS]);
|
|
333
307
|
expect(lastSendTestMessageProps.wecrmAccounts).toEqual([]);
|
|
334
308
|
expect(typeof lastSendTestMessageProps.onSaveDeliverySettings).toBe('function');
|
|
335
309
|
expect(lastSendTestMessageProps.isLoadingSenderDetails).toBe(false);
|
|
@@ -3501,107 +3475,4 @@ describe('CommonTestAndPreview', () => {
|
|
|
3501
3475
|
});
|
|
3502
3476
|
});
|
|
3503
3477
|
});
|
|
3504
|
-
|
|
3505
|
-
describe('SMS DLT and mustache tag discrimination (smsTemplateHasMustacheTags / buildSyntheticSmsMustacheTags)', () => {
|
|
3506
|
-
it('should return no tags for SMS content that contains only DLT {#var#} tokens', async () => {
|
|
3507
|
-
render(
|
|
3508
|
-
<TestWrapper>
|
|
3509
|
-
<CommonTestAndPreview
|
|
3510
|
-
{...defaultProps}
|
|
3511
|
-
channel={CHANNELS.SMS}
|
|
3512
|
-
formData={{ 0: { 'sms-editor': 'Order {#orderId#} is confirmed' } }}
|
|
3513
|
-
extractedTags={[]}
|
|
3514
|
-
/>
|
|
3515
|
-
</TestWrapper>
|
|
3516
|
-
);
|
|
3517
|
-
|
|
3518
|
-
await waitFor(() => expect(lastLeftPanelContentProps).not.toBeNull());
|
|
3519
|
-
|
|
3520
|
-
// smsTemplateHasMustacheTags returns false for DLT-only → extractedTags is []
|
|
3521
|
-
expect(lastLeftPanelContentProps.extractedTags).toEqual([]);
|
|
3522
|
-
});
|
|
3523
|
-
|
|
3524
|
-
it('should build synthetic tags from {{mustache}} tokens, excluding DLT {#var#} tokens', async () => {
|
|
3525
|
-
render(
|
|
3526
|
-
<TestWrapper>
|
|
3527
|
-
<CommonTestAndPreview
|
|
3528
|
-
{...defaultProps}
|
|
3529
|
-
channel={CHANNELS.SMS}
|
|
3530
|
-
formData={{ 0: { 'sms-editor': 'Hi {{name}}, order {#orderId#} shipped' } }}
|
|
3531
|
-
extractedTags={[]}
|
|
3532
|
-
/>
|
|
3533
|
-
</TestWrapper>
|
|
3534
|
-
);
|
|
3535
|
-
|
|
3536
|
-
await waitFor(() => expect(lastLeftPanelContentProps).not.toBeNull());
|
|
3537
|
-
|
|
3538
|
-
const tags = lastLeftPanelContentProps.extractedTags;
|
|
3539
|
-
// Only {{name}} should become a tag — {#orderId#} must be excluded
|
|
3540
|
-
expect(tags).toHaveLength(1);
|
|
3541
|
-
expect(tags[0].name).toBe('name');
|
|
3542
|
-
});
|
|
3543
|
-
|
|
3544
|
-
it('should use API-extracted tags when present instead of building synthetic ones', async () => {
|
|
3545
|
-
const apiTags = [{ name: 'firstName', metaData: { userDriven: true }, children: [] }];
|
|
3546
|
-
render(
|
|
3547
|
-
<TestWrapper>
|
|
3548
|
-
<CommonTestAndPreview
|
|
3549
|
-
{...defaultProps}
|
|
3550
|
-
channel={CHANNELS.SMS}
|
|
3551
|
-
formData={{ 0: { 'sms-editor': 'Hi {{firstName}}' } }}
|
|
3552
|
-
extractedTags={apiTags}
|
|
3553
|
-
/>
|
|
3554
|
-
</TestWrapper>
|
|
3555
|
-
);
|
|
3556
|
-
|
|
3557
|
-
await waitFor(() => expect(lastLeftPanelContentProps).not.toBeNull());
|
|
3558
|
-
|
|
3559
|
-
// API tags take priority over buildSyntheticSmsMustacheTags
|
|
3560
|
-
expect(lastLeftPanelContentProps.extractedTags).toEqual(apiTags);
|
|
3561
|
-
});
|
|
3562
|
-
});
|
|
3563
|
-
|
|
3564
|
-
describe('handleDiscardCustomValues — preview reset', () => {
|
|
3565
|
-
it('should call updatePreviewRequested when handleDiscardCustomValues is invoked', async () => {
|
|
3566
|
-
render(
|
|
3567
|
-
<TestWrapper>
|
|
3568
|
-
<CommonTestAndPreview
|
|
3569
|
-
{...defaultProps}
|
|
3570
|
-
channel={CHANNELS.SMS}
|
|
3571
|
-
formData={{ 0: { 'sms-editor': 'Hi {{name}}' } }}
|
|
3572
|
-
/>
|
|
3573
|
-
</TestWrapper>
|
|
3574
|
-
);
|
|
3575
|
-
|
|
3576
|
-
await waitFor(() => expect(lastCustomValuesEditorProps).not.toBeNull());
|
|
3577
|
-
|
|
3578
|
-
lastCustomValuesEditorProps.handleDiscardCustomValues();
|
|
3579
|
-
|
|
3580
|
-
expect(mockActions.updatePreviewRequested).toHaveBeenCalledTimes(1);
|
|
3581
|
-
});
|
|
3582
|
-
|
|
3583
|
-
it('should call updatePreviewRequested with RCS channel when handleDiscardCustomValues is invoked for RCS with fallback', async () => {
|
|
3584
|
-
render(
|
|
3585
|
-
<TestWrapper>
|
|
3586
|
-
<CommonTestAndPreview
|
|
3587
|
-
{...defaultProps}
|
|
3588
|
-
channel={CHANNELS.RCS}
|
|
3589
|
-
smsFallbackContent={{
|
|
3590
|
-
templateContent: 'Fallback {{name}}',
|
|
3591
|
-
content: 'Fallback {{name}}',
|
|
3592
|
-
}}
|
|
3593
|
-
formData={{}}
|
|
3594
|
-
/>
|
|
3595
|
-
</TestWrapper>
|
|
3596
|
-
);
|
|
3597
|
-
|
|
3598
|
-
await waitFor(() => expect(lastCustomValuesEditorProps).not.toBeNull());
|
|
3599
|
-
|
|
3600
|
-
lastCustomValuesEditorProps.handleDiscardCustomValues();
|
|
3601
|
-
|
|
3602
|
-
// updatePreviewRequested is called; syncSmsFallbackPreview (which hits Api directly) is NOT called
|
|
3603
|
-
expect(mockActions.updatePreviewRequested).toHaveBeenCalledTimes(1);
|
|
3604
|
-
});
|
|
3605
|
-
});
|
|
3606
3478
|
});
|
|
3607
|
-
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
-
call, put, takeLatest,
|
|
8
|
+
call, put, takeLatest, all,
|
|
9
9
|
} from 'redux-saga/effects';
|
|
10
10
|
import {
|
|
11
11
|
searchCustomersSaga,
|
|
@@ -1333,26 +1333,9 @@ 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
|
-
|
|
1337
|
-
);
|
|
1338
|
-
const firstAction = {
|
|
1339
|
-
payload: { channel: 'RCS', orgUnitId: 10 },
|
|
1340
|
-
};
|
|
1341
|
-
expect(generator.next(firstAction).value).toEqual(
|
|
1342
|
-
fork(getSenderDetailsSaga, firstAction)
|
|
1343
|
-
);
|
|
1344
|
-
expect(generator.next({ '@@redux-saga/TASK': true }).value).toEqual(
|
|
1345
|
-
take('app/CommonTestAndPreview/GET_SENDER_DETAILS_REQUESTED')
|
|
1346
|
-
);
|
|
1347
|
-
const secondAction = {
|
|
1348
|
-
payload: { channel: 'RCS', orgUnitId: 20 },
|
|
1349
|
-
};
|
|
1350
|
-
expect(generator.next(secondAction).value).toEqual(
|
|
1351
|
-
cancel({ '@@redux-saga/TASK': true })
|
|
1352
|
-
);
|
|
1353
|
-
expect(generator.next().value).toEqual(
|
|
1354
|
-
fork(getSenderDetailsSaga, secondAction)
|
|
1336
|
+
takeLatest('app/CommonTestAndPreview/GET_SENDER_DETAILS_REQUESTED', getSenderDetailsSaga)
|
|
1355
1337
|
);
|
|
1338
|
+
expect(generator.next().done).toBe(true);
|
|
1356
1339
|
});
|
|
1357
1340
|
|
|
1358
1341
|
it('should watch for GET_WECRM_ACCOUNTS_REQUESTED', () => {
|
|
@@ -1471,24 +1454,34 @@ describe('CommonTestAndPreview Sagas', () => {
|
|
|
1471
1454
|
const action = { payload: {} };
|
|
1472
1455
|
const generator = getWeCrmAccountsSaga(action);
|
|
1473
1456
|
generator.next();
|
|
1474
|
-
const
|
|
1475
|
-
expect(
|
|
1457
|
+
const err = generator.throw(new Error('API failed'));
|
|
1458
|
+
expect(err.value).toEqual(
|
|
1476
1459
|
put({ type: GET_WECRM_ACCOUNTS_FAILURE, payload: { error: 'API failed' } })
|
|
1477
1460
|
);
|
|
1478
1461
|
expect(generator.next().done).toBe(true);
|
|
1479
1462
|
});
|
|
1480
1463
|
});
|
|
1481
1464
|
|
|
1482
|
-
|
|
1483
|
-
|
|
1465
|
+
describe('commonTestAndPreviewSaga', () => {
|
|
1466
|
+
it('should fork all watcher sagas', () => {
|
|
1467
|
+
const generator = commonTestAndPreviewSaga();
|
|
1484
1468
|
|
|
1485
|
-
|
|
1469
|
+
expect(generator.next().value).toEqual(
|
|
1470
|
+
all([
|
|
1471
|
+
watchSearchCustomers(),
|
|
1472
|
+
watchExtractTags(),
|
|
1473
|
+
watchUpdatePreview(),
|
|
1474
|
+
watchSendTestMessage(),
|
|
1475
|
+
watchFetchTestCustomers(),
|
|
1476
|
+
watchFetchTestGroups(),
|
|
1477
|
+
watchCreateMessageMeta(),
|
|
1478
|
+
watchGetPrefilledValues(),
|
|
1479
|
+
watchGetSenderDetails(),
|
|
1480
|
+
watchGetWeCrmAccounts(),
|
|
1481
|
+
])
|
|
1482
|
+
);
|
|
1486
1483
|
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
const watchers = Array.isArray(effectData) ? effectData : Object.values(effectData || {});
|
|
1490
|
-
expect(Array.isArray(watchers)).toBe(true);
|
|
1491
|
-
expect(watchers.length).toBeGreaterThan(0);
|
|
1492
|
-
expect(generator.next().done).toBe(true);
|
|
1484
|
+
expect(generator.next().done).toBe(true);
|
|
1485
|
+
});
|
|
1493
1486
|
});
|
|
1494
1487
|
});
|
|
@@ -12,8 +12,10 @@ import { loadItem } from 'services/localStorageApi';
|
|
|
12
12
|
import { intlShape, injectIntl } from 'react-intl';
|
|
13
13
|
import { withRouter } from 'react-router-dom';
|
|
14
14
|
import { get } from 'lodash';
|
|
15
|
-
import CapNavigation from '@capillarytech/cap-ui-library/
|
|
15
|
+
import CapNavigation from '@capillarytech/cap-ui-library/CapNavigationSPA';
|
|
16
16
|
import { CAP_WHITE } from '@capillarytech/cap-ui-library/styled/variables';
|
|
17
|
+
import { MFEModuleHeader } from '@capillarytech/cap-ui-utils';
|
|
18
|
+
import { MFE_MODULE_HEADER } from './mfeModuleHeader.config';
|
|
17
19
|
import injectSaga from '../../utils/injectSaga';
|
|
18
20
|
import sagas from './saga';
|
|
19
21
|
import messages from './messages';
|
|
@@ -30,12 +32,11 @@ import { CapLeftNavigatioOpenCss, CapLeftNavigationCss } from './style';
|
|
|
30
32
|
import { EMBEDDED } from './constants';
|
|
31
33
|
|
|
32
34
|
const CapWrapper = styled.div`
|
|
33
|
-
position: absolute;
|
|
34
35
|
padding: 0;
|
|
35
36
|
background-color: ${CAP_WHITE};
|
|
36
37
|
width: 100%;
|
|
37
38
|
display: flex;
|
|
38
|
-
|
|
39
|
+
|
|
39
40
|
|
|
40
41
|
.sidebar-container {
|
|
41
42
|
margin-right: 10px;
|
|
@@ -46,10 +47,8 @@ const CapWrapper = styled.div`
|
|
|
46
47
|
`;
|
|
47
48
|
|
|
48
49
|
const ComponentWrapper = styled.div`
|
|
49
|
-
max-width: 1140px;
|
|
50
|
-
margin: 0 auto;
|
|
51
50
|
width: 100%;
|
|
52
|
-
padding:
|
|
51
|
+
padding: 0;
|
|
53
52
|
`;
|
|
54
53
|
|
|
55
54
|
export class NavigationBar extends React.Component {
|
|
@@ -58,6 +57,7 @@ export class NavigationBar extends React.Component {
|
|
|
58
57
|
this.state = this.initializeSelectedProduct();
|
|
59
58
|
}
|
|
60
59
|
|
|
60
|
+
|
|
61
61
|
initializeSelectedProduct = () => {
|
|
62
62
|
const { location, intl: { formatMessage } } = this.props;
|
|
63
63
|
const { pathname } = location;
|
|
@@ -179,6 +179,7 @@ export class NavigationBar extends React.Component {
|
|
|
179
179
|
const topbarIcons = this.getTopbarIcons(showDocumentationBot);
|
|
180
180
|
const headerOverideCss = leftNavbarExpandedProp ? CapLeftNavigatioOpenCss : CapLeftNavigationCss;
|
|
181
181
|
return (
|
|
182
|
+
<>
|
|
182
183
|
<CapNavigation
|
|
183
184
|
className="creatives-main-container"
|
|
184
185
|
showContent
|
|
@@ -212,7 +213,8 @@ export class NavigationBar extends React.Component {
|
|
|
212
213
|
</CapWrapper>
|
|
213
214
|
</div>
|
|
214
215
|
</CapNavigation>
|
|
215
|
-
|
|
216
|
+
<MFEModuleHeader history={this.props.history} {...MFE_MODULE_HEADER} />
|
|
217
|
+
</>
|
|
216
218
|
);
|
|
217
219
|
}
|
|
218
220
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Static, module-specific config for the shared MFE module-header wrapper
|
|
2
|
+
// (MFEModuleHeader). Runtime values (history) are supplied in NavigationBar.
|
|
3
|
+
export const MFE_MODULE_HEADER = {
|
|
4
|
+
header: {
|
|
5
|
+
name: 'Creatives',
|
|
6
|
+
description: 'Store your creative templates for all your channels',
|
|
7
|
+
showBorder: false,
|
|
8
|
+
showSettings: false,
|
|
9
|
+
},
|
|
10
|
+
landingRoutes: [
|
|
11
|
+
'/creatives/ui/v2/',
|
|
12
|
+
'/creatives/ui/v2',
|
|
13
|
+
'/v2/',
|
|
14
|
+
'/v2'
|
|
15
|
+
],
|
|
16
|
+
};
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
|
|
14
14
|
.shell-v2 {
|
|
15
15
|
position: relative;
|
|
16
|
-
|
|
16
|
+
-webkit-transform: translate(-50%);
|
|
17
|
+
transform: translate(-50%);
|
|
18
|
+
left: 50%;
|
|
17
19
|
padding: 0 10px;
|
|
18
20
|
width: 450px;
|
|
19
21
|
|
|
@@ -535,21 +537,20 @@
|
|
|
535
537
|
.unicode-disabled{
|
|
536
538
|
font-size: 16px;
|
|
537
539
|
}
|
|
538
|
-
position: absolute;
|
|
539
|
-
overflow: auto;
|
|
540
|
-
top: 0;
|
|
541
|
-
left: 17%;
|
|
542
|
-
white-space: pre-wrap;
|
|
543
|
-
word-break: break-word;
|
|
544
|
-
max-height: 100%;
|
|
545
|
-
line-height: 0.875rem;
|
|
546
|
-
font-size: 0.625rem;
|
|
547
|
-
font-family: 'open-sans';
|
|
548
|
-
|
|
549
540
|
&.sms {
|
|
541
|
+
max-height: 100%;
|
|
542
|
+
position: absolute;
|
|
550
543
|
// width: initial;
|
|
544
|
+
overflow: auto;
|
|
545
|
+
top: 0;
|
|
546
|
+
left: 17%;
|
|
547
|
+
white-space: pre-wrap;
|
|
548
|
+
word-break: break-word;
|
|
549
|
+
line-height: 14px;
|
|
551
550
|
padding: 0 8px 8px 8px;
|
|
551
|
+
font-size: 10px;
|
|
552
552
|
width: 100%;
|
|
553
|
+
font-family: 'open-sans';
|
|
553
554
|
.rcs-image{
|
|
554
555
|
width: 100%;
|
|
555
556
|
height: 123px;
|
|
@@ -570,9 +571,6 @@
|
|
|
570
571
|
margin-top: 0;
|
|
571
572
|
padding: 4px 8px;
|
|
572
573
|
}
|
|
573
|
-
&.rcs-desc {
|
|
574
|
-
margin-top: $CAP_SPACE_08;
|
|
575
|
-
}
|
|
576
574
|
&.rcs-button-text{
|
|
577
575
|
color: $FONT_COLOR_05;
|
|
578
576
|
.rcs-icon{
|
|
@@ -584,12 +582,22 @@
|
|
|
584
582
|
&:not(.sms){
|
|
585
583
|
padding: 4px;
|
|
586
584
|
background: #3F51B5;
|
|
585
|
+
position: absolute;
|
|
587
586
|
// width: initial;
|
|
587
|
+
overflow: auto;
|
|
588
|
+
top: 0;
|
|
589
|
+
left: 17%;
|
|
590
|
+
white-space: pre-wrap;
|
|
591
|
+
word-break: break-word;
|
|
588
592
|
border-radius: 4px;
|
|
593
|
+
max-height: 100%;
|
|
589
594
|
color: #FFFFFF;
|
|
590
595
|
width: 70%;
|
|
591
596
|
min-height: 26px;
|
|
597
|
+
line-height: 14px;
|
|
592
598
|
padding: 8px;
|
|
599
|
+
font-size: 10px;
|
|
600
|
+
font-family: 'open-sans';
|
|
593
601
|
}
|
|
594
602
|
&.message-pop-carousel {
|
|
595
603
|
position: relative;
|
|
@@ -834,13 +842,9 @@
|
|
|
834
842
|
|
|
835
843
|
.msg-container-carousel {
|
|
836
844
|
display: flex;
|
|
837
|
-
width: 100%;
|
|
838
845
|
.scroll-container {
|
|
839
846
|
overflow-x: auto;
|
|
840
|
-
overflow-y: hidden;
|
|
841
847
|
display: flex;
|
|
842
|
-
flex-wrap: nowrap;
|
|
843
|
-
width: 100%;
|
|
844
848
|
padding-top: $CAP_SPACE_06;
|
|
845
849
|
padding-right: $CAP_SPACE_06;
|
|
846
850
|
white-space: nowrap;
|
|
@@ -861,32 +865,6 @@
|
|
|
861
865
|
}
|
|
862
866
|
}
|
|
863
867
|
|
|
864
|
-
.shell-v2.rcs-preview {
|
|
865
|
-
// Collapse the default 8px margin so the divider renders as a clean hairline, not a white gap.
|
|
866
|
-
.whatsapp-divider {
|
|
867
|
-
margin: 0;
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
// Override `.message-pop:not(.sms)` (blue background) for RCS carousel cards.
|
|
871
|
-
.msg-container.sms {
|
|
872
|
-
.message-pop.sms {
|
|
873
|
-
.rcs-carousel-card {
|
|
874
|
-
background: $CAP_WHITE;
|
|
875
|
-
color: $CAP_G01;
|
|
876
|
-
width: 10.4rem;
|
|
877
|
-
left: 0;
|
|
878
|
-
flex-shrink: 0;
|
|
879
|
-
padding: $CAP_SPACE_04 0 $CAP_SPACE_08;
|
|
880
|
-
border-radius: $CAP_SPACE_04; // 4px (Figma RCS carousel card corner)
|
|
881
|
-
|
|
882
|
-
.carousel-title {
|
|
883
|
-
font-weight: 700 !important;
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
|
|
890
868
|
.align-center {
|
|
891
869
|
text-align: center;
|
|
892
870
|
}
|
|
@@ -1085,6 +1063,8 @@
|
|
|
1085
1063
|
}
|
|
1086
1064
|
.video-icon {
|
|
1087
1065
|
position: absolute;
|
|
1066
|
+
right: -17px;
|
|
1067
|
+
bottom: -17px;
|
|
1088
1068
|
}
|
|
1089
1069
|
|
|
1090
1070
|
.zalo-preview-container {
|