Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

File Input #118

Closed
claviska opened this issue Jul 18, 2020 · 12 comments
Closed

File Input #118

claviska opened this issue Jul 18, 2020 · 12 comments
Assignees
Labels
feature Feature requests. new component Feature request for a new component. Please vote with reactions!

Comments

@claviska
Copy link
Member

claviska commented Jul 18, 2020

Native file inputs currently work with <sl-form>, but it would be nice to have a more intuitive file input component. Perhaps something that shows each file as a clearable tag after the user makes a selection. Not sure on the UI, but features should include:

  • accept prop
  • multiple prop
  • Drag and drop files
  • Pasting files
  • A standard file input control
  • A button variation
  • Dropzone with file list

Please vote for this feature using 👍 or 👎

@claviska claviska added the feature Feature requests. label Jul 18, 2020
@claviska claviska self-assigned this Jul 18, 2020
@mkitzmann
Copy link

Our team (@kaiszybiak, @preethivenkatesh-sdase, @janborkensteinm, @mkitzmann) prepared a proposal for file input components. We are looking forward to your feedback and would like to discuss the next steps.

We suggest to build 4 separate components:

  • Dropzone
  • FileList
  • FileListItem
  • FileInput

These components could be used individually or composed together (see animation below).
We have created drafts for the properties, events, slots, CSS Custom Properties and CSS Parts.
In the next step, we would suggest to open individual issues for each component, so the requirements for each one can be discussed separately.

Dropzone.Animation.mov

Dropzone

Dropzones are areas into which files can be dragged and dropped to upload a file.

It might be worth looking into if we could utilize an existing library here like https://github.com/dropzone/dropzone. There has also been some similar work done in Adobe Spectrum Web Components.

Dropzone

Properties

Name Description Reflects Type Default
accept A string that defines the file types the file input should accept. Defaults to '*' string '*'
disabled Disables the dropzone. boolean false
multiple Indicates whether the form control accepts one or more values boolean false
noButton If true, hides button to open the native file selection dialog boolean false
noDrag If true, disables drag 'n' drop boolean false
maxFileSize An optional maximum size of a file that will be considered valid. number | undefined -
maxFiles An optional maximum amount of files that will be considered valid. number | undefined -
label An optional overwrite for the upload label string | undefined -
buttonLabel An optional overwrite for the preview button label string | undefined -
lang The locale to render the component in. string -

Events

Name React Event Description Event Detail
sl-drag-leave onSlDragLeave Emitted when dragged files have been moved out of the dropzone area without having been dropped. -
sl-drag-over onSlDragOver Emitted when files have been dragged over the dropzone area, but not yet dropped. -
sl-drop onSlDrop Emitted when dragged files have been dropped on the dropzone area. -
sl-change onSlChange Emitted when files have been uploaded via the dropzone or file dialog. -
sl-drop-accepted onSlDropAccepted Emitted when dragged files have been dropped on the dropzone area and were accepted. -
sl-drop-rejected onSlDropRejected Emitted when dragged files have been dropped on the dropzone area but were rejected. -
sl-file-dialog-open onSlFileDialogOpen Emitted when native file dialog is prompted. -
sl-file-dialog-cancel onSlFileDialogCancel Emitted when native file dialog is closed without selection. -

Slots

Name Description
content The dropzone's content. Alternatively, you can use the icon slot and label prop.
icon The dropzone's icon.

CSS Custom Properties

Name Description Default
--border-radius The border radius for dropzone edges.
--border-width The width of dropzone borders.

CSS Parts

Name Description
base The component's internal wrapper.
content The preview slot's container.

Dependencies

This component imports the following dependencies.

  • <sl-button>
  • <sl-icon>
  • <sl-spinner>

FileList

A file list provides a list of files and is composed of FileListItem components.

File List

Events

Name React Event Description Event Detail
sl-remove onSlRemove Emitted when a menu item is removed. { item: SlFileListItem }

Slots

Name Description
(default) The menu's content, including menu items, menu labels, and dividers.

CSS Parts

Name Description
base The component's internal wrapper.

FileListItem

File list items represent an uploaded file and provides information about file type, file size etc.

Properties

Name Description Reflects Type Default
loading Draws the item in a loading state. boolean false
progressValue The current progress, 0 to 100. Only respects is loading prop is true. number 0
indeterminate When true, percentage is ignored, the label is hidden, and the progress bar is drawn in an indeterminate state. Only respects is loading prop is true. boolean false
progressLabel A custom label for the progress bar's aria label. Only respects is loading prop is true. string ''
lang The locale to render the component in. string -
warning Draws the item in a warning state. boolean false
value A unique value to store in the menu item. This can be used as a way to identify menu items when selected. string ''
size The size of the file in bytes as a read-only 64-bit integer. string ''
type The MIME type of the file as a read-only string or an empty string if the type couldn't be determined. string ''

Slots

Name Description
(default) The menu item's label.
icon The menu item's icon.
warning-icon The menu item's warning icon.
remove-icon The menu item's remove icon.

CSS Parts

Name Description
base The component's internal wrapper.
label The menu item label.

