@mindlogic-ai/logician-ui 5.0.0-alpha.2 → 5.0.0-alpha.4

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 (256) hide show
  1. package/dist/components/Badge/Badge.d.ts.map +1 -1
  2. package/dist/components/Badge/Badge.js +5 -3
  3. package/dist/components/Badge/Badge.js.map +1 -1
  4. package/dist/components/Badge/Badge.mjs +5 -3
  5. package/dist/components/Badge/Badge.mjs.map +1 -1
  6. package/dist/components/Badge/Badge.types.d.ts +7 -5
  7. package/dist/components/Badge/Badge.types.d.ts.map +1 -1
  8. package/dist/components/Banner/Banner.types.d.ts +24 -1
  9. package/dist/components/Banner/Banner.types.d.ts.map +1 -1
  10. package/dist/components/Card/Card.types.d.ts +12 -0
  11. package/dist/components/Card/Card.types.d.ts.map +1 -1
  12. package/dist/components/ColorMode/ColorModeToggle.d.ts +7 -2
  13. package/dist/components/ColorMode/ColorModeToggle.d.ts.map +1 -1
  14. package/dist/components/ColorMode/ColorModeToggle.js +8 -3
  15. package/dist/components/ColorMode/ColorModeToggle.js.map +1 -1
  16. package/dist/components/ColorMode/ColorModeToggle.mjs +8 -3
  17. package/dist/components/ColorMode/ColorModeToggle.mjs.map +1 -1
  18. package/dist/components/Confetti/Confetti.types.d.ts +5 -1
  19. package/dist/components/Confetti/Confetti.types.d.ts.map +1 -1
  20. package/dist/components/Confetti/confettiColors.d.ts +5 -5
  21. package/dist/components/Confetti/confettiColors.js +5 -5
  22. package/dist/components/Confetti/confettiColors.mjs +5 -5
  23. package/dist/components/ErrorFallback/ErrorFallback.types.d.ts +6 -0
  24. package/dist/components/ErrorFallback/ErrorFallback.types.d.ts.map +1 -1
  25. package/dist/components/Form/FormModalFooter/FormModalFooter.types.d.ts +4 -1
  26. package/dist/components/Form/FormModalFooter/FormModalFooter.types.d.ts.map +1 -1
  27. package/dist/components/LineGraph/LineGraph.d.ts.map +1 -1
  28. package/dist/components/LineGraph/LineGraph.js +4 -5
  29. package/dist/components/LineGraph/LineGraph.js.map +1 -1
  30. package/dist/components/LineGraph/LineGraph.mjs +5 -6
  31. package/dist/components/LineGraph/LineGraph.mjs.map +1 -1
  32. package/dist/components/LineGraph/LineGraph.types.d.ts +12 -2
  33. package/dist/components/LineGraph/LineGraph.types.d.ts.map +1 -1
  34. package/dist/components/LineGraph/seriesColors.d.ts +12 -8
  35. package/dist/components/LineGraph/seriesColors.d.ts.map +1 -1
  36. package/dist/components/LineGraph/seriesColors.js +8 -7
  37. package/dist/components/LineGraph/seriesColors.js.map +1 -1
  38. package/dist/components/LineGraph/seriesColors.mjs +8 -7
  39. package/dist/components/LineGraph/seriesColors.mjs.map +1 -1
  40. package/dist/components/Masonry/Masonry.types.d.ts +8 -3
  41. package/dist/components/Masonry/Masonry.types.d.ts.map +1 -1
  42. package/dist/components/Menu/MenuItem.types.d.ts +8 -0
  43. package/dist/components/Menu/MenuItem.types.d.ts.map +1 -1
  44. package/dist/components/Modal/ModalCloseButton.d.ts +1 -1
  45. package/dist/components/Modal/ModalCloseButton.js +1 -1
  46. package/dist/components/Modal/ModalCloseButton.mjs +1 -1
  47. package/dist/components/Pagination/Pagination.d.ts +11 -10
  48. package/dist/components/Pagination/Pagination.d.ts.map +1 -1
  49. package/dist/components/Pagination/Pagination.js +11 -10
  50. package/dist/components/Pagination/Pagination.js.map +1 -1
  51. package/dist/components/Pagination/Pagination.mjs +11 -10
  52. package/dist/components/Pagination/Pagination.mjs.map +1 -1
  53. package/dist/components/ProgressBar/ProgressBar.types.d.ts +9 -0
  54. package/dist/components/ProgressBar/ProgressBar.types.d.ts.map +1 -1
  55. package/dist/components/RadialProgress/RadialProgress.types.d.ts +21 -1
  56. package/dist/components/RadialProgress/RadialProgress.types.d.ts.map +1 -1
  57. package/dist/components/SeeMoreButton/SeeMoreButton.types.d.ts +8 -0
  58. package/dist/components/SeeMoreButton/SeeMoreButton.types.d.ts.map +1 -1
  59. package/dist/components/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  60. package/dist/components/SegmentedControl/SegmentedControl.js +6 -7
  61. package/dist/components/SegmentedControl/SegmentedControl.js.map +1 -1
  62. package/dist/components/SegmentedControl/SegmentedControl.mjs +6 -7
  63. package/dist/components/SegmentedControl/SegmentedControl.mjs.map +1 -1
  64. package/dist/components/SegmentedProgressBar/SegmentedProgressBar.types.d.ts +2 -0
  65. package/dist/components/SegmentedProgressBar/SegmentedProgressBar.types.d.ts.map +1 -1
  66. package/dist/components/Toast/Toast.styles.d.ts +6 -2
  67. package/dist/components/Toast/Toast.styles.d.ts.map +1 -1
  68. package/dist/components/Toast/Toast.styles.js +18 -10
  69. package/dist/components/Toast/Toast.styles.js.map +1 -1
  70. package/dist/components/Toast/Toast.styles.mjs +18 -10
  71. package/dist/components/Toast/Toast.styles.mjs.map +1 -1
  72. package/dist/theme/colors.d.ts +2 -2
  73. package/dist/theme/colors.d.ts.map +1 -1
  74. package/dist/theme/colors.js +22 -2
  75. package/dist/theme/colors.js.map +1 -1
  76. package/dist/theme/colors.mjs +23 -3
  77. package/dist/theme/colors.mjs.map +1 -1
  78. package/dist/theme/palette.generated.d.ts +98 -0
  79. package/dist/theme/palette.generated.d.ts.map +1 -1
  80. package/dist/theme/palette.generated.js +99 -0
  81. package/dist/theme/palette.generated.js.map +1 -1
  82. package/dist/theme/palette.generated.mjs +99 -1
  83. package/dist/theme/palette.generated.mjs.map +1 -1
  84. package/dist/theme/paletteSwatches.d.ts +20 -0
  85. package/dist/theme/paletteSwatches.d.ts.map +1 -1
  86. package/package.json +1 -1
  87. package/src/components/Accordion/Accordion.stories.tsx +85 -0
  88. package/src/components/Accordion/Accordion.usage.mdx +200 -0
  89. package/src/components/Appear/Appear.usage.mdx +107 -0
  90. package/src/components/Avatar/Avatar.stories.tsx +71 -1
  91. package/src/components/Avatar/Avatar.usage.mdx +166 -0
  92. package/src/components/Badge/Badge.stories.tsx +193 -3
  93. package/src/components/Badge/Badge.tsx +5 -3
  94. package/src/components/Badge/Badge.types.ts +7 -5
  95. package/src/components/Badge/Badge.usage.mdx +265 -0
  96. package/src/components/Banner/Banner.stories.tsx +152 -2
  97. package/src/components/Banner/Banner.types.ts +30 -1
  98. package/src/components/Banner/Banner.usage.mdx +233 -0
  99. package/src/components/BottomSheet/BottomSheet.stories.tsx +168 -1
  100. package/src/components/BottomSheet/BottomSheet.usage.mdx +216 -0
  101. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +59 -0
  102. package/src/components/Breadcrumb/Breadcrumb.usage.mdx +181 -0
  103. package/src/components/Button/Button.stories.tsx +263 -3
  104. package/src/components/Button/Button.usage.mdx +342 -0
  105. package/src/components/Card/Card.stories.tsx +106 -0
  106. package/src/components/Card/Card.types.ts +12 -0
  107. package/src/components/Card/Card.usage.mdx +216 -0
  108. package/src/components/Checkbox/Checkbox.stories.tsx +140 -13
  109. package/src/components/Checkbox/Checkbox.usage.mdx +214 -0
  110. package/src/components/Chip/Chip.usage.mdx +78 -0
  111. package/src/components/Code/Code.stories.tsx +60 -0
  112. package/src/components/Code/Code.usage.mdx +187 -0
  113. package/src/components/CodeTabs/CodeTabs.stories.tsx +53 -0
  114. package/src/components/CodeTabs/CodeTabs.usage.mdx +176 -0
  115. package/src/components/Collapsible/Collapsible.stories.tsx +58 -0
  116. package/src/components/Collapsible/Collapsible.usage.mdx +173 -0
  117. package/src/components/ColorMode/ColorMode.stories.tsx +50 -0
  118. package/src/components/ColorMode/ColorMode.usage.mdx +135 -0
  119. package/src/components/ColorMode/ColorModeToggle.tsx +10 -3
  120. package/src/components/Confetti/Confetti.types.ts +5 -1
  121. package/src/components/Confetti/Confetti.usage.mdx +111 -0
  122. package/src/components/Confetti/confettiColors.ts +5 -5
  123. package/src/components/Container/Container.stories.tsx +32 -0
  124. package/src/components/Container/Container.usage.mdx +130 -0
  125. package/src/components/CopyableCode/CopyableCode.stories.tsx +62 -0
  126. package/src/components/CopyableCode/CopyableCode.usage.mdx +168 -0
  127. package/src/components/CountBadge/CountBadge.stories.tsx +69 -0
  128. package/src/components/CountBadge/CountBadge.usage.mdx +215 -0
  129. package/src/components/CountUp/CountUp.usage.mdx +107 -0
  130. package/src/components/DatePicker/DatePicker.usage.mdx +177 -0
  131. package/src/components/DatePicker/RangeDatePicker.stories.tsx +87 -3
  132. package/src/components/ErrorFallback/ErrorFallback.types.ts +6 -0
  133. package/src/components/ErrorFallback/ErrorFallback.usage.mdx +127 -0
  134. package/src/components/ExpandableText/ExpandableText.stories.tsx +55 -0
  135. package/src/components/ExpandableText/ExpandableText.usage.mdx +176 -0
  136. package/src/components/FileInput/FileInput.stories.tsx +38 -1
  137. package/src/components/FileInput/FileInput.usage.mdx +178 -0
  138. package/src/components/FileItem/FileItem.stories.tsx +71 -0
  139. package/src/components/FileItem/FileItem.usage.mdx +180 -0
  140. package/src/components/FileList/FileList.stories.tsx +25 -0
  141. package/src/components/FileList/FileList.usage.mdx +165 -0
  142. package/src/components/FlyTo/FlyTo.usage.mdx +99 -0
  143. package/src/components/Form/Form.stories.tsx +149 -0
  144. package/src/components/Form/Form.usage.mdx +201 -0
  145. package/src/components/Form/FormModalFooter/FormModalFooter.types.ts +4 -1
  146. package/src/components/FormControl/FormControl.stories.tsx +35 -0
  147. package/src/components/FormControl/FormControl.usage.mdx +128 -0
  148. package/src/components/FormIntegration/FormIntegration.usage.mdx +158 -0
  149. package/src/components/FormLabel/FormLabel.stories.tsx +71 -0
  150. package/src/components/FormLabel/FormLabel.usage.mdx +125 -0
  151. package/src/components/Icon/Icon.stories.tsx +61 -1
  152. package/src/components/Icon/Icon.usage.mdx +158 -0
  153. package/src/components/IconButton/IconButton.stories.tsx +209 -5
  154. package/src/components/IconButton/IconButton.usage.mdx +273 -0
  155. package/src/components/InfoSprinkle/InfoSprinkle.stories.tsx +77 -1
  156. package/src/components/InfoSprinkle/InfoSprinkle.usage.mdx +189 -0
  157. package/src/components/InlineCode/InlineCode.stories.tsx +75 -0
  158. package/src/components/InlineCode/InlineCode.usage.mdx +142 -0
  159. package/src/components/Input/Input.stories.tsx +180 -0
  160. package/src/components/Input/Input.usage.mdx +278 -0
  161. package/src/components/LineGraph/LineGraph.stories.tsx +112 -0
  162. package/src/components/LineGraph/LineGraph.tsx +4 -5
  163. package/src/components/LineGraph/LineGraph.types.ts +12 -2
  164. package/src/components/LineGraph/LineGraph.usage.mdx +186 -0
  165. package/src/components/LineGraph/seriesColors.test.ts +11 -10
  166. package/src/components/LineGraph/seriesColors.ts +18 -16
  167. package/src/components/Loaders/Loaders.stories.tsx +93 -0
  168. package/src/components/Loaders/Loaders.usage.mdx +180 -0
  169. package/src/components/LogicianProvider/LogicianProvider.usage.mdx +98 -0
  170. package/src/components/Markdown/Markdown.stories.tsx +53 -0
  171. package/src/components/Markdown/Markdown.usage.mdx +182 -0
  172. package/src/components/Masonry/Masonry.stories.tsx +63 -1
  173. package/src/components/Masonry/Masonry.types.ts +8 -3
  174. package/src/components/Masonry/Masonry.usage.mdx +142 -0
  175. package/src/components/MaxLengthIndicator/MaxLengthIndicator.stories.tsx +57 -0
  176. package/src/components/MaxLengthIndicator/MaxLengthIndicator.usage.mdx +125 -0
  177. package/src/components/Menu/Menu.stories.tsx +146 -2
  178. package/src/components/Menu/Menu.usage.mdx +230 -0
  179. package/src/components/Menu/MenuItem.types.ts +8 -0
  180. package/src/components/Modal/Modal.stories.tsx +177 -1
  181. package/src/components/Modal/Modal.usage.mdx +262 -0
  182. package/src/components/Modal/ModalCloseButton.tsx +1 -1
  183. package/src/components/MonthPicker/MonthPicker.stories.tsx +30 -0
  184. package/src/components/MonthPicker/MonthPicker.usage.mdx +191 -0
  185. package/src/components/Pagination/Pagination.stories.tsx +90 -1
  186. package/src/components/Pagination/Pagination.tsx +11 -10
  187. package/src/components/Pagination/Pagination.usage.mdx +186 -0
  188. package/src/components/PasswordInput/PasswordInput.stories.tsx +51 -0
  189. package/src/components/PasswordInput/PasswordInput.usage.mdx +193 -0
  190. package/src/components/PinInput/PinInput.stories.tsx +91 -0
  191. package/src/components/PinInput/PinInput.usage.mdx +194 -0
  192. package/src/components/Popover/Popover.stories.tsx +104 -1
  193. package/src/components/Popover/Popover.usage.mdx +197 -0
  194. package/src/components/ProgressBar/ProgressBar.stories.tsx +92 -1
  195. package/src/components/ProgressBar/ProgressBar.types.ts +9 -0
  196. package/src/components/ProgressBar/ProgressBar.usage.mdx +210 -0
  197. package/src/components/Pulse/Pulse.usage.mdx +102 -0
  198. package/src/components/RadialProgress/RadialProgress.stories.tsx +74 -1
  199. package/src/components/RadialProgress/RadialProgress.types.ts +21 -1
  200. package/src/components/RadialProgress/RadialProgress.usage.mdx +153 -0
  201. package/src/components/Radio/Radio.stories.tsx +119 -0
  202. package/src/components/Radio/Radio.usage.mdx +187 -0
  203. package/src/components/Reveal/Reveal.usage.mdx +105 -0
  204. package/src/components/ScaledContext/ScaledContext.usage.mdx +111 -0
  205. package/src/components/SeeMoreButton/SeeMoreButton.stories.tsx +146 -2
  206. package/src/components/SeeMoreButton/SeeMoreButton.types.ts +8 -0
  207. package/src/components/SeeMoreButton/SeeMoreButton.usage.mdx +213 -0
  208. package/src/components/SegmentedControl/SegmentedControl.stories.tsx +69 -0
  209. package/src/components/SegmentedControl/SegmentedControl.tsx +6 -7
  210. package/src/components/SegmentedControl/SegmentedControl.usage.mdx +209 -0
  211. package/src/components/SegmentedProgressBar/SegmentedProgressBar.stories.tsx +54 -0
  212. package/src/components/SegmentedProgressBar/SegmentedProgressBar.types.ts +2 -0
  213. package/src/components/SegmentedProgressBar/SegmentedProgressBar.usage.mdx +145 -0
  214. package/src/components/Select/Select.stories.tsx +209 -1
  215. package/src/components/Select/Select.usage.mdx +278 -0
  216. package/src/components/Shake/Shake.usage.mdx +107 -0
  217. package/src/components/Slider/Slider.stories.tsx +116 -0
  218. package/src/components/Slider/Slider.usage.mdx +179 -0
  219. package/src/components/Spinner/Spinner.stories.tsx +64 -0
  220. package/src/components/Spinner/Spinner.usage.mdx +136 -0
  221. package/src/components/SwapTransition/SwapTransition.usage.mdx +102 -0
  222. package/src/components/Switch/Switch.stories.tsx +131 -6
  223. package/src/components/Switch/Switch.usage.mdx +199 -0
  224. package/src/components/Table/Table.stories.tsx +159 -1
  225. package/src/components/Table/Table.usage.mdx +235 -0
  226. package/src/components/Tabs/Tabs.stories.tsx +86 -0
  227. package/src/components/Tabs/Tabs.usage.mdx +219 -0
  228. package/src/components/Tag/Tag.stories.tsx +181 -4
  229. package/src/components/Tag/Tag.usage.mdx +257 -0
  230. package/src/components/Textarea/Textarea.stories.tsx +113 -0
  231. package/src/components/Textarea/Textarea.usage.mdx +186 -0
  232. package/src/components/Toast/Toast.stories.tsx +185 -3
  233. package/src/components/Toast/Toast.styles.ts +18 -10
  234. package/src/components/Toast/Toast.usage.mdx +265 -0
  235. package/src/components/Tooltip/Tooltip.stories.tsx +86 -1
  236. package/src/components/Tooltip/Tooltip.usage.mdx +211 -0
  237. package/src/components/Tree/Tree.stories.tsx +89 -0
  238. package/src/components/Tree/Tree.usage.mdx +207 -0
  239. package/src/components/Typography/Typography.stories.tsx +117 -0
  240. package/src/components/Typography/Typography.usage.mdx +251 -0
  241. package/src/components/Workflow/Workflow.usage.mdx +199 -0
  242. package/src/components/noRawScales.test.ts +5 -1
  243. package/src/foundations/BrandColour.mdx +364 -0
  244. package/src/foundations/BrandColour.stories.tsx +334 -0
  245. package/src/guidelines/ComponentPageTemplate.mdx +84 -0
  246. package/src/guidelines/FeedbackActions.mdx +162 -23
  247. package/src/guidelines/_components/Lang.tsx +175 -0
  248. package/src/guidelines/_components/TokenTable.tsx +136 -0
  249. package/src/theme/BazePalette.stories.tsx +111 -1
  250. package/src/theme/Palette.stories.tsx +2 -2
  251. package/src/theme/SemanticTokens.mdx +75 -16
  252. package/src/theme/claude.md +15 -9
  253. package/src/theme/colors.ts +33 -3
  254. package/src/theme/contrast.test.ts +32 -0
  255. package/src/theme/palette.generated.ts +99 -0
  256. package/src/theme/paletteSwatches.ts +35 -0
@@ -0,0 +1,165 @@
1
+ import { ArgTypes, Canvas, Meta } from '@storybook/blocks';
2
+
3
+ import { En, Ko, LangToggle, Verdict } from '../../guidelines/_components/Lang';
4
+ import * as FileListStories from './FileList.stories';
5
+
6
+ <Meta of={FileListStories} name="Usage" />
7
+
8
+ <LangToggle />
9
+
10
+ <En>
11
+
12
+ # FileList
13
+
14
+ A bordered list of `FileItem` rows that shows the first few files and reveals
15
+ more on request.
16
+
17
+ ## When to use
18
+
19
+ - The files attached to something: a chatbot's knowledge files, a project's
20
+ uploads, a policy's documents.
21
+
22
+ ## When not to use
23
+
24
+ - One file → `FileItem`.
25
+ - Hundreds of files the user searches, sorts or selects → `Table`.
26
+
27
+ ## Props
28
+
29
+ </En>
30
+
31
+ <Ko>
32
+
33
+ # FileList
34
+
35
+ `FileItem` 행을 테두리 안에 모아, 처음 몇 개만 보여 주고 요청하면 더 펼치는
36
+ 목록입니다.
37
+
38
+ ## 언제 쓰나요
39
+
40
+ - 어떤 대상에 붙은 파일들: 챗봇의 지식 파일, 프로젝트에 올린 파일, 정책 문서.
41
+
42
+ ## 언제 쓰지 않나요
43
+
44
+ - 파일이 하나라면 `FileItem`을 씁니다.
45
+ - 검색, 정렬, 선택이 필요한 수백 개의 파일은 `Table`을 씁니다.
46
+
47
+ ## 속성
48
+
49
+ </Ko>
50
+
51
+ <ArgTypes
52
+ of={FileListStories}
53
+ include={[
54
+ 'files',
55
+ 'visibleCount',
56
+ 'onFileDownload',
57
+ 'onFileDelete',
58
+ 'stagger',
59
+ ]}
60
+ />
61
+
62
+ <En>
63
+
64
+ - Shows `visibleCount` rows (default 3); the "see more" button adds three at
65
+ a time. With no files it renders nothing.
66
+ - A row gets a download button only when the file has a `fileUrl` and you pass
67
+ `onFileDownload`.
68
+ - `onFileDelete` may return a promise; the row shows a spinner until it
69
+ settles, and a second click on the same row is ignored meanwhile.
70
+
71
+ ## Do / Don't
72
+
73
+ </En>
74
+
75
+ <Ko>
76
+
77
+ - `visibleCount`개 행(기본 3개)을 보여 주고, "더 보기" 버튼을 누를 때마다 세 개씩
78
+ 늘립니다. 파일이 없으면 아무것도 그리지 않습니다.
79
+ - 내려받기 버튼은 파일에 `fileUrl`이 있고 `onFileDownload`를 넘겼을 때만 생깁니다.
80
+ - `onFileDelete`는 프로미스를 돌려줘도 됩니다. 끝날 때까지 그 행에 스피너가 보이고, 그
81
+ 사이 같은 행을 다시 눌러도 무시합니다.
82
+
83
+ ## 이렇게 하세요 / 이렇게 하지 마세요
84
+
85
+ </Ko>
86
+
87
+ <Canvas of={FileListStories.DoDownloadAndDelete} />
88
+ <Verdict
89
+ kind="do"
90
+ en="Pass fileUrl for files that can be downloaded and let uploads in progress show their own bar."
91
+ ko="내려받을 수 있는 파일에는 fileUrl을 넘기고, 올리는 중인 파일은 진행 막대가 보이게 둡니다."
92
+ />
93
+
94
+ <En>
95
+
96
+ ## Content
97
+
98
+ - When the list is empty, say so outside it (no files yet, and how to add
99
+ one); FileList renders nothing.
100
+ - Put a heading or label above it that says whose files these are.
101
+
102
+ ## Accessibility
103
+
104
+ What FileList does for you:
105
+
106
+ - Renders a real list (`<ul>` / `<li>`), so a screen reader announces the
107
+ number of files (WCAG 1.3.1).
108
+
109
+ Known gaps, and what to do:
110
+
111
+ - **The "see more" button has no focus ring.** Its styles are reset with
112
+ `all: unset`, which removes the outline (WCAG 2.4.7).
113
+ - Its count shows the initial `visibleCount`, not the number of rows on
114
+ screen, after the first click.
115
+ - Rows inherit the `FileItem` gaps (errors in tooltips, English cancel
116
+ label).
117
+
118
+ ## Tokens
119
+
120
+ The frame and row dividers are `border.subtle`; the "see more" text and
121
+ chevron are `fg.subtle`. Row colours are listed on the FileItem page.
122
+
123
+ ## Related
124
+
125
+ - **FileItem**: one row.
126
+ - **FileInput**: adding files.
127
+ - **Table**: large file sets.
128
+
129
+ </En>
130
+
131
+ <Ko>
132
+
133
+ ## 문구
134
+
135
+ - 목록이 비면 FileList는 아무것도 그리지 않으므로, 목록 밖에서 아직 파일이 없다는 것과
136
+ 추가하는 방법을 알려 줍니다.
137
+ - 위에 누구의 파일인지 알려 주는 제목이나 라벨을 둡니다.
138
+
139
+ ## 접근성
140
+
141
+ FileList가 알아서 하는 일
142
+
143
+ - 실제 목록(`<ul>`·`<li>`)으로 그리므로 화면 낭독기가 파일 개수를 알려 줍니다(WCAG
144
+ 1.3.1).
145
+
146
+ 알려진 한계와 대처
147
+
148
+ - **"더 보기" 버튼에 초점 표시가 없습니다.** 스타일을 `all: unset`으로 지워서 윤곽선이
149
+ 사라집니다(WCAG 2.4.7).
150
+ - 한 번 누른 뒤에도 버튼의 숫자는 화면에 보이는 행 수가 아니라 처음 `visibleCount`를
151
+ 보여 줍니다.
152
+ - 각 행은 `FileItem`의 한계(툴팁 속 오류, 영어 취소 버튼 이름)를 그대로 가집니다.
153
+
154
+ ## 토큰
155
+
156
+ 테두리와 행 구분선은 `border.subtle`, "더 보기" 글자와 화살표는 `fg.subtle`입니다. 행의
157
+ 색은 FileItem 페이지에 있습니다.
158
+
159
+ ## 관련 컴포넌트
160
+
161
+ - **FileItem**: 한 줄.
162
+ - **FileInput**: 파일 추가.
163
+ - **Table**: 많은 파일.
164
+
165
+ </Ko>
@@ -0,0 +1,99 @@
1
+ import { ArgTypes, Canvas, Meta } from '@storybook/blocks';
2
+
3
+ import { En, Ko, LangToggle } from '../../guidelines/_components/Lang';
4
+ import * as FlyToStories from './FlyTo.stories';
5
+
6
+ <Meta of={FlyToStories} name="Usage" />
7
+
8
+ <LangToggle />
9
+
10
+ <En>
11
+
12
+ # FlyTo
13
+
14
+ Sends a ghost of an element arcing from one rect to another, to say "this went
15
+ there": a reward chip into a balance, a file into a folder.
16
+
17
+ </En>
18
+
19
+ <Ko>
20
+
21
+ # FlyTo
22
+
23
+ 요소의 복제본을 한 위치에서 다른 위치로 포물선을 그리며 날려 보내, "이것이 저기로
24
+ 갔다"를 보여 줍니다. 보상 칩이 잔액으로, 파일이 폴더로 들어가는 장면에 씁니다.
25
+
26
+ </Ko>
27
+
28
+ <Canvas of={FlyToStories.Default} />
29
+
30
+ <En>
31
+
32
+ ## When to use
33
+
34
+ - A reward the reader just earned travels to the balance or counter it adds
35
+ to, and the balance updates when it lands (`onDone`).
36
+ - An item moves to a place that is visible on screen, and seeing it go there
37
+ explains where it went.
38
+
39
+ ## When not to use
40
+
41
+ - The target is not on screen, or there is no meaningful source → skip the
42
+ flight and `Pulse` the value that changed.
43
+ - Only a number changed → `CountUp`, optionally inside a `Pulse`.
44
+ - Something new appears in place → `Appear`.
45
+
46
+ ## Reduced motion and accessibility
47
+
48
+ - With `prefers-reduced-motion: reduce` nothing flies: FlyTo renders nothing and
49
+ calls `onDone` right after mounting, so whatever the flight was sequencing
50
+ still happens.
51
+ - The ghost is decoration. It renders into a portal at the end of the page, so
52
+ a screen reader can reach a copy of its text there. Pass `aria-hidden` when
53
+ the ghost holds text.
54
+ - The meaning lives in the state change, not the flight. Update the target
55
+ value in `onDone`, and if the reader must hear it, announce it yourself
56
+ (WCAG 4.1.3).
57
+ - The ghost ignores the pointer (`pointer-events: none`), so it never blocks
58
+ a click.
59
+
60
+ ## Props
61
+
62
+ `from` and `to` are viewport rects, usually from `getBoundingClientRect()`.
63
+ The caller mounts FlyTo to start a flight and unmounts it in `onDone`.
64
+
65
+ </En>
66
+
67
+ <Ko>
68
+
69
+ ## 언제 쓰나요
70
+
71
+ - 사용자가 방금 받은 보상이 그것이 더해질 잔액이나 카운터로 날아가고, 도착하는 순간
72
+ 잔액이 바뀔 때(`onDone`).
73
+ - 항목이 화면에 보이는 다른 자리로 옮겨 가고, 날아가는 모습이 어디로 갔는지를 설명해 줄 때.
74
+
75
+ ## 언제 쓰지 않나요
76
+
77
+ - 목적지가 화면에 없거나 출발점이 뚜렷하지 않다면 날리지 말고, 바뀐 값에 `Pulse`를
78
+ 씁니다.
79
+ - 숫자만 바뀌었다면 `CountUp`을 쓰고, 필요하면 `Pulse`로 감쌉니다.
80
+ - 새 요소가 제자리에 나타난다면 `Appear`를 씁니다.
81
+
82
+ ## 동작 줄이기와 접근성
83
+
84
+ - `prefers-reduced-motion: reduce`에서는 아무것도 날지 않습니다. FlyTo는 아무것도
85
+ 그리지 않고 마운트 직후 `onDone`을 부르므로, 비행 뒤에 이어지던 일은 그대로 진행됩니다.
86
+ - 복제본은 장식입니다. 페이지 끝의 포털에 그려지므로 화면 낭독기가 거기서 그 글자를 한
87
+ 번 더 만날 수 있습니다. 복제본에 글자가 있다면 `aria-hidden`을 넘깁니다.
88
+ - 뜻은 비행이 아니라 상태 변화에 있습니다. `onDone`에서 목적지 값을 바꾸고, 사용자가
89
+ 들어야 하는 변화라면 직접 알립니다(WCAG 4.1.3).
90
+ - 복제본은 포인터를 받지 않으므로(`pointer-events: none`) 클릭을 막지 않습니다.
91
+
92
+ ## 속성
93
+
94
+ `from`과 `to`는 뷰포트 기준 사각형이며 보통 `getBoundingClientRect()`에서 얻습니다.
95
+ 비행을 시작하려면 FlyTo를 마운트하고, `onDone`에서 언마운트합니다.
96
+
97
+ </Ko>
98
+
99
+ <ArgTypes of={FlyToStories} include={['from', 'to', 'lift', 'onDone']} />
@@ -0,0 +1,149 @@
1
+ import { useState } from 'react';
2
+ import { Box, Dialog } from '@chakra-ui/react';
3
+ import type { Meta, StoryObj } from '@storybook/react';
4
+
5
+ import { Button } from '../Button';
6
+ import { Input } from '../Input';
7
+ import { SelectField } from '../Select';
8
+ import { Switch } from '../Switch';
9
+ import { Textarea } from '../Textarea';
10
+ import { Subtext } from '../Typography';
11
+ import {
12
+ FieldRow,
13
+ FormFooter,
14
+ FormModalFooter,
15
+ FormScrollArea,
16
+ FormSection,
17
+ } from '.';
18
+
19
+ /**
20
+ * The Form building blocks together: `FormSection` groups, `FieldRow` fields,
21
+ * a `FormFooter` pinned under a `FormScrollArea`. Each part has its own story
22
+ * set below this one.
23
+ */
24
+ const meta = {
25
+ title: 'Components/Form',
26
+ parameters: { layout: 'padded' },
27
+ } satisfies Meta;
28
+
29
+ export default meta;
30
+
31
+ type Story = StoryObj;
32
+
33
+ const MODELS = [
34
+ { label: 'GPT-5', value: 'gpt-5' },
35
+ { label: 'Claude Sonnet', value: 'claude-sonnet' },
36
+ { label: 'Gemini Flash', value: 'gemini-flash' },
37
+ ];
38
+
39
+ const SettingsPanel = ({ withErrors = false }: { withErrors?: boolean }) => {
40
+ const [name, setName] = useState(withErrors ? '' : '사내 규정 도우미');
41
+ return (
42
+ <Box
43
+ h="460px"
44
+ w="400px"
45
+ borderWidth="1px"
46
+ borderColor="border.subtle"
47
+ borderRadius="12px"
48
+ overflow="hidden"
49
+ >
50
+ <FormScrollArea>
51
+ <Box p={4} flex={1}>
52
+ <FormSection title="기본 정보" gap={4}>
53
+ <FieldRow
54
+ label="챗봇 이름"
55
+ required
56
+ error={
57
+ withErrors && !name ? '챗봇 이름을 입력해 주세요.' : undefined
58
+ }
59
+ >
60
+ <Input value={name} onChange={(e) => setName(e.target.value)} />
61
+ </FieldRow>
62
+ <FieldRow label="설명" helperText="목록에서 이름 아래에 보입니다.">
63
+ <Textarea rows={3} />
64
+ </FieldRow>
65
+ </FormSection>
66
+ <FormSection title="동작" divider mt={6} gap={4}>
67
+ <FieldRow label="기본 모델">
68
+ <SelectField
69
+ ariaLabel="기본 모델"
70
+ options={MODELS}
71
+ defaultValue="gpt-5"
72
+ />
73
+ </FieldRow>
74
+ <Switch display="flex" justifyContent="space-between" w="100%">
75
+ <Switch.Label>웹 검색 사용</Switch.Label>
76
+ <Switch.Control />
77
+ </Switch>
78
+ </FormSection>
79
+ </Box>
80
+ <FormFooter
81
+ meta={<Subtext color="fg.muted">오후 2:14에 저장됨</Subtext>}
82
+ action={<Button variant="primary">저장</Button>}
83
+ />
84
+ </FormScrollArea>
85
+ </Box>
86
+ );
87
+ };
88
+
89
+ /** A settings panel: two sections, a pinned footer with meta and action. */
90
+ export const Basic: Story = {
91
+ render: () => <SettingsPanel />,
92
+ };
93
+
94
+ // ───────────────────────── Usage-page specimens ─────────────────────────
95
+ // Pulled into `Form.usage.mdx` with <Canvas of={…} />. Hidden (`!dev`).
96
+
97
+ export const DoErrorsAtTheField: Story = {
98
+ tags: ['!dev'],
99
+ name: 'Usage / Do: errors at the field',
100
+ render: () => <SettingsPanel withErrors />,
101
+ };
102
+
103
+ export const DoModalFooter: Story = {
104
+ tags: ['!dev'],
105
+ name: 'Usage / Do: FormModalFooter in a modal form',
106
+ render: () => (
107
+ // `ModalFooter` reads the dialog's style context; a closed root provides it
108
+ // without opening anything.
109
+ <Dialog.Root>
110
+ <Box
111
+ w="480px"
112
+ bg="bg.surface"
113
+ borderWidth="1px"
114
+ borderColor="border.subtle"
115
+ borderRadius="12px"
116
+ >
117
+ <FormModalFooter
118
+ onDelete={() => {}}
119
+ deleteLabel="그룹 삭제"
120
+ onCancel={() => {}}
121
+ cancelLabel="취소"
122
+ submitLabel="저장"
123
+ onSubmit={() => {}}
124
+ />
125
+ </Box>
126
+ </Dialog.Root>
127
+ ),
128
+ };
129
+
130
+ export const DontHandRolledFooter: Story = {
131
+ tags: ['!dev'],
132
+ name: "Usage / Don't: a hand-rolled footer",
133
+ render: () => (
134
+ <Box
135
+ w="480px"
136
+ p={4}
137
+ display="flex"
138
+ gap={2}
139
+ bg="bg.surface"
140
+ borderWidth="1px"
141
+ borderColor="border.subtle"
142
+ borderRadius="12px"
143
+ >
144
+ <Button variant="primary">저장</Button>
145
+ <Button variant="danger">그룹 삭제</Button>
146
+ <Button variant="subtle">취소</Button>
147
+ </Box>
148
+ ),
149
+ };
@@ -0,0 +1,201 @@
1
+ import { Canvas, Meta } from '@storybook/blocks';
2
+
3
+ import { En, Ko, LangToggle, Verdict } from '../../guidelines/_components/Lang';
4
+ import * as FormStories from './Form.stories';
5
+
6
+ <Meta of={FormStories} name="Usage" />
7
+
8
+ <LangToggle />
9
+
10
+ <En>
11
+
12
+ # Form
13
+
14
+ The layout parts every form shares: fields, sections, a scroll area and the
15
+ footer with the actions.
16
+
17
+ | Part | What it does |
18
+ | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
19
+ | `FieldRow` | One field: label (with `description` and an `endAdornment` slot), the control, `helperText` and `error`. Wraps `FormControl`, so ids and ARIA are wired for you. |
20
+ | `FieldLabel` / `FieldHelp` / `FieldError` | The pieces `FieldRow` is made of, for layouts it does not cover. `FieldError` renders nothing when empty. |
21
+ | `FormSection` | A titled group of fields with one gap (default `4`). `divider` adds a top rule; `collapsible` (with a `title`) turns it into an accordion, and `hasError` flags a collapsed section that hides errors. |
22
+ | `FormSectionHeader` | The section title row (`H5`, description, `titleAdornment`, `action`) on its own. |
23
+ | `FormScrollArea` | A full-height scrolling column, so a sticky footer pins to its bottom. |
24
+ | `FormFooter` | A sticky footer for panel forms: `start` on the left, `meta` (saved time, cost) and `action` on the right. |
25
+ | `FormModalFooter` | The modal footer: optional delete on the far left, Cancel, and a primary submit with its own loading and disabled state. |
26
+
27
+ ## When to use
28
+
29
+ - Any settings panel, create or edit modal, or sidebar form. Start with
30
+ `FieldRow` for every field and `FormSection` for every group.
31
+ - `FormFooter` in a panel that scrolls; `FormModalFooter` in a modal.
32
+
33
+ ## When not to use
34
+
35
+ - A single search box or filter bar → the control on its own, with an
36
+ `ariaLabel` or visible label.
37
+ - A confirm dialog with no fields → `Modal` with its own footer.
38
+
39
+ ## Do / Don't
40
+
41
+ </En>
42
+
43
+ <Ko>
44
+
45
+ # Form
46
+
47
+ 모든 폼이 함께 쓰는 배치 부품입니다. 필드, 섹션, 스크롤 영역, 동작 버튼이 있는 푸터가
48
+ 있습니다.
49
+
50
+ | 부품 | 하는 일 |
51
+ | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
52
+ | `FieldRow` | 필드 하나. 라벨(`description`, `endAdornment` 자리 포함), 컨트롤, `helperText`, `error`. `FormControl`을 감싸므로 id와 ARIA 연결을 대신 해 줍니다. |
53
+ | `FieldLabel` / `FieldHelp` / `FieldError` | `FieldRow`를 이루는 조각. `FieldRow`로 안 되는 배치에 씁니다. `FieldError`는 비어 있으면 아무것도 그리지 않습니다. |
54
+ | `FormSection` | 제목이 있는 필드 묶음. 간격은 하나로 통일합니다(기본 `4`). `divider`는 위에 구분선을 긋고, `collapsible`(`title` 필요)은 접히는 묶음으로 바꾸며, `hasError`는 접힌 섹션에 숨은 오류를 표시합니다. |
55
+ | `FormSectionHeader` | 섹션 제목 줄(`H5`, 설명, `titleAdornment`, `action`)만 따로 쓸 때. |
56
+ | `FormScrollArea` | 높이를 꽉 채우고 스크롤되는 세로 영역. 붙박이 푸터가 아래에 고정됩니다. |
57
+ | `FormFooter` | 패널 폼의 붙박이 푸터. 왼쪽에 `start`, 오른쪽에 `meta`(저장 시각, 비용)와 `action`. |
58
+ | `FormModalFooter` | 모달 푸터. 맨 왼쪽의 삭제(선택), 취소, 로딩과 비활성 상태를 스스로 가진 기본 제출 버튼. |
59
+
60
+ ## 언제 쓰나요
61
+
62
+ - 설정 패널, 만들기·수정 모달, 사이드바 폼 어디서나 씁니다. 필드마다 `FieldRow`를,
63
+ 묶음마다 `FormSection`을 먼저 씁니다.
64
+ - 스크롤되는 패널에는 `FormFooter`를, 모달에는 `FormModalFooter`를 씁니다.
65
+
66
+ ## 언제 쓰지 않나요
67
+
68
+ - 검색창이나 필터 막대 하나는 컨트롤만 두고 `ariaLabel`이나 보이는 라벨을 붙입니다.
69
+ - 입력란 없이 확인만 받는 대화상자는 `Modal`의 푸터를 씁니다.
70
+
71
+ ## 이렇게 하세요 / 이렇게 하지 마세요
72
+
73
+ </Ko>
74
+
75
+ <Canvas of={FormStories.Basic} />
76
+ <Verdict
77
+ kind="do"
78
+ en="Build panels from FormSection and FieldRow, and pin the action in a FormFooter inside a FormScrollArea."
79
+ ko="패널은 FormSection과 FieldRow로 짜고, 동작 버튼은 FormScrollArea 안의 FormFooter에 고정합니다."
80
+ />
81
+
82
+ <Canvas of={FormStories.DoErrorsAtTheField} />
83
+ <Verdict
84
+ kind="do"
85
+ en="Show each error under its own field with FieldRow error. The field is marked invalid for assistive technology at the same time."
86
+ ko="오류는 FieldRow의 error로 해당 필드 바로 아래에 보여 줍니다. 보조기술에도 그 필드가 오류 상태라고 함께 전달됩니다."
87
+ />
88
+
89
+ <Canvas of={FormStories.DoModalFooter} />
90
+ <Verdict
91
+ kind="do"
92
+ en="Use FormModalFooter in modals: delete far left, Cancel, then the primary action last."
93
+ ko="모달에는 FormModalFooter를 씁니다. 삭제는 맨 왼쪽, 그다음 취소, 기본 동작은 맨 끝입니다."
94
+ />
95
+
96
+ <Canvas of={FormStories.DontHandRolledFooter} />
97
+ <Verdict
98
+ kind="dont"
99
+ en="Hand-roll the footer. The order changes from modal to modal, and the danger action ends up next to Save."
100
+ ko="푸터를 직접 만들지 마세요. 모달마다 순서가 달라지고, 위험한 동작이 저장 버튼 옆에 붙게 됩니다."
101
+ />
102
+
103
+ <En>
104
+
105
+ ## Content
106
+
107
+ - Section titles are nouns (기본 정보, 동작). Field rules are on the Input and
108
+ Select pages.
109
+ - Footer labels name the result (저장, 만들기, 그룹 삭제). The design system
110
+ owns no copy: `cancelLabel`, `submitLabel` and `deleteLabel` come from your
111
+ translations.
112
+ - `meta` is a fact, not an instruction: 오후 2:14에 저장됨, 10 크레딧.
113
+
114
+ ## Accessibility
115
+
116
+ What these parts do for you:
117
+
118
+ - `FieldRow` associates the label, sets `aria-invalid` when `error` is set,
119
+ and points `aria-describedby` at the helper and error text. `required`
120
+ shows the asterisk and marks the control required.
121
+ - `FormModalFooter` keeps the submit a `type="submit"` button inside a
122
+ `<form>`, so Enter submits; pass `onSubmit` only for a portalled or
123
+ form-less modal. `isSubmitting` disables every button so the modal cannot be
124
+ dismissed mid-request.
125
+ - A collapsed `FormSection` with `hasError` swaps its chevron for a danger
126
+ glyph, so hidden errors are not missed.
127
+
128
+ What you still have to do:
129
+
130
+ - **Name controls that `FieldRow` cannot reach.** `SelectField`,
131
+ `ComboboxField` and `Slider` take their name from their own label part: pass
132
+ the row's label as `ariaLabel` (`ACCESSIBILITY.md` §1).
133
+ - A flat `FormSection` and `FormSectionHeader` render the title as an `<h5>`. Check
134
+ that the level fits the page outline; where it does not, leave `title` out
135
+ and put your own heading of the right level above the fields (WCAG 1.3.1;
136
+ KWCAG 5.2.4.2 제목 제공).
137
+ - On submit, move focus to the first field with an error, or summarise the
138
+ errors at the top (WCAG 3.3.1).
139
+
140
+ ## Tokens
141
+
142
+ Form parts are layout. `FormFooter` uses `bg.surface` with a `border.subtle`
143
+ top rule, `FormSection` `divider` uses `border.subtle`, and `FieldError` uses
144
+ `danger.fg`. Section descriptions and `FieldLabel` descriptions are
145
+ `fg.muted`.
146
+
147
+ ## Related
148
+
149
+ - **FormControl** / **FormLabel**: the primitives under `FieldRow`.
150
+ - **FormIntegration**: wiring these to Formik or React Hook Form.
151
+ - **Modal**: `FormModalFooter` sits in `ModalFooter`.
152
+
153
+ </En>
154
+
155
+ <Ko>
156
+
157
+ ## 문구
158
+
159
+ - 섹션 제목은 명사로 씁니다(기본 정보, 동작). 필드 문구 규칙은 Input과 Select 페이지에
160
+ 있습니다.
161
+ - 푸터 버튼에는 결과를 씁니다(저장, 만들기, 그룹 삭제). 디자인 시스템은 문구를 가지지
162
+ 않으므로 `cancelLabel`, `submitLabel`, `deleteLabel`은 앱의 번역에서 넘깁니다.
163
+ - `meta`에는 지시가 아니라 사실을 씁니다. 예: 오후 2:14에 저장됨, 10 크레딧.
164
+
165
+ ## 접근성
166
+
167
+ 이 부품들이 알아서 하는 일
168
+
169
+ - `FieldRow`는 라벨을 연결하고, `error`가 있으면 `aria-invalid`를 붙이며, 도움말과 오류
170
+ 문구를 `aria-describedby`로 가리킵니다. `required`를 주면 별표가 보이고 컨트롤도 필수로
171
+ 표시됩니다.
172
+ - `FormModalFooter`는 `<form>` 안에서 제출 버튼을 `type="submit"`으로 두어 Enter로
173
+ 제출되게 합니다. `onSubmit`은 포털로 그려지거나 `<form>`이 없는 모달에서만 넘깁니다.
174
+ `isSubmitting`이면 모든 버튼이 비활성이 되어 요청 중에 모달이 닫히지 않습니다.
175
+ - `hasError`인 `FormSection`이 접혀 있으면 펼침 화살표 대신 위험 표시가 보여 숨은
176
+ 오류를 놓치지 않습니다.
177
+
178
+ 직접 챙겨야 하는 일
179
+
180
+ - **`FieldRow`가 닿지 않는 컨트롤에는 따로 이름을 줍니다.** `SelectField`,
181
+ `ComboboxField`, `Slider`는 자기 라벨 부품에서 이름을 가져오므로 행의 라벨을
182
+ `ariaLabel`로 넘깁니다(`ACCESSIBILITY.md` 1절).
183
+ - 펼친 `FormSection`과 `FormSectionHeader`는 제목을 `<h5>`로 그립니다. 그 수준이 화면 구조에
184
+ 맞는지 확인하고, 맞지 않으면 `title`을 비우고 필드 위에 알맞은 수준의 제목을 직접
185
+ 둡니다(WCAG 1.3.1, KWCAG 5.2.4.2 제목 제공).
186
+ - 제출할 때 오류가 있으면 첫 오류 필드로 초점을 옮기거나 위쪽에 오류를 모아 보여
187
+ 줍니다(WCAG 3.3.1).
188
+
189
+ ## 토큰
190
+
191
+ Form 부품은 배치용입니다. `FormFooter`는 `bg.surface`에 `border.subtle` 윗선을,
192
+ `FormSection`의 `divider`는 `border.subtle`을, `FieldError`는 `danger.fg`를 씁니다.
193
+ 섹션 설명과 `FieldLabel` 설명은 `fg.muted`입니다.
194
+
195
+ ## 관련 컴포넌트
196
+
197
+ - **FormControl** / **FormLabel**: `FieldRow` 아래의 기본 부품.
198
+ - **FormIntegration**: Formik이나 React Hook Form과 연결하기.
199
+ - **Modal**: `FormModalFooter`는 `ModalFooter` 안에 들어갑니다.
200
+
201
+ </Ko>
@@ -25,7 +25,10 @@ export interface FormModalFooterProps extends Omit<
25
25
  cancelLabel: ReactNode;
