@capillarytech/creatives-library 8.0.80-alpha.0 → 8.0.81

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.
@@ -1199,13 +1199,14 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
1199
1199
  }
1200
1200
 
1201
1201
  getValidationData = () => {
1202
+
1202
1203
  const that = this;
1203
1204
  const formData = _.cloneDeep(this.state.formData);
1204
1205
  _.forEach(formData, (val, index) => {
1205
1206
  if (!isNaN(index)) {
1206
1207
  _.forEach(val.selectedLanguages, (language) => {
1208
+
1207
1209
  if (val[language].is_drag_drop) {
1208
- console.log('***getValidationData -1210', val[language].drag_drop_id, language);
1209
1210
  that.props.actions.getCmsData('edm', val[language].drag_drop_id, language);
1210
1211
  }
1211
1212
  });
@@ -1937,7 +1938,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
1937
1938
  }
1938
1939
  });
1939
1940
  } else if (this.state.mode === 'preview' || this.state.mode === 'switchEditor') {
1940
- console.log('***HandleEdmSave -1940', this.state.formData[`${this.state.currentTab - 1}`][this.state.formData[`${this.state.currentTab - 1}`].activeTab].drag_drop_id);
1941
1941
  this.props.actions.getCmsData('edm', this.state.formData[`${this.state.currentTab - 1}`][this.state.formData[`${this.state.currentTab - 1}`].activeTab].drag_drop_id);
1942
1942
  if (this.state.mode === 'switchEditor') {
1943
1943
  let schema = _.cloneDeep(this.state.schema);
@@ -55,7 +55,9 @@ export function* getAllAssets(assetType, queryParams) {
55
55
 
56
56
  export function* getCmsSetting({cmsType, projectId, cmsMode, langId, isEdmSupport}) {
57
57
  try {
58
+
58
59
  const result = yield call(Api.getCmsTemplateSettings, cmsType, projectId, cmsMode, langId, isEdmSupport);
60
+
59
61
  yield put({ type: types.GET_CMS_EDITOR_DETAILS_SUCCESS, settings: result.data.response.cmsDetails });
60
62
  } catch (error) {
61
63
  yield put({ type: types.GET_CMS_EDITOR_DETAILS_FAILURE, error });
@@ -65,6 +67,7 @@ export function* getCmsSetting({cmsType, projectId, cmsMode, langId, isEdmSuppor
65
67
  export function* getCmsData({cmsType, projectId, langId}) {
66
68
  try {
67
69
  const result = yield call(Api.getCmsTemplateData, cmsType, projectId, langId);
70
+
68
71
  yield put({ type: types.GET_CMS_EDITOR_DATA_SUCCESS, data: result.data.response.data, langId: result.data.response.langId });
69
72
  } catch (error) {
70
73
  yield put({ type: types.GET_CMS_EDITOR_DATA_FAILURE, error });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.80-alpha.0",
4
+ "version": "8.0.81",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
package/services/api.js CHANGED
@@ -2,13 +2,12 @@
2
2
  * Created by vivek on 3/5/17.
3
3
  */
4
4
  import 'whatwg-fetch';
5
- import { GA, decompressJsonObject } from '@capillarytech/cap-ui-utils';
6
- import get from 'lodash/get';
7
5
  import { loadItem, clearItem } from './localStorageApi';
8
6
  import config from '../config/app';
9
7
  import pathConfig from '../config/path';
10
8
  import getSchema from './getSchema';
11
9
  import * as API from '../utils/ApiCaller';
10
+ import { GA, decompressJsonObject } from '@capillarytech/cap-ui-utils';
12
11
  import { addBaseToTemplate } from '../utils/commonUtils';
13
12
  import { EMAIL, SMS } from '../v2Containers/CreativesContainer/constants';
14
13
  let API_ENDPOINT = config.development.api_endpoint;
@@ -301,17 +300,15 @@ export const getAllAssets = ({assetType, queryParams = {}}) => {
301
300
  });
302
301
  // const url = `https://nightly.capillary.in/arya/api/v1/creatives/assets/${assetType}?query=${JSON.stringify(queryParams)}`;
303
302
  return request(url, getAPICallObject('GET'));
304
- };
305
-
303
+ }
304
+ ;
306
305
  export const deleteAssetById = ({assetId, assetType}) => {
307
306
  const url = `${API_ENDPOINT}/assets/${assetId}/${assetType}`;
308
307
  return request(url, getAPICallObject('DELETE'));
309
308
  // return API.deleteResource(url);
310
309
  };
311
310
 
312
- export const uploadFile = ({
313
- file, assetType, fileParams, mode, wechatParams, whatsappParams,
314
- }) => {
311
+ export const uploadFile = ({file, assetType, fileParams, mode, wechatParams, whatsappParams}) => {
315
312
  const data = new FormData();
316
313
  let fileName = '';
317
314
  try {
@@ -406,20 +403,14 @@ export const getCmsData = (cmsType, projectId, langId) => {
406
403
  return API.get(url);
407
404
  };
408
405
 
409
- export const getEdmTemplates = async () => {
406
+ export const getEdmTemplates = () => {
410
407
  const url = `${API_ENDPOINT}/templates/email/default`;
411
- const compressedEdmTemplates = await request(url, getAPICallObject('GET'));
412
- const response = get(compressedEdmTemplates, 'response', '');
413
- const decompressedEdmTemplates = decompressJsonObject(response);
414
- return { ...compressedEdmTemplates, response: addBaseToTemplate(decompressedEdmTemplates) };
408
+ return request(url, getAPICallObject('GET'));
415
409
  };
416
410
 
417
- export const getDefaultBeeTemplates = async () => {
411
+ export const getDefaultBeeTemplates = () => {
418
412
  const url = `${API_ENDPOINT}/templates/v2/email/default`;
419
- const compressedDefaultBeeTemplates = await request(url, getAPICallObject('GET'));
420
- const response = get(compressedDefaultBeeTemplates, 'response', '');
421
- const decompressedBeeTemplates = decompressJsonObject(response);
422
- return { ...compressedDefaultBeeTemplates, response: addBaseToTemplate(decompressedBeeTemplates) };
413
+ return request(url, getAPICallObject('GET'));
423
414
  };
424
415
 
425
416
  // All about line
@@ -12,22 +12,17 @@ import {
12
12
  askAiraForLiquid,
13
13
  getLiquidTags,
14
14
  fetchSchemaForEntity,
15
- getDefaultBeeTemplates,
16
- getEdmTemplates,
17
15
  } from '../api';
18
16
  import { mockData } from './mockData';
19
17
  import getSchema from '../getSchema';
20
18
  const sampleFile = require('../../assets/line.png');
21
19
 
22
20
  let mockDecompressJsonObject;
23
- let mockAddBaseToTemplate;
24
21
  jest.mock('@capillarytech/cap-ui-utils', () => {
25
22
  mockDecompressJsonObject = jest.fn();
26
- mockAddBaseToTemplate = jest.fn();
27
23
  return {
28
24
  ...jest.requireActual('@capillarytech/cap-ui-utils'),
29
25
  decompressJsonObject: mockDecompressJsonObject,
30
- addBaseToTemplate: mockAddBaseToTemplate,
31
26
  };
32
27
  });
33
28
 
@@ -141,7 +136,7 @@ describe('getAllTemplates -- Test with valid responses', () => {
141
136
  global.fetch.mockReturnValue(Promise.resolve({json: () => Promise.resolve(),}));
142
137
  mockDecompressJsonObject.mockReturnValue();
143
138
  expect(await getAllTemplates({channel: 'email'})).toEqual({
144
- response: undefined,
139
+ "response": undefined,
145
140
  });
146
141
  });
147
142
 
@@ -498,114 +493,3 @@ describe('fetchSchemaForEntity', () => {
498
493
  expect(console.error).toHaveBeenCalledWith(mockError);
499
494
  });
500
495
  });
501
-
502
- describe('Email Template APIs', () => {
503
- beforeEach(() => {
504
- global.fetch = jest.fn();
505
- });
506
-
507
- afterEach(() => {
508
- jest.restoreAllMocks();
509
- });
510
-
511
- const sharedTests = (apiFunction, functionName) => {
512
- it('should handle successful response', async () => {
513
- const mockResponse = { response: 'compressed-data' };
514
- const mockDecompressed = { template: 'decompressed-data' };
515
- const mockWithBase = { template: 'data-with-base' };
516
-
517
- global.fetch.mockReturnValue(Promise.resolve({
518
- status: 200,
519
- json: () => Promise.resolve(mockResponse),
520
- }));
521
- mockDecompressJsonObject.mockReturnValue(mockDecompressed);
522
- mockAddBaseToTemplate.mockReturnValue(mockWithBase);
523
-
524
- const result = await apiFunction();
525
- expect(result).toEqual({
526
- ...mockResponse,
527
- response: mockDecompressed,
528
- });
529
- });
530
-
531
- it('should handle response property as empty object', async () => {
532
- const mockResponse = { response: {} };
533
-
534
- global.fetch.mockReturnValue(Promise.resolve({
535
- status: 200,
536
- json: () => Promise.resolve(mockResponse),
537
- }));
538
- mockDecompressJsonObject.mockReturnValue({});
539
- mockAddBaseToTemplate.mockReturnValue({});
540
-
541
- const result = await apiFunction();
542
- expect(result).toEqual({
543
- ...mockResponse,
544
- response: {},
545
- });
546
- });
547
-
548
- it('should handle missing response property', async () => {
549
- const mockResponse = {};
550
-
551
- global.fetch.mockReturnValue(Promise.resolve({
552
- status: 200,
553
- json: () => Promise.resolve(mockResponse),
554
- }));
555
- mockDecompressJsonObject.mockReturnValue('');
556
- mockAddBaseToTemplate.mockReturnValue('');
557
-
558
- const result = await apiFunction();
559
- expect(result).toEqual({
560
- response: '',
561
- });
562
- });
563
-
564
- it('should handle null response', async () => {
565
- global.fetch.mockReturnValue(Promise.resolve({
566
- json: () => Promise.resolve(null),
567
- }));
568
- mockDecompressJsonObject.mockReturnValue('');
569
- mockAddBaseToTemplate.mockReturnValue('');
570
-
571
- const result = await apiFunction();
572
- expect(result).toEqual({
573
- response: '',
574
- });
575
- });
576
-
577
- it('should handle undefined response', async () => {
578
- global.fetch.mockReturnValue(Promise.resolve({
579
- json: () => Promise.resolve(undefined),
580
- }));
581
- mockDecompressJsonObject.mockReturnValue('');
582
- mockAddBaseToTemplate.mockReturnValue('');
583
-
584
- const result = await apiFunction();
585
- expect(result).toEqual({
586
- response: '',
587
- });
588
- });
589
-
590
- it('should handle fetch failure', async () => {
591
- global.fetch.mockRejectedValue(new Error('Network error'));
592
- mockDecompressJsonObject.mockReturnValue(undefined);
593
- mockAddBaseToTemplate.mockReturnValue(undefined);
594
- console.error = jest.fn();
595
-
596
- const result = await apiFunction();
597
-
598
- expect(result).toEqual({
599
- response: undefined,
600
- });
601
- });
602
- };
603
-
604
- describe('getEdmTemplates', () => {
605
- sharedTests(getEdmTemplates, 'getEdmTemplates');
606
- });
607
-
608
- describe('getDefaultBeeTemplates', () => {
609
- sharedTests(getDefaultBeeTemplates, 'getDefaultBeeTemplates');
610
- });
611
- });
@@ -998,7 +998,7 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
998
998
  if (tagValidationResponse?.isContentEmpty) {
999
999
  errorString += this.props.intl.formatMessage(messages.emailBodyEmptyError);
1000
1000
  // Adds a bypass for cases where content is initially empty in the creation flow.
1001
- if(this.liquidFlow && !this.props.isEdit){
1001
+ if(this.liquidFlow){
1002
1002
  errorString = "";
1003
1003
  isLiquidValid = true;
1004
1004
  }
@@ -1102,13 +1102,16 @@ class FormBuilder extends React.Component { // eslint-disable-line react/prefer-
1102
1102
  const handleResult = (result) => {
1103
1103
  const {formatMessage} = this.props.intl;
1104
1104
  // Checks for errors in the result and displays them if any are found.
1105
- if (result?.errors?.length > 0) {
1105
+ const validString = /\S/.test(content);
1106
+ // If the content is empty or there are errors in the result, display the necessary validation messages.
1107
+ if (result?.errors?.length > 0 || !validString) {
1106
1108
  this.setState((prevState) => ({
1107
1109
  liquidErrorMessage: {
1108
1110
  ...prevState.liquidErrorMessage,
1111
+ STANDARD_ERROR_MSG: !validString ? [...(prevState.liquidErrorMessage?.STANDARD_ERROR_MSG || []), formatMessage(messages.emailBodyEmptyError)] : prevState.liquidErrorMessage?.STANDARD_ERROR_MSG,
1109
1112
  LIQUID_ERROR_MSG: result?.errors?.map(error => error?.message) ?? [formatMessage(messages.somethingWentWrong)],
1110
- },
1111
- }) , () => {
1113
+ }
1114
+ }), () => {
1112
1115
  this.props.showLiquidErrorInFooter(this.state.liquidErrorMessage);
1113
1116
  });
1114
1117
  this.props.stopValidation();
@@ -31,6 +31,7 @@ export function* getTemplateDetails(id, channel) {
31
31
  export function* getCmsData({cmsType, projectId}) {
32
32
  try {
33
33
  const result = yield call(Api.getCmsData, cmsType, projectId);
34
+
34
35
  yield put({ type: types.GET_CMS_EDITOR_DATA_SUCCESS, data: decodeURIComponent(result.data.response.cmsData.htmlContent) });
35
36
  } catch (error) {
36
37
  yield put({ type: types.GET_CMS_EDITOR_DATA_FAILURE, error });
@@ -36,7 +36,7 @@ import { TRACK_CREATE_EMAIL, TRACK_EDIT_EMAIL, BEE_PLUGIN, CREATE, EDIT } from '
36
36
  import { FONT_COLOR_05 } from '@capillarytech/cap-ui-library/styled/variables';
37
37
  import { gtmPush } from '../../utils/gtmTrackers';
38
38
  const {CapCustomCardList} = CapCustomCard;
39
- import {storeS3FileSizeDetails, CREATIVES_S3_ASSET_FILESIZES} from '../../utils/cdnTransformation';
39
+ import {transformEmailTemplates, storeS3FileSizeDetails, CREATIVES_S3_ASSET_FILESIZES} from '../../utils/cdnTransformation';
40
40
  import { CUSTOMER_BARCODE_TAG } from '../../containers/App/constants';
41
41
  import injectReducer from '../../utils/injectReducer';
42
42
  import v2EmailReducer from './reducer';
@@ -199,9 +199,9 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
199
199
  this.setState({isDragDrop: true});
200
200
  }
201
201
  if (this.props.params.id) {
202
- this.props.actions.getCmsSetting(BEE_PLUGIN, _.get(this.props.Templates.BEETemplate, 'versions.base.drag_drop_id', this.props.Templates.BEETemplate?._id), 'open', undefined, isBEESupport, isBEEAppEnable);
202
+ this.props.actions.getCmsSetting(BEE_PLUGIN, this.props.Templates.BEETemplate._id, 'open', undefined, isBEESupport, isBEEAppEnable);
203
203
  } else if (this.props.location.query.module !== "library" || (this.props.location.query.module === "library" && !this.props.templateData)) {
204
- this.props.actions.getCmsSetting(BEE_PLUGIN, _.get(this.props.Templates.BEETemplate, 'versions.base.drag_drop_id', this.props.Templates.BEETemplate?._id), 'create', undefined, isBEESupport, isBEEAppEnable);
204
+ this.props.actions.getCmsSetting(BEE_PLUGIN, this.props.Templates.BEETemplate._id, 'create', undefined, isBEESupport, isBEEAppEnable);
205
205
  }
206
206
  }
207
207
  this.setState({ content: (this.props.Templates.selectedEmailLayout ? this.props.Templates.selectedEmailLayout : ''), formData});
@@ -497,7 +497,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
497
497
  const isDragDrop = this.state.formData[`${this.state.currentTab - 1}`][this.state.formData[`${this.state.currentTab - 1}`].activeTab].is_drag_drop;
498
498
  const isBEESupport = (this.props.location.query.isBEESupport !== "false") || false;
499
499
  if (isDragDrop && isBEESupport && this.checkBeeEditorAllowedForLibrary()) {
500
- console.log('***Handle EDM -500', this.edmEvent, JSON.parse(this.edmEvent.data));
501
500
  const evtData = JSON.parse(this.edmEvent.data);
502
501
  if (evtData.action === 'editBackground') {
503
502
  this.edmEvent.source.postMessage(JSON.stringify({
@@ -512,7 +511,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
512
511
  },
513
512
  }), "*");
514
513
  } else if (evtData.action === 'edit') {
515
- console.log('***Handle EDM -515', this.edmEvent, JSON.parse(this.edmEvent.data));
516
514
  this.edmEvent.source.postMessage(JSON.stringify({
517
515
  _dynId: JSON.parse(this.edmEvent.data)._dynId,
518
516
  action: "setProps",
@@ -750,8 +748,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
750
748
  }
751
749
  getValidationData = () => {
752
750
  const that = this;
753
- console.log('***getValidationData -753', this.props);
754
- let id = _.get(this.props, 'Email.templateDetails._id', '') || _.get(this.props, 'Templates.BEETemplate.versions.base.drag_drop_id', '');
751
+ let id = _.get(this.props, 'Email.templateDetails._id', '') || _.get(this.props, 'Templates.BEETemplate._id', '');
755
752
  const formData = _.cloneDeep(this.state.formData);
756
753
  if (!id) {
757
754
  id = _.get(formData, `base[${formData.base.activeTab}].id`, '');
@@ -762,7 +759,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
762
759
  if (!isNaN(index)) {
763
760
  _.forEach(val.selectedLanguages, (language) => {
764
761
  if (val[language].is_drag_drop && this.checkBeeEditorAllowedForLibrary()) {
765
- console.log('***getValidationData -764', val[language].drag_drop_id, language, id);
766
762
  that.props.actions.getCmsData(BEE_PLUGIN, id, language);
767
763
  }
768
764
  });
@@ -932,7 +928,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
932
928
  // formData.base.activeTab = language;
933
929
  // }
934
930
  if (language && editData.versions.base[language].is_drag_drop && isBEEAppEnable) {
935
- console.log('***getCmsSetting -934', editData, language);
936
931
  this.props.actions.getCmsSetting(BEE_PLUGIN, editData._id, 'open', language, isBEESupport, isBEEAppEnable);
937
932
  }
938
933
  });
@@ -1410,8 +1405,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
1410
1405
  }
1411
1406
  });
1412
1407
  } else if (this.state.mode === 'preview' || this.state.mode === 'switchEditor') {
1413
- let id = _.get(this.props, 'Email.templateDetails._id', '') || _.get(this.props, 'Templates.BEETemplate.versions.base.drag_drop_id', '');
1414
- console.log('***Handle EDM -864', id);
1408
+ let id = _.get(this.props, 'Email.templateDetails._id', '') || _.get(this.props, 'Templates.BEETemplate._id', '');
1415
1409
  const formData = _.cloneDeep(this.state.formData);
1416
1410
  if (!id) {
1417
1411
  id = _.get(formData, `base[${formData.base.activeTab}].id`, '');
@@ -1526,7 +1520,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
1526
1520
  storeS3FileSizeDetails(data?.metaInfo?.secure_file_path, data?.metaInfo?.file_size);
1527
1521
 
1528
1522
  if (isDragDrop && isEdmSupport && this.checkBeeEditorAllowedForLibrary()) {
1529
- console.log('***Handle EDM -1524', this.edmEvent, JSON.parse(this.edmEvent.data));
1530
1523
  const evtData = JSON.parse(this.edmEvent.data);
1531
1524
  if (evtData.action === 'editBackground') {
1532
1525
  this.edmEvent.source.postMessage(JSON.stringify({
@@ -1541,7 +1534,6 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
1541
1534
  },
1542
1535
  }), "*");
1543
1536
  } else if (evtData.action === 'edit') {
1544
- console.log('***Handle EDM -1539', this.edmEvent, JSON.parse(this.edmEvent.data));
1545
1537
  this.edmEvent.source.postMessage(JSON.stringify({
1546
1538
  _dynId: JSON.parse(this.edmEvent.data)._dynId,
1547
1539
  action: "setProps",
@@ -2510,9 +2502,7 @@ export class Email extends React.Component { // eslint-disable-line react/prefer
2510
2502
  };
2511
2503
  handleEdmDefaultTemplateSelection = (isSelected, id) => {
2512
2504
  if (isSelected) {
2513
- console.log('***handleEdmDefaultTemplateSelection -2512', this.props.Templates.cmsTemplates);
2514
2505
  const data = _.find(this.props.Templates.cmsTemplates, {_id: id});
2515
- console.log('***handleEdmDefaultTemplateSelection -2513', data);
2516
2506
  // const type = this.props.location.query.type;
2517
2507
  // const module = this.props.location.query.module ? this.props.location.query.module : 'default';
2518
2508
  // const isLanguageSupport = (this.props.location.query.isLanguageSupport) ? this.props.location.query.isLanguageSupport : true;
@@ -0,0 +1,103 @@
1
+ export const emptyInputs = {
2
+ emptyObject: {},
3
+ nullInput: null,
4
+ undefinedInput: undefined,
5
+ expectedOutput: {}
6
+ };
7
+
8
+ export const noValidPackageIdsInput = {
9
+ input: {
10
+ standard: [
11
+ { _id: '1', definition: { stickerUrl: 'url1' } },
12
+ { _id: '2', definition: {} }
13
+ ],
14
+ custom: [
15
+ { _id: '3', definition: null },
16
+ { _id: '4' }
17
+ ]
18
+ },
19
+ expectedOutput: {}
20
+ };
21
+
22
+ export const singleCategoryInput = {
23
+ input: {
24
+ standard: [
25
+ { _id: '1', definition: { packageId: 'pkg1', stickerUrl: 'url1' } },
26
+ { _id: '2', definition: { packageId: 'pkg1', stickerUrl: 'url2' } },
27
+ { _id: '3', definition: { packageId: 'pkg2', stickerUrl: 'url3' } }
28
+ ]
29
+ },
30
+ expectedOutput: {
31
+ pkg1: [
32
+ { _id: '1', definition: { packageId: 'pkg1', stickerUrl: 'url1' } },
33
+ { _id: '2', definition: { packageId: 'pkg1', stickerUrl: 'url2' } }
34
+ ],
35
+ pkg2: [
36
+ { _id: '3', definition: { packageId: 'pkg2', stickerUrl: 'url3' } }
37
+ ]
38
+ }
39
+ };
40
+
41
+ export const multipleCategoriesInput = {
42
+ input: {
43
+ standard: [
44
+ { _id: '1', definition: { packageId: 'pkg1', stickerUrl: 'url1' } },
45
+ { _id: '2', definition: { packageId: 'pkg2', stickerUrl: 'url2' } }
46
+ ],
47
+ custom: [
48
+ { _id: '3', definition: { packageId: 'pkg1', stickerUrl: 'url3' } },
49
+ { _id: '4', definition: { packageId: 'pkg3', stickerUrl: 'url4' } }
50
+ ],
51
+ extended: [
52
+ { _id: '5', definition: { packageId: 'pkg2', stickerUrl: 'url5' } }
53
+ ]
54
+ },
55
+ expectedOutput: {
56
+ pkg1: [
57
+ { _id: '1', definition: { packageId: 'pkg1', stickerUrl: 'url1' } },
58
+ { _id: '3', definition: { packageId: 'pkg1', stickerUrl: 'url3' } }
59
+ ],
60
+ pkg2: [
61
+ { _id: '2', definition: { packageId: 'pkg2', stickerUrl: 'url2' } },
62
+ { _id: '5', definition: { packageId: 'pkg2', stickerUrl: 'url5' } }
63
+ ],
64
+ pkg3: [
65
+ { _id: '4', definition: { packageId: 'pkg3', stickerUrl: 'url4' } }
66
+ ]
67
+ }
68
+ };
69
+
70
+ export const nonArrayCategoriesInput = {
71
+ input: {
72
+ standard: [
73
+ { _id: '1', definition: { packageId: 'pkg1', stickerUrl: 'url1' } }
74
+ ],
75
+ custom: 'not an array',
76
+ extended: null
77
+ },
78
+ expectedOutput: {
79
+ pkg1: [
80
+ { _id: '1', definition: { packageId: 'pkg1', stickerUrl: 'url1' } }
81
+ ]
82
+ }
83
+ };
84
+
85
+ export const mixedStickerDataInput = {
86
+ input: {
87
+ standard: [
88
+ { _id: '1', definition: { packageId: 'pkg1', stickerUrl: 'url1' } },
89
+ { _id: '2', definition: { stickerUrl: 'url2' } },
90
+ { _id: '3' }
91
+ ],
92
+ custom: [
93
+ { _id: '4', definition: { packageId: 'pkg1', stickerUrl: 'url4' } },
94
+ { _id: '5', definition: { packageId: null, stickerUrl: 'url5' } }
95
+ ]
96
+ },
97
+ expectedOutput: {
98
+ pkg1: [
99
+ { _id: '1', definition: { packageId: 'pkg1', stickerUrl: 'url1' } },
100
+ { _id: '4', definition: { packageId: 'pkg1', stickerUrl: 'url4' } }
101
+ ]
102
+ }
103
+ };
@@ -0,0 +1,37 @@
1
+ import { groupLineStickers } from '../utils';
2
+ import {
3
+ emptyInputs,
4
+ noValidPackageIdsInput,
5
+ singleCategoryInput,
6
+ multipleCategoriesInput,
7
+ nonArrayCategoriesInput,
8
+ mixedStickerDataInput
9
+ } from './mockData';
10
+
11
+ describe('groupLineStickers', () => {
12
+ it('should return empty object when input is empty', () => {
13
+ expect(groupLineStickers(emptyInputs.emptyObject)).toEqual(emptyInputs.expectedOutput);
14
+ expect(groupLineStickers(emptyInputs.nullInput)).toEqual(emptyInputs.expectedOutput);
15
+ expect(groupLineStickers(emptyInputs.undefinedInput)).toEqual(emptyInputs.expectedOutput);
16
+ });
17
+
18
+ it('should handle input with no valid packageIds', () => {
19
+ expect(groupLineStickers(noValidPackageIdsInput.input)).toEqual(noValidPackageIdsInput.expectedOutput);
20
+ });
21
+
22
+ it('should group stickers by packageId within single category', () => {
23
+ expect(groupLineStickers(singleCategoryInput.input)).toEqual(singleCategoryInput.expectedOutput);
24
+ });
25
+
26
+ it('should group stickers by packageId across multiple categories', () => {
27
+ expect(groupLineStickers(multipleCategoriesInput.input)).toEqual(multipleCategoriesInput.expectedOutput);
28
+ });
29
+
30
+ it('should skip non-array categories', () => {
31
+ expect(groupLineStickers(nonArrayCategoriesInput.input)).toEqual(nonArrayCategoriesInput.expectedOutput);
32
+ });
33
+
34
+ it('should handle mixed valid and invalid sticker data', () => {
35
+ expect(groupLineStickers(mixedStickerDataInput.input)).toEqual(mixedStickerDataInput.expectedOutput);
36
+ });
37
+ });
@@ -6,15 +6,26 @@ import CapColumn from '@capillarytech/cap-ui-library/CapColumn';
6
6
 
7
7
  export const groupLineStickers = (lineStickers) => {
8
8
  const stickerGroup = {};
9
- lineStickers.forEach((lineSticker) => {
10
- const packageId = get(lineSticker, 'definition.packageId');
11
- if (packageId) {
12
- const stickerGroupPackageId = stickerGroup[packageId];
13
- if (stickerGroupPackageId && Array.isArray(stickerGroupPackageId)) {
14
- stickerGroup[packageId].push(lineSticker);
15
- } else {
16
- stickerGroup[packageId] = [];
17
- }
9
+ // Return early if lineStickers is null or undefined
10
+ if (!lineStickers) return stickerGroup;
11
+
12
+ // Now we can safely iterate as lineStickers is defined
13
+ Object?.keys(lineStickers)?.forEach((key) => {
14
+ const category = lineStickers[key];
15
+ if (Array.isArray(category)) {
16
+ //iterating over each category [standard, custom, extended]
17
+ category?.forEach((lineSticker) => {
18
+ const packageId = get(lineSticker, 'definition.packageId');
19
+ if (packageId) {
20
+ const stickerGroupPackageId = stickerGroup[packageId];
21
+ if (stickerGroupPackageId && Array.isArray(stickerGroupPackageId)) {
22
+ stickerGroup[packageId].push(lineSticker);
23
+ } else {
24
+ //initialising with first sticker for each package ID
25
+ stickerGroup[packageId] = [lineSticker];
26
+ }
27
+ }
28
+ });
18
29
  }
19
30
  });
20
31
  return stickerGroup;
@@ -35,7 +46,7 @@ export const getContent = ({ lineStickers, onStickerSelect }) => (
35
46
  >
36
47
  <CapImage
37
48
  src={definition.stickerUrl}
38
- rest={{ height: 70 }}
49
+ rest = {{ height: 70 }}
39
50
  rest={{
40
51
  height: 70,
41
52
  onMouseOut: e => (e.currentTarget.src = definition.stickerUrl),