@transferwise/components 46.38.0 → 46.40.0

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 (202) hide show
  1. package/build/index.js +198 -461
  2. package/build/index.js.map +1 -1
  3. package/build/index.mjs +198 -461
  4. package/build/index.mjs.map +1 -1
  5. package/build/types/flowNavigation/backButton/BackButton.d.ts +5 -17
  6. package/build/types/flowNavigation/backButton/BackButton.d.ts.map +1 -1
  7. package/build/types/flowNavigation/backButton/index.d.ts +2 -2
  8. package/build/types/flowNavigation/backButton/index.d.ts.map +1 -1
  9. package/build/types/index.d.ts +3 -1
  10. package/build/types/index.d.ts.map +1 -1
  11. package/build/types/info/Info.d.ts +2 -2
  12. package/build/types/info/Info.d.ts.map +1 -1
  13. package/build/types/info/index.d.ts +1 -1
  14. package/build/types/info/index.d.ts.map +1 -1
  15. package/build/types/overlayHeader/OverlayHeader.d.ts +9 -18
  16. package/build/types/overlayHeader/OverlayHeader.d.ts.map +1 -1
  17. package/build/types/overlayHeader/index.d.ts +2 -1
  18. package/build/types/overlayHeader/index.d.ts.map +1 -1
  19. package/build/types/processIndicator/ProcessIndicator.d.ts +1 -1
  20. package/build/types/processIndicator/ProcessIndicator.d.ts.map +1 -1
  21. package/build/types/upload/Upload.d.ts +91 -55
  22. package/build/types/upload/Upload.d.ts.map +1 -1
  23. package/build/types/upload/Upload.messages.d.ts +42 -60
  24. package/build/types/upload/Upload.messages.d.ts.map +1 -1
  25. package/build/types/upload/index.d.ts +2 -2
  26. package/build/types/upload/index.d.ts.map +1 -1
  27. package/build/types/upload/steps/completeStep/completeStep.d.ts +11 -18
  28. package/build/types/upload/steps/completeStep/completeStep.d.ts.map +1 -1
  29. package/build/types/upload/steps/completeStep/index.d.ts +2 -1
  30. package/build/types/upload/steps/completeStep/index.d.ts.map +1 -1
  31. package/build/types/upload/steps/index.d.ts +3 -4
  32. package/build/types/upload/steps/index.d.ts.map +1 -1
  33. package/build/types/upload/steps/processingStep/index.d.ts +2 -1
  34. package/build/types/upload/steps/processingStep/index.d.ts.map +1 -1
  35. package/build/types/upload/steps/processingStep/processingStep.d.ts +11 -13
  36. package/build/types/upload/steps/processingStep/processingStep.d.ts.map +1 -1
  37. package/build/types/upload/steps/uploadImageStep/index.d.ts +2 -1
  38. package/build/types/upload/steps/uploadImageStep/index.d.ts.map +1 -1
  39. package/build/types/upload/steps/uploadImageStep/uploadImageStep.d.ts +14 -18
  40. package/build/types/upload/steps/uploadImageStep/uploadImageStep.d.ts.map +1 -1
  41. package/build/types/upload/utils/asyncFileRead/asyncFileRead.d.ts +1 -1
  42. package/build/types/upload/utils/asyncFileRead/asyncFileRead.d.ts.map +1 -1
  43. package/build/types/upload/utils/asyncFileRead/index.d.ts +1 -1
  44. package/build/types/upload/utils/asyncFileRead/index.d.ts.map +1 -1
  45. package/build/types/upload/utils/getFileType/getFileType.d.ts +1 -1
  46. package/build/types/upload/utils/getFileType/getFileType.d.ts.map +1 -1
  47. package/build/types/upload/utils/getFileType/index.d.ts +1 -1
  48. package/build/types/upload/utils/getFileType/index.d.ts.map +1 -1
  49. package/build/types/upload/utils/index.d.ts +5 -7
  50. package/build/types/upload/utils/index.d.ts.map +1 -1
  51. package/build/types/upload/utils/isSizeValid/index.d.ts +1 -1
  52. package/build/types/upload/utils/isSizeValid/index.d.ts.map +1 -1
  53. package/build/types/upload/utils/isSizeValid/isSizeValid.d.ts +1 -1
  54. package/build/types/upload/utils/isSizeValid/isSizeValid.d.ts.map +1 -1
  55. package/build/types/upload/utils/isTypeValid/index.d.ts +1 -1
  56. package/build/types/upload/utils/isTypeValid/index.d.ts.map +1 -1
  57. package/build/types/upload/utils/isTypeValid/isTypeValid.d.ts +1 -1
  58. package/build/types/upload/utils/isTypeValid/isTypeValid.d.ts.map +1 -1
  59. package/build/types/upload/utils/postData/index.d.ts +1 -1
  60. package/build/types/upload/utils/postData/index.d.ts.map +1 -1
  61. package/build/types/upload/utils/postData/postData.d.ts +11 -1
  62. package/build/types/upload/utils/postData/postData.d.ts.map +1 -1
  63. package/package.json +24 -26
  64. package/src/accordion/Accordion.spec.js +5 -5
  65. package/src/accordion/AccordionItem/AccordionItem.spec.js +2 -2
  66. package/src/actionButton/ActionButton.spec.tsx +4 -5
  67. package/src/alert/Alert.spec.tsx +4 -4
  68. package/src/alert/Alert.story.tsx +6 -5
  69. package/src/button/Button.spec.js +4 -5
  70. package/src/card/Card.spec.tsx +4 -4
  71. package/src/carousel/Carousel.spec.tsx +17 -17
  72. package/src/checkbox/Checkbox.spec.tsx +0 -2
  73. package/src/checkboxButton/CheckboxButton.spec.tsx +0 -2
  74. package/src/checkboxOption/CheckboxOption.spec.tsx +0 -2
  75. package/src/chevron/Chevron.spec.tsx +0 -1
  76. package/src/chips/Chips.spec.tsx +0 -1
  77. package/src/chips/Chips.story.tsx +5 -3
  78. package/src/circularButton/CircularButton.spec.tsx +4 -5
  79. package/src/common/RadioButton/RadioButton.spec.tsx +2 -2
  80. package/src/common/card/Card.story.tsx +1 -0
  81. package/src/common/closeButton/CloseButton.spec.tsx +0 -1
  82. package/src/common/flowHeader/FlowHeader.spec.tsx +0 -1
  83. package/src/dateInput/DateInput.story.tsx +21 -16
  84. package/src/dateLookup/DateLookup.rtl.spec.tsx +18 -16
  85. package/src/dateLookup/DateLookup.testingLibrary.spec.js +47 -44
  86. package/src/dateLookup/DateLookup.tests.story.tsx +4 -2
  87. package/src/decision/Decision.spec.js +0 -2
  88. package/src/dimmer/Dimmer.rtl.spec.js +10 -10
  89. package/src/drawer/Drawer.rtl.spec.tsx +2 -2
  90. package/src/emphasis/Emphasis.spec.tsx +0 -1
  91. package/src/field/Field.spec.tsx +2 -2
  92. package/src/flowNavigation/FlowNavigation.spec.js +0 -2
  93. package/src/flowNavigation/animatedLabel/AnimatedLabel.spec.js +0 -1
  94. package/src/flowNavigation/backButton/BackButton.tsx +29 -0
  95. package/src/flowNavigation/backButton/index.ts +2 -0
  96. package/src/header/Header.spec.tsx +6 -6
  97. package/src/image/Image.spec.tsx +0 -1
  98. package/src/index.ts +3 -1
  99. package/src/info/Info.story.tsx +15 -9
  100. package/src/info/Info.tsx +2 -2
  101. package/src/info/index.ts +1 -1
  102. package/src/inlineAlert/InlineAlert.spec.tsx +0 -1
  103. package/src/inputs/SelectInput.spec.tsx +26 -47
  104. package/src/link/Link.spec.tsx +0 -1
  105. package/src/listItem/ListItem.spec.tsx +0 -1
  106. package/src/moneyInput/MoneyInput.rtl.spec.tsx +2 -2
  107. package/src/moneyInput/MoneyInput.story.tsx +1 -4
  108. package/src/overlayHeader/{OverlayHeader.spec.js → OverlayHeader.spec.tsx} +1 -1
  109. package/src/overlayHeader/{OverlayHeader.story.js → OverlayHeader.story.tsx} +10 -2
  110. package/src/overlayHeader/{OverlayHeader.js → OverlayHeader.tsx} +12 -19
  111. package/src/overlayHeader/index.ts +2 -0
  112. package/src/phoneNumberInput/PhoneNumberInput.story.tsx +1 -0
  113. package/src/popover/Popover.spec.tsx +10 -10
  114. package/src/processIndicator/ProcessIndicator.tsx +1 -1
  115. package/src/progress/Progress.spec.tsx +0 -1
  116. package/src/progressBar/ProgressBar.spec.tsx +0 -1
  117. package/src/segmentedControl/SegmentedControl.spec.tsx +10 -11
  118. package/src/select/Select.spec.js +71 -71
  119. package/src/test-utils/index.js +1 -1
  120. package/src/test-utils/jest.setup.js +2 -0
  121. package/src/tooltip/Tooltip.spec.tsx +15 -16
  122. package/src/upload/Upload.spec.js +3 -14
  123. package/src/upload/Upload.story.tsx +37 -0
  124. package/src/upload/{Upload.js → Upload.tsx} +164 -169
  125. package/src/upload/index.ts +2 -0
  126. package/src/upload/steps/completeStep/completeStep.spec.js +3 -2
  127. package/src/upload/steps/completeStep/completeStep.tsx +74 -0
  128. package/src/upload/steps/completeStep/index.ts +2 -0
  129. package/src/upload/steps/{index.js → index.ts} +0 -1
  130. package/src/upload/steps/processingStep/index.ts +2 -0
  131. package/src/upload/steps/processingStep/processingStep.tsx +53 -0
  132. package/src/upload/steps/uploadImageStep/index.ts +2 -0
  133. package/src/upload/steps/uploadImageStep/{uploadImageStep.js → uploadImageStep.tsx} +17 -23
  134. package/src/upload/utils/asyncFileRead/asyncFileRead.spec.ts +14 -0
  135. package/src/upload/utils/asyncFileRead/asyncFileRead.ts +12 -0
  136. package/src/upload/utils/getFileType/getFileType.spec.ts +22 -0
  137. package/src/upload/utils/getFileType/getFileType.ts +16 -0
  138. package/src/upload/utils/{index.js → index.ts} +0 -2
  139. package/src/upload/utils/isSizeValid/{isSizeValid.spec.js → isSizeValid.spec.ts} +3 -3
  140. package/src/upload/utils/isSizeValid/isSizeValid.ts +3 -0
  141. package/src/upload/utils/isTypeValid/isTypeValid.spec.ts +62 -0
  142. package/src/upload/utils/isTypeValid/isTypeValid.ts +19 -0
  143. package/src/upload/utils/postData/postData.spec.ts +65 -0
  144. package/src/upload/utils/postData/postData.ts +36 -0
  145. package/src/uploadInput/UploadInput.spec.tsx +9 -10
  146. package/src/uploadInput/UploadInput.story.tsx +8 -180
  147. package/src/uploadInput/UploadInput.tests.story.tsx +212 -0
  148. package/src/uploadInput/UploadInput.tsx +1 -1
  149. package/src/uploadInput/uploadButton/UploadButton.spec.tsx +4 -4
  150. package/src/uploadInput/uploadItem/UploadItem.spec.tsx +4 -4
  151. package/build/types/upload/steps/mediaUploadStep/index.d.ts +0 -2
  152. package/build/types/upload/steps/mediaUploadStep/index.d.ts.map +0 -1
  153. package/build/types/upload/steps/mediaUploadStep/mediaUploadStep.d.ts +0 -24
  154. package/build/types/upload/steps/mediaUploadStep/mediaUploadStep.d.ts.map +0 -1
  155. package/build/types/upload/uploadSteps.d.ts +0 -5
  156. package/build/types/upload/uploadSteps.d.ts.map +0 -1
  157. package/build/types/upload/utils/getSupportedSpotMimeTypes/getSupportedSpotMimeTypes.d.ts +0 -2
  158. package/build/types/upload/utils/getSupportedSpotMimeTypes/getSupportedSpotMimeTypes.d.ts.map +0 -1
  159. package/build/types/upload/utils/getSupportedSpotMimeTypes/index.d.ts +0 -2
  160. package/build/types/upload/utils/getSupportedSpotMimeTypes/index.d.ts.map +0 -1
  161. package/build/types/upload/utils/requestMedia/index.d.ts +0 -2
  162. package/build/types/upload/utils/requestMedia/index.d.ts.map +0 -1
  163. package/build/types/upload/utils/requestMedia/requestMedia.d.ts +0 -2
  164. package/build/types/upload/utils/requestMedia/requestMedia.d.ts.map +0 -1
  165. package/src/flowNavigation/backButton/BackButton.js +0 -32
  166. package/src/flowNavigation/backButton/BackButton.spec.js +0 -16
  167. package/src/flowNavigation/backButton/__snapshots__/BackButton.spec.js.snap +0 -37
  168. package/src/flowNavigation/backButton/index.js +0 -3
  169. package/src/overlayHeader/index.js +0 -1
  170. package/src/upload/Upload.story.js +0 -36
  171. package/src/upload/index.js +0 -2
  172. package/src/upload/steps/completeStep/completeStep.js +0 -98
  173. package/src/upload/steps/completeStep/index.js +0 -1
  174. package/src/upload/steps/mediaUploadStep/index.js +0 -1
  175. package/src/upload/steps/mediaUploadStep/mediaUploadStep.js +0 -80
  176. package/src/upload/steps/mediaUploadStep/mediaUploadStep.spec.js +0 -77
  177. package/src/upload/steps/processingStep/index.js +0 -1
  178. package/src/upload/steps/processingStep/processingStep.js +0 -73
  179. package/src/upload/steps/uploadImageStep/index.js +0 -1
  180. package/src/upload/uploadSteps.ts +0 -5
  181. package/src/upload/utils/asyncFileRead/asyncFileRead.js +0 -11
  182. package/src/upload/utils/asyncFileRead/asyncFileRead.spec.js +0 -17
  183. package/src/upload/utils/getFileType/getFileType.js +0 -19
  184. package/src/upload/utils/getFileType/getFileType.spec.js +0 -33
  185. package/src/upload/utils/getSupportedSpotMimeTypes/getSupportedSpotMimeTypes.js +0 -18
  186. package/src/upload/utils/getSupportedSpotMimeTypes/getSupportedSpotMimeTypes.spec.js +0 -22
  187. package/src/upload/utils/getSupportedSpotMimeTypes/index.js +0 -1
  188. package/src/upload/utils/isSizeValid/isSizeValid.js +0 -1
  189. package/src/upload/utils/isTypeValid/isTypeValid.js +0 -26
  190. package/src/upload/utils/isTypeValid/isTypeValid.spec.js +0 -68
  191. package/src/upload/utils/postData/postData.js +0 -18
  192. package/src/upload/utils/postData/postData.spec.js +0 -109
  193. package/src/upload/utils/requestMedia/index.js +0 -1
  194. package/src/upload/utils/requestMedia/requestMedia.js +0 -26
  195. package/src/upload/utils/requestMedia/requestMedia.spec.js +0 -44
  196. /package/src/overlayHeader/__snapshots__/{OverlayHeader.spec.js.snap → OverlayHeader.spec.tsx.snap} +0 -0
  197. /package/src/upload/{Upload.messages.js → Upload.messages.ts} +0 -0
  198. /package/src/upload/utils/asyncFileRead/{index.js → index.ts} +0 -0
  199. /package/src/upload/utils/getFileType/{index.js → index.ts} +0 -0
  200. /package/src/upload/utils/isSizeValid/{index.js → index.ts} +0 -0
  201. /package/src/upload/utils/isTypeValid/{index.js → index.ts} +0 -0
  202. /package/src/upload/utils/postData/{index.js → index.ts} +0 -0
