@capillarytech/creatives-library 9.0.29 → 9.0.30

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 (147) hide show
  1. package/constants/unified.js +29 -0
  2. package/global-styles.js +1 -1
  3. package/package.json +1 -1
  4. package/services/tests/api.test.js +35 -20
  5. package/utils/commonUtils.js +22 -1
  6. package/utils/rcsPayloadUtils.js +102 -0
  7. package/utils/templateVarUtils.js +198 -0
  8. package/utils/tests/rcsPayloadUtils.test.js +295 -0
  9. package/utils/tests/templateVarUtils.test.js +204 -0
  10. package/v2Components/CapActionButton/constants.js +21 -1
  11. package/v2Components/CapActionButton/index.js +261 -144
  12. package/v2Components/CapActionButton/index.scss +245 -14
  13. package/v2Components/CapActionButton/messages.js +32 -3
  14. package/v2Components/CapActionButton/tests/index.test.js +74 -19
  15. package/v2Components/CapImageUpload/index.js +2 -2
  16. package/v2Components/CapTagList/index.js +9 -0
  17. package/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -48
  18. package/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +8 -2
  19. package/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +214 -21
  20. package/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +16 -0
  21. package/v2Components/CommonTestAndPreview/DeliverySettings/index.js +83 -9
  22. package/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +30 -0
  23. package/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +58 -11
  24. package/v2Components/CommonTestAndPreview/SendTestMessage.js +10 -5
  25. package/v2Components/CommonTestAndPreview/UnifiedPreview/RcsPreviewContent.js +186 -22
  26. package/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +381 -80
  27. package/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +155 -8
  28. package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +11 -0
  29. package/v2Components/CommonTestAndPreview/constants.js +35 -2
  30. package/v2Components/CommonTestAndPreview/index.js +770 -231
  31. package/v2Components/CommonTestAndPreview/messages.js +45 -3
  32. package/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
  33. package/v2Components/CommonTestAndPreview/sagas.js +25 -6
  34. package/v2Components/CommonTestAndPreview/tests/CommonTestAndPreview.addTestCustomer.test.js +1 -0
  35. package/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +412 -257
  36. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +337 -63
  37. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +118 -5
  38. package/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +341 -0
  39. package/v2Components/CommonTestAndPreview/tests/PreviewSection.test.js +8 -1
  40. package/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +108 -15
  41. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/RcsPreviewContent.test.js +281 -283
  42. package/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
  43. package/v2Components/CommonTestAndPreview/tests/index.test.js +133 -4
  44. package/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
  45. package/v2Components/CommonTestAndPreview/tests/sagas.test.js +31 -24
  46. package/v2Components/CommonTestAndPreview/tests/utils.test.js +151 -0
  47. package/v2Components/CommonTestAndPreview/utils.js +84 -0
  48. package/v2Components/SmsFallback/SmsFallbackLocalSelector.js +91 -0
  49. package/v2Components/SmsFallback/constants.js +94 -0
  50. package/v2Components/SmsFallback/index.js +958 -0
  51. package/v2Components/SmsFallback/index.scss +266 -0
  52. package/v2Components/SmsFallback/messages.js +78 -0
  53. package/v2Components/SmsFallback/smsFallbackUtils.js +120 -0
  54. package/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
  55. package/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
  56. package/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
  57. package/v2Components/SmsFallback/tests/smsFallbackUi.test.js +208 -0
  58. package/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +309 -0
  59. package/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +471 -0
  60. package/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
  61. package/v2Components/TemplatePreview/_templatePreview.scss +45 -25
  62. package/v2Components/TemplatePreview/constants.js +2 -0
  63. package/v2Components/TemplatePreview/index.js +147 -32
  64. package/v2Components/TemplatePreview/tests/index.test.js +142 -0
  65. package/v2Components/TestAndPreviewSlidebox/CustomValuesEditor.js +6 -6
  66. package/v2Components/TestAndPreviewSlidebox/index.js +13 -1
  67. package/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
  68. package/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
  69. package/v2Components/VarSegmentMessageEditor/constants.js +2 -0
  70. package/v2Components/VarSegmentMessageEditor/index.js +125 -0
  71. package/v2Components/VarSegmentMessageEditor/index.scss +46 -0
  72. package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +4 -1
  73. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +4 -0
  74. package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +20 -20
  75. package/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +27 -0
  76. package/v2Containers/CreativesContainer/SlideBoxContent.js +37 -4
  77. package/v2Containers/CreativesContainer/SlideBoxFooter.js +14 -5
  78. package/v2Containers/CreativesContainer/SlideBoxHeader.js +36 -5
  79. package/v2Containers/CreativesContainer/constants.js +11 -0
  80. package/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +77 -0
  81. package/v2Containers/CreativesContainer/index.js +322 -106
  82. package/v2Containers/CreativesContainer/index.scss +102 -1
  83. package/v2Containers/CreativesContainer/tests/CreativesSlideBoxWrapper.test.js +58 -0
  84. package/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
  85. package/v2Containers/CreativesContainer/tests/SlideBoxContent.smsDltPreview.test.js +73 -0
  86. package/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +37 -0
  87. package/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +103 -34
  88. package/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +193 -15
  89. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +88 -0
  90. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +549 -57
  91. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +20 -15
  92. package/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
  93. package/v2Containers/CreativesContainer/tests/index.test.js +71 -9
  94. package/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
  95. package/v2Containers/MobilePush/Create/test/saga.test.js +2 -2
  96. package/v2Containers/Rcs/carouselUtils.js +224 -0
  97. package/v2Containers/Rcs/components/CarouselCard.js +317 -0
  98. package/v2Containers/Rcs/components/CarouselCardButtons.js +113 -0
  99. package/v2Containers/Rcs/components/CarouselCardMedia.js +136 -0
  100. package/v2Containers/Rcs/components/CarouselCharacterCount.js +31 -0
  101. package/v2Containers/Rcs/components/CarouselDimensionSelection.js +80 -0
  102. package/v2Containers/Rcs/constants.js +132 -16
  103. package/v2Containers/Rcs/index.js +1822 -946
  104. package/v2Containers/Rcs/index.scss +443 -8
  105. package/v2Containers/Rcs/messages.js +45 -22
  106. package/v2Containers/Rcs/rcsLibraryHydrationUtils.js +494 -0
  107. package/v2Containers/Rcs/tests/CarouselCard.test.js +464 -0
  108. package/v2Containers/Rcs/tests/CarouselCardButtons.test.js +211 -0
  109. package/v2Containers/Rcs/tests/CarouselCardMedia.test.js +160 -0
  110. package/v2Containers/Rcs/tests/CarouselCharacterCount.test.js +50 -0
  111. package/v2Containers/Rcs/tests/CarouselDimensionSelection.test.js +119 -0
  112. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +74440 -39387
  113. package/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +10 -74
  114. package/v2Containers/Rcs/tests/carouselUtils.test.js +916 -0
  115. package/v2Containers/Rcs/tests/index.test.js +275 -40
  116. package/v2Containers/Rcs/tests/mockData.js +38 -0
  117. package/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +912 -0
  118. package/v2Containers/Rcs/tests/utils.test.js +682 -30
  119. package/v2Containers/Rcs/utils.js +514 -12
  120. package/v2Containers/Sms/Create/index.js +115 -48
  121. package/v2Containers/Sms/smsFormDataHelpers.js +67 -0
  122. package/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  123. package/v2Containers/SmsTrai/Create/index.js +9 -4
  124. package/v2Containers/SmsTrai/Edit/constants.js +2 -0
  125. package/v2Containers/SmsTrai/Edit/index.js +678 -169
  126. package/v2Containers/SmsTrai/Edit/index.scss +126 -0
  127. package/v2Containers/SmsTrai/Edit/messages.js +14 -4
  128. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +5615 -3014
  129. package/v2Containers/SmsWrapper/index.js +37 -8
  130. package/v2Containers/TagList/index.js +5 -0
  131. package/v2Containers/Templates/TemplatesActionBar.js +101 -0
  132. package/v2Containers/Templates/_templates.scss +196 -12
  133. package/v2Containers/Templates/actions.js +11 -0
  134. package/v2Containers/Templates/constants.js +2 -0
  135. package/v2Containers/Templates/index.js +131 -59
  136. package/v2Containers/Templates/sagas.js +57 -13
  137. package/v2Containers/Templates/tests/TemplatesActionBar.test.js +129 -0
  138. package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1060 -1015
  139. package/v2Containers/Templates/tests/sagas.test.js +199 -16
  140. package/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
  141. package/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
  142. package/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
  143. package/v2Containers/TemplatesV2/index.js +88 -25
  144. package/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
  145. package/v2Containers/WeChat/MapTemplates/test/saga.test.js +9 -9
  146. package/v2Containers/Whatsapp/index.js +3 -20
  147. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +578 -34
