FileUpload
An interactive element that allows users to upload content of their own.
The Basics
What it is
FileUpload allow users to upload content of their own. A file uploader is commonly found in forms but can also live as a standalone element.
FileUpload has 2 variants:
- Simple: Triggered by clicking on a button.
- Drag and Drop: Triggered either by clicking on a tertiary button or by dragging files into a designated area.
FileUpload can also be used within a Forge Lightbox, an example of this is shown in the Demos section.
- Lightbox: Uses a Drag and Drop variant but exists within a lightbox. This can be triggered by anything that makes sense in the context of a design, such as a button.
How it works
Simple
- The user clicks button or uses the space bar or enter key to activate it.
- Button triggers the opening of a dialog box that is provided by the user's OS that affords the selection of one or more files.
- Once files are selected loading and error status indicators allow the user to follow the progress of their selection's upload.
- Upload complete indicators optionally allow the user to remove individual uploaded files.
Drag and Drop
- The user clicks tertiary button or uses the space bar or enter key to activate it or drags the desired files into the area around the component designated by a dotted line.
- Using the tertiary button will trigger the opening of a dialog box that affords the selection of one or more files and is provided by the user's OS.
- Upon file selection, files will be displayed in a 'preview' state as tags within the designated drag area.
- The presence of tags will activate a button for confirming the selection before the files will actually upload. This intermediary step acts as a safeguard against erroneous drag selections.
- Once files are selected loading, linear progress bars and associated error messages (as needed) will display below the designated drag area.
- Upload complete indicators allow the user to optionally remove individual uploaded files as well as undo inadvertent upload removals.
When to use
- To upload one or more files
- To upload files by dragging and dropping
- To show the process of uploading
- To remove uploaded files from the same workflow that uploaded them
When not to use
- For triggering workflows that are not specifically intended to upload files
What to use instead
Use a button to open a new workflow on a new page. Note, this is not a true alternative as FileUpload should still be utilized at the time of file selection and upload confirmation.
How to use
Use FileUpload any time file uploading is needed.
Variants
Simple
- Use the Simple variant when you need to conserve space on the screen, increasing the opportunity for visibility for form elements that may appear lower on the screen.
Drag and Drop
- Drag and Drop should only be enabled if there is adequate space for the user to drag an item into the space.
- Drag and Drop is a good option when the user is likely to be in a context where other windows with draggable items are likely to be present, such as File Manager (Windows) or Finder (Mac).
FileUpload can also be used within a Forge Lightbox, an example of this is shown in the Demos section.
- Use within a Lightbox when you want to isolate your upload sequence from the main workflow that triggered your FileUpload.
Sizes
Each variant is available in all sizes. Choose a size based on your use case:
- Fixed: A fixed 550 px width. Best for wide screens where a responsive width would be too large.
- Responsive: Recommended for most use cases. A responsive width that fills its container.
Options
- Disabled: Displays the Button as disabled when users must meet a requirement (like filling out a form) before activating the Button.
- File Information: Displays optional metadata defined by the implementing application.
Style
Design details
- Drag and Drop, including when in the form of a Lightbox, is centered and fills the container horizontally.
- FileUpload should not be used in containers narrower than 360 px, as that will be too narrow for a full error statement to be shown.
- If the container is too narrow to display all the upload information, do not show optional file information
- If the container is too narrow to show the file name, truncate the file name to "FileNa...1.txt" and reveal the fill name on hover.
Placement and hierarchy
When used in a Form, a single Simple or Drag and Drop version of FileUpload is left-aligned with the left side of the main input fields.
Demos
Coding
Developer tips
Updating state
FileUpload cannot actually upload a file to a remote server. Only application
code can do this. However, there are some state transitions that
FileUpload can do on its own, such as adding files to the pending list. Other state transitions cannot be made
internal to FileUpload, like transitioning from in-progress
to completed
.
One common pattern in React components is to make a component
controlled
by having an onChange
callback function update application state, then
update a value
prop in the controlled component. This could have been done
for FileUpload, but then every application would need to write a reducer to manage all state transitions. Instead, FileUpload
manages its state internally, but exposes its dispatch
method via the ref
prop. This dispatch
method can be called to inform FileUpload of changes,
such as upload progress changes or a successful upload. If an application wishes
to render a FileUpload with previously-uploaded files, the defaultFiles
prop
can be used to set the initial state. If an application wants to control
every state transition within FileUpload, then set-state
actions can be
dispatched. FileUpload's reducer, fileUploadReducer
is exported, so it can be
used within an application's reducer if desired.
There are three broad categories of state transitions:
- State transitions initiated by FileUpload, but do not require application input, such as adding files to the pending list.
- State transitions that are initiated by FileUpload, but the application may wish to add additional information, such as setting the percentComplete for an upload that has just started.
- State transitions initiated by the app. This includes updating percentComplete or marking an upload as completed.
The application is informed of any state transitions initiated by FileUpload via
the onChange callback function. If the application returns a FileUploadAction
object, then that action will be processed by fileUploadReducer
instead of
the original action. It is not recommended to dispatch actions directly from
this callback, as this will lead to two dispatch events, one from the original
FileUploadAction
, and another for the custom action.
Any state transitions initiated by the app are dispatched via
ref.current.dispatch()
.
Supported actions
The supported actions are as follows:
action.type | other action field | Description |
---|---|---|
set-state | state |
Replaces the current state. Recommended only when tight control over the behavior of FileUpload is required. |
pending | affectedFiles | Adds files to the pending list. |
remove-pending | affectedFile | Removes a file from the pending list. |
start | affectedFiles |
Starts a file upload, removing it from the pending list. Each file in affectedFiles can optionally have |
update-progress | affectedFiles |
Updates the progress of a file in the pending list Update |
error | affectedFiles |
Marks files in the in-progress list as having an error. Modify |
retry | affectedFile |
Retries uploading a file. File may either be in the error or completed lists Like the |
completed | affectedFiles |
Marks a file as uploaded Optionally, It's also recommended to set |
delete | affectedFile |
Marks a file as deleted, or removes an errored file from the error list. Optionally, |
undo-delete | affectedFile |
Undoes a file deletion, moving it back to the completed list If your application has hard-deleted the file and needs to re-upload it, return a |
Limitations of client-side validation
FileUpload does some client-side validation of file extensions and fileUploadLimit,
but it has its limitations. With behavior="simple"
, files bypass the "pending"
stage and go directly to "start". Without an upload button, there's no way to
allow the user to correct validation errors before uploading. Similarly, there's
nothing preventing a user from giving an executable file a .txt extension, and
having that pass file extension validation. Multiple files with the same name
are supported, under the assumption that they are from different directories on
the host machine, but the same file could be uploaded multiple times
as well. It's a little tricker to do when using the "Browse Files" button, but
it can be done. Extremely large files could also be uploaded.
Virus Scanning
Server-side validation of uploaded files is paramount to ensure vulnerable components or files are not uploaded.
Most front-end virus prevention or file validation can be bypassed with enough technical know-how. Thus, virus scanning should be done on the back end, not the front end. As front-end library, Forge cannot control the back end of AthenaOne and provide server-side validation in FileUpload.
At best, FileUpload can restrict files based on their file extension. This can be used to make it more difficult to upload executable files, but a malicious user could always rename an executable file with an approved extension, so server-side validation is still required.
Repository
Implementation links
FileUpload directory in Bitbucket
Implementation details
It is strongly recommended to familiarize yourself with the Forge source code. While this documentation is a best effort to document the intent and usage of a component, sometimes some features only become clear when looking at the source code. Also, looking at Forge's source code may help identify and fix bugs in either your application or Forge itself.
Storybook files
Forge maintains at least one storybook file per component. While the primary audience for these files is typically the Forge team, these storybook files may cover usages of the component not covered by a demo. The storybook for the latest version of forge can be found at go/forge-storybook-lts.
Testing library
Forge strongly encourages using testing-library to write tests for your application.
"The more your tests resemble the way your software is used, the more confidence they can give you."
If you're having trouble testing a Forge component using testing-library, it would be a good idea to see how Forge tests its own components. For the most part, Forge tries to use screen.getByRole as much as it can, as that API provides the best feedback on a11y compliance. Forge discourages the use of document.querySelector and screen.getByTestId as both APIs encourage using implementation details to test your component, and discourage adding roles to your component.
With that being said, many of Forge's components were not built with accessibility in mind. These components do break the recommendations listed above.
Import statements
In Nimbus applications
athenaOne serves the Forge bundle independently from your application's bundle. Importing Forge components directly from '@athena/forge'
takes advantage of this feature.
import { FileUpload } from '@athena/forge'
In standalone applications
Importing components using the exact path to the module takes advantage of webpack's tree shaking feature. Webpack will include only that module and its dependencies.
import FileUpload from '@athena/forge/FileUpload';
To use this import guidance, Typescript applications must use typescript >= 4.7.3
, and should add this setting to their tsconfig.json file:
{"compilerOptions": {"moduleResolution": "Node16",}}
If this setting doesn't work for your application, use this import statement instead:
import FileUpload from '@athena/forge/dist/FileUpload';