@@ -0,0 +1,37 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { fn } from '@storybook/test';
3
+
4
+ import Upload from '.';
5
+ import { MAX_SIZE_DEFAULT } from './Upload';
6
+
7
+ const meta = {
8
+ component: Upload,
9
+ tags: ['autodocs'],
10
+ argTypes: {
11
+ maxSize: {
12
+ control: {
13
+ type: 'number',
14
+ min: 0,
15
+ },
16
+ },
17
+ },
18
+ } satisfies Meta<typeof Upload>;
19
+
20
+ export default meta;
21
+ type Story = StoryObj<typeof meta>;
22
+
23
+ export const Basic = {
24
+ args: {
25
+ maxSize: MAX_SIZE_DEFAULT,
26
+ usAccept: 'image/*',
27
+ usLabel: 'Front of your ID document',
28
+ httpOptions: {
29
+ url: 'https://httpbin.org/post',
30
+ method: 'POST',
31
+ },
32
+ onStart: fn(),
33
+ onSuccess: fn(),
34
+ onFailure: fn(),
35
+ onCancel: fn(),
36
+ },
37
+ } satisfies Story;
@@ -1,68 +1,130 @@
1
1
  import { Plus as PlusIcon } from '@transferwise/icons';
2
2
  import classNames from 'classnames';
