Skip to content
Snippets Groups Projects
Commit f6be9dde authored by Klaas Kliffen's avatar Klaas Kliffen :satellite:
Browse files

Merge branch 'add-fileinput' into 'main'

feat: initial FileInput

See merge request !37
parents 5cd84a1c 622589b4
No related branches found
No related tags found
1 merge request!37feat: initial FileInput
Pipeline #75423 passed
import {
ChangeEvent,
Dispatch,
SetStateAction,
useEffect,
useRef,
InputHTMLAttributes,
useId,
} from "react";
import Typography from "src/components/Typography";
interface Props extends InputHTMLAttributes<HTMLInputElement> {
label: string;
files: File[];
onFilesChange: Dispatch<SetStateAction<File[]>>;
}
/**
* Controlled File Input component
* @param props
* @constructor
*/
const FileInput = (props: Props) => {
const inputRef = useRef<HTMLInputElement>(null);
const inputId = useId();
const { files, onFilesChange, label, ...rest } = props;
/**
* Workaround for not being able to set the value directly on <input type="file">
* Using the https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer
* API to construct a valid file list; not available in IE!
*/
useEffect(() => {
const dataTransfer = new DataTransfer();
files.forEach((file: File) => dataTransfer.items.add(file));
if (inputRef.current) inputRef.current.files = dataTransfer.files;
}, [files]);
const handleChange = (evt: ChangeEvent<HTMLInputElement>) => {
const fileList = evt.currentTarget.files;
if (!fileList) {
onFilesChange([]);
} else {
onFilesChange(Array.from(fileList));
}
};
return (
<div className="flex flex-col">
<label htmlFor={inputId} className="-mt-3.5">
<Typography text={label} variant="paragraph" />
</label>
<input
className="mt-3"
id={inputId}
ref={inputRef}
type="file"
onChange={handleChange}
{...rest}
/>
</div>
);
};
export default FileInput;
...@@ -6,6 +6,7 @@ export { default as Button } from "./components/Button"; ...@@ -6,6 +6,7 @@ export { default as Button } from "./components/Button";
export { default as Checkbox } from "./components/Checkbox"; export { default as Checkbox } from "./components/Checkbox";
export { default as DateInput } from "./components/DateInput"; export { default as DateInput } from "./components/DateInput";
export { default as Dropdown } from "./components/Dropdown"; export { default as Dropdown } from "./components/Dropdown";
export { default as FileInput } from "./components/FileInput";
export { default as Icon } from "./components/Icon"; export { default as Icon } from "./components/Icon";
export { default as Modal } from "./components/Modal"; export { default as Modal } from "./components/Modal";
export { default as ProgressBar } from "./components/ProgressBar"; export { default as ProgressBar } from "./components/ProgressBar";
......
import { Meta } from "@storybook/react";
import { useState } from "react";
import FileInput from "src/components/FileInput";
const meta = {
title: "Components/File Input",
component: FileInput,
} satisfies Meta;
export default meta;
export const SimpleFileInput = () => {
const [files, setFiles] = useState<File[]>([]);
return (
<FileInput label="My File Input" files={files} onFilesChange={setFiles} />
);
};
export const UseSelectedFile = () => {
const [files, setFiles] = useState<File[]>([]);
return (
<div>
<FileInput label="Managed file" files={files} onFilesChange={setFiles} />
<div>
Selected file:
<br />
{files.length === 0 ? "No file selected." : files[0].name}
</div>
</div>
);
};
export const MultipleFilesInput = () => {
const [files, setFiles] = useState<File[]>([]);
return (
<div>
<FileInput
label="Show me them files!"
files={files}
onFilesChange={setFiles}
multiple
/>
<ul>
<li>Selected files:</li>
{files.length === 0 && <li>No files selected.</li>}
{files.map((f) => (
<li key={f.name}>{f.name}</li>
))}
</ul>
</div>
);
};
export const AcceptOnlyTxtFiles = () => {
const [files, setFiles] = useState<File[]>([]);
return (
<div>
<FileInput
label="Text Only!"
files={files}
onFilesChange={setFiles}
accept="text/plain"
/>
<div>
Selected file:
<br />
{files.length === 0 ? "No file selected." : files[0].name}
</div>
</div>
);
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment