@capillarytech/creatives-library 8.0.242-alpha.0 → 8.0.242-alpha.10

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 (216) hide show
  1. package/assets/Android.png +0 -0
  2. package/assets/iOS.png +0 -0
  3. package/constants/unified.js +2 -1
  4. package/initialReducer.js +2 -0
  5. package/package.json +1 -1
  6. package/sagas/__tests__/assetPolling.test.js +74 -3
  7. package/sagas/assetPolling.js +8 -1
  8. package/services/api.js +10 -5
  9. package/services/tests/api.test.js +18 -0
  10. package/translations/en.json +0 -1
  11. package/utils/common.js +5 -0
  12. package/utils/commonUtils.js +14 -1
  13. package/utils/tests/commonUtil.test.js +224 -0
  14. package/utils/transformTemplateConfig.js +0 -10
  15. package/utils/transformerUtils.js +0 -42
  16. package/v2Components/CapDeviceContent/index.js +61 -56
  17. package/v2Components/CapImageUpload/constants.js +0 -2
  18. package/v2Components/CapImageUpload/index.js +14 -54
  19. package/v2Components/CapImageUpload/index.scss +1 -4
  20. package/v2Components/CapImageUpload/messages.js +0 -4
  21. package/v2Components/CapTagList/index.js +6 -1
  22. package/v2Components/CapTagListWithInput/index.js +5 -1
  23. package/v2Components/CapTagListWithInput/messages.js +1 -1
  24. package/v2Components/CapWhatsappCTA/tests/index.test.js +5 -0
  25. package/v2Components/ErrorInfoNote/index.js +412 -72
  26. package/v2Components/ErrorInfoNote/messages.js +22 -0
  27. package/v2Components/ErrorInfoNote/style.scss +279 -2
  28. package/v2Components/HtmlEditor/HTMLEditor.js +220 -91
  29. package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1132 -133
  30. package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +17 -12
  31. package/v2Components/HtmlEditor/_htmlEditor.scss +107 -45
  32. package/v2Components/HtmlEditor/_index.lazy.scss +1 -1
  33. package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +13 -101
  34. package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +148 -139
  35. package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +2 -1
  36. package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
  37. package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +9 -0
  38. package/v2Components/HtmlEditor/components/EditorToolbar/index.js +1 -1
  39. package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +22 -0
  40. package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +4 -7
  41. package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +35 -45
  42. package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +1 -3
  43. package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
  44. package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +7 -6
  45. package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +3 -6
  46. package/v2Components/HtmlEditor/components/PreviewPane/index.js +10 -11
  47. package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +1 -1
  48. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +70 -72
  49. package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +49 -31
  50. package/v2Components/HtmlEditor/components/ValidationTabs/_validationTabs.scss +254 -0
  51. package/v2Components/HtmlEditor/components/ValidationTabs/index.js +362 -0
  52. package/v2Components/HtmlEditor/components/ValidationTabs/messages.js +51 -0
  53. package/v2Components/HtmlEditor/constants.js +29 -20
  54. package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +373 -16
  55. package/v2Components/HtmlEditor/hooks/useEditorContent.js +5 -2
  56. package/v2Components/HtmlEditor/hooks/useInAppContent.js +88 -146
  57. package/v2Components/HtmlEditor/index.js +1 -1
  58. package/v2Components/HtmlEditor/messages.js +95 -85
  59. package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +99 -101
  60. package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +23 -25
  61. package/v2Components/HtmlEditor/utils/validationAdapter.js +34 -41
  62. package/v2Components/MobilePushPreviewV2/index.js +32 -7
  63. package/v2Components/TemplatePreview/_templatePreview.scss +44 -24
  64. package/v2Components/TemplatePreview/index.js +47 -32
  65. package/v2Components/TemplatePreview/messages.js +4 -0
  66. package/v2Components/TestAndPreviewSlidebox/index.js +31 -25
  67. package/v2Containers/App/constants.js +0 -5
  68. package/v2Containers/BeeEditor/index.js +82 -80
  69. package/v2Containers/BeePopupEditor/constants.js +10 -0
  70. package/v2Containers/BeePopupEditor/index.js +193 -0
  71. package/v2Containers/BeePopupEditor/tests/index.test.js +627 -0
  72. package/v2Containers/Cap/tests/__snapshots__/index.test.js.snap +0 -1
  73. package/v2Containers/CreativesContainer/SlideBoxContent.js +148 -120
  74. package/v2Containers/CreativesContainer/SlideBoxFooter.js +9 -3
  75. package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -2
  76. package/v2Containers/CreativesContainer/constants.js +1 -2
  77. package/v2Containers/CreativesContainer/index.js +173 -193
  78. package/v2Containers/CreativesContainer/messages.js +4 -4
  79. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +38 -50
  80. package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +36 -0
  81. package/v2Containers/Email/actions.js +7 -0
  82. package/v2Containers/Email/constants.js +5 -1
  83. package/v2Containers/Email/index.js +13 -0
  84. package/v2Containers/Email/messages.js +32 -0
  85. package/v2Containers/Email/reducer.js +12 -1
  86. package/v2Containers/Email/sagas.js +41 -6
  87. package/v2Containers/Email/tests/__snapshots__/reducer.test.js.snap +2 -0
  88. package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1046 -0
  89. package/v2Containers/EmailWrapper/components/EmailWrapperView.js +193 -7
  90. package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +40 -74
  91. package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +2 -67
  92. package/v2Containers/EmailWrapper/constants.js +2 -0
  93. package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +436 -67
  94. package/v2Containers/EmailWrapper/index.js +99 -23
  95. package/v2Containers/EmailWrapper/messages.js +61 -1
  96. package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +111 -77
  97. package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +376 -0
  98. package/v2Containers/InApp/__tests__/sagas.test.js +363 -0
  99. package/v2Containers/InApp/actions.js +7 -0
  100. package/v2Containers/InApp/constants.js +20 -4
  101. package/v2Containers/InApp/index.js +801 -357
  102. package/v2Containers/InApp/index.scss +4 -3
  103. package/v2Containers/InApp/messages.js +7 -3
  104. package/v2Containers/InApp/reducer.js +21 -3
  105. package/v2Containers/InApp/sagas.js +29 -9
  106. package/v2Containers/InApp/selectors.js +25 -5
  107. package/v2Containers/InApp/tests/index.test.js +154 -50
  108. package/v2Containers/InApp/tests/reducer.test.js +34 -0
  109. package/v2Containers/InApp/tests/sagas.test.js +61 -9
  110. package/v2Containers/InApp/tests/selectors.test.js +612 -0
  111. package/v2Containers/InAppWrapper/components/InAppWrapperView.js +162 -0
  112. package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +267 -0
  113. package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +9 -0
  114. package/v2Containers/InAppWrapper/constants.js +16 -0
  115. package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +473 -0
  116. package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +198 -0
  117. package/v2Containers/InAppWrapper/index.js +148 -0
  118. package/v2Containers/InAppWrapper/messages.js +49 -0
  119. package/v2Containers/InappAdvance/index.js +1099 -0
  120. package/v2Containers/InappAdvance/index.scss +10 -0
  121. package/v2Containers/InappAdvance/tests/index.test.js +448 -0
  122. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -3
  123. package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -2
  124. package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -25
  125. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -18
  126. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -46
  127. package/v2Containers/SmsTrai/Create/tests/__snapshots__/index.test.js.snap +0 -4
  128. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -8
  129. package/v2Containers/TagList/index.js +67 -1
  130. package/v2Containers/Templates/ChannelTypeIllustration.js +1 -13
  131. package/v2Containers/Templates/_templates.scss +56 -200
  132. package/v2Containers/Templates/actions.js +1 -2
  133. package/v2Containers/Templates/constants.js +0 -1
  134. package/v2Containers/Templates/index.js +124 -277
  135. package/v2Containers/Templates/messages.js +4 -24
  136. package/v2Containers/Templates/reducer.js +0 -2
  137. package/v2Containers/Templates/tests/index.test.js +0 -10
  138. package/v2Containers/TemplatesV2/index.js +2 -3
  139. package/v2Containers/TemplatesV2/messages.js +0 -4
  140. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +35 -132
  141. package/v2Components/CapImageUrlUpload/constants.js +0 -19
  142. package/v2Components/CapImageUrlUpload/index.js +0 -455
  143. package/v2Components/CapImageUrlUpload/index.scss +0 -35
  144. package/v2Components/CapImageUrlUpload/messages.js +0 -47
  145. package/v2Containers/EmailWrapper/tests/EmailWrapperView.test.js +0 -214
  146. package/v2Containers/WebPush/Create/components/ButtonForm.js +0 -175
  147. package/v2Containers/WebPush/Create/components/ButtonItem.js +0 -101
  148. package/v2Containers/WebPush/Create/components/ButtonList.js +0 -144
  149. package/v2Containers/WebPush/Create/components/_buttons.scss +0 -246
  150. package/v2Containers/WebPush/Create/components/tests/ButtonForm.test.js +0 -554
  151. package/v2Containers/WebPush/Create/components/tests/ButtonItem.test.js +0 -607
  152. package/v2Containers/WebPush/Create/components/tests/ButtonList.test.js +0 -633
  153. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonForm.test.js.snap +0 -666
  154. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonItem.test.js.snap +0 -74
  155. package/v2Containers/WebPush/Create/components/tests/__snapshots__/ButtonList.test.js.snap +0 -80
  156. package/v2Containers/WebPush/Create/index.js +0 -1755
  157. package/v2Containers/WebPush/Create/index.scss +0 -123
  158. package/v2Containers/WebPush/Create/messages.js +0 -199
  159. package/v2Containers/WebPush/Create/preview/DevicePreviewContent.js +0 -241
  160. package/v2Containers/WebPush/Create/preview/NotificationContainer.js +0 -290
  161. package/v2Containers/WebPush/Create/preview/PreviewContent.js +0 -81
  162. package/v2Containers/WebPush/Create/preview/PreviewControls.js +0 -240
  163. package/v2Containers/WebPush/Create/preview/PreviewDisclaimer.js +0 -23
  164. package/v2Containers/WebPush/Create/preview/WebPushPreview.js +0 -144
  165. package/v2Containers/WebPush/Create/preview/assets/Light.svg +0 -53
  166. package/v2Containers/WebPush/Create/preview/assets/Top.svg +0 -5
  167. package/v2Containers/WebPush/Create/preview/assets/chrome-icon.png +0 -0
  168. package/v2Containers/WebPush/Create/preview/assets/edge-icon.png +0 -0
  169. package/v2Containers/WebPush/Create/preview/assets/firefox-icon.svg +0 -106
  170. package/v2Containers/WebPush/Create/preview/assets/iOS.svg +0 -26
  171. package/v2Containers/WebPush/Create/preview/assets/opera-icon.svg +0 -18
  172. package/v2Containers/WebPush/Create/preview/assets/safari-icon.svg +0 -29
  173. package/v2Containers/WebPush/Create/preview/components/AndroidMobileChromeHeader.js +0 -44
  174. package/v2Containers/WebPush/Create/preview/components/AndroidMobileExpanded.js +0 -110
  175. package/v2Containers/WebPush/Create/preview/components/IOSHeader.js +0 -45
  176. package/v2Containers/WebPush/Create/preview/components/NotificationExpandedContent.js +0 -72
  177. package/v2Containers/WebPush/Create/preview/components/NotificationHeader.js +0 -55
  178. package/v2Containers/WebPush/Create/preview/components/WindowsChromeExpanded.js +0 -70
  179. package/v2Containers/WebPush/Create/preview/components/tests/AndroidMobileExpanded.test.js +0 -512
  180. package/v2Containers/WebPush/Create/preview/components/tests/__snapshots__/AndroidMobileExpanded.test.js.snap +0 -77
  181. package/v2Containers/WebPush/Create/preview/config/notificationMappings.js +0 -527
  182. package/v2Containers/WebPush/Create/preview/constants.js +0 -162
  183. package/v2Containers/WebPush/Create/preview/notification-container.scss +0 -104
  184. package/v2Containers/WebPush/Create/preview/preview.scss +0 -409
  185. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-chrome.scss +0 -300
  186. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-edge.scss +0 -12
  187. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-firefox.scss +0 -12
  188. package/v2Containers/WebPush/Create/preview/styles/_android-mobile-opera.scss +0 -12
  189. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-chrome.scss +0 -303
  190. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-edge.scss +0 -11
  191. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-firefox.scss +0 -11
  192. package/v2Containers/WebPush/Create/preview/styles/_android-tablet-opera.scss +0 -11
  193. package/v2Containers/WebPush/Create/preview/styles/_base.scss +0 -188
  194. package/v2Containers/WebPush/Create/preview/styles/_ios.scss +0 -106
  195. package/v2Containers/WebPush/Create/preview/styles/_ipados.scss +0 -107
  196. package/v2Containers/WebPush/Create/preview/styles/_macos-chrome.scss +0 -75
  197. package/v2Containers/WebPush/Create/preview/styles/_windows-chrome.scss +0 -174
  198. package/v2Containers/WebPush/Create/preview/tests/DevicePreviewContent.test.js +0 -909
  199. package/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +0 -1077
  200. package/v2Containers/WebPush/Create/preview/tests/PreviewControls.test.js +0 -723
  201. package/v2Containers/WebPush/Create/preview/tests/WebPushPreview.test.js +0 -943
  202. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/DevicePreviewContent.test.js.snap +0 -128
  203. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/NotificationContainer.test.js.snap +0 -121
  204. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/PreviewControls.test.js.snap +0 -144
  205. package/v2Containers/WebPush/Create/preview/tests/__snapshots__/WebPushPreview.test.js.snap +0 -127
  206. package/v2Containers/WebPush/Create/utils/urlValidation.js +0 -116
  207. package/v2Containers/WebPush/Create/utils/urlValidation.test.js +0 -449
  208. package/v2Containers/WebPush/actions.js +0 -60
  209. package/v2Containers/WebPush/constants.js +0 -108
  210. package/v2Containers/WebPush/index.js +0 -2
  211. package/v2Containers/WebPush/reducer.js +0 -104
  212. package/v2Containers/WebPush/sagas.js +0 -119
  213. package/v2Containers/WebPush/selectors.js +0 -65
  214. package/v2Containers/WebPush/tests/reducer.test.js +0 -863
  215. package/v2Containers/WebPush/tests/sagas.test.js +0 -566
  216. package/v2Containers/WebPush/tests/selectors.test.js +0 -960
