@evoke-platform/ui-components 1.6.1-dev.1 → 1.7.0-testing.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/dist/published/components/custom/Form/FormComponents/DocumentComponent/Document.js +15 -11
- package/dist/published/components/custom/Form/FormComponents/DocumentComponent/DocumentComponent.d.ts +1 -1
- package/dist/published/components/custom/Form/FormComponents/DocumentComponent/DocumentComponent.js +6 -9
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js +14 -9
- package/dist/published/components/custom/util.d.ts +0 -1
- package/dist/published/components/custom/util.js +0 -28
- package/package.json +2 -4
|
@@ -5,7 +5,6 @@ import { useDropzone } from 'react-dropzone';
|
|
|
5
5
|
import { UploadCloud } from '../../../../../icons';
|
|
6
6
|
import { Skeleton, Snackbar, Typography } from '../../../../core';
|
|
7
7
|
import { Box, Grid } from '../../../../layout';
|
|
8
|
-
import { createAcceptObject } from '../../../util';
|
|
9
8
|
import { getPrefixedUrl } from '../../utils';
|
|
10
9
|
import { DocumentList } from './DocumentList';
|
|
11
10
|
export const Document = (props) => {
|
|
@@ -16,10 +15,10 @@ export const Document = (props) => {
|
|
|
16
15
|
let allowedTypesMessage = '';
|
|
17
16
|
if (validate?.allowedFileExtensions?.length) {
|
|
18
17
|
if (validate.allowedFileExtensions.length === 1) {
|
|
19
|
-
allowedTypesMessage = validate.allowedFileExtensions[0]
|
|
18
|
+
allowedTypesMessage = `Only ${validate.allowedFileExtensions[0]} files are allowed`;
|
|
20
19
|
}
|
|
21
20
|
else {
|
|
22
|
-
allowedTypesMessage =
|
|
21
|
+
allowedTypesMessage = `Allowed file types are: ${validate.allowedFileExtensions.slice(0, -1).join(', ')} or ${validate.allowedFileExtensions.slice(-1)[0]}`;
|
|
23
22
|
}
|
|
24
23
|
}
|
|
25
24
|
const maxSizeInBytes = Number.isFinite(validate?.maxSizeInKB)
|
|
@@ -47,25 +46,28 @@ export const Document = (props) => {
|
|
|
47
46
|
}
|
|
48
47
|
};
|
|
49
48
|
const handleUpload = async (files) => {
|
|
50
|
-
const newDocuments = [...(documents ?? []), ...
|
|
49
|
+
const newDocuments = [...(documents ?? []), ...files];
|
|
51
50
|
setDocuments(newDocuments);
|
|
52
51
|
handleChange(property.id, newDocuments);
|
|
53
52
|
};
|
|
54
53
|
const uploadDisabled = !!validate?.maxDocuments && (documents?.length ?? 0) >= validate.maxDocuments;
|
|
55
54
|
const { getRootProps, getInputProps, open, fileRejections } = useDropzone({
|
|
56
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
57
55
|
onDrop: (files) => handleUpload(files),
|
|
58
56
|
disabled: uploadDisabled,
|
|
59
|
-
accept: validate?.allowedFileExtensions
|
|
57
|
+
accept: validate?.allowedFileExtensions
|
|
58
|
+
? {
|
|
59
|
+
'type/custom': validate.allowedFileExtensions.map((extension) => extension.startsWith('.') ? extension : `.${extension}`),
|
|
60
|
+
}
|
|
61
|
+
: undefined,
|
|
60
62
|
maxSize: maxSizeInBytes,
|
|
61
63
|
});
|
|
62
64
|
useEffect(() => {
|
|
63
65
|
const errors = [];
|
|
64
66
|
if (fileRejections.some((fileRejection) => fileRejection.errors.some((error) => error.code === 'file-invalid-type'))) {
|
|
65
|
-
errors.push(`Invalid file extension.
|
|
67
|
+
errors.push(`Invalid file extension. ${allowedTypesMessage}`);
|
|
66
68
|
}
|
|
67
69
|
if (fileRejections.some((fileRejection) => fileRejection.errors.some((error) => error.code === 'file-too-large'))) {
|
|
68
|
-
errors.push(`File size exceeds the
|
|
70
|
+
errors.push(`File size exceeds the maximum limit of ${formattedMaxSize}`);
|
|
69
71
|
}
|
|
70
72
|
onFileRejections(errors);
|
|
71
73
|
}, [fileRejections, onFileRejections]);
|
|
@@ -96,14 +98,16 @@ export const Document = (props) => {
|
|
|
96
98
|
color: '#637381',
|
|
97
99
|
textAlign: 'center',
|
|
98
100
|
fontSize: '12px',
|
|
99
|
-
} },
|
|
101
|
+
} },
|
|
102
|
+
allowedTypesMessage,
|
|
103
|
+
".")),
|
|
100
104
|
formattedMaxSize && (React.createElement(Typography, { sx: {
|
|
101
105
|
color: '#637381',
|
|
102
106
|
textAlign: 'center',
|
|
103
107
|
fontSize: '12px',
|
|
104
108
|
} }, validate?.maxDocuments === 1
|
|
105
|
-
? `
|
|
106
|
-
: `The
|
|
109
|
+
? `Maximum size is ${formattedMaxSize}.`
|
|
110
|
+
: `The maximum size of each document is ${formattedMaxSize}.`)))))),
|
|
107
111
|
canUpdateProperty && isNil(hasUpdatePermission) && (React.createElement(Skeleton, { variant: "rectangular", height: formattedMaxSize || allowedTypesMessage ? '136px' : '115px', sx: { margin: '5px 0', borderRadius: '8px' } })),
|
|
108
112
|
React.createElement(DocumentList, { property: property, instance: instance, objectId: objectId, handleChange: handleChange, value: value, apiServices: apiServices, setSnackbarError: (type, message) => setSnackbarError({ message, type }), canUpdateProperty: canUpdateProperty && !!hasUpdatePermission }),
|
|
109
113
|
React.createElement(Snackbar, { open: !!snackbarError?.message, handleClose: () => setSnackbarError(null), message: snackbarError?.message, error: snackbarError?.type === 'error' })));
|
|
@@ -5,6 +5,7 @@ export declare class DocumentComponent extends ReactComponent {
|
|
|
5
5
|
[x: string]: any;
|
|
6
6
|
static schema: any;
|
|
7
7
|
errorDetails: any;
|
|
8
|
+
fileRejectionsErrors: string[];
|
|
8
9
|
componentRoot?: Root;
|
|
9
10
|
constructor(component: any, options: any, data: any);
|
|
10
11
|
init(): void;
|
|
@@ -16,7 +17,6 @@ export declare class DocumentComponent extends ReactComponent {
|
|
|
16
17
|
*/
|
|
17
18
|
manageFormErrors(): void;
|
|
18
19
|
handleChange: (key: string, value?: (File | SavedDocumentReference)[] | null) => void;
|
|
19
|
-
handleFileRejections(fileRejectionsErrors: string[]): void;
|
|
20
20
|
handleValidation(value?: (File | SavedDocumentReference)[] | null): void;
|
|
21
21
|
beforeSubmit(): Promise<void>;
|
|
22
22
|
attachReact(element: Element): void;
|
package/dist/published/components/custom/Form/FormComponents/DocumentComponent/DocumentComponent.js
CHANGED
|
@@ -13,6 +13,9 @@ export class DocumentComponent extends ReactComponent {
|
|
|
13
13
|
canUpdateProperty: !component.readOnly,
|
|
14
14
|
hideLabel: true,
|
|
15
15
|
}, options, data);
|
|
16
|
+
// File rejection errors should be displayed, but shouldn't prevent
|
|
17
|
+
// submission
|
|
18
|
+
this.fileRejectionsErrors = [];
|
|
16
19
|
this.handleChange = (key, value) => {
|
|
17
20
|
delete this.errorDetails['api-error'];
|
|
18
21
|
this.setValue(value);
|
|
@@ -26,7 +29,6 @@ export class DocumentComponent extends ReactComponent {
|
|
|
26
29
|
};
|
|
27
30
|
this.errorDetails = {};
|
|
28
31
|
this.handleChange = this.handleChange.bind(this);
|
|
29
|
-
this.handleFileRejections = this.handleFileRejections.bind(this);
|
|
30
32
|
}
|
|
31
33
|
init() {
|
|
32
34
|
this.on(`api-error`, (details) => {
|
|
@@ -61,7 +63,8 @@ export class DocumentComponent extends ReactComponent {
|
|
|
61
63
|
return !isEmpty(this.errorDetails);
|
|
62
64
|
}
|
|
63
65
|
errorMessages() {
|
|
64
|
-
|
|
66
|
+
const errors = [...Object.values(this.errorDetails), ...this.fileRejectionsErrors];
|
|
67
|
+
return errors.join('; ');
|
|
65
68
|
}
|
|
66
69
|
/**
|
|
67
70
|
* Synchronizes out-of-the-box form.io errors with this field's errorDetails object
|
|
@@ -88,12 +91,6 @@ export class DocumentComponent extends ReactComponent {
|
|
|
88
91
|
}
|
|
89
92
|
});
|
|
90
93
|
}
|
|
91
|
-
handleFileRejections(fileRejectionsErrors) {
|
|
92
|
-
delete this.errorDetails['file-rejections'];
|
|
93
|
-
if (fileRejectionsErrors.length > 0) {
|
|
94
|
-
this.errorDetails['file-rejections'] = `${fileRejectionsErrors.join('; ')}.`;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
94
|
handleValidation(value) {
|
|
98
95
|
const validate = this.component.validate;
|
|
99
96
|
const amountOfDocuments = value?.length ?? 0;
|
|
@@ -138,6 +135,6 @@ export class DocumentComponent extends ReactComponent {
|
|
|
138
135
|
const inputId = `${this.component.id}-input`;
|
|
139
136
|
return ReactDOM.render(React.createElement("div", null,
|
|
140
137
|
React.createElement(FormComponentWrapper, { ...this.component, inputId: inputId, viewOnly: !this.component.canUpdateProperty, errorMessage: this.errorMessages() },
|
|
141
|
-
React.createElement(Document, { ...this.component, id: inputId, handleChange: this.handleChange, error: this.hasErrors(), value: this.dataValue, onFileRejections: this.
|
|
138
|
+
React.createElement(Document, { ...this.component, id: inputId, handleChange: this.handleChange, error: this.hasErrors(), value: this.dataValue, onFileRejections: (errors) => (this.fileRejectionsErrors = errors) }))), root);
|
|
142
139
|
}
|
|
143
140
|
}
|
package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js
CHANGED
|
@@ -7,7 +7,6 @@ import { InfoRounded, UploadCloud } from '../../../../../../icons';
|
|
|
7
7
|
import { useFormContext } from '../../../../../../theme/hooks';
|
|
8
8
|
import { Skeleton, Snackbar, Typography } from '../../../../../core';
|
|
9
9
|
import { Box, Grid } from '../../../../../layout';
|
|
10
|
-
import { createAcceptObject } from '../../../../util';
|
|
11
10
|
import { getPrefixedUrl } from '../../utils';
|
|
12
11
|
import { DocumentList } from './DocumentList';
|
|
13
12
|
export const Document = (props) => {
|
|
@@ -20,10 +19,10 @@ export const Document = (props) => {
|
|
|
20
19
|
let allowedTypesMessage = '';
|
|
21
20
|
if (validate?.allowedFileExtensions?.length) {
|
|
22
21
|
if (validate.allowedFileExtensions.length === 1) {
|
|
23
|
-
allowedTypesMessage = validate.allowedFileExtensions[0]
|
|
22
|
+
allowedTypesMessage = `Only ${validate.allowedFileExtensions[0]} files are allowed`;
|
|
24
23
|
}
|
|
25
24
|
else {
|
|
26
|
-
allowedTypesMessage =
|
|
25
|
+
allowedTypesMessage = `Allowed file types are: ${validate.allowedFileExtensions.slice(0, -1).join(', ')} or ${validate.allowedFileExtensions.slice(-1)[0]}`;
|
|
27
26
|
}
|
|
28
27
|
}
|
|
29
28
|
const maxSizeInBytes = Number.isFinite(validate?.maxSizeInKB)
|
|
@@ -59,15 +58,19 @@ export const Document = (props) => {
|
|
|
59
58
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
60
59
|
onDrop: (files) => handleUpload(files),
|
|
61
60
|
disabled: uploadDisabled,
|
|
62
|
-
accept: validate?.allowedFileExtensions
|
|
61
|
+
accept: validate?.allowedFileExtensions
|
|
62
|
+
? {
|
|
63
|
+
'type/custom': validate.allowedFileExtensions.map((extension) => extension.startsWith('.') ? extension : `.${extension}`),
|
|
64
|
+
}
|
|
65
|
+
: undefined,
|
|
63
66
|
maxSize: maxSizeInBytes,
|
|
64
67
|
});
|
|
65
68
|
const errors = [];
|
|
66
69
|
if (fileRejections.some((fileRejection) => fileRejection.errors.some((error) => error.code === 'file-invalid-type'))) {
|
|
67
|
-
errors.push(`Invalid file extension.
|
|
70
|
+
errors.push(`Invalid file extension. ${allowedTypesMessage}`);
|
|
68
71
|
}
|
|
69
72
|
if (fileRejections.some((fileRejection) => fileRejection.errors.some((error) => error.code === 'file-too-large'))) {
|
|
70
|
-
errors.push(`File size exceeds the
|
|
73
|
+
errors.push(`File size exceeds the maximum limit of ${formattedMaxSize}`);
|
|
71
74
|
}
|
|
72
75
|
return (React.createElement(React.Fragment, null,
|
|
73
76
|
canUpdateProperty && hasUpdatePermission && (React.createElement(Box, { sx: {
|
|
@@ -96,14 +99,16 @@ export const Document = (props) => {
|
|
|
96
99
|
color: '#637381',
|
|
97
100
|
textAlign: 'center',
|
|
98
101
|
fontSize: '12px',
|
|
99
|
-
} },
|
|
102
|
+
} },
|
|
103
|
+
allowedTypesMessage,
|
|
104
|
+
".")),
|
|
100
105
|
formattedMaxSize && (React.createElement(Typography, { sx: {
|
|
101
106
|
color: '#637381',
|
|
102
107
|
textAlign: 'center',
|
|
103
108
|
fontSize: '12px',
|
|
104
109
|
} }, validate?.maxDocuments === 1
|
|
105
|
-
? `
|
|
106
|
-
: `The
|
|
110
|
+
? `Maximum size is ${formattedMaxSize}.`
|
|
111
|
+
: `The maximum size of each document is ${formattedMaxSize}.`)))))),
|
|
107
112
|
canUpdateProperty && isNil(hasUpdatePermission) && (React.createElement(Skeleton, { variant: "rectangular", height: formattedMaxSize || allowedTypesMessage ? '136px' : '115px', sx: { margin: '5px 0', borderRadius: '8px' } })),
|
|
108
113
|
React.createElement(DocumentList, { id: id, instance: instance, handleChange: handleChange, value: value, setSnackbarError: (type, message) => setSnackbarError({ message, type }), canUpdateProperty: canUpdateProperty && !!hasUpdatePermission }),
|
|
109
114
|
React.createElement(Snackbar, { open: !!snackbarError?.message, handleClose: () => setSnackbarError(null), message: snackbarError?.message, error: snackbarError?.type === 'error' }),
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { isEqual, isObject, transform } from 'lodash';
|
|
2
|
-
import mime from 'mime-types';
|
|
3
2
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4
3
|
export function difference(object, base) {
|
|
5
4
|
if (!object) {
|
|
@@ -15,30 +14,3 @@ export function difference(object, base) {
|
|
|
15
14
|
}
|
|
16
15
|
});
|
|
17
16
|
}
|
|
18
|
-
// Helper function to convert file extensions array to dropzone accept format
|
|
19
|
-
export const createAcceptObject = (allowedTypes) => {
|
|
20
|
-
if (!allowedTypes.length)
|
|
21
|
-
return undefined;
|
|
22
|
-
const acceptObject = {};
|
|
23
|
-
const customExtensions = [];
|
|
24
|
-
// First pass: collect custom extensions from allowedTypes that don't map to standard MIME types
|
|
25
|
-
allowedTypes.forEach((extension) => {
|
|
26
|
-
const mimeType = mime.lookup(extension);
|
|
27
|
-
// It's a custom extension
|
|
28
|
-
if (!mimeType) {
|
|
29
|
-
customExtensions.push(extension.startsWith('.') ? extension : `.${extension}`);
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
// If it is mapped to a value, add it to the accept object
|
|
33
|
-
if (!acceptObject[mimeType]) {
|
|
34
|
-
acceptObject[mimeType] = [];
|
|
35
|
-
}
|
|
36
|
-
acceptObject[mimeType].push(extension.startsWith('.') ? extension : `.${extension}`);
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
if (customExtensions.length) {
|
|
40
|
-
// Add custom files extensions to the accept object
|
|
41
|
-
acceptObject['file/custom'] = customExtensions;
|
|
42
|
-
}
|
|
43
|
-
return acceptObject;
|
|
44
|
-
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evoke-platform/ui-components",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0-testing.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/published/index.js",
|
|
6
6
|
"module": "dist/published/index.js",
|
|
@@ -56,7 +56,6 @@
|
|
|
56
56
|
"@types/jest": "^28.1.4",
|
|
57
57
|
"@types/json-logic-js": "^2.0.8",
|
|
58
58
|
"@types/luxon": "^3.4.2",
|
|
59
|
-
"@types/mime-types": "^3.0.1",
|
|
60
59
|
"@types/nanoid-dictionary": "^4.2.3",
|
|
61
60
|
"@types/node": "^18.0.0",
|
|
62
61
|
"@types/react": "^18.0.17",
|
|
@@ -88,7 +87,7 @@
|
|
|
88
87
|
"yalc": "^1.0.0-pre.53"
|
|
89
88
|
},
|
|
90
89
|
"peerDependencies": {
|
|
91
|
-
"@evoke-platform/context": "^1.3.0",
|
|
90
|
+
"@evoke-platform/context": "^1.3.0-testing.9",
|
|
92
91
|
"react": "^18.1.0",
|
|
93
92
|
"react-dom": "^18.1.0"
|
|
94
93
|
},
|
|
@@ -120,7 +119,6 @@
|
|
|
120
119
|
"formiojs": "^4.15.0-rc.23",
|
|
121
120
|
"html-react-parser": "^5.1.18",
|
|
122
121
|
"luxon": "^2.5.2",
|
|
123
|
-
"mime-type": "^5.0.3",
|
|
124
122
|
"msw": "^2.7.3",
|
|
125
123
|
"nanoid": "^5.0.8",
|
|
126
124
|
"nanoid-dictionary": "^4.3.0",
|