@capillarytech/creatives-library 8.0.168 → 8.0.170-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/app.js +4 -0
  2. package/containers/App/constants.js +2 -0
  3. package/package.json +2 -1
  4. package/utils/commonUtils.js +50 -0
  5. package/utils/test-utils.js +6 -2
  6. package/utils/tests/newrelic.test.js +546 -0
  7. package/v2Components/CapActionButton/index.js +52 -12
  8. package/v2Components/CapActionButton/messages.js +4 -0
  9. package/v2Components/CapActionButton/tests/index.test.js +135 -0
  10. package/v2Components/CapDeviceContent/index.js +5 -0
  11. package/v2Components/CapInAppCTA/index.js +29 -14
  12. package/v2Components/CapInAppCTA/index.scss +0 -2
  13. package/v2Components/CapInAppCTA/messages.js +4 -0
  14. package/v2Components/CapMpushCTA/index.js +54 -38
  15. package/v2Components/CapMpushCTA/index.scss +2 -2
  16. package/v2Components/CapMpushCTA/messages.js +4 -0
  17. package/v2Components/CapTagListWithInput/index.js +169 -0
  18. package/v2Components/CapTagListWithInput/messages.js +10 -0
  19. package/v2Components/FormBuilder/index.js +93 -1
  20. package/v2Components/TestAndPreviewSlidebox/PreviewSection.js +1 -1
  21. package/v2Components/TestAndPreviewSlidebox/index.js +24 -4
  22. package/v2Containers/Email/index.js +64 -3
  23. package/v2Containers/Email/initialSchema.js +7 -21
  24. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +2 -2
  25. package/v2Containers/Line/Container/Wrapper/tests/index.test.js +56 -1
  26. package/v2Containers/Line/Container/Wrapper/utils.js +6 -4
  27. package/v2Containers/MobilePush/Create/index.js +24 -3
  28. package/v2Containers/MobilePush/commonMethods.js +25 -3
  29. package/v2Containers/MobilePushNew/components/CtaButtons.js +20 -0
  30. package/v2Containers/MobilePushNew/components/MediaUploaders.js +31 -3
  31. package/v2Containers/MobilePushNew/components/PlatformContentFields.js +34 -3
  32. package/v2Containers/MobilePushNew/components/tests/MediaUploaders.test.js +200 -5
  33. package/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +59 -1
  34. package/v2Containers/MobilePushNew/index.js +9 -0
  35. package/v2Containers/MobilePushNew/index.scss +2 -1
  36. package/v2Containers/Rcs/index.js +77 -71
  37. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +15270 -492
  38. package/v2Containers/Viber/index.js +102 -76