Dependencies

This component imports the following dependencies.

  • <sl-button>
  • <sl-icon>
  • <sl-progress-bar>
  • <sl-spinner>

FileInput

File inputs can be used to upload one or more files from the local file system.

This could be a separate component or extend the default input component. Its properties should align with the native file input, so it can be used in forms (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file)

File Input

Properties

Name Description Reflects Type Default
accept A string that defines the file types the file input should accept. Defaults to '*'. Only works if input type is 'file' string '*'
multiple Indicates whether the form control accepts one or more values. Only works if input type is 'file' boolean false
maxFileSize An optional maximum size of a file that will be considered valid. number | undefined -
maxFiles An optional maximum amount of files that will be considered valid. number | undefined -
size The input's size. 'small' | 'medium' | 'large' 'medium'
name The input's name attribute. string -
filled Draws a filled input. boolean false
warning Draws the input in a warning state. boolean false
pill Draws a pill-style input with rounded edges. boolean false
label The input's label. Alternatively, you can use the label slot. string ''
warningLabel The input's warning label. Alternatively, you can use the warning-label slot. string ''
buttonLabel The input's button label. Alternatively, you can use the button-label slot. string ''
helpText
help-text
The input's help text. Alternatively, you can use the help-text slot. string ''
placeholder The input's placeholder text. string -
lang The locale to render the component in. string -
disabled Disables the input. boolean false
required Makes the input a required field. boolean false
invalid This will be true when the control is in an invalid state. Validity is determined by props such as, required, maxFileSize, maxFiles, and accept using the browser's constraint validation API. boolean false

Events

Name React Event Description Event Detail
sl-change onSlChange Emitted when an alteration to the control's value is committed by the user. -
sl-clear onSlClear Emitted when the clear button is activated. -
sl-input onSlInput Emitted when the control receives input and its value changes. -
sl-focus onSlFocus Emitted when the control gains focus. -
sl-blur onSlBlur Emitted when the control loses focus. -
sl-remove onSlRemove Emitted when file tag is removed. -
sl-file-dialog-open onSlFileDialogOpen Emitted when native file dialog is prompted. -
sl-file-dialog-cancel onSlFileDialogCancel Emitted when native file dialog is closed without selection. -

Slots

Name Description
label The file input's label. Alternatively, you can use the label prop.
button The file input's button. Alternatively, you can use the buttonLabel prop.
warning-label The file input's warning label. Alternatively, you can use the warningLabel prop.
preview-tag The tag representing an uploaded file.
prefix Used to prepend an icon or similar element to the input.
suffix Used to append an icon or similar element to the input.
help-text Help text that describes how to use the input. Alternatively, you can use the help-text prop.

CSS Parts

Name Description
form-control The form control that wraps the label, input, and help-text.
form-control-label The label's wrapper.
form-control-input The input's wrapper.
form-control-help-text The help text's wrapper.
base The component's internal wrapper.
preview-tag The component's preview tag wrapper.
button The file dialog button.
input The input control.
prefix The input prefix container.
suffix The input suffix container.

Dependencies

This component imports the following dependencies.

  • <sl-button>
  • <sl-icon>
  • <sl-spinner>

@mkitzmann
Copy link

We would like to prepare a PR for these components in the next couple of weeks. So it would be great to know if this is something that would be of interest to the community.
@claviska Would you be open to having separate issues for each component or do you prefer to handle it in this issue?

@claviska
Copy link
Member Author

At first glance, this looks really good and well thought out. Unfortunately, it was submitted the first day of my vacation so I won’t be able to give it a proper review until I get back next week. However, separate PRs would be better so we can keep each one focused and get them merged faster.

Thanks for contributing this, and apologies for the delay. I can’t wait to get home and review it!

@claviska
Copy link
Member Author

Dropzone / FileList / FileListItem

Apologies for the delay. I'm really liking Dropzone, FileList, and FileListItem. It looks like FileList and FileListItem aren't listed as dependencies of Dropzone — in that case, is the user expected to listen for events and populate the FileList themselves? I was thinking it would be automatic, and I'm not sure if that's what you're intending or not.

In other words, users would ideally be able to do this:

<form>
  <sl-dropzone ...></sl-dropzone>
</form>

And everything just works. Of course, they can listen to events and do other custom things as needed, but the default behavior should submit files like a regular file input and allow them to add/remove files without having to code anything custom for it.

