aural-ui 3.0.7 → 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 (183) hide show
  1. package/dist/components/aspect-ratio/AspectRatio.stories.tsx +290 -1199
  2. package/dist/components/avatar/Avatar.stories.tsx +235 -237
  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/button/index.tsx +7 -7
  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 -620
  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 +533 -856
  16. package/dist/components/dialog/Dialog.stories.tsx +505 -949
  17. package/dist/components/divider/Divider.stories.tsx +265 -502
  18. package/dist/components/dot-loader/DotLoader.stories.tsx +256 -257
  19. package/dist/components/drawer/Drawer.stories.tsx +659 -993
  20. package/dist/components/drawer/index.tsx +3 -3
  21. package/dist/components/dropdown/Dropdown.stories.tsx +643 -1018
  22. package/dist/components/form/Form.stories.tsx +560 -274
  23. package/dist/components/helper-text/HelperText.stories.tsx +199 -200
  24. package/dist/components/hover-card/HoverCard.stories.tsx +318 -1221
  25. package/dist/components/icon-button/IconButton.stories.tsx +837 -194
  26. package/dist/components/if-else/if-else.stories.tsx +370 -83
  27. package/dist/components/input/Input.stories.tsx +436 -368
  28. package/dist/components/label/Label.stories.tsx +156 -154
  29. package/dist/components/list/List.stories.tsx +485 -822
  30. package/dist/components/marquee/Marquee.stories.tsx +356 -694
  31. package/dist/components/otp-inputs/OtpInputs.stories.tsx +352 -410
  32. package/dist/components/overlay/Overlay.stories.tsx +452 -818
  33. package/dist/components/overlay/index.tsx +4 -4
  34. package/dist/components/pagination/Pagination.stories.tsx +721 -210
  35. package/dist/components/popover/Popover.stories.tsx +484 -873
  36. package/dist/components/radio/Radio.stories.tsx +432 -124
  37. package/dist/components/resizable/Resizable.stories.tsx +496 -752
  38. package/dist/components/scroll-area/ScrollArea.stories.tsx +384 -1006
  39. package/dist/components/search/Search.stories.tsx +314 -575
  40. package/dist/components/select/Select.stories.tsx +684 -787
  41. package/dist/components/sheet/Sheet.stories.tsx +671 -936
  42. package/dist/components/skelton/Skelton.stories.tsx +230 -764
  43. package/dist/components/slider/Slider.stories.tsx +384 -737
  44. package/dist/components/stepper/Stepper.stories.tsx +371 -514
  45. package/dist/components/switch/Switch.stories.tsx +461 -208
  46. package/dist/components/switch-case/SwitchCase.stories.tsx +367 -188
  47. package/dist/components/table/Table.stories.tsx +770 -914
  48. package/dist/components/tabs/Tabs.stories.tsx +459 -1400
  49. package/dist/components/tag/Tag.stories.tsx +714 -542
  50. package/dist/components/textarea/TextArea.stories.tsx +621 -562
  51. package/dist/components/thumbnail-tags/ThumbnailTags.stories.tsx +228 -148
  52. package/dist/components/toast/Toast.stories.tsx +452 -1333
  53. package/dist/components/toggle/Toggle.stories.tsx +488 -909
  54. package/dist/components/tooltip/Tooltip.stories.tsx +344 -1372
  55. package/dist/components/typography/Typography.stories.tsx +406 -89
  56. package/dist/hooks/use-change-state/UseChangeState.stories.tsx +309 -606
  57. package/dist/hooks/use-previous/UsePrevious.stories.tsx +367 -917
  58. package/dist/hooks/use-standalone-pagination/UseStandalonePagination.stories.tsx +639 -867
  59. package/dist/icons/Icons.stories.tsx +0 -12
  60. package/dist/icons/ai-avatar-icon/AiAvatarIcon.stories.tsx +226 -1013
  61. package/dist/icons/alert-icon/AlertIcon.stories.tsx +109 -929
  62. package/dist/icons/all-icons.tsx +124 -87
  63. package/dist/icons/angle-down-icon/AngleDownIcon.stories.tsx +140 -971
  64. package/dist/icons/apple-logo-icon/AppleLogoIcon.stories.tsx +148 -888
  65. package/dist/icons/arrow-box-left-icon/ArrowBoxLeftIcon.stories.tsx +135 -1019
  66. package/dist/icons/arrow-corner-up-left-icon/ArrowCornerUpLeftIcon.stories.tsx +137 -953
  67. package/dist/icons/arrow-corner-up-right-icon/ArrowCornerUpRightIcon.stories.tsx +138 -997
  68. package/dist/icons/arrow-left-icon/ArrowLeftIcon.stories.tsx +136 -942
  69. package/dist/icons/arrow-right-icon/ArrowRightIcon.stories.tsx +148 -1092
  70. package/dist/icons/arrow-right-up-icon/ArrowRightUpIcon.stories.tsx +146 -1211
  71. package/dist/icons/art-board-icon/ArtBoardIcon.stories.tsx +126 -615
  72. package/dist/icons/audio-bar-icon/AudioBarIcon.stories.tsx +144 -1164
  73. package/dist/icons/backward-ten-seconds-icon/BackwardTenSecondsIcon.stories.tsx +167 -985
  74. package/dist/icons/bubble-check-icon/BubbleCheckIcon.stories.tsx +122 -1179
  75. package/dist/icons/bubble-crossed-icon/BubbleCrossedIcon.stories.tsx +124 -1168
  76. package/dist/icons/bubble-sparkle-icon/BubbleSparkleIcon.stories.tsx +119 -850
  77. package/dist/icons/camera-icon/CameraIcon.stories.tsx +112 -1213
  78. package/dist/icons/capital-a-letter-icon/CapitalALetterIcon.stories.tsx +117 -934
  79. package/dist/icons/chevron-double-left-icon/ChevronDoubleLeftIcon.stories.tsx +160 -961
  80. package/dist/icons/chevron-double-right-icon/ChevronDoubleRightIcon.stories.tsx +163 -961
  81. package/dist/icons/chevron-down-icon/ChevronDownIcon.stories.tsx +144 -942
  82. package/dist/icons/chevron-left-icon/ChevronLeftIcon.stories.tsx +129 -966
  83. package/dist/icons/chevron-right-icon/ChevronRightIcon.stories.tsx +147 -964
  84. package/dist/icons/chevron-up-icon/ChevronUpIcon.stories.tsx +145 -975
  85. package/dist/icons/circle-tick-icon/CircleTickIcon.stories.tsx +150 -1142
  86. package/dist/icons/circular-play-icon/CircularPlayIcon.stories.tsx +114 -461
  87. package/dist/icons/coin-icon/CoinIcon.stories.tsx +124 -1322
  88. package/dist/icons/coin-toons-icon/CoinToonsIcon.stories.tsx +117 -1318
  89. package/dist/icons/column-wide-add-icon/ColumnWideAddIcon.stories.tsx +114 -903
  90. package/dist/icons/command-icon/CommandIcon.stories.tsx +127 -1042
  91. package/dist/icons/copy-icon/CopyIcon.stories.tsx +123 -962
  92. package/dist/icons/cross-circle-icon/CrossCircleIcon.stories.tsx +147 -999
  93. package/dist/icons/cross-icon/CrossIcon.stories.tsx +139 -960
  94. package/dist/icons/download-icon/DownloadIcon.stories.tsx +126 -820
  95. package/dist/icons/edit-big-icon/EditBigIcon.stories.tsx +124 -1031
  96. package/dist/icons/email-icon/EmailIcon.stories.tsx +115 -936
  97. package/dist/icons/expand-icon/ExpandIcon.stories.tsx +112 -1111
  98. package/dist/icons/eye-close-icon/EyeCloseIcon.stories.tsx +144 -1025
  99. package/dist/icons/eye-open-icon/EyeOpenIcon.stories.tsx +143 -1036
  100. package/dist/icons/feature-shine-icon/FeatureShineIcon.stories.tsx +127 -1011
  101. package/dist/icons/file-chart-icon/FileChartIcon.stories.tsx +126 -1056
  102. package/dist/icons/file-text-icon/FileTextIcon.stories.tsx +125 -614
  103. package/dist/icons/filter-bar-row-icon/FilterBarRowIcon.stories.tsx +119 -1050
  104. package/dist/icons/forward-ten-seconds-icon/ForwardTenSecondsIcon.stories.tsx +169 -989
  105. package/dist/icons/git-branch-icon/GitBranchIcon.stories.tsx +115 -1145
  106. package/dist/icons/git-fork-icon/GitForkIcon.stories.tsx +115 -1122
  107. package/dist/icons/globe-icon/GlobeIcon.stories.tsx +130 -313
  108. package/dist/icons/google-logo-icon/GoogleLogoIcon.stories.tsx +145 -940
  109. package/dist/icons/grip-vertical-icon/GripVerticalIcon.stories.tsx +119 -1174
  110. package/dist/icons/head-icon/HeadIcon.stories.tsx +111 -916
  111. package/dist/icons/heart-icon/HeartIcon.stories.tsx +120 -1019
  112. package/dist/icons/image-avatar-sparkle-icon/ImageAvatarSparkleIcon.stories.tsx +119 -683
  113. package/dist/icons/image-icon/ImageIcon.stories.tsx +105 -1121
  114. package/dist/icons/import-folder-icon/ImportFolderIcon.stories.tsx +111 -1192
  115. package/dist/icons/import-left-arrow-folder-icon/ImportLeftArrowFolderIcon.stories.tsx +136 -1256
  116. package/dist/icons/indian-flag-icon/IndianFlagIcon.stories.tsx +159 -962
  117. package/dist/icons/instagram-icon/InstagramIcon.stories.tsx +161 -1385
  118. package/dist/icons/layout-column-icon/LayoutColumnIcon.stories.tsx +124 -972
  119. package/dist/icons/layout-left-icon/LayoutLeftIcon.stories.tsx +119 -948
  120. package/dist/icons/layout-right-icon/LayoutRightIcon.stories.tsx +119 -942
  121. package/dist/icons/light-bulb-simple-icon/LightBulbSimpleIcon.stories.tsx +108 -1215
  122. package/dist/icons/linked-in-icon/LinkedInIcon.stories.tsx +154 -1517
  123. package/dist/icons/magic-book-icon/MagicBookIcon.stories.tsx +110 -1188
  124. package/dist/icons/magic-edit-icon/MagicEditIcon.stories.tsx +119 -678
  125. package/dist/icons/maintenance-icon/MaintenanceIcon.stories.tsx +123 -1184
  126. package/dist/icons/message-icon/MessageIcon.stories.tsx +114 -538
  127. package/dist/icons/minimize-icon/MinimizeIcon.stories.tsx +116 -1158
  128. package/dist/icons/moon-icon/MoonIcon.stories.tsx +120 -536
  129. package/dist/icons/move-horizontal-icon/MoveHorizontalIcon.stories.tsx +109 -1184
  130. package/dist/icons/move-vertical-icon/MoveVerticalIcon.stories.tsx +115 -1134
  131. package/dist/icons/musical-note-icon/MusicalNoteIcon.stories.tsx +119 -971
  132. package/dist/icons/notepad-icon/NotepadIcon.stories.tsx +111 -1100
  133. package/dist/icons/notes-icon/NotesIcon.stories.tsx +119 -1101
  134. package/dist/icons/page-search-icon/PageSearchIcon.stories.tsx +109 -1111
  135. package/dist/icons/page-text-icon/PageTextIcon.stories.tsx +122 -684
  136. package/dist/icons/paint-roll-icon/PaintRollIcon.stories.tsx +113 -954
  137. package/dist/icons/paper-plane-icon/PaperPlaneIcon.stories.tsx +112 -877
  138. package/dist/icons/pause-icon/PauseIcon.stories.tsx +113 -1000
  139. package/dist/icons/pencil-icon/PencilIcon.stories.tsx +115 -1070
  140. package/dist/icons/phone-icon/PhoneIcon.stories.tsx +115 -978
  141. package/dist/icons/plus-icon/PlusIcon.stories.tsx +106 -1093
  142. package/dist/icons/pocket-studio-icon/PocketStudioIcon.stories.tsx +107 -829
  143. package/dist/icons/scroll-down-icon/ScrollDownIcon.stories.tsx +102 -469
  144. package/dist/icons/search-icon/SearchIcon.stories.tsx +111 -1124
  145. package/dist/icons/setting-icon/SettingIcon.stories.tsx +107 -970
  146. package/dist/icons/share-icon/ShareIcon.stories.tsx +120 -1025
  147. package/dist/icons/shield-icon/ShieldIcon.stories.tsx +117 -931
  148. package/dist/icons/site-logo-icon/SiteLogoIcon.stories.tsx +137 -1104
  149. package/dist/icons/skip-backward-icon/SkipBackwardIcon.stories.tsx +172 -982
  150. package/dist/icons/skip-forward-icon/SkipForwardIcon.stories.tsx +164 -983
  151. package/dist/icons/sparkles-soft-icon/SparklesSoftIcon.stories.tsx +105 -958
  152. package/dist/icons/spinner-gradient-icon/SpinnerGradientIcon.stories.tsx +158 -580
  153. package/dist/icons/spinner-gradient-icon/index.tsx +6 -1
  154. package/dist/icons/spinner-solid-icon/SpinnerSolidIcon.stories.tsx +158 -587
  155. package/dist/icons/spinner-solid-icon/index.tsx +6 -1
  156. package/dist/icons/spinner-solid-neutral-icon/SpinnerSolidINeutralcon.stories.tsx +146 -682
  157. package/dist/icons/spinner-solid-neutral-icon/index.tsx +1 -1
  158. package/dist/icons/star-icon/StarIcon.stories.tsx +124 -904
  159. package/dist/icons/store-coin-icon/StoreCoinIcon.stories.tsx +112 -964
  160. package/dist/icons/suggestion-icon/SuggestionIcon.stories.tsx +116 -852
  161. package/dist/icons/sun-icon/SunIcon.stories.tsx +120 -831
  162. package/dist/icons/text-color-icon/TextColorIcon.stories.tsx +116 -950
  163. package/dist/icons/text-indicator-icon/TextIndicatorIcon.stories.tsx +123 -980
  164. package/dist/icons/threads-icon/ThreadsIcon.stories.tsx +156 -1427
  165. package/dist/icons/tick-circle-icon/TickCircleIcon.stories.tsx +146 -1142
  166. package/dist/icons/tick-icon/TickIcon.stories.tsx +145 -1276
  167. package/dist/icons/trash-icon/TrashIcon.stories.tsx +108 -933
  168. package/dist/icons/twitter-x-icon/TwitterXIcon.stories.tsx +157 -1402
  169. package/dist/icons/upload-icon/UploadIcon.stories.tsx +115 -889
  170. package/dist/icons/vertical-menu-icon/VerticalMenuIcon.stories.tsx +118 -984
  171. package/dist/icons/video-play-list-icon/VideoPlaylistIcon.stories.tsx +125 -1049
  172. package/dist/icons/voice-playing-icon/VoicePlayingIcon.stories.tsx +123 -1356
  173. package/dist/icons/volume-full-icon/VolumeFullIcon.stories.tsx +110 -1171
  174. package/dist/icons/volume-half-icon/VolumeHalfIcon.stories.tsx +112 -1093
  175. package/dist/icons/volume-off-icon/VolumeOffIcon.stories.tsx +115 -1087
  176. package/dist/icons/warning-icon/WarningIcon.stories.tsx +122 -1046
  177. package/dist/icons/youtube-icon/YoutubeIcon.stories.tsx +161 -936
  178. package/dist/index.cjs +84 -84
  179. package/dist/index.js +84 -84
  180. package/dist/styles/aural-all-theme.css +1222 -0
  181. package/dist/styles/{aural-theme.css → aural-dark-theme.css} +15 -3
  182. package/dist/styles/aural-light-theme.css +1047 -0
  183. 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,755 +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="rounded bg-gray-100 px-1">{value || "none"}</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