3
- import PropTypes from 'prop-types';
4
3
  import { Component } from 'react';
5
- import { injectIntl } from 'react-intl';
4
+ import { injectIntl, WrappedComponentProps } from 'react-intl';
6
5
 
7
- import { Status, Typography } from '../common';
6
+ import { Size, Typography } from '../common';
8
7
  import Title from '../title';
9
8
 
10
9
  import messages from './Upload.messages';
11
- import { UploadImageStep, MediaUploadStep, ProcessingStep, CompleteStep } from './steps';
12
- import { UploadStep } from './uploadSteps';
10
+ import { UploadImageStep, ProcessingStep, CompleteStep } from './steps';
13
11
  import { postData, asyncFileRead, isSizeValid, isTypeValid, getFileType } from './utils';
14
-
15
- const PROCESS_STATE = ['error', 'success'];
12
+ import { PostDataFetcher, PostDataHTTPOptions, ResponseError } from './utils/postData/postData';
13
+ import { ProcessIndicatorStatus } from '../processIndicator';
16
14
 
17
15
  /*
18
16
  * This delay is required for the isError/isSuccess to be fired after isProcessing so the processIndicator, will be
19
17
  * rendered first and then updated with the right status.
20
18
  */
21
19
  const ANIMATION_FIX = 10;
