Skip to content
Snippets Groups Projects
Commit 9308cab1 authored by Alf Eaton's avatar Alf Eaton
Browse files

Add file attachments to review + decision forms

Refactor Files molecule to allow subclassing as Attachments and Supplementary.
parent 31174bf1
No related branches found
No related tags found
No related merge requests found
import React from 'react'
import Icon from './Icon'
import classes from './Attachment.local.scss'
const Attachment = ({ value }) => (
<a
key={value.url}
download={value.name}
href={value.url}
className={classes.attachment}>
<span className={classes.icon}>
<Icon color="cornflowerblue">paperclip</Icon>
</span>
<span className={classes.filename}>
{value.name}
</span>
</a>
)
export default Attachment
.attachment {
display: flex;
align-items: center;
color: inherit;
text-decoration: none;
}
.icon {
margin-right: 10px;
display: inline-flex;
}
.filename {
font-family: var(--font-mono);
}
A file attached to a note.
```js
const value = {
name: faker.system.commonFileName(),
url: faker.internet.url()
};
<Attachment value={value}/>
```
...@@ -3,7 +3,7 @@ import classes from './File.local.scss' ...@@ -3,7 +3,7 @@ import classes from './File.local.scss'
const extension = ({ name }) => name.replace(/^.+\./, '') const extension = ({ name }) => name.replace(/^.+\./, '')
const File = ({ value, file, error, progress }) => ( const File = ({ value }) => (
<div className={classes.root}> <div className={classes.root}>
<div className={classes.icon}> <div className={classes.icon}>
<div className={classes.extension}> <div className={classes.extension}>
......
/* atom */ /* atom */
export { default as Attachment } from './atoms/Attachment'
export { default as Button } from './atoms/Button' export { default as Button } from './atoms/Button'
export { default as Checkbox } from './atoms/Checkbox' export { default as Checkbox } from './atoms/Checkbox'
export { default as Icon } from './atoms/Icon' export { default as Icon } from './atoms/Icon'
...@@ -10,7 +11,9 @@ export { default as ValidatedField } from './atoms/ValidatedField' ...@@ -10,7 +11,9 @@ export { default as ValidatedField } from './atoms/ValidatedField'
/* molecules */ /* molecules */
export { default as AppBar } from './molecules/AppBar' export { default as AppBar } from './molecules/AppBar'
export { default as Attachments } from './molecules/Attachments'
export { default as CheckboxGroup } from './molecules/CheckboxGroup' export { default as CheckboxGroup } from './molecules/CheckboxGroup'
export { default as Files } from './molecules/Files' export { default as Files } from './molecules/Files'
export { default as Supplementary } from './molecules/Supplementary'
export { default as RadioGroup } from './molecules/RadioGroup' export { default as RadioGroup } from './molecules/RadioGroup'
export { default as YesOrNo } from './molecules/YesOrNo' export { default as YesOrNo } from './molecules/YesOrNo'
import React from 'react'
import Files from './Files'
import Attachment from '../atoms/Attachment'
import classes from './Attachments.local.scss'
import Icon from '../atoms/Icon'
// TODO: show upload progress
class Attachments extends React.Component {
render () {
return (
<Files
{...this.props}
buttonText="Attach file"
uploadingFile={({ file, progress, error }) => (
<div className={classes.uploading}>
<span className={classes.icon}>
<Icon color="cornflowerblue">paperclip</Icon>
</span>
<span className={classes.filename}>
{error ? error : 'Uploading…'}
</span>
</div>
)}
uploadedFile={value => (
<Attachment
key={value.url}
value={value}/>
)}
/>
)
}
}
export default Attachments
.uploading {
display: flex;
align-items: center;
}
.icon {
color: gray;
margin-right: 10px;
}
.filename {
color: gray;
}
A list of files attached to a note, and a button to attach a new file.
```js
const value = [
{
name: faker.system.commonFileName(),
url: faker.internet.url()
},
{
name: faker.system.commonFileName(),
url: faker.internet.url()
}
];
<Attachments
value={value}
uploadFile={file => new XMLHttpRequest()}/>
```
import React from 'react' import React from 'react'
import classes from './Files.local.scss' import classes from './Files.local.scss'
import Upload from './Upload' import Upload from './Upload'
import File from '../atoms/File'
class Files extends React.Component { class Files extends React.Component {
constructor (props) { constructor (props) {
...@@ -46,7 +45,7 @@ class Files extends React.Component { ...@@ -46,7 +45,7 @@ class Files extends React.Component {
} }
render () { render () {
const { name } = this.props const { name, buttonText, uploadingFile, uploadedFile } = this.props
const { values, uploads } = this.state const { values, uploads } = this.state
return ( return (
...@@ -56,7 +55,7 @@ class Files extends React.Component { ...@@ -56,7 +55,7 @@ class Files extends React.Component {
type="button" type="button"
className={classes.button} className={classes.button}
onClick={() => this.fileInput.click()}> onClick={() => this.fileInput.click()}>
Upload files {buttonText}
</button> </button>
<input <input
...@@ -74,14 +73,12 @@ class Files extends React.Component { ...@@ -74,14 +73,12 @@ class Files extends React.Component {
key={upload.file.name} key={upload.file.name}
file={upload.file} file={upload.file}
request={upload.request} request={upload.request}
handleUploadedFile={this.handleUploadedFile}/> handleUploadedFile={this.handleUploadedFile}
render={uploadingFile}
/>
))} ))}
{values && values.map(value => ( {values && values.map(uploadedFile)}
<File
key={value.name}
value={value}/>
))}
</div> </div>
</div> </div>
) )
......
...@@ -15,5 +15,8 @@ const value = [ ...@@ -15,5 +15,8 @@ const value = [
<Files <Files
value={value} value={value}
buttonText="Choose a file to upload"
uploadingFile={({ file, progress, error }) => <div style={{color:'gray'}}>{file.name}</div>}
uploadedFile={value => <div>{value.name}</div>}
uploadFile={file => new XMLHttpRequest()}/> uploadFile={file => new XMLHttpRequest()}/>
``` ```
import React from 'react'
import Files from './Files'
import UploadingFile from '../atoms/UploadingFile'
import File from '../atoms/File'
class Supplementary extends React.Component {
render () {
return (
<Files
{...this.props}
buttonText="▲ Upload files"
uploadingFile={({ file, progress, error }) => (
<UploadingFile
key={file.name}
file={file}
progress={progress}
error={error}/>
)}
uploadedFile={value => (
<File
key={value.url}
value={value}/>
)}/>
)
}
}
export default Supplementary
A list of supplementary files, and a button to upload a new file.
```js
const value = [
{
name: faker.system.commonFileName(),
url: faker.internet.url()
},
{
name: faker.system.commonFileName(),
url: faker.internet.url()
}
];
<Supplementary
value={value}
uploadFile={file => new XMLHttpRequest()}/>
```
import React from 'react' import React from 'react'
import UploadingFile from '../atoms/UploadingFile'
// TODO: retry on error // TODO: retry on error
// TODO: make this a HOC for <UploadingFile>? // TODO: make this a HOC for <UploadingFile>?
...@@ -58,15 +57,10 @@ class Upload extends React.Component { ...@@ -58,15 +57,10 @@ class Upload extends React.Component {
} }
render () { render () {
const { file } = this.props const { file, render } = this.props
const { progress, error } = this.state const { progress, error } = this.state
return ( return render({ file, progress, error })
<UploadingFile
file={file}
progress={progress}
error={error}/>
)
} }
} }
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment