@capillarytech/creatives-library 8.0.201 → 8.0.204

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.
@@ -11,7 +11,7 @@ import CapNotification from '@capillarytech/cap-ui-library/CapNotification';
11
11
  import { injectIntl, FormattedMessage } from 'react-intl';
12
12
  import classnames from 'classnames';
13
13
  import {
14
- isEmpty, get, forEach, cloneDeep, debounce,
14
+ isEmpty, get, forEach, cloneDeep,
15
15
  } from 'lodash';
16
16
  import { connect } from 'react-redux';
17
17
  import { createStructuredSelector } from 'reselect';
@@ -105,8 +105,6 @@ export class Creatives extends React.Component {
105
105
  // NEW: Test and Preview feature state
106
106
  showTestAndPreviewSlidebox: false,
107
107
  isTestAndPreviewMode: false, // Add flag to track Test & Preview mode
108
- // Performance optimization: Local template name for immediate UI feedback
109
- localTemplateName: '',
110
108
  };
111
109
  this.liquidFlow = Boolean(commonUtil.hasLiquidSupportFeature());
112
110
  this.creativesTemplateSteps = {
@@ -114,9 +112,6 @@ export class Creatives extends React.Component {
114
112
  2: 'templateSelection', // only for email in current flows wil be used for mpush, line and wechat as well.
115
113
  3: 'createTemplateContent',
116
114
  };
117
-
118
- // Performance optimization: Debounced template name update
119
- this.debouncedTemplateNameUpdate = debounce(this.performTemplateNameUpdate.bind(this), 300);
120
115
  }
121
116
 
122
117
  componentWillUnmount() {
@@ -124,11 +119,6 @@ export class Creatives extends React.Component {
124
119
  this.props.templateActions.resetTemplateStoreData();
125
120
  }
126
121
  this.props.globalActions.clearMetaEntities();
127
-
128
- // Cleanup debounced function
129
- if (this.debouncedTemplateNameUpdate) {
130
- this.debouncedTemplateNameUpdate.cancel();
131
- }
132
122
  }
133
123
 