434
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">
435
272
  <SelectField
436
- label="Controlled Select"
437
- placeholder="Select a fruit"
438
- value={value}
439
- onValueChange={setValue}
440
- 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"
441
276
  decoration="outline"
442
277
  fullWidth
443
278
  >
444
- <SelectItem value="apple">Apple</SelectItem>
445
- <SelectItem value="banana">Banana</SelectItem>
446
- <SelectItem value="orange">Orange</SelectItem>
447
- <SelectItem value="grape">Grape</SelectItem>
448
- <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>
449
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>
450
288
  </div>
451
- <div className="flex gap-2">
452
- <Button size="sm" onClick={() => setValue("apple")}>
453
- Set to Apple
454
- </Button>
455
- <Button size="sm" variant="outline" onClick={() => setValue("")}>
456
- Clear
457
- </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>
458
325
  </div>
459
326
  </div>
460
- )
461
- },
327
+ </div>
328
+ ),
462
329
  }
463
330
 
464
- export const DisabledStates: Story = {
465
- render: () => (
466
- <div className="max-w-md space-y-6">
467
- <SelectField
468
- label="Disabled Select"
469
- placeholder="This select is disabled"
470
- disabled
471
- helperText="This select cannot be interacted with"
472
- decoration="outline"
473
- fullWidth
474
- >
475
- <SelectItem value="option1">Option 1</SelectItem>
476
- <SelectItem value="option2">Option 2</SelectItem>
477
- </SelectField>
331
+ // ─── 4. States ───────────────────────────────────────────────────────────────
478
332
 
479
- <SelectField
480
- label="Select with Disabled Items"
481
- placeholder="Some items are disabled"
482
- helperText="Some options may be unavailable"
483
- decoration="filled"
484
- fullWidth
485
- >
486
- <SelectItem value="available1">Available Option 1</SelectItem>
487
- <SelectItem value="disabled1" disabled>
488
- Disabled Option 1
489
- </SelectItem>
490
- <SelectItem value="available2">Available Option 2</SelectItem>
491
- <SelectItem value="disabled2" disabled>
492
- Disabled Option 2
493
- </SelectItem>
494
- </SelectField>
495
- </div>
496
- ),
333
+ export const States: Story = {
497
334
  parameters: {
498
335
  docs: {
499
336
  description: {
500
- 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).",
501
339
  },
502
340
  },
503
341
  },
504
- }
505
-
506
- export const WithGroups: Story = {
507
342
  render: () => (
508
- <SelectField
509
- label="Technology Stack"
510
- placeholder="Select a technology"
511
- decoration="outline"
512
- helperText="Choose from frontend or backend technologies"
513
- fullWidth
514
- >
515
- <SelectGroup>
516
- <SelectLabel className="px-2 pt-2 pb-1">Frontend</SelectLabel>
517
- <SelectItem value="react">React</SelectItem>
518
- <SelectSeparator />
519
- <SelectItem value="vue">Vue</SelectItem>
520
- <SelectSeparator />
521
- <SelectItem value="angular">Angular</SelectItem>
522
- </SelectGroup>
523
- <SelectSeparator />
524
- <SelectGroup>
525
- <SelectLabel className="px-2 pt-2 pb-1">Backend</SelectLabel>
526
- <SelectItem value="nodejs">Node.js</SelectItem>
527
- <SelectSeparator />
528
- <SelectItem value="python">Python</SelectItem>
529
- <SelectSeparator />
530
- <SelectItem value="java">Java</SelectItem>
531
- </SelectGroup>
532
- </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>
533
404
  ),
405
+ }
406
+
407
+ // ─── 5. Interactive ──────────────────────────────────────────────────────────
408
+
409
+ export const Interactive: Story = {
534
410
  parameters: {
535
411
  docs: {
536
412
  description: {
537
- story: `
538
- Select with grouped options using \`SelectGroup\`, \`SelectLabel\`, and \`SelectSeparator\` components.
539
- Useful for organizing related options into categories.
540
- `,
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.",
541
415
  },
542
416
  },
543
417
  },
544
- }
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")
545
424
 
