@arbor-education/design-system.components 3.5.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/.github/workflows/pr-review-approve.yaml +2 -1
  2. package/CHANGELOG.md +6 -0
  3. package/dist/components/formField/FormField.d.ts +4 -0
  4. package/dist/components/formField/FormField.d.ts.map +1 -1
  5. package/dist/components/formField/FormField.js +2 -1
  6. package/dist/components/formField/FormField.js.map +1 -1
  7. package/dist/components/formField/FormField.stories.d.ts.map +1 -1
  8. package/dist/components/formField/FormField.stories.js +10 -7
  9. package/dist/components/formField/FormField.stories.js.map +1 -1
  10. package/dist/components/formField/FormField.test.js +14 -0
  11. package/dist/components/formField/FormField.test.js.map +1 -1
  12. package/dist/components/formField/inputs/singleFile/SingleFileInput.d.ts +14 -0
  13. package/dist/components/formField/inputs/singleFile/SingleFileInput.d.ts.map +1 -0
  14. package/dist/components/formField/inputs/singleFile/SingleFileInput.js +35 -0
  15. package/dist/components/formField/inputs/singleFile/SingleFileInput.js.map +1 -0
  16. package/dist/components/formField/inputs/singleFile/SingleFileInput.stories.d.ts +170 -0
  17. package/dist/components/formField/inputs/singleFile/SingleFileInput.stories.d.ts.map +1 -0
  18. package/dist/components/formField/inputs/singleFile/SingleFileInput.stories.js +242 -0
  19. package/dist/components/formField/inputs/singleFile/SingleFileInput.stories.js.map +1 -0
  20. package/dist/components/formField/inputs/singleFile/SingleFileInput.test.d.ts +2 -0
  21. package/dist/components/formField/inputs/singleFile/SingleFileInput.test.d.ts.map +1 -0
  22. package/dist/components/formField/inputs/singleFile/SingleFileInput.test.js +135 -0
  23. package/dist/components/formField/inputs/singleFile/SingleFileInput.test.js.map +1 -0
  24. package/dist/index.css +18 -0
  25. package/dist/index.css.map +1 -1
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +1 -0
  29. package/dist/index.js.map +1 -1
  30. package/package.json +1 -1
  31. package/src/components/formField/FormField.stories.tsx +10 -7
  32. package/src/components/formField/FormField.test.tsx +35 -0
  33. package/src/components/formField/FormField.tsx +5 -0
  34. package/src/components/formField/inputs/singleFile/SingleFileInput.stories.tsx +339 -0
  35. package/src/components/formField/inputs/singleFile/SingleFileInput.test.tsx +181 -0
  36. package/src/components/formField/inputs/singleFile/SingleFileInput.tsx +101 -0
  37. package/src/components/formField/inputs/singleFile/singleFileInput.scss +17 -0
  38. package/src/index.scss +1 -0
  39. package/src/index.ts +1 -0
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -26,6 +26,7 @@ export { NumberInput } from './components/formField/inputs/number/NumberInput.js
26
26
  export { RadioButtonGroup } from './components/formField/inputs/radio/RadioButtonGroup.js';
27
27
  export { RadioButtonInput } from './components/formField/inputs/radio/RadioButtonInput.js';
28
28
  export { SelectDropdown } from './components/formField/inputs/selectDropdown/SelectDropdown.js';
29
+ export { SingleFileInput } from './components/formField/inputs/singleFile/SingleFileInput.js';
29
30
  export { Skeleton } from './components/skeleton/Skeleton.js';
30
31
  export { TextInput } from './components/formField/inputs/text/TextInput.js';
31
32
  export { TextArea } from './components/formField/inputs/textArea/TextArea.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arbor-education/design-system.components",
