@dile/crud 2.8.8 → 2.9.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.
@@ -0,0 +1,35 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import './ajax.js';
3
+
4
+ describe('dile-ajax', () => {
5
+ afterEach(() => {
6
+ document.body.innerHTML = '';
7
+ });
8
+
9
+ it('appends arrays of files as multiple "key[]" entries when sending data as FormData', async () => {
10
+ const el = document.createElement('dile-ajax');
11
+ document.body.appendChild(el);
12
+ await el.updateComplete;
13
+
14
+ const files = [new File(['a'], 'a.txt'), new File(['b'], 'b.txt')];
15
+ el.data = { title: 'Docs', files, empty: null };
16
+ el._prepareFormData();
17
+
18
+ expect(el.formData.get('title')).toBe('Docs');
19
+ expect(el.formData.getAll('files[]').map(file => file.name)).toEqual(['a.txt', 'b.txt']);
20
+ expect(el.formData.has('files')).toBe(false);
21
+ expect(el.formData.has('empty')).toBe(false);
22
+ });
23
+
24
+ it('keeps sending other arrays as a single entry', async () => {
25
+ const el = document.createElement('dile-ajax');
26
+ document.body.appendChild(el);
27
+ await el.updateComplete;
28
+
29
+ el.data = { tags: [1, 2, 3] };
30
+ el._prepareFormData();
31
+
32
+ expect(el.formData.get('tags')).toBe('1,2,3');
33
+ expect(el.formData.has('tags[]')).toBe(false);
34
+ });
35
+ });
@@ -192,12 +192,22 @@ export class DileAjax extends DileAxios(DileI18nMixin(LitElement)) {
192
192
  this.formData = new FormData();
193
193
  }
194
194
 