22
- const MAX_SIZE_DEFAULT = 5000000;
23
- const UPLOAD_STEP_COMPONENTS = {
24
- [UploadStep.UPLOAD_IMAGE_STEP]: UploadImageStep,
25
- [UploadStep.MEDIA_UPLOAD_STEP]: MediaUploadStep,
26
- };
27
-
28
- class Upload extends Component {
29
- constructor(props) {
20
+ export const MAX_SIZE_DEFAULT = 5000000;
21
+
22
+ export enum UploadStep {
23
+ UPLOAD_IMAGE_STEP = 'uploadImageStep',
24
+ }
25
+
26
+ export interface UploadProps extends WrappedComponentProps {
27
+ animationDelay?: number;
28
+ csButtonText?: string;
29
+ csFailureText?: string;
30
+ csSuccessText?: string;
31
+ csTooLargeMessage?: string;
32
+ csWrongTypeMessage?: string;
33
+ httpOptions?: PostDataHTTPOptions & {
34
+ fileInputName?: string;
35
+ data?: Record<string, string | Blob>;
36
+ };
37
+ /**
38
+ * You can provide a fetcher function with the same interface as the global fetch function, which is used by default.
39
+ * function fetcher(input: RequestInfo, init?: RequestInit): Promise<Response>
40
+ */
41
+ fetcher?: PostDataFetcher;
42
+ maxSize?: number;
43
+ psButtonText?: string;
44
+ psButtonDisabled?: boolean;
45
+ psProcessingText?: string;
46
+ size?: `${Size.SMALL | Size.MEDIUM | Size.LARGE}`;
47
+ /**
48
+ * You can provide multiple rules separated by comma, e.g.: "application/pdf,image/*".
49
+ * Using "*" will allow every file type to be uploaded.
50
+ */
51
+ usAccept?: string;
52
+ usButtonText?: string;
53
+ usDisabled?: boolean;
54
+ usDropMessage?: string;
55
+ usHelpImage?: React.ReactNode;
56
+ usLabel?: string;
57
+ usPlaceholder?: string;
58
+ /** @deprecated Only a single variant exists, please remove this prop. */
59
+ uploadStep?: `${UploadStep}`;
60
+ onCancel?: () => void;
61
+ onFailure?: (error: unknown) => void;
62
+ onStart?: (file: File) => void;
63
+ onSuccess?: (response: string | Response, fileName: string) => void;
64
+ }
65
+
66
+ interface UploadState {
67
+ fileName: string;
68
+ isDroppable: boolean;
69
+ isComplete: boolean;
70
+ isError: boolean;
71
+ isImage: boolean;
72
+ isProcessing: boolean;
73
+ isSuccess: boolean;
74
+ response: unknown;
75
+ uploadedImage: string | undefined;
76
+ }
77
+
78
+ export class Upload extends Component<UploadProps, UploadState> {
79
+ declare props: UploadProps & Required<Pick<UploadProps, keyof typeof Upload.defaultProps>>;
80
+
81
+ static defaultProps = {
82
+ animationDelay: 700,
83
+ maxSize: MAX_SIZE_DEFAULT,
84
+ psButtonDisabled: false,
85
+ size: 'md',
86
+ usAccept: 'image/*',
87
+ usDisabled: false,
88
+ usLabel: '',
89
+ } satisfies Partial<UploadProps>;
90
+
91
+ dragCounter = 0;
92
+ timeouts = 0;
93
+
94
+ constructor(props: UploadProps) {
30
95
  super(props);
31
- this.dragCounter = 0;
32
- this.timeouts = null;
33
96
 
34
97
  this.state = {
35
98
  fileName: '',
99
+ isDroppable: false,
36
100
  isComplete: false,
37
101
  isError: false,
38
102
  isImage: false,
39
103
  isProcessing: false,
40
104
  isSuccess: false,
41
- response: null,
42
- uploadedImage: null,
105
+ response: undefined,
106
+ uploadedImage: undefined,
43
107
  };
44
108
  }
45
109
 
46
- getErrorMessage(status) {
110
+ getErrorMessage(status?: number) {
111
+ const { csFailureText, csTooLargeMessage, csWrongTypeMessage, maxSize, intl } = this.props;
47
112
  switch (status) {
48
113
  case 413:
49
114
  return (
50
- this.props.csTooLargeMessage ||
51
- this.props.intl.formatMessage(messages.csTooLargeMessage, {
52
- maxSize: this.props.maxSize / 1000000,
115
+ csTooLargeMessage ||
116
+ intl.formatMessage(messages.csTooLargeMessage, {
117
+ maxSize: maxSize / 1000000,
53
118
  })
54
119
  );
55
120
  case 415:
56
- return (
57
- this.props.csWrongTypeMessage ||
58
- this.props.intl.formatMessage(messages.csWrongTypeMessage)
59
- );
121
+ return csWrongTypeMessage || intl.formatMessage(messages.csWrongTypeMessage);
60
122
  default:
61
- return this.props.csFailureText || this.props.intl.formatMessage(messages.csFailureText);
123
+ return csFailureText || intl.formatMessage(messages.csFailureText);
62
124
  }
63
125
  }
64
126
 
65
- onDragLeave(event) {
127
+ onDragLeave(event: React.DragEvent<HTMLDivElement>) {
66
128
  event.preventDefault();
67
129
  this.dragCounter -= 1;
68
130
  if (this.dragCounter === 0) {
@@ -70,7 +132,7 @@ class Upload extends Component {
70
132
  }
71
133
  }
72
134
 
73
- onDragEnter(event) {
135
+ onDragEnter(event: React.DragEvent<HTMLDivElement>) {
74
136
  event.preventDefault();
75
137
  this.dragCounter += 1;
76
138
  const { usDisabled } = this.props;
@@ -80,90 +142,85 @@ class Upload extends Component {
80
142
  }
81
143
  }
82
144
 
83
- onDrop(event) {
145
+ async onDrop(event: React.DragEvent<HTMLDivElement>) {
84
146
  const { isProcessing } = this.state;
85
147
  event.preventDefault();
86
148
  if (!isProcessing) {
87
149
  this.reset();
88
150
  }
89
151
 
90
- if (event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files[0]) {
91
- this.fileDropped(event.dataTransfer.files[0]);
152
+ if (event.dataTransfer?.files?.[0]) {
153
+ await this.fileDropped(event.dataTransfer.files[0]);
92
154
  }
93
155
  }
94
156
 
95
- onAnimationCompleted = async (status) => {
157
+ onAnimationCompleted = async (status: ProcessIndicatorStatus) => {
96
158
  const { response, isProcessing, fileName } = this.state;
97
- // Success.
98
159
  const { animationDelay } = this.props;
99
- if (isProcessing && status === Status.SUCCEEDED) {
160
+
161
+ if (isProcessing && status === 'succeeded') {
100
162
  const { onSuccess } = this.props;
101
- this.timeouts = setTimeout(() => {
163
+ this.timeouts = window.setTimeout(() => {
102
164
  this.setState(
103
165
  {
104
166
  isProcessing: false,
105
167
  isComplete: true,
106
168
  },
107
- () => (onSuccess ? onSuccess(response, fileName) : {}),
169
+ onSuccess ? () => onSuccess(response as string | Response, fileName) : undefined,
108
170
  );
109
171
  }, animationDelay);
110
172
  }
111
- // Failure.
112
- if (isProcessing && status === Status.FAILED) {
173
+
174
+ if (isProcessing && status === 'failed') {
113
175
  const { onFailure } = this.props;
114
- this.timeouts = setTimeout(() => {
176
+ this.timeouts = window.setTimeout(() => {
115
177
  this.setState(
116
178
  {
117
179
  isProcessing: false,
118
180
  isComplete: true,
119
181
  },
120
- () => (onFailure ? onFailure(response) : {}),
182
+ onFailure ? () => onFailure(response) : undefined,
121
183
  );
122
184
  }, animationDelay);
123
185
  }
124
186
  };
125
187
 
126
- asyncPost = (file) => {
188
+ asyncPost = async (file: File) => {
127
189
  const { httpOptions, fetcher } = this.props;
128
- const { fileInputName = file.name, data = {} } = httpOptions || {};
190
+ if (httpOptions == null) {
191
+ throw new Error('Cannot find HTTP options');
192
+ }
193
+
194
+ const { fileInputName = file.name, data = {} } = httpOptions;
129
195
 
130
196
  const formData = new FormData();
131
197
  formData.append(fileInputName, file);
132
198
  Object.keys(data).forEach((key) => formData.append(key, data[key]));
133
- return postData(this.prepareHttpOptions(httpOptions), formData, fetcher);
199
+ return postData(httpOptions, formData, fetcher);
134
200
  };
135
201
 
136
- asyncResponse = (response, type) => {
202
+ asyncResponse = (response: unknown, type: 'success' | 'error') => {
137
203
  // Gives time to the animation callback to fire.
138
- this.timeouts = setTimeout(() => {
204
+ this.timeouts = window.setTimeout(() => {
139
205
  this.setState({
140
206
  response,
141
- isError: type === PROCESS_STATE[0],
142
- isSuccess: type === PROCESS_STATE[1],
207
+ isError: type === 'error',
208
+ isSuccess: type === 'success',
143
209
  });
144
210
  }, ANIMATION_FIX);
145
211
  };
146
212
 
147
- prepareHttpOptions = (httpOptions) => {
148
- if (!httpOptions.url) {
149
- throw new Error('You must supply a URL to post image data asynchronously');
150
- }
151
- return httpOptions;
152
- };
153
-
154
- handleOnClear = (event) => {
213
+ handleOnClear: React.MouseEventHandler<HTMLButtonElement> = (event) => {
155
214
  event.preventDefault();
156
215
  const { onCancel } = this.props;
157
- if (onCancel) {
158
- onCancel();
159
- }
216
+ onCancel?.();
160
217
 
161
218
  this.reset();
162
219
  };
163
220
 
164
221
  reset = () => {
165
222
  this.dragCounter = 0;
166
- clearTimeout(this.timeouts);
223
+ window.clearTimeout(this.timeouts);
167
224
  this.setState({
168
225
  isComplete: false,
169
226
  isError: false,
@@ -172,7 +229,7 @@ class Upload extends Component {
172
229
  });
173
230
  };
174
231
 
175
- showDataImage = (dataUrl) => {
232
+ showDataImage = (dataUrl: string) => {
176
233
  const { isImage } = this.state;
177
234
  if (isImage) {
178
235
  this.setState({
@@ -181,7 +238,7 @@ class Upload extends Component {
181
238
  }
182
239
  };
183
240
 
184
- fileDropped = async (file) => {
241
+ fileDropped = async (file: File) => {
185
242
  const { httpOptions, maxSize, onStart, usDisabled, usAccept } = this.props;
186
243
  const { isProcessing } = this.state;
187
244
 
@@ -199,16 +256,14 @@ class Upload extends Component {
199
256
  isProcessing: true,
200
257
  });
201
258
 
202
- if (onStart) {
203
- onStart(file);
204
- }
259
+ onStart?.(file);
205
260
 
206
261
  let file64 = null;
207
262
 
208
263
  try {
209
264
  file64 = await asyncFileRead(file);
210
265
  } catch (error) {
211
- this.asyncResponse(error, PROCESS_STATE[0]);
266
+ this.asyncResponse(error, 'error');
212
267
  }
213
268
 
214
269
  if (!file64) {
@@ -220,35 +275,43 @@ class Upload extends Component {
220
275
  });
221
276
 
222
277
  if (!isTypeValid(file, usAccept, file64)) {
223
- const response = {
224
- status: 415,
225
- statusText: 'Unsupported Media Type',
226
- };
227
- this.asyncResponse(response, PROCESS_STATE[0]);
278
+ this.asyncResponse(
279
+ new ResponseError(
280
+ new Response(null, {
281
+ status: 415,
282
+ statusText: 'Unsupported Media Type',
283
+ }),
284
+ ),
285
+ 'error',
286
+ );
228
287
  return false;
229
288
  }
230
289
 
231
290
  if (!isSizeValid(file, maxSize)) {
232
- const response = {
233
- status: 413,
234
- statusText: 'Request Entity Too Large',
235
- };
236
- this.asyncResponse(response, PROCESS_STATE[0]);
291
+ this.asyncResponse(
292
+ new ResponseError(
293
+ new Response(null, {
294
+ status: 413,
295
+ statusText: 'Request Entity Too Large',
296
+ }),
297
+ ),
298
+ 'error',
299
+ );
237
300
  return false;
238
301
  }
239
302
 
240
303
  if (httpOptions) {
241
304
  // Post the file to provided endpoint
242
- return await this.asyncPost(file)
243
- .then((response) => this.asyncResponse(response, 'success'))
244
- .then(() => {
245
- this.showDataImage(file64);
246
- return true;
247
- })
248
- .catch((error) => {
249
- this.asyncResponse(error, PROCESS_STATE[0]);
250
- return false;
251
- });
305
+ let response;
306
+ try {
307
+ response = await this.asyncPost(file);
308
+ } catch (error) {
309
+ this.asyncResponse(error, 'error');
310
+ return false;
311
+ }
312
+ this.asyncResponse(response, 'success');
313
+ this.showDataImage(file64);
314
+ return true;
252
315
  }
253
316
  // Post on form submit. And return the encoded image.
254
317
  this.showDataImage(file64);
@@ -258,6 +321,7 @@ class Upload extends Component {
258
321
 
259
322
  render() {
260
323
  const {
324
+ maxSize,
261
325
  usDropMessage,
262
326
  usAccept,
263
327
  usButtonText,
@@ -271,7 +335,6 @@ class Upload extends Component {
271
335
  csButtonText,
272
336
  csSuccessText,
273
337
  size,
274
- uploadStep,
275
338
  intl,
276
339
  } = this.props;
277
340
 
@@ -287,8 +350,6 @@ class Upload extends Component {
287
350
  uploadedImage,
288
351
  } = this.state;
289
352
 
290
- const UploadStepComponent = UPLOAD_STEP_COMPONENTS[uploadStep] || UploadImageStep;
291
-
292
353
  return (
293
354
  <div
294
355
  className={classNames('droppable-area', {
@@ -303,12 +364,12 @@ class Upload extends Component {
303
364
  })}
304
365
  onDragEnter={(event) => this.onDragEnter(event)}
305
366
  onDragLeave={(event) => this.onDragLeave(event)}
306
- onDrop={(event) => this.onDrop(event)}
367
+ onDrop={async (event) => this.onDrop(event)}
307
368
  onDragOver={(event) => event.preventDefault()}
308
369
  >
309
370
  {!isProcessing && !isComplete && (
310
- <UploadStepComponent
311
- fileDropped={(file) => this.fileDropped(file)}
371
+ <UploadImageStep
372
+ fileDropped={async (file) => this.fileDropped(file)}
312
373
  isComplete={isComplete}
313
374
  usAccept={usAccept}
314
375
  usButtonText={usButtonText || intl.formatMessage(messages.usButtonText)}
@@ -317,7 +378,7 @@ class Upload extends Component {
317
378
  usLabel={usLabel}
318
379
  usPlaceholder={
319
380
  usPlaceholder ||
320
- intl.formatMessage(messages.usPlaceholder, { maxSize: this.props.maxSize / 1000000 })
381
+ intl.formatMessage(messages.usPlaceholder, { maxSize: maxSize / 1000000 })
321
382
  }
322
383
  />
323
384
  )}
@@ -330,7 +391,7 @@ class Upload extends Component {
330
391
  psButtonText={psButtonText || intl.formatMessage(messages.psButtonText)}
331
392
  psProcessingText={psProcessingText || intl.formatMessage(messages.psProcessingText)}
332
393
  psButtonDisabled={psButtonDisabled}
333
- onAnimationCompleted={(status) => this.onAnimationCompleted(status)}
394
+ onAnimationCompleted={async (status) => this.onAnimationCompleted(status)}
334
395
  onClear={(event) => this.handleOnClear(event)}
335
396
  />
336
397
  )}
@@ -343,7 +404,14 @@ class Upload extends Component {
343
404
  isError={isError}
344
405
  isImage={isImage}
345
406
  csButtonText={csButtonText || intl.formatMessage(messages.csButtonText)}
346
- csFailureText={this.getErrorMessage(response?.status)}
407
+ csFailureText={this.getErrorMessage(
408
+ response != null &&
409
+ typeof response === 'object' &&
410
+ 'status' in response &&
411
+ typeof response.status === 'number'
412
+ ? response.status
413
+ : undefined,
414
+ )}
347
415
  csSuccessText={csSuccessText || intl.formatMessage(messages.csSuccessText)}
348
416
  uploadedImage={uploadedImage}
349
417
  onClear={(event) => this.handleOnClear(event)}
@@ -366,77 +434,4 @@ class Upload extends Component {
366
434
  }
367
435
  }
368
436
 
369
- Upload.propTypes = {
370
- animationDelay: PropTypes.number,
371
- csButtonText: PropTypes.string,
372
- csFailureText: PropTypes.string,
373
- csSuccessText: PropTypes.string,
374
- csTooLargeMessage: PropTypes.string,
375
- csWrongTypeMessage: PropTypes.string,
376
- httpOptions: PropTypes.shape({
377
- url: PropTypes.string.isRequired,
378
- method: PropTypes.oneOf(['POST', 'PUT', 'PATCH']),
379
- fileInputName: PropTypes.string,
380
- data: PropTypes.object,
381
- headers: PropTypes.object,
382
- }),
383
- /**
384
- * You can provide a fetcher function with the same interface as the global fetch function, which is used by default.
385
- * function fetcher(input: RequestInfo, init?: RequestInit): Promise<Response>
386
- */
387
- fetcher: PropTypes.func,
388
- maxSize: PropTypes.number,
389
- onCancel: PropTypes.func,
390
- onFailure: PropTypes.func,
391
- onStart: PropTypes.func,
392
- onSuccess: PropTypes.func,
393
- psButtonText: PropTypes.string,
394
- psButtonDisabled: PropTypes.bool,
395
- psProcessingText: PropTypes.string,
396
- size: PropTypes.oneOf(['sm', 'md', 'lg']),
397
- /**
398
- * You can provide multiple rules separated by comma, e.g.: "application/pdf,image/*".
399
- * Using "*" will allow every file type to be uploaded.
400
- */
401
- usAccept: PropTypes.string,
402
- usButtonText: PropTypes.string,
403
- usDisabled: PropTypes.bool,
404
- usDropMessage: PropTypes.string,
405
- usHelpImage: PropTypes.node,
406
- usLabel: PropTypes.string,
407
- usPlaceholder: PropTypes.string,
408
- uploadStep: PropTypes.oneOf(['uploadImageStep', 'mediaUploadStep']),
409
- };
410
-
411
- Upload.defaultProps = {
412
- animationDelay: 700,
413
- csButtonText: undefined,
414
- csFailureText: undefined,
415
- csSuccessText: undefined,
416
- csTooLargeMessage: undefined,
417
- csWrongTypeMessage: undefined,
418
- httpOptions: null,
419
- maxSize: MAX_SIZE_DEFAULT,
420
- onCancel: null,
421
- onFailure: null,
422
- onStart: null,
423
- onSuccess: null,
424
- psButtonText: undefined,
425
- psButtonDisabled: false,
426
- psProcessingText: undefined,
427
- size: 'md',
428
- usAccept: 'image/*',
429
- usButtonText: undefined,
430
- usDisabled: false,
431
- usDropMessage: undefined,
432
- usHelpImage: '',
433
- usLabel: '',
434
- usPlaceholder: undefined,
435
- uploadStep: UploadStep.UPLOAD_IMAGE_STEP,
436
- };
437
-
438
- // this export is necessary for react-to-typescript-definitions
439
- // to be able to properly generate TS types, this is due to us wrapping this component in `injectIntl` before exporting
440
- export { Upload };
441
-
442
437
  export default injectIntl(Upload);
@@ -0,0 +1,2 @@
1
+ export { default, UploadStep } from './Upload';
2
+ export type { UploadProps } from './Upload';
@@ -1,8 +1,9 @@
1
- import { AlertCircle as AlertCircleIcon, Document as DocumentIcon } from '@transferwise/icons';
1
+ import { Document as DocumentIcon } from '@transferwise/icons';
2
2
  import { shallow } from 'enzyme';
3
3
 
4
4
  import Body from '../../../body';
5
5
  import Button from '../../../button';
6
+ import StatusIcon from '../../../statusIcon/StatusIcon';
6
7
 
7
8
  import CompleteStep from '.';
8
9
 
@@ -44,7 +45,7 @@ describe('CompleteStep', () => {
44
45
  it('renders errorMessage and icon when error is true', () => {
45
46
  component = shallow(<CompleteStep {...COMPLETED_STEP_PROPS} isError />);
46
47
  expect(component.find('p').text()).toBe(COMPLETED_STEP_PROPS.csFailureText);
47
- expect(component.find(AlertCircleIcon)).toHaveLength(1);
48
+ expect(component.find(StatusIcon)).toHaveLength(1);
48
49
  });
49
50
  });
50
51
 
@@ -0,0 +1,74 @@
1
+ import { Document as DocumentIcon } from '@transferwise/icons';
2
+
3
+ import { Typography } from '../../..';
4
+ import Body from '../../../body';
5
+ import Button from '../../../button';
6
+ import { Sentiment, Size } from '../../../common';
7
+ import StatusIcon from '../../../statusIcon';
8
+ import Title from '../../../title';
9
+
10
+ export interface CompleteStepProps {
11
+ csButtonText: string;
12
+ csSuccessText: string;
13
+ csFailureText: string;
14
+ fileName: string;
15
+ isComplete: boolean;
16
+ isError: boolean;
17
+ isImage: boolean;
18
+ uploadedImage?: string;
19
+ onClear: React.MouseEventHandler<HTMLButtonElement>;
20
+ }
21
+
22
+ export default function CompleteStep({
23
+ csButtonText,
24
+ csFailureText,
25
+ csSuccessText,
26
+ fileName,
27
+ isComplete,
28
+ isError,
29
+ isImage,
30
+ onClear,
31
+ uploadedImage,
32
+ }: CompleteStepProps) {
33
+ return (
34
+ <div className="droppable-complete-card droppable-card" aria-hidden={!isComplete}>
35
+ <div className="droppable-card-content">
36
+ <div
37
+ className="droppable-card-content d-flex flex-column align-items-center"
38
+ aria-live="polite"
39
+ >
40
+ {isError ? (
41
+ <>
42
+ <StatusIcon size={Size.LARGE} sentiment={Sentiment.NEGATIVE} />
43
+ {csFailureText && <p className="m-t-2 m-b-0">{csFailureText}</p>}
44
+ </>
45
+ ) : (
46
+ <>
47
+ {isImage && uploadedImage ? (
48
+ <img src={uploadedImage} alt="OK" className="thumbnail " />
49
+ ) : (
50
+ <DocumentIcon />
51
+ )}
52
+
53
+ {fileName && (
54
+ <Body as="p" className="m-b-0">
55
+ {fileName}
56
+ </Body>
57
+ )}
58
+ {csSuccessText && (
59
+ <Title className="caption m-t-1" type={Typography.TITLE_BODY}>
60
+ {csSuccessText}
61
+ </Title>
62
+ )}
63
+ </>
64
+ )}
65
+ </div>
66
+ {csButtonText && (
67
+ <Button className={isError ? 'm-t-2' : 'm-t-1'} onClick={onClear}>
68
+ {csButtonText}
69
+ </Button>
70
+ )}
71
+ </div>
72
+ </div>
73
+ );
74
+ }
@@ -0,0 +1,2 @@
1
+ export { default } from './completeStep';
2
+ export type { CompleteStepProps } from './completeStep';
@@ -1,4 +1,3 @@
1
1
  export { default as UploadImageStep } from './uploadImageStep';
2
- export { default as MediaUploadStep } from './mediaUploadStep';
3
2
  export { default as ProcessingStep } from './processingStep';
4
3
  export { default as CompleteStep } from './completeStep';
@@ -0,0 +1,2 @@
1
+ export { default } from './processingStep';
2
+ export type { ProcessingStepProps } from './processingStep';