546
- export const FormIntegration: Story = {
547
- render: () => (
548
- <form
549
- className="max-w-md space-y-4 rounded-lg border p-4"
550
- onSubmit={(e) => {
551
- e.preventDefault()
552
- const formData = new FormData(e.currentTarget)
553
- alert(
554
- `Selected values:\nFruit: ${formData.get("fruit")}\nColor: ${formData.get("color")}`
555
- )
556
- }}
557
- >
558
- <SelectField
559
- label="Favorite Fruit"
560
- placeholder="Select a fruit"
561
- name="fruit"
562
- required
563
- helperText="Required field"
564
- decoration="outline"
565
- fullWidth
566
- >
567
- <SelectItem value="apple">Apple</SelectItem>
568
- <SelectItem value="banana">Banana</SelectItem>
569
- <SelectItem value="orange">Orange</SelectItem>
570
- </SelectField>
425
+ const countryVariant = country ? "success" : "default"
426
+ const countryHelper = country
427
+ ? "Country selected successfully"
428
+ : "Please select your country"
571
429
 
572
- <SelectField
573
- label="Favorite Color"
574
- placeholder="Select a color"
575
- name="color"
576
- helperText="Optional field"
577
- decoration="filled"
578
- fullWidth
579
- >
580
- <SelectItem value="red">Red</SelectItem>
581
- <SelectItem value="blue">Blue</SelectItem>
582
- <SelectItem value="green">Green</SelectItem>
583
- </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
+ ]
584
435
 
585
- <Button type="submit" className="w-full">
586
- Submit Form
587
- </Button>
588
- </form>
589
- ),
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 = {
590
538
  parameters: {
591
539
  docs: {
592
540
  description: {
593
- story: `
594
- Select components integrated with a form. Shows how to use \`name\` and \`required\` props
595
- for form validation and submission. The select values will be included in form data.
596
- `,
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.",
597
543
  },
598
544
  },
599
545
  },
600
- }
601
-
602
- export const AtomicComposition: Story = {
603
546
  render: () => {
604
- const [value, setValue] = React.useState("")
547
+ const [atomicValue, setAtomicValue] = React.useState("")
605
548
 
606
549
  return (
607
- <SelectRoot fullWidth className="max-w-md">
608
- <SelectLabel required>Custom Composed Select</SelectLabel>
609
-
610
- <SelectWrapper>
611
- <Select value={value} onValueChange={setValue}>
612
- <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"
613
566
  decoration="outline"
614
- variant="default"
615
- aria-describedby="custom-select-helper"
567
+ fullWidth
616
568
  >
617
- <SelectValue placeholder="Built with atomic components..." />
618
- </SelectTrigger>
569
+ <SelectItem value="react">React</SelectItem>
570
+ <SelectItem value="vue">Vue</SelectItem>
571
+ </SelectField>
572
+ </div>
573
+ </div>
619
574
 
620
- <SelectContent>
621
- <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>
622
629
  <SelectSeparator />
623
- <SelectItem value="flexible">Flexible Composition</SelectItem>
630
+ <SelectItem value="banana">Banana</SelectItem>
624
631
  <SelectSeparator />
625
- <SelectItem value="accessible">Accessible</SelectItem>
626
- </SelectContent>
627
- </Select>
628
- </SelectWrapper>
629
-
630
- <SelectHelperText id="custom-select-helper" variant="default">
631
- This is built using atomic components for maximum flexibility
632
- </SelectHelperText>
633
- </SelectRoot>
632
+ <SelectItem value="cherry">Cherry</SelectItem>
633
+ </SelectField>
634
+ </div>
635
+ </div>
636
+ </div>
634
637
  )
635
638
  },
639
+ }
640
+
641
+ // ─── 7. UseCases ─────────────────────────────────────────────────────────────
642
+
643
+ export const UseCases: Story = {
636
644
  parameters: {
637
645
  docs: {
638
646
  description: {
639
647
  story:
640
- "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.",
641
649
  },
642
650
  },
643
651
  },
644
- }
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("")
645
657
 
