@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.
- package/build/index.js +198 -461
- package/build/index.js.map +1 -1
- package/build/index.mjs +198 -461
- package/build/index.mjs.map +1 -1
- package/build/types/flowNavigation/backButton/BackButton.d.ts +5 -17
- package/build/types/flowNavigation/backButton/BackButton.d.ts.map +1 -1
- package/build/types/flowNavigation/backButton/index.d.ts +2 -2
- package/build/types/flowNavigation/backButton/index.d.ts.map +1 -1
- package/build/types/index.d.ts +3 -1
- package/build/types/index.d.ts.map +1 -1
- package/build/types/info/Info.d.ts +2 -2
- package/build/types/info/Info.d.ts.map +1 -1
- package/build/types/info/index.d.ts +1 -1
- package/build/types/info/index.d.ts.map +1 -1
- package/build/types/overlayHeader/OverlayHeader.d.ts +9 -18
- package/build/types/overlayHeader/OverlayHeader.d.ts.map +1 -1
- package/build/types/overlayHeader/index.d.ts +2 -1
- package/build/types/overlayHeader/index.d.ts.map +1 -1
- package/build/types/processIndicator/ProcessIndicator.d.ts +1 -1
- package/build/types/processIndicator/ProcessIndicator.d.ts.map +1 -1
- package/build/types/upload/Upload.d.ts +91 -55
- package/build/types/upload/Upload.d.ts.map +1 -1
- package/build/types/upload/Upload.messages.d.ts +42 -60
- package/build/types/upload/Upload.messages.d.ts.map +1 -1
- package/build/types/upload/index.d.ts +2 -2
- package/build/types/upload/index.d.ts.map +1 -1
- package/build/types/upload/steps/completeStep/completeStep.d.ts +11 -18
- package/build/types/upload/steps/completeStep/completeStep.d.ts.map +1 -1
- package/build/types/upload/steps/completeStep/index.d.ts +2 -1
- package/build/types/upload/steps/completeStep/index.d.ts.map +1 -1
- package/build/types/upload/steps/index.d.ts +3 -4
- package/build/types/upload/steps/index.d.ts.map +1 -1
- package/build/types/upload/steps/processingStep/index.d.ts +2 -1
- package/build/types/upload/steps/processingStep/index.d.ts.map +1 -1
- package/build/types/upload/steps/processingStep/processingStep.d.ts +11 -13
- package/build/types/upload/steps/processingStep/processingStep.d.ts.map +1 -1
- package/build/types/upload/steps/uploadImageStep/index.d.ts +2 -1
- package/build/types/upload/steps/uploadImageStep/index.d.ts.map +1 -1
- package/build/types/upload/steps/uploadImageStep/uploadImageStep.d.ts +14 -18
- package/build/types/upload/steps/uploadImageStep/uploadImageStep.d.ts.map +1 -1
- package/build/types/upload/utils/asyncFileRead/asyncFileRead.d.ts +1 -1
- package/build/types/upload/utils/asyncFileRead/asyncFileRead.d.ts.map +1 -1
- package/build/types/upload/utils/asyncFileRead/index.d.ts +1 -1
- package/build/types/upload/utils/asyncFileRead/index.d.ts.map +1 -1
- package/build/types/upload/utils/getFileType/getFileType.d.ts +1 -1
- package/build/types/upload/utils/getFileType/getFileType.d.ts.map +1 -1
- package/build/types/upload/utils/getFileType/index.d.ts +1 -1
- package/build/types/upload/utils/getFileType/index.d.ts.map +1 -1
- package/build/types/upload/utils/index.d.ts +5 -7
- package/build/types/upload/utils/index.d.ts.map +1 -1
- package/build/types/upload/utils/isSizeValid/index.d.ts +1 -1
- package/build/types/upload/utils/isSizeValid/index.d.ts.map +1 -1
- package/build/types/upload/utils/isSizeValid/isSizeValid.d.ts +1 -1
- package/build/types/upload/utils/isSizeValid/isSizeValid.d.ts.map +1 -1
- package/build/types/upload/utils/isTypeValid/index.d.ts +1 -1
- package/build/types/upload/utils/isTypeValid/index.d.ts.map +1 -1
- package/build/types/upload/utils/isTypeValid/isTypeValid.d.ts +1 -1
- package/build/types/upload/utils/isTypeValid/isTypeValid.d.ts.map +1 -1
- package/build/types/upload/utils/postData/index.d.ts +1 -1
- package/build/types/upload/utils/postData/index.d.ts.map +1 -1
- package/build/types/upload/utils/postData/postData.d.ts +11 -1
- package/build/types/upload/utils/postData/postData.d.ts.map +1 -1
- package/package.json +24 -26
- package/src/accordion/Accordion.spec.js +5 -5
- package/src/accordion/AccordionItem/AccordionItem.spec.js +2 -2
- package/src/actionButton/ActionButton.spec.tsx +4 -5
- package/src/alert/Alert.spec.tsx +4 -4
- package/src/alert/Alert.story.tsx +6 -5
- package/src/button/Button.spec.js +4 -5
- package/src/card/Card.spec.tsx +4 -4
- package/src/carousel/Carousel.spec.tsx +17 -17
- package/src/checkbox/Checkbox.spec.tsx +0 -2
- package/src/checkboxButton/CheckboxButton.spec.tsx +0 -2
- package/src/checkboxOption/CheckboxOption.spec.tsx +0 -2
- package/src/chevron/Chevron.spec.tsx +0 -1
- package/src/chips/Chips.spec.tsx +0 -1
- package/src/chips/Chips.story.tsx +5 -3
- package/src/circularButton/CircularButton.spec.tsx +4 -5
- package/src/common/RadioButton/RadioButton.spec.tsx +2 -2
- package/src/common/card/Card.story.tsx +1 -0
- package/src/common/closeButton/CloseButton.spec.tsx +0 -1
- package/src/common/flowHeader/FlowHeader.spec.tsx +0 -1
- package/src/dateInput/DateInput.story.tsx +21 -16
- package/src/dateLookup/DateLookup.rtl.spec.tsx +18 -16
- package/src/dateLookup/DateLookup.testingLibrary.spec.js +47 -44
- package/src/dateLookup/DateLookup.tests.story.tsx +4 -2
- package/src/decision/Decision.spec.js +0 -2
- package/src/dimmer/Dimmer.rtl.spec.js +10 -10
- package/src/drawer/Drawer.rtl.spec.tsx +2 -2
- package/src/emphasis/Emphasis.spec.tsx +0 -1
- package/src/field/Field.spec.tsx +2 -2
- package/src/flowNavigation/FlowNavigation.spec.js +0 -2
- package/src/flowNavigation/animatedLabel/AnimatedLabel.spec.js +0 -1
- package/src/flowNavigation/backButton/BackButton.tsx +29 -0
- package/src/flowNavigation/backButton/index.ts +2 -0
- package/src/header/Header.spec.tsx +6 -6
- package/src/image/Image.spec.tsx +0 -1
- package/src/index.ts +3 -1
- package/src/info/Info.story.tsx +15 -9
- package/src/info/Info.tsx +2 -2
- package/src/info/index.ts +1 -1
- package/src/inlineAlert/InlineAlert.spec.tsx +0 -1
- package/src/inputs/SelectInput.spec.tsx +26 -47
- package/src/link/Link.spec.tsx +0 -1
- package/src/listItem/ListItem.spec.tsx +0 -1
- package/src/moneyInput/MoneyInput.rtl.spec.tsx +2 -2
- package/src/moneyInput/MoneyInput.story.tsx +1 -4
- package/src/overlayHeader/{OverlayHeader.spec.js → OverlayHeader.spec.tsx} +1 -1
- package/src/overlayHeader/{OverlayHeader.story.js → OverlayHeader.story.tsx} +10 -2
- package/src/overlayHeader/{OverlayHeader.js → OverlayHeader.tsx} +12 -19
- package/src/overlayHeader/index.ts +2 -0
- package/src/phoneNumberInput/PhoneNumberInput.story.tsx +1 -0
- package/src/popover/Popover.spec.tsx +10 -10
- package/src/processIndicator/ProcessIndicator.tsx +1 -1
- package/src/progress/Progress.spec.tsx +0 -1
- package/src/progressBar/ProgressBar.spec.tsx +0 -1
- package/src/segmentedControl/SegmentedControl.spec.tsx +10 -11
- package/src/select/Select.spec.js +71 -71
- package/src/test-utils/index.js +1 -1
- package/src/test-utils/jest.setup.js +2 -0
- package/src/tooltip/Tooltip.spec.tsx +15 -16
- package/src/upload/Upload.spec.js +3 -14
- package/src/upload/Upload.story.tsx +37 -0
- package/src/upload/{Upload.js → Upload.tsx} +164 -169
- package/src/upload/index.ts +2 -0
- package/src/upload/steps/completeStep/completeStep.spec.js +3 -2
- package/src/upload/steps/completeStep/completeStep.tsx +74 -0
- package/src/upload/steps/completeStep/index.ts +2 -0
- package/src/upload/steps/{index.js → index.ts} +0 -1
- package/src/upload/steps/processingStep/index.ts +2 -0
- package/src/upload/steps/processingStep/processingStep.tsx +53 -0
- package/src/upload/steps/uploadImageStep/index.ts +2 -0
- package/src/upload/steps/uploadImageStep/{uploadImageStep.js → uploadImageStep.tsx} +17 -23
- package/src/upload/utils/asyncFileRead/asyncFileRead.spec.ts +14 -0
- package/src/upload/utils/asyncFileRead/asyncFileRead.ts +12 -0
- package/src/upload/utils/getFileType/getFileType.spec.ts +22 -0
- package/src/upload/utils/getFileType/getFileType.ts +16 -0
- package/src/upload/utils/{index.js → index.ts} +0 -2
- package/src/upload/utils/isSizeValid/{isSizeValid.spec.js → isSizeValid.spec.ts} +3 -3
- package/src/upload/utils/isSizeValid/isSizeValid.ts +3 -0
- package/src/upload/utils/isTypeValid/isTypeValid.spec.ts +62 -0
- package/src/upload/utils/isTypeValid/isTypeValid.ts +19 -0
- package/src/upload/utils/postData/postData.spec.ts +65 -0
- package/src/upload/utils/postData/postData.ts +36 -0
- package/src/uploadInput/UploadInput.spec.tsx +9 -10
- package/src/uploadInput/UploadInput.story.tsx +8 -180
- package/src/uploadInput/UploadInput.tests.story.tsx +212 -0
- package/src/uploadInput/UploadInput.tsx +1 -1
- package/src/uploadInput/uploadButton/UploadButton.spec.tsx +4 -4
- package/src/uploadInput/uploadItem/UploadItem.spec.tsx +4 -4
- package/build/types/upload/steps/mediaUploadStep/index.d.ts +0 -2
- package/build/types/upload/steps/mediaUploadStep/index.d.ts.map +0 -1
- package/build/types/upload/steps/mediaUploadStep/mediaUploadStep.d.ts +0 -24
- package/build/types/upload/steps/mediaUploadStep/mediaUploadStep.d.ts.map +0 -1
- package/build/types/upload/uploadSteps.d.ts +0 -5
- package/build/types/upload/uploadSteps.d.ts.map +0 -1
- package/build/types/upload/utils/getSupportedSpotMimeTypes/getSupportedSpotMimeTypes.d.ts +0 -2
- package/build/types/upload/utils/getSupportedSpotMimeTypes/getSupportedSpotMimeTypes.d.ts.map +0 -1
- package/build/types/upload/utils/getSupportedSpotMimeTypes/index.d.ts +0 -2
- package/build/types/upload/utils/getSupportedSpotMimeTypes/index.d.ts.map +0 -1
- package/build/types/upload/utils/requestMedia/index.d.ts +0 -2
- package/build/types/upload/utils/requestMedia/index.d.ts.map +0 -1
- package/build/types/upload/utils/requestMedia/requestMedia.d.ts +0 -2
- package/build/types/upload/utils/requestMedia/requestMedia.d.ts.map +0 -1
- package/src/flowNavigation/backButton/BackButton.js +0 -32
- package/src/flowNavigation/backButton/BackButton.spec.js +0 -16
- package/src/flowNavigation/backButton/__snapshots__/BackButton.spec.js.snap +0 -37
- package/src/flowNavigation/backButton/index.js +0 -3
- package/src/overlayHeader/index.js +0 -1
- package/src/upload/Upload.story.js +0 -36
- package/src/upload/index.js +0 -2
- package/src/upload/steps/completeStep/completeStep.js +0 -98
- package/src/upload/steps/completeStep/index.js +0 -1
- package/src/upload/steps/mediaUploadStep/index.js +0 -1
- package/src/upload/steps/mediaUploadStep/mediaUploadStep.js +0 -80
- package/src/upload/steps/mediaUploadStep/mediaUploadStep.spec.js +0 -77
- package/src/upload/steps/processingStep/index.js +0 -1
- package/src/upload/steps/processingStep/processingStep.js +0 -73
- package/src/upload/steps/uploadImageStep/index.js +0 -1
- package/src/upload/uploadSteps.ts +0 -5
- package/src/upload/utils/asyncFileRead/asyncFileRead.js +0 -11
- package/src/upload/utils/asyncFileRead/asyncFileRead.spec.js +0 -17
- package/src/upload/utils/getFileType/getFileType.js +0 -19
- package/src/upload/utils/getFileType/getFileType.spec.js +0 -33
- package/src/upload/utils/getSupportedSpotMimeTypes/getSupportedSpotMimeTypes.js +0 -18
- package/src/upload/utils/getSupportedSpotMimeTypes/getSupportedSpotMimeTypes.spec.js +0 -22
- package/src/upload/utils/getSupportedSpotMimeTypes/index.js +0 -1
- package/src/upload/utils/isSizeValid/isSizeValid.js +0 -1
- package/src/upload/utils/isTypeValid/isTypeValid.js +0 -26
- package/src/upload/utils/isTypeValid/isTypeValid.spec.js +0 -68
- package/src/upload/utils/postData/postData.js +0 -18
- package/src/upload/utils/postData/postData.spec.js +0 -109
- package/src/upload/utils/requestMedia/index.js +0 -1
- package/src/upload/utils/requestMedia/requestMedia.js +0 -26
- package/src/upload/utils/requestMedia/requestMedia.spec.js +0 -44
- /package/src/overlayHeader/__snapshots__/{OverlayHeader.spec.js.snap → OverlayHeader.spec.tsx.snap} +0 -0
- /package/src/upload/{Upload.messages.js → Upload.messages.ts} +0 -0
- /package/src/upload/utils/asyncFileRead/{index.js → index.ts} +0 -0
- /package/src/upload/utils/getFileType/{index.js → index.ts} +0 -0
- /package/src/upload/utils/isSizeValid/{index.js → index.ts} +0 -0
- /package/src/upload/utils/isTypeValid/{index.js → index.ts} +0 -0
- /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 {
|
|
6
|
+
import { Size, Typography } from '../common';
|
|
8
7
|
import Title from '../title';
|
|
9
8
|
|
|
10
9
|
import messages from './Upload.messages';
|
|
11
|
-
import { UploadImageStep,
|
|
12
|
-
import { UploadStep } from './uploadSteps';
|
|
10
|
+
import { UploadImageStep, ProcessingStep, CompleteStep } from './steps';
|
|
13
11
|
import { postData, asyncFileRead, isSizeValid, isTypeValid, getFileType } from './utils';
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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:
|
|
42
|
-
uploadedImage:
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
maxSize:
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
() => (
|
|
169
|
+
onSuccess ? () => onSuccess(response as string | Response, fileName) : undefined,
|
|
108
170
|
);
|
|
109
171
|
}, animationDelay);
|
|
110
172
|
}
|
|
111
|
-
|
|
112
|
-
if (isProcessing && status ===
|
|
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
|
-
() =>
|
|
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
|
-
|
|
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(
|
|
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 ===
|
|
142
|
-
isSuccess: type ===
|
|
207
|
+
isError: type === 'error',
|
|
208
|
+
isSuccess: type === 'success',
|
|
143
209
|
});
|
|
144
210
|
}, ANIMATION_FIX);
|
|
145
211
|
};
|
|
146
212
|
|
|
147
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
-
<
|
|
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:
|
|
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(
|
|
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);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
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(
|
|
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
|
+
}
|