134
124
  componentDidMount() {
@@ -146,29 +136,6 @@ export class Creatives extends React.Component {
146
136
 
147
137
  onEnterTemplateName = () => {
148
138
  this.setState({ templateNameExists: true });
149
- }
150
-
151
- // Performance optimized template name update
152
- performTemplateNameUpdate = (value, formData, onFormDataChange) => {
153
- const isEmptyTemplateName = !value.trim();
154
- const newFormData = { ...formData, 'template-name': value, 'isTemplateNameEdited': true };
155
-
156
- this.setState({ isTemplateNameEmpty: isEmptyTemplateName });
157
- onFormDataChange(newFormData);
158
- }
159
-
160
- // Update template name immediately for UI feedback
161
- updateTemplateNameImmediately = (value, formData, onFormDataChange) => {
162
- const isEmptyTemplateName = !value.trim();
163
-
164
- // 1. IMMEDIATE: Update local state for instant UI feedback
165
- this.setState({
166
- isTemplateNameEmpty: isEmptyTemplateName,
167
- localTemplateName: value
168
- });
169
-
170
- // 2. DEBOUNCED: Only debounce the expensive onFormDataChange call
171
- this.debouncedTemplateNameUpdate(value, formData, onFormDataChange);
172
139
  };
173
140
 
174
141
  onRemoveTemplateName = () => {
@@ -1428,30 +1395,21 @@ export class Creatives extends React.Component {
1428
1395
  } />
1429
1396
  )
1430
1397
 
1431
- templateNameComponentInput = ({ formData, onFormDataChange, name }) => {
1432
- // Use local state for immediate UI feedback, fallback to prop value
1433
- const displayValue = this.state.localTemplateName !== '' ? this.state.localTemplateName : name;
1434
-
1435
- return (
1436
- <CapInput
1437
- value={displayValue}
1438
- suffix={<span />}
1439
- onBlur={() => {
1440
- this.setState({
1441
- isEditName: false,
1442
- localTemplateName: '' // Clear local state on blur
1443
- }, () => {
1444
- this.showTemplateName({ formData, onFormDataChange });
1445
- });
1446
- }}
1447
- onChange={(ev) => {
1448
- const { value } = ev.currentTarget;
1449
- // Use optimized update for better performance
1450
- this.updateTemplateNameImmediately(value, formData, onFormDataChange);
1451
- }}
1452
- />
1453
- );
1454
- }
1398
+ templateNameComponentInput = ({ formData, onFormDataChange, name }) => (
1399
+ <CapInput
1400
+ value={name}
1401
+ suffix={<span />}
1402
+ onBlur={() => { this.setState({ isEditName: false }, () => { this.showTemplateName({ formData, onFormDataChange }); }); }}
1403
+ onChange={(ev) => {
1404
+ const { value } = ev.currentTarget;
1405
+ const isEmptyTemplateName = !value.trim();
1406
+
1407
+ const newFormData = { ...formData, 'template-name': value, 'isTemplateNameEdited': true };
1408
+ this.setState({ isTemplateNameEmpty: isEmptyTemplateName });
1409
+ onFormDataChange(newFormData);
1410
+ }}
1411
+ />
1412
+ )
1455
1413
 
1456
1414
  showTemplateName = ({ formData, onFormDataChange }) => { //gets called from email/index after template data is fetched
1457
1415
  const { slidBoxContent, currentChannel, isEditName } = this.state;
@@ -1465,10 +1423,7 @@ export class Creatives extends React.Component {
1465
1423
  if (name && !isEditName) {
1466
1424
  this.setState({ showTemplateNameComponentEdit: false });
1467
1425
  } else if (isEditName) {
1468
- this.setState({
1469
- showTemplateNameComponentEdit: true,
1470
- localTemplateName: name || '' // Initialize local state with current value
1471
- });
1426
+ this.setState({ showTemplateNameComponentEdit: true });
1472
1427
  }
1473
1428
  }
1474
1429
  }
@@ -5,7 +5,6 @@ import { SlideBoxContent } from '../SlideBoxContent';
5
5
  import mockdata from '../../mockdata';
6
6
  import { templateDetailsImage, templateDetailsVideo, templateDetailsText, templateDetails_ } from '../../Viber/tests/mockData';
7
7
  import { CREATE, RICH_MEDIA } from '../../WeChat/Wrapper/constants';
8
- import * as commonUtils from '../../../utils/common';
9
8
 
10
9
  const {
11
10
  whatsappEditTemplateData,
@@ -51,11 +50,6 @@ describe('Test SlideBoxContent container', () => {
51
50
  global.Date = originalDate;
52
51
  });
53
52
 
54
- afterEach(() => {
55
- // Clean up all mocks after each test
56
- jest.restoreAllMocks();
57
- });
58
-
59
53
  const renderFunction = (channel, mode, templateData, additionalProps = {}) => {
60
54
  renderedComponent = shallowWithIntl(
61
55
  <SlideBoxContent
@@ -706,315 +700,4 @@ describe('Test SlideBoxContent container', () => {
706
700
  expect(renderedComponent).toMatchSnapshot();
707
701
  });
708
702
  });
709
-
710
- // Mobile Push Loyalty Module Tests
711
- describe('Mobile Push with Loyalty Module', () => {
712
- it('Should render MobliPushEdit for loyalty module in library mode (edit)', () => {
713
- renderFunction('MOBILEPUSH', 'editTemplate', {
714
- _id: 'test-id',
715
- type: 'MOBILEPUSH' // Use uppercase to match constants.MOBILE_PUSH
716
- }, {
717
- isFullMode: false,
718
- isLoyaltyModule: true
719
- });
720
- expect(renderedComponent).toMatchSnapshot();
721
- });
722
-
723
- it('Should render MobilePushNew for loyalty module in full mode (edit)', () => {
724
- renderFunction('MOBILEPUSH', 'editTemplate', {
725
- _id: 'test-id',
726
- type: 'MOBILEPUSH' // Use uppercase to match constants.MOBILE_PUSH
727
- }, {
728
- isFullMode: true,
729
- isLoyaltyModule: true
730
- });
731
- expect(renderedComponent).toMatchSnapshot();
732
- });
733
-
734
- it('Should render MobilepushWrapper for loyalty module in library mode (create)', () => {
735
- renderFunction('MOBILEPUSH', 'createTemplate', {
736
- mode: 'create',
737
- type: 'MOBILEPUSH' // Use uppercase to match constants.MOBILE_PUSH
738
- }, {
739
- isFullMode: false,
740
- isLoyaltyModule: true
741
- });
742
- expect(renderedComponent).toMatchSnapshot();
743
- });
744
-
745
- it('Should render MobilePushNew for loyalty module in full mode (create)', () => {
746
- renderFunction('MOBILEPUSH', 'createTemplate', {
747
- mode: 'create',
748
- type: 'MOBILEPUSH' // Use uppercase to match constants.MOBILE_PUSH
749
- }, {
750
- isFullMode: true,
751
- isLoyaltyModule: true
752
- });
753
- expect(renderedComponent).toMatchSnapshot();
754
- });
755
-
756
- it('Should render MobilePushNew for non-loyalty module in library mode (edit)', () => {
757
- renderFunction('MOBILEPUSH', 'editTemplate', {
758
- _id: 'test-id',
759
- type: 'MOBILEPUSH' // Use uppercase to match constants.MOBILE_PUSH
760
- }, {
761
- isFullMode: false,
762
- isLoyaltyModule: false
763
- });
764
- expect(renderedComponent).toMatchSnapshot();
765
- });
766
-
767
- it('Should render MobilePushNew for non-loyalty module in library mode (create)', () => {
768
- renderFunction('MOBILEPUSH', 'createTemplate', {
769
- mode: 'create',
770
- type: 'MOBILEPUSH' // Use uppercase to match constants.MOBILE_PUSH
771
- }, {
772
- isFullMode: false,
773
- isLoyaltyModule: false
774
- });
775
- expect(renderedComponent).toMatchSnapshot();
776
- });
777
-
778
- it('Should render MobilePushNew for non-loyalty module in full mode (edit)', () => {
779
- renderFunction('MOBILEPUSH', 'editTemplate', {
780
- _id: 'test-id',
781
- type: 'MOBILEPUSH' // Use uppercase to match constants.MOBILE_PUSH
782
- }, {
783
- isFullMode: true,
784
- isLoyaltyModule: false
785
- });
786
- expect(renderedComponent).toMatchSnapshot();
787
- });
788
-
789
- it('Should render MobilePushNew for non-loyalty module in full mode (create)', () => {
790
- renderFunction('MOBILEPUSH', 'createTemplate', {
791
- mode: 'create',
792
- type: 'MOBILEPUSH' // Use uppercase to match constants.MOBILE_PUSH
793
- }, {
794
- isFullMode: true,
795
- isLoyaltyModule: false
796
- });
797
- expect(renderedComponent).toMatchSnapshot();
798
- });
799
- });
800
-
801
- // Additional edge case tests to cover uncovered lines
802
- describe('Edge Cases for Uncovered Lines', () => {
803
- it('Should handle getLineType with LINE channel and empty templateData', () => {
804
- // This should cover lines 72-74 in getLineType function
805
- // LINE channel, not full mode, templateData exists but has no _id, no messageBody, and not isDefault
806
- const emptyLineTemplateData = {
807
- type: 'LINE',
808
- isDefault: false,
809
- // No _id and no versions.base.content.messages[0]
810
- };
811
- renderFunction('LINE', 'editTemplate', emptyLineTemplateData, {
812
- isFullMode: false
813
- });
814
- expect(renderedComponent).toMatchSnapshot();
815
- });
816
-
817
- it('Should handle SMS channel with DLT content processing', () => {
818
- // This should cover line 251 in SMS content processing
819
- // Need isDltEnabled = true and updatedSmsEditor as array
820
- // Mock hasTraiDltFeature to return true
821
- jest.spyOn(commonUtils, 'hasTraiDltFeature').mockReturnValue(true);
822
-
823
- const smsTemplateData = {
824
- type: 'SMS',
825
- versions: {
826
- base: {
827
- 'updated-sms-editor': ['Line 1', 'Line 2', 'Line 3'], // Array to trigger join()
828
- 'sms-editor': 'Fallback SMS content'
829
- }
830
- }
831
- };
832
- renderFunction('SMS', 'preview', smsTemplateData, {
833
- smsRegister: 'DLT', // Enable DLT for library mode
834
- isFullMode: false
835
- });
836
-
837
- expect(renderedComponent).toMatchSnapshot();
838
- });
839
-
840
- it('Should handle LINE channel with video content', () => {
841
- // This should cover lines 314-315 for video detection
842
- // Need a LINE message with video property but no text, previewImageUrl, selectedSticker, baseUrl, template, or contents
843
- const lineTemplateDataWithVideo = {
844
- type: 'LINE',
845
- versions: {
846
- base: {
847
- content: {
848
- messages: [{
849
- video: {
850
- originalContentUrl: 'https://example.com/video.mp4',
851
- externalLink: {
852
- linkUri: 'https://example.com/link'
853
- }
854
- }
855
- // No text, previewImageUrl, selectedSticker, baseUrl, template, or contents
856
- }]
857
- }
858
- }
859
- }
860
- };
861
- renderFunction('LINE', 'preview', lineTemplateDataWithVideo);
862
- expect(renderedComponent).toMatchSnapshot();
863
- });
864
-
865
- it('Should handle default case in getChannelPreviewContent', () => {
866
- // This should cover line 395 - default case return
867
- const unknownChannelData = {
868
- versions: {
869
- base: {
870
- content: {
871
- customField: 'custom content'
872
- }
873
- }
874
- }
875
- };
876
- renderFunction('UNKNOWN_CHANNEL', 'preview', unknownChannelData);
877
- expect(renderedComponent).toMatchSnapshot();
878
- });
879
-
880
- it('Should handle WeChat channel in different modes', () => {
881
- const wechatTemplateData = {
882
- type: 'WECHAT',
883
- versions: {
884
- base: {
885
- content: {
886
- msgcontent: 'RICH_MEDIA_WECHAT'
887
- }
888
- }
889
- }
890
- };
891
- renderFunction('WECHAT', 'editTemplate', wechatTemplateData);
892
- expect(renderedComponent).toMatchSnapshot();
893
- });
894
-
895
- it('Should handle InApp channel preview', () => {
896
- const inappTemplateData = {
897
- type: 'INAPP',
898
- versions: {
899
- base: {
900
- content: {
901
- message: 'Test InApp message'
902
- }
903
- }
904
- }
905
- };
906
- renderFunction('INAPP', 'preview', inappTemplateData);
907
- expect(renderedComponent).toMatchSnapshot();
908
- });
909
-
910
- it('Should handle Email channel with different modes', () => {
911
- const emailTemplateData = {
912
- type: 'EMAIL',
913
- _id: 'email-123',
914
- versions: {
915
- base: {
916
- content: {
917
- subject: 'Test Email',
918
- html_content: '<p>Test content</p>'
919
- }
920
- }
921
- }
922
- };
923
- renderFunction('EMAIL', 'editTemplate', emailTemplateData, {
924
- isFullMode: true
925
- });
926
- expect(renderedComponent).toMatchSnapshot();
927
- });
928
-
929
- it('Should handle Call Task channel', () => {
930
- const callTaskData = {
931
- type: 'CALL_TASK',
932
- versions: {
933
- base: {
934
- content: {
935
- message: 'Call task message'
936
- }
937
- }
938
- }
939
- };
940
- renderFunction('CALL_TASK', 'editTemplate', callTaskData);
941
- expect(renderedComponent).toMatchSnapshot();
942
- });
943
-
944
- it('Should handle FTP channel', () => {
945
- const ftpData = {
946
- type: 'FTP',
947
- versions: {
948
- base: {
949
- content: {
950
- message: 'FTP message'
951
- }
952
- }
953
- }
954
- };
955
- renderFunction('FTP', 'editTemplate', ftpData);
956
- expect(renderedComponent).toMatchSnapshot();
957
- });
958
- });
959
-
960
- // Additional comprehensive test cases
961
- describe('Additional Channel Coverage', () => {
962
- it('Should handle Zalo channel', () => {
963
- const zaloData = {
964
- type: 'ZALO',
965
- versions: {
966
- base: {
967
- content: {
968
- message: 'Zalo message'
969
- }
970
- }
971
- }
972
- };
973
- renderFunction('ZALO', 'editTemplate', zaloData);
974
- expect(renderedComponent).toMatchSnapshot();
975
- });
976
-
977
- it('Should handle templates mode for various channels', () => {
978
- renderFunction('EMAIL', 'templates', {});
979
- expect(renderedComponent).toMatchSnapshot();
980
- });
981
-
982
- it('Should handle createTemplate mode with different options', () => {
983
- renderFunction('SMS', 'createTemplate', { mode: 'create' }, {
984
- isFullMode: true,
985
- smsRegister: 'DLT'
986
- });
987
- expect(renderedComponent).toMatchSnapshot();
988
- });
989
-
990
- it('Should handle complex LINE template with multiple message types', () => {
991
- const complexLineData = {
992
- type: 'LINE',
993
- versions: {
994
- base: {
995
- content: {
996
- messages: [
997
- {
998
- text: 'Hello',
999
- previewImageUrl: 'https://example.com/image.jpg'
1000
- },
1001
- {
1002
- selectedSticker: {
1003
- stickerUrl: 'https://example.com/sticker.png',
1004
- stickerId: '123',
1005
- packageId: '456'
1006
- }
1007
- },
1008
- {
1009
- baseUrl: 'https://example.com/map'
1010
- }
1011
- ]
1012
- }
1013
- }
1014
- }
1015
- };
1016
- renderFunction('LINE', 'preview', complexLineData);
1017
- expect(renderedComponent).toMatchSnapshot();
1018
- });
1019
- });
1020
703
  });