646
- export const AllVariantsAllDecorations: Story = {
647
- render: () => (
648
- <div className="grid max-w-6xl grid-cols-1 gap-6 lg:grid-cols-3">
649
- {/* Underline decoration */}
650
- <div className="space-y-4">
651
- <h3 className="text-fm-primary text-lg font-semibold">Underline</h3>
652
- <SelectField
653
- label="Default"
654
- placeholder="Default underline..."
655
- variant="default"
656
- decoration="underline"
657
- helperText="Default variant"
658
- fullWidth
659
- >
660
- <SelectItem value="option1">Option 1</SelectItem>
661
- <SelectItem value="option2">Option 2</SelectItem>
662
- </SelectField>
663
- <SelectField
664
- label="Error"
665
- placeholder="Error underline..."
666
- variant="error"
667
- decoration="underline"
668
- helperText="Something went wrong"
669
- fullWidth
670
- >
671
- <SelectItem value="option1">Option 1</SelectItem>
672
- <SelectItem value="option2">Option 2</SelectItem>
673
- </SelectField>
674
- <SelectField
675
- label="Warning"
676
- placeholder="Warning underline..."
677
- variant="warning"
678
- decoration="underline"
679
- helperText="Please check selection"
680
- fullWidth
681
- >
682
- <SelectItem value="option1">Option 1</SelectItem>
683
- <SelectItem value="option2">Option 2</SelectItem>
684
- </SelectField>
685
- <SelectField
686
- label="Success"
687
- placeholder="Success underline..."
688
- variant="success"
689
- decoration="underline"
690
- helperText="Great choice!"
691
- fullWidth
692
- >
693
- <SelectItem value="option1">Option 1</SelectItem>
694
- <SelectItem value="option2">Option 2</SelectItem>
695
- </SelectField>
696
- </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>
697
704
 
698
- {/* Outline decoration */}
699
- <div className="space-y-4">
700
- <h3 className="text-fm-primary text-lg font-semibold">Outline</h3>
701
- <SelectField
702
- label="Default"
703
- placeholder="Default outline..."
704
- variant="default"
705
- decoration="outline"
706
- helperText="Default variant"
707
- fullWidth
708
- >
709
- <SelectItem value="option1">Option 1</SelectItem>
710
- <SelectItem value="option2">Option 2</SelectItem>
711
- </SelectField>
712
- <SelectField
713
- label="Error"
714
- placeholder="Error outline..."
715
- variant="error"
716
- decoration="outline"
717
- helperText="Something went wrong"
718
- fullWidth
719
- >
720
- <SelectItem value="option1">Option 1</SelectItem>
721
- <SelectItem value="option2">Option 2</SelectItem>
722
- </SelectField>
723
- <SelectField
724
- label="Warning"
725
- placeholder="Warning outline..."
726
- variant="warning"
727
- decoration="outline"
728
- helperText="Please check selection"
729
- fullWidth
730
- >
731
- <SelectItem value="option1">Option 1</SelectItem>
732
- <SelectItem value="option2">Option 2</SelectItem>
733
- </SelectField>
734
- <SelectField
735
- label="Success"
736
- placeholder="Success outline..."
737
- variant="success"
738
- decoration="outline"
739
- helperText="Great choice!"
740
- fullWidth
741
- >
742
- <SelectItem value="option1">Option 1</SelectItem>
743
- <SelectItem value="option2">Option 2</SelectItem>
744
- </SelectField>
745
- </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>
746
738
 
747
- {/* Filled decoration */}
748
- <div className="space-y-4">
749
- <h3 className="text-fm-primary text-lg font-semibold">Filled</h3>
750
- <SelectField
751
- label="Default"
752
- placeholder="Default filled..."
753
- variant="default"
754
- decoration="filled"
755
- helperText="Default variant"
756
- fullWidth
757
- >
758
- <SelectItem value="option1">Option 1</SelectItem>
759
- <SelectItem value="option2">Option 2</SelectItem>
760
- </SelectField>
761
- <SelectField
762
- label="Error"
763
- placeholder="Error filled..."
764
- variant="error"
765
- decoration="filled"
766
- helperText="Something went wrong"
767
- fullWidth
768
- >
769
- <SelectItem value="option1">Option 1</SelectItem>
770
- <SelectItem value="option2">Option 2</SelectItem>
771
- </SelectField>
772
- <SelectField
773
- label="Warning"
774
- placeholder="Warning filled..."
775
- variant="warning"
776
- decoration="filled"
777
- helperText="Please check selection"
778
- fullWidth
779
- >
780
- <SelectItem value="option1">Option 1</SelectItem>
781
- <SelectItem value="option2">Option 2</SelectItem>
782
- </SelectField>
783
- <SelectField
784
- label="Success"
785
- placeholder="Success filled..."
786
- variant="success"
787
- decoration="filled"
788
- helperText="Great choice!"
789
- fullWidth
790
- >
791
- <SelectItem value="option1">Option 1</SelectItem>
792
- <SelectItem value="option2">Option 2</SelectItem>
793
- </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>
794
787
  </div>
795
- </div>
796
- ),
788
+ )
789
+ },
790
+ }
791
+
792
+ // ─── 8. Accessibility ────────────────────────────────────────────────────────
793
+
794
+ export const Accessibility: Story = {
797
795
  parameters: {
798
796
  docs: {
799
797
  description: {
800
798
  story:
801
- "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.",
802
800
  },
803
801
  },
804
802
  },
805
- }
806
-
807
- export const ComplexFormExample: Story = {
808
- render: () => {
809
- const [country, setCountry] = React.useState("")
810
- const [framework, setFramework] = React.useState("")
811
-
812
- return (
813
- <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>
814
810
  <SelectField
815
811
  label="Country"
816
- placeholder="Select your country"
817
- value={country}
818
- onValueChange={setCountry}
812
+ placeholder="Select your country"
819
813
  required
820
- helperText={
821
- country
822
- ? "Country selected successfully"
823
- : "Please select your country"
824
- }
825
- variant={country ? "success" : "default"}
814
+ helperText="Required — used for regional settings"
826
815
  decoration="outline"
827
816
  fullWidth
828
817
  >
829
818
  <SelectItem value="us">United States</SelectItem>
830
819
  <SelectItem value="ca">Canada</SelectItem>
831
820
  <SelectItem value="uk">United Kingdom</SelectItem>
832
- <SelectItem value="de">Germany</SelectItem>
833
- <SelectItem value="fr">France</SelectItem>
834
821
  </SelectField>
822
+ </div>
835
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>
836
829
  <SelectField
837
- label="Preferred Framework"
838
- placeholder="Choose your framework"
839
- value={framework}
840
- onValueChange={setFramework}
841
- helperText="This will help us customize your experience"
842
- decoration="filled"
830
+ label="Plan"
831
+ placeholder="Select a plan…"
832
+ variant="error"
833
+ helperText="Please select a plan to continue"
834
+ decoration="outline"
843
835
  fullWidth
844
836
  >
845
- <SelectGroup>
846
- <SelectLabel className="px-2 pt-2 pb-1">Popular</SelectLabel>
847
- <SelectItem value="react">React</SelectItem>
848
- <SelectSeparator />
849
- <SelectItem value="vue">Vue.js</SelectItem>
850
- <SelectSeparator />
851
- <SelectItem value="angular">Angular</SelectItem>
852
- </SelectGroup>
853
- <SelectSeparator />
854
- <SelectGroup>
855
- <SelectLabel className="px-2 pt-2 pb-1">Others</SelectLabel>
856
- <SelectItem value="svelte">Svelte</SelectItem>
857
- <SelectSeparator />
858
- <SelectItem value="solid">SolidJS</SelectItem>
859
- </SelectGroup>
837
+ <SelectItem value="starter">Starter</SelectItem>
838
+ <SelectItem value="pro">Pro</SelectItem>
860
839
  </SelectField>
861
840
  </div>
862
- )
863
- },
864
- parameters: {
865
- docs: {
866
- description: {
867
- story:
868
- "An example showing multiple SelectField components in a form with different configurations, decorations, and dynamic states based on user input.",
869
- },
870
- },
871
- },
872
- }
873
841
 
