Skip to content

Commit 30cf21d

Browse files
Update file picker to return an array of files
1 parent fa687ec commit 30cf21d

File tree

18 files changed

+92
-88
lines changed

18 files changed

+92
-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 | BaseComponentContext | 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 & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -35,31 +35,6 @@ import { StockImages } from "./StockImagesTab/StockImages";
3535
import UploadFilePickerTab from "./UploadFilePickerTab/UploadFilePickerTab";
3636
import MultipleUploadFilePickerTab from "./MultipleUploadFilePickerTab/MultipleUploadFilePickerTab";
3737
import WebSearchTab from "./WebSearchTab/WebSearchTab";
38-
39-
// Localization
40-
41-
42-
43-
44-
45-
46-
47-
48-
49-
50-
51-
52-
53-
54-
55-
56-
57-
58-
59-
60-
61-
62-
6338

6439

6540
export class FilePicker extends React.Component<
@@ -143,7 +118,7 @@ export class FilePicker extends React.Component<
143118
accepts: accepts,
144119
context: this.props.context,
145120
onClose: () => this._handleClosePanel(),
146-
onSave: (value: IFilePickerResult) => {
121+
onSave: (value: IFilePickerResult[]) => {
147122
this._handleSave(value);
148123
},
149124
};
@@ -325,14 +300,14 @@ export class FilePicker extends React.Component<
325300
/**
326301
* On save action
327302
*/
328-
private _handleSave = (filePickerResult: IFilePickerResult) => {
303+
private _handleSave = (filePickerResult: IFilePickerResult[]) => {
329304
this.props.onSave(filePickerResult);
330305
this.setState({
331306
panelOpen: false,
332307
});
333308
}
334309

335-
private _handleOnChange = (filePickerResult: IFilePickerResult) => {
310+
private _handleOnChange = (filePickerResult: IFilePickerResult[]) => {
336311
if (this.props.onChange) {
337312
this.props.onChange(filePickerResult);
338313
}

src/controls/filePicker/FilePicker.types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export interface FilePickerBreadcrumbItem extends IBreadcrumbItem {
1010
export interface IFilePickerTab {
1111
context: BaseComponentContext;
1212
accepts: string[];
13-
onSave: (value: IFilePickerResult) => void;
13+
onSave: (value: IFilePickerResult[]) => void;
1414
onClose: () => void;
1515
}
1616

src/controls/filePicker/IFilePickerProps.ts

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

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

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

155155
/**
156156
* 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: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { IFilePickerResult } from "../FilePicker.types";
22

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

src/controls/filePicker/MultipleUploadFilePickerTab/MultipleUploadFilePickerTab.tsx

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,16 @@ export default class MultipleUploadFilePickerTab extends React.Component<IMultip
2121
};
2222
}
2323

24-
private displayFileNames = (filesResult) => {
24+
private displayFileNames = (filesResult: IFilePickerResult[]) => {
2525
const result = [];
2626
for (var i = 0; i < filesResult.length; i++) {
27-
result.push(<div key={i.toString()} className={styles.localTabFilename}>{filesResult[i].name}</div>);
27+
result.push(<div key={i.toString()} className={styles.localTabFilename}>{filesResult[i].fileName}</div>);
2828
}
2929
return result;
3030
}
3131

3232
public render(): React.ReactElement<IMultipleUploadFilePickerTabProps> {
33-
const { filesResult } = this.state;
33+
const { filePickerResult } = this.state;
3434
const acceptedFilesExtensions = this.props.accepts ? this.props.accepts.join(",") : null;
3535

3636
return (
@@ -41,7 +41,7 @@ export default class MultipleUploadFilePickerTab extends React.Component<IMultip
4141
<div className={css(styles.tab, styles.tabOffset)}>
4242
<DragDropFiles
4343
iconName="BulkUpload"
44-
onDrop={this._handleFileUploadDragDrop}
44+
onDrop={this._handleFileUpload}
4545
>
4646
<div className={styles.localTabDragDropFile}>
4747
<input
@@ -56,15 +56,15 @@ export default class MultipleUploadFilePickerTab extends React.Component<IMultip
5656
</DragDropFiles>
5757

5858
<div>
59-
{(filesResult) ? this.displayFileNames(filesResult) : ""}
59+
{(filePickerResult) ? this.displayFileNames(filePickerResult) : ""}
6060
</div>
6161

6262
{this.props.renderCustomMultipleUploadTabContent && this.props.renderCustomMultipleUploadTabContent(this.state.filePickerResult)}
6363
</div>
6464
<div className={styles.actionButtonsContainer}>
6565
<div className={styles.actionButtons}>
6666
<PrimaryButton
67-
disabled={!filesResult}
67+
disabled={!filePickerResult}
6868
onClick={() => this._handleSave()} className={styles.actionButton}>{strings.ListItemAttachmentslPlaceHolderButtonLabel + " " + strings.OneDriveRootFolderName}</PrimaryButton>
6969
<DefaultButton onClick={() => this._handleClose()} className={styles.actionButton}>{strings.CancelButtonLabel}</DefaultButton>
7070
</div>
@@ -76,13 +76,29 @@ export default class MultipleUploadFilePickerTab extends React.Component<IMultip
7676
/**
7777
* Gets called when files are uploaded via drag and drop
7878
*/
79-
private _handleFileUploadDragDrop = (files) => {
79+
private _handleFileUpload = (files: File[]) => {
8080
if (files.length < 1) {
8181
return;
8282
} else {
83+
84+
const filePickerResultsArray: IFilePickerResult[] = [];
85+
for (var i = 0; i < files.length; i++) {
86+
const file: File = files[i];
87+
const filePickerResult: IFilePickerResult = {
88+
fileAbsoluteUrl: null,
89+
fileName: file.name,
90+
fileSize: file.size,
91+
fileNameWithoutExtension: GeneralHelper.getFileNameWithoutExtension(file.name),
92+
downloadFileContent: () => { return Promise.resolve(file); }
93+
};
94+
filePickerResultsArray.push(filePickerResult);
95+
}
96+
8397
this.setState({
84-
filesResult: files
98+
filePickerResult: filePickerResultsArray
8599
});
100+
101+
this.props.onChange(filePickerResultsArray);
86102
}
87103
}
88104

@@ -93,29 +109,20 @@ export default class MultipleUploadFilePickerTab extends React.Component<IMultip
93109
if (!event || event.target.files.length < 1) {
94110
return;
95111
} else {
96-
this.setState({
97-
filesResult: event.target.files
98-
});
112+
const files: File[] = [];
113+
for (var i = 0; i < event.target.files.length; i++) {
114+
const file: File = event.target.files.item(i);
115+
files.push(file);
116+
}
117+
this._handleFileUpload(files);
99118
}
100119
}
101120

102121
/**
103122
* Saves base64 encoded image back to property pane file picker
104123
*/
105124
private _handleSave = () => {
106-
if (this.state.filesResult) {
107-
const files: File[] = this.state.filesResult;
108-
for (var i = 0; i < files.length; i++) {
109-
const filePickerResult: IFilePickerResult = {
110-
fileAbsoluteUrl: null,
111-
fileName: files[i].name,
112-
fileSize: files[i].size,
113-
fileNameWithoutExtension: GeneralHelper.getFileNameWithoutExtension(files[i].name),
114-
downloadFileContent: () => { return Promise.resolve(files[i]); }
115-
};
116-
this.props.onSave(filePickerResult);
117-
}
118-
}
125+
this.props.onSave(this.state.filePickerResult);
119126
}
120127

121128
/**

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
/**

src/controls/filePicker/SiteFilePickerTab/SiteFilePickerTab.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ export default class SiteFilePickerTab extends React.Component<ISiteFilePickerTa
129129
* Called when user saves
130130
*/
131131
private _handleSave = () => {
132-
this.props.onSave(this.state.filePickerResult);
132+
this.props.onSave([this.state.filePickerResult]);
133133
}
134134

135135
/**

src/controls/filePicker/StockImagesTab/StockImages.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export class StockImages extends React.Component<IStockImagesProps> {
6161
};
6262
}
6363

64-
this.props.onSave(filePickerResult);
64+
this.props.onSave([filePickerResult]);
6565
}
6666

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

33
export interface IUploadFilePickerTabProps extends IFilePickerTab {
4-
onChange: (value: IFilePickerResult) => void;
4+
onChange: (value: IFilePickerResult[]) => void;
55
renderCustomUploadTabContent: (filePickerResult: IFilePickerResult) => JSX.Element | null;
66
}

src/controls/filePicker/UploadFilePickerTab/UploadFilePickerTab.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,14 @@ export default class UploadFilePickerTab extends React.Component<IUploadFilePick
104104
filePreview: undefined
105105
});
106106

107-
this.props.onChange(filePickerResult);
107+
this.props.onChange([filePickerResult]);
108108
}
109109

110110
/**
111111
* Saves base64 encoded image back to property pane file picker
112112
*/
113113
private _handleSave = () => {
114-
this.props.onSave(this.state.filePickerResult);
114+
this.props.onSave([this.state.filePickerResult]);
115115
}
116116

117117
/**

src/controls/filePicker/WebSearchTab/WebSearchTab.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ export default class WebSearchTab extends React.Component<IWebSearchTabProps, IW
448448
* Calls property pane file picker's save function
449449
*/
450450
private _handleSave = () => {
451-
this.props.onSave(this.state.filePickerResult);
451+
this.props.onSave([this.state.filePickerResult]);
452452
}
453453

454454
/**

0 commit comments

Comments
 (0)