@availity/mui-file-selector 0.1.3 → 0.2.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/CHANGELOG.md +19 -0
- package/dist/index.d.mts +12 -4
- package/dist/index.d.ts +12 -4
- package/package.json +4 -3
- package/src/lib/Dropzone.test.tsx +1 -1
- package/src/lib/Dropzone.tsx +122 -109
- package/src/lib/ErrorAlert.test.tsx +11 -0
- package/src/lib/ErrorAlert.tsx +46 -0
- package/src/lib/FileList.test.tsx +42 -8
- package/src/lib/FileList.tsx +44 -23
- package/src/lib/FilePickerBtn.test.tsx +1 -1
- package/src/lib/FilePickerBtn.tsx +23 -29
- package/src/lib/FileSelector.stories.tsx +9 -17
- package/src/lib/FileSelector.tsx +162 -56
- package/src/lib/FileTypesMessage.test.tsx +6 -0
- package/src/lib/FileTypesMessage.tsx +6 -0
- package/src/lib/HeaderMessage.tsx +6 -0
- package/src/lib/UploadProgressBar.test.tsx +17 -0
- package/src/lib/UploadProgressBar.tsx +12 -4
- package/src/lib/useFileDelivery.tsx +11 -2
- package/src/lib/useUploadCore.tsx +8 -12
- package/src/lib/util.ts +1 -1
- package/src/lib/FilePickerInput.md +0 -82
- package/src/lib/FileRow.md +0 -70
|
@@ -1,14 +1,30 @@
|
|
|
1
|
-
import { ChangeEvent,
|
|
1
|
+
import { ChangeEvent, RefObject } from 'react';
|
|
2
2
|
import type { DropzoneInputProps } from 'react-dropzone';
|
|
3
3
|
import { useFormContext } from 'react-hook-form';
|
|
4
4
|
import { Button, ButtonProps } from '@availity/mui-button';
|
|
5
5
|
import { Input } from '@availity/mui-form-utils';
|
|
6
6
|
|
|
7
7
|
type FilePickerBtnProps = {
|
|
8
|
+
/**
|
|
9
|
+
* Name attribute for the input field, used by react-hook-form for form state management.
|
|
10
|
+
*/
|
|
8
11
|
name: string;
|
|
9
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Callback function triggered when files are selected through the input.
|
|
14
|
+
*/
|
|
15
|
+
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
|
16
|
+
/**
|
|
17
|
+
* Optional ID attribute for the file input element.
|
|
18
|
+
*/
|
|
10
19
|
inputId?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Additional props to customize the underlying input element.
|
|
22
|
+
*/
|
|
11
23
|
inputProps?: DropzoneInputProps & { ref?: RefObject<HTMLInputElement> };
|
|
24
|
+
/**
|
|
25
|
+
* Maximum allowed size per file in bytes. Files exceeding this size will be rejected.
|
|
26
|
+
*/
|
|
27
|
+
maxSize?: number;
|
|
12
28
|
} & Omit<ButtonProps, 'onChange'>;
|
|
13
29
|
|
|
14
30
|
export const FilePickerBtn = ({
|
|
@@ -18,35 +34,13 @@ export const FilePickerBtn = ({
|
|
|
18
34
|
inputId,
|
|
19
35
|
inputProps = {},
|
|
20
36
|
maxSize,
|
|
37
|
+
onChange,
|
|
21
38
|
onClick,
|
|
22
39
|
...rest
|
|
23
40
|
}: FilePickerBtnProps) => {
|
|
24
|
-
const { register
|
|
25
|
-
|
|
26
|
-
const { accept, multiple, ref, style, type: inputType, onChange } = inputProps;
|
|
27
|
-
|
|
28
|
-
const handleOnChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
29
|
-
const { files } = event.target;
|
|
30
|
-
|
|
31
|
-
const value: File[] = [];
|
|
32
|
-
if (files) {
|
|
33
|
-
// FileList is not iterable. Must use for loop for now
|
|
34
|
-
for (let i = 0; i < files.length; i++) {
|
|
35
|
-
if (maxSize) {
|
|
36
|
-
console.log('file is too big:', files[i].size > maxSize);
|
|
37
|
-
}
|
|
38
|
-
value[i] = files[i];
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
setValue(name, value);
|
|
43
|
-
|
|
44
|
-
// if (onChange) onChange(event);
|
|
45
|
-
};
|
|
41
|
+
const { register } = useFormContext();
|
|
46
42
|
|
|
47
|
-
const
|
|
48
|
-
if (onClick) onClick(event);
|
|
49
|
-
};
|
|
43
|
+
const { accept, multiple, ref, style, type: inputType } = inputProps;
|
|
50
44
|
|
|
51
45
|
const field = register(name);
|
|
52
46
|
|
|
@@ -54,7 +48,7 @@ export const FilePickerBtn = ({
|
|
|
54
48
|
<>
|
|
55
49
|
<Input
|
|
56
50
|
{...field}
|
|
57
|
-
onChange={
|
|
51
|
+
onChange={onChange}
|
|
58
52
|
value=""
|
|
59
53
|
inputRef={ref}
|
|
60
54
|
type={inputType}
|
|
@@ -66,7 +60,7 @@ export const FilePickerBtn = ({
|
|
|
66
60
|
}}
|
|
67
61
|
id={inputId}
|
|
68
62
|
/>
|
|
69
|
-
<Button color={color} {...rest} onClick={
|
|
63
|
+
<Button color={color} {...rest} onClick={onClick} fullWidth={false}>
|
|
70
64
|
{children}
|
|
71
65
|
</Button>
|
|
72
66
|
</>
|
|
@@ -3,16 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|
|
3
3
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
4
4
|
import { Paper } from '@availity/mui-paper';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
type FileSelectorProps = {
|
|
9
|
-
name: string;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const FileSelector = (props: FileSelectorProps) => {
|
|
13
|
-
console.log(props);
|
|
14
|
-
return <div>placeholder</div>;
|
|
15
|
-
};
|
|
6
|
+
import { FileSelector, FileSelectorProps } from './FileSelector';
|
|
16
7
|
|
|
17
8
|
const meta: Meta<typeof FileSelector> = {
|
|
18
9
|
title: 'Components/File Selector/File Selector',
|
|
@@ -47,12 +38,13 @@ export const _FileSelector: StoryObj<typeof FileSelector> = {
|
|
|
47
38
|
),
|
|
48
39
|
args: {
|
|
49
40
|
name: 'file-selector',
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
41
|
+
allowedFileTypes: [],
|
|
42
|
+
clientId: '123',
|
|
43
|
+
customerId: '456',
|
|
44
|
+
bucketId: '789',
|
|
45
|
+
retryDelays: [],
|
|
46
|
+
maxSize: 1 * 1024 * 1024, // 1MB
|
|
47
|
+
isCloud: true,
|
|
48
|
+
multiple: true,
|
|
57
49
|
},
|
|
58
50
|
};
|
package/src/lib/FileSelector.tsx
CHANGED
|
@@ -1,45 +1,130 @@
|
|
|
1
|
-
import { ReactNode, useState } from 'react';
|
|
1
|
+
import { ChangeEvent, ReactNode, useState } from 'react';
|
|
2
2
|
import { useForm, FormProvider } from 'react-hook-form';
|
|
3
3
|
import type { FileRejection } from 'react-dropzone/typings/react-dropzone';
|
|
4
|
-
import
|
|
4
|
+
import { useQueryClient } from '@tanstack/react-query';
|
|
5
|
+
import Upload, { UploadOptions } from '@availity/upload-core';
|
|
6
|
+
import { Button } from '@availity/mui-button';
|
|
7
|
+
import { Grid } from '@availity/mui-layout';
|
|
8
|
+
import { Typography } from '@availity/mui-typography';
|
|
5
9
|
|
|
6
10
|
import { Dropzone } from './Dropzone';
|
|
11
|
+
import { ErrorAlert } from './ErrorAlert';
|
|
7
12
|
import { FileList } from './FileList';
|
|
8
13
|
import { FileTypesMessage } from './FileTypesMessage';
|
|
9
|
-
import { useUploadCore } from './useUploadCore';
|
|
10
|
-
import { Typography } from '@availity/mui-typography';
|
|
11
14
|
|
|
12
15
|
const CLOUD_URL = '/cloud/web/appl/vault/upload/v1/resumable';
|
|
13
16
|
|
|
14
17
|
export type FileSelectorProps = {
|
|
18
|
+
/**
|
|
19
|
+
* Name attribute for the form field. Used by react-hook-form for form state management
|
|
20
|
+
* and must be unique within the form context
|
|
21
|
+
*/
|
|
15
22
|
name: string;
|
|
23
|
+
/**
|
|
24
|
+
* The ID of the bucket where files will be uploaded
|
|
25
|
+
*/
|
|
16
26
|
bucketId: string;
|
|
27
|
+
/**
|
|
28
|
+
* The customer ID associated with the upload
|
|
29
|
+
*/
|
|
17
30
|
customerId: string;
|
|
31
|
+
/**
|
|
32
|
+
* Regular expression pattern of allowed characters in file names
|
|
33
|
+
* @example "a-zA-Z0-9-_."
|
|
34
|
+
*/
|
|
18
35
|
allowedFileNameCharacters?: string;
|
|
36
|
+
/**
|
|
37
|
+
* List of allowed file extensions. Each extension must start with a dot
|
|
38
|
+
* @example ['.pdf', '.doc', '.docx']
|
|
39
|
+
* @default []
|
|
40
|
+
*/
|
|
19
41
|
allowedFileTypes?: `.${string}`[];
|
|
42
|
+
/**
|
|
43
|
+
* Optional content to render below the file upload area
|
|
44
|
+
*/
|
|
20
45
|
children?: ReactNode;
|
|
46
|
+
/**
|
|
47
|
+
* Client identifier used for upload authentication
|
|
48
|
+
*/
|
|
21
49
|
clientId: string;
|
|
22
|
-
|
|
23
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Whether the file selector is disabled
|
|
52
|
+
* @default false
|
|
53
|
+
*/
|
|
24
54
|
disabled?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Custom endpoint URL for file uploads. If not provided, default endpoint will be used
|
|
57
|
+
*/
|
|
25
58
|
endpoint?: string;
|
|
26
|
-
|
|
27
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Whether to use the cloud upload endpoint
|
|
61
|
+
* When true, uses '/cloud/web/appl/vault/upload/v1/resumable'
|
|
62
|
+
*/
|
|
28
63
|
isCloud?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Label text or element displayed above the upload area
|
|
66
|
+
* @default 'Upload file'
|
|
67
|
+
*/
|
|
29
68
|
label?: ReactNode;
|
|
69
|
+
/**
|
|
70
|
+
* Maximum number of files that can be uploaded simultaneously
|
|
71
|
+
*/
|
|
30
72
|
maxFiles?: number;
|
|
73
|
+
/**
|
|
74
|
+
* Maximum file size allowed per file in bytes
|
|
75
|
+
* Use Kibi or Mibibytes. eg: 1kb = 1024 bytes; 1mb = 1024kb
|
|
76
|
+
*/
|
|
31
77
|
maxSize: number;
|
|
78
|
+
/**
|
|
79
|
+
* Whether multiple file selection is allowed
|
|
80
|
+
* @default true
|
|
81
|
+
*/
|
|
32
82
|
multiple?: boolean;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Callback fired when files are selected
|
|
85
|
+
* @param event - The change event containing the selected file(s)
|
|
86
|
+
*/
|
|
87
|
+
onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
|
|
88
|
+
/**
|
|
89
|
+
* Callback fired when the form is submitted
|
|
90
|
+
* @param uploads - Array of Upload instances for the submitted files
|
|
91
|
+
* @param values - Object containing the form values, with files indexed by the name prop
|
|
92
|
+
*/
|
|
93
|
+
onSubmit?: (uploads: Upload[], values: Record<string, File[]>) => void;
|
|
94
|
+
/**
|
|
95
|
+
* Callback fired when a file is successfully uploaded
|
|
96
|
+
*/
|
|
97
|
+
onSuccess?: UploadOptions['onSuccess'];
|
|
98
|
+
/**
|
|
99
|
+
* Callback fired when an error occurs during upload
|
|
100
|
+
*/
|
|
101
|
+
onError?: UploadOptions['onError'];
|
|
102
|
+
/**
|
|
103
|
+
* Array of functions to execute before file upload begins.
|
|
104
|
+
* Each function should return a boolean indicating whether to proceed with the upload.
|
|
105
|
+
* @default []
|
|
106
|
+
*/
|
|
38
107
|
onFilePreUpload?: (() => boolean)[];
|
|
39
|
-
|
|
40
|
-
|
|
108
|
+
/**
|
|
109
|
+
* Callback fired when a file is removed from the upload list
|
|
110
|
+
* @param files - Array of remaining files
|
|
111
|
+
* @param removedUploadId - ID of the removed upload
|
|
112
|
+
*/
|
|
113
|
+
onUploadRemove?: (files: File[], removedUploadId: string) => void;
|
|
114
|
+
/**
|
|
115
|
+
* Array of delays (in milliseconds) between upload retry attempts
|
|
116
|
+
*/
|
|
117
|
+
retryDelays?: UploadOptions['retryDelays'];
|
|
41
118
|
};
|
|
42
119
|
|
|
120
|
+
// Below props were removed from availity-react version. Perserving here in case needed later
|
|
121
|
+
// deliverFileOnSubmit?: boolean;
|
|
122
|
+
// deliveryChannel?: string;
|
|
123
|
+
// fileDeliveryMetadata?: Record<string, unknown> | ((file: Upload) => Record<string, unknown>);
|
|
124
|
+
// onDeliveryError?: (error: unknown) => void;
|
|
125
|
+
// onDeliverySuccess?: () => void;
|
|
126
|
+
// onFileDelivery?: (upload: Upload) => void;
|
|
127
|
+
|
|
43
128
|
export const FileSelector = ({
|
|
44
129
|
name,
|
|
45
130
|
allowedFileNameCharacters,
|
|
@@ -48,36 +133,33 @@ export const FileSelector = ({
|
|
|
48
133
|
clientId,
|
|
49
134
|
children,
|
|
50
135
|
customerId,
|
|
51
|
-
deliverFileOnSubmit = false,
|
|
52
|
-
deliveryChannel,
|
|
53
136
|
disabled = false,
|
|
54
137
|
endpoint,
|
|
55
|
-
fileDeliveryMetadata,
|
|
56
|
-
getDropRejectionMessages,
|
|
57
138
|
isCloud,
|
|
58
139
|
label = 'Upload file',
|
|
59
|
-
maxFiles
|
|
140
|
+
maxFiles,
|
|
60
141
|
maxSize,
|
|
61
142
|
multiple = true,
|
|
62
|
-
|
|
63
|
-
// onDeliverySuccess,
|
|
143
|
+
onChange,
|
|
64
144
|
onSubmit,
|
|
65
145
|
onSuccess,
|
|
66
146
|
onError,
|
|
67
147
|
onFilePreUpload = [],
|
|
68
148
|
onUploadRemove,
|
|
69
|
-
|
|
149
|
+
retryDelays,
|
|
70
150
|
}: FileSelectorProps) => {
|
|
71
|
-
// const classes = classNames(
|
|
72
|
-
// className,
|
|
73
|
-
// metadata.touched ? 'is-touched' : 'is-untouched',
|
|
74
|
-
// metadata.touched && metadata.error && 'is-invalid'
|
|
75
|
-
// );
|
|
76
151
|
const [totalSize, setTotalSize] = useState(0);
|
|
152
|
+
const [fileRejections, setFileRejections] = useState<(FileRejection & { id: number })[]>([]);
|
|
153
|
+
|
|
154
|
+
const client = useQueryClient();
|
|
77
155
|
|
|
78
|
-
const methods = useForm(
|
|
156
|
+
const methods = useForm({
|
|
157
|
+
defaultValues: {
|
|
158
|
+
[name]: [] as File[],
|
|
159
|
+
},
|
|
160
|
+
});
|
|
79
161
|
|
|
80
|
-
const options:
|
|
162
|
+
const options: UploadOptions = {
|
|
81
163
|
bucketId,
|
|
82
164
|
customerId,
|
|
83
165
|
clientId,
|
|
@@ -86,62 +168,86 @@ export const FileSelector = ({
|
|
|
86
168
|
allowedFileNameCharacters,
|
|
87
169
|
onError,
|
|
88
170
|
onSuccess,
|
|
171
|
+
retryDelays,
|
|
89
172
|
};
|
|
90
173
|
|
|
91
174
|
if (onFilePreUpload) options.onPreStart = onFilePreUpload;
|
|
92
175
|
if (endpoint) options.endpoint = endpoint;
|
|
93
176
|
if (isCloud) options.endpoint = CLOUD_URL;
|
|
94
177
|
|
|
95
|
-
const
|
|
178
|
+
const handleOnRemoveFile = (uploadId: string, upload: Upload) => {
|
|
179
|
+
const prevFiles = methods.watch(name);
|
|
180
|
+
const newFiles = prevFiles.filter((file) => file.name !== upload.file.name);
|
|
96
181
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (newFiles.length !== uploads.length) {
|
|
101
|
-
const removedFile = uploads.find((upload) => upload.id === uploadId);
|
|
182
|
+
if (newFiles.length !== prevFiles.length) {
|
|
183
|
+
const removedFile = prevFiles.find((file) => file.name === upload.file.name);
|
|
102
184
|
|
|
103
185
|
methods.setValue(name, newFiles);
|
|
104
186
|
|
|
105
|
-
if (
|
|
106
|
-
|
|
187
|
+
if (removedFile?.size) setTotalSize(totalSize - removedFile.size);
|
|
188
|
+
|
|
107
189
|
if (onUploadRemove) onUploadRemove(newFiles, uploadId);
|
|
108
190
|
}
|
|
109
191
|
};
|
|
110
192
|
|
|
111
|
-
const
|
|
112
|
-
|
|
193
|
+
const files = methods.watch(name);
|
|
194
|
+
|
|
195
|
+
const handleOnSubmit = (values: Record<string, File[]>) => {
|
|
196
|
+
if (values[name].length === 0) return;
|
|
197
|
+
|
|
198
|
+
const queries = client.getQueriesData<Upload>(['upload']);
|
|
199
|
+
const uploads = [];
|
|
200
|
+
for (const [, data] of queries) {
|
|
201
|
+
if (data) uploads.push(data);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (onSubmit) onSubmit(uploads, values);
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const handleRemoveRejection = (id: number) => {
|
|
208
|
+
const rejections = fileRejections.filter((value) => value.id !== id);
|
|
209
|
+
setFileRejections(rejections);
|
|
113
210
|
};
|
|
114
211
|
|
|
115
212
|
return (
|
|
116
213
|
<FormProvider {...methods}>
|
|
117
|
-
<form onSubmit={methods.handleSubmit(
|
|
214
|
+
<form onSubmit={methods.handleSubmit(handleOnSubmit)}>
|
|
118
215
|
<>
|
|
119
216
|
<Typography>{label}</Typography>
|
|
120
217
|
<Dropzone
|
|
121
218
|
name={name}
|
|
122
|
-
allowedFileNameCharacters={allowedFileNameCharacters}
|
|
123
219
|
allowedFileTypes={allowedFileTypes}
|
|
124
|
-
bucketId={bucketId}
|
|
125
|
-
clientId={clientId}
|
|
126
|
-
customerId={customerId}
|
|
127
|
-
deliverFileOnSubmit={deliverFileOnSubmit}
|
|
128
|
-
deliveryChannel={deliveryChannel}
|
|
129
220
|
disabled={disabled}
|
|
130
|
-
|
|
131
|
-
fileDeliveryMetadata={fileDeliveryMetadata}
|
|
132
|
-
getDropRejectionMessages={getDropRejectionMessages}
|
|
133
|
-
isCloud={isCloud}
|
|
221
|
+
maxFiles={maxFiles}
|
|
134
222
|
maxSize={maxSize}
|
|
135
223
|
multiple={multiple}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
onFileDelivery={onFileDelivery}
|
|
224
|
+
onChange={onChange}
|
|
225
|
+
setFileRejections={setFileRejections}
|
|
226
|
+
setTotalSize={setTotalSize}
|
|
140
227
|
/>
|
|
141
228
|
<FileTypesMessage allowedFileTypes={allowedFileTypes} maxFileSize={maxSize} />
|
|
142
229
|
</>
|
|
143
230
|
{children}
|
|
144
|
-
|
|
231
|
+
|
|
232
|
+
{fileRejections.length > 0
|
|
233
|
+
? fileRejections.map((rejection) => (
|
|
234
|
+
<ErrorAlert
|
|
235
|
+
key={rejection.id}
|
|
236
|
+
errors={rejection.errors}
|
|
237
|
+
fileName={rejection.file.name}
|
|
238
|
+
id={rejection.id}
|
|
239
|
+
onClose={() => handleRemoveRejection(rejection.id)}
|
|
240
|
+
/>
|
|
241
|
+
))
|
|
242
|
+
: null}
|
|
243
|
+
<FileList files={files} options={options} onRemoveFile={handleOnRemoveFile} />
|
|
244
|
+
{files.length > 0 && (
|
|
245
|
+
<Grid xs={12} justifyContent="end" display="flex" paddingTop={2.5}>
|
|
246
|
+
<Button type="submit" sx={{ marginLeft: 'auto', marginRight: 0 }}>
|
|
247
|
+
Submit
|
|
248
|
+
</Button>
|
|
249
|
+
</Grid>
|
|
250
|
+
)}
|
|
145
251
|
</form>
|
|
146
252
|
</FormProvider>
|
|
147
253
|
);
|
|
@@ -8,4 +8,10 @@ describe('FileTypesMessage', () => {
|
|
|
8
8
|
|
|
9
9
|
expect(screen.getByText(/All file types allowed/)).toBeTruthy();
|
|
10
10
|
});
|
|
11
|
+
|
|
12
|
+
test('should show file size', () => {
|
|
13
|
+
render(<FileTypesMessage allowedFileTypes={[]} maxFileSize={1000} />);
|
|
14
|
+
|
|
15
|
+
expect(screen.getByText(/Maximum file size is/)).toBeTruthy();
|
|
16
|
+
});
|
|
11
17
|
});
|
|
@@ -3,7 +3,13 @@ import { Typography } from '@availity/mui-typography';
|
|
|
3
3
|
import { formatBytes } from './util';
|
|
4
4
|
|
|
5
5
|
type FileTypesMessageProps = {
|
|
6
|
+
/**
|
|
7
|
+
* Allowed file type extensions. Each extension should be prefixed with a ".". eg: .txt, .pdf, .png
|
|
8
|
+
*/
|
|
6
9
|
allowedFileTypes: `.${string}`[];
|
|
10
|
+
/**
|
|
11
|
+
* Maximum size per file in bytes. This will be formatted. eg: 1024 * 20 = 20 KB
|
|
12
|
+
*/
|
|
7
13
|
maxFileSize: number;
|
|
8
14
|
};
|
|
9
15
|
|
|
@@ -3,7 +3,13 @@ import { Typography } from '@availity/mui-typography';
|
|
|
3
3
|
import { formatBytes } from './util';
|
|
4
4
|
|
|
5
5
|
export type HeaderMessageProps = {
|
|
6
|
+
/**
|
|
7
|
+
* Maximum number of files allowed
|
|
8
|
+
*/
|
|
6
9
|
maxFiles: number;
|
|
10
|
+
/**
|
|
11
|
+
* Maximum combined total size of all files
|
|
12
|
+
*/
|
|
7
13
|
maxSize: number;
|
|
8
14
|
};
|
|
9
15
|
|
|
@@ -20,4 +20,21 @@ describe('UploadProgressBar', () => {
|
|
|
20
20
|
|
|
21
21
|
expect(screen.getByText('50%')).toBeTruthy();
|
|
22
22
|
});
|
|
23
|
+
|
|
24
|
+
test('should show error message', () => {
|
|
25
|
+
const mockUpload: unknown = {
|
|
26
|
+
onProgress: [],
|
|
27
|
+
onError: [],
|
|
28
|
+
onSuccess: [],
|
|
29
|
+
errorMessage: 'error message',
|
|
30
|
+
file: {
|
|
31
|
+
name: 'test',
|
|
32
|
+
},
|
|
33
|
+
percentage: 0,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
render(<UploadProgressBar upload={mockUpload as Upload} />);
|
|
37
|
+
|
|
38
|
+
expect(screen.getByText('error message')).toBeTruthy();
|
|
39
|
+
});
|
|
23
40
|
});
|
|
@@ -6,13 +6,21 @@ import { Typography } from '@availity/mui-typography';
|
|
|
6
6
|
import { WarningTriangleIcon } from '@availity/mui-icon';
|
|
7
7
|
|
|
8
8
|
export type UploadProgressBarProps = {
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* The upload instance returned by creating a new Upload via @availity/upload-core.
|
|
11
|
+
*/
|
|
10
12
|
upload: Upload;
|
|
11
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Callback function to hook into the onProgress within the Upload instance provided in the upload prop.
|
|
15
|
+
*/
|
|
12
16
|
onProgress?: (upload: Upload) => void;
|
|
13
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Callback function to hook into the onSuccess within the Upload instance provided in the upload prop.
|
|
19
|
+
*/
|
|
14
20
|
onSuccess?: (upload: Upload) => void;
|
|
15
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Callback function to hook into the onError within the Upload instance provided in the upload prop.
|
|
23
|
+
*/
|
|
16
24
|
onError?: (upload: Upload) => void;
|
|
17
25
|
};
|
|
18
26
|
|
|
@@ -4,14 +4,23 @@ import Upload from '@availity/upload-core';
|
|
|
4
4
|
import { AxiosResponse } from 'axios';
|
|
5
5
|
|
|
6
6
|
export type UploadDeliveryOptions = {
|
|
7
|
+
/** ID of the vault bucket */
|
|
7
8
|
bucketId: string;
|
|
9
|
+
/** Client ID to be attached to the request */
|
|
8
10
|
clientId: string;
|
|
11
|
+
/** Customer ID of the organization submitting the request */
|
|
9
12
|
customerId: string;
|
|
13
|
+
/** Delivery Channel for the AvFileDeliveryApi */
|
|
10
14
|
deliveryChannel?: string;
|
|
15
|
+
/** Determine whether AvFileDeliveryApi should be automatically called or if the component should wait */
|
|
11
16
|
deliverFileOnSubmit?: boolean;
|
|
17
|
+
/** Metadata to be sent with the request. Can be an object or function that returns an object */
|
|
12
18
|
fileDeliveryMetadata?: Record<string, unknown> | ((upload: Upload) => Record<string, unknown>);
|
|
19
|
+
/** Callback function for when the upload succeeds */
|
|
13
20
|
onSuccess?: (responses: unknown[]) => void;
|
|
21
|
+
/** Callback function for when the upload fails */
|
|
14
22
|
onError?: (responses: unknown[]) => void;
|
|
23
|
+
/** The upload instance returned by creating a new Upload via @availity/upload-core. */
|
|
15
24
|
uploads: Upload[];
|
|
16
25
|
};
|
|
17
26
|
|
|
@@ -22,8 +31,8 @@ export function useFileDelivery({
|
|
|
22
31
|
deliveryChannel,
|
|
23
32
|
deliverFileOnSubmit,
|
|
24
33
|
fileDeliveryMetadata,
|
|
25
|
-
onSuccess,
|
|
26
|
-
onError,
|
|
34
|
+
// onSuccess,
|
|
35
|
+
// onError,
|
|
27
36
|
uploads,
|
|
28
37
|
}: UploadDeliveryOptions) {
|
|
29
38
|
const errors = {};
|
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
import { useQuery } from '@tanstack/react-query';
|
|
2
|
-
import Upload, {
|
|
2
|
+
import Upload, { UploadOptions } from '@availity/upload-core';
|
|
3
3
|
|
|
4
|
-
function
|
|
5
|
-
|
|
6
|
-
const upload = new Upload(file, options);
|
|
4
|
+
function startUpload(file: File, options: UploadOptions) {
|
|
5
|
+
const upload = new Upload(file, options);
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
upload.start();
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
});
|
|
9
|
+
return upload;
|
|
12
10
|
}
|
|
13
11
|
|
|
14
|
-
export function useUploadCore(
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const isQueryEnabled = files.length > 0;
|
|
12
|
+
export function useUploadCore(file: File, options: UploadOptions) {
|
|
13
|
+
const isQueryEnabled = !!file;
|
|
18
14
|
|
|
19
|
-
return useQuery(['upload',
|
|
15
|
+
return useQuery(['upload', file.name, options], () => startUpload(file, options), {
|
|
20
16
|
enabled: isQueryEnabled,
|
|
21
17
|
retry: false,
|
|
22
18
|
});
|