874
- // Legacy stories for backward compatibility with the old API
875
- export const LegacyDefault: Story = {
876
- parameters: {
877
- docs: {
878
- description: {
879
- story:
880
- "Legacy API example using the atomic Select components directly (for backward compatibility).",
881
- },
882
- },
883
- },
884
- render: () => (
885
- <Select>
886
- <SelectTrigger
887
- className="w-[180px]"
888
- variant="default"
889
- decoration="underline"
890
- >
891
- <SelectValue placeholder="Select a fruit" />
892
- </SelectTrigger>
893
- <SelectContent>
894
- <SelectItem value="apple">Apple</SelectItem>
895
- <SelectSeparator />
896
- <SelectItem value="banana">Banana</SelectItem>
897
- <SelectSeparator />
898
- <SelectItem value="orange">Orange</SelectItem>
899
- <SelectSeparator />
900
- <SelectItem value="grape">Grape</SelectItem>
901
- <SelectSeparator />
902
- <SelectItem value="strawberry">Strawberry</SelectItem>
903
- </SelectContent>
904
- </Select>
905
- ),
906
- }
907
-
908
- export const LongList: Story = {
909
- render: () => (
910
- <SelectField
911
- label="Country Selection"
912
- placeholder="Select a country"
913
- helperText="Scroll to see more options"
914
- decoration="outline"
915
- fullWidth
916
- >
917
- <SelectItem value="us">United States</SelectItem>
918
- <SelectSeparator />
919
- <SelectItem value="ca">Canada</SelectItem>
920
- <SelectSeparator />
921
- <SelectItem value="uk">United Kingdom</SelectItem>
922
- <SelectSeparator />
923
- <SelectItem value="fr">France</SelectItem>
924
- <SelectSeparator />
925
- <SelectItem value="de">Germany</SelectItem>
926
- <SelectSeparator />
927
- <SelectItem value="it">Italy</SelectItem>
928
- <SelectSeparator />
929
- <SelectItem value="es">Spain</SelectItem>
930
- <SelectSeparator />
931
- <SelectItem value="au">Australia</SelectItem>
932
- <SelectSeparator />
933
- <SelectItem value="jp">Japan</SelectItem>
934
- <SelectSeparator />
935
- <SelectItem value="kr">South Korea</SelectItem>
936
- <SelectSeparator />
937
- <SelectItem value="in">India</SelectItem>
938
- <SelectSeparator />
939
- <SelectItem value="br">Brazil</SelectItem>
940
- <SelectSeparator />
941
- <SelectItem value="mx">Mexico</SelectItem>
942
- <SelectSeparator />
943
- <SelectItem value="ar">Argentina</SelectItem>
944
- <SelectSeparator />
945
- <SelectItem value="cl">Chile</SelectItem>
946
- <SelectSeparator />
947
- <SelectItem value="pe">Peru</SelectItem>
948
- <SelectSeparator />
949
- <SelectItem value="co">Colombia</SelectItem>
950
- <SelectSeparator />
951
- <SelectItem value="ve">Venezuela</SelectItem>
952
- <SelectSeparator />
953
- <SelectItem value="ec">Ecuador</SelectItem>
954
- <SelectSeparator />
955
- <SelectItem value="uy">Uruguay</SelectItem>
956
- </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>
957
865
  ),
958
- parameters: {
959
- docs: {
960
- description: {
961
- story: `
962
- Select with many options that demonstrates scrolling behavior.
963
- The content area becomes scrollable when it exceeds the maximum height,
964
- and scroll buttons appear automatically at the top and bottom.
965
- `,
966
- },
967
- },
968
- },
969
866
  }