3
- "version": "3.5.0",
3
+ "version": "3.6.0",
4
4
  "description": "The component library for the design system (the baby)",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -45,7 +45,7 @@ const USAGE_GUIDANCE = [
45
45
  '',
46
46
  '---',
47
47
  '',
48
- '### The thirteen input types',
48
+ '### The input types',
49
49
  '',
50
50
  '| `inputType` | Renders | Typical use |',
51
51
  '|---|---|---|',
@@ -62,6 +62,7 @@ const USAGE_GUIDANCE = [
62
62
  '| `"checkboxGroup"` | `CheckboxGroup` | Several independent selections (support needs, alert recipients) |',
63
63
  '| `"toggle"` | `Toggle` | Instant on/off settings embedded in a form |',
64
64
  '| `"radio"` | `RadioButtonGroup` | One choice from a short, mutually-exclusive list |',
65
+ '| `"singleFile"` | `SingleFileInput` | One attachment submitted with a native form POST |',
65
66
  ].join('\n');
66
67
 
67
68
  const DEVELOPER_NOTES = [
@@ -168,7 +169,8 @@ const RELATED_COMPONENTS = [
168
169
  '[Label](?path=/docs/components-formfield-inputs-label--docs) · ',
169
170
  '[CheckboxInput](?path=/docs/components-formfield-inputs-checkbox--docs) · ',
170
171
  '[RadioButtonInput](?path=/docs/components-formfield-inputs-radiobutton--docs) · ',
171
- '[Toggle](?path=/docs/components-toggle--docs)',
172
+ '[Toggle](?path=/docs/components-toggle--docs) · ',
173
+ '[SingleFileInput](?path=/docs/components-formfield-inputs-singlefileinput--docs)',
172
174
  ].join('\n');
173
175
 
174
176
  const PROPS_INTRO = 'The preview below is wired to the **Controls** panel — tweak any prop to see the story update in real time.';
@@ -233,7 +235,7 @@ const meta = {
233
235
  },
234
236
  inputType: {
235
237
  control: 'select',
236
- options: ['text', 'textarea', 'number', 'time', 'colourPicker', 'selectDropdown', 'datePicker', 'combobox', 'checkbox', 'checkboxGroup', 'toggle', 'radio'],
238
+ options: ['text', 'textarea', 'number', 'time', 'colourPicker', 'selectDropdown', 'datePicker', 'combobox', 'checkbox', 'checkboxGroup', 'toggle', 'radio', 'singleFile'],
237
239
  description: [
238
240
  'Determines which input component is rendered.',
239
241
  'Defaults to `"text"` (renders `TextInput`).',
@@ -246,11 +248,12 @@ const meta = {
246
248
  '`"checkbox"` → `CheckboxInput` (single opt-in/consent);',
247
249
  '`"checkboxGroup"` → `CheckboxGroup` (several independent selections);',
248
250
  '`"toggle"` → `Toggle` (instant on/off switch);',
249
- '`"radio"` → `RadioButtonGroup` (one choice from a short list).',
250
- 'The last four require `inputProps` — see the developer notes.',
251
+ '`"radio"` → `RadioButtonGroup` (one choice from a short list);',
252
+ '`"singleFile"` → `SingleFileInput` (one file for a native form POST).',
253
+ '`"checkbox"`, `"checkboxGroup"`, `"toggle"` and `"radio"` require `inputProps` — see the developer notes.',
251
254
  ].join(' '),
252
255
  table: {
253
- type: { summary: "'text' | 'textarea' | 'number' | 'time' | 'colourPicker' | 'selectDropdown' | 'datePicker' | 'combobox' | 'checkbox' | 'checkboxGroup' | 'toggle' | 'radio'" },
256
+ type: { summary: "'text' | 'textarea' | 'number' | 'time' | 'colourPicker' | 'selectDropdown' | 'datePicker' | 'combobox' | 'checkbox' | 'checkboxGroup' | 'toggle' | 'radio' | 'singleFile'" },
254
257
  defaultValue: { summary: "'text'" },
255
258
  },
256
259
  },
@@ -325,7 +328,7 @@ const meta = {
325
328
  '**Required, not optional, for `"checkbox"`, `"checkboxGroup"`, `"toggle"`, and `"radio"`.**',
326
329
  ].join(' '),
327
330
  table: {
328
- type: { summary: 'TextInputProps | TextAreaProps | NumberInputProps | TimeInputProps | ColourPickerDropdownProps | SelectDropdownInputProps | DatePickerProps | DateTimePickerProps | ComboboxProps | CheckboxInputProps | CheckboxGroupProps | Toggle.Props | RadioButtonGroupProps' },
331
+ type: { summary: 'TextInputProps | TextAreaProps | NumberInputProps | TimeInputProps | ColourPickerDropdownProps | SelectDropdownInputProps | DatePickerProps | DateTimePickerProps | ComboboxProps | CheckboxInputProps | CheckboxGroupProps | Toggle.Props | RadioButtonGroupProps | SingleFileInputProps' },
329
332
  },
330
333
  },
331
334
  },
@@ -194,4 +194,39 @@ describe('FormField component', () => {
194
194
  expect(input.closest('.ds-combobox__trigger')).toHaveClass('ds-combobox__trigger--error');
195
195
  expect(screen.getByText('Choose a student')).toBeInTheDocument();
196
196
  });
197
+
198
+ test('renders a single file input when inputType is singleFile', () => {
199
+ render(
200
+ <FormField
201
+ id="attachment"
202
+ label="Attachment"
203
+ inputType="singleFile"
204
+ fieldDescription="PDF only"
205
+ helperLinkText="File guidance"
206
+ helperLinkUrl="https://example.com/guidance"
207
+ inputProps={{ name: 'attachment' }}
208
+ />,
209
+ );
210
+
211
+ const field = screen.getByRole('textbox', { name: 'Attachment' });
212
+ expect(field).toHaveAccessibleDescription('PDF only');
213
+ expect(screen.getByRole('button', { name: 'Choose file' })).toBeInTheDocument();
214
+ expect(screen.getByRole('link', { name: 'Attachment helper link' })).toBeInTheDocument();
215
+ });
216
+
217
+ test('marks a single file input as invalid and describes it by the error when errorText is provided', () => {
218
+ render(
219
+ <FormField
220
+ id="attachment"
221
+ label="Attachment"
222
+ inputType="singleFile"
223
+ errorText="File is too large"
224
+ />,
225
+ );
226
+
227
+ const field = screen.getByRole('textbox', { name: 'Attachment' });
228
+ expect(field).toBeInvalid();
229
+ expect(field).toHaveClass('ds-input--error');
230
+ expect(field).toHaveAccessibleDescription('File is too large');
231
+ });
197
232
  });