195
+ _isFileArray(value) {
196
+ return Array.isArray(value) && value.some(item => item instanceof Blob);
197
+ }
198
+
195
199
  _addObjectToFormData(data) {
196
200
  if (!this.formData) {
197
- this.createFormData();
201
+ this._createFormData();
198
202
  }
199
203
  for (const key in data) {
200
- if (data[key] !== null && data[key] !== undefined) {
204
+ if (this._isFileArray(data[key])) {
205
+ data[key].forEach(item => {
206
+ if (item !== null && item !== undefined) {
207
+ this.formData.append(`${key}[]`, item);
208
+ }
209
+ });
210
+ } else if (data[key] !== null && data[key] !== undefined) {
201
211
  this.formData.append(key, data[key]);
202
212
  }
203
213
  }
@@ -0,0 +1,32 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import './file-uploader.js';
3
+
4
+ describe('dile-file-uploader', () => {
5
+ afterEach(() => {
6
+ document.body.innerHTML = '';
7
+ });
8
+
9
+ async function renderUploader(html) {
10
+ document.body.innerHTML = html;
11
+ const el = document.body.querySelector('dile-file-uploader');
12
+ await el.updateComplete;
13
+ const form = el.shadowRoot.querySelector('dile-file-uploader-form');
14
+ await form.updateComplete;
15
+ const dropFile = form.shadowRoot.querySelector('dile-drop-file');
16
+ await dropFile.updateComplete;
17
+ return { el, form, dropFile };
18
+ }
19
+
20
+ it('uses a single file field named "file" by default', async () => {
21
+ const { dropFile } = await renderUploader('<dile-file-uploader endpoint="/upload"></dile-file-uploader>');
22
+ expect(dropFile.getAttribute('name')).toBe('file');
23
+ expect(dropFile.multiple).toBe(false);
24
+ });
25
+
26
+ it('uses a multiple field named "files" when multiple is set', async () => {
27
+ const { form, dropFile } = await renderUploader('<dile-file-uploader endpoint="/upload" multiple></dile-file-uploader>');
28
+ expect(dropFile.getAttribute('name')).toBe('files');
29
+ expect(dropFile.multiple).toBe(true);
30
+ expect(form.getData()).toEqual({ files: [] });
31
+ });
32
+ });
@@ -20,6 +20,7 @@ export class DileFileUploader extends DileI18nMixin(LitElement) {
20
20
  saveLabel: { type: String },
21
21
  allowedExtensions: { type: Array },
22
22
  responseAdapter: { type: Object },
23
+ multiple: { type: Boolean },
23
24
  };
24
25
  }
25
26
 
@@ -27,6 +28,7 @@ export class DileFileUploader extends DileI18nMixin(LitElement) {
27
28
  super();
28
29
  this.responseAdapter = new ResponseApiAdapter();
29
30
  this.allowedExtensions = ['*'];
31
+ this.multiple = false;
30
32
  }
31
33
 
32
34
  render() {
@@ -45,6 +47,7 @@ export class DileFileUploader extends DileI18nMixin(LitElement) {
45
47
  id="form"
46
48
  label="${this.selectLabelComputed(this.selectFileLabel, this.translations)}"
47
49
  .allowedExtensions=${this.allowedExtensions}
50
+ ?multiple=${this.multiple}
48
51
  .translations=${this.translations}
49
52
  ></dile-file-uploader-form>
50
53
  </dile-ajax-form>
@@ -52,7 +55,13 @@ export class DileFileUploader extends DileI18nMixin(LitElement) {
52
55
  }
53
56
 
54
57
  selectLabelComputed(label, translations) {
55
- return label ? label : translations?.select_file_label ? translations.select_file_label : 'Select a file';
58
+ if (label) {
59
+ return label;
60
+ }
61
+ if (this.multiple) {
62
+ return translations?.select_files_label ? translations.select_files_label : 'Select files';
63
+ }
64
+ return translations?.select_file_label ? translations.select_file_label : 'Select a file';
56
65
  }
57
66
 
58
67
  saveLabelComputed(label, translations) {
@@ -19,18 +19,20 @@ export class DileFileUploaderForm extends DileForm(LitElement) {
19
19
  label: { type: String },
20
20
  allowedExtensions: { type: Array },
21
21
  translations: { type: Object },
22
+ multiple: { type: Boolean },
22
23
  };
23
24
  }
24
25
 
25
26
  render() {
26
27
  return html`
27
28
  <dile-drop-file
28
- name="file"
29
+ name="${this.multiple ? 'files' : 'file'}"
29
30
  label="${this.label}"
30
31
  id="dropfile"
32
+ ?multiple=${this.multiple}
31
33
  .allowedExtensions=${this.allowedExtensions}
32
- dropLabel="${this.translations.file_drop}"
33
- buttonLabel="${this.translations.select_file}"
34
+ dropLabel="${this.multiple ? this.translations.files_drop : this.translations.file_drop}"
35
+ buttonLabel="${this.multiple ? this.translations.select_files : this.translations.select_file}"
34
36
  selectedFileLabel="${this.translations.selected_file}"
35
37
  extensionErrorMessage="${this.translations.extension_allowed}"
36
38
  ></dile-drop-file>
package/lib/i18n/en.js CHANGED
@@ -51,6 +51,9 @@ export const translations = {
51
51
  file_drop: "Drop here your file",
52
52
  select_file: "Select file",
53
53
  selected_file: "Selected file",
54
+ select_files_label: "Select files",
55
+ files_drop: "Drop here your files",
56
+ select_files: "Select files",
54
57
  extension_allowed: "Only this file extensions are allowed: ",
55
58
  image_empty_file: "The selected file is empty",
56
59
  image_invalid_file: "The selected file is not a valid image or it's corrupted",
package/lib/i18n/es.js CHANGED
@@ -51,6 +51,9 @@ export const translations = {
51
51
  file_drop: "Suelta aquí el archivo",
52
52
  select_file: "Seleccionar archivo",
53
53
  selected_file: "Archivo seleccionado",
54
+ select_files_label: "Selecciona archivos",
55
+ files_drop: "Suelta aquí los archivos",
56
+ select_files: "Seleccionar archivos",
54
57
  extension_allowed: "Solo se permiten estas extensiones: ",
55
58
  image_empty_file: "El archivo seleccionado está vacío",
56
59
  image_invalid_file: "El archivo seleccionado no es una imagen válida o está corrupto",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dile/crud",
3
- "version": "2.8.8",
3
+ "version": "2.9.2",
4
4
  "description": "Components to create a generic crud system based on Web Components and Lit",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -25,12 +25,12 @@
25
25
  },
26
26
  "homepage": "https://dile-components.com/",
27
27
  "dependencies": {
28
- "@dile/ui": "^3.15.0",
28
+ "@dile/ui": "^3.16.2",
29
29
  "axios": "^1.19.0",
30
30
  "lit": "^2.7.0 || ^3.0.0"
31
31
  },
32
32
  "publishConfig": {
33
33
  "access": "public"
34
34
  },
35
- "gitHead": "5ef91e1892e270fade848c64082184fa3fbddb53"
35
+ "gitHead": "0f1a6553d287b706db6f42c7001a25fdebc3bd3a"
36
36
  }