FileUpload
View .mdStyled drop zone exposing stable react-dropzone options and render state.
Default
Loading interactive example. Full source is available below.
View example source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import * as React from 'react';
import { MessageBar } from "@coreai-ds/react-components";
import { MessageBarBody } from "@coreai-ds/react-components";
import { MessageBarTitle } from "@coreai-ds/react-components";
import { FileUpload } from "@coreai-ds/react-components";
import { Text } from "@coreai-ds/react-components";
export const Default = () => {
const maxFileSize = 5 * 1024 * 1024;
const acceptedFileTypes = {
'application/pdf': ['.pdf'],
'text/plain': ['.txt'],
'image/png': ['.png'],
'image/jpeg': ['.jpg', '.jpeg'],
};
const instructionsId = React.useId();
const errorId = React.useId();
const [names, setNames] = React.useState<string[]>([]);
const [error, setError] = React.useState<string>();
const styles = /* css */ `
.file-upload-default-example {
display: flex;
flex-direction: column;
max-width: 720px;
padding: var(--spacingHorizontalL);
row-gap: var(--spacingVerticalL);
}
`;
return (<>
<style>{styles}</style>
<div className="file-upload-default-example">
{error ? (<MessageBar id={errorId} intent="error">
<MessageBarBody>
<MessageBarTitle>Unable to add file</MessageBarTitle>
{error}
</MessageBarBody>
</MessageBar>) : null}
<FileUpload accept={acceptedFileTypes} ariaDescribedBy={error ? `${instructionsId} ${errorId}` : instructionsId} ariaLabel="Choose files" maxSize={maxFileSize} minSize={1} onDropAccepted={(added) => {
setError(undefined);
setNames(added.map((file) => file.name));
}} onDropRejected={(rejections) => setError(rejections
.map(({ file, errors }) => `${file.name}: ${errors.map(({ message }) => message).join(', ')}`)
.join('; '))}>
{({ isDragActive, isDragAccept, isDragReject }) => (<>
<Text preset="body">
{isDragReject
? 'This file is not supported'
: isDragAccept
? 'Drop to add the file'
: isDragActive
? 'Checking file…'
: 'Drag files here or click to browse'}
</Text>
<Text id={instructionsId} foreground={3} preset="bodyS">
PDF, TXT, PNG, or JPEG · Max 5 MB
</Text>
</>)}
</FileUpload>
{names.length > 0 ? <Text preset="bodyS">Selected: {names.join(', ')}</Text> : null}
</div>
</>);
};
export default Default;
API referenceGenerated CoreAI prop and type summaries
API
Generated CoreAI prop summaries, not replacement type declarations. Most native DOM/event and ARIA props are omitted. ? means optional; JSDoc shows defaults, deprecations, and conditional requirements. Defaults assume the standard CoreAI provider.
FileUpload API
interface FileUploadProps {
accept?: Accept;
/**
* IDs of elements that describe the drop zone and its file input.
*/
ariaDescribedBy?: string;
/**
* Localized accessible name for the drop zone and its file input.
*/
ariaLabel: string;
/**
* Presentational content shown inside the drop zone, optionally based on its
* current drag state. Do not include interactive controls such as buttons or
* links because the root is already interactive.
*/
children: ReactNode | ((state: FileUploadState) => ReactNode);
className?: string;
disabled?: boolean;
maxFiles?: number;
maxSize?: number;
minSize?: number;
onDrop?: (<T extends File>(acceptedFiles: T[], fileRejections: FileRejection[], event: DropEvent) => void);
onDropAccepted?: (<T extends File>(files: T[], event: DropEvent) => void);
onDropRejected?: ((fileRejections: FileRejection[], event: DropEvent) => void);
/**
* Allows getting a ref to the component instance.
* Once the component unmounts, React will set `ref.current` to `null`
* (or call the ref with `null` if you passed a callback ref).
*/
ref?: LegacyRef<HTMLDivElement>;
validator?: (<T extends File>(file: T) => FileError | readonly FileError[] | null);
}
Implementation
CoreAI 0.12.0; Fluent 9.74.9.
Import components from @coreai-ds/react-components. CoreAI guidance above takes precedence over API demonstration examples.
Custom Content
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import * as React from 'react';
import { MessageBar } from "@coreai-ds/react-components";
import { MessageBarBody } from "@coreai-ds/react-components";
import { MessageBarTitle } from "@coreai-ds/react-components";
import { FileUpload } from "@coreai-ds/react-components";
import { DocumentArrowUp24Regular } from "@fluentui/react-icons";
import { Text } from "@coreai-ds/react-components";
export const CustomDropZoneContent = () => {
const maxFileSize = 5 * 1024 * 1024;
const instructionsId = React.useId();
const errorId = React.useId();
const [selectedFile, setSelectedFile] = React.useState<File>();
const [error, setError] = React.useState<string>();
const styles = /* css */ `
.file-upload-custom-content-example {
display: flex;
flex-direction: column;
max-width: 720px;
padding: var(--spacingHorizontalL);
row-gap: var(--spacingVerticalL);
}
.file-upload-custom-content-example__drop-zone {
align-items: center;
background-color: var(--colorBrandBackground2);
border: var(--strokeWidthThin) solid var(--colorBrandStroke1);
min-height: 180px;
padding: var(--spacingVerticalXXL);
}
.file-upload-custom-content-example__drop-zone:hover {
background-color: var(--colorBrandBackground2Hover);
}
.file-upload-custom-content-example__drop-zone:active {
background-color: var(--colorBrandBackground2Pressed);
}
.file-upload-custom-content-example__upload-icon {
margin-bottom: var(--spacingVerticalXXS);
}
`;
return (<>
<style>{styles}</style>
<div className="file-upload-custom-content-example">
{error ? (<MessageBar id={errorId} intent="error">
<MessageBarBody>
<MessageBarTitle>Unable to add file</MessageBarTitle>
{error}
</MessageBarBody>
</MessageBar>) : null}
<FileUpload ariaLabel={selectedFile ? 'Replace knowledge file' : 'Select a knowledge file'} ariaDescribedBy={error ? `${instructionsId} ${errorId}` : instructionsId} accept={{
'application/pdf': ['.pdf'],
'text/plain': ['.txt'],
'text/markdown': ['.md'],
}} className="file-upload-custom-content-example__drop-zone" maxSize={maxFileSize} multiple={false} onDropAccepted={(added) => {
setError(undefined);
setSelectedFile(added[0]);
}} onDropRejected={(rejections) => setError(rejections
.map(({ file, errors }) => `${file.name}: ${errors.map(({ message }) => message).join(', ')}`)
.join('; '))}>
<DocumentArrowUp24Regular aria-hidden className="file-upload-custom-content-example__upload-icon"/>
<Text preset="heading5">
{selectedFile ? selectedFile.name : 'Select a knowledge file'}
</Text>
<Text foreground={3} preset="bodyS">
{selectedFile
? 'Drop another file or click to replace this selection'
: 'Drop a product guide or click to browse'}
</Text>
<Text foreground={3} preset="bodyS">
PDF, TXT, or Markdown · Max 5 MB
</Text>
</FileUpload>
</div>
</>);
};
export default CustomDropZoneContent;
Disabled
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import * as React from 'react';
import { FileUpload } from "@coreai-ds/react-components";
import { Text } from "@coreai-ds/react-components";
export const Disabled = () => {
const instructionsId = React.useId();
const styles = /* css */ `
.file-upload-disabled-example {
display: flex;
flex-direction: column;
max-width: 720px;
padding: var(--spacingHorizontalL);
row-gap: var(--spacingVerticalL);
}
`;
return (<>
<style>{styles}</style>
<FileUpload className="file-upload-disabled-example" ariaDescribedBy={instructionsId} ariaLabel="File upload unavailable" disabled>
<Text foreground="disabled" preset="body">
File upload is unavailable
</Text>
<Text id={instructionsId} foreground="disabled" preset="bodyS">
Contact your administrator for access.
</Text>
</FileUpload>
</>);
};
export default Disabled;
With File Grid
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import * as React from 'react';
import { Spinner } from "@coreai-ds/react-components";
import { Badge } from "@coreai-ds/react-components";
import { TableCellLayout } from "@coreai-ds/react-components";
import { Button } from "@coreai-ds/react-components";
import { ArrowClockwise20Regular } from "@fluentui/react-icons";
import { Delete20Regular } from "@fluentui/react-icons";
import { MessageBar } from "@coreai-ds/react-components";
import { MessageBarBody } from "@coreai-ds/react-components";
import { MessageBarTitle } from "@coreai-ds/react-components";
import { FileUpload } from "@coreai-ds/react-components";
import { Text } from "@coreai-ds/react-components";
import { DataGrid } from "@coreai-ds/react-components";
import { DataGridHeader } from "@coreai-ds/react-components";
import { DataGridRow } from "@coreai-ds/react-components";
import { DataGridHeaderCell } from "@coreai-ds/react-components";
import { DataGridBody } from "@coreai-ds/react-components";
import { DataGridCell } from "@coreai-ds/react-components";
export const WithFileGrid = () => {
type UploadStatus = 'pending' | 'uploading' | 'uploaded' | 'failed';
interface UploadItem {
id: string;
name: string;
size: number;
status: UploadStatus;
}
const maxFileSize = 5 * 1024 * 1024;
const acceptedFileTypes = {
'application/pdf': ['.pdf'],
'text/plain': ['.txt'],
'image/png': ['.png'],
'image/jpeg': ['.jpg', '.jpeg'],
};
const initialFiles: UploadItem[] = [
{
id: 'product-guide.pdf',
name: 'product-guide.pdf',
size: 1842176,
status: 'uploaded',
},
];
const createUploadItem = (file: File): UploadItem => ({
id: `${file.name}-${file.lastModified}-${file.size}`,
name: file.name,
size: file.size,
status: 'pending',
});
const formatFileSize = (bytes: number): string => {
const megabytes = bytes / (1024 * 1024);
return `${megabytes.toLocaleString(undefined, { maximumFractionDigits: 2 })} MB`;
};
const instructionsId = React.useId();
const errorId = React.useId();
const [files, setFiles] = React.useState<UploadItem[]>(initialFiles);
const [error, setError] = React.useState<string>();
const handleRetry = (file: UploadItem) => {
setFiles((prev) => prev.map((item) => (item.id === file.id ? { ...item, status: 'uploading' } : item)));
window.setTimeout(() => {
setFiles((prev) => prev.map((item) => (item.id === file.id ? { ...item, status: 'uploaded' } : item)));
}, 800);
};
const handleDelete = (file: UploadItem) => {
setFiles((prev) => prev.filter((item) => item.id !== file.id));
};
const renderUploadStatus = (status: UploadStatus): React.ReactNode => {
if (status === 'uploading') {
return <Spinner as="span" label="Uploading" labelPosition="after" size="tiny"/>;
}
if (status === 'uploaded') {
return (<Badge appearance="tint" color="success">
Uploaded
</Badge>);
}
if (status === 'failed') {
return (<Badge appearance="tint" color="danger">
Failed
</Badge>);
}
return 'Pending';
};
const columns = [
{
columnId: 'name',
compare: (a: UploadItem, b: UploadItem) => a.name.localeCompare(b.name),
renderHeaderCell: () => 'Name',
renderCell: (item: UploadItem) => <TableCellLayout truncate>{item.name}</TableCellLayout>,
},
{
columnId: 'size',
compare: (a: UploadItem, b: UploadItem) => a.size - b.size,
renderHeaderCell: () => 'Size',
renderCell: (item: UploadItem) => (<TableCellLayout>{formatFileSize(item.size)}</TableCellLayout>),
},
{
columnId: 'status',
compare: (a: UploadItem, b: UploadItem) => a.status.localeCompare(b.status),
renderHeaderCell: () => 'Status',
renderCell: (item: UploadItem) => (<TableCellLayout>{renderUploadStatus(item.status)}</TableCellLayout>),
},
{
columnId: 'actions',
compare: () => 0,
renderHeaderCell: () => 'Actions',
renderCell: (item: UploadItem) => (<TableCellLayout className="file-upload-grid-example__actions">
{item.status === 'failed' ? (<Button appearance="subtle" aria-label={`Retry ${item.name}`} icon={<ArrowClockwise20Regular aria-hidden/>} onClick={() => handleRetry(item)}/>) : null}
<Button appearance="subtle" aria-label={`Delete ${item.name}`} disabled={item.status === 'uploading'} icon={<Delete20Regular aria-hidden/>} onClick={() => handleDelete(item)}/>
</TableCellLayout>),
},
];
const handleFilesAdded = (added: readonly File[]) => {
setError(undefined);
setFiles((prev) => {
const next = [...prev];
for (const file of added) {
const item = createUploadItem(file);
if (!next.some((existing) => existing.id === item.id)) {
next.push(item);
}
}
return next;
});
// Simulate upload progress for newly added files.
for (const file of added) {
const id = createUploadItem(file).id;
window.setTimeout(() => {
setFiles((prev) => prev.map((item) => (item.id === id ? { ...item, status: 'uploading' } : item)));
}, 200);
window.setTimeout(() => {
setFiles((prev) => prev.map((item) => item.id === id
? {
...item,
status: file.name.toLowerCase().includes('fail') ? 'failed' : 'uploaded',
}
: item));
}, 1200);
}
};
const styles = /* css */ `
.file-upload-grid-example {
display: flex;
flex-direction: column;
max-width: 720px;
padding: var(--spacingHorizontalL);
row-gap: var(--spacingVerticalL);
}
.file-upload-grid-example__actions {
align-items: center;
column-gap: var(--spacingHorizontalXXS);
display: flex;
}
`;
return (<>
<style>{styles}</style>
<div className="file-upload-grid-example">
{error ? (<MessageBar id={errorId} intent="error">
<MessageBarBody>
<MessageBarTitle>Unable to add file</MessageBarTitle>
{error}
</MessageBarBody>
</MessageBar>) : null}
<FileUpload accept={acceptedFileTypes} ariaDescribedBy={error ? `${instructionsId} ${errorId}` : instructionsId} ariaLabel="Choose files" maxSize={maxFileSize} minSize={1} multiple onDropAccepted={handleFilesAdded} onDropRejected={(rejections) => setError(rejections
.map(({ file, errors }) => `${file.name}: ${errors.map(({ message }) => message).join(', ')}`)
.join('; '))}>
<Text preset="body">Drag files here or click to browse</Text>
<Text id={instructionsId} foreground={3} preset="bodyS">
PDF, TXT, PNG, or JPEG · Max 5 MB
</Text>
</FileUpload>
{files.length === 0 ? (<Text foreground={3} preset="bodyS">
No files yet. Drop or browse to add some.
</Text>) : (<DataGrid aria-label="Selected files" columns={columns} getRowId={(item) => item.id} items={files} sortable>
<DataGridHeader>
<DataGridRow>
{({ renderHeaderCell }) => (<DataGridHeaderCell>{renderHeaderCell()}</DataGridHeaderCell>)}
</DataGridRow>
</DataGridHeader>
<DataGridBody<UploadItem>>
{({ item, rowId }) => (<DataGridRow<UploadItem> key={rowId}>
{({ renderCell, columnId }) => (<DataGridCell focusMode={columnId === 'actions' ? 'group' : undefined}>
{renderCell(item)}
</DataGridCell>)}
</DataGridRow>)}
</DataGridBody>
</DataGrid>)}
</div>
</>);
};
export default WithFileGrid;