@@ -6,6 +6,7 @@ import { Icon } from '../icon/Icon.js';
6
6
  import { ColourPickerDropdown } from './inputs/colourPickerDropdown/ColourPickerDropdown.js';
7
7
  import { NumberInput } from './inputs/number/NumberInput.js';
8
8
  import { SelectDropdown } from './inputs/selectDropdown/SelectDropdown.js';
9
+ import { SingleFileInput } from './inputs/singleFile/SingleFileInput.js';
9
10
  import { TextInput } from './inputs/text/TextInput.js';
10
11
  import { TimeInput } from './inputs/time/TimeInput.js';
11
12
  import { TextArea } from './inputs/textArea/TextArea.js';
@@ -37,6 +38,7 @@ export type FormFieldProps = {
37
38
  | { inputType?: 'checkboxGroup'; inputProps: CheckboxGroup.Props }
38
39
  | { inputType?: 'toggle'; inputProps: Toggle.Props }
39
40
  | { inputType?: 'radio'; inputProps: RadioButtonGroup.Props }
41
+ | { inputType?: 'singleFile'; inputProps?: SingleFileInput.Props }
40
42
  );
41
43
 
42
44
  export const FormField = (props: FormFieldProps) => {
@@ -106,6 +108,9 @@ export const FormField = (props: FormFieldProps) => {
106
108
  {inputType === 'radio' && (
107
109
  <RadioButtonGroup {...sharedProps} {...(inputProps as RadioButtonGroup.Props)} />
108
110
  )}
111
+ {inputType === 'singleFile' && (
112
+ <SingleFileInput {...sharedProps} {...(inputProps as SingleFileInput.Props)} />
113
+ )}
109
114
  {((helperLinkText && helperLinkUrl) || errorText) && (
110
115
  <div className="ds-form-field__message">
111
116
  {errorText && (
@@ -0,0 +1,339 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import {
3
+ Controls,
4
+ Heading as DocHeading,
5
+ Markdown,
6
+ Primary as DocPrimary,
7
+ Stories,
8
+ Subtitle,
9
+ Title,
10
+ } from '@storybook/addon-docs/blocks';
11
+ import { useState } from 'react';
12
+ import { expect, fn, userEvent, within } from 'storybook/test';
13
+ import { FormField } from '../../FormField.js';
14
+ import { SingleFileInput } from './SingleFileInput.js';
15
+
16
+ const DESCRIPTION_INTRO = [
17
+ 'The **SingleFileInput** lets a user pick exactly one file for a conventional form POST.',
18
+ 'It shows the chosen file name in a read-only text field, with a button that opens the native file picker.',
19
+ 'The selected file is submitted under the `name` you give it, like any other form control.',
20
+ ].join('\n');
21
+
22
+ const USAGE_GUIDANCE = [
23
+ '### When to use',
24
+ '',
25
+ '- Attaching one document to a form that is submitted with a normal `<form>` POST: a consent letter, a photo, a CSV import',
26
+ '- Anywhere a legacy single-file field needs a Design System equivalent',
27
+ '- Inside `FormField` with `inputType="singleFile"` (the recommended pattern in real forms)',
28
+ '',
29
+ '---',
30
+ '',
31
+ '### When NOT to use',
32
+ '',
33
+ '| Instead of SingleFileInput... | Use... | Why |',
34
+ '|---|---|---|',
35
+ '| Several files, drag and drop, upload progress or per-file delete | [`FileUpload`](?path=/docs/components-fileupload--docs) | Async multi-file dropzone |',
36
+ '| Free-form text | [`TextInput`](?path=/docs/components-formfield-inputs-textinput--docs) | No file is involved |',
37
+ '',
38
+ '---',
39
+ '',
40
+ '### Design guidance',
41
+ '',
42
+ '- **Width is always 100%** of its container: size the wrapper, not the input.',
43
+ '- **Use FormField in real forms** so the label, description, helper link and error text are wired up for you.',
44
+ '- **hasError + disabled is an anti-pattern** because a user cannot correct an error on a field they cannot edit.',
45
+ ].join('\n');
46
+
47
+ const DEVELOPER_NOTES = [
48
+ '### Critical usage patterns',
49
+ '',
50
+ '**1. Reading the selected `File`.** `onChange` receives the native change event from the hidden file input.',
51
+ 'Read `event.currentTarget.files?.[0]` **inside the handler** (React clears `currentTarget` afterwards) to validate',
52
+ 'type or size, then show the result through `FormField`\'s `errorText`.',
53
+ '',
54
+ '**2. Form submission.** `name`, `accept` and `required` are applied to the native `<input type="file">`, which is',
55
+ 'the only control that carries a `name`, so only the file itself is submitted.',
56
+ 'The visible read-only field never submits.',
57
+ '',
58
+ '**3. `ref`.** The forwarded ref points at the native file input, so `ref.current.files` works as usual.',
59
+ 'To reset the selection, submit a form containing the field or dispatch a DOM `reset` event on that form —',
60
+ 'setting `ref.current.value = ""` clears the native input but leaves the displayed file name stale,',
61
+ 'because a programmatic value change does not fire `change`.',
62
+ '',
63
+ '**4. Always provide `id` inside FormField.** The label targets the visible read-only field, which is what',
64
+ 'assistive technology lands on.',
65
+ '',
66
+ '**5. Single file only.** `multiple` is not supported.',
67
+ '',
68
+ '**6. No focus or key handlers.** `onFocus`, `onBlur` and `onKeyDown` are not accepted: the native input never receives focus.',
69
+ '',
70
+ '---',
71
+ '',
72
+ '### Accessibility',
73
+ '',
74
+ '- Keyboard: `Tab` moves from the read-only field to the **Choose file** button; `Enter` or `Space` opens the picker.',
75
+ '- The hidden native input is removed from the tab order and the accessibility tree, so the field is not announced twice.',
76
+ '- The chosen file name is the value of the labelled read-only field, so it is announced on focus.',
77
+ '- Pair `hasError` with `aria-invalid={true}` when used standalone. `FormField` does this automatically.',
78
+ ].join('\n');
79
+
80
+ const RELATED_COMPONENTS = [
81
+ '## Related components',
82
+ '',
83
+ '[FormField](?path=/docs/components-formfield--docs) · [FileUpload](?path=/docs/components-fileupload--docs) · [Button](?path=/docs/components-button--docs)',
84
+ ].join('\n');
85
+
86
+ function SingleFileInputDocsPage() {
87
+ return (
88
+ <>
89
+ <Title />
90
+ <Subtitle />
91
+ <Markdown>{DESCRIPTION_INTRO}</Markdown>
92
+ <DocHeading>Interactive example</DocHeading>
93
+ <DocPrimary />
94
+ <Controls />
95
+ <DocHeading>Usage guidance</DocHeading>
96
+ <Markdown>{USAGE_GUIDANCE}</Markdown>
97
+ <DocHeading>Developer notes</DocHeading>
98
+ <Markdown>{DEVELOPER_NOTES}</Markdown>
99
+ <DocHeading>Examples</DocHeading>
100
+ <Stories title="" />
101
+ <Markdown>{RELATED_COMPONENTS}</Markdown>
102
+ </>
103
+ );
104
+ }
105
+
106
+ const meta = {
107
+ title: 'Components/FormField/Inputs/SingleFileInput',
108
+ component: SingleFileInput,
109
+ tags: ['autodocs'],
110
+ parameters: {
111
+ layout: 'padded',
112
+ docs: {
113
+ page: SingleFileInputDocsPage,
114
+ },
115
+ },
116
+ args: {
117
+ 'aria-label': 'Attachment',
118
+ 'onChange': fn(),
119
+ },
120
+ argTypes: {
121
+ 'hasError': {
122
+ description: 'Applies error-state styling. Does not set `aria-invalid`: pair it when used standalone.',
123
+ control: 'boolean',
124
+ table: { type: { summary: 'boolean' }, defaultValue: { summary: 'false' } },
125
+ },
126
+ 'disabled': {
127
+ description: 'Disables the field, the button and the native file input.',
128
+ control: 'boolean',
129
+ table: { type: { summary: 'boolean' }, defaultValue: { summary: 'false' } },
130
+ },
131
+ 'required': {
132
+ description: 'Marks the native file input as required for form validation and the visible field as required for assistive technology.',
133
+ control: 'boolean',
134
+ table: { type: { summary: 'boolean' } },
135
+ },
136
+ 'name': {
137
+ description: 'Name the selected file is submitted under.',
138
+ control: 'text',
139
+ table: { type: { summary: 'string' } },
140
+ },
141
+ 'accept': {
142
+ description: 'Comma-separated list of accepted file types, passed to the native file input (for example `.pdf,image/*`).',
143
+ control: 'text',
144
+ table: { type: { summary: 'string' } },
145
+ },
146
+ 'buttonLabel': {
147
+ description: 'Label of the button that opens the native file picker.',
148
+ control: 'text',
149
+ table: { type: { summary: 'string' }, defaultValue: { summary: "'Choose file'" } },
150
+ },
151
+ 'placeholder': {
152
+ description: 'Text shown in the read-only field while no file is chosen.',
153
+ control: 'text',
154
+ table: { type: { summary: 'string' }, defaultValue: { summary: "'No file chosen'" } },
155
+ },
156
+ 'id': {
157
+ description: 'Set on the visible read-only field, so a `<label htmlFor>` targets it. `FormField` passes this automatically.',
158
+ control: 'text',
159
+ table: { type: { summary: 'string' } },
160
+ },
161
+ 'className': {
162
+ description: 'Extra class name(s) added to the wrapper element, alongside `ds-single-file-input`.',
163
+ control: 'text',
164
+ table: { type: { summary: 'string' } },
165
+ },
166
+ 'aria-label': {
167
+ description: 'Accessible name for the visible read-only field when there is no visible label.',
168
+ control: 'text',
169
+ table: { type: { summary: 'string' } },
170
+ },
171
+ 'aria-labelledby': {
172
+ description: 'ID(s) of the element(s) that label the visible read-only field.',
173
+ control: 'text',
174
+ table: { type: { summary: 'string' } },
175
+ },
176
+ 'aria-describedby': {
177
+ description: 'ID(s) of the element(s) that describe the visible read-only field, such as helper or error text. `FormField` sets this automatically.',
178
+ control: 'text',
179
+ table: { type: { summary: 'string' } },
180
+ },
181
+ 'aria-invalid': {
182
+ description: 'Marks the visible read-only field as invalid for assistive technology. `FormField` sets this automatically from `errorText`.',
183
+ control: 'boolean',
184
+ table: { type: { summary: 'boolean' } },
185
+ },
186
+ 'onChange': {
187
+ description: 'Fired with the native change event. Read `event.currentTarget.files?.[0]` to get the selected `File`.',
188
+ control: false,
189
+ table: { type: { summary: '(event: ChangeEvent<HTMLInputElement>) => void' } },
190
+ },
191
+ },
192
+ } satisfies Meta<typeof SingleFileInput>;
193
+
194
+ export default meta;
195
+ type Story = StoryObj<typeof SingleFileInput>;
196
+
197
+ const withDescription = (story: Story, description: string): Story => ({
198
+ ...story,
199
+ parameters: {
200
+ ...story.parameters,
201
+ docs: {
202
+ ...story.parameters?.docs,
203
+ description: { story: description },
204
+ },
205
+ },
206
+ });
207
+
208
+ const sampleFile = () => new File(['%PDF-1.4'], 'consent-letter.pdf', { type: 'application/pdf' });
209
+
210
+ const wrap = (node: React.ReactNode) => (
211
+ <div style={{ maxWidth: '28rem', padding: 'var(--spacing-xlarge)' }}>{node}</div>
212
+ );
213
+
214
+ export const Default: Story = withDescription(
215
+ {
216
+ render: args => wrap(<SingleFileInput {...args} />),
217
+ },
218
+ 'No file chosen yet. The read-only field shows its placeholder and the **Choose file** button opens the native picker.',
219
+ );
220
+
221
+ export const Selected: Story = withDescription(
222
+ {
223
+ render: args => wrap(<SingleFileInput {...args} />),
224
+ play: async ({ canvasElement, args }) => {
225
+ const canvas = within(canvasElement);
226
+ const nativeInput = canvasElement.querySelector<HTMLInputElement>('input[type="file"]');
227
+ await userEvent.upload(nativeInput as HTMLInputElement, sampleFile());
228
+
229
+ await expect(canvas.getByRole('textbox', { name: 'Attachment' })).toHaveValue('consent-letter.pdf');
230
+ await expect(args.onChange).toHaveBeenCalled();
231
+ },
232
+ },
233
+ 'After a file is chosen its name replaces the placeholder in the read-only field.',
234
+ );
235
+
236
+ export const Disabled: Story = withDescription(
237
+ {
238
+ args: { disabled: true },
239
+ render: args => wrap(<SingleFileInput {...args} />),
240
+ },
241
+ 'The field, button and native input are all disabled.',
242
+ );
243
+
244
+ export const WithError: Story = withDescription(
245
+ {
246
+ render: () => wrap(
247
+ <FormField
248
+ id="attachment-error"
249
+ label="Attachment"
250
+ inputType="singleFile"
251
+ errorText="Attach a file to continue"
252
+ inputProps={{ name: 'attachment', required: true }}
253
+ />,
254
+ ),
255
+ play: async ({ canvasElement }) => {
256
+ const canvas = within(canvasElement);
257
+ const field = canvas.getByRole('textbox', { name: 'Attachment' });
258
+
259
+ await expect(field).toBeInvalid();
260
+ await expect(field).toHaveAccessibleDescription('Attach a file to continue');
261
+ },
262
+ },
263
+ 'Inside `FormField`, `errorText` turns on the error styling, `aria-invalid` and the described-by link to the message.',
264
+ );
265
+
266
+ const ValidationTemplate = () => {
267
+ const [error, setError] = useState<string>();
268
+ const maxBytes = 1024;
269
+
270
+ return (
271
+ <FormField
272
+ id="attachment-validated"
273
+ label="Attachment"
274
+ inputType="singleFile"
275
+ fieldDescription="PDF, 1KB maximum"
276
+ errorText={error}
277
+ inputProps={{
278
+ name: 'attachment',
279
+ accept: '.pdf',
280
+ onChange: (event) => {
281
+ const file = event.currentTarget.files?.[0];
282
+ setError(file && file.size > maxBytes ? `${file.name} is larger than 1KB` : undefined);
283
+ },
284
+ }}
285
+ />
286
+ );
287
+ };
288
+
289
+ export const WithValidation: Story = withDescription(
290
+ {
291
+ render: () => wrap(<ValidationTemplate />),
292
+ play: async ({ canvasElement }) => {
293
+ const canvas = within(canvasElement);
294
+ const nativeInput = canvasElement.querySelector<HTMLInputElement>('input[type="file"]');
295
+ const oversized = new File([new Uint8Array(2048)], 'big.pdf', { type: 'application/pdf' });
296
+
297
+ await userEvent.upload(nativeInput as HTMLInputElement, oversized);
298
+
299
+ await expect(canvas.getByText('big.pdf is larger than 1KB')).toBeInTheDocument();
300
+ await expect(canvas.getByRole('textbox', { name: 'Attachment' })).toBeInvalid();
301
+ },
302
+ },
303
+ 'The consumer reads the selected `File` in `onChange`, validates it, and reports the result through `errorText`.',
304
+ );
305
+
306
+ export const InAForm: Story = withDescription(
307
+ {
308
+ render: () => wrap(
309
+ <form>
310
+ <FormField
311
+ id="attachment-form"
312
+ label="Consent letter"
313
+ inputType="singleFile"
314
+ fieldDescription="Attach the signed letter"
315
+ helperLinkText="Letter template"
316
+ helperLinkUrl="https://example.com/template"
317
+ inputProps={{ name: 'consentLetter', accept: '.pdf' }}
318
+ />
319
+ </form>,
320
+ ),
321
+ play: async ({ canvasElement }) => {
322
+ const canvas = within(canvasElement);
323
+ const form = canvasElement.querySelector('form') as HTMLFormElement;
324
+ const nativeInput = form.querySelector('input[type="file"]') as HTMLInputElement;
325
+
326
+ // user-event only stubs `files`, so build a real FileList for FormData to read.
327
+ const transfer = new DataTransfer();
328
+ transfer.items.add(sampleFile());
329
+ nativeInput.files = transfer.files;
330
+ nativeInput.dispatchEvent(new Event('change', { bubbles: true }));
331
+
332
+ const submitted = new FormData(form).get('consentLetter');
333
+ await expect(submitted).toBeInstanceOf(File);
334
+ await expect((submitted as File).name).toBe('consent-letter.pdf');
335
+ await expect(canvas.getByRole('textbox', { name: 'Consent letter' })).toHaveValue('consent-letter.pdf');
336
+ },
337
+ },
338
+ 'Within a native `<form>`, the chosen file is submitted under the file input\'s `name`.',
339
+ );