Skip to content

Commit b56438e

Browse files
loivseniOvergaard
andauthored
bug(uui-file-dropzone): change event is dispatched too early with more than 100 files (#848)
* Bugfix: Dropzone dispatch event too early. * multiple property with folder logic * add disallow folder upload story * docs: convert story to StoryObj to better map attributes * fix: ensure file browse uses same logic as file drop * test: add cases for mimetypes and simplify logic * fix: make sure mimetypes are accepted by using the right for-of loop --------- Co-authored-by: Jacob Overgaard <[email protected]>
1 parent 7092b47 commit b56438e

File tree

3 files changed

+187
-91
lines changed

3 files changed

+187
-91
lines changed

packages/uui-file-dropzone/lib/uui-file-dropzone.element.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ export class UUIFileDropzoneElement extends LabelMixin('', LitElement) {
123123
if (this._isAccepted(file)) {
124124
files.push(file);
125125
}
126-
} else if (!this.disallowFolderUpload) {
126+
} else if (!this.disallowFolderUpload && this.multiple) {
127127
// Entry is a directory
128128
const dir = this._getEntry(entry);
129129

@@ -133,6 +133,7 @@ export class UUIFileDropzoneElement extends LabelMixin('', LitElement) {
133133
}
134134
}
135135
}
136+
136137
return { files, folders };
137138
}
138139

@@ -184,7 +185,7 @@ export class UUIFileDropzoneElement extends LabelMixin('', LitElement) {
184185
}
185186

186187
// readEntries only reads up to 100 entries at a time. It is on purpose we call readEntries recursively.
187-
readEntries(reader);
188+
await readEntries(reader);
188189

189190
resolve();
190191
}, reject);
@@ -215,12 +216,12 @@ export class UUIFileDropzoneElement extends LabelMixin('', LitElement) {
215216
return true;
216217
}
217218