@@ -202,11 +202,33 @@ function getLinkTypeFields({inputFieldsArgs, fieldIndex, deepLinkOptions, formDa
202
202
  identifier: inputId,
203
203
 
204
204
  cols: [
205
-
205
+ {
206
+ // Add label control for external link input
207
+ id: "title-tagList",
208
+ label: "Add label",
209
+ type: "tag-list",
210
+ metaType: "List",
211
+ datatype: "select",
212
+ required: true,
213
+ fluid: true,
214
+ onlyDisplay: true,
215
+ style: {
216
+ marginRight: "10%",
217
+ marginTop: "-3%",
218
+ },
219
+ styling: "semantic",
220
+ order: 2,
221
+ customComponent: true,
222
+ // Helps handler identify the destination input
223
+ target: inputId,
224
+ supportedEvents: [
225
+ "onTagSelect",
226
+ ],
227
+ },
206
228
  {
207
229
  id: inputId,
208
230
  placeholder: "External Link",
209
- type: "input", //Resembles component to be used
231
+ type: "input", //Resembles component to be used
210
232
  metaType: "text",
211
233
  datatype: "string",
212
234
  errorMessage: <FormattedMessage {...messages.invalidExternalLink}/>,
@@ -215,7 +237,7 @@ function getLinkTypeFields({inputFieldsArgs, fieldIndex, deepLinkOptions, formDa
215
237
  width: 18,
216
238
  offset: 1,
217
239
  style: {
218
- width: '100%',
240
+ width: '95%',
219
241
  },
220
242
  styling: "semantic",
221
243
  order: 1,
@@ -19,6 +19,11 @@ const CtaButtons = ({
19
19
  updateHandler,
20
20
  deleteHandler,
21
21
  deepLink,
22
+ location,
23
+ tags,
24
+ injectedTags,
25
+ selectedOfferDetails,
26
+ handleOnTagsContextChange,
22
27
  }) => {
23
28
  // Local state to control CTA form visibility
24
29
  const [showPrimaryCTA, setShowPrimaryCTA] = useState(false);
@@ -130,6 +135,11 @@ const CtaButtons = ({
130
135
  deleteHandler={handleDelete}
131
136
  deepLink={deepLink}
132
137
  buttonType={PRIMARY}
138
+ location={location}
139
+ tags={tags}
140
+ injectedTags={injectedTags}
141
+ selectedOfferDetails={selectedOfferDetails}
142
+ handleOnTagsContextChange={handleOnTagsContextChange}
133
143
  />
134
144
  )}
135
145
  {isPrimaryButtonSaved && !isSecondaryButtonSaved && !shouldShowSecondaryCTA && (
@@ -174,10 +184,20 @@ CtaButtons.propTypes = {
174
184
  updateHandler: PropTypes.func.isRequired,
175
185
  deleteHandler: PropTypes.func.isRequired,
176
186
  deepLink: PropTypes.array,
187
+ handleOnTagsContextChange: PropTypes.func,
188
+ location: PropTypes.object,
189
+ tags: PropTypes.array,
190
+ injectedTags: PropTypes.object,
191
+ selectedOfferDetails: PropTypes.array,
177
192
  };
178
193
 
179
194
  CtaButtons.defaultProps = {
180
195
  deepLink: [],
196
+ handleOnTagsContextChange: () => {},
197
+ location: {},
198
+ tags: [],
199
+ injectedTags: {},
200
+ selectedOfferDetails: [],
181
201
  };
182
202
 
183
203
  export default memo(CtaButtons);
@@ -43,6 +43,7 @@ import {
43
43
  } from "../constants";
44
44
  import messages from "../messages";
45
45
  import { validateExternalLink, validateDeepLink } from "../utils";
46
+ import TagList from "../../TagList";
46
47
 
47
48
  // Initial carousel data structure
48
49
  const CAROUSEL_INITIAL_DATA = {
@@ -102,6 +103,11 @@ const MediaUploaders = ({
102
103
  setCarouselActiveTabIndex,
103
104
  carouselLinkErrors = {}, // Carousel link errors from parent
104
105
  updateCarouselLinkError, // Function to update carousel link errors in parent
106
+ location,
107
+ tags,
108
+ injectedTags,
109
+ selectedOfferDetails,
110
+ handleOnTagsContextChange,
105
111
  }) => {
106
112
  // Carousel state management - separate for Android and iOS
107
113
  const [carouselMediaType, setCarouselMediaType] = useState(IMAGE.toLowerCase());
@@ -563,6 +569,14 @@ const MediaUploaders = ({
563
569
 
564
570
  const button = card?.buttons[0]; // We're handling only one button for now
565
571
 
572
+ const onButtonTagSelect = useCallback(
573
+ (value) => {
574
+ const newUrl = `${button?.externalLinkValue}{{${value}}}`;
575
+ handleCarouselExternalLinkChange(cardIndex, newUrl);
576
+ },
577
+ [handleCarouselExternalLinkChange, button?.externalLinkValue]
578
+ );
579
+
566
580
  return (
567
581
  <>
568
582
  <CapDivider />
@@ -633,9 +647,23 @@ const MediaUploaders = ({
633
647
  )}
634
648
  {button?.linkType === EXTERNAL_LINK && (
635
649
  <CapColumn span={14}>
636
- <CapHeading type="h4" className="buttons-heading">
637
- {formatMessage(messages.externalLink)}
638
- </CapHeading>
650
+ <CapRow>
651
+ <CapHeading type="h4" className="buttons-heading">
652
+ {formatMessage(messages.externalLink)}
653
+ </CapHeading>
654
+ <TagList
655
+ moduleFilterEnabled={
656
+ location?.query?.type !== "embedded"
657
+ }
658
+ label={formatMessage(messages.addLabels)}
659
+ onTagSelect={onButtonTagSelect}
660
+ onContextChange={handleOnTagsContextChange}
661
+ location={location}
662
+ tags={tags}
663
+ injectedTags={injectedTags}
664
+ selectedOfferDetails={selectedOfferDetails}
665
+ />
666
+ </CapRow>
639
667
  <CapInput
640
668
  id={`mobile-push-external-link-input-${activeTab}-${cardIndex}`}
641
669
  onChange={(e) => handleCarouselExternalLinkChange(cardIndex, e.target.value)}
@@ -36,6 +36,10 @@ const PlatformContentFields = ({
36
36
  linkProps,
37
37
  sameContent,
38
38
  formatMessage,
39
+ location,
40
+ tags,
41
+ injectedTags,
42
+ selectedOfferDetails,
39
43
  }) => {
40
44
  const { title: titleError, message: messageError } = errors;
41
45
  const {
@@ -145,6 +149,14 @@ const PlatformContentFields = ({
145
149
  [tagListProps, onTagSelect, handleOnTagsContextChange]
146
150
  );
147
151
 
152
+ const onButtonTagSelect = useCallback(
153
+ (value) => {
154
+ const newUrl = `${externalLinkValue}{{${value}}}`;
155
+ handleExternalLinkChange(newUrl);
156
+ },
157
+ [handleExternalLinkChange, externalLinkValue]
158
+ );
159
+
148
160
  return (
149
161
  <>
150
162
  {sameContent && (
@@ -214,6 +226,11 @@ const PlatformContentFields = ({
214
226
  mediaType={content.mediaType}
215
227
  {...mediaUploaderProps}
216
228
  formatMessage={formatMessage}
229
+ location={location}
230
+ tags={tags}
231
+ injectedTags={injectedTags}
232
+ selectedOfferDetails={selectedOfferDetails}
233
+ handleOnTagsContextChange={handleOnTagsContextChange}
217
234
  />
218
235
  </CapRow>
219
236
  {content?.mediaType !== CAROUSEL && (
@@ -281,9 +298,23 @@ const PlatformContentFields = ({
281
298
  )}
282
299
  {content.linkType === EXTERNAL_LINK && (
283
300
  <CapColumn span={14}>
284
- <CapHeading type="h4" className="buttons-heading">
285
- {formatMessage(messages.externalLink)}
286
- </CapHeading>
301
+ <CapRow style={{ display: "flex" }}>
302
+ <CapHeading type="h4" className="buttons-heading">
303
+ {formatMessage(messages.externalLink)}
304
+ </CapHeading>
305
+ <TagList
306
+ moduleFilterEnabled={
307
+ location?.query?.type !== "embedded"
308
+ }
309
+ label={formatMessage(messages.addLabels)}
310
+ onTagSelect={onButtonTagSelect}
311
+ onContextChange={handleOnTagsContextChange}
312
+ location={location}
313
+ tags={tags}
314
+ injectedTags={injectedTags}
315
+ selectedOfferDetails={selectedOfferDetails}
316
+ />
317
+ </CapRow>
287
318
  <CapInput
288
319
  id="mobile-push-external-link-input"
289
320
  onChange={onExternalLinkChange}
@@ -53,6 +53,149 @@ jest.mock('../../../../v2Components/CapImageUpload', () =>
53
53
  }
54
54
  );
55
55
 
56
+ // Mock Cap UI Library components that use styled-components
57
+ jest.mock('@capillarytech/cap-ui-library/CapButton', () =>
58
+ function MockCapButton({ children, ...props }) {
59
+ return <button {...props}>{children}</button>;
60
+ }
61
+ );
62
+
63
+ jest.mock('@capillarytech/cap-ui-library/CapCard', () =>
64
+ function MockCapCard({ children, title, extra, ...props }) {
65
+ return (
66
+ <div {...props} data-testid="cap-card">
67
+ {title && <div data-testid="card-title">{title}</div>}
68
+ {extra && <div data-testid="card-extra">{extra}</div>}
69
+ {children}
70
+ </div>
71
+ );
72
+ }
73
+ );
74
+
75
+ jest.mock('@capillarytech/cap-ui-library/CapIcon', () =>
76
+ function MockCapIcon({ type, ...props }) {
77
+ return <span {...props} data-testid={`cap-icon-${type}`}>Icon</span>;
78
+ }
79
+ );
80
+
81
+ jest.mock('@capillarytech/cap-ui-library/CapRadioGroup', () =>
82
+ function MockCapRadioGroup({ options, value, onChange, ...props }) {
83
+ return (
84
+ <div {...props}>
85
+ {options?.map((option, index) => (
86
+ <label key={index}>
87
+ <input
88
+ type="radio"
89
+ checked={value === option.value}
90
+ onChange={() => onChange(option.value)}
91
+ />
92
+ {option.label}
93
+ </label>
94
+ ))}
95
+ </div>
96
+ );
97
+ }
98
+ );
99
+
100
+ jest.mock('@capillarytech/cap-ui-library/CapSelect', () => ({
101
+ CapCustomSelect: function MockCapCustomSelect({ options, value, onChange, placeholder, ...props }) {
102
+ return (
103
+ <select {...props} value={value} onChange={(e) => onChange(e.target.value)}>
104
+ <option value="">{placeholder}</option>
105
+ {options?.map((option, index) => (
106
+ <option key={index} value={option.value}>
107
+ {option.label}
108
+ </option>
109
+ ))}
110
+ </select>
111
+ );
112
+ }
113
+ }));
114
+
115
+ jest.mock('@capillarytech/cap-ui-library/CapRow', () =>
116
+ function MockCapRow({ children, ...props }) {
117
+ return <div {...props} data-testid="cap-row">{children}</div>;
118
+ }
119
+ );
120
+
121
+ jest.mock('@capillarytech/cap-ui-library/CapColumn', () =>
122
+ function MockCapColumn({ children, span, ...props }) {
123
+ return <div {...props} data-testid="cap-column">{children}</div>;
124
+ }
125
+ );
126
+
127
+ jest.mock('@capillarytech/cap-ui-library/CapHeading', () =>
128
+ function MockCapHeading({ children, type, ...props }) {
129
+ const Tag = type === 'h4' ? 'h4' : 'div';
130
+ return <Tag {...props} data-testid="cap-heading">{children}</Tag>;
131
+ }
132
+ );
133
+
134
+ jest.mock('@capillarytech/cap-ui-library/CapDivider', () =>
135
+ function MockCapDivider({ type, ...props }) {
136
+ return <hr {...props} data-testid="cap-divider" />;
137
+ }
138
+ );
139
+
140
+ jest.mock('@capillarytech/cap-ui-library/CapCheckbox', () =>
141
+ function MockCapCheckbox({ children, checked, onChange, ...props }) {
142
+ return (
143
+ <label>
144
+ <input
145
+ type="checkbox"
146
+ checked={checked}
147
+ onChange={(e) => onChange(e)}
148
+ {...props}
149
+ />
150
+ {children}
151
+ </label>
152
+ );
153
+ }
154
+ );
155
+
156
+ jest.mock('@capillarytech/cap-ui-library/CapLabel', () =>
157
+ function MockCapLabel({ children, ...props }) {
158
+ return <label {...props} data-testid="cap-label">{children}</label>;
159
+ }
160
+ );
161
+
162
+ jest.mock('@capillarytech/cap-ui-library/CapInput', () =>
163
+ function MockCapInput({ onChange, value, placeholder, error, ...props }) {
164
+ return (
165
+ <input
166
+ type="text"
167
+ value={value || ''}
168
+ onChange={onChange}
169
+ placeholder={placeholder}
170
+ {...props}
171
+ data-testid="cap-input"
172
+ />
173
+ );
174
+ }
175
+ );
176
+
177
+ jest.mock('@capillarytech/cap-ui-library/CapError', () =>
178
+ function MockCapError({ children, ...props }) {
179
+ return <div {...props} data-testid="cap-error">{children}</div>;
180
+ }
181
+ );
182
+
183
+ jest.mock('../../../../v2Containers/TagList', () =>
184
+ function MockTagList({ label, onTagSelect, onContextChange, ...props }) {
185
+ return (
186
+ <div data-testid="tag-list" {...props}>
187
+ <button
188
+ type="button"
189
+ onClick={() => onTagSelect && onTagSelect('test-tag')}
190
+ data-testid="tag-select-button"
191
+ >
192
+ {label || 'Add Label'}
193
+ </button>
194
+ </div>
195
+ );
196
+ }
197
+ );
198
+
56
199
  jest.mock('../../../../v2Components/CapVideoUpload', () =>
57
200
  function MockCapVideoUpload(props) {
58
201
  return (
@@ -352,7 +495,7 @@ const mockStore = configureStore({}, initialReducer, history);
352
495
 
353
496
  // Helper function for formatMessage
354
497
  const mockFormatMessage = (message, values = {}) => {
355
- if (typeof message === 'object' && message.defaultMessage) {
498
+ if (message && typeof message === 'object' && message.defaultMessage) {
356
499
  let result = message.defaultMessage;
357
500
  if (values && typeof values === 'object') {
358
501
  Object.keys(values).forEach(key => {
@@ -361,7 +504,7 @@ const mockFormatMessage = (message, values = {}) => {
361
504
  }
362
505
  return result;
363
506
  }
364
- return message;
507
+ return message || '';
365
508
  };
366
509
 
367
510
  const defaultProps = {
@@ -381,7 +524,7 @@ const defaultProps = {
381
524
  setUpdateMpushVideoSrc: jest.fn(),
382
525
  videoDataForVideo: {},
383
526
  videoDataForGif: {},
384
- formatMessage: jest.fn((message) => message.defaultMessage),
527
+ formatMessage: jest.fn(mockFormatMessage),
385
528
  linkProps: {
386
529
  deepLink: [],
387
530
  },
@@ -395,6 +538,11 @@ const defaultProps = {
395
538
  setCarouselActiveTabIndex: jest.fn(),
396
539
  carouselLinkErrors: {},
397
540
  updateCarouselLinkError: jest.fn(),
541
+ location: {},
542
+ tags: [],
543
+ injectedTags: {},
544
+ selectedOfferDetails: [],
545
+ handleOnTagsContextChange: jest.fn(),
398
546
  };
399
547
 
400
548
  const renderComponent = (props = {}) => {
@@ -515,7 +663,7 @@ describe('MediaUploaders', () => {
515
663
  onCarouselDataChange: mockOnCarouselDataChange,
516
664
  carouselActiveTabIndex: 0,
517
665
  activeTab: 'ANDROID',
518
- formatMessage: (message) => message.defaultMessage,
666
+ formatMessage: mockFormatMessage,
519
667
  });
520
668
 
521
669
  // Check if the carousel component is rendered
@@ -601,7 +749,7 @@ describe('MediaUploaders', () => {
601
749
  onCarouselDataChange: mockOnCarouselDataChange,
602
750
  carouselActiveTabIndex: 0,
603
751
  activeTab: 'ANDROID',
604
- formatMessage: (message) => message.defaultMessage,
752
+ formatMessage: mockFormatMessage,
605
753
  });
606
754
 
607
755
  // First check if the checkbox is checked
@@ -4026,5 +4174,52 @@ describe('MediaUploaders', () => {
4026
4174
  // Verify the component renders without errors
4027
4175
  expect(document.querySelector('.mobile-push-carousel-tab')).toBeTruthy();
4028
4176
  });
4177
+
4178
+ it('should handle tag selection for external link in carousel buttons', () => {
4179
+ const mockHandleCarouselExternalLinkChange = jest.fn();
4180
+
4181
+ const { container } = renderComponent({
4182
+ mediaType: 'CAROUSEL',
4183
+ carouselData: [
4184
+ {
4185
+ mediaType: 'image',
4186
+ imageUrl: 'test.jpg',
4187
+ buttons: [{
4188
+ actionOnClick: true,
4189
+ linkType: 'EXTERNAL_LINK',
4190
+ deepLinkValue: '',
4191
+ deepLinkKeys: [],
4192
+ externalLinkValue: 'https://example.com/',
4193
+ }],
4194
+ }
4195
+ ],
4196
+ carouselActiveTabIndex: 0,
4197
+ activeTab: 'ANDROID',
4198
+ formatMessage: mockFormatMessage,
4199
+ onCarouselDataChange: mockHandleCarouselExternalLinkChange,
4200
+ });
4201
+
4202
+ // Find the TagList component and click the tag select button to trigger the real useCallback
4203
+ const tagSelectButton = container.querySelector('[data-testid="tag-select-button"]');
4204
+ expect(tagSelectButton).toBeTruthy();
4205
+
4206
+ // Click the button to trigger the real onButtonTagSelect useCallback
4207
+ fireEvent.click(tagSelectButton);
4208
+
4209
+ // Verify the handler was called with the correct parameters
4210
+ // The mock TagList calls onTagSelect with 'test-tag', so we expect that
4211
+ expect(mockHandleCarouselExternalLinkChange).toHaveBeenCalledWith(
4212
+ 'ANDROID', // platform
4213
+ expect.arrayContaining([
4214
+ expect.objectContaining({
4215
+ buttons: expect.arrayContaining([
4216
+ expect.objectContaining({
4217
+ externalLinkValue: 'https://example.com/{{test-tag}}'
4218
+ })
4219
+ ])
4220
+ })
4221
+ ])
4222
+ );
4223
+ });
4029
4224
  });
4030
4225
  });
@@ -42,7 +42,25 @@ jest.mock("@capillarytech/cap-ui-library/CapLabel", () => ({ children }) => <lab
42
42
  jest.mock("@capillarytech/cap-ui-library/CapInfoNote", () => ({ message }) => <div data-testid="info-note">{message}</div>);
43
43
 
44
44
  // Mock child components
45
- jest.mock("../../../TagList", () => () => <div data-testid="tag-list">TagList</div>);
45
+ jest.mock("../../../TagList", () => ({ onTagSelect, onContextChange, label, ...props }) => (
46
+ <div data-testid="tag-list">
47
+ <button
48
+ type="button"
49
+ onClick={() => onTagSelect && onTagSelect('test_tag')}
50
+ data-testid="tag-select-button"
51
+ >
52
+ Select Tag
53
+ </button>
54
+ <button
55
+ type="button"
56
+ onClick={() => onContextChange && onContextChange('test_context')}
57
+ data-testid="tag-context-button"
58
+ >
59
+ Change Context
60
+ </button>
61
+ <span data-testid="tag-label">{label}</span>
62
+ </div>
63
+ ));
46
64
  jest.mock("../MediaUploaders", () => () => <div data-testid="media-uploaders">MediaUploaders</div>);
47
65
  jest.mock("../CtaButtons", () => () => <div data-testid="cta-buttons">CtaButtons</div>);
48
66
 
@@ -840,4 +858,44 @@ describe("PlatformContentFields", () => {
840
858
  expect(getByText('query')).toBeInTheDocument();
841
859
  });
842
860
  });
861
+
862
+ it('should handle tag selection for external link in buttons', () => {
863
+ const mockHandleExternalLinkChange = jest.fn();
864
+
865
+ // Create a test that actually renders the component and triggers the useCallback
866
+ const { container } = renderComponent({
867
+ content: {
868
+ ...defaultProps.content,
869
+ actionOnClick: true,
870
+ linkType: EXTERNAL_LINK,
871
+ },
872
+ linkProps: {
873
+ ...defaultProps.linkProps,
874
+ externalLinkValue: 'https://example.com/',
875
+ },
876
+ handlers: {
877
+ ...defaultProps.handlers,
878
+ handleExternalLinkChange: mockHandleExternalLinkChange,
879
+ },
880
+ });
881
+
882
+ // Find the TagList component that's specifically for external link (there should be 3 TagLists total)
883
+ // One for title, one for message, and one for external link
884
+ const tagLists = container.querySelectorAll('[data-testid="tag-list"]');
885
+ expect(tagLists).toHaveLength(3); // title, message, and external link TagLists
886
+
887
+ // The external link TagList should be the third one (index 2)
888
+ const externalLinkTagList = tagLists[2];
889
+ const tagSelectButton = externalLinkTagList.querySelector('[data-testid="tag-select-button"]');
890
+ expect(tagSelectButton).toBeTruthy();
891
+
892
+ // Click the button to trigger the real onButtonTagSelect useCallback
893
+ fireEvent.click(tagSelectButton);
894
+
895
+ // Verify the handler was called with the correct parameters
896
+ // The mock TagList calls onTagSelect with 'test_tag', so we expect that
897
+ expect(mockHandleExternalLinkChange).toHaveBeenCalledWith(
898
+ 'https://example.com/{{test_tag}}' // newUrl with appended tag
899
+ );
900
+ });
843
901
  });
@@ -2057,6 +2057,11 @@ const MobilePushNew = ({
2057
2057
  updateHandler,
2058
2058
  deleteHandler,
2059
2059
  deepLink,
2060
+ location,
2061
+ tags,
2062
+ injectedTags,
2063
+ selectedOfferDetails,
2064
+ handleOnTagsContextChange,
2060
2065
  };
2061
2066
 
2062
2067
  const linkProps = {
@@ -2085,6 +2090,10 @@ const MobilePushNew = ({
2085
2090
  linkProps={linkProps}
2086
2091
  sameContent={sameContent}
2087
2092
  formatMessage={formatMessage}
2093
+ location={location}
2094
+ tags={tags}
2095
+ injectedTags={injectedTags}
2096
+ selectedOfferDetails={selectedOfferDetails}
2088
2097
  />
2089
2098
  );
2090
2099
  }, [androidContent, iosContent, androidTitleError, iosTitleError, androidMessageError, iosMessageError, androidExternalLinkError, iosExternalLinkError, androidDeepLinkError, iosDeepLinkError, androidDeepLinkKeysError, iosDeepLinkKeysError, formatMessage, activeTab, imageSrc, isFullMode, imageData, androidAssetList, iosAssetList, videoState, videoData, location, tags, injectedTags, selectedOfferDetails, primaryButtonAndroid, secondaryButtonAndroid, primaryButtonIos, secondaryButtonIos, ctaData, deepLink, mobilePushActions, carouselActiveTabIndex, carouselLinkErrors, handleTitleChange, handleMessageChange, handleMediaTypeChange, handleActionOnClickChange, handleLinkTypeChange, handleDeepLinkChange, handleDeepLinkKeysChange, handleExternalLinkChange, onTagSelect, handleOnTagsContextChange, setUpdateMpushImageSrc, updateOnMpushImageReUpload, setUpdateMpushVideoSrc, updateOnMpushVideoReUpload, clearImageDataByMediaType, handleCarouselDataChange, updateCarouselLinkError, sameContent, updateHandler, deleteHandler]
@@ -150,7 +150,8 @@
150
150
 
151
151
  .buttons-heading {
152
152
  margin-bottom: $CAP_SPACE_12;
153
- margin-top: $CAP_SPACE_16;
153
+ margin-top: $CAP_SPACE_12;
154
+ margin-right: 48%;
154
155
  }
155
156
 
156
157
  .helper-text {