If that's the intention, it would make sense to bundle the three of these components into a single PR since they're designed to work in tandem. At that point, we can dive into the API a bit more. (At a glance, I don't have any major concerns with the proposed API.)

I like the names you've chosen, but I'm worried that "Dropzone" is a bit disconnected from its dependents. It will be harder for users to discover it. I'm wondering if calling it "FileDropzone" would help with that.

Do you already have these working? I'm not sure if the video is a mock or if it's showing actual components. It looks good, though!

FileInput

The design here seems to be inspired by the browser's default file input, which I'm not a fan of. I wonder how we can improve the aesthetics while keeping it intuitive. I think it's a combination of the button + input that makes it seem...maybe outdated?

The API looks pretty good. I'd suggest a few changes:

  • warning - Is this a readonly property? It seems to imply an error occurred with one of the files being uploaded, so we should probably call it hasError instead. No pattern has been established for this yet, but we do have invalid which means we should try to stay consistent, so using has-error or erroneous instead of error would make more sense.
  • What happens when two files are uploaded and only one triggers an error? The warning state doesn't seem to account for this situation and I'm not sure what that would look like.
  • I can appreciate the desire for maxFiles, but as far as I know, there's no way to enforce this when using multiple. So if you set maxFiles to 3 and the user selects 4 files, what happens? Do we silently drop the extra file? Do we show an error? Both seem unintuitive since we can't control it in the system dialog, so maybe we should drop this attribute and let the user handle that. Or maybe I'm not clear on how the control will handle this. If you have a mockup, I'd love to see it.
  • Similarly, I'm concerned about maxFileSize. How do we inform the user that the file or files they've selected are too big? This should be really intuitive, like in your dropzone example.
  • File tags probably shouldn't truncate the filename. Even in your example, it's hard to tell what Filenam... and Filenam... are. In <sl-select>, these wrap by default. There's also a maxTagsVisible prop that controls how many will show before the +3 tag appears.

Next Steps

I'd love to see a PR for Dropzone, FileList, and FileListItem. This is a really intuitive approach to file uploads and I'm excited to dive in deeper and play with the APIs. I'm sure I'll have more feedback once I see it them action!

With those components, do we even need a separate FileInput? It seems redundant to have both, and I question whether we can get the same level of UX in FileInput as we have in Dropzone.

One common alternative — and something that Shoelace v1 had, is a FileButton. This is just a simple button that, when clicked, opens the system upload dialog and emits an event with the selected file(s).

CleanShot 2022-05-25 at 08 26 07@2x

It's less featureful compared to the proposed FileInput, but I've found it useful for many applications (e.g. selecting an avatar, one-click uploads) and may be enough to fill the gap between a simple file input and a full-featured Dropzone.

@mkitzmann
Copy link

mkitzmann commented Jun 1, 2022

Thanks for the feedback @claviska!

It sounds like a good idea to drop the FileInput component. It would probably just be a more "compact" version of the Dropzone and FileList without that same level of usability.

The FileButton could actually be a feature of the Dropzone, since that is pretty much the same functionality just styled differently. So maybe we could add a buttonOnly prop that displays only the button slot of the Dropzone.

The Dropzone should definitely just work without needing to handle the FileList manually. We will extend the list of dependencies. Maybe it should have a hideFileList prop for cases where the FileList is not needed.

We can prepare a PR for FileDropzone, FileList and FileListItem and we will let you know once we have a first draft.

@jaredcwhite
Copy link
Contributor

Just chiming in to say I'm very excited about this. Working with dropzones/multiple file uploads with progress has always been pretty tricky, even with battle-tested well-known libraries. And I don't know of any in the WC space. I'll definitely be able to make good use of this!

@petergrau
Copy link

We started working on this and will contirbute it once we have a first "stable" version!

@claviska
Copy link
Member Author

I'd love to provide feedback as you go. That will make it easier to keep the PR inline with Shoelace's quality standards for code, APIs, etc.

Do you have a preview branch anywhere? I'm more than happy to provide feedback as you go!

@petergrau
Copy link

I'd love to provide feedback as you go. That will make it easier to keep the PR inline with Shoelace's quality standards for code, APIs, etc.

Do you have a preview branch anywhere? I'm more than happy to provide feedback as you go!

Happy to receive the feedback - especially since it is our first component. I think we should have something worth the feedback end of this or beginning of this week!

@claviska claviska added the new component Feature request for a new component. Please vote with reactions! label Nov 8, 2022
@claviska claviska added this to the 2.0 (stable) milestone Nov 8, 2022
@claviska claviska removed this from the 2.0 (stable) milestone Jan 4, 2023
@masi
Copy link

masi commented May 1, 2023

This spec is huge!

For accessible file input buttons yoou need "only" the FileInput. It is the only element anyway that will work with keyboards and screen readers. The fancy designs look good, but will need some to work with anything but a mouse.

@petergrau Has any work been done?

@claviska
Copy link
Member Author

claviska commented May 1, 2023

@masi There's an early proof of concept for a simpler [incomplete] version you can find here.

#813 (comment)

Also note that you can continue to use <input type="file"> in the meantime.

@masi
Copy link

masi commented May 5, 2023

Also note that you can continue to use <input type="file"> in the meantime.
Sure, no worries.

I have my own code, but no web component, that adds some a11y features (my main concern) and a bit of styling.

I am worried by the design mockups that the end result is a an all singing and dancing horror for screen reader (and keyboard) users.

@shoelace-style shoelace-style locked and limited conversation to collaborators Oct 16, 2023
@claviska claviska converted this issue into discussion #1649 Oct 16, 2023

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
feature Feature requests. new component Feature request for a new component. Please vote with reactions!
Projects
None yet
Development

No branches or pull requests

5 participants