aural-ui 4.0.1 → 4.1.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 (174) hide show
  1. package/dist/components/aspect-ratio/AspectRatio.stories.tsx +290 -1228
  2. package/dist/components/avatar/Avatar.stories.tsx +219 -235
  3. package/dist/components/badge/Badge.stories.tsx +379 -116
  4. package/dist/components/banner/Banner.stories.tsx +445 -391
  5. package/dist/components/breadcrumb/Breadcrumb.stories.tsx +453 -199
  6. package/dist/components/button/Button.stories.tsx +585 -230
  7. package/dist/components/card/Card.stories.tsx +619 -301
  8. package/dist/components/char-count/CharCount.stories.tsx +350 -248
  9. package/dist/components/checkbox/Checkbox.stories.tsx +309 -167
  10. package/dist/components/chip/Chip.stories.tsx +362 -168
  11. package/dist/components/circular-loader/CircularLoader.stories.tsx +221 -636
  12. package/dist/components/clamp-lines/ClampLines.stories.tsx +246 -117
  13. package/dist/components/collapsible/Collapsible.stories.tsx +391 -252
  14. package/dist/components/command/Command.stories.tsx +530 -867
  15. package/dist/components/dialog/Dialog.stories.tsx +501 -950
  16. package/dist/components/divider/Divider.stories.tsx +264 -527
  17. package/dist/components/dot-loader/DotLoader.stories.tsx +256 -257
  18. package/dist/components/drawer/Drawer.stories.tsx +659 -1023
  19. package/dist/components/dropdown/Dropdown.stories.tsx +643 -1028
  20. package/dist/components/form/Form.stories.tsx +560 -274
  21. package/dist/components/helper-text/HelperText.stories.tsx +199 -200
  22. package/dist/components/hover-card/HoverCard.stories.tsx +318 -1254
  23. package/dist/components/icon-button/IconButton.stories.tsx +837 -194
  24. package/dist/components/if-else/if-else.stories.tsx +370 -83
  25. package/dist/components/input/Input.stories.tsx +436 -368
  26. package/dist/components/label/Label.stories.tsx +156 -154
  27. package/dist/components/list/List.stories.tsx +484 -835
  28. package/dist/components/marquee/Marquee.stories.tsx +356 -712
  29. package/dist/components/otp-inputs/OtpInputs.stories.tsx +352 -422
  30. package/dist/components/overlay/Overlay.stories.tsx +452 -824
  31. package/dist/components/pagination/Pagination.stories.tsx +721 -210
  32. package/dist/components/popover/Popover.stories.tsx +481 -896
  33. package/dist/components/radio/Radio.stories.tsx +432 -124
  34. package/dist/components/resizable/Resizable.stories.tsx +495 -799
  35. package/dist/components/scroll-area/ScrollArea.stories.tsx +383 -1059
  36. package/dist/components/search/Search.stories.tsx +312 -595
  37. package/dist/components/select/Select.stories.tsx +684 -789
  38. package/dist/components/sheet/Sheet.stories.tsx +671 -950
  39. package/dist/components/skelton/Skelton.stories.tsx +230 -764
  40. package/dist/components/slider/Slider.stories.tsx +383 -760
  41. package/dist/components/stepper/Stepper.stories.tsx +371 -514
  42. package/dist/components/switch/Switch.stories.tsx +461 -208
  43. package/dist/components/switch-case/SwitchCase.stories.tsx +367 -188
  44. package/dist/components/table/Table.stories.tsx +770 -916
  45. package/dist/components/tabs/Tabs.stories.tsx +458 -1455
  46. package/dist/components/tag/Tag.stories.tsx +714 -542
  47. package/dist/components/textarea/TextArea.stories.tsx +621 -562
  48. package/dist/components/thumbnail-tags/ThumbnailTags.stories.tsx +228 -154
  49. package/dist/components/toast/Toast.stories.tsx +452 -1339
  50. package/dist/components/toggle/Toggle.stories.tsx +488 -931
  51. package/dist/components/tooltip/Tooltip.stories.tsx +344 -1388
  52. package/dist/components/typography/Typography.stories.tsx +406 -89
  53. package/dist/hooks/use-change-state/UseChangeState.stories.tsx +309 -606
  54. package/dist/hooks/use-previous/UsePrevious.stories.tsx +367 -917
  55. package/dist/hooks/use-standalone-pagination/UseStandalonePagination.stories.tsx +639 -867
  56. package/dist/icons/Icons.stories.tsx +0 -12
  57. package/dist/icons/ai-avatar-icon/AiAvatarIcon.stories.tsx +223 -1060
  58. package/dist/icons/alert-icon/AlertIcon.stories.tsx +106 -968
  59. package/dist/icons/all-icons.tsx +37 -16
  60. package/dist/icons/angle-down-icon/AngleDownIcon.stories.tsx +137 -1010
  61. package/dist/icons/apple-logo-icon/AppleLogoIcon.stories.tsx +145 -935
  62. package/dist/icons/arrow-box-left-icon/ArrowBoxLeftIcon.stories.tsx +132 -1046
  63. package/dist/icons/arrow-corner-up-left-icon/ArrowCornerUpLeftIcon.stories.tsx +134 -986
  64. package/dist/icons/arrow-corner-up-right-icon/ArrowCornerUpRightIcon.stories.tsx +135 -1028
  65. package/dist/icons/arrow-left-icon/ArrowLeftIcon.stories.tsx +133 -971
  66. package/dist/icons/arrow-right-icon/ArrowRightIcon.stories.tsx +145 -1123
  67. package/dist/icons/arrow-right-up-icon/ArrowRightUpIcon.stories.tsx +143 -1252
  68. package/dist/icons/art-board-icon/ArtBoardIcon.stories.tsx +123 -632
  69. package/dist/icons/audio-bar-icon/AudioBarIcon.stories.tsx +141 -1223
  70. package/dist/icons/backward-ten-seconds-icon/BackwardTenSecondsIcon.stories.tsx +164 -1018
  71. package/dist/icons/bubble-check-icon/BubbleCheckIcon.stories.tsx +121 -1236
  72. package/dist/icons/bubble-crossed-icon/BubbleCrossedIcon.stories.tsx +121 -1213
  73. package/dist/icons/bubble-sparkle-icon/BubbleSparkleIcon.stories.tsx +116 -893
  74. package/dist/icons/camera-icon/CameraIcon.stories.tsx +109 -1254
  75. package/dist/icons/capital-a-letter-icon/CapitalALetterIcon.stories.tsx +114 -975
  76. package/dist/icons/chevron-double-left-icon/ChevronDoubleLeftIcon.stories.tsx +157 -994
  77. package/dist/icons/chevron-double-right-icon/ChevronDoubleRightIcon.stories.tsx +160 -992
  78. package/dist/icons/chevron-down-icon/ChevronDownIcon.stories.tsx +140 -970
  79. package/dist/icons/chevron-left-icon/ChevronLeftIcon.stories.tsx +126 -993
  80. package/dist/icons/chevron-right-icon/ChevronRightIcon.stories.tsx +144 -987
  81. package/dist/icons/chevron-up-icon/ChevronUpIcon.stories.tsx +141 -1007
  82. package/dist/icons/circle-tick-icon/CircleTickIcon.stories.tsx +147 -1187
  83. package/dist/icons/circular-play-icon/CircularPlayIcon.stories.tsx +110 -476
  84. package/dist/icons/coin-icon/CoinIcon.stories.tsx +120 -1364
  85. package/dist/icons/coin-toons-icon/CoinToonsIcon.stories.tsx +113 -1360
  86. package/dist/icons/column-wide-add-icon/ColumnWideAddIcon.stories.tsx +111 -942
  87. package/dist/icons/command-icon/CommandIcon.stories.tsx +124 -1087
  88. package/dist/icons/copy-icon/CopyIcon.stories.tsx +119 -996
  89. package/dist/icons/cross-circle-icon/CrossCircleIcon.stories.tsx +144 -1046
  90. package/dist/icons/cross-icon/CrossIcon.stories.tsx +136 -999
  91. package/dist/icons/download-icon/DownloadIcon.stories.tsx +123 -857
  92. package/dist/icons/edit-big-icon/EditBigIcon.stories.tsx +121 -1080
  93. package/dist/icons/email-icon/EmailIcon.stories.tsx +112 -979
  94. package/dist/icons/expand-icon/ExpandIcon.stories.tsx +109 -1146
  95. package/dist/icons/eye-close-icon/EyeCloseIcon.stories.tsx +141 -1068
  96. package/dist/icons/eye-open-icon/EyeOpenIcon.stories.tsx +140 -1081
  97. package/dist/icons/feature-shine-icon/FeatureShineIcon.stories.tsx +124 -1050
  98. package/dist/icons/file-chart-icon/FileChartIcon.stories.tsx +123 -1091
  99. package/dist/icons/file-text-icon/FileTextIcon.stories.tsx +122 -633
  100. package/dist/icons/filter-bar-row-icon/FilterBarRowIcon.stories.tsx +116 -1087
  101. package/dist/icons/forward-ten-seconds-icon/ForwardTenSecondsIcon.stories.tsx +166 -1020
  102. package/dist/icons/git-branch-icon/GitBranchIcon.stories.tsx +112 -1182
  103. package/dist/icons/git-fork-icon/GitForkIcon.stories.tsx +112 -1155
  104. package/dist/icons/globe-icon/GlobeIcon.stories.tsx +127 -325
  105. package/dist/icons/google-logo-icon/GoogleLogoIcon.stories.tsx +142 -985
  106. package/dist/icons/grip-vertical-icon/GripVerticalIcon.stories.tsx +116 -1217
  107. package/dist/icons/head-icon/HeadIcon.stories.tsx +108 -953
  108. package/dist/icons/heart-icon/HeartIcon.stories.tsx +117 -1060
  109. package/dist/icons/image-avatar-sparkle-icon/ImageAvatarSparkleIcon.stories.tsx +116 -716
  110. package/dist/icons/image-icon/ImageIcon.stories.tsx +102 -1164
  111. package/dist/icons/import-folder-icon/ImportFolderIcon.stories.tsx +108 -1233
  112. package/dist/icons/import-left-arrow-folder-icon/ImportLeftArrowFolderIcon.stories.tsx +133 -1289
  113. package/dist/icons/indian-flag-icon/IndianFlagIcon.stories.tsx +155 -1012
  114. package/dist/icons/instagram-icon/InstagramIcon.stories.tsx +158 -1438
  115. package/dist/icons/layout-column-icon/LayoutColumnIcon.stories.tsx +121 -1011
  116. package/dist/icons/layout-left-icon/LayoutLeftIcon.stories.tsx +116 -981
  117. package/dist/icons/layout-right-icon/LayoutRightIcon.stories.tsx +116 -979
  118. package/dist/icons/light-bulb-simple-icon/LightBulbSimpleIcon.stories.tsx +105 -1252
  119. package/dist/icons/linked-in-icon/LinkedInIcon.stories.tsx +151 -1554
  120. package/dist/icons/magic-book-icon/MagicBookIcon.stories.tsx +107 -1227
  121. package/dist/icons/magic-edit-icon/MagicEditIcon.stories.tsx +116 -707
  122. package/dist/icons/maintenance-icon/MaintenanceIcon.stories.tsx +119 -1226
  123. package/dist/icons/message-icon/MessageIcon.stories.tsx +111 -557
  124. package/dist/icons/minimize-icon/MinimizeIcon.stories.tsx +112 -1198
  125. package/dist/icons/moon-icon/MoonIcon.stories.tsx +117 -557
  126. package/dist/icons/move-horizontal-icon/MoveHorizontalIcon.stories.tsx +106 -1235
  127. package/dist/icons/move-vertical-icon/MoveVerticalIcon.stories.tsx +112 -1185
  128. package/dist/icons/musical-note-icon/MusicalNoteIcon.stories.tsx +116 -1012
  129. package/dist/icons/notepad-icon/NotepadIcon.stories.tsx +108 -1137
  130. package/dist/icons/notes-icon/NotesIcon.stories.tsx +116 -1138
  131. package/dist/icons/page-search-icon/PageSearchIcon.stories.tsx +106 -1146
  132. package/dist/icons/page-text-icon/PageTextIcon.stories.tsx +119 -719
  133. package/dist/icons/paint-roll-icon/PaintRollIcon.stories.tsx +110 -999
  134. package/dist/icons/paper-plane-icon/PaperPlaneIcon.stories.tsx +109 -912
  135. package/dist/icons/pause-icon/PauseIcon.stories.tsx +110 -1041
  136. package/dist/icons/pencil-icon/PencilIcon.stories.tsx +112 -1109
  137. package/dist/icons/phone-icon/PhoneIcon.stories.tsx +112 -1023
  138. package/dist/icons/plus-icon/PlusIcon.stories.tsx +103 -1132
  139. package/dist/icons/pocket-studio-icon/PocketStudioIcon.stories.tsx +104 -870
  140. package/dist/icons/scroll-down-icon/ScrollDownIcon.stories.tsx +99 -476
  141. package/dist/icons/search-icon/SearchIcon.stories.tsx +108 -1161
  142. package/dist/icons/setting-icon/SettingIcon.stories.tsx +104 -1009
  143. package/dist/icons/share-icon/ShareIcon.stories.tsx +117 -1064
  144. package/dist/icons/shield-icon/ShieldIcon.stories.tsx +114 -974
  145. package/dist/icons/site-logo-icon/SiteLogoIcon.stories.tsx +134 -1160
  146. package/dist/icons/skip-backward-icon/SkipBackwardIcon.stories.tsx +169 -1017
  147. package/dist/icons/skip-forward-icon/SkipForwardIcon.stories.tsx +161 -1016
  148. package/dist/icons/sparkles-soft-icon/SparklesSoftIcon.stories.tsx +102 -1001
  149. package/dist/icons/spinner-gradient-icon/SpinnerGradientIcon.stories.tsx +155 -593
  150. package/dist/icons/spinner-solid-icon/SpinnerSolidIcon.stories.tsx +155 -608
  151. package/dist/icons/spinner-solid-neutral-icon/SpinnerSolidINeutralcon.stories.tsx +142 -712
  152. package/dist/icons/star-icon/StarIcon.stories.tsx +120 -946
  153. package/dist/icons/store-coin-icon/StoreCoinIcon.stories.tsx +109 -1013
  154. package/dist/icons/suggestion-icon/SuggestionIcon.stories.tsx +113 -891
  155. package/dist/icons/sun-icon/SunIcon.stories.tsx +117 -864
  156. package/dist/icons/text-color-icon/TextColorIcon.stories.tsx +113 -989
  157. package/dist/icons/text-indicator-icon/TextIndicatorIcon.stories.tsx +120 -1027
  158. package/dist/icons/threads-icon/ThreadsIcon.stories.tsx +153 -1476
  159. package/dist/icons/tick-circle-icon/TickCircleIcon.stories.tsx +143 -1187
  160. package/dist/icons/tick-icon/TickIcon.stories.tsx +142 -1322
  161. package/dist/icons/trash-icon/TrashIcon.stories.tsx +105 -970
  162. package/dist/icons/twitter-x-icon/TwitterXIcon.stories.tsx +154 -1457
  163. package/dist/icons/upload-icon/UploadIcon.stories.tsx +112 -930
  164. package/dist/icons/vertical-menu-icon/VerticalMenuIcon.stories.tsx +115 -1019
  165. package/dist/icons/video-play-list-icon/VideoPlaylistIcon.stories.tsx +122 -1092
  166. package/dist/icons/voice-playing-icon/VoicePlayingIcon.stories.tsx +120 -1401
  167. package/dist/icons/volume-full-icon/VolumeFullIcon.stories.tsx +107 -1212
  168. package/dist/icons/volume-half-icon/VolumeHalfIcon.stories.tsx +109 -1122
  169. package/dist/icons/volume-off-icon/VolumeOffIcon.stories.tsx +112 -1124
  170. package/dist/icons/warning-icon/WarningIcon.stories.tsx +119 -1083
  171. package/dist/icons/youtube-icon/YoutubeIcon.stories.tsx +158 -983
  172. package/dist/index.cjs +1 -1
  173. package/dist/index.js +1 -1
  174. package/package.json +1 -1
@@ -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
  }