218-
for (const mimeType in this._acceptedMimeTypes) {
219+
for (const mimeType of this._acceptedMimeTypes) {
219220
if (fileType === mimeType) {
220221
return true;
221222
} else if (
222223
mimeType.endsWith('/*') &&
223-
fileType.startsWith(mimeType.replace('/*', ''))
224+
fileType.startsWith(mimeType.replace('*', ''))
224225
) {
225226
return true;
226227
}
@@ -244,9 +245,12 @@ export class UUIFileDropzoneElement extends LabelMixin('', LitElement) {
244245

245246
if (this.multiple === false && fileSystemResult.files.length) {
246247
fileSystemResult.files = [fileSystemResult.files[0]];
248+
fileSystemResult.folders = [];
247249
}
248250

249-
this._getAllEntries(items);
251+
if (!fileSystemResult.files.length && !fileSystemResult.folders.length) {
252+
return;
253+
}
250254

251255
this.dispatchEvent(
252256
new UUIFileDropzoneEvent(UUIFileDropzoneEvent.CHANGE, {
@@ -274,9 +278,19 @@ export class UUIFileDropzoneElement extends LabelMixin('', LitElement) {
274278
private _onFileInputChange() {
275279
const files = this._input.files ? Array.from(this._input.files) : [];
276280

281+
if (this.multiple === false && files.length > 1) {
282+
files.splice(1, files.length - 1);
283+
}
284+
285+
const allowedFiles = files.filter(file => this._isAccepted(file));
286+
287+
if (!allowedFiles.length) {
288+
return;
289+
}
290+
277291
this.dispatchEvent(
278292
new UUIFileDropzoneEvent(UUIFileDropzoneEvent.CHANGE, {
279-
detail: { files: files },
293+
detail: { files: allowedFiles, folders: [] },
280294
}),
281295
);
282296
}
Lines changed: 82 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { Meta, StoryFn } from '@storybook/web-components';
1+
import { Meta, StoryObj } from '@storybook/web-components';
22
import { action } from '@storybook/addon-actions';
33
import { html } from 'lit';
4-
import type { UUIFileDropzoneEvent } from './UUIFileDropzoneEvent';
4+
import { UUIFileDropzoneEvent } from './UUIFileDropzoneEvent';
55
import type { UUIFileDropzoneElement } from './uui-file-dropzone.element';
66

77
import '@umbraco-ui/uui-button/lib';
@@ -10,7 +10,7 @@ import '@umbraco-ui/uui-symbol-file-dropzone/lib';
1010
import './uui-file-dropzone.element';
1111
import readme from '../README.md?raw';
1212

13-
const meta: Meta<typeof UUIFileDropzoneElement> = {
13+
const meta: Meta<UUIFileDropzoneElement> = {
1414
id: 'uui-file-dropzone',
1515
title: 'Inputs/Files/File Dropzone',
1616
component: 'uui-file-dropzone',
@@ -31,83 +31,99 @@ const meta: Meta<typeof UUIFileDropzoneElement> = {
3131

3232
export default meta;
3333

34-
const handleFileChange = (e: UUIFileDropzoneEvent) => {
35-
console.log('event.detail: ', e.detail);
36-
action('change')(e);
37-
};
34+
type Story = StoryObj<UUIFileDropzoneElement>;
3835

39-
const Template: StoryFn<UUIFileDropzoneElement> = props => {
40-
return html`
41-
<uui-file-dropzone
42-
@change=${handleFileChange}
43-
.accept=${props.accept}
44-
?multiple=${props.multiple}
45-
label="Drop files here"></uui-file-dropzone>
46-
`;
36+
const handleFileChange = (e: Event) => {
37+
if (!(e instanceof UUIFileDropzoneEvent)) {
38+
return;
39+
}
40+
console.log('event.detail: ', e.detail);
41+
action('change')(e.detail);
4742
};
4843

49-
export const AAAOverview = Template.bind({});
50-
AAAOverview.storyName = 'Overview';
44+
// Attach event listener to the story to log the event
45+
document.addEventListener('change', handleFileChange);
5146

52-
export const Multiple = Template.bind({});
53-
Multiple.args = {
54-
multiple: true,
47+
export const AAAOverview: Story = {
48+
name: 'Overview',
5549
};
56-
Multiple.parameters = {
57-
docs: {
58-
description: {
59-
story:
60-
'When the multiple attribute is specified, the file input allows the user to select more than one file.',
50+
51+
export const Multiple: Story = {
52+
args: {
53+
multiple: true,
54+
},
55+
parameters: {
56+
docs: {
57+
description: {
58+
story:
59+
'When the multiple attribute is specified, the file input allows the user to select more than one file.',
60+
},
6161
},
6262
},
6363
};
6464

65-
export const Accept = Template.bind({});
66-
Accept.args = {
67-
accept: 'image/*',
68-
};
69-
Accept.parameters = {
70-
docs: {
71-
description: {
72-
story:
73-
'The accept attribute takes as its value a comma-separated list of one or more file types, or unique file type specifiers, describing which file types to allow. See the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept) for more information.',
65+
export const Accept: Story = {
66+
args: {
67+
accept: 'image/*',
68+
},
69+
parameters: {
70+
docs: {
71+
description: {
72+
story:
73+
'The accept attribute takes as its value a comma-separated list of one or more file types, or unique file type specifiers, describing which file types to allow. See the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept) for more information.',
74+
},
7475
},
7576
},
7677
};
7778

78-
export const BrowseFiles: StoryFn<UUIFileDropzoneElement> = props => {
79-
const handleBrowse = () => {
80-
const dropzone = document.getElementById(
81-
'browse-dropzone',
82-
) as UUIFileDropzoneElement;
83-
dropzone.browse();
84-
};
85-
86-
return html`
87-
<uui-file-dropzone
88-
id="browse-dropzone"
89-
.accept=${props.accept}
90-
?multiple=${props.multiple}
91-
@change=${handleFileChange}
92-
label="Drop files here">
93-
Drop files here
94-
<uui-button
95-
style="margin-top: 9px;"
96-
@click=${handleBrowse}
97-
look="primary"
98-
label="Browse"></uui-button>
99-
</uui-file-dropzone>
100-
`;
79+
export const DisallowFolderUpload: Story = {
80+
args: {
81+
disallowFolderUpload: true,
82+
},
83+
parameters: {
84+
docs: {
85+
description: {
86+
story:
87+
'The disallow-folder-upload attribute prevents the user from uploading folders.',
88+
},
89+
},
90+
},
10191
};
10292

103-
BrowseFiles.parameters = {
104-
docs: {
105-
description: {
106-
story:
107-
'The browse method allows the user to select a file from their computer.',
108-
},
109-
source: {
110-
code: `
93+
export const BrowseFiles: Story = {
94+
render: props => {
95+
const handleBrowse = () => {
96+
const dropzone = document.getElementById(
97+
'browse-dropzone',
98+
) as UUIFileDropzoneElement;
99+
dropzone.browse();
100+
};
101+
102+
return html`
103+
<uui-file-dropzone
104+
id="browse-dropzone"
105+
.accept=${props.accept}
106+
?multiple=${props.multiple}
107+
?disallow-folder-upload=${props.disallowFolderUpload}
108+
@change=${handleFileChange}
109+
label="Drop files here">
110+
Drop files here
111+
<uui-button
112+
style="margin-top: 9px;"
113+
@click=${handleBrowse}
114+
look="primary"
115+
label="Browse"></uui-button>
116+
</uui-file-dropzone>
117+
`;
118+
},
119+
parameters: {
120+
docs: {
121+
description: {
122+
story:
123+
'The browse method allows the user to select a file from their computer.',
124+
},
125+
source: {
126+
code: `
111127
const handleBrowse = () => {
112128
const dropzone = document.getElementById('browse-dropzone');
113129
dropzone.browse();
@@ -117,6 +133,7 @@ const handleBrowse = () => {
117133
Drop files here
118134
<uui-button style="margin-top: 9px;" @click="handleBrowse" look="primary">Browse</uui-button>
119135
</uui-file-dropzone>`,
136+
},
120137
},
121138
},
122139
};

0 commit comments

Comments
 (0)