aural-ui 4.0.1 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/README.md +8 -1
  2. package/dist/components/aspect-ratio/AspectRatio.stories.tsx +290 -1228
  3. package/dist/components/avatar/Avatar.stories.tsx +219 -235
  4. package/dist/components/badge/Badge.stories.tsx +379 -116
  5. package/dist/components/banner/Banner.stories.tsx +445 -391
  6. package/dist/components/breadcrumb/Breadcrumb.stories.tsx +453 -199
  7. package/dist/components/button/Button.stories.tsx +585 -230
  8. package/dist/components/card/Card.stories.tsx +619 -301
  9. package/dist/components/char-count/CharCount.stories.tsx +350 -248
  10. package/dist/components/checkbox/Checkbox.stories.tsx +309 -167
  11. package/dist/components/chip/Chip.stories.tsx +362 -168
  12. package/dist/components/circular-loader/CircularLoader.stories.tsx +221 -636
  13. package/dist/components/clamp-lines/ClampLines.stories.tsx +246 -117
  14. package/dist/components/collapsible/Collapsible.stories.tsx +391 -252
  15. package/dist/components/command/Command.stories.tsx +530 -867
  16. package/dist/components/dialog/Dialog.stories.tsx +501 -950
  17. package/dist/components/divider/Divider.stories.tsx +264 -527
  18. package/dist/components/dot-loader/DotLoader.stories.tsx +256 -257
  19. package/dist/components/drawer/Drawer.stories.tsx +659 -1023
  20. package/dist/components/dropdown/Dropdown.stories.tsx +643 -1028
  21. package/dist/components/form/Form.stories.tsx +560 -274
  22. package/dist/components/helper-text/HelperText.stories.tsx +199 -200
  23. package/dist/components/hover-card/HoverCard.stories.tsx +318 -1254
  24. package/dist/components/icon-button/IconButton.stories.tsx +837 -194
  25. package/dist/components/if-else/if-else.stories.tsx +370 -83
  26. package/dist/components/input/Input.stories.tsx +436 -368
  27. package/dist/components/label/Label.stories.tsx +156 -154
  28. package/dist/components/list/List.stories.tsx +484 -835
  29. package/dist/components/marquee/Marquee.stories.tsx +356 -712
  30. package/dist/components/otp-inputs/OtpInputs.stories.tsx +352 -422
  31. package/dist/components/overlay/Overlay.stories.tsx +452 -824
  32. package/dist/components/pagination/Pagination.stories.tsx +721 -210
  33. package/dist/components/popover/Popover.stories.tsx +481 -896
  34. package/dist/components/radio/Radio.stories.tsx +432 -124
  35. package/dist/components/resizable/Resizable.stories.tsx +495 -799
  36. package/dist/components/scroll-area/ScrollArea.stories.tsx +383 -1059
  37. package/dist/components/search/Search.stories.tsx +312 -595
  38. package/dist/components/select/Select.stories.tsx +684 -789
  39. package/dist/components/sheet/Sheet.stories.tsx +671 -950
  40. package/dist/components/skelton/Skelton.stories.tsx +230 -764
  41. package/dist/components/slider/Slider.stories.tsx +383 -760
  42. package/dist/components/stepper/Stepper.stories.tsx +371 -514
  43. package/dist/components/switch/Switch.stories.tsx +461 -208
  44. package/dist/components/switch-case/SwitchCase.stories.tsx +367 -188
  45. package/dist/components/table/Table.stories.tsx +770 -916
  46. package/dist/components/tabs/Tabs.stories.tsx +458 -1455
  47. package/dist/components/tag/Tag.stories.tsx +714 -542
  48. package/dist/components/textarea/TextArea.stories.tsx +621 -562
  49. package/dist/components/thumbnail-tags/ThumbnailTags.stories.tsx +228 -154
  50. package/dist/components/toast/Toast.stories.tsx +452 -1339
  51. package/dist/components/toggle/Toggle.stories.tsx +488 -931
  52. package/dist/components/tooltip/Tooltip.stories.tsx +344 -1388
  53. package/dist/components/typography/Typography.stories.tsx +406 -89
  54. package/dist/hooks/use-change-state/UseChangeState.stories.tsx +309 -606
  55. package/dist/hooks/use-previous/UsePrevious.stories.tsx +367 -917
  56. package/dist/hooks/use-standalone-pagination/UseStandalonePagination.stories.tsx +639 -867
  57. package/dist/icons/Icons.stories.tsx +0 -12
  58. package/dist/icons/ai-avatar-icon/AiAvatarIcon.stories.tsx +223 -1060
  59. package/dist/icons/alert-icon/AlertIcon.stories.tsx +106 -968
  60. package/dist/icons/all-icons.tsx +37 -16
  61. package/dist/icons/angle-down-icon/AngleDownIcon.stories.tsx +137 -1010
  62. package/dist/icons/apple-logo-icon/AppleLogoIcon.stories.tsx +145 -935
  63. package/dist/icons/arrow-box-left-icon/ArrowBoxLeftIcon.stories.tsx +132 -1046
  64. package/dist/icons/arrow-corner-up-left-icon/ArrowCornerUpLeftIcon.stories.tsx +134 -986
  65. package/dist/icons/arrow-corner-up-right-icon/ArrowCornerUpRightIcon.stories.tsx +135 -1028
  66. package/dist/icons/arrow-left-icon/ArrowLeftIcon.stories.tsx +133 -971
  67. package/dist/icons/arrow-right-icon/ArrowRightIcon.stories.tsx +145 -1123
  68. package/dist/icons/arrow-right-up-icon/ArrowRightUpIcon.stories.tsx +143 -1252
  69. package/dist/icons/art-board-icon/ArtBoardIcon.stories.tsx +123 -632
  70. package/dist/icons/audio-bar-icon/AudioBarIcon.stories.tsx +141 -1223
  71. package/dist/icons/backward-ten-seconds-icon/BackwardTenSecondsIcon.stories.tsx +164 -1018
  72. package/dist/icons/bubble-check-icon/BubbleCheckIcon.stories.tsx +121 -1236
  73. package/dist/icons/bubble-crossed-icon/BubbleCrossedIcon.stories.tsx +121 -1213
  74. package/dist/icons/bubble-sparkle-icon/BubbleSparkleIcon.stories.tsx +116 -893
  75. package/dist/icons/camera-icon/CameraIcon.stories.tsx +109 -1254
  76. package/dist/icons/capital-a-letter-icon/CapitalALetterIcon.stories.tsx +114 -975
  77. package/dist/icons/chevron-double-left-icon/ChevronDoubleLeftIcon.stories.tsx +157 -994
  78. package/dist/icons/chevron-double-right-icon/ChevronDoubleRightIcon.stories.tsx +160 -992
  79. package/dist/icons/chevron-down-icon/ChevronDownIcon.stories.tsx +140 -970
  80. package/dist/icons/chevron-left-icon/ChevronLeftIcon.stories.tsx +126 -993
  81. package/dist/icons/chevron-right-icon/ChevronRightIcon.stories.tsx +144 -987
  82. package/dist/icons/chevron-up-icon/ChevronUpIcon.stories.tsx +141 -1007
  83. package/dist/icons/circle-tick-icon/CircleTickIcon.stories.tsx +147 -1187
  84. package/dist/icons/circular-play-icon/CircularPlayIcon.stories.tsx +110 -476
  85. package/dist/icons/coin-icon/CoinIcon.stories.tsx +120 -1364
  86. package/dist/icons/coin-toons-icon/CoinToonsIcon.stories.tsx +113 -1360
  87. package/dist/icons/column-wide-add-icon/ColumnWideAddIcon.stories.tsx +111 -942
  88. package/dist/icons/command-icon/CommandIcon.stories.tsx +124 -1087
  89. package/dist/icons/copy-icon/CopyIcon.stories.tsx +119 -996
  90. package/dist/icons/cross-circle-icon/CrossCircleIcon.stories.tsx +144 -1046
  91. package/dist/icons/cross-icon/CrossIcon.stories.tsx +136 -999
  92. package/dist/icons/download-icon/DownloadIcon.stories.tsx +123 -857
  93. package/dist/icons/edit-big-icon/EditBigIcon.stories.tsx +121 -1080
  94. package/dist/icons/email-icon/EmailIcon.stories.tsx +112 -979
  95. package/dist/icons/expand-icon/ExpandIcon.stories.tsx +109 -1146
  96. package/dist/icons/eye-close-icon/EyeCloseIcon.stories.tsx +141 -1068
  97. package/dist/icons/eye-open-icon/EyeOpenIcon.stories.tsx +140 -1081
  98. package/dist/icons/feature-shine-icon/FeatureShineIcon.stories.tsx +124 -1050
  99. package/dist/icons/file-chart-icon/FileChartIcon.stories.tsx +123 -1091
  100. package/dist/icons/file-text-icon/FileTextIcon.stories.tsx +122 -633
  101. package/dist/icons/filter-bar-row-icon/FilterBarRowIcon.stories.tsx +116 -1087
  102. package/dist/icons/forward-ten-seconds-icon/ForwardTenSecondsIcon.stories.tsx +166 -1020
  103. package/dist/icons/git-branch-icon/GitBranchIcon.stories.tsx +112 -1182
  104. package/dist/icons/git-fork-icon/GitForkIcon.stories.tsx +112 -1155
  105. package/dist/icons/globe-icon/GlobeIcon.stories.tsx +127 -325
  106. package/dist/icons/google-logo-icon/GoogleLogoIcon.stories.tsx +142 -985
  107. package/dist/icons/grip-vertical-icon/GripVerticalIcon.stories.tsx +116 -1217
  108. package/dist/icons/head-icon/HeadIcon.stories.tsx +108 -953
  109. package/dist/icons/heart-icon/HeartIcon.stories.tsx +117 -1060
  110. package/dist/icons/image-avatar-sparkle-icon/ImageAvatarSparkleIcon.stories.tsx +116 -716
  111. package/dist/icons/image-icon/ImageIcon.stories.tsx +102 -1164
  112. package/dist/icons/import-folder-icon/ImportFolderIcon.stories.tsx +108 -1233
  113. package/dist/icons/import-left-arrow-folder-icon/ImportLeftArrowFolderIcon.stories.tsx +133 -1289
  114. package/dist/icons/indian-flag-icon/IndianFlagIcon.stories.tsx +155 -1012
  115. package/dist/icons/instagram-icon/InstagramIcon.stories.tsx +158 -1438
  116. package/dist/icons/layout-column-icon/LayoutColumnIcon.stories.tsx +121 -1011
  117. package/dist/icons/layout-left-icon/LayoutLeftIcon.stories.tsx +116 -981
  118. package/dist/icons/layout-right-icon/LayoutRightIcon.stories.tsx +116 -979
  119. package/dist/icons/light-bulb-simple-icon/LightBulbSimpleIcon.stories.tsx +105 -1252
  120. package/dist/icons/linked-in-icon/LinkedInIcon.stories.tsx +151 -1554
  121. package/dist/icons/magic-book-icon/MagicBookIcon.stories.tsx +107 -1227
  122. package/dist/icons/magic-edit-icon/MagicEditIcon.stories.tsx +116 -707
  123. package/dist/icons/maintenance-icon/MaintenanceIcon.stories.tsx +119 -1226
  124. package/dist/icons/message-icon/MessageIcon.stories.tsx +111 -557
  125. package/dist/icons/minimize-icon/MinimizeIcon.stories.tsx +112 -1198
  126. package/dist/icons/moon-icon/MoonIcon.stories.tsx +117 -557
  127. package/dist/icons/move-horizontal-icon/MoveHorizontalIcon.stories.tsx +106 -1235
  128. package/dist/icons/move-vertical-icon/MoveVerticalIcon.stories.tsx +112 -1185
  129. package/dist/icons/musical-note-icon/MusicalNoteIcon.stories.tsx +116 -1012
  130. package/dist/icons/notepad-icon/NotepadIcon.stories.tsx +108 -1137
  131. package/dist/icons/notes-icon/NotesIcon.stories.tsx +116 -1138
  132. package/dist/icons/page-search-icon/PageSearchIcon.stories.tsx +106 -1146
  133. package/dist/icons/page-text-icon/PageTextIcon.stories.tsx +119 -719
  134. package/dist/icons/paint-roll-icon/PaintRollIcon.stories.tsx +110 -999
  135. package/dist/icons/paper-plane-icon/PaperPlaneIcon.stories.tsx +109 -912
  136. package/dist/icons/pause-icon/PauseIcon.stories.tsx +110 -1041
  137. package/dist/icons/pencil-icon/PencilIcon.stories.tsx +112 -1109
  138. package/dist/icons/phone-icon/PhoneIcon.stories.tsx +112 -1023
  139. package/dist/icons/plus-icon/PlusIcon.stories.tsx +103 -1132
  140. package/dist/icons/pocket-studio-icon/PocketStudioIcon.stories.tsx +104 -870
  141. package/dist/icons/scroll-down-icon/ScrollDownIcon.stories.tsx +99 -476
  142. package/dist/icons/search-icon/SearchIcon.stories.tsx +108 -1161
  143. package/dist/icons/setting-icon/SettingIcon.stories.tsx +104 -1009
  144. package/dist/icons/share-icon/ShareIcon.stories.tsx +117 -1064
  145. package/dist/icons/shield-icon/ShieldIcon.stories.tsx +114 -974
  146. package/dist/icons/site-logo-icon/SiteLogoIcon.stories.tsx +134 -1160
  147. package/dist/icons/skip-backward-icon/SkipBackwardIcon.stories.tsx +169 -1017
  148. package/dist/icons/skip-forward-icon/SkipForwardIcon.stories.tsx +161 -1016
  149. package/dist/icons/sparkles-soft-icon/SparklesSoftIcon.stories.tsx +102 -1001
  150. package/dist/icons/spinner-gradient-icon/SpinnerGradientIcon.stories.tsx +155 -593
  151. package/dist/icons/spinner-solid-icon/SpinnerSolidIcon.stories.tsx +155 -608
  152. package/dist/icons/spinner-solid-neutral-icon/SpinnerSolidINeutralcon.stories.tsx +142 -712
  153. package/dist/icons/star-icon/StarIcon.stories.tsx +120 -946
  154. package/dist/icons/store-coin-icon/StoreCoinIcon.stories.tsx +109 -1013
  155. package/dist/icons/suggestion-icon/SuggestionIcon.stories.tsx +113 -891
  156. package/dist/icons/sun-icon/SunIcon.stories.tsx +117 -864
  157. package/dist/icons/text-color-icon/TextColorIcon.stories.tsx +113 -989
  158. package/dist/icons/text-indicator-icon/TextIndicatorIcon.stories.tsx +120 -1027
  159. package/dist/icons/threads-icon/ThreadsIcon.stories.tsx +153 -1476
  160. package/dist/icons/tick-circle-icon/TickCircleIcon.stories.tsx +143 -1187
  161. package/dist/icons/tick-icon/TickIcon.stories.tsx +142 -1322
  162. package/dist/icons/trash-icon/TrashIcon.stories.tsx +105 -970
  163. package/dist/icons/twitter-x-icon/TwitterXIcon.stories.tsx +154 -1457
  164. package/dist/icons/upload-icon/UploadIcon.stories.tsx +112 -930
  165. package/dist/icons/vertical-menu-icon/VerticalMenuIcon.stories.tsx +115 -1019
  166. package/dist/icons/video-play-list-icon/VideoPlaylistIcon.stories.tsx +122 -1092
  167. package/dist/icons/voice-playing-icon/VoicePlayingIcon.stories.tsx +120 -1401
  168. package/dist/icons/volume-full-icon/VolumeFullIcon.stories.tsx +107 -1212
  169. package/dist/icons/volume-half-icon/VolumeHalfIcon.stories.tsx +109 -1122
  170. package/dist/icons/volume-off-icon/VolumeOffIcon.stories.tsx +112 -1124
  171. package/dist/icons/warning-icon/WarningIcon.stories.tsx +119 -1083
  172. package/dist/icons/youtube-icon/YoutubeIcon.stories.tsx +158 -983
  173. package/dist/index.cjs +90 -90
  174. package/dist/index.js +90 -90
  175. package/package.json +8 -3
