@transferwise/components 46.39.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 +176 -419
- package/build/index.js.map +1 -1
- package/build/index.mjs +176 -419
- package/build/index.mjs.map +1 -1
- package/build/types/index.d.ts +2 -1
- package/build/types/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 +3 -3
- package/src/index.ts +2 -1
- package/src/processIndicator/ProcessIndicator.tsx +1 -1
- 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.tsx +1 -1
- 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/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/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,53 @@
|
|
|
1
|
+
import Button from '../../../button';
|
|
2
|
+
import { Status, Typography } from '../../../common';
|
|
3
|
+
import ProcessIndicator, { ProcessIndicatorStatus } from '../../../processIndicator';
|
|
4
|
+
import Title from '../../../title';
|
|
5
|
+
|
|
6
|
+
export interface ProcessingStepProps {
|
|
7
|
+
isComplete: boolean;
|
|
8
|
+
isError: boolean;
|
|
9
|
+
isSuccess: boolean;
|
|
10
|
+
onAnimationCompleted: (status: ProcessIndicatorStatus) => void;
|
|
11
|
+
onClear: React.MouseEventHandler<HTMLButtonElement>;
|
|
12
|
+
psButtonText: string;
|
|
13
|
+
psProcessingText: string;
|
|
14
|
+
psButtonDisabled: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default function ProcessingStep({
|
|
18
|
+
isComplete,
|
|
19
|
+
isError,
|
|
20
|
+
isSuccess,
|
|
21
|
+
onAnimationCompleted,
|
|
22
|
+
onClear,
|
|
23
|
+
psButtonText,
|
|
24
|
+
psProcessingText,
|
|
25
|
+
psButtonDisabled,
|
|
26
|
+
}: ProcessingStepProps) {
|
|
27
|
+
let processStatus = Status.PROCESSING;
|
|
28
|
+
if (isError) {
|
|
29
|
+
processStatus = Status.FAILED;
|
|
30
|
+
}
|
|
31
|
+
if (isSuccess) {
|
|
32
|
+
processStatus = Status.SUCCEEDED;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div className="droppable-processing-card droppable-card" aria-hidden={isComplete}>
|
|
37
|
+
<div className="droppable-card-content">
|
|
38
|
+
<ProcessIndicator
|
|
39
|
+
status={processStatus}
|
|
40
|
+
onAnimationCompleted={(status) => onAnimationCompleted(status)}
|
|
41
|
+
/>
|
|
42
|
+
<Title className="m-y-2" type={Typography.TITLE_BODY} aria-live="polite">
|
|
43
|
+
{psProcessingText}
|
|
44
|
+
</Title>
|
|
45
|
+
{psButtonText && (
|
|
46
|
+
<Button disabled={psButtonDisabled} onClick={onClear}>
|
|
47
|
+
{psButtonText}
|
|
48
|
+
</Button>
|
|
49
|
+
)}
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import { Upload as UploadIcon } from '@transferwise/icons';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
2
|
import { createRef, PureComponent } from 'react';
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
export interface UploadImageStepProps {
|
|
5
|
+
fileDropped: (file: File) => void;
|
|
6
|
+
isComplete: boolean;
|
|
7
|
+
usAccept: string;
|
|
8
|
+
usButtonText: string;
|
|
9
|
+
usDisabled: boolean;
|
|
10
|
+
usHelpImage: React.ReactNode;
|
|
11
|
+
usLabel: string;
|
|
12
|
+
usPlaceholder: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default class UploadImageStep extends PureComponent<UploadImageStepProps> {
|
|
16
|
+
uploadInputRef = createRef<HTMLInputElement>();
|
|
10
17
|
|
|
11
18
|
onManualUpload = () => {
|
|
12
19
|
const { fileDropped } = this.props;
|
|
13
|
-
|
|
14
|
-
|
|
20
|
+
const file = this.uploadInputRef.current?.files?.[0];
|
|
21
|
+
if (file != null) {
|
|
15
22
|
fileDropped(file);
|
|
16
23
|
}
|
|
17
24
|
};
|
|
@@ -43,7 +50,7 @@ class UploadImageStep extends PureComponent {
|
|
|
43
50
|
<div className="droppable-card-content">
|
|
44
51
|
<div className="m-b-3">{this.getImage()}</div>
|
|
45
52
|
{usLabel && <h4 className="np-text-title-body m-b-1">{usLabel}</h4>}
|
|
46
|
-
{usPlaceholder && <p className="np-text-body-large m-b-3">{
|
|
53
|
+
{usPlaceholder && <p className="np-text-body-large m-b-3">{String(usPlaceholder)}</p>}
|
|
47
54
|
<label className={`btn btn-primary btn-md ${usDisabled ? 'disabled' : ''}`}>
|
|
48
55
|
{usButtonText ? (
|
|
49
56
|
<span>{usButtonText}</span>
|
|
@@ -53,7 +60,7 @@ class UploadImageStep extends PureComponent {
|
|
|
53
60
|
<input
|
|
54
61
|
ref={this.uploadInputRef}
|
|
55
62
|
type="file"
|
|
56
|
-
accept={usAccept === '*' ?
|
|
63
|
+
accept={usAccept === '*' ? undefined : usAccept}
|
|
57
64
|
className="tw-droppable-input hidden"
|
|
58
65
|
disabled={usDisabled}
|
|
59
66
|
name="file-upload"
|
|
@@ -66,16 +73,3 @@ class UploadImageStep extends PureComponent {
|
|
|
66
73
|
);
|
|
67
74
|
}
|
|
68
75
|
}
|
|
69
|
-
|
|
70
|
-
UploadImageStep.propTypes = {
|
|
71
|
-
fileDropped: PropTypes.func.isRequired,
|
|
72
|
-
isComplete: PropTypes.bool.isRequired,
|
|
73
|
-
usAccept: PropTypes.string.isRequired,
|
|
74
|
-
usButtonText: PropTypes.string.isRequired,
|
|
75
|
-
usDisabled: PropTypes.bool.isRequired,
|
|
76
|
-
usHelpImage: PropTypes.node.isRequired,
|
|
77
|
-
usLabel: PropTypes.string.isRequired,
|
|
78
|
-
usPlaceholder: PropTypes.string.isRequired,
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
export default UploadImageStep;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { asyncFileRead } from '.';
|
|
2
|
+
|
|
3
|
+
describe('asyncFileRead', () => {
|
|
4
|
+
it('should resolve with data64', async () => {
|
|
5
|
+
const file = new Blob(['foo'], { type: 'text/plain' });
|
|
6
|
+
await expect(asyncFileRead(file)).resolves.toBe('data:text/plain;base64,Zm9v');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('should reject if wrong file is given', async () => {
|
|
10
|
+
const file = 'Not a blob';
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
12
|
+
await expect(asyncFileRead(file as any)).rejects.toThrow();
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export async function asyncFileRead(file: Blob) {
|
|
2
|
+
return new Promise<string>((resolve, reject) => {
|
|
3
|
+
const reader = new FileReader();
|
|
4
|
+
reader.addEventListener('load', () => {
|
|
5
|
+
resolve(reader.result as string);
|
|
6
|
+
});
|
|
7
|
+
reader.addEventListener('error', () => {
|
|
8
|
+
reject(reader.error ?? new Error('Cannot read file'));
|
|
9
|
+
});
|
|
10
|
+
reader.readAsDataURL(file);
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { getFileType } from '.';
|
|
2
|
+
|
|
3
|
+
const imageFile = new Blob(undefined, { type: 'image/png' });
|
|
4
|
+
|
|
5
|
+
const data64Img = 'data:image/png;something';
|
|
6
|
+
|
|
7
|
+
describe('getFileType', () => {
|
|
8
|
+
describe('returns empty string', () => {
|
|
9
|
+
it(`when file has empty type and file64 hasn't been provided`, () => {
|
|
10
|
+
expect(getFileType(new Blob())).toBe('');
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe('returns file-type', () => {
|
|
15
|
+
it('when file has type defined', () => {
|
|
16
|
+
expect(getFileType(imageFile)).toBe(imageFile.type);
|
|
17
|
+
});
|
|
18
|
+
it('when file has no type and file64 is supported', () => {
|
|
19
|
+
expect(getFileType(new Blob(), data64Img)).toBe('image/png');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function getFileType(file: Blob, file64?: string) {
|
|
2
|
+
if (file.type) {
|
|
3
|
+
return file.type;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
if (file64) {
|
|
7
|
+
const regex = /^data:([a-z]+\/[a-z]+);/;
|
|
8
|
+
const typeFromEncoded = regex.exec(file64);
|
|
9
|
+
|
|
10
|
+
if (typeFromEncoded?.[1]) {
|
|
11
|
+
return typeFromEncoded[1];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return '';
|
|
16
|
+
}
|
|
@@ -3,5 +3,3 @@ export { asyncFileRead } from './asyncFileRead';
|
|
|
3
3
|
export { isSizeValid } from './isSizeValid';
|
|
4
4
|
export { isTypeValid } from './isTypeValid';
|
|
5
5
|
export { getFileType } from './getFileType';
|
|
6
|
-
export { getSupportedSpotMimeTypes } from './getSupportedSpotMimeTypes';
|
|
7
|
-
export { requestMedia } from './requestMedia';
|
|
@@ -4,14 +4,14 @@ const MAXSIZE = 5000;
|
|
|
4
4
|
|
|
5
5
|
describe('isSizeValid', () => {
|
|
6
6
|
it('should return true for valid size', () => {
|
|
7
|
-
expect(isSizeValid(
|
|
7
|
+
expect(isSizeValid(new Blob([new ArrayBuffer(MAXSIZE)]), MAXSIZE)).toBe(true);
|
|
8
8
|
});
|
|
9
9
|
|
|
10
10
|
it('should return false for valid size', () => {
|
|
11
|
-
expect(isSizeValid(
|
|
11
|
+
expect(isSizeValid(new Blob([new ArrayBuffer(MAXSIZE + 1)]), MAXSIZE)).toBe(false);
|
|
12
12
|
});
|
|
13
13
|
|
|
14
14
|
it('should return false for invalid MaxSize', () => {
|
|
15
|
-
expect(isSizeValid(
|
|
15
|
+
expect(isSizeValid(new Blob([new ArrayBuffer(MAXSIZE)]), MAXSIZE - 0.5)).toBe(false);
|
|
16
16
|
});
|
|
17
17
|
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { isTypeValid } from '.';
|
|
2
|
+
|
|
3
|
+
const pdfFile = new Blob(undefined, {
|
|
4
|
+
type: 'application/pdf',
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
const pngFile = new Blob(undefined, {
|
|
8
|
+
type: 'image/png',
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const data64Img = 'data:image/png;something';
|
|
12
|
+
|
|
13
|
+
describe('isTypeValid', () => {
|
|
14
|
+
describe('when type is provided', () => {
|
|
15
|
+
it.each([pdfFile, pngFile])('returns true for wildcard rule %s', (file) => {
|
|
16
|
+
expect(isTypeValid(file, '*')).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('returns true for matching type', () => {
|
|
20
|
+
expect(isTypeValid(pdfFile, 'application/*')).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('returns true for matching type when multiple rules provided', () => {
|
|
24
|
+
expect(isTypeValid(pdfFile, 'application/*, image/*')).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('returns true for matching subtype', () => {
|
|
28
|
+
expect(isTypeValid(pdfFile, 'application/pdf')).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('returns true for matching subtype when multiple rules provided', () => {
|
|
32
|
+
expect(isTypeValid(pngFile, 'application/xls, image/png')).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('can parse multiple types with extra whitespace', () => {
|
|
36
|
+
expect(isTypeValid(pngFile, ' application/xls, image/png ')).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('returns false for unsupported type', () => {
|
|
40
|
+
expect(isTypeValid(pngFile, 'application/*')).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('returns false for unsupported subtype', () => {
|
|
44
|
+
expect(isTypeValid(pdfFile, 'application/xls')).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('returns false for unsupported subtype when multiple rules provided', () => {
|
|
48
|
+
expect(isTypeValid(pngFile, 'application/xls, image/jpeg')).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe('when type is not provided', () => {
|
|
52
|
+
it('returns true for supported file', () => {
|
|
53
|
+
expect(isTypeValid(new Blob(), 'image/png', data64Img)).toBe(true);
|
|
54
|
+
expect(isTypeValid(new Blob(), 'image/*', data64Img)).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('returns false for unsupported file', () => {
|
|
58
|
+
expect(isTypeValid(new Blob(), 'image/jpeg', data64Img)).toBe(false);
|
|
59
|
+
expect(isTypeValid(new Blob(), 'application/*', data64Img)).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { getFileType } from '../getFileType';
|
|
2
|
+
|
|
3
|
+
export function isTypeValid(file: Blob, rule: string, file64?: string) {
|
|
4
|
+
if (!rule) {
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const allowedTypes = rule.replace(/\s/g, '').split(',');
|
|
9
|
+
const fileType = getFileType(file, file64);
|
|
10
|
+
|
|
11
|
+
if (rule === '*' || allowedTypes.includes(fileType)) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return allowedTypes.some((type) => {
|
|
16
|
+
const [typeAllowed, extensionAllowed] = type.split('/');
|
|
17
|
+
return extensionAllowed === '*' && fileType.includes(typeAllowed);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { postData } from '.';
|
|
2
|
+
import { ResponseError } from './postData';
|
|
3
|
+
|
|
4
|
+
const HTTPOPTIONS = { url: 'a-url' };
|
|
5
|
+
const DATA = 'some-data';
|
|
6
|
+
|
|
7
|
+
describe('postData', () => {
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
(global.fetch as jest.Mock).mockClear();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should work with resolve', async () => {
|
|
13
|
+
const RESOLVE_RESPONSE = new Response();
|
|
14
|
+
jest.spyOn(global, 'fetch').mockImplementation(async () => RESOLVE_RESPONSE);
|
|
15
|
+
|
|
16
|
+
await expect(postData(HTTPOPTIONS, DATA)).resolves.toBe(RESOLVE_RESPONSE);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should throw when call fails', async () => {
|
|
20
|
+
const REJECT_RESPONSE = new ResponseError(
|
|
21
|
+
new Response(null, { status: 500, statusText: 'Rejected' }),
|
|
22
|
+
);
|
|
23
|
+
jest.spyOn(global, 'fetch').mockImplementation(async () => {
|
|
24
|
+
throw REJECT_RESPONSE;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
await expect(postData(HTTPOPTIONS, DATA)).rejects.toThrow(REJECT_RESPONSE);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should throw an Error when API returns an error code', async () => {
|
|
31
|
+
const ERROR_RESPONSE = new Response(null, { status: 500, statusText: 'Internal server error' });
|
|
32
|
+
jest.spyOn(global, 'fetch').mockImplementation(async () => ERROR_RESPONSE);
|
|
33
|
+
|
|
34
|
+
await expect(postData(HTTPOPTIONS, DATA)).rejects.toThrow();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should pass additional form data to request body', async () => {
|
|
38
|
+
const mockFetch = jest.fn(async () => new Response());
|
|
39
|
+
jest.spyOn(global, 'fetch').mockImplementation(mockFetch);
|
|
40
|
+
|
|
41
|
+
const data = new FormData();
|
|
42
|
+
data.append('file', 'file');
|
|
43
|
+
data.append('profileId', '1');
|
|
44
|
+
await postData(HTTPOPTIONS, data);
|
|
45
|
+
|
|
46
|
+
expect(mockFetch).toHaveBeenCalledWith('a-url', { method: 'POST', body: data });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should override `Content-type` and add any custom headers to the request', async () => {
|
|
50
|
+
const mockFetch = jest.fn(async () => new Response());
|
|
51
|
+
jest.spyOn(global, 'fetch').mockImplementation(mockFetch);
|
|
52
|
+
|
|
53
|
+
const headers: HeadersInit = {
|
|
54
|
+
'Content-type': 'foo',
|
|
55
|
+
'Accept-language': 'hu',
|
|
56
|
+
};
|
|
57
|
+
await postData({ ...HTTPOPTIONS, headers }, DATA);
|
|
58
|
+
|
|
59
|
+
expect(mockFetch).toHaveBeenCalledWith('a-url', {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers,
|
|
62
|
+
body: DATA,
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export class ResponseError extends Error {
|
|
2
|
+
response: Response;
|
|
3
|
+
status: number;
|
|
4
|
+
|
|
5
|
+
constructor(response: Response, message?: string) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'ResponseError';
|
|
8
|
+
this.response = response;
|
|
9
|
+
this.status = response.status;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface PostDataHTTPOptions extends Omit<RequestInit, 'body'> {
|
|
14
|
+
url: string;
|
|
15
|
+
method?: 'POST' | 'PUT' | 'PATCH';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type PostDataFetcher = (input: string, init: RequestInit) => Promise<Response>;
|
|
19
|
+
|
|
20
|
+
export async function postData(
|
|
21
|
+
{ url, method = 'POST', ...httpOptions }: PostDataHTTPOptions,
|
|
22
|
+
data: RequestInit['body'],
|
|
23
|
+
fetcher: PostDataFetcher = fetch,
|
|
24
|
+
) {
|
|
25
|
+
const response = await fetcher(url, {
|
|
26
|
+
method,
|
|
27
|
+
body: data,
|
|
28
|
+
...httpOptions,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
throw new ResponseError(response, response.statusText);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return response;
|
|
36
|
+
}
|
|
@@ -236,7 +236,7 @@ const UploadInput = ({
|
|
|
236
236
|
const { name } = file;
|
|
237
237
|
const id = generateFileId(file);
|
|
238
238
|
|
|
239
|
-
const allowedFileTypes =
|
|
239
|
+
const allowedFileTypes = typeof fileTypes === 'string' ? fileTypes : fileTypes.join(',');
|
|
240
240
|
|
|
241
241
|
// Check if file type is valid
|
|
242
242
|
if (!isTypeValid(file, allowedFileTypes)) {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/upload/steps/mediaUploadStep/index.js"],"names":[],"mappings":""}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export default MediaUploadStep;
|
|
2
|
-
declare function MediaUploadStep({ isComplete, usAccept, usButtonText, usDisabled, usHelpImage, usLabel, usPlaceholder, fileDropped, }: {
|
|
3
|
-
isComplete: any;
|
|
4
|
-
usAccept: any;
|
|
5
|
-
usButtonText: any;
|
|
6
|
-
usDisabled: any;
|
|
7
|
-
usHelpImage: any;
|
|
8
|
-
usLabel: any;
|
|
9
|
-
usPlaceholder: any;
|
|
10
|
-
fileDropped: any;
|
|
11
|
-
}): import("react").JSX.Element;
|
|
12
|
-
declare namespace MediaUploadStep {
|
|
13
|
-
namespace propTypes {
|
|
14
|
-
let fileDropped: any;
|
|
15
|
-
let isComplete: any;
|
|
16
|
-
let usAccept: any;
|
|
17
|
-
let usButtonText: any;
|
|
18
|
-
let usDisabled: any;
|
|
19
|
-
let usHelpImage: any;
|
|
20
|
-
let usLabel: any;
|
|
21
|
-
let usPlaceholder: any;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
//# sourceMappingURL=mediaUploadStep.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mediaUploadStep.d.ts","sourceRoot":"","sources":["../../../../../src/upload/steps/mediaUploadStep/mediaUploadStep.js"],"names":[],"mappings":";AASA;;;;;;;;;gCAyDC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"uploadSteps.d.ts","sourceRoot":"","sources":["../../../src/upload/uploadSteps.ts"],"names":[],"mappings":"AACA,oBAAY,UAAU;IACpB,iBAAiB,oBAAoB;IACrC,iBAAiB,oBAAoB;CACtC"}
|
package/build/types/upload/utils/getSupportedSpotMimeTypes/getSupportedSpotMimeTypes.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"getSupportedSpotMimeTypes.d.ts","sourceRoot":"","sources":["../../../../../src/upload/utils/getSupportedSpotMimeTypes/getSupportedSpotMimeTypes.js"],"names":[],"mappings":"AAGO,+DAcN"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/upload/utils/getSupportedSpotMimeTypes/index.js"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/upload/utils/requestMedia/index.js"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"requestMedia.d.ts","sourceRoot":"","sources":["../../../../../src/upload/utils/requestMedia/requestMedia.js"],"names":[],"mappings":"AAAO,8DAcH"}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { action } from '@storybook/addon-actions';
|
|
2
|
-
import { boolean, number, select, text } from '@storybook/addon-knobs';
|
|
3
|
-
|
|
4
|
-
import Upload from '.';
|
|
5
|
-
|
|
6
|
-
export default {
|
|
7
|
-
component: Upload,
|
|
8
|
-
title: 'Forms/Upload',
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export const Basic = () => {
|
|
12
|
-
const size = select('size', ['sm', 'md', 'lg'], 'md');
|
|
13
|
-
const maxSize = number('max file size', 4000000);
|
|
14
|
-
const usAccept = text('accepted format', '*');
|
|
15
|
-
const usDisabled = boolean('disabled', false);
|
|
16
|
-
const psButtonDisabled = boolean('psButtonDisabled', false);
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<Upload
|
|
20
|
-
maxSize={maxSize}
|
|
21
|
-
size={size}
|
|
22
|
-
usAccept={usAccept}
|
|
23
|
-
usLabel="Front of your ID document"
|
|
24
|
-
usDisabled={usDisabled}
|
|
25
|
-
psButtonDisabled={psButtonDisabled}
|
|
26
|
-
httpOptions={{
|
|
27
|
-
url: 'https://httpbin.org/post',
|
|
28
|
-
method: 'POST',
|
|
29
|
-
}}
|
|
30
|
-
onStart={(file) => action('onStart', file)}
|
|
31
|
-
onSuccess={(httpResponse, fileName) => action('onSuccess', httpResponse, fileName)}
|
|
32
|
-
onFailure={(httpResponse) => action('onFailure', httpResponse)}
|
|
33
|
-
onCancel={() => action('onCancel')}
|
|
34
|
-
/>
|
|
35
|
-
);
|
|
36
|
-
};
|
package/src/upload/index.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { AlertCircle as AlertCircleIcon, Document as DocumentIcon } from '@transferwise/icons';
|
|
2
|
-
import { useTheme } from '@wise/components-theming';
|
|
3
|
-
import classNames from 'classnames';
|
|
4
|
-
import PropTypes from 'prop-types';
|
|
5
|
-
|
|
6
|
-
import { Priority, Typography } from '../../..';
|
|
7
|
-
import Body from '../../../body';
|
|
8
|
-
import Button from '../../../button';
|
|
9
|
-
import { Sentiment, Size } from '../../../common';
|
|
10
|
-
import StatusIcon from '../../../statusIcon';
|
|
11
|
-
import Title from '../../../title';
|
|
12
|
-
|
|
13
|
-
const CompleteStep = (props) => {
|
|
14
|
-
const { isModern } = useTheme();
|
|
15
|
-
const {
|
|
16
|
-
csButtonText,
|
|
17
|
-
csFailureText,
|
|
18
|
-
csSuccessText,
|
|
19
|
-
fileName,
|
|
20
|
-
isComplete,
|
|
21
|
-
isError,
|
|
22
|
-
isImage,
|
|
23
|
-
onClear,
|
|
24
|
-
uploadedImage,
|
|
25
|
-
} = props;
|
|
26
|
-
return (
|
|
27
|
-
<div className="droppable-complete-card droppable-card" aria-hidden={!isComplete}>
|
|
28
|
-
<div className="droppable-card-content">
|
|
29
|
-
<div
|
|
30
|
-
className="droppable-card-content d-flex flex-column align-items-center"
|
|
31
|
-
aria-live="polite"
|
|
32
|
-
>
|
|
33
|
-
{isError ? (
|
|
34
|
-
<>
|
|
35
|
-
{isModern ? (
|
|
36
|
-
<StatusIcon size={Size.LARGE} sentiment={Sentiment.NEGATIVE} />
|
|
37
|
-
) : (
|
|
38
|
-
<AlertCircleIcon size={24} className="text-negative" />
|
|
39
|
-
)}
|
|
40
|
-
{csFailureText && (
|
|
41
|
-
<p className={classNames('m-t-2', { 'm-b-0': isModern })}>{csFailureText}</p>
|
|
42
|
-
)}
|
|
43
|
-
</>
|
|
44
|
-
) : (
|
|
45
|
-
<>
|
|
46
|
-
{isImage && uploadedImage ? (
|
|
47
|
-
<img src={uploadedImage} alt="OK" className="thumbnail " />
|
|
48
|
-
) : (
|
|
49
|
-
<DocumentIcon />
|
|
50
|
-
)}
|
|
51
|
-
|
|
52
|
-
{fileName && (
|
|
53
|
-
<Body as="p" className="m-b-0">
|
|
54
|
-
{fileName}
|
|
55
|
-
</Body>
|
|
56
|
-
)}
|
|
57
|
-
{csSuccessText && (
|
|
58
|
-
<Title className="caption m-t-1" type={Typography.TITLE_BODY}>
|
|
59
|
-
{csSuccessText}
|
|
60
|
-
</Title>
|
|
61
|
-
)}
|
|
62
|
-
</>
|
|
63
|
-
)}
|
|
64
|
-
</div>
|
|
65
|
-
{csButtonText && (
|
|
66
|
-
<Button
|
|
67
|
-
priority={isModern ? Priority.PRIMARY : Priority.SECONDARY}
|
|
68
|
-
className={classNames({
|
|
69
|
-
'm-t-1': isModern && !isError,
|
|
70
|
-
'm-t-2': isModern && isError,
|
|
71
|
-
'm-t-3': !isModern,
|
|
72
|
-
})}
|
|
73
|
-
onClick={(event) => onClear(event)}
|
|
74
|
-
>
|
|
75
|
-
{csButtonText}
|
|
76
|
-
</Button>
|
|
77
|
-
)}
|
|
78
|
-
</div>
|
|
79
|
-
</div>
|
|
80
|
-
);
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
CompleteStep.propTypes = {
|
|
84
|
-
csButtonText: PropTypes.string.isRequired,
|
|
85
|
-
csSuccessText: PropTypes.string.isRequired,
|
|
86
|
-
csFailureText: PropTypes.string.isRequired,
|
|
87
|
-
fileName: PropTypes.string.isRequired,
|
|
88
|
-
isComplete: PropTypes.bool.isRequired,
|
|
89
|
-
isError: PropTypes.bool.isRequired,
|
|
90
|
-
isImage: PropTypes.bool.isRequired,
|
|
91
|
-
onClear: PropTypes.func.isRequired,
|
|
92
|
-
uploadedImage: PropTypes.string,
|
|
93
|
-
};
|
|
94
|
-
CompleteStep.defaultProps = {
|
|
95
|
-
uploadedImage: null,
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
export default CompleteStep;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from './completeStep';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from './mediaUploadStep';
|