@popsure/dirty-swan 0.38.1 → 0.38.2

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.
@@ -7,20 +7,20 @@ const mockOnFileSelect = jest.fn();
7
7
  const mockOnRemoveFile = jest.fn();
8
8
  const file = new File(['DummyFile'], 'dummy.png', { type: 'image/png' });
9
9
 
10
- const inputTestId = "ds-drop-input";
11
- const spinnerTestId = "ds-filecell-spinner";
12
- const progressbarTestId = "ds-filecell-progressbar";
10
+ const inputTestId = 'ds-drop-input';
11
+ const spinnerTestId = 'ds-filecell-spinner';
12
+ const progressbarTestId = 'ds-filecell-progressbar';
13
13
  const uploadedFilesMock = {
14
- id: "123",
15
- name: "File name",
16
- progress: 100,
17
- type: "jpg",
14
+ id: '123',
15
+ name: 'File name',
16
+ progress: 100,
17
+ type: 'jpg',
18
18
  };
19
19
 
20
20
  const setup = ({
21
- uploadedFiles = [],
22
- uploading = false,
23
- ...rest
21
+ uploadedFiles = [],
22
+ uploading = false,
23
+ ...rest
24
24
  }: Partial<MultiDropzoneProps>) => {
25
25
  return render(
26
26
  <MultiDropzone
@@ -34,7 +34,7 @@ const setup = ({
34
34
  };
35
35
 
36
36
  describe('MultiDropzone component', () => {
37
- it("should call onFileSelect on files change", async () => {
37
+ it('should call onFileSelect on files change', async () => {
38
38
  const { getByTestId, user } = setup({});
39
39
  const files = [file, file];
40
40
 
@@ -44,19 +44,22 @@ describe('MultiDropzone component', () => {
44
44
  });
45
45
 
46
46
  describe('Error states', () => {
47
- it("should show max files error message", () => {
47
+ it('should show max files error message', () => {
48
48
  const screen = setup({
49
49
  maxFiles: 1,
50
- uploadedFiles: [uploadedFilesMock, {
51
- ...uploadedFilesMock,
52
- id: "222"
53
- }],
50
+ uploadedFiles: [
51
+ uploadedFilesMock,
52
+ {
53
+ ...uploadedFilesMock,
54
+ id: '222',
55
+ },
56
+ ],
54
57
  });
55
-
56
- expect(screen.getByText("Too many files.")).toBeVisible();
58
+
59
+ expect(screen.getByText('Too many files.')).toBeVisible();
57
60
  });
58
61
 
59
- it("should show max file size error message", async () => {
62
+ it('should show max file size error message', async () => {
60
63
  const { getByTestId, getByText, user } = setup({ maxSize: 10 });
61
64
  const bigFile = file;
62
65
  Object.defineProperty(bigFile, 'size', { value: 1024 });
@@ -64,12 +67,12 @@ describe('MultiDropzone component', () => {
64
67
  await user.upload(getByTestId(inputTestId), [bigFile]);
65
68
 
66
69
  expect(
67
- getByText("File is too large. It must be less than 10 Bytes.")
70
+ getByText('File is too large. It must be less than 10 Bytes.')
68
71
  ).toBeInTheDocument();
69
72
  });
70
73
 
71
- it("should show wrong filetype error message", async () => {
72
- const { getByTestId, getByText } = setup({ accept: "document" });
74
+ it('should show wrong filetype error message', async () => {
75
+ const { getByTestId, getByText } = setup({ accept: 'document' });
73
76
  const input = getByTestId(inputTestId);
74
77
 
75
78
  await act(async () => {
@@ -79,12 +82,14 @@ describe('MultiDropzone component', () => {
79
82
  });
80
83
 
81
84
  expect(
82
- getByText("File type must be one of DOC, DOCX, PDF")
85
+ getByText('File type must be one of DOC, DOCX, PDF')
83
86
  ).toBeInTheDocument();
84
87
  });
85
88
 
86
- it("should remove wrong filetype error message", async () => {
87
- const { getByAltText, getByTestId, queryByText, user } = setup({ accept: "document" });
89
+ it('should remove wrong filetype error message', async () => {
90
+ const { getByAltText, getByTestId, queryByText, user } = setup({
91
+ accept: 'document',
92
+ });
88
93
  const input = getByTestId(inputTestId);
89
94
 
90
95
  await act(async () => {
@@ -93,135 +98,150 @@ describe('MultiDropzone component', () => {
93
98
  fireEvent.change(input, { target: { files: [file] } });
94
99
  });
95
100
 
96
- await user.click(getByAltText("remove"));
101
+ await user.click(getByAltText('remove'));
97
102
 
98
- expect(queryByText("File type must be one of DOC, DOCX, PDF")).not.toBeInTheDocument();
103
+ expect(
104
+ queryByText('File type must be one of DOC, DOCX, PDF')
105
+ ).not.toBeInTheDocument();
99
106
  });
100
107
  });
101
108
 
102
109
  describe('Copy text', () => {
103
- it("should show uploader text", () => {
110
+ it('should show uploader text', () => {
104
111
  const screen = setup({});
105
112
 
106
- expect(screen.getByText("Choose file or drag & drop")).toBeInTheDocument();
113
+ expect(
114
+ screen.getByText('Choose file or drag & drop')
115
+ ).toBeInTheDocument();
107
116
  });
108
117
 
109
- it("should show uploader text translated", () => {
110
- const instructionsText = "Drag drop file";
118
+ it('should show uploader text translated', () => {
119
+ const instructionsText = 'Drag drop file';
111
120
  const screen = setup({
112
- textOverrides: { instructionsText }
121
+ textOverrides: { instructionsText },
113
122
  });
114
123
 
115
124
  expect(screen.getByText(instructionsText)).toBeInTheDocument();
116
125
  });
117
126
 
118
- it("should show image accept file type label", () => {
119
- const screen = setup({ accept: "image" });
127
+ it('should show image accept file type label', () => {
128
+ const screen = setup({ accept: 'image' });
120
129
 
121
130
  expect(
122
- screen.getByText("Supports HEIC, BMP, JPEG, JPG, PNG")
131
+ screen.getByText('Supports HEIC, BMP, JPEG, JPG, PNG')
123
132
  ).toBeInTheDocument();
124
133
  });
125
134
 
126
- it("should show document accept file type label", () => {
127
- const screen = setup({ accept: "document" });
135
+ it('should show document accept file type label', () => {
136
+ const screen = setup({ accept: 'document' });
128
137
 
129
- expect(
130
- screen.getByText("Supports DOC, DOCX, PDF")
131
- ).toBeInTheDocument();
138
+ expect(screen.getByText('Supports DOC, DOCX, PDF')).toBeInTheDocument();
132
139
  });
133
140
 
134
- it("should custom document accept file type label", () => {
135
- const screen = setup({ accept: {
136
- "application/pdf": [".pdf"],
137
- "image/jpg": [".jpg"],
138
- } });
141
+ it('should custom document accept file type label', () => {
142
+ const screen = setup({
143
+ accept: {
144
+ 'application/pdf': ['.pdf'],
145
+ 'image/jpg': ['.jpg'],
146
+ },
147
+ });
139
148
 
140
- expect(
141
- screen.getByText("Supports PDF, JPG")
142
- ).toBeInTheDocument();
149
+ expect(screen.getByText('Supports PDF, JPG')).toBeInTheDocument();
143
150
  });
144
151
 
145
- it("should show disabled text if is uploading", () => {
152
+ it('should show disabled text if is uploading', () => {
146
153
  const screen = setup({ uploading: true });
147
154
 
148
155
  expect(
149
- screen.getByText("Please wait while uploading file...")
156
+ screen.getByText('Please wait while uploading file...')
150
157
  ).toBeInTheDocument();
151
158
  });
159
+
160
+ it('should associate input with its label', () => {
161
+ const { getByLabelText } = setup({});
162
+ const input = getByLabelText('Choose file or drag & drop');
163
+
164
+ expect(input).toBeInTheDocument();
165
+ });
152
166
  });
153
167
 
154
168
  describe('Uploaded files', () => {
155
- it("should show uploaded files", () => {
169
+ it('should show uploaded files', () => {
156
170
  const screen = setup({
157
171
  uploadedFiles: [uploadedFilesMock],
158
172
  });
159
173
 
160
- expect(
161
- screen.getByText(uploadedFilesMock.name)
162
- ).toBeInTheDocument();
174
+ expect(screen.getByText(uploadedFilesMock.name)).toBeInTheDocument();
163
175
  });
164
176
 
165
- it("should call onRemoveFile with uploaded file id", () => {
177
+ it('should call onRemoveFile with uploaded file id', () => {
166
178
  const screen = setup({
167
179
  uploadedFiles: [uploadedFilesMock],
168
180
  });
169
181
 
170
- screen.getByAltText("remove").click();
182
+ screen.getByAltText('remove').click();
171
183
 
172
184
  expect(mockOnRemoveFile).toBeCalledWith(uploadedFilesMock.id);
173
185
  });
174
186
 
175
- it("should show uploaded file with uploading label", () => {
187
+ it('should show uploaded file with uploading label', () => {
176
188
  const screen = setup({
177
189
  uploadedFiles: [{ ...uploadedFilesMock, progress: 50 }],
178
190
  });
179
191
 
180
- expect(screen.getByText("Uploading...")).toBeInTheDocument();
192
+ expect(screen.getByText('Uploading...')).toBeInTheDocument();
181
193
  });
182
194
 
183
- it("should show uploaded file with progress bar", () => {
195
+ it('should show uploaded file with progress bar', () => {
184
196
  const screen = setup({
185
- uploadedFiles: [{
186
- ...uploadedFilesMock,
187
- progress: 50,
188
- }],
197
+ uploadedFiles: [
198
+ {
199
+ ...uploadedFilesMock,
200
+ progress: 50,
201
+ },
202
+ ],
189
203
  });
190
204
 
191
205
  expect(screen.getByTestId(progressbarTestId)).toBeInTheDocument();
192
206
  });
193
207
 
194
- it("should show uploaded file with no progress bar", () => {
208
+ it('should show uploaded file with no progress bar', () => {
195
209
  const screen = setup({
196
- uploadedFiles: [{
197
- ...uploadedFilesMock,
198
- progress: 50,
199
- showProgressBar: false
200
- }],
210
+ uploadedFiles: [
211
+ {
212
+ ...uploadedFilesMock,
213
+ progress: 50,
214
+ showProgressBar: false,
215
+ },
216
+ ],
201
217
  });
202
218
 
203
219
  expect(screen.queryByTestId(progressbarTestId)).not.toBeInTheDocument();
204
220
  });
205
221
 
206
- it("should show uploaded file with loading spinner", () => {
222
+ it('should show uploaded file with loading spinner', () => {
207
223
  const screen = setup({
208
- uploadedFiles: [{
209
- ...uploadedFilesMock,
210
- progress: 50,
211
- showLoadingSpinner: true
212
- }],
224
+ uploadedFiles: [
225
+ {
226
+ ...uploadedFilesMock,
227
+ progress: 50,
228
+ showLoadingSpinner: true,
229
+ },
230
+ ],
213
231
  });
214
232
 
215
233
  expect(screen.getByTestId(spinnerTestId)).toBeInTheDocument();
216
234
  });
217
235
 
218
- it("should show uploaded file with no loading spinner", () => {
236
+ it('should show uploaded file with no loading spinner', () => {
219
237
  const screen = setup({
220
- uploadedFiles: [{
221
- ...uploadedFilesMock,
222
- progress: 50,
223
- showLoadingSpinner: false
224
- }],
238
+ uploadedFiles: [
239
+ {
240
+ ...uploadedFilesMock,
241
+ progress: 50,
242
+ showLoadingSpinner: false,
243
+ },
244
+ ],
225
245
  });
226
246
 
227
247
  expect(screen.queryByTestId(spinnerTestId)).not.toBeInTheDocument();
@@ -1,4 +1,4 @@
1
- import { useCallback, useState } from 'react';
1
+ import { useCallback, useState, useRef } from 'react';
2
2
  import classnames from 'classnames';
3
3
  import { useDropzone, FileRejection } from 'react-dropzone';
4
4
  import AnimateHeight from 'react-animate-height';
@@ -6,20 +6,20 @@ import generateId from '../../util/generateId';
6
6
  import styles from './style.module.scss';
7
7
  import icons from './icons/index'; // TODO: inline all of the svgs
8
8
  import UploadFileCell from './UploadFileCell';
9
- import {
10
- formatAcceptFileList,
11
- getErrorMessage,
12
- getFormattedAcceptObject,
13
- getUploadStatus
9
+ import {
10
+ formatAcceptFileList,
11
+ getErrorMessage,
12
+ getFormattedAcceptObject,
13
+ getUploadStatus,
14
14
  } from './utils';
15
15
 
16
- import {
17
- AcceptType,
18
- ErrorMessage,
16
+ import {
17
+ AcceptType,
18
+ ErrorMessage,
19
19
  FileType,
20
- TextOverrides,
21
- UploadedFile,
22
- UploadStatus
20
+ TextOverrides,
21
+ UploadedFile,
22
+ UploadStatus,
23
23
  } from './types';
24
24
 
25
25
  import { formatBytes } from '../../util/formatBytes';
@@ -50,21 +50,23 @@ const MultiDropzone = ({
50
50
  const [errors, setErrors] = useState<ErrorMessage[]>([]);
51
51
  const formattedAccept = getFormattedAcceptObject(accept);
52
52
  const fileList = formatAcceptFileList(formattedAccept);
53
- const maxSizePlaceholder = maxSize && maxSize > 0
54
- ? `${textOverrides?.sizeUpToText || "up to"} ${formatBytes(maxSize)}`
55
- : "";
56
- const placeholder = `${textOverrides?.supportsTextShort || "Supports"} ${fileList || "JPEG, PNG, PDF"} ${maxSizePlaceholder}`;
53
+ const maxSizePlaceholder =
54
+ maxSize && maxSize > 0
55
+ ? `${textOverrides?.sizeUpToText || 'up to'} ${formatBytes(maxSize)}`
56
+ : '';
57
+ const placeholder = `${textOverrides?.supportsTextShort || 'Supports'} ${
58
+ fileList || 'JPEG, PNG, PDF'
59
+ } ${maxSizePlaceholder}`;
57
60
  const isOverMaxFiles = maxFiles > 0 && uploadedFiles.length > maxFiles;
58
61
 
59
- const removeError = (removeId: string) => (
60
- setErrors(errors.filter(({ id }) => id !== removeId))
61
- );
62
+ const removeError = (removeId: string) =>
63
+ setErrors(errors.filter(({ id }) => id !== removeId));
62
64
 
63
65
  const onDrop = useCallback(
64
66
  (acceptedFiles: File[], filesRejected: FileRejection[]) => {
65
67
  onFileSelect(acceptedFiles);
66
68
 
67
- setErrors((previousErrors) => ([
69
+ setErrors((previousErrors) => [
68
70
  ...previousErrors,
69
71
  ...filesRejected.map(({ errors }) => ({
70
72
  id: generateId(),
@@ -73,13 +75,12 @@ const placeholder = `${textOverrides?.supportsTextShort || "Supports"} ${fileLis
73
75
  { fileList, maxSize },
74
76
  textOverrides
75
77
  ),
76
- }))
77
- ]));
78
+ })),
79
+ ]);
78
80
  },
79
81
  [fileList, maxSize, onFileSelect, textOverrides]
80
82
  );
81
83
 
82
-
83
84
  const { getRootProps, getInputProps } = useDropzone({
84
85
  accept: formattedAccept,
85
86
  disabled: uploading,
@@ -87,6 +88,8 @@ const placeholder = `${textOverrides?.supportsTextShort || "Supports"} ${fileLis
87
88
  onDrop,
88
89
  });
89
90
 
91
+ const uniqueId = useRef(generateId());
92
+
90
93
  return (
91
94
  <div className={styles.container}>
92
95
  <div
@@ -100,6 +103,7 @@ const placeholder = `${textOverrides?.supportsTextShort || "Supports"} ${fileLis
100
103
  >
101
104
  <input
102
105
  data-testid="ds-drop-input"
106
+ id={uniqueId.current}
103
107
  {...getInputProps()}
104
108
  />
105
109
  <img
@@ -107,31 +111,39 @@ const placeholder = `${textOverrides?.supportsTextShort || "Supports"} ${fileLis
107
111
  src={isCondensed ? icons.uploadSmallIcon : icons.uploadIcon}
108
112
  alt="purple cloud with an arrow"
109
113
  />
110
- <div className={`p-h4 mt8 ${isCondensed ? styles.textInline : ''}`}>
114
+ <label
115
+ htmlFor={uniqueId.current}
116
+ className={`p-h4 mt8 d-block c-pointer ${
117
+ isCondensed ? styles.textInline : ''
118
+ }`}
119
+ >
111
120
  {uploading
112
121
  ? textOverrides?.currentlyUploadingText ||
113
122
  'Please wait while uploading file...'
114
123
  : textOverrides?.instructionsText || 'Choose file or drag & drop'}
115
- </div>
124
+ </label>
116
125
  <div className="p-p--small tc-grey-500">
117
126
  {textOverrides?.supportsText || placeholder}
118
127
  </div>
119
128
  </div>
120
129
 
121
- {errors.map(({ id, message }) => message && (
122
- <UploadFileCell
123
- uploadStatus="ERROR"
124
- file={{
125
- error: message,
126
- id,
127
- name: message,
128
- progress: 0,
129
- }}
130
- key={id}
131
- onRemoveFile={() => removeError(id)}
132
- uploading={false}
133
- />
134
- ))}
130
+ {errors.map(
131
+ ({ id, message }) =>
132
+ message && (
133
+ <UploadFileCell
134
+ uploadStatus="ERROR"
135
+ file={{
136
+ error: message,
137
+ id,
138
+ name: message,
139
+ progress: 0,
140
+ }}
141
+ key={id}
142
+ onRemoveFile={() => removeError(id)}
143
+ uploading={false}
144
+ />
145
+ )
146
+ )}
135
147
 
136
148
  {uploadedFiles.length > 0 && (
137
149
  <div className="w100 mt16">
@@ -149,7 +161,7 @@ const placeholder = `${textOverrides?.supportsTextShort || "Supports"} ${fileLis
149
161
 
150
162
  <AnimateHeight duration={300} height={isOverMaxFiles ? 'auto' : 0}>
151
163
  <p className="tc-red-500 p-p--small">
152
- {textOverrides?.tooManyFilesError || "Too many files."}
164
+ {textOverrides?.tooManyFilesError || 'Too many files.'}
153
165
  </p>
154
166
  </AnimateHeight>
155
167
  </div>
@@ -1,4 +1,4 @@
1
- @use "../../scss/public/grid" as *;
1
+ @use '../../scss/public/grid' as *;
2
2
 
3
3
  .container {
4
4
  background-color: transparent;
@@ -33,5 +33,5 @@
33
33
 
34
34
  .dropzoneContainerDisabled {
35
35
  pointer-events: none;
36
- opacity: 0.4
36
+ opacity: 0.4;
37
37
  }