Skip to content

Commit

Permalink
feat(Upload): add new Upload component (#1581)
Browse files Browse the repository at this point in the history
* feat(Upload)

Add Upload component mvp.

Still missing some features:
- Drag and drop to upload file

* Use types instead of interface

* Ensure css idiomatic ordering (declaration)

Ref.: https://github.com/necolas/idiomatic-css#declaration-order

* Add css dependencies

* Import icons with "as" for less duplication

* Remove unnecessary div

* Fix lists spacing

* Move less important elements down in the DOM tree

* Ensure the outline (SVG stroke element) is not select able with mouse cursor

* Ensure hidden input file element takes no space in layout

* Correct UX spacing

* Update snapshots

* Fix failing axe test by linking input labels

* Set correct spacing

* Set focus on button when file gets deleted

* Spacing correction after PR #1626

* Add mocked example files

* Animate canvas

* Update visual snapshots

* Format proerties docs

* Send skeleton properties down to sibling components

Co-authored-by: Tobias <[email protected]>
  • Loading branch information
AadneRo and tujoworker authored Oct 13, 2022
1 parent 5330e6d commit 3304347
Show file tree
Hide file tree
Showing 37 changed files with 2,883 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
title: 'Upload'
description: 'The Upload widget should be used in scenarios where the user has to upload files. Files can be uploaded by clicking button. You also have the opportunity to add descriptive texts below the title where you could put max file size, allowed fileformats etc.'
status: 'new'
showTabs: true
hideTabs:
- title: Events
---

import UploadInfo from 'Docs/uilib/components/upload/info'
import UploadDemos from 'Docs/uilib/components/upload/demos'

<UploadInfo />
<UploadDemos />
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
/**
* UI lib Component Example
*
*/

import React from 'react'
import ComponentBox from 'dnb-design-system-portal/src/shared/tags/ComponentBox'

const createMockFile = (name: string, size: number, type: string) => {
const file = new File([], name, { type })
Object.defineProperty(file, 'size', {
get() {
return size
},
})
return file
}

const useMockFiles = (setFiles, extend) => {
React.useEffect(() => {
setFiles([
{
file: createMockFile('fileName.png', 123, 'image/png'),
...extend,
},
])
}, [])
}

export const UploadPrefilledFileList = () => (
<ComponentBox
useRender
data-visual-test="upload-file-list"
scope={{ useMockFiles }}
>
{
/* jsx */ `
const Component = () => {
const {files, setFiles} = Upload.useUpload('file-list')
useMockFiles(setFiles, { errorMessage: 'This is no real file!' })
return (
<Upload
acceptedFileTypes={['jpg', 'png']}
id='file-list'
/>
)
}
render(<Component />)
`
}
</ComponentBox>
)

export const UploadBasic = () => (
<ComponentBox useRender data-visual-test="upload-basic">
{
/* jsx */ `
const Component = () => {
const {files} = Upload.useUpload('upload-basic')
return (
<Upload
acceptedFileTypes={['jpg', 'png']}
id='upload-basic'
/>
)
}
render(<Component />)
`
}
</ComponentBox>
)

export const UploadMultipleFiles = () => (
<ComponentBox useRender data-visual-test="upload-multiple-files">
{
/* jsx */ `
const Component = () => {
const {files, setFiles} = Upload.useUpload('upload-multiple-files')
return (
<Upload
acceptedFileTypes={['jpg', 'png']}
id='upload-multiple-files'
multipleFiles={true}
/>
)
}
render(<Component />)
`
}
</ComponentBox>
)
export const UploadRemoveFile = () => (
<ComponentBox useRender data-visual-test="upload-remove-files">
{
/* jsx */ `
const Component = () => {
const {files, setFiles} = Upload.useUpload('upload-remove-files')
return (
<>
<Upload
acceptedFileTypes={['jpg', 'png']}
id='upload-remove-files'
/>
<Button
top='small'
disabled={files.length < 1}
onClick={() => setFiles([])}
>
Remove selected files
</Button>
</>
)
}
render(<Component />)
`
}
</ComponentBox>
)

export const UploadIsLoading = () => (
<ComponentBox
scope={{ useMockFiles }}
useRender
data-visual-test="upload-is-loading"
>
{
/* jsx */ `
const Component = () => {
const {files, setFiles} = Upload.useUpload('upload-is-loading')
useMockFiles(setFiles, { isLoading: true })
return (
<>
<Upload
acceptedFileTypes={['jpg', 'png']}
id='upload-is-loading'
/>
<ToggleButton top='small' disabled={files.length < 1} checked onChange={({ checked }) => {
setFiles(files.map((file) => {
return {...file, isLoading: checked}
}))
}}
>Files is loading toggle</ToggleButton>
</>
)
}
render(<Component />)
`
}
</ComponentBox>
)

export const UploadErrorMessage = () => (
<ComponentBox useRender data-visual-test="upload-error-message">
{
/* jsx */ `
const Component = () => {
const {files, setFiles} = Upload.useUpload('upload-error-message')
return (
<>
<Upload
acceptedFileTypes={['jpg', 'png']}
id='upload-error-message'
/>
<ToggleButton
top='small'
disabled={files.length < 1}
onChange={
({ checked }) => {
setFiles(
files.map((file) => {
return {...file, errorMessage: checked? 'custom error message': null}
})
)
}
}
>
Toggle error message
</ToggleButton>
</>
)
}
render(<Component />)
`
}
</ComponentBox>
)

export const UploadAcceptedFormats = () => (
<ComponentBox useRender>
{
/* jsx */ `
const Component = () => {
const {files, setFiles} = Upload.useUpload('upload-accepted-formats')
return (
<Upload
acceptedFileTypes={['png', 'jpg', 'pdf']}
id='upload-accepted-formats'
/>
)
}
render(<Component />)
`
}
</ComponentBox>
)

export const UploadCustomText = () => (
<ComponentBox>
{
/* jsx */ `
<Upload
acceptedFileTypes={['jpg', 'png']}
id='upload-custom-text'
title='custom title'
text='custom text'
formatsDescription='custom formatsDescription'
fileSizeDescription='custom fileSizeDescription'
fileSizeContent='custom fileSizeContent'
uploadButtonText='custom uploadButtonText'
uploadLoadingText='custom uploadLoadingText'
deleteButton='custom deleteButton'
/>
`
}
</ComponentBox>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
showTabs: true
---

import {
UploadBasic,
UploadPrefilledFileList,
UploadRemoveFile,
UploadMultipleFiles,
UploadIsLoading,
UploadErrorMessage,
UploadAcceptedFormats,
UploadCustomText,
} from 'Docs/uilib/components/upload/Examples'

## Demos

### Upload (default)

<UploadBasic />

### 'useUpload' React Hook

By using the `Upload.useUpload` you can remove or add files or the status displayed in the component.

<UploadRemoveFile />

### Upload multiple files

<UploadMultipleFiles />

### Upload loading state

When uploading the file you can set the loading state of the request using the `Upload.useUpload` hook and passing isLoading to the file that is being uploaded.

<UploadIsLoading />

### Upload error message

The only checks we do currently is for the file size and the file type. These errors are handled by the HTML element ´input´ so they aren't selectable. If you want any other error messages you can use the `Upload.useUpload` the same way as with the loading state.

<UploadErrorMessage />

### Upload specific accepted file formats

You can pass the file formats as a string array. This will restrict which files that can be selected.

<UploadAcceptedFormats />

### Upload custom text

All the text can be custom.

<UploadCustomText />

### Upload with prefilled error

<UploadPrefilledFileList />
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
showTabs: true
---

## Description

The Upload should be used in scenarios where the user has to upload any kind of files.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
showTabs: true
---

## Properties

| Properties | Description |
| ------------------------------------------- | ----------------------------------------------------------------------------------- |
| `acceptedFileTypes` | _(required)_ List of accepted file types. |
| `fileMaxSize` | _(optional)_ fileMaxSize is max size of each file in MB |
| `multipleFiles` | _(optional)_ if set true, accepting multiple files is allowed |
| `title` | _(optional)_ Custom text property. Replaces the default title |
| `text` | _(optional)_ Custom text property. Replaces the default text |
| `formatsDescription` | _(optional)_ Custom text property. Replaces the default accepted format description |
| `fileSizeDescription` | _(optional)_ Custom text property. Replaces the default max file size description |
| `fileSizeContent` | _(optional)_ Custom text property. Replaces the default file size content |
| `uploadButtonText` | _(optional)_ Custom text property. Replaces the default upload button text |
| `uploadLoadingText` | _(optional)_ Custom text property. Replaces the default loading text |
| `deleteButton` | _(optional)_ Custom text property. Replaces the default delete button text |
| `fileListAriaLabel` | _(optional)_ Custom text property. Replaces the default list aria label |
| `skeleton` | _(optional)_ Skeleton should be applied when loading content Default: null. |
| [Space](/uilib/components/space/properties) | _(optional)_ Spacing properties like `top` or `bottom` are supported. |
14 changes: 14 additions & 0 deletions packages/dnb-eufemia/src/components/Upload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* ATTENTION: This file is auto generated by using "prepareTemplates".
* Do not change the content!
*
*/

/**
* Library Index upload to autogenerate all the components and extensions
* Used by "prepareUploads"
*/

import Upload from './upload/Upload'
export * from './upload/Upload'
export default Upload
2 changes: 2 additions & 0 deletions packages/dnb-eufemia/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import Textarea from './textarea/Textarea'
import Timeline from './timeline/Timeline'
import ToggleButton from './toggle-button/ToggleButton'
import Tooltip from './tooltip/Tooltip'
import Upload from './upload/Upload'
import VisuallyHidden from './visually-hidden/VisuallyHidden'

// define / export all the available components
Expand Down Expand Up @@ -102,5 +103,6 @@ export {
Timeline,
ToggleButton,
Tooltip,
Upload,
VisuallyHidden,
}
3 changes: 3 additions & 0 deletions packages/dnb-eufemia/src/components/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import Textarea from './textarea/Textarea'
import Timeline from './timeline/Timeline'
import ToggleButton from './toggle-button/ToggleButton'
import Tooltip from './tooltip/Tooltip'
import Upload from './upload/Upload'
import VisuallyHidden from './visually-hidden/VisuallyHidden'

// define / export all the available components
Expand Down Expand Up @@ -104,6 +105,7 @@ export {
Timeline,
ToggleButton,
Tooltip,
Upload,
VisuallyHidden,
}

Expand Down Expand Up @@ -153,6 +155,7 @@ export const getComponents = () => {
Timeline,
ToggleButton,
Tooltip,
Upload,
VisuallyHidden,
}
}
Expand Down
Loading

0 comments on commit 3304347

Please sign in to comment.