@georapbox/files-dropzone-element 2.1.1 → 3.0.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/.editorconfig +15 -0
- package/.nvmrc +1 -0
- package/.prettierignore +6 -0
- package/.prettierrc +11 -0
- package/.vscode/extensions.json +8 -0
- package/.vscode/settings.json +8 -0
- package/.zed/settings.json +9 -0
- package/CHANGELOG.md +144 -0
- package/README.md +119 -103
- package/custom-elements-manifest.config.mjs +48 -0
- package/dist/custom-elements.json +509 -0
- package/dist/files-dropzone-defined.d.ts +1 -1
- package/dist/files-dropzone-defined.d.ts.map +1 -1
- package/dist/files-dropzone-defined.js +5 -5
- package/dist/files-dropzone-defined.js.map +4 -4
- package/dist/files-dropzone.d.ts +55 -68
- package/dist/files-dropzone.d.ts.map +1 -1
- package/dist/files-dropzone.js +5 -5
- package/dist/files-dropzone.js.map +4 -4
- package/dist/files-selector.d.ts +9 -0
- package/dist/files-selector.d.ts.map +1 -0
- package/dist/{utils/is-valid-file.d.ts → is-valid-file.d.ts} +1 -1
- package/dist/is-valid-file.d.ts.map +1 -0
- package/docs/css/main.css +190 -0
- package/docs/css/water.css +674 -0
- package/docs/favicon-32x32.png +0 -0
- package/docs/index.html +144 -0
- package/docs/js/main.js +154 -0
- package/docs/lib/files-dropzone.js +93 -0
- package/docs/lib/highlight/highlight-github.css +160 -0
- package/docs/lib/highlight/highlight.min.js +1160 -0
- package/eslint.config.mjs +42 -0
- package/package.json +52 -30
- package/scripts/build.mjs +32 -0
- package/scripts/copy-build-to-docs.mjs +17 -0
- package/scripts/dev.mjs +24 -0
- package/src/files-dropzone-defined.js +1 -1
- package/src/files-dropzone.js +70 -89
- package/test/files-dropzone-upgrade.test.js +39 -0
- package/test/files-dropzone.test.js +397 -0
- package/test/files-selector.test.js +96 -0
- package/test/is-valid-file.test.js +78 -0
- package/tsconfig.json +15 -0
- package/web-test-runner.config.js +13 -0
- package/dist/utils/files-selector.d.ts +0 -2
- package/dist/utils/files-selector.d.ts.map +0 -1
- package/dist/utils/is-valid-file.d.ts.map +0 -1
- /package/src/{utils/files-selector.js → files-selector.js} +0 -0
- /package/src/{utils/is-valid-file.js → is-valid-file.js} +0 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import { elementUpdated, expect, fixture, fixtureCleanup, html } from '@open-wc/testing';
|
|
2
|
+
import sinon from 'sinon';
|
|
3
|
+
import { FilesDropzone } from '../src/files-dropzone.js';
|
|
4
|
+
|
|
5
|
+
FilesDropzone.define();
|
|
6
|
+
|
|
7
|
+
describe('files-dropzone', () => {
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
fixtureCleanup();
|
|
10
|
+
sinon.restore();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe('accessibility', () => {
|
|
14
|
+
it('passes accessibility test if enabled', async () => {
|
|
15
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
16
|
+
await expect(el).to.be.accessible();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('passes accessibility test if disabled', async () => {
|
|
20
|
+
const el = await fixture(html`<files-dropzone disabled></files-dropzone>`);
|
|
21
|
+
await expect(el).to.be.accessible();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('dropzone should have attribute role="button"', async () => {
|
|
25
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
26
|
+
const dropzone = el.shadowRoot.querySelector('[part="dropzone"]');
|
|
27
|
+
expect(dropzone).to.have.attribute('role', 'button');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('dropzone should have attribute tabindex="0" if enabled', async () => {
|
|
31
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
32
|
+
const dropzone = el.shadowRoot.querySelector('[part="dropzone"]');
|
|
33
|
+
expect(dropzone).to.have.attribute('tabindex', '0');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('dropzone should have attribute tabindex="0" if enabled', async () => {
|
|
37
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
38
|
+
const dropzone = el.shadowRoot.querySelector('[part="dropzone"]');
|
|
39
|
+
expect(dropzone).to.have.attribute('tabindex', '0');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('dropzone should not have attribute tabindex if disabled', async () => {
|
|
43
|
+
const el = await fixture(html`<files-dropzone disabled></files-dropzone>`);
|
|
44
|
+
const dropzone = el.shadowRoot.querySelector('[part="dropzone"]');
|
|
45
|
+
expect(dropzone).to.not.have.attribute('tabindex');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('dropzone should have aria-disabled="false" if enabled', async () => {
|
|
49
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
50
|
+
const dropzone = el.shadowRoot.querySelector('[part="dropzone"]');
|
|
51
|
+
expect(dropzone).to.have.attribute('aria-disabled', 'false');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('dropzone should have aria-disabled="true" if disabled', async () => {
|
|
55
|
+
const el = await fixture(html`<files-dropzone disabled></files-dropzone>`);
|
|
56
|
+
const dropzone = el.shadowRoot.querySelector('[part="dropzone"]');
|
|
57
|
+
expect(dropzone).to.have.attribute('aria-disabled', 'true');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('slots', () => {
|
|
62
|
+
it('has a default slot', async () => {
|
|
63
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
64
|
+
const slot = el.shadowRoot.querySelector('slot');
|
|
65
|
+
expect(slot).to.exist;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('has a default slot with text', async () => {
|
|
69
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
70
|
+
const slot = el.shadowRoot.querySelector('slot');
|
|
71
|
+
expect(slot.assignedNodes({ flatten: true })[0].textContent).to.contain(
|
|
72
|
+
"Drag 'n' drop files here, or click to select files"
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('changes content of the default slot', async () => {
|
|
77
|
+
const el = await fixture(html`<files-dropzone>foo</files-dropzone>`);
|
|
78
|
+
const slot = el.shadowRoot.querySelector('slot');
|
|
79
|
+
expect(slot.assignedNodes({ flatten: true })[0].textContent).to.contain('foo');
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe('attributes/properties', () => {
|
|
84
|
+
// accept property
|
|
85
|
+
it('property accept is "image/*" when attribute accept is "image/*"', async () => {
|
|
86
|
+
const el = await fixture(html`<files-dropzone accept="image/*"></files-dropzone>`);
|
|
87
|
+
expect(el.accept).to.equal('image/*');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('property accept is empty string when attribute accept is not set', async () => {
|
|
91
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
92
|
+
expect(el.accept).to.equal('');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('attribute accept is "image/*" when property accept is "image/*"', async () => {
|
|
96
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
97
|
+
el.accept = 'image/*';
|
|
98
|
+
await elementUpdated(el);
|
|
99
|
+
expect(el.getAttribute('accept')).to.equal('image/*');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// disabled property
|
|
103
|
+
it('property disabled is true when attribute disabled is set', async () => {
|
|
104
|
+
const el = await fixture(html`<files-dropzone disabled></files-dropzone>`);
|
|
105
|
+
expect(el.disabled).to.be.true;
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('property disabled is false when attribute disabled is not set', async () => {
|
|
109
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
110
|
+
expect(el.disabled).to.be.false;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('attribute disabled is set when property disabled is set', async () => {
|
|
114
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
115
|
+
el.disabled = true;
|
|
116
|
+
await elementUpdated(el);
|
|
117
|
+
expect(el.hasAttribute('disabled')).to.be.true;
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('attribute disabled is removed when property disabled is false', async () => {
|
|
121
|
+
const el = await fixture(html`<files-dropzone disabled></files-dropzone>`);
|
|
122
|
+
el.disabled = false;
|
|
123
|
+
await elementUpdated(el);
|
|
124
|
+
expect(el.hasAttribute('disabled')).to.be.false;
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// maxFiles property
|
|
128
|
+
it('property maxFiles is 5 when attribute max-files is "5"', async () => {
|
|
129
|
+
const el = await fixture(html`<files-dropzone max-files="5"></files-dropzone>`);
|
|
130
|
+
expect(el.maxFiles).to.equal(5);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('property maxFiles is Infinity when attribute max-files is not set', async () => {
|
|
134
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
135
|
+
expect(el.maxFiles).to.equal(Infinity);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('property maxFiles is Infinity when attribute max-files is "0"', async () => {
|
|
139
|
+
const el = await fixture(html`<files-dropzone max-files="0"></files-dropzone>`);
|
|
140
|
+
expect(el.maxFiles).to.equal(Infinity);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('property maxFiles is Infinity when attribute max-files is "-1"', async () => {
|
|
144
|
+
const el = await fixture(html`<files-dropzone max-files="-1"></files-dropzone>`);
|
|
145
|
+
expect(el.maxFiles).to.equal(Infinity);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('property maxFiles is Infinity when attribute max-files is "foo"', async () => {
|
|
149
|
+
const el = await fixture(html`<files-dropzone max-files="foo"></files-dropzone>`);
|
|
150
|
+
expect(el.maxFiles).to.equal(Infinity);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('attribute max-files is "5" when property maxFiles is 5', async () => {
|
|
154
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
155
|
+
el.maxFiles = 5;
|
|
156
|
+
await elementUpdated(el);
|
|
157
|
+
expect(el.getAttribute('max-files')).to.equal('5');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('attribute max-files is "-1" when property maxFiles is -1', async () => {
|
|
161
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
162
|
+
el.maxFiles = -1;
|
|
163
|
+
await elementUpdated(el);
|
|
164
|
+
expect(el.getAttribute('max-files')).to.equal('-1');
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('attribute max-files is "undefined" when property maxFiles is undefined', async () => {
|
|
168
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
169
|
+
el.maxFiles = void 0;
|
|
170
|
+
await elementUpdated(el);
|
|
171
|
+
expect(el.getAttribute('max-files')).to.equal('undefined');
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// maxSize property
|
|
175
|
+
it('property maxSize is Infinity when attribute max-size is not set', async () => {
|
|
176
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
177
|
+
expect(el.maxSize).to.equal(Infinity);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('property maxSize is 1024 when attribute max-size is "1024"', async () => {
|
|
181
|
+
const el = await fixture(html`<files-dropzone max-size="1024"></files-dropzone>`);
|
|
182
|
+
expect(el.maxSize).to.equal(1024);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('property maxSize is 0 when attribute max-size is "0"', async () => {
|
|
186
|
+
const el = await fixture(html`<files-dropzone max-size="0"></files-dropzone>`);
|
|
187
|
+
expect(el.maxSize).to.equal(0);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('property maxSize is -1 when attribute max-size is "-1"', async () => {
|
|
191
|
+
const el = await fixture(html`<files-dropzone max-size="-1"></files-dropzone>`);
|
|
192
|
+
expect(el.maxSize).to.equal(-1);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('attribute max-size is "1024" when property maxSize is 1024', async () => {
|
|
196
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
197
|
+
el.maxSize = 1024;
|
|
198
|
+
await elementUpdated(el);
|
|
199
|
+
expect(el.getAttribute('max-size')).to.equal('1024');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('attribute max-size is "undefined" when property maxSize is undefined', async () => {
|
|
203
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
204
|
+
el.maxSize = void 0;
|
|
205
|
+
await elementUpdated(el);
|
|
206
|
+
expect(el.getAttribute('max-size')).to.equal('undefined');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('attribute max-size is "null" when property maxSize is null', async () => {
|
|
210
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
211
|
+
el.maxSize = null;
|
|
212
|
+
await elementUpdated(el);
|
|
213
|
+
expect(el.getAttribute('max-size')).to.equal('null');
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// minSize property
|
|
217
|
+
it('property minSize is 0 when attribute min-size is not set', async () => {
|
|
218
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
219
|
+
expect(el.minSize).to.equal(0);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('property minSize is 1024 when attribute min-size is "1024"', async () => {
|
|
223
|
+
const el = await fixture(html`<files-dropzone min-size="1024"></files-dropzone>`);
|
|
224
|
+
expect(el.minSize).to.equal(1024);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('property minSize is 0 when attribute min-size is "0"', async () => {
|
|
228
|
+
const el = await fixture(html`<files-dropzone min-size="0"></files-dropzone>`);
|
|
229
|
+
expect(el.minSize).to.equal(0);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('property minSize is -1 when attribute min-size is "-1"', async () => {
|
|
233
|
+
const el = await fixture(html`<files-dropzone min-size="-1"></files-dropzone>`);
|
|
234
|
+
expect(el.minSize).to.equal(-1);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('attribute min-size is "1024" when property minSize is 1024', async () => {
|
|
238
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
239
|
+
el.minSize = 1024;
|
|
240
|
+
await elementUpdated(el);
|
|
241
|
+
expect(el.getAttribute('min-size')).to.equal('1024');
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('attribute min-size is "undefined" when property minSize is undefined', async () => {
|
|
245
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
246
|
+
el.minSize = void 0;
|
|
247
|
+
await elementUpdated(el);
|
|
248
|
+
expect(el.getAttribute('min-size')).to.equal('undefined');
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('attribute min-size is "null" when property minSize is null', async () => {
|
|
252
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
253
|
+
el.minSize = null;
|
|
254
|
+
await elementUpdated(el);
|
|
255
|
+
expect(el.getAttribute('min-size')).to.equal('null');
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// multiple property
|
|
259
|
+
it('property multiple is true when attribute multiple is set', async () => {
|
|
260
|
+
const el = await fixture(html`<files-dropzone multiple></files-dropzone>`);
|
|
261
|
+
expect(el.multiple).to.be.true;
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('property multiple is false when attribute multiple is not set', async () => {
|
|
265
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
266
|
+
expect(el.multiple).to.be.false;
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('attribute multiple is set when property multiple is set', async () => {
|
|
270
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
271
|
+
el.multiple = true;
|
|
272
|
+
await elementUpdated(el);
|
|
273
|
+
expect(el.hasAttribute('multiple')).to.be.true;
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it('attribute multiple is removed when property multiple is false', async () => {
|
|
277
|
+
const el = await fixture(html`<files-dropzone multiple></files-dropzone>`);
|
|
278
|
+
el.multiple = false;
|
|
279
|
+
await elementUpdated(el);
|
|
280
|
+
expect(el.hasAttribute('multiple')).to.be.false;
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// autoFocus property
|
|
284
|
+
it('property autoFocus is true when attribute auto-focus is set', async () => {
|
|
285
|
+
const el = await fixture(html`<files-dropzone auto-focus></files-dropzone>`);
|
|
286
|
+
expect(el.autoFocus).to.be.true;
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('property autoFocus is false when attribute auto-focus is not set', async () => {
|
|
290
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
291
|
+
expect(el.autoFocus).to.be.false;
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('attribute auto-focus is set when property autoFocus is set', async () => {
|
|
295
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
296
|
+
el.autoFocus = true;
|
|
297
|
+
await elementUpdated(el);
|
|
298
|
+
expect(el.hasAttribute('auto-focus')).to.be.true;
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('attribute auto-focus is removed when property autoFocus is false', async () => {
|
|
302
|
+
const el = await fixture(html`<files-dropzone auto-focus></files-dropzone>`);
|
|
303
|
+
el.autoFocus = false;
|
|
304
|
+
await elementUpdated(el);
|
|
305
|
+
expect(el.hasAttribute('auto-focus')).to.be.false;
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('should be focused when property autoFocus is true', async () => {
|
|
309
|
+
const el = await fixture(html`<files-dropzone auto-focus></files-dropzone>`);
|
|
310
|
+
expect(el.matches(':focus')).to.be.true;
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// noStyle property
|
|
314
|
+
it('property noStyle is true when attribute no-style is set', async () => {
|
|
315
|
+
const el = await fixture(html`<files-dropzone no-style></files-dropzone>`);
|
|
316
|
+
expect(el.noStyle).to.be.true;
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('property noStyle is false when attribute no-style is not set', async () => {
|
|
320
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
321
|
+
expect(el.noStyle).to.be.false;
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it('attribute no-style is set when property noStyle is set', async () => {
|
|
325
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
326
|
+
el.noStyle = true;
|
|
327
|
+
await elementUpdated(el);
|
|
328
|
+
expect(el.hasAttribute('no-style')).to.be.true;
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it('attribute no-style is removed when property noStyle is false', async () => {
|
|
332
|
+
const el = await fixture(html`<files-dropzone no-style></files-dropzone>`);
|
|
333
|
+
el.noStyle = false;
|
|
334
|
+
await elementUpdated(el);
|
|
335
|
+
expect(el.hasAttribute('no-style')).to.be.false;
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
describe('Events', () => {
|
|
340
|
+
// dropzone-dragenter
|
|
341
|
+
it('fires dropzone-dragenter event when dragenter event is fired', async () => {
|
|
342
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
343
|
+
const dropzone = el.shadowRoot.querySelector('[part="dropzone"]');
|
|
344
|
+
const spy = sinon.spy();
|
|
345
|
+
el.addEventListener('dropzone-dragenter', spy);
|
|
346
|
+
dropzone.dispatchEvent(new DragEvent('dragenter'));
|
|
347
|
+
sinon.assert.calledOnce(spy);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it('it does not fire dropzone-dragenter event when dragenter event is fired and disabled is set', async () => {
|
|
351
|
+
const el = await fixture(html`<files-dropzone disabled></files-dropzone>`);
|
|
352
|
+
const dropzone = el.shadowRoot.querySelector('[part="dropzone"]');
|
|
353
|
+
const spy = sinon.spy();
|
|
354
|
+
el.addEventListener('dropzone-dragenter', spy);
|
|
355
|
+
dropzone.dispatchEvent(new DragEvent('dragenter'));
|
|
356
|
+
sinon.assert.notCalled(spy);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
// dropzone-dragover
|
|
360
|
+
it('fires dropzone-dragover event when dragover event is fired', async () => {
|
|
361
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
362
|
+
const dropzone = el.shadowRoot.querySelector('[part="dropzone"]');
|
|
363
|
+
const spy = sinon.spy();
|
|
364
|
+
el.addEventListener('dropzone-dragover', spy);
|
|
365
|
+
dropzone.dispatchEvent(new DragEvent('dragover', { dataTransfer: new DataTransfer() }));
|
|
366
|
+
sinon.assert.calledOnce(spy);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('it does not fire dropzone-dragover event when dragover event is fired and disabled is set', async () => {
|
|
370
|
+
const el = await fixture(html`<files-dropzone disabled></files-dropzone>`);
|
|
371
|
+
const dropzone = el.shadowRoot.querySelector('[part="dropzone"]');
|
|
372
|
+
const spy = sinon.spy();
|
|
373
|
+
el.addEventListener('dropzone-dragover', spy);
|
|
374
|
+
dropzone.dispatchEvent(new DragEvent('dragover', { dataTransfer: new DataTransfer() }));
|
|
375
|
+
sinon.assert.notCalled(spy);
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
// dropzone-dragleave
|
|
379
|
+
it('fires dropzone-dragleave event when dragleave event is fired', async () => {
|
|
380
|
+
const el = await fixture(html`<files-dropzone></files-dropzone>`);
|
|
381
|
+
const dropzone = el.shadowRoot.querySelector('[part="dropzone"]');
|
|
382
|
+
const spy = sinon.spy();
|
|
383
|
+
el.addEventListener('dropzone-dragleave', spy);
|
|
384
|
+
dropzone.dispatchEvent(new DragEvent('dragleave'));
|
|
385
|
+
sinon.assert.calledOnce(spy);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('it does not fire dropzone-dragleave event when dragleave event is fired and disabled is set', async () => {
|
|
389
|
+
const el = await fixture(html`<files-dropzone disabled></files-dropzone>`);
|
|
390
|
+
const dropzone = el.shadowRoot.querySelector('[part="dropzone"]');
|
|
391
|
+
const spy = sinon.spy();
|
|
392
|
+
el.addEventListener('dropzone-dragleave', spy);
|
|
393
|
+
dropzone.dispatchEvent(new DragEvent('dragleave'));
|
|
394
|
+
sinon.assert.notCalled(spy);
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
});
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { expect } from '@open-wc/testing';
|
|
2
|
+
import { getFilesFromEvent } from '../src/files-selector.js';
|
|
3
|
+
|
|
4
|
+
function createFile(name, size, type) {
|
|
5
|
+
const file = new File([], name, { type });
|
|
6
|
+
Object.defineProperty(file, 'size', {
|
|
7
|
+
get() {
|
|
8
|
+
return size;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
return file;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function createDataTransferWithFiles(files = []) {
|
|
15
|
+
return {
|
|
16
|
+
dataTransfer: {
|
|
17
|
+
files,
|
|
18
|
+
items: files.map(file => ({
|
|
19
|
+
kind: 'file',
|
|
20
|
+
size: file.size,
|
|
21
|
+
type: file.type,
|
|
22
|
+
getAsFile: () => file,
|
|
23
|
+
webkitGetAsEntry: () => ({
|
|
24
|
+
isFile: true,
|
|
25
|
+
isDirectory: false,
|
|
26
|
+
file: callback => callback(file),
|
|
27
|
+
createReader: () => ({
|
|
28
|
+
readEntries: callback => callback([])
|
|
29
|
+
}),
|
|
30
|
+
fullPath: file.name,
|
|
31
|
+
name: file.name
|
|
32
|
+
})
|
|
33
|
+
})),
|
|
34
|
+
types: ['Files']
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function createFileList(files = []) {
|
|
40
|
+
return {
|
|
41
|
+
target: {
|
|
42
|
+
files
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
describe('files-dropzone/files-selector', () => {
|
|
48
|
+
it('should return an empty array if no files are selected', async () => {
|
|
49
|
+
const event = createDataTransferWithFiles([]);
|
|
50
|
+
expect(await getFilesFromEvent(event)).to.be.empty;
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('shoule return an array of files if files are selected', async () => {
|
|
54
|
+
const files = [createFile('foo.txt', 1024, 'text/plain'), createFile('bar.png', 2048, 'text/png')];
|
|
55
|
+
const event = createDataTransferWithFiles(files);
|
|
56
|
+
expect(await getFilesFromEvent(event)).to.deep.equal(files);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should add a `path` property to the file', async () => {
|
|
60
|
+
const files = [createFile('foo.txt', 1024, 'text/plain'), createFile('bar.png', 2048, 'text/png')];
|
|
61
|
+
const event = createDataTransferWithFiles(files);
|
|
62
|
+
for (const file of await getFilesFromEvent(event)) {
|
|
63
|
+
expect(file).to.have.property('path');
|
|
64
|
+
expect(file.path).to.equal(file.name);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should add `type` property to the file if not present but file extension exists', async () => {
|
|
69
|
+
const files = [createFile('foo.txt', 1024, ''), createFile('bar.png', 2048, '')];
|
|
70
|
+
const event = createDataTransferWithFiles(files);
|
|
71
|
+
const filesFromEvent = await getFilesFromEvent(event);
|
|
72
|
+
expect(filesFromEvent[0]).to.have.property('type', 'text/plain');
|
|
73
|
+
expect(filesFromEvent[1]).to.have.property('type', 'image/png');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should ignore thumbnail cache files for macOS and Windows', async () => {
|
|
77
|
+
const files = [createFile('.DS_Store', 1024, ''), createFile('Thumbs.db', 2048, '')];
|
|
78
|
+
const event = createDataTransferWithFiles(files);
|
|
79
|
+
expect(await getFilesFromEvent(event)).to.be.empty;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should ignore non files', async () => {
|
|
83
|
+
const event = {
|
|
84
|
+
dataTransfer: {
|
|
85
|
+
items: [{ kind: 'string', type: 'text/plain' }]
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
expect(await getFilesFromEvent(event)).to.be.empty;
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should get files from a FileList', async () => {
|
|
92
|
+
const files = [createFile('foo.txt', 1024, 'text/plain'), createFile('bar.png', 2048, 'text/png')];
|
|
93
|
+
const event = createFileList(files);
|
|
94
|
+
expect(await getFilesFromEvent(event)).to.deep.equal(files);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { expect } from '@open-wc/testing';
|
|
2
|
+
import { isValidFile } from '../src/is-valid-file.js';
|
|
3
|
+
|
|
4
|
+
describe('files-dropzone/is-valid-file', () => {
|
|
5
|
+
it('file is valid if no file type specifiers are provided', async () => {
|
|
6
|
+
const file = new File(['foo'], 'foo.txt', {
|
|
7
|
+
type: 'text/plain'
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
expect(isValidFile(file)).to.be.true;
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('file is valid if MIME type matches accepted MIME types', async () => {
|
|
14
|
+
const file = new File(['foo'], 'foo.txt', {
|
|
15
|
+
type: 'text/plain'
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
expect(isValidFile(file, 'text/plain')).to.be.true;
|
|
19
|
+
expect(isValidFile(file, 'text/*')).to.be.true;
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('file is valid if MIME type matches accepted MIME types (multiple)', async () => {
|
|
23
|
+
const file = new File(['foo'], 'foo.txt', {
|
|
24
|
+
type: 'text/plain'
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
expect(isValidFile(file, 'text/plain, text/html')).to.be.true;
|
|
28
|
+
expect(isValidFile(file, 'text/plain, text/*')).to.be.true;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("file is valid if file's extension matches accepted extension", async () => {
|
|
32
|
+
const file = new File(['foo'], 'foo.txt', {
|
|
33
|
+
type: 'text/plain'
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
expect(isValidFile(file, '.txt')).to.be.true;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("file is valid if file's extension matches accepted extension (multiple)", async () => {
|
|
40
|
+
const file = new File(['foo'], 'foo.txt', {
|
|
41
|
+
type: 'text/plain'
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
expect(isValidFile(file, '.txt, .html')).to.be.true;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('file is invalid if MIME type does not match accepted MIME types', async () => {
|
|
48
|
+
const file = new File(['foo'], 'foo.txt', {
|
|
49
|
+
type: 'text/plain'
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
expect(isValidFile(file, 'image/*')).to.be.false;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('file is invalid if MIME type does not match accepted MIME types (multiple)', async () => {
|
|
56
|
+
const file = new File(['foo'], 'foo.txt', {
|
|
57
|
+
type: 'text/plain'
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(isValidFile(file, 'image/*, text/html')).to.be.false;
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("file is invalid if file's extension does not match accepted extension", async () => {
|
|
64
|
+
const file = new File(['foo'], 'foo.txt', {
|
|
65
|
+
type: 'text/plain'
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
expect(isValidFile(file, '.html')).to.be.false;
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('file is invalid if accepted file type specifiers is an invalid value', async () => {
|
|
72
|
+
const file = new File(['foo'], 'foo.txt', {
|
|
73
|
+
type: 'text/plain'
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
expect(isValidFile(file, 'invalid-mime-type')).to.be.false;
|
|
77
|
+
});
|
|
78
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"include": ["src/**/*"],
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"strict": true,
|
|
5
|
+
"target": "es2022",
|
|
6
|
+
"module": "preserve",
|
|
7
|
+
"moduleResolution": "bundler",
|
|
8
|
+
"allowJs": true,
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"emitDeclarationOnly": true,
|
|
11
|
+
"declarationMap": true,
|
|
12
|
+
"rootDir": "src",
|
|
13
|
+
"outDir": "dist"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { playwrightLauncher } from '@web/test-runner-playwright';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
rootDir: '.',
|
|
5
|
+
files: 'test/*.test.js',
|
|
6
|
+
concurrentBrowsers: 3,
|
|
7
|
+
nodeResolve: true,
|
|
8
|
+
browsers: [
|
|
9
|
+
playwrightLauncher({ product: 'chromium' }),
|
|
10
|
+
playwrightLauncher({ product: 'firefox' }),
|
|
11
|
+
playwrightLauncher({ product: 'webkit' })
|
|
12
|
+
]
|
|
13
|
+
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"files-selector.d.ts","sourceRoot":"","sources":["../../src/utils/files-selector.js"],"names":[],"mappings":"AAiRO,uCAHI,GAAC,GACC,OAAO,CAAC,IAAI,EAAE,CAAC,CAU3B"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"is-valid-file.d.ts","sourceRoot":"","sources":["../../src/utils/is-valid-file.js"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,kCAJW,IAAI,2BACJ,MAAM,GACJ,OAAO,CAqCnB"}
|
|
File without changes
|
|
File without changes
|