@@ -2,6 +2,93 @@
2
2
 
3
3
  $classPrefix: add-creatives-section;
4
4
 
5
+ /* Local SMS template picker: fill slidebox height; global .v2-pagination-container uses 100vh-20rem and leaves a dead zone inside slideboxes */
6
+ .#{$classPrefix}.creatives-slidebox--local-sms-templates {
7
+ .cap-slide-box-v2-container {
8
+ display: flex;
9
+ flex-direction: column;
10
+ min-height: 0;
11
+ max-height: 100vh;
12
+ }
13
+
14
+ .slidebox-content-container {
15
+ flex: 1;
16
+ min-height: 0;
17
+ display: flex;
18
+ flex-direction: column;
19
+ overflow: hidden;
20
+ }
21
+
22
+ .slidebox-content-container > div {
23
+ flex: 1;
24
+ min-height: 0;
25
+ display: flex;
26
+ flex-direction: column;
27
+ overflow: hidden;
28
+ }
29
+
30
+ /* TemplatesV2 root: fill slidebox so the template grid can flex instead of using 100vh-based pagination height */
31
+ .slidebox-content-container .creatives-templates-container--local-sms.library-mode {
32
+ flex: 1;
33
+ min-height: 0;
34
+ display: flex;
35
+ flex-direction: column;
36
+ overflow: hidden;
37
+ height: auto;
38
+ max-height: 100%;
39
+ }
40
+ }
41
+
42
+ /*
43
+ * Liquid-error spacing must stay inside the content column. margin-bottom on
44
+ * .slidebox-content-container added to the in-flow height past 100vh, so the outer
45
+ * .cap-slide-box-v2-container gained a second scrollbar. Margin/padding values are
46
+ * static per modifier class, driven off the same enum CreativesSlideBoxWrapper.js maps from.
47
+ */
48
+ .creatives-slide-box-wrapper {
49
+ .cap-slide-box-v2-container {
50
+ .slidebox-content-container {
51
+ margin-bottom: 0;
52
+ box-sizing: border-box;
53
+ &.has-footer {
54
+ overflow-x: hidden;
55
+ }
56
+ }
57
+ .slidebox-header.has-footer,
58
+ .slidebox-footer.has-footer {
59
+ overflow-x: hidden;
60
+ }
61
+ }
62
+
63
+ &--margin-32 .cap-slide-box-v2-container {
64
+ .slidebox-header {
65
+ margin-bottom: $CAP_SPACE_32;
66
+ }
67
+ .slidebox-content-container {
68
+ padding-bottom: $CAP_SPACE_32;
69
+ }
70
+ }
71
+ &--margin-56 .cap-slide-box-v2-container {
72
+ .slidebox-header {
73
+ margin-bottom: $CAP_SPACE_56;
74
+ }
75
+ .slidebox-content-container {
76
+ padding-bottom: $CAP_SPACE_56;
77
+ }
78
+ }
79
+ &--margin-64 .cap-slide-box-v2-container {
80
+ .slidebox-header {
81
+ margin-bottom: $CAP_SPACE_64;
82
+ }
83
+ .slidebox-content-container {
84
+ padding-bottom: $CAP_SPACE_64;
85
+ }
86
+ }
87
+ &--footer-margin .cap-slide-box-v2-container .slidebox-footer {
88
+ margin-bottom: $CAP_SPACE_16;
89
+ }
90
+ }
91
+
5
92
  .#{$classPrefix} {
6
93
  &.creatives-library-mode{
7
94
  .sms-create-container, .sms-email-container{
@@ -80,5 +167,19 @@ $classPrefix: add-creatives-section;
80
167
  }
81
168
 
82
169
  .template-footer-width {
83
- width: 100%;;
170
+ width: 100%;
84
171
  }
172
+
173
+ .slidebox-footer-actions {
174
+ display: flex;
175
+ flex-wrap: nowrap;
176
+ align-items: center;
177
+ gap: 0.75rem;
178
+ min-width: 0;
179
+
180
+ .ant-btn,
181
+ button {
182
+ flex-shrink: 0;
183
+ }
184
+ }
185
+
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+ import React from 'react';
5
+ import { render } from '@testing-library/react';
6
+ import '@testing-library/jest-dom';
7
+ import { CAP_SPACE_32 } from '@capillarytech/cap-ui-library/styled/variables';
8
+ import CreativesSlideBoxWrapper from '../CreativesSlideBoxWrapper';
9
+
10
+ describe('CreativesSlideBoxWrapper', () => {
11
+ it('renders children inside a div with the base wrapper class', () => {
12
+ const { container, getByText } = render(
13
+ <CreativesSlideBoxWrapper slideBoxWrapperMargin={CAP_SPACE_32}>
14
+ <span>content</span>
15
+ </CreativesSlideBoxWrapper>,
16
+ );
17
+ expect(getByText('content')).toBeInTheDocument();
18
+ const wrapperDiv = container.firstChild;
19
+ expect(wrapperDiv).not.toHaveAttribute('style');
20
+ expect(wrapperDiv.className).toBe('creatives-slide-box-wrapper creatives-slide-box-wrapper--margin-32');
21
+ });
22
+
23
+ it('renders no margin modifier class when slideBoxWrapperMargin is 0', () => {
24
+ const { container } = render(
25
+ <CreativesSlideBoxWrapper slideBoxWrapperMargin={0}>
26
+ <span>content</span>
27
+ </CreativesSlideBoxWrapper>,
28
+ );
29
+ expect(container.firstChild.className).toBe('creatives-slide-box-wrapper');
30
+ });
31
+
32
+ it('appends the provided className to the base wrapper class', () => {
33
+ const { container } = render(
34
+ <CreativesSlideBoxWrapper className="extra-class">
35
+ <span>content</span>
36
+ </CreativesSlideBoxWrapper>,
37
+ );
38
+ expect(container.firstChild.className).toBe('creatives-slide-box-wrapper extra-class');
39
+ });
40
+
41
+ it('applies the footer margin modifier class when shouldApplyFooterMargin is true', () => {
42
+ const { container } = render(
43
+ <CreativesSlideBoxWrapper shouldApplyFooterMargin>
44
+ <span>content</span>
45
+ </CreativesSlideBoxWrapper>,
46
+ );
47
+ expect(container.firstChild.className).toBe('creatives-slide-box-wrapper creatives-slide-box-wrapper--footer-margin');
48
+ });
49
+
50
+ it('spreads any remaining props onto the wrapper div', () => {
51
+ const { container } = render(
52
+ <CreativesSlideBoxWrapper data-testid="wrapper-rest-prop">
53
+ <span>content</span>
54
+ </CreativesSlideBoxWrapper>,
55
+ );
56
+ expect(container.firstChild).toHaveAttribute('data-testid', 'wrapper-rest-prop');
57
+ });
58
+ });
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Covers `localTemplatesConfig` / `useLocalTemplates` branch in SlideBoxContent → TemplatesV2
3
+ * (embedded SMS template list / RCS SMS fallback).
4
+ */
5
+ import React from 'react';
6
+ import { mountWithIntl, shallowWithIntl } from '../../../helpers/intl-enzym-test-helpers';
7
+ import { SlideBoxContent } from '../SlideBoxContent';
8
+
9
+ const mockTemplatesV2 = jest.fn(() => <div data-testid="templates-v2-mock" />);
10
+ jest.mock('../../TemplatesV2', () => ({
11
+ __esModule: true,
12
+ default: (props) => mockTemplatesV2(props),
13
+ }));
14
+
15
+ const baseProps = {
16
+ slidBoxContent: 'templates',
17
+ currentChannel: 'SMS',
18
+ onSelectTemplate: jest.fn(),
19
+ onPreviewTemplate: jest.fn(),
20
+ onCreateNew: jest.fn(),
21
+ onChannelChange: jest.fn(),
22
+ location: { pathname: '/sms', query: {}, search: '' },
23
+ cap: {},
24
+ channelsToHide: [],
25
+ channelsToDisable: [],
26
+ handleClose: jest.fn(),
27
+ messageDetails: {},
28
+ onCreateComplete: jest.fn(),
29
+ };
30
+
31
+ describe('SlideBoxContent local templates (TemplatesV2)', () => {
32
+ beforeEach(() => {
33
+ mockTemplatesV2.mockClear();
34
+ });
35
+
36
+ // Use mount when asserting TemplatesV2 mock calls: shallow does not invoke children under styled CreativesWrapper.
37
+ it('renders TemplatesV2 in full mode when useLocalTemplates is true', () => {
38
+ mountWithIntl(
39
+ <SlideBoxContent
40
+ {...baseProps}
41
+ isFullMode
42
+ localTemplatesConfig={{
43
+ useLocalTemplates: true,
44
+ localTemplates: [],
45
+ localTemplatesLoading: false,
46
+ }}
47
+ />,
48
+ );
49
+ expect(mockTemplatesV2).toHaveBeenCalled();
50
+ const passed = mockTemplatesV2.mock.calls[mockTemplatesV2.mock.calls.length - 1][0];
51
+ expect(passed.localTemplatesConfig.useLocalTemplates).toBe(true);
52
+ });
53
+
54
+ it('does not render TemplatesV2 in full mode when useLocalTemplates is false', () => {
55
+ shallowWithIntl(
56
+ <SlideBoxContent
57
+ {...baseProps}
58
+ isFullMode
59
+ localTemplatesConfig={{ useLocalTemplates: false }}
60
+ />,
61
+ );
62
+ expect(mockTemplatesV2).not.toHaveBeenCalled();
63
+ });
64
+
65
+ it('renders TemplatesV2 in library mode without localTemplates flag', () => {
66
+ mountWithIntl(
67
+ <SlideBoxContent
68
+ {...baseProps}
69
+ isFullMode={false}
70
+ />,
71
+ );
72
+ expect(mockTemplatesV2).toHaveBeenCalled();
73
+ });
74
+
75
+ it('merges top-level local template props via pick when localTemplatesConfig is omitted', () => {
76
+ mountWithIntl(
77
+ <SlideBoxContent
78
+ {...baseProps}
79
+ isFullMode
80
+ useLocalTemplates
81
+ localTemplates={[{ _id: '1' }]}
82
+ localTemplatesLoading={false}
83
+ />,
84
+ );
85
+ expect(mockTemplatesV2).toHaveBeenCalled();
86
+ const passed = mockTemplatesV2.mock.calls[mockTemplatesV2.mock.calls.length - 1][0];
87
+ expect(passed.localTemplatesConfig.useLocalTemplates).toBe(true);
88
+ expect(passed.localTemplatesConfig.localTemplates).toEqual([{ _id: '1' }]);
89
+ });
90
+ });
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Covers the SMS `getChannelPreviewContent` DLT-enabled branch in SlideBoxContent, which joins
3
+ * `updated-sms-editor` segments into a single string for the preview. Isolated in its own file
4
+ * (mirrors SlideBoxContent.localTemplates.test.js) because it mocks TemplatePreview and
5
+ * isTraiDLTEnable, which would otherwise affect the large shared snapshot suite.
6
+ */
7
+ import React from 'react';
8
+ import { mountWithIntl } from '../../../helpers/intl-enzym-test-helpers';
9
+ import * as commonUtil from '../../../utils/common';
10
+ import { SlideBoxContent } from '../SlideBoxContent';
11
+
12
+ const mockTemplatePreview = jest.fn(() => <div data-testid="template-preview-mock" />);
13
+ jest.mock('../../../v2Components/TemplatePreview', () => ({
14
+ __esModule: true,
15
+ default: (props) => mockTemplatePreview(props),
16
+ }));
17
+
18
+ describe('SlideBoxContent getChannelPreviewContent — SMS DLT branch', () => {
19
+ const onCreateComplete = jest.fn();
20
+
21
+ const renderFunction = (templateData) => mountWithIntl(
22
+ <SlideBoxContent
23
+ slidBoxContent="preview"
24
+ currentChannel="SMS"
25
+ templateData={templateData}
26
+ onCreateComplete={onCreateComplete}
27
+ />,
28
+ );
29
+
30
+ beforeEach(() => {
31
+ mockTemplatePreview.mockClear();
32
+ });
33
+
34
+ afterEach(() => {
35
+ jest.restoreAllMocks();
36
+ });
37
+
38
+ it('joins updated-sms-editor segments into one string when DLT is enabled', () => {
39
+ jest.spyOn(commonUtil, 'isTraiDLTEnable').mockReturnValue(true);
40
+
41
+ renderFunction({
42
+ type: 'SMS',
43
+ versions: {
44
+ base: {
45
+ 'updated-sms-editor': ['Hello', ' ', 'World'],
46
+ 'sms-editor': 'Should not be used',
47
+ },
48
+ },
49
+ });
50
+
51
+ expect(mockTemplatePreview).toHaveBeenCalledWith(
52
+ expect.objectContaining({ content: 'Hello World' }),
53
+ );
54
+ });
55
+
56
+ it('falls back to sms-editor when DLT is enabled but updated-sms-editor is empty', () => {
57
+ jest.spyOn(commonUtil, 'isTraiDLTEnable').mockReturnValue(true);
58
+
59
+ renderFunction({
60
+ type: 'SMS',
61
+ versions: {
62
+ base: {
63
+ 'updated-sms-editor': '',
64
+ 'sms-editor': 'Plain SMS content',
65
+ },
66
+ },
67
+ });
68
+
69
+ expect(mockTemplatePreview).toHaveBeenCalledWith(
70
+ expect.objectContaining({ content: 'Plain SMS content' }),
71
+ );
72
+ });
73
+ });
@@ -272,6 +272,21 @@ describe('Test SlideBoxContent container', () => {
272
272
  renderFunction('LINE', 'createTemplate', templateData, { isFullMode: false });
273
273
  expect(renderedComponent).toMatchSnapshot();
274
274
  });
275
+
276
+ it('Should handle LINE channel in non-full mode with template data that is neither an edit nor a default', () => {
277
+ // templateData is present but has no _id, no message body, and isDefault is falsy,
278
+ // so getLineType falls through to the final `return {}` (isCreate/isEdit both false).
279
+ const templateData = {
280
+ isDefault: false,
281
+ };
282
+ renderFunction('LINE', 'createTemplate', templateData, { isFullMode: false });
283
+ expect(renderedComponent).toMatchSnapshot();
284
+ });
285
+
286
+ it('Should derive isCreate/isEdit from slidBoxContent in non-full mode when templateData is absent', () => {
287
+ renderFunction('LINE', 'createTemplate', null, { isFullMode: false });
288
+ expect(renderedComponent).toMatchSnapshot();
289
+ });
275
290
  });