26
26
  /** Primary submit button label. */
27
27
  submitLabel: ReactNode;
28
- /** Palette for the primary button. Defaults to `primary`. */
28
+ /**
29
+ * @deprecated 5.0 Button has no colour palette. Only `'danger'` changes
30
+ * anything (a danger submit button); every other value renders `primary`.
31
+ */
29
32
  submitColorPalette?: ButtonColorPalette;
30
33
  /**
31
34
  * In-flight state — shows the submit spinner and disables every button so the
@@ -1,6 +1,7 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
2
  import { Stack } from '@chakra-ui/react';
3
3
 
4
+ import { FieldError, FieldRow } from '../Form';
4
5
  import { FormLabel } from '../FormLabel';
5
6
  import { Input } from '../Input';
6
7
  import { FormControl } from './FormControl';
@@ -76,3 +77,37 @@ export const ReadOnly: Story = {
76
77
  </FormControl>
77
78
  ),
78
79
  };
80
+
81
+ // ───────────────────────── Usage-page specimens ─────────────────────────
82
+ // Pulled into `FormControl.usage.mdx` with <Canvas of={…} />. Hidden (`!dev`).
83
+
84
+ export const DoFieldRowFirst: Story = {
85
+ tags: ['!dev'],
86
+ parameters: { layout: 'padded' },
87
+ name: 'Usage / Do: FieldRow for the common field',
88
+ render: () => (
89
+ <Stack w="320px">
90
+ <FieldRow
91
+ label="표시 이름"
92
+ helperText="다른 멤버에게 보이는 이름입니다."
93
+ error="이름을 입력해 주세요."
94
+ required
95
+ >
96
+ <Input />
97
+ </FieldRow>
98
+ </Stack>
99
+ ),
100
+ };
101
+
102
+ export const DoInvalidOnTheField: Story = {
103
+ tags: ['!dev'],
104
+ parameters: { layout: 'padded' },
105
+ name: 'Usage / Do: invalid on FormControl',
106
+ render: () => (
107
+ <FormControl invalid required w="320px">
108
+ <FormLabel>업무용 이메일</FormLabel>
109
+ <Input type="email" value="name@company" onChange={() => {}} />
110
+ <FieldError>이메일 주소 형식이 아닙니다.</FieldError>
111
+ </FormControl>
112
+ ),
113
+ };