← All proposals

Dropzone

A net-new component, `Dropzone`:

Net-new componentOpen in Storybook

proposals/Dropzone/Dropzone.stories.tsx82 lines · View on GitHub
import { useState } from 'react';

import { Meta, StoryObj } from '@storybook/react-vite';

import { Dropzone } from './Dropzone';

const meta: Meta<typeof Dropzone> = {
	tags: ['new-pattern', 'ready-for-dev'],
	title: 'Component Proposals/Dropzone',
	component: Dropzone,
	parameters: {
		docs: {
			description: {
				component:
					'**New pattern:** not in PDS v2 today. A compact area to add files by dragging them in or by browsing: a dashed outline with an upload icon and one line of text, the end of which ("browse") is a button that opens the file dialog. PDS has the File Upload (a form field with a choose-file button), but no drag and drop.',
			},
		},
	},
	argTypes: {
		accept: { control: 'text', table: { category: 'Component configuration' } },
		browseLabel: { control: 'text', table: { category: 'Content' } },
		className: { table: { category: 'Base attributes' } },
		disabled: {
			control: 'boolean',
			table: { category: 'Component configuration' },
		},
		label: { control: 'text', table: { category: 'Content' } },
		multiple: {
			control: 'boolean',
			table: { category: 'Component configuration' },
		},
		onFilesDrop: { table: { category: 'Functions' } },
	},
	args: {
		accept: 'image/*,.pdf',
		browseLabel: 'browse',
		disabled: false,
		label: 'Drop an image or brief, or',
		multiple: true,
	},
};

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {};

export const Disabled: Story = {
	args: { disabled: true },
};

export const Interactive: Story = {
	parameters: {
		docs: {
			description: {
				story:
					'Drag files from your computer onto the zone, or press browse. The zone highlights while a file is over it. The screen owns the list: here the file names are listed under the zone, as a stand-in for chips or previews. `accept` filters the drop as well as the dialog.',
			},
		},
	},
	render: (args) => {
		const [names, setNames] = useState<string[]>([]);

		return (
			<div style={{ display: 'grid', gap: 'var(--pds-spacing-s)' }}>
				<Dropzone
					{...args}
					onFilesDrop={(files) =>
						setNames((list) => [...list, ...files.map((file) => file.name)])
					}
				/>
				<ul className='pds-copy-s'>
					{names.map((name, index) => (
						<li key={`${name}-${index}`}>{name}</li>
					))}
				</ul>
			</div>
		);
	},
};