276
291
 
277
292
  describe('getViber utility function', () => {
@@ -359,6 +374,28 @@ describe('Test SlideBoxContent container', () => {
359
374
  });
360
375
  expect(renderedComponent).toMatchSnapshot();
361
376
  });
377
+
378
+ it('renders the direct Email component for a BEE (drag-drop) template regardless of hasSupportCKEditor', () => {
379
+ const emailTemplateData = {
380
+ type: 'EMAIL',
381
+ _id: '123',
382
+ base: {
383
+ is_drag_drop: true,
384
+ },
385
+ versions: {
386
+ base: {
387
+ content: {},
388
+ },
389
+ },
390
+ };
391
+
392
+ renderFunction('EMAIL', 'editTemplate', emailTemplateData, {
393
+ ...commonEmailProps,
394
+ isTestAndPreviewMode: false,
395
+ showTestAndPreviewSlidebox: false,
396
+ });
397
+ expect(renderedComponent).toMatchSnapshot();
398
+ });
362
399
  });
363
400
 
364
401
  describe('getChannelPreviewContent utility function for LINE messages type detection', () => {
@@ -531,62 +531,131 @@ describe('shouldCheckValidation (line 79)', () => {
531
531
  });
532
532
  });
533
533
 
534
- describe('SlideBoxFooter — continue and preview states', () => {
534
+ describe('SlideBoxFooter — isHtmlEditorValidationStateActive via issueCounts', () => {
535
535
  beforeEach(() => {
536
536
  jest.clearAllMocks();
537
+ });
538
+
539
+ it('activates via issueCounts.total > 0 even when validationComplete=false and hasErrors=false', () => {
537
540
  const { hasSupportCKEditor } = require('../../../utils/common');
538
541
  hasSupportCKEditor.mockReturnValue(false);
539
- });
540
542
 
541
- it('renders the Continue button when shouldShowContinueFooter returns true', () => {
542
543
  renderComponent({
543
544
  ...baseFooterProps,
544
- shouldShowDoneFooter: jest.fn().mockReturnValue(false),
545
- shouldShowContinueFooter: jest.fn().mockReturnValue(true),
546
- isContinueButtonDisabled: false,
547
- // null lets the component fall back to messages.continue (has a valid id)
548
- // The defaultProp {} has no id, which causes FormattedMessage to throw
549
- continueButtonLabel: null,
545
+ currentChannel: 'EMAIL',
546
+ slidBoxContent: 'editTemplate',
547
+ htmlEditorValidationState: {
548
+ validationComplete: false,
549
+ hasErrors: false,
550
+ errorsAcknowledged: false,
551
+ isContentEmpty: false,
552
+ issueCounts: { html: 1, label: 0, liquid: 0, total: 1 },
553
+ },
550
554
  });
551
- const continueBtn = screen.getByRole('button', { name: /continue/i });
552
- expect(continueBtn).toBeTruthy();
553
- expect(continueBtn).toBeEnabled();
555
+ // isHtmlEditorValidationStateActive = true (via issueCounts.total > 0)
556
+ // isHTMLEditorModeInEdit = true, shouldCheckValidation = true
557
+ // hasValidationErrors = false, shouldDisableButtons = false (no hasErrors)
558
+ const updateBtn = screen.getByRole('button', { name: /update/i });
559
+ expect(updateBtn).toBeEnabled();
554
560
  });
555
561
 
556
- it('disables the Continue button when isContinueButtonDisabled is true', () => {
562
+ it('issueCounts.total=0 with validationComplete=false not activated (BEE edit mode)', () => {
563
+ const { hasSupportCKEditor } = require('../../../utils/common');
564
+ hasSupportCKEditor.mockReturnValue(false);
565
+
557
566
  renderComponent({
558
567
  ...baseFooterProps,
559
- shouldShowDoneFooter: jest.fn().mockReturnValue(false),
560
- shouldShowContinueFooter: jest.fn().mockReturnValue(true),
561
- isContinueButtonDisabled: true,
562
- continueButtonLabel: null,
568
+ currentChannel: 'EMAIL',
569
+ slidBoxContent: 'editTemplate',
570
+ htmlEditorValidationState: {
571
+ validationComplete: false,
572
+ hasErrors: false,
573
+ errorsAcknowledged: false,
574
+ isContentEmpty: false,
575
+ issueCounts: { html: 0, label: 0, liquid: 0, total: 0 },
576
+ },
563
577
  });
564
- const continueBtn = screen.getByRole('button', { name: /continue/i });
565
- expect(continueBtn).toBeDisabled();
578
+ // isHtmlEditorValidationStateActive = false isHTMLEditorModeInEdit = false
579
+ // isBEEEditor = true (isEditMode && !isHtmlEditorValidationStateActive)
580
+ // shouldCheckValidation = false → button enabled
581
+ const updateBtn = screen.getByRole('button', { name: /update/i });
582
+ expect(updateBtn).toBeEnabled();
583
+ });
584
+ });
585
+
586
+ describe('SlideBoxFooter — isBEEEditor detection in create mode via emailCreateMode=editor', () => {
587
+ beforeEach(() => {
588
+ jest.clearAllMocks();
566
589
  });
567
590
 
568
- it('renders the Edit button when slidBoxContent is preview', () => {
591
+ it('detects BEE editor when emailCreateMode=editor and selectedEmailCreateMode is not html_editor (create mode)', () => {
592
+ const { hasSupportCKEditor } = require('../../../utils/common');
593
+ hasSupportCKEditor.mockReturnValue(false);
594
+
569
595
  renderComponent({
570
596
  ...baseFooterProps,
571
- slidBoxContent: 'preview',
572
- shouldShowDoneFooter: jest.fn().mockReturnValue(false),
573
- shouldShowContinueFooter: jest.fn().mockReturnValue(false),
574
- onEditTemplate: jest.fn(),
597
+ currentChannel: 'EMAIL',
598
+ slidBoxContent: 'createTemplate',
599
+ emailCreateMode: 'editor',
600
+ selectedEmailCreateMode: 'drag_drop',
601
+ htmlEditorValidationState: {
602
+ validationComplete: false,
603
+ hasErrors: false,
604
+ isContentEmpty: false,
605
+ issueCounts: { html: 0, label: 0, liquid: 0, total: 0 },
606
+ },
575
607
  });
576
- const editBtn = screen.getByRole('button', { name: /edit/i });
577
- expect(editBtn).toBeTruthy();
608
+ // selectedEmailCreateMode=drag_drop isBEEEditor = true (DRAG_DROP branch)
609
+ // shouldCheckValidation = false → button not disabled by validation
610
+ const createBtn = screen.getByRole('button', { name: /create/i });
611
+ expect(createBtn).toBeInTheDocument();
578
612
  });
579
613
 
580
- it('calls onEditTemplate when the Edit button is clicked in preview mode', () => {
581
- const onEditTemplate = jest.fn();
614
+ it('enables primary action button when emailCreateMode=editor but mode is not HTML editor (BEE editor path)', () => {
615
+ const { hasSupportCKEditor } = require('../../../utils/common');
616
+ hasSupportCKEditor.mockReturnValue(false);
617
+
582
618
  renderComponent({
583
619
  ...baseFooterProps,
584
- slidBoxContent: 'preview',
585
- shouldShowDoneFooter: jest.fn().mockReturnValue(false),
586
- shouldShowContinueFooter: jest.fn().mockReturnValue(false),
587
- onEditTemplate,
620
+ currentChannel: 'EMAIL',
621
+ slidBoxContent: 'createTemplate',
622
+ emailCreateMode: 'editor',
623
+ selectedEmailCreateMode: undefined,
624
+ htmlEditorValidationState: {
625
+ validationComplete: false,
626
+ hasErrors: false,
627
+ isContentEmpty: false,
628
+ issueCounts: { html: 0, label: 0, liquid: 0, total: 0 },
629
+ },
630
+ });
631
+ // isHTMLEditorModeInCreate = false (selectedEmailCreateMode is undefined, not html_editor)
632
+ // emailCreateMode === 'editor' && !isHTMLEditorMode → isBEEEditor = true
633
+ const createBtn = screen.getByRole('button', { name: /create/i });
634
+ expect(createBtn).toBeInTheDocument();
635
+ expect(createBtn).not.toBeDisabled();
636
+ });
637
+
638
+ describe('getSaveButtonLabel', () => {
639
+ it('shows the creating-template label when isFullMode and isCreatingTemplate are both true', () => {
640
+ renderComponent({
641
+ ...baseFooterProps,
642
+ isFullMode: true,
643
+ isCreatingTemplate: true,
644
+ slidBoxContent: 'editTemplate',
645
+ });
646
+ // getFullModeSaveBtn short-circuits on isCreatingTemplate before checking EDIT_TEMPLATE,
647
+ // so the label is "Done" (creativesTemplatesDone) rather than "Update".
648
+ expect(screen.getByRole('button', { name: /done/i })).toBeInTheDocument();
649
+ });
650
+
651
+ it('falls back to the non-full-mode save label when isFullMode is false', () => {
652
+ renderComponent({
653
+ ...baseFooterProps,
654
+ isFullMode: false,
655
+ isCreatingTemplate: false,
656
+ slidBoxContent: 'editTemplate',
657
+ });
658
+ expect(screen.getByRole('button', { name: /done/i })).toBeInTheDocument();
588
659
  });
589
- screen.getByRole('button', { name: /edit/i }).click();
590
- expect(onEditTemplate).toHaveBeenCalled();
591
660
  });
592
661
  });