@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.
- package/.github/workflows/pr-review-approve.yaml +2 -1
- package/CHANGELOG.md +6 -0
- package/dist/components/formField/FormField.d.ts +4 -0
- package/dist/components/formField/FormField.d.ts.map +1 -1
- package/dist/components/formField/FormField.js +2 -1
- package/dist/components/formField/FormField.js.map +1 -1
- package/dist/components/formField/FormField.stories.d.ts.map +1 -1
- package/dist/components/formField/FormField.stories.js +10 -7
- package/dist/components/formField/FormField.stories.js.map +1 -1
- package/dist/components/formField/FormField.test.js +14 -0
- package/dist/components/formField/FormField.test.js.map +1 -1
- package/dist/components/formField/inputs/singleFile/SingleFileInput.d.ts +14 -0
- package/dist/components/formField/inputs/singleFile/SingleFileInput.d.ts.map +1 -0
- package/dist/components/formField/inputs/singleFile/SingleFileInput.js +35 -0
- package/dist/components/formField/inputs/singleFile/SingleFileInput.js.map +1 -0
- package/dist/components/formField/inputs/singleFile/SingleFileInput.stories.d.ts +170 -0
- package/dist/components/formField/inputs/singleFile/SingleFileInput.stories.d.ts.map +1 -0
- package/dist/components/formField/inputs/singleFile/SingleFileInput.stories.js +242 -0
- package/dist/components/formField/inputs/singleFile/SingleFileInput.stories.js.map +1 -0
- package/dist/components/formField/inputs/singleFile/SingleFileInput.test.d.ts +2 -0
- package/dist/components/formField/inputs/singleFile/SingleFileInput.test.d.ts.map +1 -0
- package/dist/components/formField/inputs/singleFile/SingleFileInput.test.js +135 -0
- package/dist/components/formField/inputs/singleFile/SingleFileInput.test.js.map +1 -0
- package/dist/index.css +18 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/formField/FormField.stories.tsx +10 -7
- package/src/components/formField/FormField.test.tsx +35 -0
- package/src/components/formField/FormField.tsx +5 -0
- package/src/components/formField/inputs/singleFile/SingleFileInput.stories.tsx +339 -0
- package/src/components/formField/inputs/singleFile/SingleFileInput.test.tsx +181 -0
- package/src/components/formField/inputs/singleFile/SingleFileInput.tsx +101 -0
- package/src/components/formField/inputs/singleFile/singleFileInput.scss +17 -0
- package/src/index.scss +1 -0
- package/src/index.ts +1 -0
package/dist/index.d.ts.map
CHANGED
|
@@ -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":"AAAA,OAAO,EAAE,SAAS,EAAuB,MAAM,gCAAgC,CAAC;AAChF,OAAO,EAAE,WAAW,EAAyB,MAAM,oCAAoC,CAAC;AACxF,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACjE,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACjE,OAAO,EAAE,MAAM,EAAyD,MAAM,0BAA0B,CAAC;AACzG,OAAO,EAAE,IAAI,EAAkB,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,YAAY,EAA0B,MAAM,sCAAsC,CAAC;AAC5F,OAAO,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC;AAC1E,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACpE,OAAO,EAAE,UAAU,EAA6E,MAAM,kCAAkC,CAAC;AACzI,OAAO,EAAE,UAAU,EAAwB,MAAM,kCAAkC,CAAC;AACpF,OAAO,EACL,SAAS,GAQV,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,wCAAwC,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,oDAAoD,CAAC;AACnF,OAAO,EAAE,aAAa,EAAE,MAAM,oDAAoD,CAAC;AACnF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uEAAuE,CAAC;AAC7G,OAAO,EAAE,WAAW,EAAE,MAAM,gDAAgD,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,oDAAoD,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,oDAAoD,CAAC;AACtF,OAAO,EAAE,cAAc,EAAE,MAAM,2DAA2D,CAAC;AAC3F,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,4CAA4C,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,+CAA+C,CAAC;AACzE,OAAO,EAAE,SAAS,EAAE,MAAM,4CAA4C,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAiB,MAAM,8BAA8B,CAAC;AACvF,OAAO,EAAE,QAAQ,EAAsG,MAAM,8BAA8B,CAAC;AAC5J,OAAO,EAAE,OAAO,EAAqB,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,OAAO,EAA+F,MAAM,4BAA4B,CAAC;AAClJ,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAC;AAC1E,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,8CAA8C,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,oDAAoD,CAAC;AACzF,OAAO,EAAE,mBAAmB,EAAE,MAAM,oDAAoD,CAAC;AACzF,OAAO,EAAE,uBAAuB,EAAE,MAAM,wDAAwD,CAAC;AACjG,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAC;AACjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,qDAAqD,CAAC;AAC3F,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAErD,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,EAAE,OAAO,EAAiE,MAAM,4BAA4B,CAAC;AACpH,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AACrH,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC;AAC1E,OAAO,EACL,gBAAgB,GAIjB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oDAAoD,CAAC;AACzF,OAAO,EACL,2BAA2B,EAAE,cAAc,EAAE,iBAAiB,EAAE,+BAA+B,EAAE,mBAAmB,GACrH,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,IAAI,EAAkB,MAAM,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -45,7 +45,7 @@ const USAGE_GUIDANCE = [
|
|
|
45
45
|
'',
|
|
46
46
|
'---',
|
|
47
47
|
'',
|
|
48
|
-
'### The
|
|
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
|
-
'
|
|
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
|
+
);
|