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

Add FileUploader widget #1540

Open
wants to merge 35 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d662c22
Add FileUploader widget
msssk Aug 27, 2020
65a510f
Add FileUploadInput widget
msssk Aug 28, 2020
dfed22e
FileUploadInput: add Label demo and update label typing
msssk Aug 28, 2020
e10e060
FileUploader: update to use FileUploadInput
msssk Aug 28, 2020
618054b
FileUploader: add dnd support
msssk Aug 28, 2020
1e358ea
WIP: convert dnd middleware to reactive API
msssk Aug 28, 2020
f8775e4
Fix dnd API and update examples
msssk Aug 31, 2020
c840868
Update themes
msssk Aug 31, 2020
908a433
WIP: experiment with more focused fileDrop middleware
msssk Sep 1, 2020
237511e
Refactor dnd middleware to fileDrop
msssk Sep 1, 2020
75d7bd1
FileUploadInput: improve Multiple example
msssk Sep 2, 2020
ddc5694
fileDrop middleware: add doc comment
msssk Sep 2, 2020
52832a0
FileUploader: add dnd and validation
msssk Sep 2, 2020
502b1d9
FileUploader: fix stale files bug
msssk Sep 2, 2020
95cb60c
Update themes, change dnd border to dashed
msssk Sep 2, 2020
2024969
Pass theme to child widgets
msssk Sep 2, 2020
8bd6cc7
Update build config, add FileUploadInput tests
msssk Sep 2, 2020
6feb6f1
FileUploadInput: abandon middleware, render children, update examples
msssk Sep 4, 2020
5773d2f
* FileUploadInput: add design doc
msssk Sep 5, 2020
726ad1f
Update tests
msssk Sep 8, 2020
f2d6d8f
Update tests
msssk Sep 9, 2020
ab02f56
Update tests
msssk Sep 9, 2020
76d8454
Update tests to check for preventDefault
msssk Sep 9, 2020
188ef5b
FileUploadInput: don't handle drag events when allowDnd=false
msssk Sep 9, 2020
7655ea0
FileUploadInput: make onValue property required
msssk Sep 10, 2020
e5e79b7
FileUploader: fix file rendering, add controlled props
msssk Sep 10, 2020
4a63f8c
Update tests
msssk Sep 10, 2020
78fbeae
FileUploader: add controlled support
msssk Sep 15, 2020
a1be024
FileUploader: fix File handling for FF/Edge
msssk Sep 16, 2020
7e72aae
Fix: error opening file dialog in Firefox
msssk Sep 16, 2020
8b639dc
Implement controlled functionality; update docs
msssk Sep 17, 2020
21d4132
FileUploader: update tests
msssk Sep 17, 2020
aa1ff8e
FileUploader: update tests
msssk Sep 18, 2020
19368f9
test fixes
tomdye Sep 21, 2020
f9f3e19
fix tests and typings
tomdye Dec 14, 2020
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
2 changes: 2 additions & 0 deletions .dojorc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
"src/date-input",
"src/dialog",
"src/email-input",
"src/file-upload-input",
"src/file-uploader",
"src/floating-action-button",
"src/form",
"src/grid",
Expand Down
81 changes: 81 additions & 0 deletions src/examples/src/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,17 @@ import AnimatedDialog from './widgets/dialog/AnimatedDialog';
import FocusTrappedDialog from './widgets/dialog/FocusTrappedDialog';
import ActionsDialog from './widgets/dialog/ActionsDialog';
import BasicEmailInput from './widgets/email-input/Basic';
import BasicFileUploadInput from './widgets/file-upload-input/Basic';
import DisabledFileUploadInput from './widgets/file-upload-input/Disabled';
import LabelledFileUploadInput from './widgets/file-upload-input/Labelled';
import MultipleFileUploadInput from './widgets/file-upload-input/Multiple';
import NoDropFileUploadInput from './widgets/file-upload-input/NoDrop';
import BasicFileUploader from './widgets/file-uploader/Basic';
import ControlledFileUploader from './widgets/file-uploader/Controlled';
import CustomValidatorFileUploader from './widgets/file-uploader/CustomValidator';
import DisabledFileUploader from './widgets/file-uploader/Disabled';
import MultipleFileUploader from './widgets/file-uploader/Multiple';
import ValidatedFileUploader from './widgets/file-uploader/Validated';
import Advanced from './widgets/grid/Advanced';
import BasicFab from './widgets/floating-action-button/Basic';
import ExtendedFab from './widgets/floating-action-button/Extended';
Expand Down Expand Up @@ -731,6 +742,76 @@ export const config = {
}
}
},
'file-upload-input': {
filename: 'index',
overview: {
example: {
title: 'Basic FileUploadInput',
filename: 'Basic',
module: BasicFileUploadInput
}
},
examples: [
{
title: 'Disabled FileUploadInput',
filename: 'Disabled',
module: DisabledFileUploadInput
},
{
title: 'Multiple FileUploadInput',
filename: 'Multiple',
module: MultipleFileUploadInput,
description:
'Demonstrates using child `content` property to render information about the uploaded files that is available to the `onValue` callback.'
},
{
title: 'FileUploadInput with label',
filename: 'Labelled',
module: LabelledFileUploadInput
},
{
title: 'FileUploadInput with no DnD',
filename: 'NoDrop',
module: NoDropFileUploadInput
}
]
},
'file-uploader': {
filename: 'index',
overview: {
example: {
filename: 'Basic',
module: BasicFileUploader
}
},
examples: [
{
title: 'Disabled FileUploader',
filename: 'Disabled',
module: DisabledFileUploader
},
{
title: 'Multiple FileUploader',
filename: 'Multiple',
module: MultipleFileUploader
},
{
title: 'Validated FileUploader',
filename: 'Validated',
module: ValidatedFileUploader
},
{
title: 'FileUploader with custom validator',
filename: 'CustomValidator',
module: CustomValidatorFileUploader
},
{
title: 'Controlled FileUploader',
filename: 'Controlled',
module: ControlledFileUploader
}
]
},
'floating-action-button': {
overview: {
example: {
Expand Down
21 changes: 21 additions & 0 deletions src/examples/src/widgets/file-upload-input/Basic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { create, tsx } from '@dojo/framework/core/vdom';
import icache from '@dojo/framework/core/middleware/icache';
import { FileUploadInput } from '@dojo/widgets/file-upload-input';
import Example from '../../Example';

const factory = create({ icache });

export default factory(function Basic({ middleware: { icache } }) {
const selectedFiles: File[] = icache.getOrSet('selectedFiles', []);

function onValue(files: File[]) {
icache.set('selectedFiles', files);
}

return (
<Example>
<FileUploadInput onValue={onValue} />
<div>Selected file: {selectedFiles.length ? selectedFiles[0].name : 'none'}</div>
</Example>
);
});
17 changes: 17 additions & 0 deletions src/examples/src/widgets/file-upload-input/Disabled.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { create, tsx } from '@dojo/framework/core/vdom';
import { FileUploadInput } from '@dojo/widgets/file-upload-input';
import Example from '../../Example';

const factory = create();

export default factory(function Disabled() {
function onValue() {
// do something with files
}

return (
<Example>
<FileUploadInput disabled onValue={onValue} />
</Example>
);
});
25 changes: 25 additions & 0 deletions src/examples/src/widgets/file-upload-input/Labelled.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { create, tsx } from '@dojo/framework/core/vdom';
import icache from '@dojo/framework/core/middleware/icache';
import { FileUploadInput } from '@dojo/widgets/file-upload-input';
import Example from '../../Example';

const factory = create({ icache });

export default factory(function Labelled({ middleware: { icache } }) {
const selectedFiles: File[] = icache.getOrSet('selectedFiles', []);

function onValue(files: File[]) {
icache.set('selectedFiles', files);
}

return (
<Example>
<FileUploadInput accept="image/*" onValue={onValue}>
{{
label: 'Upload a profile image'
}}
</FileUploadInput>
<div>Selected file: {selectedFiles.length ? selectedFiles[0].name : 'none'}</div>
</Example>
);
});
49 changes: 49 additions & 0 deletions src/examples/src/widgets/file-upload-input/Multiple.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { create, tsx } from '@dojo/framework/core/vdom';
import icache from '@dojo/framework/core/middleware/icache';
import { FileUploadInput } from '@dojo/widgets/file-upload-input';
import Example from '../../Example';

import * as css from './multiple.m.css';

const factory = create({ icache });

export default factory(function Multiple({ middleware: { icache } }) {
const selectedFiles: File[] = icache.getOrSet('selectedFiles', []);

function onValue(files: File[]) {
icache.set('selectedFiles', files);
}

return (
<Example>
<FileUploadInput onValue={onValue} multiple>
{{
content: selectedFiles.length ? (
<table classes={[css.table]}>
<thead>
<th>Name</th>
<th>Modified</th>
<th>Type</th>
<th>Bytes</th>
</thead>
<tbody>
{selectedFiles.map(function(file) {
return (
<tr key={file.name}>
<td>{file.name}</td>
<td>{new Date(file.lastModified).toLocaleString()}</td>
<td>{file.type}</td>
<td>{String(file.size)}</td>
</tr>
);
})}
</tbody>
</table>
) : (
''
)
}}
</FileUploadInput>
</Example>
);
});
21 changes: 21 additions & 0 deletions src/examples/src/widgets/file-upload-input/NoDrop.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { create, tsx } from '@dojo/framework/core/vdom';
import icache from '@dojo/framework/core/middleware/icache';
import { FileUploadInput } from '@dojo/widgets/file-upload-input';
import Example from '../../Example';

const factory = create({ icache });

export default factory(function NoDrop({ middleware: { icache } }) {
const selectedFiles: File[] = icache.getOrSet('selectedFiles', []);

function onValue(files: File[]) {
icache.set('selectedFiles', files);
}

return (
<Example>
<FileUploadInput allowDnd={false} onValue={onValue} />
<div>Selected file: {selectedFiles.length ? selectedFiles[0].name : 'none'}</div>
</Example>
);
});
3 changes: 3 additions & 0 deletions src/examples/src/widgets/file-upload-input/multiple.m.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.table {
width: 100%;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const table: string;
17 changes: 17 additions & 0 deletions src/examples/src/widgets/file-uploader/Basic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { create, tsx } from '@dojo/framework/core/vdom';
import FileUploader from '@dojo/widgets/file-uploader';
import Example from '../../Example';

const factory = create();

export default factory(function Basic() {
function onValue() {
// do something with files
}

return (
<Example>
<FileUploader onValue={onValue} />
</Example>
);
});
46 changes: 46 additions & 0 deletions src/examples/src/widgets/file-uploader/Controlled.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { create, tsx } from '@dojo/framework/core/vdom';
import icache from '@dojo/framework/core/middleware/icache';
import FileUploader, { FileWithValidation } from '@dojo/widgets/file-uploader';
import Example from '../../Example';

const factory = create({ icache });

export default factory(function Controlled({ middleware: { icache } }) {
function validateFiles(files: FileWithValidation[]) {
return files.map(function(file) {
// Files bigger than 100KB are marked invalid
const valid = file.size <= 100 * 1024;
file.valid = valid;
// Each file can include a message for the valid state as well as invalid
file.message = valid ? 'File is valid' : 'File is too big';

return file;
});
}

// onValue receives any files selected from the file dialog or
// dragged and dropped from the OS
function onValue(files: File[]) {
// Validation and manipulation of the selected files is done
// entirely external to the FileUploader widget.
// This line both validates the files and truncates the total count to 4.
const validatedFiles = validateFiles(files).slice(0, 4);

icache.set('files', validatedFiles);
}

// If FileUploader receives a value for `files` then it will only render that.
// If it receives a falsy value then it will render whatever files the user selects.
// To ensure no files are rendered pass an empty array.
const files = icache.getOrSet('files', []);

return (
<Example>
<FileUploader files={files} multiple onValue={onValue}>
{{
label: 'Controlled FileUploader'
}}
</FileUploader>
</Example>
);
});
32 changes: 32 additions & 0 deletions src/examples/src/widgets/file-uploader/CustomValidator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { create, tsx } from '@dojo/framework/core/vdom';
import FileUploader from '@dojo/widgets/file-uploader';
import Example from '../../Example';

const factory = create();

export default factory(function CustomValidator() {
function onValue() {
// do something with files
}

function validateName(file: File) {
if (file.name === 'validfile.txt') {
return { valid: true };
} else {
return {
message: 'File name must be "validfile.txt"',
valid: false
};
}
}

return (
<Example>
<FileUploader customValidator={validateName} onValue={onValue}>
{{
label: 'Upload a file named "validfile.txt"'
}}
</FileUploader>
</Example>
);
});
17 changes: 17 additions & 0 deletions src/examples/src/widgets/file-uploader/Disabled.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { create, tsx } from '@dojo/framework/core/vdom';
import FileUploader from '@dojo/widgets/file-uploader';
import Example from '../../Example';

const factory = create();

export default factory(function Disabled() {
function onValue() {
// do something with files
}

return (
<Example>
<FileUploader disabled onValue={onValue} />
</Example>
);
});
17 changes: 17 additions & 0 deletions src/examples/src/widgets/file-uploader/Multiple.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { create, tsx } from '@dojo/framework/core/vdom';
import FileUploader from '@dojo/widgets/file-uploader';
import Example from '../../Example';

const factory = create();

export default factory(function Multiple() {
function onValue() {
// do something with files
}

return (
<Example>
<FileUploader multiple onValue={onValue} />
</Example>
);
});
Loading