@@ -15,89 +15,38 @@ import {
15
15
  } from "@/ui/components/select"
16
16
  import type { Meta, StoryObj } from "@storybook/react-vite"
17
17
 
18
- import { Button } from "../button"
18
+ import { AuralComponentDocsPage } from "src/ui/story-spec/components/component-story-docs-page"
19
+
20
+ // ─── Meta ─────────────────────────────────────────────────────────────────────
19
21
 
20
22
  const meta: Meta<typeof SelectField> = {
21
23
  title: "Components/UI/Select",
22
24
  component: SelectField,
23
25
  parameters: {
24
26
  layout: "centered",
25
- backgrounds: {
26
- default: "dark",
27
- values: [
28
- { name: "dark", value: "#0a0a0a" },
29
- { name: "light", value: "#ffffff" },
30
- ],
31
- },
32
27
  docs: {
33
28
  description: {
34
- component: `
35
- A customizable select component built on top of Radix UI Select primitives with atomic design principles.
36
- Provides a dropdown selection interface with support for multiple styling variants, decorations,
37
- required fields, helper text, grouping, separators, scroll buttons, and comprehensive keyboard navigation.
38
-
39
- ## Features
40
- - Built on Radix UI for accessibility
41
- - Multiple styling variants (default, error, warning, success)
42
- - Multiple decorations (underline, outline, filled)
43
- - Required field support with visual indicators
44
- - Helper text support with variant styling
45
- - Support for grouped options
46
- - Scroll buttons for long lists
47
- - Keyboard navigation
48
- - Disabled states
49
- - Atomic design composition
50
- - Unstyled mode for custom styling
51
- - CVA-based styling system
52
- - Custom icons (ChevronDown, ChevronUp, Tick)
53
- - Full accessibility support (ARIA attributes)
54
-
55
- ## Usage
56
-
57
- ### Simple Usage with SelectField
58
- \`\`\`tsx
59
- import { SelectField, SelectItem } from '@/ui/components/select'
60
-
61
- <SelectField
62
- label="Country"
63
- placeholder="Select a country..."
64
- required
65
- helperText="Please select your country"
66
- >
67
- <SelectItem value="us">United States</SelectItem>
68
- <SelectItem value="ca">Canada</SelectItem>
69
- </SelectField>
70
- \`\`\`
71
-
72
- ### Atomic Composition
73
- \`\`\`tsx
74
- import {
75
- Select,
76
- SelectContent,
77
- SelectItem,
78
- SelectRoot,
79
- SelectTrigger,
80
- SelectValue,
81
- SelectWrapper,
82
- } from '@/ui/components/select'
83
-
84
- <SelectRoot fullWidth>
85
- <SelectLabel required>Country</SelectLabel>
86
- <SelectWrapper>
87
- <Select>
88
- <SelectTrigger>
89
- <SelectValue placeholder="Select..." />
90
- </SelectTrigger>
91
- <SelectContent>
92
- <SelectItem value="us">United States</SelectItem>
93
- </SelectContent>
94
- </Select>
95
- </SelectWrapper>
96
- <SelectHelperText>Please select your country</SelectHelperText>
97
- </SelectRoot>
98
- \`\`\`
99
- `,
29
+ component:
30
+ "A customisable select built on Radix UI primitives with atomic composition support. Provides a dropdown selection interface with validation variants (default, error, warning, success), three decoration styles (underline, outline, filled), label, helper text, grouping, separators, scroll buttons, and full keyboard navigation and ARIA accessibility.",
100
31
  },
32
+ page: () => (
33
+ <AuralComponentDocsPage
34
+ features={[
35
+ {
36
+ title: "4 Validation States",
37
+ description: "Default to success",
38
+ },
39
+ {
40
+ title: "3 Decoration Styles",
41
+ description: "Underline, outline, filled",
42
+ },
43
+ {
44
+ title: "Groups & Separators",
45
+ description: "Organised option lists",
46
+ },
47
+ ]}
48
+ />
49
+ ),
101
50
  },
102
51
  },
103
52
  tags: ["autodocs"],
@@ -105,108 +54,36 @@ import {
105
54
  label: {
106
55
  control: { type: "text" },
107
56
  description: "Label text for the select",
108
- table: {
109
- type: { summary: "ReactNode" },
110
- defaultValue: { summary: "undefined" },
111
- },
112
57
  },
113
58
  placeholder: {
114
59
  control: { type: "text" },
115
- description: "Placeholder text shown when no option is selected",
116
- table: {
117
- type: { summary: "string" },
118
- defaultValue: { summary: "undefined" },
119
- },
60
+ description: "Placeholder shown when no option is selected",
120
61
  },
121
62
  helperText: {
122
63
  control: { type: "text" },
123
64
  description: "Helper text displayed below the select",
124
- table: {
125
- type: { summary: "ReactNode" },
126
- defaultValue: { summary: "undefined" },
127
- },
128
65
  },
129
66
  variant: {
130
67
  control: { type: "select" },
131
68
  options: ["default", "error", "warning", "success"],
132
- description: "Visual variant affecting border and helper text colors",
133
- table: {
134
- type: { summary: '"default" | "error" | "warning" | "success"' },
135
- defaultValue: { summary: '"default"' },
136
- },
69
+ description: "Visual validation state",
137
70
  },
138
71
  decoration: {
139
72
  control: { type: "select" },
140
73
  options: ["underline", "outline", "filled"],
141
- description: "Visual style of the select border and background",
142
- table: {
143
- type: { summary: '"underline" | "outline" | "filled"' },
144
- defaultValue: { summary: '"underline"' },
145
- },
74
+ description: "Border and background style",
146
75
  },
147
76
  required: {
148
77
  control: { type: "boolean" },
149
- description: "Whether the select is required (adds asterisk to label)",
150
- table: {
151
- type: { summary: "boolean" },
152
- defaultValue: { summary: "false" },
153
- },
78
+ description: "Adds an asterisk to the label and aria-required",
154
79
  },
155
80
  disabled: {
156
81
  control: { type: "boolean" },
157
- description: "Whether the select is disabled and non-interactive",
158
- table: {
159
- type: { summary: "boolean" },
160
- defaultValue: { summary: "false" },
161
- },
82
+ description: "Disables the select entirely",
162
83
  },
163
84
  fullWidth: {
164
85
  control: { type: "boolean" },
165
- description: "Whether the select takes full width of its container",
166
- table: {
167
- type: { summary: "boolean" },
168
- defaultValue: { summary: "false" },
169
- },
170
- },
171
- value: {
172
- control: { type: "text" },
173
- description: "Controlled value of the select",
174
- table: {
175
- type: { summary: "string" },
176
- defaultValue: { summary: "undefined" },
177
- },
178
- },
179
- onValueChange: {
180
- action: "valueChanged",
181
- description: "Callback fired when the value changes",
182
- table: {
183
- type: { summary: "(value: string) => void" },
184
- },
185
- },
186
- classes: {
187
- control: { type: "object" },
188
- description: "Override classes for different parts of the component",
189
- table: {
190
- type: {
191
- summary:
192
- "{ root?: string; label?: string; wrapper?: string; trigger?: string; content?: string; helperText?: string }",
193
- },
194
- defaultValue: { summary: "{}" },
195
- },
196
- },
197
- name: {
198
- control: "text",
199
- description: "The name attribute for form submission",
200
- table: {
201
- type: { summary: "string" },
202
- },
203
- },
204
- id: {
205
- control: "text",
206
- description: "The id attribute for the select",
207
- table: {
208
- type: { summary: "string" },
209
- },
86
+ description: "Expands the select to fill its container",
210
87
  },
211
88
  },
212
89
  args: {
@@ -215,757 +92,775 @@ import {
215
92
  required: false,
216
93
  disabled: false,
217
94
  fullWidth: false,
95
+ label: "Label",
96
+ placeholder: "Select an option…",
218
97
  },
219
98
  }
220
99
 
221
100
  export default meta
222
101
  type Story = StoryObj<typeof meta>
223
102
 
224
- export const Default: Story = {
225
- args: {
226
- label: "Fruit Selection",
227
- placeholder: "Select a fruit",
228
- decoration: "underline",
229
- fullWidth: true,
230
- },
231
- render: (args) => (
232
- <SelectField {...args}>
233
- <SelectItem value="apple">Apple</SelectItem>
234
- <SelectItem value="banana">Banana</SelectItem>
235
- <SelectItem value="orange">Orange</SelectItem>
236
- <SelectItem value="grape">Grape</SelectItem>
237
- <SelectItem value="strawberry">Strawberry</SelectItem>
238
- </SelectField>
239
- ),
240
- }
103
+ // ─── 1. Playground ───────────────────────────────────────────────────────────
241
104
 
242
- // Decoration variants
243
- export const DecorationVariants: Story = {
244
- render: () => (
245
- <div className="max-w-md space-y-6">
246
- <div>
247
- <h3 className="text-fm-primary mb-2 text-sm font-medium">Underline</h3>
248
- <SelectField
249
- label="Underline Style"
250
- placeholder="Minimalist underline style..."
251
- decoration="underline"
252
- fullWidth
253
- >
254
- <SelectItem value="option1">Option 1</SelectItem>
255
- <SelectItem value="option2">Option 2</SelectItem>
256
- <SelectItem value="option3">Option 3</SelectItem>
257
- </SelectField>
258
- </div>
259
-
260
- <div>
261
- <h3 className="text-fm-primary mb-2 text-sm font-medium">Outline</h3>
262
- <SelectField
263
- label="Outline Style"
264
- placeholder="Traditional outlined style..."
265
- decoration="outline"
266
- fullWidth
267
- >
268
- <SelectItem value="option1">Option 1</SelectItem>
269
- <SelectItem value="option2">Option 2</SelectItem>
270
- <SelectItem value="option3">Option 3</SelectItem>
271
- </SelectField>
272
- </div>
273
-
274
- <div>
275
- <h3 className="text-fm-primary mb-2 text-sm font-medium">Filled</h3>
276
- <SelectField
277
- label="Filled Style"
278
- placeholder="Modern filled style..."
279
- decoration="filled"
280
- fullWidth
281
- >
282
- <SelectItem value="option1">Option 1</SelectItem>
283
- <SelectItem value="option2">Option 2</SelectItem>
284
- <SelectItem value="option3">Option 3</SelectItem>
285
- </SelectField>
286
- </div>
287
- </div>
288
- ),
105
+ export const Playground: Story = {
289
106
  parameters: {
290
107
  docs: {
291
108
  description: {
292
109
  story:
293
- "Three decoration styles available: underline (minimalist), outline (traditional), and filled (modern with background).",
110
+ "Use the Storybook controls panel to configure every prop. The select below reflects your selections live.",
294
111
  },
295
112
  },
296
113
  },
297
- }
298
-
299
- export const WithRequiredField: Story = {
300
- args: {
301
- label: "Required Selection",
302
- placeholder: "Please select an option",
303
- required: true,
304
- helperText: "This field is required",
305
- decoration: "outline",
306
- fullWidth: true,
307
- },
308
114
  render: (args) => (
309
- <SelectField {...args}>
310
- <SelectItem value="option1">Option 1</SelectItem>
311
- <SelectItem value="option2">Option 2</SelectItem>
312
- <SelectItem value="option3">Option 3</SelectItem>
313
- </SelectField>
115
+ <div className="w-80 space-y-4">
116
+ <SelectField {...args} fullWidth>
117
+ <SelectItem value="option1">Option 1</SelectItem>
118
+ <SelectItem value="option2">Option 2</SelectItem>
119
+ <SelectItem value="option3">Option 3</SelectItem>
120
+ <SelectItem value="option4">Option 4</SelectItem>
121
+ </SelectField>
122
+ <div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border px-4 py-3">
123
+ <code className="text-fm-secondary text-fm-md leading-fm-md font-(--font-fm-mono)">
124
+ {`<SelectField variant="${args.variant}" decoration="${args.decoration}"${args.required ? " required" : ""}${args.disabled ? " disabled" : ""} />`}
125
+ </code>
126
+ </div>
127
+ </div>
314
128
  ),
315
- parameters: {
316
- docs: {
317
- description: {
318
- story: "Select field with required indicator (asterisk) in the label.",
319
- },
320
- },
321
- },
322
129
  }
323
130
 
324
- export const Variants: Story = {
131
+ // ─── 2. AllVariants ──────────────────────────────────────────────────────────
132
+
133
+ export const AllVariants: Story = {
325
134
  parameters: {
326
135
  docs: {
327
136
  description: {
328
- story: `
329
- Different visual variants of the select component. Each variant uses different colors
330
- to communicate different states or purposes:
331
- - **Default**: Standard styling
332
- - **Error**: Red styling for error states
333
- - **Warning**: Yellow/orange styling for warnings
334
- - **Success**: Green styling for success states
335
- `,
137
+ story:
138
+ "Full matrix of all four validation variants (default, error, warning, success) across all three decoration styles (underline, outline, filled).",
336
139
  },
337
140
  },
338
141
  },
339
- render: () => (
340
- <div className="max-w-md space-y-6">
341
- <SelectField
342
- label="Default Variant"
343
- placeholder="Select an option..."
344
- variant="default"
345
- decoration="outline"
346
- helperText="This is the default variant"
347
- fullWidth
348
- >
349
- <SelectItem value="option1">Option 1</SelectItem>
350
- <SelectItem value="option2">Option 2</SelectItem>
351
- <SelectItem value="option3">Option 3</SelectItem>
352
- </SelectField>
353
-
354
- <SelectField
355
- label="Error Variant"
356
- placeholder="Select an option..."
357
- variant="error"
358
- decoration="outline"
359
- helperText="Something went wrong"
360
- fullWidth
361
- >
362
- <SelectItem value="option1">Option 1</SelectItem>
363
- <SelectItem value="option2">Option 2</SelectItem>
364
- <SelectItem value="option3">Option 3</SelectItem>
365
- </SelectField>
366
-
367
- <SelectField
368
- label="Warning Variant"
369
- placeholder="Select an option..."
370
- variant="warning"
371
- decoration="outline"
372
- helperText="Please double-check your selection"
373
- fullWidth
374
- >
375
- <SelectItem value="option1">Option 1</SelectItem>
376
- <SelectItem value="option2">Option 2</SelectItem>
377
- <SelectItem value="option3">Option 3</SelectItem>
378
- </SelectField>
379
-
380
- <SelectField
381
- label="Success Variant"
382
- placeholder="Select an option..."
383
- variant="success"
384
- decoration="outline"
385
- helperText="Great choice!"
386
- fullWidth
387
- >
388
- <SelectItem value="option1">Option 1</SelectItem>
389
- <SelectItem value="option2">Option 2</SelectItem>
390
- <SelectItem value="option3">Option 3</SelectItem>
391
- </SelectField>
392
- </div>
393
- ),
394
- }
142
+ render: () => {
143
+ const variants = [
144
+ {
145
+ variant: "default" as const,
146
+ label: "Default",
147
+ helperText: "Helper text",
148
+ },
149
+ {
150
+ variant: "error" as const,
151
+ label: "Error",
152
+ helperText: "Something went wrong",
153
+ },
154
+ {
155
+ variant: "warning" as const,
156
+ label: "Warning",
157
+ helperText: "Double-check your selection",
158
+ },
159
+ {
160
+ variant: "success" as const,
161
+ label: "Success",
162
+ helperText: "Great choice!",
163
+ },
164
+ ]
165
+ const decorations = [
166
+ { decoration: "underline" as const, label: "Underline" },
167
+ { decoration: "outline" as const, label: "Outline" },
168
+ { decoration: "filled" as const, label: "Filled" },
169
+ ]
395
170
 
396
- export const WithHelperText: Story = {
397
- args: {
398
- label: "Framework Selection",
399
- placeholder: "Choose your preferred framework",
400
- helperText: "Select the framework you're most comfortable with",
401
- decoration: "filled",
402
- fullWidth: true,
171
+ return (
172
+ <div className="space-y-10">
173
+ {decorations.map(({ decoration, label: decLabel }) => (
174
+ <div key={decoration}>
175
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-4 font-medium">
176
+ {decLabel}
177
+ </h4>
178
+ <div className="flex flex-wrap gap-6">
179
+ {variants.map(({ variant, label, helperText }) => (
180
+ <div key={variant} className="w-48 space-y-2 text-center">
181
+ <SelectField
182
+ label={label}
183
+ placeholder="Select…"
184
+ variant={variant}
185
+ decoration={decoration}
186
+ helperText={helperText}
187
+ fullWidth
188
+ >
189
+ <SelectItem value="opt1">Option 1</SelectItem>
190
+ <SelectItem value="opt2">Option 2</SelectItem>
191
+ <SelectItem value="opt3">Option 3</SelectItem>
192
+ </SelectField>
193
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
194
+ {label}
195
+ </p>
196
+ </div>
197
+ ))}
198
+ </div>
199
+ </div>
200
+ ))}
201
+ </div>
202
+ )
403
203
  },
404
- render: (args) => (
405
- <SelectField {...args}>
406
- <SelectItem value="react">React</SelectItem>
407
- <SelectItem value="vue">Vue.js</SelectItem>
408
- <SelectItem value="angular">Angular</SelectItem>
409
- <SelectItem value="svelte">Svelte</SelectItem>
410
- </SelectField>
411
- ),
412
204
  }
413
205
 
414
- export const Controlled: Story = {
206
+ // ─── 3. Configurations ───────────────────────────────────────────────────────
207
+
208
+ export const Configurations: Story = {
415
209
  parameters: {
416
210
  docs: {
417
211
  description: {
418
- story: `
419
- Example of a controlled select component. The value is managed by the parent component state.
420
- Use this pattern when you need to control the select value programmatically.
421
- `,
212
+ story:
213
+ "Non-variant configuration axes: required field indicator, disabled whole select, individual disabled options, and grouped options with separators.",
422
214
  },
423
215
  },
424
216
  },
425
- render: function ControlledSelect() {
426
- const [value, setValue] = React.useState<string>("")
217
+ render: () => (
218
+ <div className="w-full max-w-sm space-y-10">
219
+ {/* Required */}
220
+ <div>
221
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-4 font-medium">
222
+ Required
223
+ </h4>
224
+ <div className="space-y-2 text-center">
225
+ <SelectField
226
+ label="Country"
227
+ placeholder="Select your country…"
228
+ required
229
+ helperText="This field is required"
230
+ decoration="outline"
231
+ fullWidth
232
+ >
233
+ <SelectItem value="us">United States</SelectItem>
234
+ <SelectItem value="ca">Canada</SelectItem>
235
+ <SelectItem value="uk">United Kingdom</SelectItem>
236
+ </SelectField>
237
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
238
+ required — asterisk added to label
239
+ </p>
240
+ </div>
241
+ </div>
427
242
 
428
- return (
429
- <div className="max-w-md space-y-4">
430
- <div>
431
- <p className="text-fm-primary mb-2 text-sm">
432
- Selected value:{" "}
433
- <code className="bg-fm-surface-secondary rounded px-1">
434
- {value || "none"}
435
- </code>
243
+ {/* Disabled whole select */}
244
+ <div>
245
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-4 font-medium">
246
+ Disabled
247
+ </h4>
248
+ <div className="space-y-2 text-center">
249
+ <SelectField
250
+ label="Region"
251
+ placeholder="Not available"
252
+ disabled
253
+ helperText="This option is not available"
254
+ decoration="outline"
255
+ fullWidth
256
+ >
257
+ <SelectItem value="r1">Region 1</SelectItem>
258
+ <SelectItem value="r2">Region 2</SelectItem>
259
+ </SelectField>
260
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
261
+ disabled — entire select is non-interactive
436
262
  </p>
263
+ </div>
264
+ </div>
265
+
266
+ {/* Disabled items */}
267
+ <div>
268
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-4 font-medium">
269
+ Disabled Items
270
+ </h4>
271
+ <div className="space-y-2 text-center">
437
272
  <SelectField
438
- label="Controlled Select"
439
- placeholder="Select a fruit"
440
- value={value}
441
- onValueChange={setValue}
442
- helperText="This select is controlled by React state"
273
+ label="Plan"
274
+ placeholder="Choose a plan…"
275
+ helperText="Some plans are not available in your region"
443
276
  decoration="outline"
444
277
  fullWidth
445
278
  >
446
- <SelectItem value="apple">Apple</SelectItem>
447
- <SelectItem value="banana">Banana</SelectItem>
448
- <SelectItem value="orange">Orange</SelectItem>
449
- <SelectItem value="grape">Grape</SelectItem>
450
- <SelectItem value="strawberry">Strawberry</SelectItem>
279
+ <SelectItem value="starter">Starter — Free</SelectItem>
280
+ <SelectItem value="pro">Pro — $9/mo</SelectItem>
281
+ <SelectItem value="enterprise" disabled>
282
+ Enterprise — Contact sales
283
+ </SelectItem>
451
284
  </SelectField>
285
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
286
+ disabled items — individual options unavailable
287
+ </p>
452
288
  </div>
453
- <div className="flex gap-2">
454
- <Button size="sm" onClick={() => setValue("apple")}>
455
- Set to Apple
456
- </Button>
457
- <Button size="sm" variant="outline" onClick={() => setValue("")}>
458
- Clear
459
- </Button>
289
+ </div>
290
+
291
+ {/* Grouped options */}
292
+ <div>
293
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-4 font-medium">
294
+ Grouped Options
295
+ </h4>
296
+ <div className="space-y-2 text-center">
297
+ <SelectField
298
+ label="Technology"
299
+ placeholder="Choose a technology…"
300
+ helperText="Frontend and backend options"
301
+ decoration="outline"
302
+ fullWidth
303
+ >
304
+ <SelectGroup>
305
+ <SelectLabel className="px-2 pt-2 pb-1">Frontend</SelectLabel>
306
+ <SelectItem value="react">React</SelectItem>
307
+ <SelectSeparator />
308
+ <SelectItem value="vue">Vue</SelectItem>
309
+ <SelectSeparator />
310
+ <SelectItem value="svelte">Svelte</SelectItem>
311
+ </SelectGroup>
312
+ <SelectSeparator />
313
+ <SelectGroup>
314
+ <SelectLabel className="px-2 pt-2 pb-1">Backend</SelectLabel>
315
+ <SelectItem value="node">Node.js</SelectItem>
316
+ <SelectSeparator />
317
+ <SelectItem value="python">Python</SelectItem>
318
+ <SelectSeparator />
319
+ <SelectItem value="go">Go</SelectItem>
320
+ </SelectGroup>
321
+ </SelectField>
322
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
323
+ SelectGroup + SelectLabel + SelectSeparator
324
+ </p>
460
325
  </div>
461
326
  </div>
462
- )
463
- },
327
+ </div>
328
+ ),
464
329
  }
465
330
 
466
- export const DisabledStates: Story = {
467
- render: () => (
468
- <div className="max-w-md space-y-6">
469
- <SelectField
470
- label="Disabled Select"
471
- placeholder="This select is disabled"
472
- disabled
473
- helperText="This select cannot be interacted with"
474
- decoration="outline"
475
- fullWidth
476
- >
477
- <SelectItem value="option1">Option 1</SelectItem>
478
- <SelectItem value="option2">Option 2</SelectItem>
479
- </SelectField>
331
+ // ─── 4. States ───────────────────────────────────────────────────────────────
480
332
 
481
- <SelectField
482
- label="Select with Disabled Items"
483
- placeholder="Some items are disabled"
484
- helperText="Some options may be unavailable"
485
- decoration="filled"
486
- fullWidth
487
- >
488
- <SelectItem value="available1">Available Option 1</SelectItem>
489
- <SelectItem value="disabled1" disabled>
490
- Disabled Option 1
491
- </SelectItem>
492
- <SelectItem value="available2">Available Option 2</SelectItem>
493
- <SelectItem value="disabled2" disabled>
494
- Disabled Option 2
495
- </SelectItem>
496
- </SelectField>
497
- </div>
498
- ),
333
+ export const States: Story = {
499
334
  parameters: {
500
335
  docs: {
501
336
  description: {
502
- story: "Examples of disabled select and individual disabled options.",
337
+ story:
338
+ "All five states side by side: default (idle), disabled, error (validation failure), warning (advisory), and success (validated).",
503
339
  },
504
340
  },
505
341
  },
506
- }
507
-
508
- export const WithGroups: Story = {
509
342
  render: () => (
510
- <SelectField
511
- label="Technology Stack"
512
- placeholder="Select a technology"
513
- decoration="outline"
514
- helperText="Choose from frontend or backend technologies"
515
- fullWidth
516
- >
517
- <SelectGroup>
518
- <SelectLabel className="px-2 pt-2 pb-1">Frontend</SelectLabel>
519
- <SelectItem value="react">React</SelectItem>
520
- <SelectSeparator />
521
- <SelectItem value="vue">Vue</SelectItem>
522
- <SelectSeparator />
523
- <SelectItem value="angular">Angular</SelectItem>
524
- </SelectGroup>
525
- <SelectSeparator />
526
- <SelectGroup>
527
- <SelectLabel className="px-2 pt-2 pb-1">Backend</SelectLabel>
528
- <SelectItem value="nodejs">Node.js</SelectItem>
529
- <SelectSeparator />
530
- <SelectItem value="python">Python</SelectItem>
531
- <SelectSeparator />
532
- <SelectItem value="java">Java</SelectItem>
533
- </SelectGroup>
534
- </SelectField>
343
+ <div className="w-full max-w-sm space-y-8">
344
+ {[
345
+ {
346
+ label: "Default",
347
+ props: {
348
+ label: "Default",
349
+ placeholder: "Select an option…",
350
+ variant: "default" as const,
351
+ helperText: "Helper text",
352
+ },
353
+ },
354
+ {
355
+ label: "Disabled",
356
+ props: {
357
+ label: "Disabled",
358
+ placeholder: "Not available",
359
+ variant: "default" as const,
360
+ disabled: true,
361
+ helperText: "This field cannot be changed",
362
+ },
363
+ },
364
+ {
365
+ label: "Error",
366
+ props: {
367
+ label: "Error",
368
+ placeholder: "Select an option…",
369
+ variant: "error" as const,
370
+ helperText: "Please select a valid option",
371
+ },
372
+ },
373
+ {
374
+ label: "Warning",
375
+ props: {
376
+ label: "Warning",
377
+ placeholder: "Select an option…",
378
+ variant: "warning" as const,
379
+ helperText: "Double-check your selection",
380
+ },
381
+ },
382
+ {
383
+ label: "Success",
384
+ props: {
385
+ label: "Success",
386
+ placeholder: "Select an option…",
387
+ variant: "success" as const,
388
+ helperText: "Looks good!",
389
+ },
390
+ },
391
+ ].map(({ label, props }) => (
392
+ <div key={label}>
393
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-3 font-medium">
394
+ {label}
395
+ </h4>
396
+ <SelectField decoration="outline" fullWidth {...props}>
397
+ <SelectItem value="opt1">Option 1</SelectItem>
398
+ <SelectItem value="opt2">Option 2</SelectItem>
399
+ <SelectItem value="opt3">Option 3</SelectItem>
400
+ </SelectField>
401
+ </div>
402
+ ))}
403
+ </div>
535
404
  ),
405
+ }
406
+
407
+ // ─── 5. Interactive ──────────────────────────────────────────────────────────
408
+
409
+ export const Interactive: Story = {
536
410
  parameters: {
537
411
  docs: {
538
412
  description: {
539
- story: `
540
- Select with grouped options using \`SelectGroup\`, \`SelectLabel\`, and \`SelectSeparator\` components.
541
- Useful for organizing related options into categories.
542
- `,
413
+ story:
414
+ "Live controlled select demo. Use the preset buttons to jump between states, or pick values directly from the dropdowns. The variant and helper text update dynamically based on selection.",
543
415
  },
544
416
  },
545
417
  },
546
- }
418
+ render: function InteractiveSelect() {
419
+ const [country, setCountry] = React.useState("")
420
+ const [framework, setFramework] = React.useState("")
421
+ const [decoration, setDecoration] = React.useState<
422
+ "underline" | "outline" | "filled"
423
+ >("outline")
547
424
 
548
- export const FormIntegration: Story = {
549
- render: () => (
550
- <form
551
- className="max-w-md space-y-4 rounded-lg border p-4"
552
- onSubmit={(e) => {
553
- e.preventDefault()
554
- const formData = new FormData(e.currentTarget)
555
- alert(
556
- `Selected values:\nFruit: ${formData.get("fruit")}\nColor: ${formData.get("color")}`
557
- )
558
- }}
559
- >
560
- <SelectField
561
- label="Favorite Fruit"
562
- placeholder="Select a fruit"
563
- name="fruit"
564
- required
565
- helperText="Required field"
566
- decoration="outline"
567
- fullWidth
568
- >
569
- <SelectItem value="apple">Apple</SelectItem>
570
- <SelectItem value="banana">Banana</SelectItem>
571
- <SelectItem value="orange">Orange</SelectItem>
572
- </SelectField>
425
+ const countryVariant = country ? "success" : "default"
426
+ const countryHelper = country
427
+ ? "Country selected successfully"
428
+ : "Please select your country"
573
429
 
574
- <SelectField
575
- label="Favorite Color"
576
- placeholder="Select a color"
577
- name="color"
578
- helperText="Optional field"
579
- decoration="filled"
580
- fullWidth
581
- >
582
- <SelectItem value="red">Red</SelectItem>
583
- <SelectItem value="blue">Blue</SelectItem>
584
- <SelectItem value="green">Green</SelectItem>
585
- </SelectField>
430
+ const presets = [
431
+ { label: "Select country", country: "us", framework: "" },
432
+ { label: "Select both", country: "ca", framework: "react" },
433
+ { label: "Clear all", country: "", framework: "" },
434
+ ]
586
435
 
587
- <Button type="submit" className="w-full">
588
- Submit Form
589
- </Button>
590
- </form>
591
- ),
436
+ return (
437
+ <div className="w-full p-8">
438
+ <div className="mx-auto max-w-3xl space-y-6">
439
+ <div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
440
+ {/* Controls panel */}
441
+ <div className="border-fm-divider-secondary bg-fm-surface-secondary space-y-5 rounded-xl border p-5">
442
+ <p className="text-fm-primary font-fm-brand text-fm-sm leading-fm-sm font-semibold tracking-widest uppercase">
443
+ Presets
444
+ </p>
445
+ <div className="space-y-2">
446
+ {presets.map(({ label, country: c, framework: f }) => (
447
+ <button
448
+ key={label}
449
+ onClick={() => {
450
+ setCountry(c)
451
+ setFramework(f)
452
+ }}
453
+ className="border-fm-divider-secondary text-fm-primary font-fm-text text-fm-sm leading-fm-sm hover:bg-fm-surface-primary w-full rounded-lg border px-3 py-2 text-left transition-colors"
454
+ >
455
+ {label}
456
+ </button>
457
+ ))}
458
+ </div>
459
+ <div className="border-fm-divider-secondary border-t pt-4" />
460
+ <p className="text-fm-primary font-fm-brand text-fm-sm leading-fm-sm font-semibold tracking-widest uppercase">
461
+ Decoration
462
+ </p>
463
+ <div className="space-y-2">
464
+ {(["underline", "outline", "filled"] as const).map((d) => (
465
+ <button
466
+ key={d}
467
+ onClick={() => setDecoration(d)}
468
+ className={`border-fm-divider-secondary text-fm-primary font-fm-text text-fm-sm leading-fm-sm w-full rounded-lg border px-3 py-2 text-left capitalize transition-colors ${decoration === d ? "bg-fm-surface-primary" : "hover:bg-fm-surface-primary"}`}
469
+ >
470
+ {d}
471
+ </button>
472
+ ))}
473
+ </div>
474
+ </div>
475
+
476
+ {/* Preview stage */}
477
+ <div className="flex flex-col gap-4 lg:col-span-2">
478
+ <SelectField
479
+ label="Country"
480
+ placeholder="Select your country…"
481
+ value={country}
482
+ onValueChange={setCountry}
483
+ required
484
+ variant={countryVariant}
485
+ helperText={countryHelper}
486
+ decoration={decoration}
487
+ fullWidth
488
+ >
489
+ <SelectItem value="us">United States</SelectItem>
490
+ <SelectItem value="ca">Canada</SelectItem>
491
+ <SelectItem value="uk">United Kingdom</SelectItem>
492
+ <SelectItem value="de">Germany</SelectItem>
493
+ <SelectItem value="fr">France</SelectItem>
494
+ </SelectField>
495
+
496
+ <SelectField
497
+ label="Framework"
498
+ placeholder="Choose your framework…"
499
+ value={framework}
500
+ onValueChange={setFramework}
501
+ helperText="This will personalise your experience"
502
+ decoration={decoration}
503
+ fullWidth
504
+ >
505
+ <SelectGroup>
506
+ <SelectLabel className="px-2 pt-2 pb-1">Popular</SelectLabel>
507
+ <SelectItem value="react">React</SelectItem>
508
+ <SelectSeparator />
509
+ <SelectItem value="vue">Vue.js</SelectItem>
510
+ <SelectSeparator />
511
+ <SelectItem value="angular">Angular</SelectItem>
512
+ </SelectGroup>
513
+ <SelectSeparator />
514
+ <SelectGroup>
515
+ <SelectLabel className="px-2 pt-2 pb-1">Others</SelectLabel>
516
+ <SelectItem value="svelte">Svelte</SelectItem>
517
+ <SelectSeparator />
518
+ <SelectItem value="solid">SolidJS</SelectItem>
519
+ </SelectGroup>
520
+ </SelectField>
521
+
522
+ <div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border px-4 py-3">
523
+ <code className="text-fm-secondary text-fm-md leading-fm-md font-(--font-fm-mono)">
524
+ {`country="${country || "(none)"}" framework="${framework || "(none)"}"`}
525
+ </code>
526
+ </div>
527
+ </div>
528
+ </div>
529
+ </div>
530
+ </div>
531
+ )
532
+ },
533
+ }
534
+
535
+ // ─── 6. Parts ────────────────────────────────────────────────────────────────
536
+
537
+ export const Parts: Story = {
592
538
  parameters: {
593
539
  docs: {
594
540
  description: {
595
- story: `
596
- Select components integrated with a form. Shows how to use \`name\` and \`required\` props
597
- for form validation and submission. The select values will be included in form data.
598
- `,
541
+ story:
542
+ "Each atomic sub-component shown individually: SelectField (convenience wrapper), SelectRoot + SelectWrapper + Select + SelectTrigger + SelectContent (atomic composition), SelectLabel (Radix label bridged to the design system Label), SelectHelperText, SelectItem with tick indicator, SelectSeparator (Divider-based), and SelectGroup for categorising options.",
599
543
  },
600
544
  },
601
545
  },
602
- }
603
-
604
- export const AtomicComposition: Story = {
605
546
  render: () => {
606
- const [value, setValue] = React.useState("")
547
+ const [atomicValue, setAtomicValue] = React.useState("")
607
548
 
608
549
  return (
609
- <SelectRoot fullWidth className="max-w-md">
610
- <SelectLabel required>Custom Composed Select</SelectLabel>
611
-
612
- <SelectWrapper>
613
- <Select value={value} onValueChange={setValue}>
614
- <SelectTrigger
550
+ <div className="w-full max-w-lg space-y-10">
551
+ {/* SelectField */}
552
+ <div>
553
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-4 font-medium">
554
+ SelectField convenience wrapper
555
+ </h4>
556
+ <div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border p-4">
557
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm mb-3">
558
+ Combines SelectRoot, SelectLabel, SelectWrapper, Select,
559
+ SelectTrigger, SelectContent, and SelectHelperText into a single
560
+ component.
561
+ </p>
562
+ <SelectField
563
+ label="Framework"
564
+ placeholder="Select a framework…"
565
+ helperText="Pick your preferred stack"
615
566
  decoration="outline"
616
- variant="default"
617
- aria-describedby="custom-select-helper"
567
+ fullWidth
618
568
  >
619
- <SelectValue placeholder="Built with atomic components..." />
620
- </SelectTrigger>
569
+ <SelectItem value="react">React</SelectItem>
570
+ <SelectItem value="vue">Vue</SelectItem>
571
+ </SelectField>
572
+ </div>
573
+ </div>
621
574
 
622
- <SelectContent>
623
- <SelectItem value="atomic">Atomic Design</SelectItem>
575
+ {/* Atomic composition */}
576
+ <div>
577
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-4 font-medium">
578
+ Atomic Composition
579
+ </h4>
580
+ <div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border p-4">
581
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm mb-3">
582
+ Build the same result manually using individual primitives for
583
+ full layout control.
584
+ </p>
585
+ <SelectRoot fullWidth>
586
+ <SelectLabel required>Custom Select</SelectLabel>
587
+ <SelectWrapper>
588
+ <Select value={atomicValue} onValueChange={setAtomicValue}>
589
+ <SelectTrigger decoration="outline" variant="default">
590
+ <SelectValue placeholder="Built with atomic parts…" />
591
+ </SelectTrigger>
592
+ <SelectContent>
593
+ <SelectItem value="atomic">Atomic Design</SelectItem>
594
+ <SelectSeparator />
595
+ <SelectItem value="flexible">
596
+ Flexible Composition
597
+ </SelectItem>
598
+ <SelectSeparator />
599
+ <SelectItem value="accessible">
600
+ Accessible by Default
601
+ </SelectItem>
602
+ </SelectContent>
603
+ </Select>
604
+ </SelectWrapper>
605
+ <SelectHelperText variant="default">
606
+ Full control via individual primitives
607
+ </SelectHelperText>
608
+ </SelectRoot>
609
+ </div>
610
+ </div>
611
+
612
+ {/* SelectSeparator */}
613
+ <div>
614
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-4 font-medium">
615
+ SelectSeparator
616
+ </h4>
617
+ <div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border p-4">
618
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm mb-3">
619
+ A Divider-based separator rendered between SelectItems to visually
620
+ group related options.
621
+ </p>
622
+ <SelectField
623
+ label="Fruit"
624
+ placeholder="Choose a fruit…"
625
+ decoration="outline"
626
+ fullWidth
627
+ >
628
+ <SelectItem value="apple">Apple</SelectItem>
624
629
  <SelectSeparator />
625
- <SelectItem value="flexible">Flexible Composition</SelectItem>
630
+ <SelectItem value="banana">Banana</SelectItem>
626
631
  <SelectSeparator />
627
- <SelectItem value="accessible">Accessible</SelectItem>
628
- </SelectContent>
629
- </Select>
630
- </SelectWrapper>
631
-
632
- <SelectHelperText id="custom-select-helper" variant="default">
633
- This is built using atomic components for maximum flexibility
634
- </SelectHelperText>
635
- </SelectRoot>
632
+ <SelectItem value="cherry">Cherry</SelectItem>
633
+ </SelectField>
634
+ </div>
635
+ </div>
636
+ </div>
636
637
  )
637
638
  },
639
+ }
640
+
641
+ // ─── 7. UseCases ─────────────────────────────────────────────────────────────
642
+
643
+ export const UseCases: Story = {
638
644
  parameters: {
639
645
  docs: {
640
646
  description: {
641
647
  story:
642
- "This example shows how to compose the Select using individual atomic components for maximum flexibility. This pattern allows for custom layouts and advanced use cases.",
648
+ "Real product-shaped examples: a user profile form with country and language selection, a subscription plan picker with disabled options, and a dynamic filter with grouped content categories.",
643
649
  },
644
650
  },
645
651
  },
646
- }
652
+ render: function UseCasesSelect() {
653
+ const [country, setCountry] = React.useState("")
654
+ const [language, setLanguage] = React.useState("")
655
+ const [plan, setPlan] = React.useState("")
656
+ const [category, setCategory] = React.useState("")
647
657
 
648
- export const AllVariantsAllDecorations: Story = {
649
- render: () => (
650
- <div className="grid max-w-6xl grid-cols-1 gap-6 lg:grid-cols-3">
651
- {/* Underline decoration */}
652
- <div className="space-y-4">
653
- <h3 className="text-fm-primary text-lg font-semibold">Underline</h3>
654
- <SelectField
655
- label="Default"
656
- placeholder="Default underline..."
657
- variant="default"
658
- decoration="underline"
659
- helperText="Default variant"
660
- fullWidth
661
- >
662
- <SelectItem value="option1">Option 1</SelectItem>
663
- <SelectItem value="option2">Option 2</SelectItem>
664
- </SelectField>
665
- <SelectField
666
- label="Error"
667
- placeholder="Error underline..."
668
- variant="error"
669
- decoration="underline"
670
- helperText="Something went wrong"
671
- fullWidth
672
- >
673
- <SelectItem value="option1">Option 1</SelectItem>
674
- <SelectItem value="option2">Option 2</SelectItem>
675
- </SelectField>
676
- <SelectField
677
- label="Warning"
678
- placeholder="Warning underline..."
679
- variant="warning"
680
- decoration="underline"
681
- helperText="Please check selection"
682
- fullWidth
683
- >
684
- <SelectItem value="option1">Option 1</SelectItem>
685
- <SelectItem value="option2">Option 2</SelectItem>
686
- </SelectField>
687
- <SelectField
688
- label="Success"
689
- placeholder="Success underline..."
690
- variant="success"
691
- decoration="underline"
692
- helperText="Great choice!"
693
- fullWidth
694
- >
695
- <SelectItem value="option1">Option 1</SelectItem>
696
- <SelectItem value="option2">Option 2</SelectItem>
697
- </SelectField>
698
- </div>
658
+ return (
659
+ <div className="mx-auto max-w-3xl space-y-8 p-8">
660
+ {/* Profile settings */}
661
+ <div className="space-y-4">
662
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
663
+ Profile Settings
664
+ </h4>
665
+ <div className="border-fm-divider-secondary bg-fm-surface-secondary max-w-md space-y-4 rounded-xl border p-5">
666
+ <SelectField
667
+ label="Country"
668
+ placeholder="Select your country…"
669
+ value={country}
670
+ onValueChange={setCountry}
671
+ required
672
+ variant={country ? "success" : "default"}
673
+ helperText={
674
+ country ? "Country saved" : "Used for regional content"
675
+ }
676
+ decoration="outline"
677
+ fullWidth
678
+ >
679
+ <SelectItem value="us">United States</SelectItem>
680
+ <SelectItem value="ca">Canada</SelectItem>
681
+ <SelectItem value="uk">United Kingdom</SelectItem>
682
+ <SelectItem value="de">Germany</SelectItem>
683
+ <SelectItem value="fr">France</SelectItem>
684
+ <SelectItem value="au">Australia</SelectItem>
685
+ </SelectField>
686
+
687
+ <SelectField
688
+ label="Language"
689
+ placeholder="Select your language…"
690
+ value={language}
691
+ onValueChange={setLanguage}
692
+ helperText="Preferred display language"
693
+ decoration="outline"
694
+ fullWidth
695
+ >
696
+ <SelectItem value="en">English</SelectItem>
697
+ <SelectItem value="de">Deutsch</SelectItem>
698
+ <SelectItem value="fr">Français</SelectItem>
699
+ <SelectItem value="es">Español</SelectItem>
700
+ <SelectItem value="ja">日本語</SelectItem>
701
+ </SelectField>
702
+ </div>
703
+ </div>
699
704
 
700
- {/* Outline decoration */}
701
- <div className="space-y-4">
702
- <h3 className="text-fm-primary text-lg font-semibold">Outline</h3>
703
- <SelectField
704
- label="Default"
705
- placeholder="Default outline..."
706
- variant="default"
707
- decoration="outline"
708
- helperText="Default variant"
709
- fullWidth
710
- >
711
- <SelectItem value="option1">Option 1</SelectItem>
712
- <SelectItem value="option2">Option 2</SelectItem>
713
- </SelectField>
714
- <SelectField
715
- label="Error"
716
- placeholder="Error outline..."
717
- variant="error"
718
- decoration="outline"
719
- helperText="Something went wrong"
720
- fullWidth
721
- >
722
- <SelectItem value="option1">Option 1</SelectItem>
723
- <SelectItem value="option2">Option 2</SelectItem>
724
- </SelectField>
725
- <SelectField
726
- label="Warning"
727
- placeholder="Warning outline..."
728
- variant="warning"
729
- decoration="outline"
730
- helperText="Please check selection"
731
- fullWidth
732
- >
733
- <SelectItem value="option1">Option 1</SelectItem>
734
- <SelectItem value="option2">Option 2</SelectItem>
735
- </SelectField>
736
- <SelectField
737
- label="Success"
738
- placeholder="Success outline..."
739
- variant="success"
740
- decoration="outline"
741
- helperText="Great choice!"
742
- fullWidth
743
- >
744
- <SelectItem value="option1">Option 1</SelectItem>
745
- <SelectItem value="option2">Option 2</SelectItem>
746
- </SelectField>
747
- </div>
705
+ {/* Subscription plan picker */}
706
+ <div className="space-y-4">
707
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
708
+ Subscription Plan
709
+ </h4>
710
+ <div className="border-fm-divider-secondary bg-fm-surface-secondary max-w-md rounded-xl border p-5">
711
+ <SelectField
712
+ label="Plan"
713
+ placeholder="Choose a plan…"
714
+ value={plan}
715
+ onValueChange={setPlan}
716
+ required
717
+ variant={plan ? "success" : "default"}
718
+ helperText={
719
+ plan === "enterprise"
720
+ ? "Contact sales to complete upgrade"
721
+ : "You can change your plan at any time"
722
+ }
723
+ decoration="filled"
724
+ fullWidth
725
+ >
726
+ <SelectItem value="starter">Starter — Free forever</SelectItem>
727
+ <SelectSeparator />
728
+ <SelectItem value="pro">Pro — $9 / month</SelectItem>
729
+ <SelectSeparator />
730
+ <SelectItem value="team">Team — $29 / month</SelectItem>
731
+ <SelectSeparator />
732
+ <SelectItem value="enterprise" disabled>
733
+ Enterprise — Contact sales
734
+ </SelectItem>
735
+ </SelectField>
736
+ </div>
737
+ </div>
748
738
 
749
- {/* Filled decoration */}
750
- <div className="space-y-4">
751
- <h3 className="text-fm-primary text-lg font-semibold">Filled</h3>
752
- <SelectField
753
- label="Default"
754
- placeholder="Default filled..."
755
- variant="default"
756
- decoration="filled"
757
- helperText="Default variant"
758
- fullWidth
759
- >
760
- <SelectItem value="option1">Option 1</SelectItem>
761
- <SelectItem value="option2">Option 2</SelectItem>
762
- </SelectField>
763
- <SelectField
764
- label="Error"
765
- placeholder="Error filled..."
766
- variant="error"
767
- decoration="filled"
768
- helperText="Something went wrong"
769
- fullWidth
770
- >
771
- <SelectItem value="option1">Option 1</SelectItem>
772
- <SelectItem value="option2">Option 2</SelectItem>
773
- </SelectField>
774
- <SelectField
775
- label="Warning"
776
- placeholder="Warning filled..."
777
- variant="warning"
778
- decoration="filled"
779
- helperText="Please check selection"
780
- fullWidth
781
- >
782
- <SelectItem value="option1">Option 1</SelectItem>
783
- <SelectItem value="option2">Option 2</SelectItem>
784
- </SelectField>
785
- <SelectField
786
- label="Success"
787
- placeholder="Success filled..."
788
- variant="success"
789
- decoration="filled"
790
- helperText="Great choice!"
791
- fullWidth
792
- >
793
- <SelectItem value="option1">Option 1</SelectItem>
794
- <SelectItem value="option2">Option 2</SelectItem>
795
- </SelectField>
739
+ {/* Content filter */}
740
+ <div className="space-y-4">
741
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
742
+ Content Filter
743
+ </h4>
744
+ <div className="border-fm-divider-secondary bg-fm-surface-secondary max-w-md rounded-xl border p-5">
745
+ <SelectField
746
+ label="Category"
747
+ placeholder="Browse by category…"
748
+ value={category}
749
+ onValueChange={setCategory}
750
+ helperText="Filter the podcast library by topic"
751
+ decoration="underline"
752
+ fullWidth
753
+ >
754
+ <SelectGroup>
755
+ <SelectLabel className="px-2 pt-2 pb-1">
756
+ News & Culture
757
+ </SelectLabel>
758
+ <SelectItem value="news">News</SelectItem>
759
+ <SelectSeparator />
760
+ <SelectItem value="politics">Politics</SelectItem>
761
+ <SelectSeparator />
762
+ <SelectItem value="history">History</SelectItem>
763
+ </SelectGroup>
764
+ <SelectSeparator />
765
+ <SelectGroup>
766
+ <SelectLabel className="px-2 pt-2 pb-1">
767
+ Science & Tech
768
+ </SelectLabel>
769
+ <SelectItem value="science">Science</SelectItem>
770
+ <SelectSeparator />
771
+ <SelectItem value="technology">Technology</SelectItem>
772
+ <SelectSeparator />
773
+ <SelectItem value="ai">Artificial Intelligence</SelectItem>
774
+ </SelectGroup>
775
+ <SelectSeparator />
776
+ <SelectGroup>
777
+ <SelectLabel className="px-2 pt-2 pb-1">Lifestyle</SelectLabel>
778
+ <SelectItem value="health">Health & Fitness</SelectItem>
779
+ <SelectSeparator />
780
+ <SelectItem value="comedy">Comedy</SelectItem>
781
+ <SelectSeparator />
782
+ <SelectItem value="true-crime">True Crime</SelectItem>
783
+ </SelectGroup>
784
+ </SelectField>
785
+ </div>
786
+ </div>
796
787
  </div>
797
- </div>
798
- ),
788
+ )
789
+ },
790
+ }
791
+
792
+ // ─── 8. Accessibility ────────────────────────────────────────────────────────
793
+
794
+ export const Accessibility: Story = {
799
795
  parameters: {
800
796
  docs: {
801
797
  description: {
802
798
  story:
803
- "Complete showcase of all visual variants across all decoration styles.",
799
+ "Accessibility-focused examples demonstrating ARIA attributes, required field announcements, error state with aria-invalid, and keyboard navigation support. The select is fully operable via keyboard: Space/Enter opens, arrow keys move through options, Escape closes.",
804
800
  },
805
801
  },
806
802
  },
807
- }
808
-
809
- export const ComplexFormExample: Story = {
810
- render: () => {
811
- const [country, setCountry] = React.useState("")
812
- const [framework, setFramework] = React.useState("")
813
-
814
- return (
815
- <div className="max-w-lg space-y-6">
803
+ render: () => (
804
+ <div className="w-full max-w-lg space-y-8">
805
+ {/* Required + aria-required */}
806
+ <div>
807
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-3 font-medium">
808
+ Required Field
809
+ </h4>
816
810
  <SelectField
817
811
  label="Country"
818
- placeholder="Select your country"
819
- value={country}
820
- onValueChange={setCountry}
812
+ placeholder="Select your country"
821
813
  required
822
- helperText={
823
- country
824
- ? "Country selected successfully"
825
- : "Please select your country"
826
- }
827
- variant={country ? "success" : "default"}
814
+ helperText="Required — used for regional settings"
828
815
  decoration="outline"
829
816
  fullWidth
830
817
  >
831
818
  <SelectItem value="us">United States</SelectItem>
832
819
  <SelectItem value="ca">Canada</SelectItem>
833
820
  <SelectItem value="uk">United Kingdom</SelectItem>
834
- <SelectItem value="de">Germany</SelectItem>
835
- <SelectItem value="fr">France</SelectItem>
836
821
  </SelectField>
822
+ </div>
837
823
 
824
+ {/* Error + aria-invalid */}
825
+ <div>
826
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-3 font-medium">
827
+ Error State (aria-invalid)
828
+ </h4>
838
829
  <SelectField
839
- label="Preferred Framework"
840
- placeholder="Choose your framework"
841
- value={framework}
842
- onValueChange={setFramework}
843
- helperText="This will help us customize your experience"
844
- decoration="filled"
830
+ label="Plan"
831
+ placeholder="Select a plan…"
832
+ variant="error"
833
+ helperText="Please select a plan to continue"
834
+ decoration="outline"
845
835
  fullWidth
846
836
  >
847
- <SelectGroup>
848
- <SelectLabel className="px-2 pt-2 pb-1">Popular</SelectLabel>
849
- <SelectItem value="react">React</SelectItem>
850
- <SelectSeparator />
851
- <SelectItem value="vue">Vue.js</SelectItem>
852
- <SelectSeparator />
853
- <SelectItem value="angular">Angular</SelectItem>
854
- </SelectGroup>
855
- <SelectSeparator />
856
- <SelectGroup>
857
- <SelectLabel className="px-2 pt-2 pb-1">Others</SelectLabel>
858
- <SelectItem value="svelte">Svelte</SelectItem>
859
- <SelectSeparator />
860
- <SelectItem value="solid">SolidJS</SelectItem>
861
- </SelectGroup>
837
+ <SelectItem value="starter">Starter</SelectItem>
838
+ <SelectItem value="pro">Pro</SelectItem>
862
839
  </SelectField>
863
840
  </div>
864
- )
865
- },
866
- parameters: {
867
- docs: {
868
- description: {
869
- story:
870
- "An example showing multiple SelectField components in a form with different configurations, decorations, and dynamic states based on user input.",
871
- },
872
- },
873
- },
874
- }
875
841
 
876
- // Legacy stories for backward compatibility with the old API
877
- export const LegacyDefault: Story = {
878
- parameters: {
879
- docs: {
880
- description: {
881
- story:
882
- "Legacy API example using the atomic Select components directly (for backward compatibility).",
883
- },
884
- },
885
- },
886
- render: () => (
887
- <Select>
888
- <SelectTrigger
889
- className="w-[180px]"
890
- variant="default"
891
- decoration="underline"
892
- >
893
- <SelectValue placeholder="Select a fruit" />
894
- </SelectTrigger>
895
- <SelectContent>
896
- <SelectItem value="apple">Apple</SelectItem>
897
- <SelectSeparator />
898
- <SelectItem value="banana">Banana</SelectItem>
899
- <SelectSeparator />
900
- <SelectItem value="orange">Orange</SelectItem>
901
- <SelectSeparator />
902
- <SelectItem value="grape">Grape</SelectItem>
903
- <SelectSeparator />
904
- <SelectItem value="strawberry">Strawberry</SelectItem>
905
- </SelectContent>
906
- </Select>
907
- ),
908
- }
909
-
910
- export const LongList: Story = {
911
- render: () => (
912
- <SelectField
913
- label="Country Selection"
914
- placeholder="Select a country"
915
- helperText="Scroll to see more options"
916
- decoration="outline"
917
- fullWidth
918
- >
919
- <SelectItem value="us">United States</SelectItem>
920
- <SelectSeparator />
921
- <SelectItem value="ca">Canada</SelectItem>
922
- <SelectSeparator />
923
- <SelectItem value="uk">United Kingdom</SelectItem>
924
- <SelectSeparator />
925
- <SelectItem value="fr">France</SelectItem>
926
- <SelectSeparator />
927
- <SelectItem value="de">Germany</SelectItem>
928
- <SelectSeparator />
929
- <SelectItem value="it">Italy</SelectItem>
930
- <SelectSeparator />
931
- <SelectItem value="es">Spain</SelectItem>
932
- <SelectSeparator />
933
- <SelectItem value="au">Australia</SelectItem>
934
- <SelectSeparator />
935
- <SelectItem value="jp">Japan</SelectItem>
936
- <SelectSeparator />
937
- <SelectItem value="kr">South Korea</SelectItem>
938
- <SelectSeparator />
939
- <SelectItem value="in">India</SelectItem>
940
- <SelectSeparator />
941
- <SelectItem value="br">Brazil</SelectItem>
942
- <SelectSeparator />
943
- <SelectItem value="mx">Mexico</SelectItem>
944
- <SelectSeparator />
945
- <SelectItem value="ar">Argentina</SelectItem>
946
- <SelectSeparator />
947
- <SelectItem value="cl">Chile</SelectItem>
948
- <SelectSeparator />
949
- <SelectItem value="pe">Peru</SelectItem>
950
- <SelectSeparator />
951
- <SelectItem value="co">Colombia</SelectItem>
952
- <SelectSeparator />
953
- <SelectItem value="ve">Venezuela</SelectItem>
954
- <SelectSeparator />
955
- <SelectItem value="ec">Ecuador</SelectItem>
956
- <SelectSeparator />
957
- <SelectItem value="uy">Uruguay</SelectItem>
958
- </SelectField>
842
+ {/* Keyboard navigation info */}
843
+ <div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border p-4">
844
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm mb-2 font-medium">
845
+ Keyboard Navigation
846
+ </p>
847
+ <ul className="space-y-1">
848
+ {[
849
+ "Space / Enter — opens the dropdown",
850
+ "Arrow Up / Arrow Down — moves between options",
851
+ "Home / End — jumps to first or last option",
852
+ "Escape closes the dropdown without selection",
853
+ "Tab — moves focus to the next focusable element",
854
+ ].map((item) => (
855
+ <li
856
+ key={item}
857
+ className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm"
858
+ >
859
+ {item}
860
+ </li>
861
+ ))}
862
+ </ul>
863
+ </div>
864
+ </div>
959
865
  ),
960
- parameters: {
961
- docs: {
962
- description: {
963
- story: `
964
- Select with many options that demonstrates scrolling behavior.
965
- The content area becomes scrollable when it exceeds the maximum height,
966
- and scroll buttons appear automatically at the top and bottom.
967
- `,
968
- },
969
- },
970
- },
971
866
  }