@evoke-platform/ui-components 1.6.1-testing.0 → 1.7.0-testing.1

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.
@@ -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 = `${validate.allowedFileExtensions.slice(0, -1).join(', ')} or ${validate.allowedFileExtensions.slice(-1)[0]}`;
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 ?? []), ...(files ?? [])];
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 ? createAcceptObject(validate.allowedFileExtensions) : undefined,
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. Allowed extensions are: ${allowedTypesMessage}`);
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 max limit of ${formattedMaxSize}`);
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
- } }, `${allowedTypesMessage}.`)),
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
- ? `Max size of ${formattedMaxSize}.`
106
- : `The max size of each document is ${formattedMaxSize}.`)))))),
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;
@@ -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
- return Object.values(this.errorDetails).join(', ');
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.handleFileRejections }))), root);
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
  }
@@ -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 = `${validate.allowedFileExtensions.slice(0, -1).join(', ')} or ${validate.allowedFileExtensions.slice(-1)[0]}`;
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 ? createAcceptObject(validate.allowedFileExtensions) : undefined,
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. Allowed extensions are: ${allowedTypesMessage}`);
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 max limit of ${formattedMaxSize}`);
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
- } }, `${allowedTypesMessage}.`)),
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
- ? `Max size of ${formattedMaxSize}.`
106
- : `The max size of each document is ${formattedMaxSize}.`)))))),
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,2 +1 @@
1
1
  export declare function difference(object?: any, base?: any): any;
2
- export declare const createAcceptObject: (allowedTypes: string[]) => Record<string, string[]> | undefined;
@@ -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.6.1-testing.0",
3
+ "version": "1.7.0-testing.1",
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-testing.9",
90
+ "@evoke-platform/context": "^1.3.1-0",
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",