Skip to content

Commit 979712d

Browse files
docs: add CsvImporter component documentation (#152)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent c6afaca commit 979712d

1 file changed

Lines changed: 212 additions & 0 deletions

File tree

docs/components/csv-importer.md

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
---
2+
title: CsvImporter
3+
description: Guided multi-step CSV import flow with drag-and-drop upload, column mapping, validation, and inline error correction
4+
---
5+
6+
# CsvImporter
7+
8+
The `CsvImporter` component provides a guided, multi-step CSV import flow rendered inside a drawer. It handles drag-and-drop file upload, interactive column mapping, Standard Schema validation, inline cell editing, and async server-side validation.
9+
10+
## Import
11+
12+
```tsx
13+
import {
14+
CsvImporter,
15+
useCsvImporter,
16+
csv,
17+
type CsvSchema,
18+
type CsvColumn,
19+
type CsvImportEvent,
20+
type CsvCellIssue,
21+
type CsvCorrection,
22+
type CsvColumnMapping,
23+
type ParsedRow,
24+
} from "@tailor-platform/app-shell";
25+
```
26+
27+
## Basic Usage
28+
29+
Use the `useCsvImporter` hook to manage state, then render `<CsvImporter>` with the returned props.
30+
31+
```tsx
32+
import { CsvImporter, useCsvImporter, csv } from "@tailor-platform/app-shell";
33+
import { Button } from "@tailor-platform/app-shell";
34+
35+
function ProductImport() {
36+
const { open, props } = useCsvImporter({
37+
schema: {
38+
columns: [
39+
{
40+
key: "name",
41+
label: "Name",
42+
required: true,
43+
aliases: ["product_name"],
44+
schema: csv.string({ min: 1 }),
45+
},
46+
{
47+
key: "price",
48+
label: "Price",
49+
schema: csv.number({ min: 0 }),
50+
},
51+
{
52+
key: "active",
53+
label: "Active",
54+
schema: csv.boolean(),
55+
},
56+
],
57+
},
58+
onImport: (event) => {
59+
console.log(event.summary);
60+
},
61+
});
62+
63+
return (
64+
<>
65+
<Button onClick={open}>Import CSV</Button>
66+
<CsvImporter {...props} />
67+
</>
68+
);
69+
}
70+
```
71+
72+
## `useCsvImporter`
73+
74+
The `useCsvImporter` hook manages the open/close state and returns both an `open` function and `props` to spread onto `<CsvImporter>`.
75+
76+
### Options
77+
78+
| Option | Type | Default | Description |
79+
| ------------- | -------------------------------------------------- | ------------------ | --------------------------------------------------------------------------- |
80+
| `schema` | `CsvSchema` || Column definitions for the import (see [CsvSchema](#csvschema)) |
81+
| `onImport` | `(event: CsvImportEvent) => void \| Promise<void>` || Called when the user confirms the import after resolving all errors |
82+
| `onValidate` | `(rows: ParsedRow[]) => Promise<CsvCellIssue[]>` || Optional async callback for server-side validation after schema checks pass |
83+
| `maxFileSize` | `number` | `10485760` (10 MB) | Maximum allowed file size in bytes |
84+
85+
### Return Value
86+
87+
| Property | Type | Description |
88+
| -------- | ------------------ | ----------------------------------------------------- |
89+
| `open` | `() => void` | Function to programmatically open the importer drawer |
90+
| `props` | `CsvImporterProps` | Props object to spread directly onto `<CsvImporter>` |
91+
92+
## `CsvImporter` Props
93+
94+
`CsvImporter` is intended to be used with props returned from `useCsvImporter`. Spread the `props` object directly onto the component.
95+
96+
```tsx
97+
<CsvImporter {...props} />
98+
```
99+
100+
## Schemas
101+
102+
### `CsvSchema`
103+
104+
```ts
105+
type CsvSchema = {
106+
columns: CsvColumn[];
107+
};
108+
```
109+
110+
### `CsvColumn`
111+
112+
| Property | Type | Default | Description |
113+
| ------------- | ------------------ | ------- | --------------------------------------------------------------------------------------- |
114+
| `key` | `string` || Internal key; becomes the object key in parsed row data |
115+
| `label` | `string` || Display label shown in the mapping UI |
116+
| `description` | `string` || Optional hint shown in the mapping UI |
117+
| `required` | `boolean` | `false` | Whether this column must be mapped before proceeding |
118+
| `aliases` | `string[]` || Alternative CSV header names for automatic matching |
119+
| `schema` | `StandardSchemaV1` || Standard Schema validator for coercion and validation (see [csv helpers](#csv-helpers)) |
120+
121+
## `csv` Helpers
122+
123+
Built-in Standard Schema validators for common CSV column types. Each helper handles both coercion and validation in a single declaration.
124+
125+
| Helper | Output type | Description |
126+
| ------------------------------------------------------------------------- | ------------------ | ----------------------------------------------------------------------- |
127+
| `csv.string(options?: { min?: number; max?: number })` | `string` | Pass-through string with optional length constraints |
128+
| `csv.number(options?: { min?: number; max?: number; integer?: boolean })` | `number` | Coerces raw CSV string to a number; rejects `NaN` |
129+
| `csv.boolean(options?: { truthy?: string[]; falsy?: string[] })` | `boolean` | Recognises `"true"/"1"/"yes"` and `"false"/"0"/"no"` (case-insensitive) |
130+
| `csv.date()` | `Date` | Coerces raw CSV string to a `Date`; rejects unparseable values |
131+
| `csv.enum(values: string[])` | `T extends string` | Validates the value is one of the allowed strings (case-sensitive) |
132+
133+
```tsx
134+
// String with required check (min: 1)
135+
csv.string({ min: 1 });
136+
137+
// Number with lower bound
138+
csv.number({ min: 0 });
139+
140+
// Integer only
141+
csv.number({ integer: true });
142+
143+
// Boolean with defaults: truthy = ["true","1","yes"], falsy = ["false","0","no"]
144+
csv.boolean();
145+
146+
// Date
147+
csv.date();
148+
149+
// Enum
150+
csv.enum(["active", "inactive", "pending"]);
151+
```
152+
153+
## Server-side Validation
154+
155+
Use `onValidate` to perform async checks (e.g. uniqueness constraints) after schema validation passes. Return an array of `CsvCellIssue` objects to mark cells with errors or warnings.
156+
157+
```tsx
158+
const { open, props } = useCsvImporter({
159+
schema: { columns: [...] },
160+
onValidate: async (rows) => {
161+
const issues = await checkUniqueness(rows);
162+
return issues; // CsvCellIssue[]
163+
},
164+
onImport: (event) => { /* ... */ },
165+
});
166+
```
167+
168+
### `CsvCellIssue`
169+
170+
| Property | Type | Description |
171+
| ----------- | ---------------------- | ---------------------------------------------------- |
172+
| `rowIndex` | `number` | 0-based row index |
173+
| `columnKey` | `string` | The schema column key |
174+
| `level` | `"error" \| "warning"` | `"error"` blocks import; `"warning"` allows import |
175+
| `message` | `string` | Human-readable message displayed inline in the table |
176+
177+
## Import Event
178+
179+
The `onImport` callback receives a `CsvImportEvent` with the following shape:
180+
181+
| Property | Type | Description |
182+
| ------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------ |
183+
| `file` | `File` | The original file selected by the user |
184+
| `mappings` | `CsvColumnMapping[]` | The confirmed column mappings |
185+
| `corrections` | `CsvCorrection[]` | Corrections made by the user in the review step |
186+
| `issues` | `CsvCellIssue[]` | Any remaining issues (warnings only — errors are resolved before import) |
187+
| `summary` | `{ totalRows, validRows, correctedRows, skippedRows, warningRows }` | Summary statistics |
188+
189+
## `buildRows`
190+
191+
A utility to reconstruct the fully processed row data on the client side from a `CsvImportEvent`. Useful when you want typed row objects on the frontend instead of sending raw file data to a server.
192+
193+
```tsx
194+
import { buildRows } from "@tailor-platform/app-shell";
195+
196+
onImport: async (event) => {
197+
const rows = await buildRows(event, schema);
198+
// rows: Record<string, unknown>[] — schema coercion and corrections applied
199+
await saveToBackend(rows);
200+
},
201+
```
202+
203+
> If you are sending the file, mappings, and corrections to a backend for processing, you do not need `buildRows`.
204+
205+
## i18n
206+
207+
`CsvImporter` includes built-in English and Japanese labels. No additional setup is required.
208+
209+
## Related Components
210+
211+
- [Sheet](./sheet.md) — Slide-in panel (the drawer used internally by CsvImporter)
212+
- [Button](./button.md) — Use as the trigger to open the importer

0 commit comments

Comments
 (0)