Skip to content

Commit 891fe4f

Browse files
Update file picker to return an array of files
* Update file picker to return an array of files * resolve issues getting file content * update FilePicker docs * update docs and test handler
1 parent eae8e36 commit 891fe4f

File tree

18 files changed

+95
-88
lines changed

18 files changed

+95
-88
lines changed

docs/documentation/docs/controls/FilePicker.md

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Currently supported locations
77
- OneDrive - tab allows to select a file from the user's One Drive.
88
- Site document libraries - tab allows to select a file from the existing site document libraries.
99
- Upload - tab allows to upload a file from local drive.
10+
- Multi-Upload - tab allows to upload multiple files from local drive.
1011
- From a link - tab allows to paste a link to the document.
1112

1213
## Overview
@@ -44,12 +45,27 @@ import { FilePicker, IFilePickerResult } from '@pnp/spfx-controls-react/lib/File
4445
bingAPIKey="<BING API KEY>"
4546
accepts= {[".gif", ".jpg", ".jpeg", ".bmp", ".dib", ".tif", ".tiff", ".ico", ".png", ".jxr", ".svg"]}
4647
buttonIcon="FileImage"
47-
onSave={(filePickerResult: IFilePickerResult) => { this.setState({filePickerResult }) }}
48-
onChange={(filePickerResult: IFilePickerResult) => { this.setState({filePickerResult }) }}
48+
onSave={(filePickerResult: IFilePickerResult[]) => { this.setState({filePickerResult }) }}
49+
onChange={(filePickerResult: IFilePickerResult[]) => { this.setState({filePickerResult }) }}
4950
context={this.props.context}
5051
/>
5152
```
5253

54+
- Sample `onSave` handler:
55+
56+
```TypeScript
57+
private _onFilePickerSave = async (filePickerResult: IFilePickerResult[]) => {
58+
this.setState({ filePickerResult: filePickerResult });
59+
if (filePickerResult && filePickerResult.length > 0) {
60+
for (var i = 0; i < filePickerResult.length; i++) {
61+
const item = filePickerResult[i];
62+
const fileResultContent = await item.downloadFileContent();
63+
console.log(fileResultContent);
64+
}
65+
}
66+
}
67+
```
68+
5369
## Implementation
5470

5571
The FilePicker component can be configured with the following properties:
@@ -60,8 +76,8 @@ The FilePicker component can be configured with the following properties:
6076
| buttonLabel | string | no | Specifies the label of the file picker button. |
6177
| buttonIcon | string | no | In case it is provided the file picker will be rendered as an action button. |
6278
buttonIconProps | IIconProps | no | In case it is provided the file picker will be rendered as an Icon the and all can define Properties for Icon |
63-
| onSave | (filePickerResult: IFilePickerResult) => void | yes | Handler when the file has been selected and picker has been closed. |
64-
| onChange | (filePickerResult: IFilePickerResult) => void | no | Handler when the file selection has been changed. |
79+
| onSave | (filePickerResult: IFilePickerResult[]) => void | yes | Handler when the file has been selected and picker has been closed. |
80+
| onChange | (filePickerResult: IFilePickerResult[]) => void | no | Handler when the file selection has been changed. |
6581
| onCancel | () => void | no | Handler when file picker has been cancelled. |
6682
| context | ExtensionContext \| WebPartContext | yes | Current context. |
6783
| accepts | string[] | no | Array of strings containing allowed files extensions. E.g. [".gif", ".jpg", ".jpeg", ".bmp", ".dib", ".tif", ".tiff", ".ico", ".png", ".jxr", ".svg"] |
@@ -81,6 +97,7 @@ The FilePicker component can be configured with the following properties:
8197
| storeLastActiveTab | boolean | no | Specifies if last active tab will be stored after the Upload panel has been closed. Note: the value of selected tab is stored in the queryString hash. Default `true` |
8298
| isPanelOpen | boolean | no | Specifies if the file picker panel is open by default or not |
8399
| renderCustomUploadTabContent | (filePickerResult: IFilePickerResult) => JSX.Element \| null | no | Optional renderer to add custom user-defined fields to "Upload" tab |
100+
| renderCustomMultipleUploadTabContent | (filePickerResult: IFilePickerResult[]) => JSX.Element \| null | no | Optional renderer to add custom user-defined fields to "Multi-Upload" tab |
84101
| renderCustomLinkTabContent | (filePickerResult: IFilePickerResult) => JSX.Element \| null | no | Optional renderer to add custom user-defined fields to "Link" tab |
85102
| includePageLibraries | boolean | no | Specifies if Site Pages library to be visible on Sites tab |
86103

src/controls/filePicker/FilePicker.tsx

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -39,29 +39,6 @@ import WebSearchTab from "./WebSearchTab/WebSearchTab";
3939
// Localization
4040

4141

42-
43-
44-
45-
46-
47-
48-
49-
50-
51-
52-
53-
54-
55-
56-
57-
58-
59-
60-
61-
62-
63-
64-
6542
export class FilePicker extends React.Component<
6643
IFilePickerProps,
6744
IFilePickerState
@@ -143,7 +120,7 @@ export class FilePicker extends React.Component<
143120
accepts: accepts,
144121
context: this.props.context,
145122
onClose: () => this._handleClosePanel(),
146-
onSave: (value: IFilePickerResult) => {
123+
onSave: (value: IFilePickerResult[]) => {
147124
this._handleSave(value);
148125
},
149126
};
@@ -325,14 +302,14 @@ export class FilePicker extends React.Component<
325302
/**
326303
* On save action
327304
*/
328-
private _handleSave = (filePickerResult: IFilePickerResult) => {
305+
private _handleSave = (filePickerResult: IFilePickerResult[]) => {
329306
this.props.onSave(filePickerResult);
330307
this.setState({
331308
panelOpen: false,
332309
});
333310
}
334311

335-
private _handleOnChange = (filePickerResult: IFilePickerResult) => {
312+
private _handleOnChange = (filePickerResult: IFilePickerResult[]) => {
336313
if (this.props.onChange) {
337314
this.props.onChange(filePickerResult);
338315
}

src/controls/filePicker/FilePicker.types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export interface FilePickerBreadcrumbItem extends IBreadcrumbItem {
1111
export interface IFilePickerTab {
1212
context: ExtensionContext | WebPartContext;
1313
accepts: string[];
14-
onSave: (value: IFilePickerResult) => void;
14+
onSave: (value: IFilePickerResult[]) => void;
1515
onClose: () => void;
1616
}
1717

@@ -33,7 +33,7 @@ export interface IFilePickerResult {
3333
fileAbsoluteUrl: string;
3434

3535
/**
36-
* Size of a selected file (in bytes). Undefined in all cases but file upload
36+
* Size of a selected file (in bytes). Undefined in all cases but file upload
3737
*/
3838
fileSize?: number;
3939

src/controls/filePicker/IFilePickerProps.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ export interface IFilePickerProps {
2828
/**
2929
* Handler when the file has been selected
3030
*/
31-
onSave:(filePickerResult: IFilePickerResult)=>void;
31+
onSave:(filePickerResult: IFilePickerResult[])=>void;
3232

3333
/**
3434
* Handler when file has been changed.
3535
*/
36-
onChange?: (filePickerResult: IFilePickerResult) => void;
36+
onChange?: (filePickerResult: IFilePickerResult[]) => void;
3737

3838
/**
3939
* Current context.
@@ -151,7 +151,7 @@ export interface IFilePickerProps {
151151
/**
152152
* Optional additional renderer for Multiple Upload tab
153153
*/
154-
renderCustomMultipleUploadTabContent?: (filePickerResult: IFilePickerResult) => JSX.Element | null;
154+
renderCustomMultipleUploadTabContent?: (filePickerResult: IFilePickerResult[]) => JSX.Element | null;
155155

156156
/**
157157
* Specifies if Site Pages library to be visible on Sites tab

src/controls/filePicker/LinkFilePickerTab/LinkFilePickerTab.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export default class LinkFilePickerTab extends React.Component<ILinkFilePickerTa
109109
* Handles when user saves
110110
*/
111111
private _handleSave = () => {
112-
this.props.onSave(this.state.filePickerResult);
112+
this.props.onSave([this.state.filePickerResult]);
113113
}
114114

115115
/**
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { IFilePickerResult, IFilePickerTab } from "../FilePicker.types";
22

33
export interface IMultipleUploadFilePickerTabProps extends IFilePickerTab {
4-
onChange: (value: IFilePickerResult) => void;
5-
renderCustomMultipleUploadTabContent: (filePickerResult: IFilePickerResult) => JSX.Element | null;
4+
onChange: (value: IFilePickerResult[]) => void;
5+
renderCustomMultipleUploadTabContent: (filePickerResult: IFilePickerResult[]) => JSX.Element | null;
66
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { IFilePickerResult } from "../FilePicker.types";
22

33
export interface IMultipleUploadFilePickerTabState {
4-
filePickerResult: IFilePickerResult;
4+
filePickerResult: IFilePickerResult[];
55
filePreview?: string;
6-
filesResult?: any;
6+
// filesResult?: any;
77
}

src/controls/filePicker/MultipleUploadFilePickerTab/MultipleUploadFilePickerTab.tsx

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import styles from './MultipleUploadFilePickerTab.module.scss';
1414

1515

1616
export default class MultipleUploadFilePicketTab extends React.Component<IMultipleUploadFilePickerTabProps, IMultipleUploadFilePickerTabState> {
17-
private _files: File[] = [];
1817

1918
constructor(props: IMultipleUploadFilePickerTabProps) {
2019
super(props);
@@ -23,16 +22,16 @@ export default class MultipleUploadFilePicketTab extends React.Component<IMultip
2322
};
2423
}
2524

26-
private displayFileNames = (filesResult) => {
25+
private displayFileNames = (filesResult: IFilePickerResult[]) => {
2726
const result = [];
2827
for (var i = 0; i < filesResult.length; i++) {
29-
result.push(<div key={i.toString()} className={styles.localTabFilename}>{filesResult[i].name}</div>);
28+
result.push(<div key={i.toString()} className={styles.localTabFilename}>{filesResult[i].fileName}</div>);
3029
}
3130
return result;
3231
}
3332

3433
public render(): React.ReactElement<IMultipleUploadFilePickerTabProps> {
35-
const { filesResult } = this.state;
34+
const { filePickerResult } = this.state;
3635
const acceptedFilesExtensions = this.props.accepts ? this.props.accepts.join(",") : null;
3736

3837
return (
@@ -44,7 +43,7 @@ export default class MultipleUploadFilePicketTab extends React.Component<IMultip
4443

4544
<DragDropFiles
4645
iconName="BulkUpload"
47-
onDrop={this._handleFileUploadDragDrop}
46+
onDrop={this._handleFileUpload}
4847
>
4948
<div className={styles.localTabDragDropFile}>
5049
<input
@@ -60,15 +59,15 @@ export default class MultipleUploadFilePicketTab extends React.Component<IMultip
6059
</DragDropFiles>
6160

6261
<div>
63-
{(filesResult) ? this.displayFileNames(filesResult) : ""}
62+
{(filePickerResult) ? this.displayFileNames(filePickerResult) : ""}
6463
</div>
6564

6665
{this.props.renderCustomMultipleUploadTabContent && this.props.renderCustomMultipleUploadTabContent(this.state.filePickerResult)}
6766
</div>
6867
<div className={styles.actionButtonsContainer}>
6968
<div className={styles.actionButtons}>
7069
<PrimaryButton
71-
disabled={!filesResult}
70+
disabled={!filePickerResult}
7271
onClick={() => this._handleSave()} className={styles.actionButton}>{strings.ListItemAttachmentslPlaceHolderButtonLabel + " " + strings.OneDriveRootFolderName}</PrimaryButton>
7372
<DefaultButton onClick={() => this._handleClose()} className={styles.actionButton}>{strings.CancelButtonLabel}</DefaultButton>
7473
</div>
@@ -80,14 +79,30 @@ export default class MultipleUploadFilePicketTab extends React.Component<IMultip
8079
/**
8180
* Gets called when files are uploaded via drag and drop
8281
*/
83-
private _handleFileUploadDragDrop = (files) => {
82+
private _handleFileUpload = (files: File[]) => {
8483

8584
if (files.length < 1) {
8685
return;
8786
} else {
87+
88+
const filePickerResultsArray: IFilePickerResult[] = [];
89+
for (var i = 0; i < files.length; i++) {
90+
const file: File = files[i];
91+
const filePickerResult: IFilePickerResult = {
92+
fileAbsoluteUrl: null,
93+
fileName: file.name,
94+
fileSize: file.size,
95+
fileNameWithoutExtension: GeneralHelper.getFileNameWithoutExtension(file.name),
96+
downloadFileContent: () => { return Promise.resolve(file); }
97+
};
98+
filePickerResultsArray.push(filePickerResult);
99+
}
100+
88101
this.setState({
89-
filesResult: files
102+
filePickerResult: filePickerResultsArray
90103
});
104+
105+
this.props.onChange(filePickerResultsArray);
91106
}
92107
}
93108

@@ -98,9 +113,13 @@ export default class MultipleUploadFilePicketTab extends React.Component<IMultip
98113
if (!event || event.target.files.length < 1) {
99114
return;
100115
} else {
101-
this.setState({
102-
filesResult: event.target.files
103-
});
116+
117+
const files: File[] = [];
118+
for (var i = 0; i < event.target.files.length; i++) {
119+
const file: File = event.target.files.item(i);
120+
files.push(file);
121+
}
122+
this._handleFileUpload(files);
104123
}
105124

106125
}
@@ -109,19 +128,7 @@ export default class MultipleUploadFilePicketTab extends React.Component<IMultip
109128
* Saves base64 encoded image back to property pane file picker
110129
*/
111130
private _handleSave = () => {
112-
if (this.state.filesResult) {
113-
const files: File[] = this.state.filesResult;
114-
for (var i = 0; i < files.length; i++) {
115-
const filePickerResult: IFilePickerResult = {
116-
fileAbsoluteUrl: null,
117-
fileName: files[i].name,
118-
fileSize: files[i].size,
119-
fileNameWithoutExtension: GeneralHelper.getFileNameWithoutExtension(files[i].name),
120-
downloadFileContent: () => { return Promise.resolve(files[i]); }
121-
};
122-
this.props.onSave(filePickerResult);
123-
}
124-
}
131+
this.props.onSave(this.state.filePickerResult);
125132
}
126133

127134
/**

src/controls/filePicker/OneDriveFilesTab/OneDriveFilesTab.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ export class OneDriveFilesTab extends React.Component<IOneDriveFilesTabProps, IO
143143
* Called when user saves
144144
*/
145145
private _handleSave = () => {
146-
this.props.onSave(this.state.filePickerResult);
146+
this.props.onSave([this.state.filePickerResult]);
147147
}
148148

149149
/**

src/controls/filePicker/RecentFilesTab/RecentFilesTab.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ export default class RecentFilesTab extends React.Component<IRecentFilesTabProps
249249
* Gets called when it is time to save the currently selected item
250250
*/
251251
private _handleSave = () => {
252-
this.props.onSave(this.state.filePickerResult);
252+
this.props.onSave([this.state.filePickerResult]);
253253
}
254254

255255
/**

0 commit comments

Comments
 (0)