@@ -1,214 +0,0 @@
1
- import React from 'react';
2
- import { render, fireEvent, within, screen } from '@testing-library/react';
3
- import '@testing-library/jest-dom';
4
- import { IntlProvider } from 'react-intl';
5
- import { Provider } from 'react-redux';
6
- import { configureStore } from '@capillarytech/vulcan-react-sdk/utils'
7
- import { initialReducer } from '../../../initialReducer';
8
- import EmailWrapperView from '../components/EmailWrapperView';
9
- import { EmailWrapperViewMockProps } from '../mockdata/mockdata';
10
- import { EMAIL_CREATE_MODES } from '../constants';
11
- import history from '../../../utils/history';
12
-
13
- // This mock needs to be before any imports
14
- jest.mock('../../../v2Containers/Email', () => ({
15
- __esModule: true,
16
- default: () => <div data-testid="email-create-container" />,
17
- }));
18
-
19
- // Mock react-router
20
- jest.mock('react-router-dom', () => ({
21
- ...jest.requireActual('react-router-dom'),
22
- useHistory: () => ({
23
- push: jest.fn(),
24
- replace: jest.fn(),
25
- go: jest.fn(),
26
- goBack: jest.fn(),
27
- goForward: jest.fn(),
28
- location: { search: '' }
29
- }),
30
- }));
31
-
32
- // Mock redux-auth-wrapper
33
- jest.mock('redux-auth-wrapper/history4/redirect', () => ({
34
- connectedRouterRedirect: () => (Component) => Component,
35
- }));
36
-
37
- // Mock react-router
38
- jest.mock('connected-react-router', () => ({
39
- push: jest.fn(),
40
- replace: jest.fn(),
41
- go: jest.fn(),
42
- goBack: jest.fn(),
43
- goForward: jest.fn(),
44
- }));
45
-
46
- // Mock the saga injector
47
- jest.mock('@capillarytech/vulcan-react-sdk/utils/injectSaga', () => ({
48
- __esModule: true,
49
- default: () => (Component) => Component,
50
- }));
51
-
52
-
53
- const initialState = {
54
- cap: { loading: false, error: null },
55
- router: { location: { pathname: '/' } }
56
- };
57
-
58
- describe('EmailWrapperView', () => {
59
- let store;
60
-
61
- beforeAll(() => {
62
- store = configureStore(initialState, initialReducer, history);
63
- });
64
-
65
- const renderWithProviders = (component) => {
66
- return render(
67
- <Provider store={store}>
68
- <IntlProvider locale="en" messages={{}}>
69
- {component}
70
- </IntlProvider>
71
- </Provider>
72
- );
73
- };
74
-
75
- it('renders mode selection UI when step is modeSelection', () => {
76
- const props = {
77
- ...EmailWrapperViewMockProps,
78
- step: 'modeSelection',
79
- };
80
-
81
- const { getByText } = renderWithProviders(<EmailWrapperView {...props} />);
82
- expect(getByText('Create using editor')).toBeInTheDocument();
83
- expect(getByText('Upload zip file')).toBeInTheDocument();
84
- });
85
-
86
- it('shows template name input in full mode', () => {
87
- const props = {
88
- ...EmailWrapperViewMockProps,
89
- isFullMode: true,
90
- step: 'modeSelection',
91
- };
92
-
93
- const { container } = renderWithProviders(<EmailWrapperView {...props} />);
94
- const input = container.querySelector('.ant-input');
95
- expect(input).toBeInTheDocument();
96
- expect(input.value).toBe(props.templateName);
97
- });
98
-
99
- it('handles template name changes', () => {
100
- const props = {
101
- ...EmailWrapperViewMockProps,
102
- isFullMode: true,
103
- step: 'modeSelection',
104
- onTemplateNameChange: jest.fn(),
105
- };
106
-
107
- const { container } = renderWithProviders(<EmailWrapperView {...props} />);
108
- const input = container.querySelector('.ant-input');
109
- expect(input).toBeInTheDocument();
110
-
111
- // Create a change event and fire it
112
- fireEvent.change(input, { target: { value: 'New Template Name' } });
113
-
114
- // Just verify that the function was called
115
- expect(props.onTemplateNameChange).toHaveBeenCalled();
116
- });
117
-
118
- it('shows upload button when in upload mode', () => {
119
- const props = {
120
- ...EmailWrapperViewMockProps,
121
- step: 'modeSelection',
122
- emailCreateMode: EMAIL_CREATE_MODES.UPLOAD,
123
- uploadButtonLabel: 'Upload',
124
- };
125
-
126
- const { getByText, container } = renderWithProviders(<EmailWrapperView {...props} />);
127
-
128
- // Find the "Upload zip file" section
129
- const modeSelectionSection = getByText('Upload zip file').closest('div');
130
-
131
- // Find any button in the upload section
132
- const uploadButton = container.querySelector('.ant-btn');
133
-
134
- // Verify that we found an upload button
135
- expect(uploadButton).toBeInTheDocument();
136
- expect(uploadButton.textContent).toContain('Upload');
137
- });
138
-
139
- it('shows loading spinner when uploading', () => {
140
- const props = {
141
- ...EmailWrapperViewMockProps,
142
- isUploading: true,
143
- emailCreateMode: EMAIL_CREATE_MODES.UPLOAD,
144
- step: 'modeSelection',
145
- };
146
-
147
- const { container } = renderWithProviders(<EmailWrapperView {...props} />);
148
- const spinner = container.querySelector('.ant-spin');
149
- expect(spinner).toBeInTheDocument();
150
- });
151
-
152
- it('shows content creation UI when not in mode selection', () => {
153
- const props = {
154
- ...EmailWrapperViewMockProps,
155
- step: 'contentCreation',
156
- isShowEmailCreate: true,
157
- };
158
-
159
- const { getByTestId } = renderWithProviders(<EmailWrapperView {...props} />);
160
- expect(getByTestId('email-create-container')).toBeInTheDocument();
161
- });
162
-
163
- it('disables upload button when template name is empty in full mode', () => {
164
- const props = {
165
- ...EmailWrapperViewMockProps,
166
- isFullMode: true,
167
- step: 'modeSelection',
168
- emailCreateMode: EMAIL_CREATE_MODES.UPLOAD,
169
- templateName: '',
170
- isTemplateNameEmpty: true,
171
- uploadButtonLabel: 'Upload',
172
- };
173
-
174
- const { getByText, container } = renderWithProviders(<EmailWrapperView {...props} />);
175
-
176
- // Find the Upload section
177
- getByText('Upload zip file');
178
-
179
- // Find button in the container
180
- const uploadButton = container.querySelector('.ant-btn');
181
-
182
- // Check if the button is disabled
183
- expect(uploadButton).toBeDisabled();
184
- });
185
-
186
- it('shows error message when template name is empty during upload', () => {
187
- const props = {
188
- ...EmailWrapperViewMockProps,
189
- isFullMode: true,
190
- step: 'modeSelection',
191
- emailCreateMode: EMAIL_CREATE_MODES.UPLOAD,
192
- templateName: '',
193
- isTemplateNameEmpty: true,
194
- // Add errorMessage directly to the props
195
- errorMessage: 'Please enter template name',
196
- intl: {
197
- formatMessage: () => 'Please enter template name',
198
- },
199
- };
200
-
201
- const { container } = renderWithProviders(<EmailWrapperView {...props} />);
202
-
203
- // Look for error feedback in the Ant Design form
204
- const errorElement = container.querySelector('.ant-form-item-explain');
205
-
206
- if (errorElement) {
207
- expect(errorElement.textContent).toContain('Please enter template name');
208
- } else {
209
- // If the standard error element is not found, look for any element containing the error text
210
- const allText = container.textContent;
211
- expect(allText).toContain('Please enter template name');
212
- }
213
- });
214
- });
@@ -1,175 +0,0 @@
1
- import React, { useState, useCallback } from 'react';
2
- import PropTypes from 'prop-types';
3
- import { FormattedMessage } from 'react-intl';
4
- import CapRow from '@capillarytech/cap-ui-library/CapRow';
5
- import CapInput from '@capillarytech/cap-ui-library/CapInput';
6
- import CapButton from '@capillarytech/cap-ui-library/CapButton';
7
- import CapHeading from '@capillarytech/cap-ui-library/CapHeading';
8
- import messages from '../messages';
9
- import { isValidHttpUrl } from '../utils/urlValidation';
10
- import { WEBPUSH_BUTTON_TYPES } from '../../constants';
11
-
12
- const BUTTON_TEXT_MAX_LENGTH = 20;
13
-
14
- const ButtonForm = ({
15
- buttonType, // 'primary' or 'secondary'
16
- formatMessage,
17
- onSave,
18
- onCancel,
19
- initialData,
20
- isEditMode,
21
- }) => {
22
- const [buttonText, setButtonText] = useState(initialData?.text || '');
23
- const [buttonUrl, setButtonUrl] = useState(initialData?.url || '');
24
- const [buttonTextError, setButtonTextError] = useState('');
25
- const [buttonUrlError, setButtonUrlError] = useState('');
26
-
27
- const validateButtonText = useCallback((value) => {
28
- if (!value || value.trim() === '') {
29
- return formatMessage(messages.buttonTextRequired);
30
- }
31
- return '';
32
- }, [formatMessage]);
33
-
34
- const validateButtonUrl = useCallback((value) => {
35
- if (!value || value.trim() === '') {
36
- return formatMessage(messages.buttonUrlRequired);
37
- }
38
-
39
- if (!isValidHttpUrl(value)) {
40
- return formatMessage(messages.buttonUrlInvalid);
41
- }
42
-
43
- return '';
44
- }, [formatMessage]);
45
-
46
- const handleButtonTextChange = useCallback((e) => {
47
- const { value } = e.target;
48
- setButtonText(value);
49
- const nextError = validateButtonText(value);
50
- setButtonTextError((prev) => (prev === nextError ? prev : nextError));
51
- }, [validateButtonText]);
52
-
53
- const handleButtonUrlChange = useCallback((e) => {
54
- const { value } = e.target;
55
- setButtonUrl(value);
56
- const nextError = validateButtonUrl(value);
57
- setButtonUrlError((prev) => (prev === nextError ? prev : nextError));
58
- }, [validateButtonUrl]);
59
-
60
- const handleSave = useCallback(() => {
61
- const textError = validateButtonText(buttonText);
62
- const urlError = validateButtonUrl(buttonUrl);
63
-
64
- if (textError || urlError) {
65
- setButtonTextError(textError);
66
- setButtonUrlError(urlError);
67
- return;
68
- }
69
-
70
- onSave({
71
- text: buttonText.trim(),
72
- url: buttonUrl.trim(),
73
- type: buttonType,
74
- });
75
- }, [buttonText, buttonUrl, buttonType, onSave, validateButtonText, validateButtonUrl]);
76
-
77
- const handleCancel = useCallback(() => {
78
- onCancel();
79
- }, [onCancel]);
80
-
81
- const isSaveDisabled = !buttonText.trim() || !buttonUrl.trim() || buttonTextError || buttonUrlError;
82
-
83
- const renderCharacterCountSuffix = () => {
84
- const currentLength = buttonText.length;
85
- const maxLength = BUTTON_TEXT_MAX_LENGTH;
86
-
87
- return (
88
- <span className="button-character-count-suffix">
89
- {`${currentLength}/${maxLength}`}
90
- </span>
91
- );
92
- };
93
-
94
- const handleKeyDown = useCallback((e) => {
95
- if (e.key === 'Enter' && !isSaveDisabled) {
96
- e.preventDefault();
97
- handleSave();
98
- }
99
- }, [handleSave, isSaveDisabled]);
100
-
101
- return (
102
- <div className="webpush-button-form" onKeyDown={handleKeyDown}>
103
- <CapRow className="button-form-row">
104
- <CapHeading type="h4" className="button-form-heading">
105
- <FormattedMessage {...messages.buttonText} />
106
- </CapHeading>
107
- <div className="button-form-field">
108
- {/* TODO: Enable "+ Add labels" control once label functionality is ready */}
109
- <CapInput
110
- id={`webpush-button-text-input-${buttonType}`}
111
- placeholder={formatMessage(messages.buttonTextPlaceholder)}
112
- value={buttonText}
113
- onChange={handleButtonTextChange}
114
- maxLength={BUTTON_TEXT_MAX_LENGTH}
115
- size="default"
116
- status={buttonTextError ? 'error' : ''}
117
- help={buttonTextError || ''}
118
- suffix={renderCharacterCountSuffix()}
119
- />
120
- </div>
121
- </CapRow>
122
- <CapRow className="button-form-row">
123
- <CapHeading type="h4" className="button-form-heading">
124
- <FormattedMessage {...messages.buttonUrlLabel} />
125
- </CapHeading>
126
- <CapInput
127
- id={`webpush-button-url-input-${buttonType}`}
128
- placeholder={formatMessage(messages.buttonUrlPlaceholder)}
129
- value={buttonUrl}
130
- onChange={handleButtonUrlChange}
131
- size="default"
132
- status={buttonUrlError ? 'error' : ''}
133
- help={buttonUrlError || ''}
134
- />
135
- </CapRow>
136
- <CapRow className="button-form-actions">
137
- <CapButton
138
- type="primary"
139
- onClick={handleSave}
140
- disabled={isSaveDisabled}
141
- className="button-form-save"
142
- >
143
- <FormattedMessage {...messages.saveButton} />
144
- </CapButton>
145
- <CapButton
146
- type="secondary"
147
- onClick={handleCancel}
148
- className="button-form-cancel"
149
- >
150
- <FormattedMessage {...(isEditMode ? messages.cancelButton : messages.deleteButton)} />
151
- </CapButton>
152
- </CapRow>
153
- </div>
154
- );
155
- };
156
-
157
- ButtonForm.propTypes = {
158
- buttonType: PropTypes.oneOf([WEBPUSH_BUTTON_TYPES.PRIMARY, WEBPUSH_BUTTON_TYPES.SECONDARY]).isRequired,
159
- formatMessage: PropTypes.func.isRequired,
160
- onSave: PropTypes.func.isRequired,
161
- onCancel: PropTypes.func.isRequired,
162
- initialData: PropTypes.shape({
163
- text: PropTypes.string,
164
- url: PropTypes.string,
165
- }),
166
- isEditMode: PropTypes.bool,
167
- };
168
-
169
- ButtonForm.defaultProps = {
170
- initialData: null,
171
- isEditMode: false,
172
- };
173
-
174
- export default ButtonForm;
175
-
@@ -1,101 +0,0 @@
1
- import React from 'react';
2
- import PropTypes from 'prop-types';
3
- import CapRow from '@capillarytech/cap-ui-library/CapRow';
4
- import CapColumn from '@capillarytech/cap-ui-library/CapColumn';
5
- import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
6
-
7
- const ButtonItem = ({
8
- button,
9
- index,
10
- onEdit,
11
- onDelete,
12
- onDragStart,
13
- onDragOver,
14
- onDrop,
15
- onDragEnd,
16
- disabled,
17
- }) => {
18
- const handleDragStart = (e) => {
19
- if (disabled) return;
20
- e.dataTransfer.effectAllowed = 'move';
21
- e.dataTransfer.setData('text/html', e.currentTarget);
22
- onDragStart(index);
23
- };
24
-
25
- const handleDragOver = (e) => {
26
- if (disabled) return;
27
- e.preventDefault();
28
- e.dataTransfer.dropEffect = 'move';
29
- onDragOver(index);
30
- };
31
-
32
- const handleDrop = (e) => {
33
- if (disabled) return;
34
- e.preventDefault();
35
- e.stopPropagation();
36
- onDrop(index);
37
- };
38
-
39
- return (
40
- <div
41
- className={`webpush-button-item ${disabled ? 'disabled' : ''}`}
42
- draggable={!disabled}
43
- onDragStart={handleDragStart}
44
- onDragOver={handleDragOver}
45
- onDrop={handleDrop}
46
- onDragEnd={onDragEnd}
47
- >
48
- <CapRow align="middle" className="button-item-content">
49
- <CapColumn span={1} className="button-item-drag-handle">
50
- <CapIcon type="drag" className="drag-icon" />
51
- </CapColumn>
52
- <CapColumn span={1} className="button-item-icon">
53
- <CapIcon type="link" className="link-icon" />
54
- </CapColumn>
55
- <CapColumn span={14} className="button-item-info">
56
- <div className="button-item-text-row">
57
- <span className="button-item-text">{button.text}</span>
58
- </div>
59
- </CapColumn>
60
- <CapColumn span={8} className="button-item-actions">
61
- <div className="button-item-url">{button.url}</div>
62
- <div className="action-icons">
63
- <CapIcon
64
- type="edit"
65
- className="action-icon"
66
- onClick={() => !disabled && onEdit(index)}
67
- />
68
- <CapIcon
69
- type="delete"
70
- className="action-icon delete-icon"
71
- onClick={() => !disabled && onDelete(index)}
72
- />
73
- </div>
74
- </CapColumn>
75
- </CapRow>
76
- </div>
77
- );
78
- };
79
-
80
- ButtonItem.propTypes = {
81
- button: PropTypes.shape({
82
- text: PropTypes.string.isRequired,
83
- url: PropTypes.string.isRequired,
84
- type: PropTypes.string.isRequired,
85
- }).isRequired,
86
- index: PropTypes.number.isRequired,
87
- onEdit: PropTypes.func.isRequired,
88
- onDelete: PropTypes.func.isRequired,
89
- onDragStart: PropTypes.func.isRequired,
90
- onDragOver: PropTypes.func.isRequired,
91
- onDrop: PropTypes.func.isRequired,
92
- onDragEnd: PropTypes.func.isRequired,
93
- disabled: PropTypes.bool,
94
- };
95
-
96
- ButtonItem.defaultProps = {
97
- disabled: false,
98
- };
99
-
100
- export default ButtonItem;
101
-
@@ -1,144 +0,0 @@
1
- import React, { useState } from 'react';
2
- import PropTypes from 'prop-types';
3
- import { FormattedMessage } from 'react-intl';
4
- import CapRow from '@capillarytech/cap-ui-library/CapRow';
5
- import CapButton from '@capillarytech/cap-ui-library/CapButton';
6
- import ButtonItem from './ButtonItem';
7
- import messages from '../messages';
8
-
9
- const ButtonList = ({
10
- buttons,
11
- onEdit,
12
- onDelete,
13
- onReorder,
14
- onAddPrimary,
15
- onAddSecondary,
16
- showAddPrimary,
17
- showAddSecondary,
18
- disabled,
19
- disableSecondaryButton,
20
- isInlineFormVisible,
21
- inlineFormIndex,
22
- renderInlineForm,
23
- }) => {
24
- const [draggedIndex, setDraggedIndex] = useState(null);
25
-
26
- const handleDragStart = (index) => {
27
- if (disabled) return;
28
- setDraggedIndex(index);
29
- };
30
-
31
- const handleDragOver = (index) => {
32
- if (disabled) return;
33
- if (draggedIndex === null || draggedIndex === index) return;
34
-
35
- // Reorder buttons
36
- onReorder(draggedIndex, index);
37
- setDraggedIndex(index);
38
- };
39
-
40
- const handleDrop = () => {
41
- if (disabled) return;
42
- };
43
-
44
- const handleDragEnd = () => {
45
- if (disabled) return;
46
- setDraggedIndex(null);
47
- };
48
-
49
- const shouldRenderInlineForm = isInlineFormVisible
50
- && typeof inlineFormIndex === 'number'
51
- && inlineFormIndex >= 0;
52
-
53
- // Don't render the container if there are no buttons
54
- if (buttons.length === 0) {
55
- return null;
56
- }
57
-
58
- return (
59
- <div className="webpush-button-list">
60
- {buttons.map((button, index) => {
61
- if (shouldRenderInlineForm && inlineFormIndex === index && typeof renderInlineForm === 'function') {
62
- return (
63
- <div key={`button-inline-form-${index}`} className="button-inline-form">
64
- {renderInlineForm()}
65
- </div>
66
- );
67
- }
68
- // Only disable items that are not being edited
69
- const isItemDisabled = disabled || (isInlineFormVisible && inlineFormIndex !== null && inlineFormIndex !== index);
70
- return (
71
- <ButtonItem
72
- key={`button-${index}`}
73
- button={button}
74
- index={index}
75
- onEdit={onEdit}
76
- onDelete={onDelete}
77
- onDragStart={handleDragStart}
78
- onDragOver={handleDragOver}
79
- onDrop={handleDrop}
80
- onDragEnd={handleDragEnd}
81
- disabled={isItemDisabled}
82
- />
83
- );
84
- })}
85
- {showAddPrimary && (
86
- <CapRow className="button-list-add-button">
87
- <CapButton
88
- type="flat"
89
- onClick={onAddPrimary}
90
- className="add-primary-button button-add-trigger"
91
- icon="plus"
92
- disabled={disabled}
93
- >
94
- <FormattedMessage {...messages.addPrimaryButton} />
95
- </CapButton>
96
- </CapRow>
97
- )}
98
- {showAddSecondary && (
99
- <CapRow className="button-list-add-button">
100
- <CapButton
101
- type="flat"
102
- onClick={onAddSecondary}
103
- className="add-secondary-button button-add-trigger"
104
- icon="plus"
105
- disabled={disableSecondaryButton || disabled}
106
- >
107
- <FormattedMessage {...messages.addSecondaryButton} />
108
- </CapButton>
109
- </CapRow>
110
- )}
111
- </div>
112
- );
113
- };
114
-
115
- ButtonList.propTypes = {
116
- buttons: PropTypes.arrayOf(PropTypes.shape({
117
- text: PropTypes.string.isRequired,
118
- url: PropTypes.string.isRequired,
119
- type: PropTypes.string.isRequired,
120
- })).isRequired,
121
- onEdit: PropTypes.func.isRequired,
122
- onDelete: PropTypes.func.isRequired,
123
- onReorder: PropTypes.func.isRequired,
124
- onAddPrimary: PropTypes.func.isRequired,
125
- onAddSecondary: PropTypes.func.isRequired,
126
- showAddPrimary: PropTypes.bool.isRequired,
127
- showAddSecondary: PropTypes.bool.isRequired,
128
- disabled: PropTypes.bool,
129
- disableSecondaryButton: PropTypes.bool,
130
- isInlineFormVisible: PropTypes.bool,
131
- inlineFormIndex: PropTypes.number,
132
- renderInlineForm: PropTypes.func,
133
- };
134
-
135
- ButtonList.defaultProps = {
136
- disabled: false,
137
- disableSecondaryButton: false,
138
- isInlineFormVisible: false,
139
- inlineFormIndex: null,
140
- renderInlineForm: null,
141
- };
142
-
143
- export default ButtonList;
144
-