@availity/mui-file-selector 1.6.2 → 1.6.4

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 CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4
4
 
5
+ ## [1.6.4](https://github.com/Availity/element/compare/@availity/mui-file-selector@1.6.3...@availity/mui-file-selector@1.6.4) (2025-04-28)
6
+
7
+ ### Dependency Updates
8
+
9
+ * `mui-form-utils` updated to version `1.6.3`
10
+ * `mui-dialog` updated to version `1.6.3`
11
+ * `mui-textfield` updated to version `1.6.3`
12
+ ## [1.6.3](https://github.com/Availity/element/compare/@availity/mui-file-selector@1.6.2...@availity/mui-file-selector@1.6.3) (2025-04-25)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * **mui-file-selector:** only throw too-many files rejection once ([3b3b22f](https://github.com/Availity/element/commit/3b3b22f8b34b73ee61be0501aba58f6adf54e3b0))
18
+
5
19
  ## [1.6.2](https://github.com/Availity/element/compare/@availity/mui-file-selector@1.6.1...@availity/mui-file-selector@1.6.2) (2025-04-24)
6
20
 
7
21
  ### Dependency Updates
package/dist/index.js CHANGED
@@ -250,9 +250,27 @@ var Dropzone = ({
250
250
  const previous = (_a2 = watch(name)) != null ? _a2 : [];
251
251
  setValue(name, previous.concat(acceptedFiles));
252
252
  if (fileRejections.length > 0) {
253
- for (const rejection of fileRejections) {
254
- rejection.id = counter.increment();
255
- }
253
+ const TOO_MANY_FILES_CODE = "too-many-files";
254
+ let hasTooManyFiles = false;
255
+ fileRejections = fileRejections.reduce(
256
+ (acc, rejection) => {
257
+ const isTooManyFiles = rejection.errors.some((error) => error.code === TOO_MANY_FILES_CODE);
258
+ if (isTooManyFiles) {
259
+ if (!hasTooManyFiles) {
260
+ hasTooManyFiles = true;
261
+ acc.push(__spreadProps(__spreadValues({}, rejection), {
262
+ id: counter.increment()
263
+ }));
264
+ }
265
+ } else {
266
+ acc.push(__spreadProps(__spreadValues({}, rejection), {
267
+ id: counter.increment()
268
+ }));
269
+ }
270
+ return acc;
271
+ },
272
+ []
273
+ );
256
274
  }
257
275
  if (setFileRejections) setFileRejections(fileRejections);
258
276
  if (onDrop) onDrop(acceptedFiles, fileRejections, event);
@@ -605,9 +623,21 @@ var TextField = (0, import_react2.forwardRef)((props, ref) => {
605
623
 
606
624
  // src/lib/UploadProgressBar.tsx
607
625
  var import_jsx_runtime10 = require("react/jsx-runtime");
626
+ var ERROR_MAPPINGS = [
627
+ {
628
+ pattern: /but has an extension for/i,
629
+ friendlyMessage: "File format does not match file extension."
630
+ }
631
+ ];
632
+ var friendlyErrorMessage = (errorMessage) => {
633
+ const mapping = ERROR_MAPPINGS.find(({ pattern }) => pattern.test(errorMessage));
634
+ return mapping ? mapping.friendlyMessage : errorMessage;
635
+ };
608
636
  var UploadProgressBar = ({ upload, onProgress, onError, onSuccess }) => {
609
637
  const [statePercentage, setStatePercentage] = (0, import_react3.useState)(upload.percentage || 0);
610
- const [errorMessage, setErrorMessage] = (0, import_react3.useState)(upload.errorMessage || "");
638
+ const [errorMessage, setErrorMessage] = (0, import_react3.useState)(
639
+ upload.errorMessage ? friendlyErrorMessage(upload.errorMessage) : ""
640
+ );
611
641
  const [password, setPassword] = (0, import_react3.useState)("");
612
642
  const [showPassword, setShowPassword] = (0, import_react3.useState)(false);
613
643
  const [modalOpen, setModalOpen] = (0, import_react3.useState)(false);
@@ -617,7 +647,7 @@ var UploadProgressBar = ({ upload, onProgress, onError, onSuccess }) => {
617
647
  if (onProgress) onProgress(upload);
618
648
  };
619
649
  const handleOnError = () => {
620
- setErrorMessage(upload.errorMessage || "Error");
650
+ setErrorMessage(upload.errorMessage ? friendlyErrorMessage(upload.errorMessage) : "Error");
621
651
  if (onError) onError(upload);
622
652
  };
623
653
  const handleOnSuccess = () => {
@@ -642,11 +672,18 @@ var UploadProgressBar = ({ upload, onProgress, onError, onSuccess }) => {
642
672
  upload.onSuccess.push(handleOnSuccess);
643
673
  upload.onError.push(handleOnError);
644
674
  return errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_mui_layout3.Box, { sx: { display: "flex", flexWrap: "wrap", columnGap: "4px" }, children: [
645
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_mui_list2.ListItemText, { slotProps: { primary: { color: "text.error", variant: "body2", component: "div" } }, sx: { wordWrap: "break-word" }, children: [
646
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_mui_icon4.WarningTriangleIcon, { sx: { verticalAlign: "middle", mt: "-2px" } }),
647
- " ",
648
- errorMessage
649
- ] }),
675
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
676
+ import_mui_list2.ListItemText,
677
+ {
678
+ slotProps: { primary: { color: "text.error", variant: "body2", component: "div" } },
679
+ sx: { wordWrap: "break-word" },
680
+ children: [
681
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_mui_icon4.WarningTriangleIcon, { sx: { verticalAlign: "middle", mt: "-2px" } }),
682
+ " ",
683
+ errorMessage
684
+ ]
685
+ }
686
+ ),
650
687
  upload.status === "encrypted" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "pwRequired", children: [
651
688
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_mui_button3.Button, { color: "secondary", size: "small", onClick: toggleModal, children: "Enter Password" }),
652
689
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Dialog, { open: modalOpen, onClose: toggleModal, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("form", { onSubmit: verifyPassword, children: [
@@ -660,7 +697,15 @@ var UploadProgressBar = ({ upload, onProgress, onError, onSuccess }) => {
660
697
  onChange: handlePasswordChange,
661
698
  autoFocus: true,
662
699
  InputProps: {
663
- endAdornment: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_mui_form_utils3.InputAdornment, { position: "end", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_mui_button3.IconButton, { title: "password visibility", onClick: () => setShowPassword((prev) => !prev), edge: "end", children: showPassword ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_mui_icon4.EyeIcon, { fontSize: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_mui_icon4.EyeSlashIcon, { fontSize: "small" }) }) })
700
+ endAdornment: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_mui_form_utils3.InputAdornment, { position: "end", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
701
+ import_mui_button3.IconButton,
702
+ {
703
+ title: "password visibility",
704
+ onClick: () => setShowPassword((prev) => !prev),
705
+ edge: "end",
706
+ children: showPassword ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_mui_icon4.EyeIcon, { fontSize: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_mui_icon4.EyeSlashIcon, { fontSize: "small" })
707
+ }
708
+ ) })
664
709
  }
665
710
  }
666
711
  ) }),
package/dist/index.mjs CHANGED
@@ -207,9 +207,27 @@ var Dropzone = ({
207
207
  const previous = (_a2 = watch(name)) != null ? _a2 : [];
208
208
  setValue(name, previous.concat(acceptedFiles));
209
209
  if (fileRejections.length > 0) {
210
- for (const rejection of fileRejections) {
211
- rejection.id = counter.increment();
212
- }
210
+ const TOO_MANY_FILES_CODE = "too-many-files";
211
+ let hasTooManyFiles = false;
212
+ fileRejections = fileRejections.reduce(
213
+ (acc, rejection) => {
214
+ const isTooManyFiles = rejection.errors.some((error) => error.code === TOO_MANY_FILES_CODE);
215
+ if (isTooManyFiles) {
216
+ if (!hasTooManyFiles) {
217
+ hasTooManyFiles = true;
218
+ acc.push(__spreadProps(__spreadValues({}, rejection), {
219
+ id: counter.increment()
220
+ }));
221
+ }
222
+ } else {
223
+ acc.push(__spreadProps(__spreadValues({}, rejection), {
224
+ id: counter.increment()
225
+ }));
226
+ }
227
+ return acc;
228
+ },
229
+ []
230
+ );
213
231
  }
214
232
  if (setFileRejections) setFileRejections(fileRejections);
215
233
  if (onDrop) onDrop(acceptedFiles, fileRejections, event);
@@ -579,9 +597,21 @@ var TextField = forwardRef((props, ref) => {
579
597
 
580
598
  // src/lib/UploadProgressBar.tsx
581
599
  import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
600
+ var ERROR_MAPPINGS = [
601
+ {
602
+ pattern: /but has an extension for/i,
603
+ friendlyMessage: "File format does not match file extension."
604
+ }
605
+ ];
606
+ var friendlyErrorMessage = (errorMessage) => {
607
+ const mapping = ERROR_MAPPINGS.find(({ pattern }) => pattern.test(errorMessage));
608
+ return mapping ? mapping.friendlyMessage : errorMessage;
609
+ };
582
610
  var UploadProgressBar = ({ upload, onProgress, onError, onSuccess }) => {
583
611
  const [statePercentage, setStatePercentage] = useState2(upload.percentage || 0);
584
- const [errorMessage, setErrorMessage] = useState2(upload.errorMessage || "");
612
+ const [errorMessage, setErrorMessage] = useState2(
613
+ upload.errorMessage ? friendlyErrorMessage(upload.errorMessage) : ""
614
+ );
585
615
  const [password, setPassword] = useState2("");
586
616
  const [showPassword, setShowPassword] = useState2(false);
587
617
  const [modalOpen, setModalOpen] = useState2(false);
@@ -591,7 +621,7 @@ var UploadProgressBar = ({ upload, onProgress, onError, onSuccess }) => {
591
621
  if (onProgress) onProgress(upload);
592
622
  };
593
623
  const handleOnError = () => {
594
- setErrorMessage(upload.errorMessage || "Error");
624
+ setErrorMessage(upload.errorMessage ? friendlyErrorMessage(upload.errorMessage) : "Error");
595
625
  if (onError) onError(upload);
596
626
  };
597
627
  const handleOnSuccess = () => {
@@ -616,11 +646,18 @@ var UploadProgressBar = ({ upload, onProgress, onError, onSuccess }) => {
616
646
  upload.onSuccess.push(handleOnSuccess);
617
647
  upload.onError.push(handleOnError);
618
648
  return errorMessage ? /* @__PURE__ */ jsxs7(Box2, { sx: { display: "flex", flexWrap: "wrap", columnGap: "4px" }, children: [
619
- /* @__PURE__ */ jsxs7(ListItemText, { slotProps: { primary: { color: "text.error", variant: "body2", component: "div" } }, sx: { wordWrap: "break-word" }, children: [
620
- /* @__PURE__ */ jsx10(WarningTriangleIcon, { sx: { verticalAlign: "middle", mt: "-2px" } }),
621
- " ",
622
- errorMessage
623
- ] }),
649
+ /* @__PURE__ */ jsxs7(
650
+ ListItemText,
651
+ {
652
+ slotProps: { primary: { color: "text.error", variant: "body2", component: "div" } },
653
+ sx: { wordWrap: "break-word" },
654
+ children: [
655
+ /* @__PURE__ */ jsx10(WarningTriangleIcon, { sx: { verticalAlign: "middle", mt: "-2px" } }),
656
+ " ",
657
+ errorMessage
658
+ ]
659
+ }
660
+ ),
624
661
  upload.status === "encrypted" && /* @__PURE__ */ jsxs7("div", { className: "pwRequired", children: [
625
662
  /* @__PURE__ */ jsx10(Button2, { color: "secondary", size: "small", onClick: toggleModal, children: "Enter Password" }),
626
663
  /* @__PURE__ */ jsx10(Dialog, { open: modalOpen, onClose: toggleModal, children: /* @__PURE__ */ jsxs7("form", { onSubmit: verifyPassword, children: [
@@ -634,7 +671,15 @@ var UploadProgressBar = ({ upload, onProgress, onError, onSuccess }) => {
634
671
  onChange: handlePasswordChange,
635
672
  autoFocus: true,
636
673
  InputProps: {
637
- endAdornment: /* @__PURE__ */ jsx10(InputAdornment, { position: "end", children: /* @__PURE__ */ jsx10(IconButton2, { title: "password visibility", onClick: () => setShowPassword((prev) => !prev), edge: "end", children: showPassword ? /* @__PURE__ */ jsx10(EyeIcon, { fontSize: "small" }) : /* @__PURE__ */ jsx10(EyeSlashIcon, { fontSize: "small" }) }) })
674
+ endAdornment: /* @__PURE__ */ jsx10(InputAdornment, { position: "end", children: /* @__PURE__ */ jsx10(
675
+ IconButton2,
676
+ {
677
+ title: "password visibility",
678
+ onClick: () => setShowPassword((prev) => !prev),
679
+ edge: "end",
680
+ children: showPassword ? /* @__PURE__ */ jsx10(EyeIcon, { fontSize: "small" }) : /* @__PURE__ */ jsx10(EyeSlashIcon, { fontSize: "small" })
681
+ }
682
+ ) })
638
683
  }
639
684
  }
640
685
  ) }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@availity/mui-file-selector",
3
- "version": "1.6.2",
3
+ "version": "1.6.4",
4
4
  "description": "Availity MUI file-selector Component - part of the @availity/element design system",
5
5
  "keywords": [
6
6
  "react",
@@ -44,7 +44,7 @@
44
44
  "@availity/mui-alert": "^1.0.5",
45
45
  "@availity/mui-button": "^1.1.3",
46
46
  "@availity/mui-divider": "^1.0.2",
47
- "@availity/mui-form-utils": "^1.2.2",
47
+ "@availity/mui-form-utils": "^1.3.0",
48
48
  "@availity/mui-icon": "^1.0.2",
49
49
  "@availity/mui-layout": "^1.0.2",
50
50
  "@availity/mui-list": "^1.0.5",
@@ -173,9 +173,34 @@ export const Dropzone = ({
173
173
  setValue(name, previous.concat(acceptedFiles));
174
174
 
175
175
  if (fileRejections.length > 0) {
176
- for (const rejection of fileRejections) {
177
- rejection.id = counter.increment();
178
- }
176
+ const TOO_MANY_FILES_CODE = 'too-many-files';
177
+ let hasTooManyFiles = false;
178
+
179
+ fileRejections = fileRejections.reduce(
180
+ (acc, rejection) => {
181
+ const isTooManyFiles = rejection.errors.some((error) => error.code === TOO_MANY_FILES_CODE);
182
+
183
+ if (isTooManyFiles) {
184
+ // Only add the first too-many-files rejection
185
+ if (!hasTooManyFiles) {
186
+ hasTooManyFiles = true;
187
+ acc.push({
188
+ ...rejection,
189
+ id: counter.increment(),
190
+ });
191
+ }
192
+ } else {
193
+ // Add all other rejection types normally
194
+ acc.push({
195
+ ...rejection,
196
+ id: counter.increment(),
197
+ });
198
+ }
199
+
200
+ return acc;
201
+ },
202
+ [] as Array<(typeof fileRejections)[0] & { id: number }>
203
+ );
179
204
  }
180
205
 
181
206
  if (setFileRejections) setFileRejections(fileRejections);
@@ -38,6 +38,23 @@ describe('UploadProgressBar', () => {
38
38
  expect(screen.getByText('error message')).toBeTruthy();
39
39
  });
40
40
 
41
+ test('should intercept file extension error message', () => {
42
+ const mockUpload: unknown = {
43
+ onProgress: [],
44
+ onError: [],
45
+ onSuccess: [],
46
+ errorMessage: '.txt is application&#x2f;pdf but has an extension for text&#x2f;plain',
47
+ file: {
48
+ name: 'test',
49
+ },
50
+ percentage: 0,
51
+ };
52
+
53
+ render(<UploadProgressBar upload={mockUpload as Upload} />);
54
+
55
+ expect(screen.getByText('File format does not match file extension.')).toBeTruthy();
56
+ });
57
+
41
58
  test('should allow passwords for encrypted files', () => {
42
59
  const mockUpload: unknown = {
43
60
  onProgress: [],
@@ -47,7 +64,7 @@ describe('UploadProgressBar', () => {
47
64
  file: {
48
65
  name: 'test',
49
66
  },
50
- status: 'encrypted'
67
+ status: 'encrypted',
51
68
  };
52
69
 
53
70
  render(<UploadProgressBar upload={mockUpload as Upload} />);
@@ -28,9 +28,32 @@ export type UploadProgressBarProps = {
28
28
  onError?: (upload: Upload) => void;
29
29
  };
30
30
 
31
+ type ErrorMapping = {
32
+ pattern: RegExp;
33
+ friendlyMessage: string;
34
+ };
35
+
36
+ /**
37
+ * These errors are separate from the errors associated with ErrorAlert.
38
+ * These are errors received in the response from vault.
39
+ */
40
+ const ERROR_MAPPINGS: ErrorMapping[] = [
41
+ {
42
+ pattern: /but has an extension for/i,
43
+ friendlyMessage: 'File format does not match file extension.',
44
+ },
45
+ ];
46
+
47
+ const friendlyErrorMessage = (errorMessage: string): string => {
48
+ const mapping = ERROR_MAPPINGS.find(({ pattern }) => pattern.test(errorMessage));
49
+ return mapping ? mapping.friendlyMessage : errorMessage;
50
+ };
51
+
31
52
  export const UploadProgressBar = ({ upload, onProgress, onError, onSuccess }: UploadProgressBarProps) => {
32
53
  const [statePercentage, setStatePercentage] = useState(upload.percentage || 0);
33
- const [errorMessage, setErrorMessage] = useState(upload.errorMessage || '');
54
+ const [errorMessage, setErrorMessage] = useState(
55
+ upload.errorMessage ? friendlyErrorMessage(upload.errorMessage) : ''
56
+ );
34
57
  const [password, setPassword] = useState('');
35
58
  const [showPassword, setShowPassword] = useState(false);
36
59
  const [modalOpen, setModalOpen] = useState(false);
@@ -43,7 +66,7 @@ export const UploadProgressBar = ({ upload, onProgress, onError, onSuccess }: Up
43
66
  };
44
67
 
45
68
  const handleOnError = () => {
46
- setErrorMessage(upload.errorMessage || 'Error');
69
+ setErrorMessage(upload.errorMessage ? friendlyErrorMessage(upload.errorMessage) : 'Error');
47
70
 
48
71
  if (onError) onError(upload);
49
72
  };
@@ -76,9 +99,12 @@ export const UploadProgressBar = ({ upload, onProgress, onError, onSuccess }: Up
76
99
  upload.onError.push(handleOnError);
77
100
 
78
101
  return errorMessage ? (
79
- <Box sx={{display: 'flex', flexWrap: 'wrap', columnGap: '4px'}}>
80
- <ListItemText slotProps={{primary: { color: 'text.error', variant: 'body2', component: 'div' }}} sx={{wordWrap: 'break-word'}}>
81
- <WarningTriangleIcon sx={{verticalAlign: 'middle', mt: '-2px'}}/> {errorMessage}
102
+ <Box sx={{ display: 'flex', flexWrap: 'wrap', columnGap: '4px' }}>
103
+ <ListItemText
104
+ slotProps={{ primary: { color: 'text.error', variant: 'body2', component: 'div' } }}
105
+ sx={{ wordWrap: 'break-word' }}
106
+ >
107
+ <WarningTriangleIcon sx={{ verticalAlign: 'middle', mt: '-2px' }} /> {errorMessage}
82
108
  </ListItemText>
83
109
  {upload.status === 'encrypted' && (
84
110
  <div className="pwRequired">
@@ -98,7 +124,11 @@ export const UploadProgressBar = ({ upload, onProgress, onError, onSuccess }: Up
98
124
  InputProps={{
99
125
  endAdornment: (
100
126
  <InputAdornment position="end">
101
- <IconButton title="password visibility" onClick={() => setShowPassword((prev) => !prev)} edge="end">
127
+ <IconButton
128
+ title="password visibility"
129
+ onClick={() => setShowPassword((prev) => !prev)}
130
+ edge="end"
131
+ >
102
132
  {showPassword ? <EyeIcon fontSize="small" /> : <EyeSlashIcon fontSize="small" />}
103
133
  </IconButton>
104
134
  </InputAdornment>
@@ -107,7 +137,9 @@ export const UploadProgressBar = ({ upload, onProgress, onError, onSuccess }: Up
107
137
  />
108
138
  </DialogContent>
109
139
  <DialogActions>
110
- <Button color="primary" type="submit">Ok</Button>
140
+ <Button color="primary" type="submit">
141
+ Ok
142
+ </Button>
111
143
  </DialogActions>
112
144
  </form>
113
145
  </Dialog>