Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add mode for selecting directories in file dialog #8057

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions dev/upload.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@
});

upload.files = files;

const selectionMode = document.querySelector('#selection-mode');
selectionMode.addEventListener('value-changed', (e) => {
upload.directory = e.detail.value === 'directory';
});
</script>
</head>

Expand All @@ -62,5 +67,12 @@
<vaadin-radio-button value="rejected" label="Rejected"></vaadin-radio-button>
<vaadin-radio-button value="error" label="Server error"></vaadin-radio-button>
</vaadin-radio-group>

<br />

<vaadin-radio-group label="Selection mode" theme="vertical" id="selection-mode">
<vaadin-radio-button value="files" checked label="Files"></vaadin-radio-button>
<vaadin-radio-button value="directory" label="Directory"></vaadin-radio-button>
</vaadin-radio-group>
</body>
</html>
1 change: 1 addition & 0 deletions packages/upload/src/vaadin-lit-upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class Upload extends UploadMixin(ElementMixin(ThemableMixin(PolylitMixin(LitElem
accept="${this.accept}"
?multiple="${this._isMultiple(this.maxFiles)}"
capture="${ifDefined(this.capture)}"
?webkitdirectory="${this.directory}"
/>
`;
}
Expand Down
20 changes: 20 additions & 0 deletions packages/upload/src/vaadin-upload-mixin.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ export interface UploadI18n {
one: string;
many: string;
};
addDirectories: {
one: string;
};
error: {
tooManyFiles: string;
fileIsTooBig: string;
Expand Down Expand Up @@ -184,6 +187,20 @@ export declare class UploadMixinClass {
*/
capture: string | null | undefined;

/**
* In directory mode, the user can select a directory instead of files.
* When selecting a directory, all files in the directory will be added
* to the upload list. Files are still filtered by the `accept` filter,
* and any non-matching files will be rejected.
*
* Note that this only allows selecting a single directory, and that
* selecting files is not supported in this mode. Browsers may request
* a confirmation from the user before allowing to upload a directory.
* In this mode it is still possible to add a combination of files and
* directories using drag and drop.
*/
directory: string;
sissbruecker marked this conversation as resolved.
Show resolved Hide resolved

/**
* The object used to localize this component.
* For changing the default localization, change the entire
Expand All @@ -201,6 +218,9 @@ export declare class UploadMixinClass {
* one: 'Upload File...',
* many: 'Upload Files...'
* },
* addDirectories: {
* one: 'Upload Directory...',
* },
* error: {
* tooManyFiles: 'Too Many Files.',
* fileIsTooBig: 'File is Too Big.',
Expand Down
31 changes: 29 additions & 2 deletions packages/upload/src/vaadin-upload-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,23 @@ export const UploadMixin = (superClass) =>
*/
capture: String,

/**
* In directory mode, the user can select a directory instead of files.
* When selecting a directory, all files in the directory will be added
* to the upload list. Files are still filtered by the `accept` filter,
* and any non-matching files will be rejected.
*
* Note that this only allows selecting a single directory, and that
* selecting files is not supported in this mode. Browsers may request
* a confirmation from the user before allowing to upload a directory.
* In this mode it is still possible to add a combination of files and
* directories using drag and drop.
*/
directory: {
type: Boolean,
value: false,
},

/**
* The object used to localize this component.
* For changing the default localization, change the entire
Expand All @@ -279,6 +296,9 @@ export const UploadMixin = (superClass) =>
* one: 'Upload File...',
* many: 'Upload Files...'
* },
* addDirectories: {
* one: 'Upload Directory...',
* },
* error: {
* tooManyFiles: 'Too Many Files.',
* fileIsTooBig: 'File is Too Big.',
Expand Down Expand Up @@ -334,6 +354,9 @@ export const UploadMixin = (superClass) =>
one: 'Upload File...',
many: 'Upload Files...',
},
addDirectories: {
one: 'Upload Directory...',
},
error: {
tooManyFiles: 'Too Many Files.',
fileIsTooBig: 'File is Too Big.',
Expand Down Expand Up @@ -392,7 +415,7 @@ export const UploadMixin = (superClass) =>

static get observers() {
return [
'__updateAddButton(_addButton, maxFiles, i18n, maxFilesReached)',
'__updateAddButton(_addButton, maxFiles, i18n, maxFilesReached, directory)',
'__updateDropLabel(_dropLabel, maxFiles, i18n)',
'__updateFileList(_fileList, files, i18n)',
'__updateMaxFilesReached(maxFiles, files)',
Expand Down Expand Up @@ -518,7 +541,11 @@ export const UploadMixin = (superClass) =>

// Only update text content for the default button element
if (addButton === this._addButtonController.defaultNode) {
addButton.textContent = this._i18nPlural(maxFiles, i18n.addFiles);
if (this.directory) {
addButton.textContent = i18n.addDirectories.one;
} else {
addButton.textContent = this._i18nPlural(maxFiles, i18n.addFiles);
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/upload/src/vaadin-upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ class Upload extends UploadMixin(ElementMixin(ThemableMixin(ControllerMixin(Poly
accept$="{{accept}}"
multiple$="[[_isMultiple(maxFiles)]]"
capture$="[[capture]]"
webkitdirectory$="[[directory]]"
/>
`;
}
Expand Down
25 changes: 25 additions & 0 deletions packages/upload/test/upload.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ describe('upload', () => {
function MockFormData() {
this.data = [];
}

MockFormData.prototype.append = function (name, value, filename) {
this.data.push({ name, value, filename });
};
Expand Down Expand Up @@ -539,4 +540,28 @@ describe('upload', () => {
expect(upload.files.length).to.equal(0);
});
});

describe('Directory mode', () => {
it('should not set webkitdirectory attribute on the file input by default', () => {
expect(upload.$.fileInput.hasAttribute('webkitdirectory')).to.be.false;
});

it('should set webkitdirectory attribute on the file input when directory mode is enabled', async () => {
upload.directory = true;
await nextRender(upload);

expect(upload.$.fileInput.hasAttribute('webkitdirectory')).to.be.true;
});

it('should use add files translation when not using directory mode', () => {
expect(upload.querySelector('[slot="add-button"]').textContent).to.be.equal(upload.i18n.addFiles.many);
});

it('should use add directories translation when using directory mode', async () => {
upload.directory = true;
await nextRender(upload);

expect(upload.querySelector('[slot="add-button"]').textContent).to.be.equal(upload.i18n.addDirectories.one);
});
});
});