@arbor-education/design-system.components 0.13.0 → 0.14.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/.agent-memory/blanche-designspert/MEMORY.md +189 -0
- package/.agent-memory/dorothy-fact-checker/MEMORY.md +228 -0
- package/.agent-memory/dorothy-fact-checker/numberinput_component.md +53 -0
- package/.agent-memory/dorothy-fact-checker/progress_component.md +36 -0
- package/.agent-memory/rose-storybookspert/MEMORY.md +105 -0
- package/.agent-memory/sophia-componentspert/MEMORY.md +34 -0
- package/{.claude/agent-memory → .agent-memory}/sophia-componentspert/components.md +170 -17
- package/{.claude → .gather}/agents/blanche-designspert.md +7 -2
- package/{.claude → .gather}/agents/dorothy-fact-checker.md +7 -2
- package/{.claude → .gather}/agents/rose-storybookspert.md +80 -11
- package/{.claude → .gather}/agents/sophia-componentspert.md +9 -4
- package/.gather/gather.yaml +9 -0
- package/{CLAUDE.md → .gather/instructions/project-overview.md} +42 -9
- package/{.claude → .gather}/skills/analyze-design/README.md +5 -0
- package/{.claude → .gather}/skills/analyze-design/SKILL.md +1 -1
- package/.gather/skills/analyze-design/meta.md +4 -0
- package/{.claude → .gather}/skills/create-page/README.md +5 -0
- package/{.claude → .gather}/skills/create-page/design-analysis-template.md +5 -0
- package/.gather/skills/create-page/meta.md +4 -0
- package/{.claude → .gather}/skills/create-page/page-template.scss +5 -0
- package/{.claude → .gather}/skills/create-page/page-template.tsx +5 -0
- package/{.claude → .gather}/skills/map-legacy/README.md +5 -0
- package/.gather/skills/map-legacy/meta.md +4 -0
- package/{.claude → .gather}/skills/migrate-page/README.md +5 -0
- package/.gather/skills/migrate-page/meta.md +4 -0
- package/.gather/skills/write-stories/README.md +157 -0
- package/.gather/skills/write-stories/SKILL.md +841 -0
- package/.gather/skills/write-stories/meta.md +4 -0
- package/.ralph/storybook-upgrade/knowledge.md +308 -0
- package/.ralph/storybook-upgrade/prd.json +777 -0
- package/.ralph/storybook-upgrade/progress.md +342 -0
- package/.storybook/DocsTemplate.tsx +122 -0
- package/.storybook/preview.ts +40 -0
- package/.stylelintignore +2 -0
- package/CHANGELOG.md +14 -0
- package/{.claude/component-library.md → component-library.md} +27 -10
- package/dist/components/articleCard/ArticleCard.d.ts +30 -0
- package/dist/components/articleCard/ArticleCard.d.ts.map +1 -0
- package/dist/components/articleCard/ArticleCard.js +24 -0
- package/dist/components/articleCard/ArticleCard.js.map +1 -0
- package/dist/components/articleCard/ArticleCard.stories.d.ts +18 -0
- package/dist/components/articleCard/ArticleCard.stories.d.ts.map +1 -0
- package/dist/components/articleCard/ArticleCard.stories.js +112 -0
- package/dist/components/articleCard/ArticleCard.stories.js.map +1 -0
- package/dist/components/articleCard/ArticleCard.test.d.ts +2 -0
- package/dist/components/articleCard/ArticleCard.test.d.ts.map +1 -0
- package/dist/components/articleCard/ArticleCard.test.js +49 -0
- package/dist/components/articleCard/ArticleCard.test.js.map +1 -0
- package/dist/components/badge/Badge.stories.d.ts +85 -6
- package/dist/components/badge/Badge.stories.d.ts.map +1 -1
- package/dist/components/badge/Badge.stories.js +626 -27
- package/dist/components/badge/Badge.stories.js.map +1 -1
- package/dist/components/banner/Banner.stories.d.ts +129 -63
- package/dist/components/banner/Banner.stories.d.ts.map +1 -1
- package/dist/components/banner/Banner.stories.js +855 -39
- package/dist/components/banner/Banner.stories.js.map +1 -1
- package/dist/components/button/Button.stories.d.ts +148 -8
- package/dist/components/button/Button.stories.d.ts.map +1 -1
- package/dist/components/button/Button.stories.js +1089 -80
- package/dist/components/button/Button.stories.js.map +1 -1
- package/dist/components/card/Card.d.ts +41 -12
- package/dist/components/card/Card.d.ts.map +1 -1
- package/dist/components/card/Card.js +46 -17
- package/dist/components/card/Card.js.map +1 -1
- package/dist/components/card/Card.stories.d.ts +9 -84
- package/dist/components/card/Card.stories.d.ts.map +1 -1
- package/dist/components/card/Card.stories.js +15 -73
- package/dist/components/card/Card.stories.js.map +1 -1
- package/dist/components/card/Card.test.js +50 -152
- package/dist/components/card/Card.test.js.map +1 -1
- package/dist/components/dot/Dot.stories.d.ts +46 -11
- package/dist/components/dot/Dot.stories.d.ts.map +1 -1
- package/dist/components/dot/Dot.stories.js +504 -15
- package/dist/components/dot/Dot.stories.js.map +1 -1
- package/dist/components/dropdown/Dropdown.stories.d.ts +89 -14
- package/dist/components/dropdown/Dropdown.stories.d.ts.map +1 -1
- package/dist/components/dropdown/Dropdown.stories.js +769 -17
- package/dist/components/dropdown/Dropdown.stories.js.map +1 -1
- package/dist/components/formField/FormField.stories.d.ts +95 -35
- package/dist/components/formField/FormField.stories.d.ts.map +1 -1
- package/dist/components/formField/FormField.stories.js +1174 -69
- package/dist/components/formField/FormField.stories.js.map +1 -1
- package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.d.ts +96 -9
- package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.d.ts.map +1 -1
- package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.js +717 -10
- package/dist/components/formField/inputs/checkbox/CheckboxInput.stories.js.map +1 -1
- package/dist/components/formField/inputs/number/NumberInput.stories.d.ts +149 -11
- package/dist/components/formField/inputs/number/NumberInput.stories.d.ts.map +1 -1
- package/dist/components/formField/inputs/number/NumberInput.stories.js +624 -10
- package/dist/components/formField/inputs/number/NumberInput.stories.js.map +1 -1
- package/dist/components/formField/inputs/radio/RadioButtonInput.stories.d.ts +74 -1
- package/dist/components/formField/inputs/radio/RadioButtonInput.stories.d.ts.map +1 -1
- package/dist/components/formField/inputs/radio/RadioButtonInput.stories.js +673 -44
- package/dist/components/formField/inputs/radio/RadioButtonInput.stories.js.map +1 -1
- package/dist/components/formField/inputs/text/TextInput.stories.d.ts +119 -1
- package/dist/components/formField/inputs/text/TextInput.stories.d.ts.map +1 -1
- package/dist/components/formField/inputs/text/TextInput.stories.js +549 -10
- package/dist/components/formField/inputs/text/TextInput.stories.js.map +1 -1
- package/dist/components/formField/inputs/textArea/TextArea.stories.d.ts +129 -4
- package/dist/components/formField/inputs/textArea/TextArea.stories.d.ts.map +1 -1
- package/dist/components/formField/inputs/textArea/TextArea.stories.js +577 -3
- package/dist/components/formField/inputs/textArea/TextArea.stories.js.map +1 -1
- package/dist/components/formField/inputs/time/TimeInput.d.ts +1 -1
- package/dist/components/formField/inputs/time/TimeInput.stories.d.ts +1 -1
- package/dist/components/heading/Heading.stories.d.ts +449 -50
- package/dist/components/heading/Heading.stories.d.ts.map +1 -1
- package/dist/components/heading/Heading.stories.js +536 -60
- package/dist/components/heading/Heading.stories.js.map +1 -1
- package/dist/components/icoText/IcoText.d.ts +37 -0
- package/dist/components/icoText/IcoText.d.ts.map +1 -0
- package/dist/components/icoText/IcoText.js +29 -0
- package/dist/components/icoText/IcoText.js.map +1 -0
- package/dist/components/icoText/IcoText.stories.d.ts +34 -0
- package/dist/components/icoText/IcoText.stories.d.ts.map +1 -0
- package/dist/components/icoText/IcoText.stories.js +24 -0
- package/dist/components/icoText/IcoText.stories.js.map +1 -0
- package/dist/components/icoText/IcoText.test.d.ts +2 -0
- package/dist/components/icoText/IcoText.test.d.ts.map +1 -0
- package/dist/components/icoText/IcoText.test.js +27 -0
- package/dist/components/icoText/IcoText.test.js.map +1 -0
- package/dist/components/icon/Icon.stories.d.ts +81 -10
- package/dist/components/icon/Icon.stories.d.ts.map +1 -1
- package/dist/components/icon/Icon.stories.js +979 -8
- package/dist/components/icon/Icon.stories.js.map +1 -1
- package/dist/components/kpiCard/KPICard.d.ts +13 -0
- package/dist/components/kpiCard/KPICard.d.ts.map +1 -0
- package/dist/components/kpiCard/KPICard.js +8 -0
- package/dist/components/kpiCard/KPICard.js.map +1 -0
- package/dist/components/kpiCard/KPICard.stories.d.ts +9 -0
- package/dist/components/kpiCard/KPICard.stories.d.ts.map +1 -0
- package/dist/components/kpiCard/KPICard.stories.js +18 -0
- package/dist/components/kpiCard/KPICard.stories.js.map +1 -0
- package/dist/components/kpiCard/KPICard.test.d.ts +2 -0
- package/dist/components/kpiCard/KPICard.test.d.ts.map +1 -0
- package/dist/components/kpiCard/KPICard.test.js +37 -0
- package/dist/components/kpiCard/KPICard.test.js.map +1 -0
- package/dist/components/kvpList/KVPList.d.ts +34 -0
- package/dist/components/kvpList/KVPList.d.ts.map +1 -0
- package/dist/components/kvpList/KVPList.js +20 -0
- package/dist/components/kvpList/KVPList.js.map +1 -0
- package/dist/components/kvpList/KVPList.stories.d.ts +27 -0
- package/dist/components/kvpList/KVPList.stories.d.ts.map +1 -0
- package/dist/components/kvpList/KVPList.stories.js +18 -0
- package/dist/components/kvpList/KVPList.stories.js.map +1 -0
- package/dist/components/kvpList/KVPList.test.d.ts +2 -0
- package/dist/components/kvpList/KVPList.test.d.ts.map +1 -0
- package/dist/components/kvpList/KVPList.test.js +29 -0
- package/dist/components/kvpList/KVPList.test.js.map +1 -0
- package/dist/components/pill/Pill.stories.d.ts +71 -19
- package/dist/components/pill/Pill.stories.d.ts.map +1 -1
- package/dist/components/pill/Pill.stories.js +573 -14
- package/dist/components/pill/Pill.stories.js.map +1 -1
- package/dist/components/progress/Progress.stories.d.ts +75 -298
- package/dist/components/progress/Progress.stories.d.ts.map +1 -1
- package/dist/components/progress/Progress.stories.js +449 -52
- package/dist/components/progress/Progress.stories.js.map +1 -1
- package/dist/components/separator/Separator.stories.d.ts +58 -5
- package/dist/components/separator/Separator.stories.d.ts.map +1 -1
- package/dist/components/separator/Separator.stories.js +443 -4
- package/dist/components/separator/Separator.stories.js.map +1 -1
- package/dist/components/singleUser/SingleUser.d.ts +1 -1
- package/dist/components/tabs/TabsItem.stories.d.ts +2 -2
- package/dist/components/tag/Tag.stories.d.ts +116 -5
- package/dist/components/tag/Tag.stories.d.ts.map +1 -1
- package/dist/components/tag/Tag.stories.js +581 -28
- package/dist/components/tag/Tag.stories.js.map +1 -1
- package/dist/index.css +194 -23
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +13 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -3
- package/dist/index.js.map +1 -1
- package/eslint.config.mts +5 -1
- package/package.json +3 -3
- package/src/components/articleCard/ArticleCard.stories.tsx +132 -0
- package/src/components/articleCard/ArticleCard.test.tsx +121 -0
- package/src/components/articleCard/ArticleCard.tsx +100 -0
- package/src/components/articleCard/articleCard.scss +39 -0
- package/src/components/badge/Badge.stories.tsx +869 -42
- package/src/components/banner/Banner.stories.tsx +1081 -63
- package/src/components/button/Button.stories.tsx +1394 -99
- package/src/components/card/Card.stories.tsx +35 -79
- package/src/components/card/Card.test.tsx +72 -190
- package/src/components/card/Card.tsx +117 -58
- package/src/components/card/card.scss +18 -31
- package/src/components/dot/Dot.stories.tsx +723 -32
- package/src/components/dropdown/Dropdown.stories.tsx +1174 -35
- package/src/components/formField/FormField.stories.tsx +1522 -105
- package/src/components/formField/inputs/checkbox/CheckboxInput.stories.tsx +1020 -15
- package/src/components/formField/inputs/number/NumberInput.stories.tsx +908 -15
- package/src/components/formField/inputs/radio/RadioButtonInput.stories.tsx +932 -51
- package/src/components/formField/inputs/text/TextInput.stories.tsx +773 -13
- package/src/components/formField/inputs/textArea/TextArea.stories.tsx +756 -8
- package/src/components/heading/Heading.stories.tsx +752 -120
- package/src/components/icoText/IcoText.stories.tsx +47 -0
- package/src/components/icoText/IcoText.test.tsx +41 -0
- package/src/components/icoText/IcoText.tsx +93 -0
- package/src/components/icoText/icoText.scss +34 -0
- package/src/components/icon/Icon.stories.tsx +1446 -12
- package/src/components/kpiCard/KPICard.stories.tsx +47 -0
- package/src/components/kpiCard/KPICard.test.tsx +60 -0
- package/src/components/kpiCard/KPICard.tsx +45 -0
- package/src/components/kpiCard/kpiCard.scss +35 -0
- package/src/components/kvpList/KVPList.stories.tsx +51 -0
- package/src/components/kvpList/KVPList.test.tsx +66 -0
- package/src/components/kvpList/KVPList.tsx +109 -0
- package/src/components/kvpList/kvpList.scss +64 -0
- package/src/components/pill/Pill.stories.tsx +867 -21
- package/src/components/progress/Progress.stories.tsx +625 -58
- package/src/components/separator/Separator.stories.tsx +730 -8
- package/src/components/separator/separator.scss +12 -3
- package/src/components/tag/Tag.stories.tsx +755 -53
- package/src/index.scss +4 -0
- package/src/index.ts +13 -4
- package/src/tokens.scss +6 -0
- package/tokens/json/Arbor.json +30 -0
- package/.claude/agent-memory/blanche-designspert/MEMORY.md +0 -64
- package/.claude/agent-memory/dorothy-fact-checker/MEMORY.md +0 -129
- package/.claude/agent-memory/rose-storybookspert/MEMORY.md +0 -29
- package/.claude/agent-memory/sophia-componentspert/MEMORY.md +0 -14
- package/.claude/design-assessment-daily-attendance-2026-04-10.md +0 -566
- package/.claude/figma-assessment-7154-58899.md +0 -404
- package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-11086-97537.md +0 -392
- package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-551-41974.md +0 -474
- package/.claude/figma-assessment-UKQfcxnT4rlHHNuiumt4o1-551-43094.md +0 -462
- package/.claude/figma-assessment-fcFK4CGzkz2fVyY3koX8ZE-7154-59061.md +0 -440
- package/.claude/migration-report-custom-report-writer-2026-02-19.md +0 -591
- /package/{.claude/agent-memory → .agent-memory}/blanche-designspert/token-review-patterns.md +0 -0
- /package/{.claude/agent-memory → .agent-memory}/rose-storybookspert/patterns.md +0 -0
- /package/{.claude → .gather}/skills/create-page/SKILL.md +0 -0
- /package/{.claude → .gather}/skills/map-legacy/SKILL.md +0 -0
- /package/{.claude → .gather}/skills/migrate-page/SKILL.md +0 -0
|
@@ -1,37 +1,576 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Controls, Heading as DocHeading, Markdown, Primary as DocPrimary, Stories, Subtitle, Title, } from '@storybook/addon-docs/blocks';
|
|
3
|
+
import { useRef, useState } from 'react';
|
|
1
4
|
import { fn } from 'storybook/test';
|
|
5
|
+
import { FormField } from '../../FormField';
|
|
2
6
|
import { TextInput } from './TextInput';
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Component description — built as joined arrays to avoid no-useless-escape
|
|
9
|
+
// on backtick code spans inside template literals.
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
const DESCRIPTION_INTRO = [
|
|
12
|
+
'The **TextInput** is the single-line text entry control in the Arbor design system.',
|
|
13
|
+
'It wraps a native `<input>` element with `forwardRef` support, two size variants,',
|
|
14
|
+
'an error state, and full pass-through of HTML input attributes.',
|
|
15
|
+
].join('\n');
|
|
16
|
+
const USAGE_GUIDANCE = [
|
|
17
|
+
'### When to use',
|
|
18
|
+
'',
|
|
19
|
+
'- Free-form single-line text: student names, year group labels, search queries, notes',
|
|
20
|
+
'- Any field where the valid input is a short string the user types directly',
|
|
21
|
+
'- As the underlying input inside a `FormField` with `inputType="text"` (the recommended pattern in real forms)',
|
|
22
|
+
'',
|
|
23
|
+
'---',
|
|
24
|
+
'',
|
|
25
|
+
'### When NOT to use',
|
|
26
|
+
'',
|
|
27
|
+
'| Instead of TextInput... | Use... | Why |',
|
|
28
|
+
'|---|---|---|',
|
|
29
|
+
'| Multi-line text | [`TextArea`](?path=/docs/components-formfield-inputs-textarea--docs) | Expands vertically; better UX for long content |',
|
|
30
|
+
'| Numeric values | [`NumberInput`](?path=/docs/components-formfield-inputs-numeric--docs) | Precision handling, stepper UI, better browser chrome |',
|
|
31
|
+
'| Time values | [`TimeInput`](?path=/docs/components-formfield-inputs-timeinput--docs) | Granularity controls, clock UI, better validation |',
|
|
32
|
+
'| Date selection | `DatePicker` | Calendar UI; avoids inconsistent native date picker chrome |',
|
|
33
|
+
'| Search / filter | [`SearchBar`](?path=/docs/components-searchbar--docs) | Search semantics, clear button, correct ARIA role |',
|
|
34
|
+
'| Selecting from a list | `SelectDropdown` or `Combobox` | Constrained selection; better keyboard and screen reader UX |',
|
|
35
|
+
'| Inline editable text | [`EditableText`](?path=/docs/components-editabletext--docs) | Uses TextInput internally; adds toggle-to-edit affordance |',
|
|
36
|
+
'',
|
|
37
|
+
'---',
|
|
38
|
+
'',
|
|
39
|
+
'### Design guidance',
|
|
40
|
+
'',
|
|
41
|
+
'- **Width is always 100%** — the input always fills its container. Control width by sizing the wrapper, not the input.',
|
|
42
|
+
'- **Stack sizes vertically** when comparing M and S — the 0.25rem height difference is subtle and easier to perceive when stacked.',
|
|
43
|
+
'- **Use FormField in real forms** — `FormField` automatically wires `hasError`, `aria-invalid`, and `aria-describedby`.',
|
|
44
|
+
' On a bare `TextInput`, you are responsible for those attributes yourself.',
|
|
45
|
+
'- **hasError + disabled is an anti-pattern** — a user cannot correct an error on a field they cannot edit.',
|
|
46
|
+
' Never show this combination.',
|
|
47
|
+
].join('\n');
|
|
48
|
+
const DEVELOPER_NOTES = [
|
|
49
|
+
'### Critical gotchas',
|
|
50
|
+
'',
|
|
51
|
+
'#### 1. `aria-invalid` is NOT automatic on bare TextInput',
|
|
52
|
+
'`hasError={true}` only adds the visual red border class (`ds-input--error`).',
|
|
53
|
+
'It carries no ARIA semantics. When using TextInput outside FormField,',
|
|
54
|
+
'you must also pass `aria-invalid="true"` so assistive technologies announce the error state.',
|
|
55
|
+
'FormField handles this automatically when `errorText` is present.',
|
|
56
|
+
'',
|
|
57
|
+
'```tsx',
|
|
58
|
+
'// BAD — visually red, but screen reader does not know there is an error',
|
|
59
|
+
'<TextInput hasError />',
|
|
60
|
+
'',
|
|
61
|
+
'// GOOD — both the visual class and the ARIA attribute are set',
|
|
62
|
+
'<TextInput hasError aria-invalid="true" aria-describedby="my-field-error" />',
|
|
63
|
+
'<span id="my-field-error">Enter a valid email address</span>',
|
|
64
|
+
'```',
|
|
65
|
+
'',
|
|
66
|
+
'#### 2. `id` is required for label association',
|
|
67
|
+
'Without an `id`, a `<label>` element cannot use `htmlFor` to associate with the input.',
|
|
68
|
+
'Outside FormField, wiring `id` and `htmlFor` is your responsibility.',
|
|
69
|
+
'`FormField` passes `id` through automatically.',
|
|
70
|
+
'',
|
|
71
|
+
'#### 3. `type="number"` and `type="time"` — use purpose-built inputs instead',
|
|
72
|
+
'TextInput passes `type` through to the native `<input>`, but `NumberInput` and `TimeInput`',
|
|
73
|
+
'provide better experiences: precision handling, stepper UI, granularity controls.',
|
|
74
|
+
'Only use `type="number"` on bare TextInput as a last resort.',
|
|
75
|
+
'',
|
|
76
|
+
'#### 4. Width is always 100%',
|
|
77
|
+
'The component always fills its container. Size the wrapper, not the input.',
|
|
78
|
+
'Never set a `width` or `max-width` directly on the TextInput element.',
|
|
79
|
+
'',
|
|
80
|
+
'#### 5. `hasError + disabled` is an anti-pattern',
|
|
81
|
+
'Visually ambiguous and semantically incorrect — a user cannot correct an error on a field',
|
|
82
|
+
'they cannot edit. Do not combine these two props.',
|
|
83
|
+
'',
|
|
84
|
+
'---',
|
|
85
|
+
'',
|
|
86
|
+
'### Accessibility',
|
|
87
|
+
'',
|
|
88
|
+
'- **Label association** — always wire `id` on the input and `htmlFor` on the label.',
|
|
89
|
+
' `FormField` does this automatically; bare TextInput consumers must do it manually.',
|
|
90
|
+
'- **Error state** — pair `hasError={true}` with `aria-invalid="true"` and',
|
|
91
|
+
' `aria-describedby` pointing to the error message element.',
|
|
92
|
+
' `FormField` handles all of this automatically when `errorText` is present.',
|
|
93
|
+
'- **Description text** — use `aria-describedby` pointing to any helper text element.',
|
|
94
|
+
' `FormField` wires this via `${id}-description` automatically.',
|
|
95
|
+
'- **disabled vs readOnly** — `disabled` removes the field from tab order entirely.',
|
|
96
|
+
' Use `readOnly` instead when the value must remain accessible and copyable,',
|
|
97
|
+
' or when a screen reader user needs to reach the field. readOnly fields ARE submitted',
|
|
98
|
+
' in forms; disabled fields are NOT.',
|
|
99
|
+
'- **Focus ring** — 3px outline using `--focus-border` and `--color-brand-500`.',
|
|
100
|
+
' Do not suppress focus rings — they are the primary keyboard navigation cue.',
|
|
101
|
+
'- **Placeholder is not a label** — never use placeholder as the only visible label.',
|
|
102
|
+
' It disappears on input and has poor colour contrast by default.',
|
|
103
|
+
'',
|
|
104
|
+
'---',
|
|
105
|
+
'',
|
|
106
|
+
'### TypeScript types',
|
|
107
|
+
'',
|
|
108
|
+
'```ts',
|
|
109
|
+
"import { TextInput } from '@arbor-education/design-system.components';",
|
|
110
|
+
'',
|
|
111
|
+
'function MyField(props: TextInput.Props) { ... }',
|
|
112
|
+
'```',
|
|
113
|
+
'',
|
|
114
|
+
'| Type | Description |',
|
|
115
|
+
'|---|---|',
|
|
116
|
+
'| `TextInput.Props` | Full props interface — `size`, `hasError`, plus all `InputHTMLAttributes<HTMLInputElement>` (minus the HTML `size` attribute) |',
|
|
117
|
+
].join('\n');
|
|
118
|
+
const RELATED_COMPONENTS = [
|
|
119
|
+
'## Related components',
|
|
120
|
+
'',
|
|
121
|
+
'[FormField](?path=/docs/components-formfield--docs)',
|
|
122
|
+
'· [TextArea](?path=/docs/components-formfield-inputs-textarea--docs)',
|
|
123
|
+
'· [NumberInput](?path=/docs/components-formfield-inputs-numeric--docs)',
|
|
124
|
+
'· [TimeInput](?path=/docs/components-formfield-inputs-timeinput--docs)',
|
|
125
|
+
'· [SearchBar](?path=/docs/components-searchbar--docs)',
|
|
126
|
+
'· [EditableText](?path=/docs/components-editabletext--docs)',
|
|
127
|
+
].join('\n');
|
|
128
|
+
const PROPS_INTRO = 'The preview below is wired to the **Controls** panel — tweak any prop to see the story update in real time.';
|
|
129
|
+
function TextInputDocsPage() {
|
|
130
|
+
return (_jsxs(_Fragment, { children: [_jsx(Title, {}), _jsx(Subtitle, {}), _jsx(Markdown, { children: DESCRIPTION_INTRO }), _jsx(DocHeading, { children: "Interactive example" }), _jsx(Markdown, { children: PROPS_INTRO }), _jsx(DocPrimary, {}), _jsx(Controls, {}), _jsx(DocHeading, { children: "Usage guidance" }), _jsx(Markdown, { children: USAGE_GUIDANCE }), _jsx(DocHeading, { children: "Developer notes" }), _jsx(Markdown, { children: DEVELOPER_NOTES }), _jsx(DocHeading, { children: "Examples" }), _jsx(Stories, { title: "" }), _jsx(Markdown, { children: RELATED_COMPONENTS })] }));
|
|
131
|
+
}
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
// Meta
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
3
135
|
const meta = {
|
|
4
136
|
title: 'Components/FormField/Inputs/TextInput',
|
|
5
137
|
component: TextInput,
|
|
6
138
|
parameters: {
|
|
7
139
|
layout: 'centered',
|
|
140
|
+
docs: {
|
|
141
|
+
page: TextInputDocsPage,
|
|
142
|
+
},
|
|
8
143
|
},
|
|
9
144
|
tags: ['autodocs'],
|
|
10
145
|
args: {
|
|
11
146
|
onChange: fn(),
|
|
147
|
+
onBlur: fn(),
|
|
12
148
|
},
|
|
13
149
|
argTypes: {
|
|
14
|
-
size: {
|
|
150
|
+
'size': {
|
|
15
151
|
control: 'select',
|
|
16
152
|
options: ['M', 'S'],
|
|
17
|
-
description:
|
|
153
|
+
description: [
|
|
154
|
+
'Controls the height of the input via the `ds-input--M` / `ds-input--S` modifier class.',
|
|
155
|
+
'`M` resolves to `--form-field-text-medium-height` (2.25rem / 36px) — the default for most form contexts.',
|
|
156
|
+
'`S` resolves to `--form-field-text-small-height` (2rem / 32px) — use in dense toolbars or compact table filters.',
|
|
157
|
+
'Note: the HTML `size` attribute is intentionally omitted from this component.',
|
|
158
|
+
'Width is always 100% of the parent container regardless of this prop.',
|
|
159
|
+
].join(' '),
|
|
160
|
+
table: {
|
|
161
|
+
type: { summary: "'M' | 'S'" },
|
|
162
|
+
defaultValue: { summary: "'M'" },
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
'hasError': {
|
|
166
|
+
control: 'boolean',
|
|
167
|
+
description: [
|
|
168
|
+
'Applies the `ds-input--error` class, which sets a red border.',
|
|
169
|
+
'When using TextInput inside `FormField`, this prop is set automatically when `errorText` is present',
|
|
170
|
+
'— do NOT pass it manually in `inputProps`.',
|
|
171
|
+
'When using bare TextInput, also pass `aria-invalid="true"` — `hasError` is purely visual',
|
|
172
|
+
'and carries no ARIA semantics on its own.',
|
|
173
|
+
].join(' '),
|
|
174
|
+
table: {
|
|
175
|
+
type: { summary: 'boolean' },
|
|
176
|
+
defaultValue: { summary: 'false' },
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
'disabled': {
|
|
180
|
+
control: 'boolean',
|
|
181
|
+
description: [
|
|
182
|
+
'Native HTML disabled attribute. Removes the field from tab order entirely.',
|
|
183
|
+
'Prevents all interaction and excludes the value from form submission.',
|
|
184
|
+
'Use `readOnly` instead when the value must remain accessible, copyable, or submitted.',
|
|
185
|
+
].join(' '),
|
|
186
|
+
table: {
|
|
187
|
+
type: { summary: 'boolean' },
|
|
188
|
+
defaultValue: { summary: 'false' },
|
|
189
|
+
},
|
|
18
190
|
},
|
|
19
|
-
|
|
191
|
+
'placeholder': {
|
|
192
|
+
control: 'text',
|
|
193
|
+
description: [
|
|
194
|
+
'Hint text displayed when the field is empty.',
|
|
195
|
+
'Never use placeholder as the only visible label — it disappears on input',
|
|
196
|
+
'and has poor colour contrast by default. Always pair with a visible `<label>` or `aria-label`.',
|
|
197
|
+
].join(' '),
|
|
198
|
+
table: {
|
|
199
|
+
type: { summary: 'string' },
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
'type': {
|
|
203
|
+
control: 'text',
|
|
204
|
+
description: [
|
|
205
|
+
'HTML input type attribute, passed through to the native `<input>` element.',
|
|
206
|
+
'Valid values for single-line text scenarios: `"text"` (default), `"email"`, `"password"`,',
|
|
207
|
+
'`"tel"`, `"url"`, `"search"`.',
|
|
208
|
+
'Avoid `"number"` and `"time"` — use `NumberInput` and `TimeInput` instead for better UX.',
|
|
209
|
+
].join(' '),
|
|
210
|
+
table: {
|
|
211
|
+
type: { summary: 'string' },
|
|
212
|
+
defaultValue: { summary: "'text'" },
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
'readOnly': {
|
|
20
216
|
control: 'boolean',
|
|
21
|
-
description:
|
|
217
|
+
description: [
|
|
218
|
+
'Makes the field focusable and selectable but not editable.',
|
|
219
|
+
'The value IS included in form submission (unlike `disabled`).',
|
|
220
|
+
'The field remains in tab order — use this instead of `disabled` when the value',
|
|
221
|
+
'must be accessible, readable, or copyable by the user.',
|
|
222
|
+
].join(' '),
|
|
223
|
+
table: {
|
|
224
|
+
type: { summary: 'boolean' },
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
'value': {
|
|
228
|
+
control: 'text',
|
|
229
|
+
description: [
|
|
230
|
+
'Controlled value — pair with `onChange` to manage state externally.',
|
|
231
|
+
'When using controlled mode, you must handle `onChange` otherwise the input will appear frozen.',
|
|
232
|
+
'For uncontrolled usage, use `defaultValue` instead.',
|
|
233
|
+
].join(' '),
|
|
234
|
+
table: {
|
|
235
|
+
type: { summary: 'string' },
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
'onChange': {
|
|
239
|
+
description: [
|
|
240
|
+
'Change event handler. Fires on every keystroke.',
|
|
241
|
+
'Required when using controlled `value` — otherwise the input will not update.',
|
|
242
|
+
].join(' '),
|
|
243
|
+
table: {
|
|
244
|
+
type: { summary: 'ChangeEventHandler<HTMLInputElement>' },
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
'onBlur': {
|
|
248
|
+
description: [
|
|
249
|
+
'Blur event handler. Fires when the field loses focus.',
|
|
250
|
+
'The canonical trigger for field-level validation — check the value here and set error state if invalid.',
|
|
251
|
+
].join(' '),
|
|
252
|
+
table: {
|
|
253
|
+
type: { summary: 'FocusEventHandler<HTMLInputElement>' },
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
'maxLength': {
|
|
257
|
+
control: 'number',
|
|
258
|
+
description: [
|
|
259
|
+
'Maximum number of characters allowed, enforced natively by the browser.',
|
|
260
|
+
'Useful for fields with a hard character limit — student reference numbers, short codes.',
|
|
261
|
+
'Consider pairing with a visible character count for longer limits.',
|
|
262
|
+
].join(' '),
|
|
263
|
+
table: {
|
|
264
|
+
type: { summary: 'number' },
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
'id': {
|
|
268
|
+
control: 'text',
|
|
269
|
+
description: [
|
|
270
|
+
'HTML `id` attribute — required for label association via `htmlFor` when used outside `FormField`.',
|
|
271
|
+
'`FormField` passes `id` through automatically. On a bare TextInput, always set this',
|
|
272
|
+
'when a visible label is present so screen readers can associate the label with the field.',
|
|
273
|
+
].join(' '),
|
|
274
|
+
table: {
|
|
275
|
+
type: { summary: 'string' },
|
|
276
|
+
},
|
|
22
277
|
},
|
|
23
|
-
|
|
278
|
+
'aria-label': {
|
|
24
279
|
control: 'text',
|
|
25
|
-
description:
|
|
280
|
+
description: [
|
|
281
|
+
'Accessible label for contexts where a visible label is not possible',
|
|
282
|
+
'(e.g. an inline filter input in a data grid header).',
|
|
283
|
+
'If a visible label exists, prefer `id` + `htmlFor` association instead.',
|
|
284
|
+
].join(' '),
|
|
285
|
+
table: {
|
|
286
|
+
type: { summary: 'string' },
|
|
287
|
+
},
|
|
26
288
|
},
|
|
27
289
|
},
|
|
28
290
|
};
|
|
29
291
|
export default meta;
|
|
30
|
-
//
|
|
31
|
-
|
|
292
|
+
// ---------------------------------------------------------------------------
|
|
293
|
+
// Helper: attach a per-story description to docs
|
|
294
|
+
// ---------------------------------------------------------------------------
|
|
295
|
+
const withDescription = (story, description) => ({
|
|
296
|
+
...story,
|
|
297
|
+
parameters: {
|
|
298
|
+
...story.parameters,
|
|
299
|
+
docs: { ...story.parameters?.docs, description: { story: description } },
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
// ---------------------------------------------------------------------------
|
|
303
|
+
// Stateful template components
|
|
304
|
+
// Named components avoid hooks-in-callbacks lint issues (react-hooks plugin
|
|
305
|
+
// is NOT configured in this project — do not add eslint-disable comments).
|
|
306
|
+
// ---------------------------------------------------------------------------
|
|
307
|
+
const ControlledWithValidationTemplate = () => {
|
|
308
|
+
const [value, setValue] = useState('');
|
|
309
|
+
const [hasError, setHasError] = useState(false);
|
|
310
|
+
const handleBlur = () => {
|
|
311
|
+
setHasError(value.length > 0 && !value.includes('@'));
|
|
312
|
+
};
|
|
313
|
+
return (_jsxs("div", { style: { maxWidth: '28rem', padding: 'var(--spacing-xlarge)', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }, children: [_jsx("label", { htmlFor: "parent-email-controlled", className: "ds-text", children: "Parent email address" }), _jsx(TextInput, { id: "parent-email-controlled", type: "email", placeholder: "jane.doe@example.com", value: value, hasError: hasError, "aria-invalid": hasError, "aria-describedby": hasError ? 'parent-email-error' : undefined, onChange: (e) => {
|
|
314
|
+
setValue(e.target.value);
|
|
315
|
+
if (hasError && e.target.value.includes('@')) {
|
|
316
|
+
setHasError(false);
|
|
317
|
+
}
|
|
318
|
+
}, onBlur: handleBlur }), hasError && (_jsx("span", { id: "parent-email-error", className: "ds-text", style: { color: 'var(--color-semantic-destructive-600)' }, children: "Enter a valid email address" })), !hasError && value.includes('@') && (_jsx("span", { className: "ds-text", style: { color: 'var(--color-semantic-success-600)' }, children: "Email looks valid" }))] }));
|
|
319
|
+
};
|
|
320
|
+
const WithForwardRefTemplate = () => {
|
|
321
|
+
const inputRef = useRef(null);
|
|
322
|
+
return (_jsxs("div", { style: { maxWidth: '28rem', padding: 'var(--spacing-xlarge)', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }, children: [_jsx("p", { className: "ds-text", style: { margin: 0, color: 'var(--color-grey-600)' }, children: "Click the button below to programmatically focus the text input." }), _jsx(TextInput, { ref: inputRef, id: "student-name-ref", placeholder: "Enter student full name", "aria-label": "Student full name" }), _jsx("button", { type: "button", className: "ds-button ds-button--secondary ds-button--M", onClick: () => inputRef.current?.focus(), children: "Focus input" })] }));
|
|
323
|
+
};
|
|
324
|
+
// ---------------------------------------------------------------------------
|
|
325
|
+
// Stories
|
|
326
|
+
// ---------------------------------------------------------------------------
|
|
327
|
+
export const Default = withDescription({
|
|
32
328
|
args: {
|
|
33
|
-
placeholder: 'Enter text...',
|
|
34
329
|
size: 'M',
|
|
330
|
+
placeholder: 'Enter student full name',
|
|
331
|
+
disabled: false,
|
|
332
|
+
hasError: false,
|
|
35
333
|
},
|
|
36
|
-
}
|
|
334
|
+
render: args => (_jsx("div", { style: { maxWidth: '28rem', padding: 'var(--spacing-xlarge)' }, children: _jsx(TextInput, { ...args }) })),
|
|
335
|
+
}, 'The interactive canvas story — every prop is wired to the **Controls** panel. Use the controls to explore sizes, error state, disabled, placeholder, and other HTML input attributes.');
|
|
336
|
+
export const Sizes = withDescription({
|
|
337
|
+
parameters: {
|
|
338
|
+
docs: {
|
|
339
|
+
source: {
|
|
340
|
+
language: 'tsx',
|
|
341
|
+
code: `
|
|
342
|
+
import { TextInput } from '@arbor-education/design-system.components';
|
|
343
|
+
|
|
344
|
+
function SizesExample() {
|
|
345
|
+
return (
|
|
346
|
+
<div style={{ maxWidth: '28rem', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
|
|
347
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
|
|
348
|
+
<label htmlFor="size-m">Medium (M) — 2.25rem</label>
|
|
349
|
+
<TextInput id="size-m" size="M" placeholder="Year group label or student name" />
|
|
350
|
+
</div>
|
|
351
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
|
|
352
|
+
<label htmlFor="size-s">Small (S) — 2rem</label>
|
|
353
|
+
<TextInput id="size-s" size="S" placeholder="Year group label or student name" />
|
|
354
|
+
</div>
|
|
355
|
+
</div>
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
export default SizesExample;
|
|
359
|
+
`.trim(),
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
render: () => (_jsxs("div", { style: { maxWidth: '28rem', padding: 'var(--spacing-xlarge)', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }, children: [_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }, children: [_jsx("label", { htmlFor: "size-m", className: "ds-text", children: "Medium (M) \u2014 2.25rem via --form-field-text-medium-height" }), _jsx(TextInput, { id: "size-m", size: "M", placeholder: "Year group label or student name" })] }), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }, children: [_jsx("label", { htmlFor: "size-s", className: "ds-text", children: "Small (S) \u2014 2rem via --form-field-text-small-height" }), _jsx(TextInput, { id: "size-s", size: "S", placeholder: "Year group label or student name" })] })] })),
|
|
364
|
+
}, 'Both sizes stacked for comparison. `M` is the default for standard form layouts. `S` is for dense toolbars or compact table filters where the medium height would feel too tall. Heights are entirely token-driven — never hardcode a height on a TextInput wrapper.');
|
|
365
|
+
export const States = withDescription({
|
|
366
|
+
parameters: {
|
|
367
|
+
docs: {
|
|
368
|
+
source: {
|
|
369
|
+
language: 'tsx',
|
|
370
|
+
code: `
|
|
371
|
+
import { TextInput } from '@arbor-education/design-system.components';
|
|
372
|
+
|
|
373
|
+
function StatesExample() {
|
|
374
|
+
return (
|
|
375
|
+
<div style={{ maxWidth: '28rem', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
|
|
376
|
+
{/* Default — empty */}
|
|
377
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
|
|
378
|
+
<label htmlFor="state-default">Default — empty</label>
|
|
379
|
+
<TextInput id="state-default" placeholder="Enter student full name" />
|
|
380
|
+
</div>
|
|
381
|
+
{/* Error — always pair hasError with aria-invalid + aria-describedby on bare TextInput */}
|
|
382
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
|
|
383
|
+
<label htmlFor="state-error">Error — invalid input</label>
|
|
384
|
+
<TextInput
|
|
385
|
+
id="state-error"
|
|
386
|
+
hasError
|
|
387
|
+
aria-invalid="true"
|
|
388
|
+
aria-describedby="state-error-msg"
|
|
389
|
+
defaultValue="not-an-email"
|
|
390
|
+
type="email"
|
|
391
|
+
/>
|
|
392
|
+
<span id="state-error-msg" style={{ color: 'var(--color-semantic-destructive-600)' }}>
|
|
393
|
+
Enter a valid email address
|
|
394
|
+
</span>
|
|
395
|
+
</div>
|
|
396
|
+
{/* Disabled */}
|
|
397
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
|
|
398
|
+
<label htmlFor="state-disabled">Disabled — not interactive</label>
|
|
399
|
+
<TextInput id="state-disabled" disabled placeholder="Field is disabled" />
|
|
400
|
+
</div>
|
|
401
|
+
</div>
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
export default StatesExample;
|
|
405
|
+
`.trim(),
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
},
|
|
409
|
+
render: () => (_jsxs("div", { style: { maxWidth: '28rem', padding: 'var(--spacing-xlarge)', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }, children: [_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }, children: [_jsx("label", { htmlFor: "state-default", className: "ds-text", children: "Default \u2014 empty" }), _jsx(TextInput, { id: "state-default", placeholder: "Enter student full name" })] }), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }, children: [_jsx("label", { htmlFor: "state-filled", className: "ds-text", children: "Filled \u2014 value present" }), _jsx(TextInput, { id: "state-filled", defaultValue: "Amara Osei-Bonsu" })] }), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }, children: [_jsx("label", { htmlFor: "state-error", className: "ds-text", children: "Error \u2014 invalid input" }), _jsx(TextInput, { id: "state-error", hasError: true, "aria-invalid": "true", "aria-describedby": "state-error-msg", defaultValue: "not-an-email", type: "email" }), _jsx("span", { id: "state-error-msg", className: "ds-text", style: { color: 'var(--color-semantic-destructive-600)' }, children: "Enter a valid email address" })] }), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }, children: [_jsx("label", { htmlFor: "state-disabled", className: "ds-text", style: { color: 'var(--color-grey-400)' }, children: "Disabled \u2014 not interactive" }), _jsx(TextInput, { id: "state-disabled", disabled: true, placeholder: "Field is disabled" })] }), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }, children: [_jsx("label", { htmlFor: "state-readonly", className: "ds-text", children: "Read only \u2014 value visible, not editable" }), _jsx(TextInput, { id: "state-readonly", readOnly: true, defaultValue: "Year 9 \u2014 Elm House" })] })] })),
|
|
410
|
+
}, 'All five states in a single view: default (empty), filled (value present), error (with `hasError` + `aria-invalid` + an error message), disabled (removed from tab order; value not submitted), and readOnly (focusable, selectable, submittable — but not editable). Note: the error state on a bare TextInput always requires `aria-invalid="true"` set manually — `FormField` handles this automatically.');
|
|
411
|
+
export const WithFormField = withDescription({
|
|
412
|
+
parameters: {
|
|
413
|
+
docs: {
|
|
414
|
+
source: {
|
|
415
|
+
language: 'tsx',
|
|
416
|
+
code: `
|
|
417
|
+
import { FormField } from '@arbor-education/design-system.components';
|
|
418
|
+
|
|
419
|
+
function WithFormFieldExample() {
|
|
420
|
+
return (
|
|
421
|
+
<div style={{ maxWidth: '28rem', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
|
|
422
|
+
<FormField
|
|
423
|
+
inputType="text"
|
|
424
|
+
label="Student name"
|
|
425
|
+
id="student-name"
|
|
426
|
+
inputProps={{ placeholder: 'Enter student full name' }}
|
|
427
|
+
/>
|
|
428
|
+
<FormField
|
|
429
|
+
inputType="text"
|
|
430
|
+
label="Parent email"
|
|
431
|
+
id="parent-email"
|
|
432
|
+
errorText="Enter a valid email address"
|
|
433
|
+
inputProps={{ placeholder: 'jane.doe@example.com', type: 'email' }}
|
|
434
|
+
/>
|
|
435
|
+
</div>
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
export default WithFormFieldExample;
|
|
439
|
+
`.trim(),
|
|
440
|
+
},
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
render: () => (_jsxs("div", { style: { maxWidth: '28rem', padding: 'var(--spacing-xlarge)', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }, children: [_jsx(FormField, { inputType: "text", label: "Student name", id: "student-name", inputProps: { placeholder: 'Enter student full name' } }), _jsx(FormField, { inputType: "text", label: "Parent email", id: "parent-email", errorText: "Enter a valid email address", inputProps: { placeholder: 'jane.doe@example.com', type: 'email' } })] })),
|
|
444
|
+
}, [
|
|
445
|
+
'**Always use `FormField` in real forms.** `FormField` with `inputType="text"` wraps TextInput and',
|
|
446
|
+
'automatically wires `hasError`, `aria-invalid`, `aria-describedby`, and the label `htmlFor` association.',
|
|
447
|
+
'The second example shows how `errorText` automatically sets the error state — do NOT pass',
|
|
448
|
+
'`hasError` or `aria-invalid` manually in `inputProps` when using FormField.',
|
|
449
|
+
].join(' '));
|
|
450
|
+
export const InputTypes = withDescription({
|
|
451
|
+
parameters: {
|
|
452
|
+
docs: {
|
|
453
|
+
source: {
|
|
454
|
+
language: 'tsx',
|
|
455
|
+
code: `
|
|
456
|
+
import { TextInput } from '@arbor-education/design-system.components';
|
|
457
|
+
|
|
458
|
+
function InputTypesExample() {
|
|
459
|
+
return (
|
|
460
|
+
<div style={{ maxWidth: '28rem', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
|
|
461
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
|
|
462
|
+
<label htmlFor="type-text">type="text" — Student name</label>
|
|
463
|
+
<TextInput id="type-text" type="text" placeholder="Amara Osei-Bonsu" />
|
|
464
|
+
</div>
|
|
465
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
|
|
466
|
+
<label htmlFor="type-email">type="email" — Parent email</label>
|
|
467
|
+
<TextInput id="type-email" type="email" placeholder="jane.doe@example.com" />
|
|
468
|
+
</div>
|
|
469
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
|
|
470
|
+
<label htmlFor="type-password">type="password" — Set password</label>
|
|
471
|
+
<TextInput id="type-password" type="password" placeholder="Min. 8 characters" />
|
|
472
|
+
</div>
|
|
473
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
|
|
474
|
+
<label htmlFor="type-tel">type="tel" — Parent phone number</label>
|
|
475
|
+
<TextInput id="type-tel" type="tel" placeholder="07700 900 123" />
|
|
476
|
+
</div>
|
|
477
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }}>
|
|
478
|
+
<label htmlFor="type-url">type="url" — School website</label>
|
|
479
|
+
<TextInput id="type-url" type="url" placeholder="https://example-school.ac.uk" />
|
|
480
|
+
</div>
|
|
481
|
+
</div>
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
export default InputTypesExample;
|
|
485
|
+
`.trim(),
|
|
486
|
+
},
|
|
487
|
+
},
|
|
488
|
+
},
|
|
489
|
+
render: () => (_jsxs("div", { style: { maxWidth: '28rem', padding: 'var(--spacing-xlarge)', display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }, children: [_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }, children: [_jsx("label", { htmlFor: "type-text", className: "ds-text", children: "type=\"text\" \u2014 Student name" }), _jsx(TextInput, { id: "type-text", type: "text", placeholder: "Amara Osei-Bonsu" })] }), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }, children: [_jsx("label", { htmlFor: "type-email", className: "ds-text", children: "type=\"email\" \u2014 Parent email" }), _jsx(TextInput, { id: "type-email", type: "email", placeholder: "jane.doe@example.com" })] }), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }, children: [_jsx("label", { htmlFor: "type-password", className: "ds-text", children: "type=\"password\" \u2014 Set password" }), _jsx(TextInput, { id: "type-password", type: "password", placeholder: "Min. 8 characters" })] }), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }, children: [_jsx("label", { htmlFor: "type-tel", className: "ds-text", children: "type=\"tel\" \u2014 Parent phone number" }), _jsx(TextInput, { id: "type-tel", type: "tel", placeholder: "07700 900 123" })] }), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-large)' }, children: [_jsx("label", { htmlFor: "type-url", className: "ds-text", children: "type=\"url\" \u2014 School website" }), _jsx(TextInput, { id: "type-url", type: "url", placeholder: "https://example-school.ac.uk" })] })] })),
|
|
490
|
+
}, 'TextInput passes `type` directly to the native `<input>`. The visual appearance is identical across all types — the difference is in browser behaviour: `email` validates address format, `password` masks characters, `tel` opens a numeric keyboard on mobile, `url` validates URL format. Avoid `type="number"` and `type="time"` — use `NumberInput` and `TimeInput` for those.');
|
|
491
|
+
export const ControlledWithValidation = withDescription({
|
|
492
|
+
render: () => _jsx(ControlledWithValidationTemplate, {}),
|
|
493
|
+
parameters: {
|
|
494
|
+
docs: {
|
|
495
|
+
source: {
|
|
496
|
+
language: 'tsx',
|
|
497
|
+
code: `
|
|
498
|
+
import { useState } from 'react';
|
|
499
|
+
import { TextInput } from '@arbor-education/design-system.components';
|
|
500
|
+
|
|
501
|
+
function EmailValidationExample() {
|
|
502
|
+
const [value, setValue] = useState('');
|
|
503
|
+
const [hasError, setHasError] = useState(false);
|
|
504
|
+
|
|
505
|
+
const handleBlur = () => {
|
|
506
|
+
setHasError(value.length > 0 && !value.includes('@'));
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
return (
|
|
510
|
+
<div>
|
|
511
|
+
<label htmlFor="parent-email">Parent email address</label>
|
|
512
|
+
<TextInput
|
|
513
|
+
id="parent-email"
|
|
514
|
+
type="email"
|
|
515
|
+
placeholder="jane.doe@example.com"
|
|
516
|
+
value={value}
|
|
517
|
+
hasError={hasError}
|
|
518
|
+
aria-invalid={hasError}
|
|
519
|
+
aria-describedby={hasError ? 'parent-email-error' : undefined}
|
|
520
|
+
onChange={(e) => {
|
|
521
|
+
setValue(e.target.value);
|
|
522
|
+
if (hasError && e.target.value.includes('@')) setHasError(false);
|
|
523
|
+
}}
|
|
524
|
+
onBlur={handleBlur}
|
|
525
|
+
/>
|
|
526
|
+
{hasError && (
|
|
527
|
+
<span id="parent-email-error">Enter a valid email address</span>
|
|
528
|
+
)}
|
|
529
|
+
</div>
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
`.trim(),
|
|
533
|
+
},
|
|
534
|
+
},
|
|
535
|
+
},
|
|
536
|
+
}, [
|
|
537
|
+
'A fully controlled email field with validation-on-blur. State is managed externally:',
|
|
538
|
+
'`value` and `onChange` track the input; `onBlur` checks for `"@"` and sets `hasError`.',
|
|
539
|
+
'When `hasError` is true, the story also sets `aria-invalid="true"` and',
|
|
540
|
+
'`aria-describedby` pointing to the error message span — this is the correct bare-TextInput',
|
|
541
|
+
'pattern (FormField handles this wiring automatically).',
|
|
542
|
+
'The error clears as soon as the user types an `"@"` character, giving immediate positive feedback.',
|
|
543
|
+
].join(' '));
|
|
544
|
+
export const WithForwardRef = withDescription({
|
|
545
|
+
render: () => _jsx(WithForwardRefTemplate, {}),
|
|
546
|
+
parameters: {
|
|
547
|
+
docs: {
|
|
548
|
+
source: {
|
|
549
|
+
language: 'tsx',
|
|
550
|
+
code: `
|
|
551
|
+
import { useRef } from 'react';
|
|
552
|
+
import { TextInput } from '@arbor-education/design-system.components';
|
|
553
|
+
|
|
554
|
+
function FocusExample() {
|
|
555
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
556
|
+
|
|
557
|
+
return (
|
|
558
|
+
<div>
|
|
559
|
+
<TextInput
|
|
560
|
+
ref={inputRef}
|
|
561
|
+
id="student-name"
|
|
562
|
+
placeholder="Enter student full name"
|
|
563
|
+
aria-label="Student full name"
|
|
564
|
+
/>
|
|
565
|
+
<button type="button" onClick={() => inputRef.current?.focus()}>
|
|
566
|
+
Focus input
|
|
567
|
+
</button>
|
|
568
|
+
</div>
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
`.trim(),
|
|
572
|
+
},
|
|
573
|
+
},
|
|
574
|
+
},
|
|
575
|
+
}, '`TextInput` uses `forwardRef` — it exposes the underlying `<input>` DOM node via `ref`. This story demonstrates programmatic focus: clicking the button calls `inputRef.current?.focus()`. Common real-world uses: restoring focus after an inline edit is saved, advancing focus in a multi-step form, or auto-focusing a search field when a panel opens.');
|
|
37
576
|
//# sourceMappingURL=TextInput.stories.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextInput.stories.js","sourceRoot":"","sources":["../../../../../src/components/formField/inputs/text/TextInput.stories.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAEpC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,IAAI,GAAG;IACX,KAAK,EAAE,uCAAuC;IAC9C,SAAS,EAAE,SAAS;IACpB,UAAU,EAAE;QACV,MAAM,EAAE,UAAU;KACnB;IACD,IAAI,EAAE,CAAC,UAAU,CAAC;IAClB,IAAI,EAAE;QACJ,QAAQ,EAAE,EAAE,EAAE;KACf;IACD,QAAQ,EAAE;QACR,IAAI,EAAE;YACJ,OAAO,EAAE,QAAQ;YACjB,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC;YACnB,WAAW,EAAE,YAAY;SAC1B;QACD,QAAQ,EAAE;YACR,OAAO,EAAE,SAAS;YAClB,WAAW,EAAE,mBAAmB;SACjC;QACD,WAAW,EAAE;YACX,OAAO,EAAE,MAAM;YACf,WAAW,EAAE,wBAAwB;SACtC;KACF;CAC+B,CAAC;AAEnC,eAAe,IAAI,CAAC;AAGpB,gBAAgB;AAChB,MAAM,CAAC,MAAM,OAAO,GAAU;IAC5B,IAAI,EAAE;QACJ,WAAW,EAAE,eAAe;QAC5B,IAAI,EAAE,GAAG;KACV;CACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"TextInput.stories.js","sourceRoot":"","sources":["../../../../../src/components/formField/inputs/text/TextInput.stories.tsx"],"names":[],"mappings":"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"}
|