@fabio.caffarello/react-design-system 1.7.0 → 1.8.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 (460) hide show
  1. package/README.md +48 -3
  2. package/dist/docs/components/ComponentStatusTable.d.ts +11 -0
  3. package/dist/ui/atoms/Accordion/Accordion.d.ts +34 -0
  4. package/dist/ui/atoms/Accordion/Accordion.stories.d.ts +11 -0
  5. package/dist/ui/atoms/Accordion/Accordion.test.d.ts +1 -0
  6. package/dist/ui/atoms/Accordion/index.d.ts +2 -0
  7. package/dist/ui/atoms/Avatar/Avatar.d.ts +30 -0
  8. package/dist/ui/atoms/Avatar/Avatar.stories.d.ts +13 -0
  9. package/dist/ui/atoms/Avatar/Avatar.test.d.ts +1 -0
  10. package/dist/ui/atoms/Avatar/AvatarGroup.d.ts +26 -0
  11. package/dist/ui/atoms/Avatar/index.d.ts +9 -0
  12. package/dist/ui/atoms/Badge/Badge.d.ts +14 -6
  13. package/dist/ui/atoms/Badge/Badge.stories.d.ts +8 -9
  14. package/dist/ui/atoms/BoxWrapper/BoxWrapper.d.ts +3 -3
  15. package/dist/ui/atoms/BoxWrapper/BoxWrapper.test.d.ts +1 -0
  16. package/dist/ui/atoms/Button/Button.d.ts +32 -10
  17. package/dist/ui/atoms/Button/Button.stories.d.ts +7 -0
  18. package/dist/ui/atoms/Button/Button.test.d.ts +1 -0
  19. package/dist/ui/atoms/Checkbox/Checkbox.d.ts +2 -1
  20. package/dist/ui/atoms/Collapsible/Collapsible.stories.d.ts +2 -0
  21. package/dist/ui/atoms/Info/Info.d.ts +2 -3
  22. package/dist/ui/atoms/Info/Info.test.d.ts +1 -0
  23. package/dist/ui/atoms/Input/Input.d.ts +14 -4
  24. package/dist/ui/atoms/Input/Input.stories.d.ts +6 -0
  25. package/dist/ui/atoms/Popover/Popover.d.ts +35 -0
  26. package/dist/ui/atoms/Popover/Popover.stories.d.ts +11 -0
  27. package/dist/ui/atoms/Popover/Popover.test.d.ts +1 -0
  28. package/dist/ui/atoms/Popover/index.d.ts +2 -0
  29. package/dist/ui/atoms/Progress/Progress.d.ts +33 -0
  30. package/dist/ui/atoms/Progress/Progress.stories.d.ts +12 -0
  31. package/dist/ui/atoms/Progress/Progress.test.d.ts +1 -0
  32. package/dist/ui/atoms/Select/Select.d.ts +18 -6
  33. package/dist/ui/atoms/Select/Select.stories.d.ts +11 -8
  34. package/dist/ui/atoms/Separator/Separator.d.ts +23 -0
  35. package/dist/ui/atoms/Separator/Separator.stories.d.ts +10 -0
  36. package/dist/ui/atoms/Separator/Separator.test.d.ts +1 -0
  37. package/dist/ui/atoms/Separator/index.d.ts +2 -0
  38. package/dist/ui/atoms/Skeleton/Skeleton.d.ts +1 -1
  39. package/dist/ui/atoms/Skeleton/Skeleton.stories.d.ts +24 -0
  40. package/dist/ui/atoms/Slider/Slider.d.ts +45 -0
  41. package/dist/ui/atoms/Slider/Slider.stories.d.ts +13 -0
  42. package/dist/ui/atoms/Slider/Slider.test.d.ts +1 -0
  43. package/dist/ui/atoms/Slider/index.d.ts +2 -0
  44. package/dist/ui/atoms/Spinner/Spinner.d.ts +22 -0
  45. package/dist/ui/atoms/Spinner/Spinner.stories.d.ts +9 -0
  46. package/dist/ui/atoms/Spinner/Spinner.test.d.ts +1 -0
  47. package/dist/ui/atoms/Switch/Switch.d.ts +28 -0
  48. package/dist/ui/atoms/Switch/Switch.stories.d.ts +11 -0
  49. package/dist/ui/atoms/Switch/Switch.test.d.ts +1 -0
  50. package/dist/ui/atoms/Switch/index.d.ts +2 -0
  51. package/dist/ui/atoms/Tooltip/Tooltip.d.ts +2 -1
  52. package/dist/ui/atoms/Tooltip/Tooltip.stories.d.ts +17 -0
  53. package/dist/ui/atoms/index.d.ts +20 -3
  54. package/dist/ui/index.d.ts +6 -1
  55. package/dist/ui/molecules/Card/Card.d.ts +6 -2
  56. package/dist/ui/molecules/Card/Card.stories.d.ts +2 -0
  57. package/dist/ui/molecules/ColorPicker/ColorPicker.d.ts +28 -0
  58. package/dist/ui/molecules/ColorPicker/ColorPicker.stories.d.ts +12 -0
  59. package/dist/ui/molecules/ColorPicker/ColorPicker.test.d.ts +1 -0
  60. package/dist/ui/molecules/ColorPicker/index.d.ts +2 -0
  61. package/dist/ui/molecules/DatePicker/DatePicker.d.ts +74 -0
  62. package/dist/ui/molecules/DatePicker/DatePicker.stories.d.ts +12 -0
  63. package/dist/ui/molecules/DatePicker/DatePicker.test.d.ts +1 -0
  64. package/dist/ui/molecules/DatePicker/DatePickerCalendar.d.ts +6 -0
  65. package/dist/ui/molecules/DatePicker/DatePickerContext.d.ts +28 -0
  66. package/dist/ui/molecules/DatePicker/DatePickerInput.d.ts +9 -0
  67. package/dist/ui/molecules/DatePicker/DatePickerProvider.d.ts +23 -0
  68. package/dist/ui/molecules/DatePicker/index.d.ts +14 -0
  69. package/dist/ui/molecules/Dropdown/Dropdown.d.ts +2 -1
  70. package/dist/ui/molecules/Dropdown/Dropdown.stories.d.ts +13 -0
  71. package/dist/ui/molecules/EmptyState/EmptyState.stories.d.ts +22 -0
  72. package/dist/ui/molecules/FileUpload/FileUpload.d.ts +37 -0
  73. package/dist/ui/molecules/FileUpload/FileUpload.stories.d.ts +12 -0
  74. package/dist/ui/molecules/FileUpload/FileUpload.test.d.ts +1 -0
  75. package/dist/ui/molecules/FileUpload/index.d.ts +2 -0
  76. package/dist/ui/molecules/Form/Form.d.ts +29 -4
  77. package/dist/ui/molecules/Form/Form.stories.d.ts +2 -0
  78. package/dist/ui/molecules/Form/FormContext.d.ts +17 -0
  79. package/dist/ui/molecules/Form/FormField.d.ts +36 -0
  80. package/dist/ui/molecules/Form/FormProvider.d.ts +14 -0
  81. package/dist/ui/molecules/Form/index.d.ts +13 -0
  82. package/dist/ui/molecules/Form/useFormFieldArray.d.ts +28 -0
  83. package/dist/ui/molecules/InputWithLabel/InputWithLabel.test.d.ts +1 -0
  84. package/dist/ui/molecules/Rating/Rating.d.ts +33 -0
  85. package/dist/ui/molecules/Rating/Rating.stories.d.ts +13 -0
  86. package/dist/ui/molecules/Rating/Rating.test.d.ts +1 -0
  87. package/dist/ui/molecules/Rating/index.d.ts +2 -0
  88. package/dist/ui/molecules/SearchInput/SearchInput.d.ts +24 -0
  89. package/dist/ui/molecules/SearchInput/SearchInput.stories.d.ts +10 -0
  90. package/dist/ui/molecules/SearchInput/SearchInput.test.d.ts +1 -0
  91. package/dist/ui/molecules/SearchInput/index.d.ts +2 -0
  92. package/dist/ui/molecules/SidebarHeader/SidebarHeader.test.d.ts +1 -0
  93. package/dist/ui/molecules/TableActions/TableActions.d.ts +31 -0
  94. package/dist/ui/molecules/TableActions/TableActions.stories.d.ts +7 -0
  95. package/dist/ui/molecules/TableActions/TableActions.test.d.ts +1 -0
  96. package/dist/ui/molecules/TableFilters/TableFilters.d.ts +37 -0
  97. package/dist/ui/molecules/TableFilters/TableFilters.stories.d.ts +7 -0
  98. package/dist/ui/molecules/TableFilters/TableFilters.test.d.ts +1 -0
  99. package/dist/ui/molecules/TablePagination/TablePagination.d.ts +29 -0
  100. package/dist/ui/molecules/TablePagination/TablePagination.stories.d.ts +8 -0
  101. package/dist/ui/molecules/TablePagination/TablePagination.test.d.ts +1 -0
  102. package/dist/ui/molecules/Tabs/Tabs.d.ts +15 -0
  103. package/dist/ui/molecules/Tabs/Tabs.stories.d.ts +10 -0
  104. package/dist/ui/molecules/Tabs/Tabs.test.d.ts +1 -0
  105. package/dist/ui/molecules/Tabs/TabsContent.d.ts +14 -0
  106. package/dist/ui/molecules/Tabs/TabsContext.d.ts +18 -0
  107. package/dist/ui/molecules/Tabs/TabsList.d.ts +12 -0
  108. package/dist/ui/molecules/Tabs/TabsProvider.d.ts +16 -0
  109. package/dist/ui/molecules/Tabs/TabsTrigger.d.ts +13 -0
  110. package/dist/ui/molecules/Tabs/index.d.ts +17 -0
  111. package/dist/ui/molecules/TimePicker/TimePicker.d.ts +29 -0
  112. package/dist/ui/molecules/TimePicker/TimePicker.stories.d.ts +12 -0
  113. package/dist/ui/molecules/TimePicker/TimePicker.test.d.ts +1 -0
  114. package/dist/ui/molecules/TimePicker/index.d.ts +2 -0
  115. package/dist/ui/molecules/index.d.ts +13 -5
  116. package/dist/ui/organisms/CommandPalette/CommandPalette.d.ts +37 -0
  117. package/dist/ui/organisms/CommandPalette/CommandPalette.stories.d.ts +11 -0
  118. package/dist/ui/organisms/CommandPalette/CommandPalette.test.d.ts +1 -0
  119. package/dist/ui/organisms/CommandPalette/index.d.ts +2 -0
  120. package/dist/ui/organisms/DataGrid/DataGrid.d.ts +84 -0
  121. package/dist/ui/organisms/DataGrid/DataGrid.stories.d.ts +14 -0
  122. package/dist/ui/organisms/DataGrid/DataGrid.test.d.ts +1 -0
  123. package/dist/ui/organisms/DataGrid/index.d.ts +2 -0
  124. package/dist/ui/organisms/Dialog/AlertDialog.d.ts +34 -0
  125. package/dist/ui/organisms/Dialog/Dialog.d.ts +58 -0
  126. package/dist/ui/organisms/Dialog/Dialog.stories.d.ts +13 -0
  127. package/dist/ui/organisms/Dialog/Dialog.test.d.ts +1 -0
  128. package/dist/ui/organisms/Dialog/DialogClose.d.ts +8 -0
  129. package/dist/ui/organisms/Dialog/DialogContent.d.ts +8 -0
  130. package/dist/ui/organisms/Dialog/DialogContext.d.ts +10 -0
  131. package/dist/ui/organisms/Dialog/DialogDescription.d.ts +4 -0
  132. package/dist/ui/organisms/Dialog/DialogFooter.d.ts +5 -0
  133. package/dist/ui/organisms/Dialog/DialogHeader.d.ts +5 -0
  134. package/dist/ui/organisms/Dialog/DialogProvider.d.ts +10 -0
  135. package/dist/ui/organisms/Dialog/DialogTitle.d.ts +5 -0
  136. package/dist/ui/organisms/Dialog/DialogTrigger.d.ts +6 -0
  137. package/dist/ui/organisms/Dialog/index.d.ts +22 -0
  138. package/dist/ui/organisms/Sidebar/Sidebar.d.ts +7 -4
  139. package/dist/ui/organisms/Sidebar/SidebarGroup/SidebarGroup.d.ts +27 -0
  140. package/dist/ui/organisms/Sidebar/SidebarGroup/SidebarGroup.stories.d.ts +11 -0
  141. package/dist/ui/organisms/Sidebar/SidebarGroup/SidebarGroup.test.d.ts +1 -0
  142. package/dist/ui/organisms/Sidebar/SidebarHeader/SidebarHeader.d.ts +19 -0
  143. package/dist/ui/organisms/Sidebar/SidebarHeader/SidebarHeader.test.d.ts +1 -0
  144. package/dist/ui/organisms/Sidebar/SidebarItem/SidebarItem.d.ts +23 -0
  145. package/dist/ui/organisms/Sidebar/SidebarItem/SidebarItem.stories.d.ts +10 -0
  146. package/dist/ui/organisms/Sidebar/SidebarItem/SidebarItem.test.d.ts +1 -0
  147. package/dist/ui/organisms/Sidebar/index.d.ts +8 -0
  148. package/dist/ui/organisms/Stepper/Stepper.d.ts +40 -0
  149. package/dist/ui/organisms/Stepper/Stepper.stories.d.ts +12 -0
  150. package/dist/ui/organisms/Stepper/Stepper.test.d.ts +1 -0
  151. package/dist/ui/organisms/Stepper/index.d.ts +2 -0
  152. package/dist/ui/organisms/Table/Table.d.ts +84 -16
  153. package/dist/ui/organisms/Table/Table.stories.d.ts +15 -0
  154. package/dist/ui/organisms/Table/TableActions/TableActions.d.ts +31 -0
  155. package/dist/ui/organisms/Table/TableActions/TableActions.stories.d.ts +7 -0
  156. package/dist/ui/organisms/Table/TableActions/TableActions.test.d.ts +1 -0
  157. package/dist/ui/organisms/Table/TableActions.d.ts +13 -0
  158. package/dist/ui/organisms/Table/TableBody.d.ts +12 -0
  159. package/dist/ui/organisms/Table/TableCell.d.ts +14 -0
  160. package/dist/ui/organisms/Table/TableContext.d.ts +75 -0
  161. package/dist/ui/organisms/Table/TableEmptyState.d.ts +11 -0
  162. package/dist/ui/organisms/Table/TableFilters/TableFilters.d.ts +37 -0
  163. package/dist/ui/organisms/Table/TableFilters/TableFilters.stories.d.ts +7 -0
  164. package/dist/ui/organisms/Table/TableFilters/TableFilters.test.d.ts +1 -0
  165. package/dist/ui/organisms/Table/TableFilters.d.ts +12 -0
  166. package/dist/ui/organisms/Table/TableHeader.d.ts +10 -0
  167. package/dist/ui/organisms/Table/TableHeaderCell.d.ts +18 -0
  168. package/dist/ui/organisms/Table/TableHeaderRow.d.ts +10 -0
  169. package/dist/ui/organisms/Table/TablePagination/TablePagination.d.ts +29 -0
  170. package/dist/ui/organisms/Table/TablePagination/TablePagination.stories.d.ts +8 -0
  171. package/dist/ui/organisms/Table/TablePagination/TablePagination.test.d.ts +1 -0
  172. package/dist/ui/organisms/Table/TablePagination.d.ts +14 -0
  173. package/dist/ui/organisms/Table/TableProvider.d.ts +55 -0
  174. package/dist/ui/organisms/Table/TableRow.d.ts +14 -0
  175. package/dist/ui/organisms/Table/TableTypes.d.ts +8 -0
  176. package/dist/ui/organisms/Table/index.d.ts +30 -0
  177. package/dist/ui/organisms/Table/useColumnResizing.d.ts +39 -0
  178. package/dist/ui/organisms/Table/useVirtualScrolling.d.ts +35 -0
  179. package/dist/ui/organisms/Timeline/Timeline.d.ts +34 -0
  180. package/dist/ui/organisms/Timeline/Timeline.stories.d.ts +12 -0
  181. package/dist/ui/organisms/Timeline/Timeline.test.d.ts +1 -0
  182. package/dist/ui/organisms/Timeline/index.d.ts +2 -0
  183. package/dist/ui/organisms/Toast/Toast.d.ts +8 -0
  184. package/dist/ui/organisms/Toast/Toast.stories.d.ts +14 -0
  185. package/dist/ui/organisms/Toast/Toast.test.d.ts +1 -0
  186. package/dist/ui/organisms/Toast/ToastContainer.d.ts +5 -0
  187. package/dist/ui/organisms/Toast/ToastContext.d.ts +21 -0
  188. package/dist/ui/organisms/Toast/ToastProvider.d.ts +7 -0
  189. package/dist/ui/organisms/Toast/index.d.ts +15 -0
  190. package/dist/ui/organisms/Toast/useToast.d.ts +35 -0
  191. package/dist/ui/organisms/index.d.ts +12 -2
  192. package/dist/ui/providers/AdvancedThemeProvider.d.ts +52 -0
  193. package/dist/ui/providers/index.d.ts +9 -0
  194. package/dist/ui/themes/ThemeBuilder.d.ts +28 -0
  195. package/dist/ui/themes/ThemeRegistry.d.ts +55 -0
  196. package/dist/ui/themes/index.d.ts +9 -0
  197. package/dist/ui/themes/types.d.ts +48 -0
  198. package/dist/ui/themes/utils.d.ts +21 -0
  199. package/dist/ui/tokens/TokenVisualizations.d.ts +41 -0
  200. package/dist/ui/tokens/animations.d.ts +65 -0
  201. package/dist/ui/tokens/borders.d.ts +61 -0
  202. package/dist/ui/tokens/gradients.d.ts +55 -0
  203. package/dist/ui/tokens/index.d.ts +31 -0
  204. package/dist/ui/tokens/opacity.d.ts +51 -0
  205. package/dist/ui/tokens/radius.d.ts +45 -0
  206. package/dist/ui/tokens/shadows.d.ts +42 -0
  207. package/dist/ui/tokens/themes/dark.d.ts +26 -26
  208. package/dist/ui/tokens/themes/light.d.ts +26 -26
  209. package/dist/ui/tokens/tokens.factory.d.ts +42 -0
  210. package/dist/ui/tokens/z-index.d.ts +44 -0
  211. package/dist/ui/utils/index.d.ts +6 -0
  212. package/package.json +50 -6
  213. package/src/docs/Accessibility.mdx +402 -0
  214. package/src/docs/BestPractices.mdx +315 -0
  215. package/src/docs/ComponentComposition.mdx +381 -0
  216. package/src/docs/ComponentStatus.mdx +177 -0
  217. package/src/docs/DesignSystem.mdx +121 -0
  218. package/src/docs/GettingStarted.mdx +284 -0
  219. package/src/docs/MigrationGuide.mdx +297 -0
  220. package/src/docs/Performance.mdx +206 -0
  221. package/src/docs/components/ComponentStatusTable.tsx +184 -0
  222. package/src/setupTests.ts +32 -0
  223. package/src/ui/atoms/Accordion/Accordion.stories.tsx +147 -0
  224. package/src/ui/atoms/Accordion/Accordion.test.tsx +86 -0
  225. package/src/ui/atoms/Accordion/Accordion.tsx +147 -0
  226. package/src/ui/atoms/Accordion/index.ts +2 -0
  227. package/src/ui/atoms/Avatar/Avatar.stories.tsx +226 -0
  228. package/src/ui/atoms/Avatar/Avatar.test.tsx +233 -0
  229. package/src/ui/atoms/Avatar/Avatar.tsx +128 -0
  230. package/src/ui/atoms/Avatar/AvatarGroup.tsx +96 -0
  231. package/src/ui/atoms/Avatar/index.ts +11 -0
  232. package/src/ui/atoms/Badge/Badge.stories.tsx +65 -56
  233. package/src/ui/atoms/Badge/Badge.test.tsx +27 -50
  234. package/src/ui/atoms/Badge/Badge.tsx +70 -27
  235. package/src/ui/atoms/BoxWrapper/BoxWrapper.stories.tsx +1 -1
  236. package/src/ui/atoms/BoxWrapper/BoxWrapper.test.tsx +27 -0
  237. package/src/ui/atoms/BoxWrapper/BoxWrapper.tsx +5 -2
  238. package/src/ui/atoms/Button/Button.stories.tsx +130 -1
  239. package/src/ui/atoms/Button/Button.test.tsx +233 -0
  240. package/src/ui/atoms/Button/Button.tsx +160 -53
  241. package/src/ui/atoms/Checkbox/Checkbox.tsx +14 -1
  242. package/src/ui/atoms/Collapsible/Collapsible.stories.tsx +47 -1
  243. package/src/ui/atoms/Collapsible/Collapsible.test.tsx +36 -24
  244. package/src/ui/atoms/Collapsible/Collapsible.tsx +9 -1
  245. package/src/ui/atoms/ErrorMessage/ErrorMessage.stories.tsx +1 -1
  246. package/src/ui/atoms/Info/Info.stories.tsx +1 -1
  247. package/src/ui/atoms/Info/Info.test.tsx +45 -0
  248. package/src/ui/atoms/Info/Info.tsx +2 -2
  249. package/src/ui/atoms/Input/Input.stories.tsx +80 -0
  250. package/src/ui/atoms/Input/Input.test.tsx +190 -36
  251. package/src/ui/atoms/Input/Input.tsx +144 -25
  252. package/src/ui/atoms/Label/Label.stories.tsx +1 -1
  253. package/src/ui/atoms/NavLink/NavLink.stories.tsx +1 -1
  254. package/src/ui/atoms/Popover/Popover.stories.tsx +157 -0
  255. package/src/ui/atoms/Popover/Popover.test.tsx +80 -0
  256. package/src/ui/atoms/Popover/Popover.tsx +256 -0
  257. package/src/ui/atoms/Popover/index.ts +2 -0
  258. package/src/ui/atoms/Progress/Progress.css +17 -0
  259. package/src/ui/atoms/Progress/Progress.stories.tsx +170 -0
  260. package/src/ui/atoms/Progress/Progress.test.tsx +134 -0
  261. package/src/ui/atoms/Progress/Progress.tsx +138 -0
  262. package/src/ui/atoms/Radio/Radio.tsx +1 -1
  263. package/src/ui/atoms/Select/Select.stories.tsx +93 -58
  264. package/src/ui/atoms/Select/Select.test.tsx +162 -46
  265. package/src/ui/atoms/Select/Select.tsx +142 -44
  266. package/src/ui/atoms/Separator/Separator.stories.tsx +88 -0
  267. package/src/ui/atoms/Separator/Separator.test.tsx +34 -0
  268. package/src/ui/atoms/Separator/Separator.tsx +81 -0
  269. package/src/ui/atoms/Separator/index.ts +2 -0
  270. package/src/ui/atoms/Skeleton/Skeleton.stories.tsx +62 -0
  271. package/src/ui/atoms/Skeleton/Skeleton.tsx +19 -2
  272. package/src/ui/atoms/Slider/Slider.stories.tsx +205 -0
  273. package/src/ui/atoms/Slider/Slider.test.tsx +53 -0
  274. package/src/ui/atoms/Slider/Slider.tsx +307 -0
  275. package/src/ui/atoms/Slider/index.ts +2 -0
  276. package/src/ui/atoms/Spinner/Spinner.stories.tsx +56 -0
  277. package/src/ui/atoms/Spinner/Spinner.test.tsx +35 -0
  278. package/src/ui/atoms/Spinner/Spinner.tsx +88 -0
  279. package/src/ui/atoms/Switch/Switch.stories.tsx +182 -0
  280. package/src/ui/atoms/Switch/Switch.test.tsx +90 -0
  281. package/src/ui/atoms/Switch/Switch.tsx +181 -0
  282. package/src/ui/atoms/Switch/index.ts +2 -0
  283. package/src/ui/atoms/Text/Text.stories.tsx +1 -1
  284. package/src/ui/atoms/Text/Text.test.tsx +48 -32
  285. package/src/ui/atoms/Textarea/Textarea.stories.tsx +1 -1
  286. package/src/ui/atoms/Tooltip/Tooltip.stories.tsx +44 -0
  287. package/src/ui/atoms/Tooltip/Tooltip.tsx +94 -6
  288. package/src/ui/atoms/index.ts +27 -4
  289. package/src/ui/index.ts +6 -1
  290. package/src/ui/molecules/Breadcrumb/Breadcrumb.stories.tsx +1 -1
  291. package/src/ui/molecules/Breadcrumb/Breadcrumb.tsx +1 -1
  292. package/src/ui/molecules/Card/Card.stories.tsx +49 -1
  293. package/src/ui/molecules/Card/Card.tsx +40 -5
  294. package/src/ui/molecules/ColorPicker/ColorPicker.stories.tsx +156 -0
  295. package/src/ui/molecules/ColorPicker/ColorPicker.test.tsx +47 -0
  296. package/src/ui/molecules/ColorPicker/ColorPicker.tsx +271 -0
  297. package/src/ui/molecules/ColorPicker/index.ts +2 -0
  298. package/src/ui/molecules/DatePicker/DatePicker.mdx +150 -0
  299. package/src/ui/molecules/DatePicker/DatePicker.stories.tsx +188 -0
  300. package/src/ui/molecules/DatePicker/DatePicker.test.tsx +381 -0
  301. package/src/ui/molecules/DatePicker/DatePicker.tsx +231 -0
  302. package/src/ui/molecules/DatePicker/DatePickerCalendar.tsx +277 -0
  303. package/src/ui/molecules/DatePicker/DatePickerContext.tsx +39 -0
  304. package/src/ui/molecules/DatePicker/DatePickerInput.tsx +147 -0
  305. package/src/ui/molecules/DatePicker/DatePickerProvider.tsx +100 -0
  306. package/src/ui/molecules/DatePicker/index.ts +16 -0
  307. package/src/ui/molecules/Dropdown/Dropdown.stories.tsx +50 -8
  308. package/src/ui/molecules/Dropdown/Dropdown.test.tsx +272 -12
  309. package/src/ui/molecules/Dropdown/Dropdown.tsx +176 -10
  310. package/src/ui/molecules/EmptyState/EmptyState.stories.tsx +24 -2
  311. package/src/ui/molecules/EmptyState/EmptyState.tsx +9 -3
  312. package/src/ui/molecules/FileUpload/FileUpload.stories.tsx +177 -0
  313. package/src/ui/molecules/FileUpload/FileUpload.test.tsx +114 -0
  314. package/src/ui/molecules/FileUpload/FileUpload.tsx +312 -0
  315. package/src/ui/molecules/FileUpload/index.ts +2 -0
  316. package/src/ui/molecules/Form/Form.mdx +145 -0
  317. package/src/ui/molecules/Form/Form.stories.tsx +121 -1
  318. package/src/ui/molecules/Form/Form.test.tsx +1 -3
  319. package/src/ui/molecules/Form/Form.tsx +95 -15
  320. package/src/ui/molecules/Form/FormContext.tsx +35 -0
  321. package/src/ui/molecules/Form/FormField.tsx +83 -0
  322. package/src/ui/molecules/Form/FormProvider.tsx +34 -0
  323. package/src/ui/molecules/Form/index.ts +21 -0
  324. package/src/ui/molecules/Form/useFormFieldArray.ts +46 -0
  325. package/src/ui/molecules/InputWithLabel/InputWithLabel.stories.tsx +1 -1
  326. package/src/ui/molecules/InputWithLabel/InputWithLabel.test.tsx +44 -0
  327. package/src/ui/molecules/InputWithLabel/InputWithLabel.tsx +3 -1
  328. package/src/ui/molecules/NavbarGroup/NavbarGroup.stories.tsx +1 -1
  329. package/src/ui/molecules/Pagination/Pagination.stories.tsx +1 -1
  330. package/src/ui/molecules/Rating/Rating.stories.tsx +206 -0
  331. package/src/ui/molecules/Rating/Rating.test.tsx +60 -0
  332. package/src/ui/molecules/Rating/Rating.tsx +173 -0
  333. package/src/ui/molecules/Rating/index.ts +2 -0
  334. package/src/ui/molecules/SearchInput/SearchInput.stories.tsx +146 -0
  335. package/src/ui/molecules/SearchInput/SearchInput.test.tsx +82 -0
  336. package/src/ui/molecules/SearchInput/SearchInput.tsx +133 -0
  337. package/src/ui/molecules/SearchInput/index.ts +2 -0
  338. package/src/ui/molecules/Tabs/Tabs.stories.tsx +229 -0
  339. package/src/ui/molecules/Tabs/Tabs.test.tsx +497 -0
  340. package/src/ui/molecules/Tabs/Tabs.tsx +58 -0
  341. package/src/ui/molecules/Tabs/TabsContent.tsx +50 -0
  342. package/src/ui/molecules/Tabs/TabsContext.tsx +36 -0
  343. package/src/ui/molecules/Tabs/TabsList.tsx +98 -0
  344. package/src/ui/molecules/Tabs/TabsProvider.tsx +53 -0
  345. package/src/ui/molecules/Tabs/TabsTrigger.tsx +111 -0
  346. package/src/ui/molecules/Tabs/index.ts +23 -0
  347. package/src/ui/molecules/TimePicker/TimePicker.stories.tsx +145 -0
  348. package/src/ui/molecules/TimePicker/TimePicker.test.tsx +41 -0
  349. package/src/ui/molecules/TimePicker/TimePicker.tsx +264 -0
  350. package/src/ui/molecules/TimePicker/index.ts +2 -0
  351. package/src/ui/molecules/index.ts +20 -7
  352. package/src/ui/organisms/CommandPalette/CommandPalette.stories.tsx +218 -0
  353. package/src/ui/organisms/CommandPalette/CommandPalette.test.tsx +85 -0
  354. package/src/ui/organisms/CommandPalette/CommandPalette.tsx +333 -0
  355. package/src/ui/organisms/CommandPalette/index.ts +2 -0
  356. package/src/ui/organisms/DataGrid/DataGrid.stories.tsx +196 -0
  357. package/src/ui/organisms/DataGrid/DataGrid.test.tsx +53 -0
  358. package/src/ui/organisms/DataGrid/DataGrid.tsx +294 -0
  359. package/src/ui/organisms/DataGrid/index.ts +2 -0
  360. package/src/ui/organisms/Dialog/AlertDialog.tsx +92 -0
  361. package/src/ui/organisms/Dialog/Dialog.mdx +200 -0
  362. package/src/ui/organisms/Dialog/Dialog.stories.tsx +226 -0
  363. package/src/ui/organisms/Dialog/Dialog.test.tsx +435 -0
  364. package/src/ui/organisms/Dialog/Dialog.tsx +79 -0
  365. package/src/ui/organisms/Dialog/DialogClose.tsx +45 -0
  366. package/src/ui/organisms/Dialog/DialogContent.tsx +149 -0
  367. package/src/ui/organisms/Dialog/DialogContext.tsx +25 -0
  368. package/src/ui/organisms/Dialog/DialogDescription.tsx +28 -0
  369. package/src/ui/organisms/Dialog/DialogFooter.tsx +18 -0
  370. package/src/ui/organisms/Dialog/DialogHeader.tsx +18 -0
  371. package/src/ui/organisms/Dialog/DialogProvider.tsx +73 -0
  372. package/src/ui/organisms/Dialog/DialogTitle.tsx +31 -0
  373. package/src/ui/organisms/Dialog/DialogTrigger.tsx +34 -0
  374. package/src/ui/organisms/Dialog/index.ts +24 -0
  375. package/src/ui/organisms/LoginBox/LoginBox.stories.tsx +1 -1
  376. package/src/ui/organisms/Modal/Modal.stories.tsx +2 -2
  377. package/src/ui/organisms/Modal/Modal.test.tsx +1 -1
  378. package/src/ui/organisms/Sidebar/Sidebar.stories.tsx +1 -1
  379. package/src/ui/organisms/Sidebar/Sidebar.test.tsx +5 -3
  380. package/src/ui/organisms/Sidebar/Sidebar.tsx +21 -6
  381. package/src/ui/{molecules → organisms/Sidebar}/SidebarGroup/SidebarGroup.stories.tsx +2 -2
  382. package/src/ui/{molecules → organisms/Sidebar}/SidebarGroup/SidebarGroup.test.tsx +32 -9
  383. package/src/ui/{molecules → organisms/Sidebar}/SidebarGroup/SidebarGroup.tsx +7 -7
  384. package/src/ui/organisms/Sidebar/SidebarHeader/SidebarHeader.test.tsx +66 -0
  385. package/src/ui/{molecules → organisms/Sidebar}/SidebarHeader/SidebarHeader.tsx +1 -2
  386. package/src/ui/{atoms → organisms/Sidebar}/SidebarItem/SidebarItem.stories.tsx +1 -1
  387. package/src/ui/{atoms → organisms/Sidebar}/SidebarItem/SidebarItem.test.tsx +9 -8
  388. package/src/ui/{atoms → organisms/Sidebar}/SidebarItem/SidebarItem.tsx +9 -3
  389. package/src/ui/organisms/Sidebar/index.ts +13 -0
  390. package/src/ui/organisms/Stepper/Stepper.stories.tsx +253 -0
  391. package/src/ui/organisms/Stepper/Stepper.test.tsx +76 -0
  392. package/src/ui/organisms/Stepper/Stepper.tsx +323 -0
  393. package/src/ui/organisms/Stepper/index.ts +2 -0
  394. package/src/ui/organisms/Table/Table.mdx +154 -0
  395. package/src/ui/organisms/Table/Table.stories.tsx +614 -4
  396. package/src/ui/organisms/Table/Table.test.tsx +86 -4
  397. package/src/ui/organisms/Table/Table.tsx +215 -99
  398. package/src/ui/organisms/Table/TableActions/TableActions.stories.tsx +88 -0
  399. package/src/ui/organisms/Table/TableActions/TableActions.test.tsx +64 -0
  400. package/src/ui/organisms/Table/TableActions/TableActions.tsx +71 -0
  401. package/src/ui/organisms/Table/TableActions.tsx +46 -0
  402. package/src/ui/organisms/Table/TableBody.tsx +137 -0
  403. package/src/ui/organisms/Table/TableCell.tsx +36 -0
  404. package/src/ui/organisms/Table/TableContext.tsx +111 -0
  405. package/src/ui/organisms/Table/TableEmptyState.tsx +51 -0
  406. package/src/ui/organisms/Table/TableFilters/TableFilters.stories.tsx +111 -0
  407. package/src/ui/organisms/Table/TableFilters/TableFilters.test.tsx +104 -0
  408. package/src/ui/organisms/Table/TableFilters/TableFilters.tsx +191 -0
  409. package/src/ui/organisms/Table/TableFilters.tsx +39 -0
  410. package/src/ui/organisms/Table/TableHeader.tsx +29 -0
  411. package/src/ui/organisms/Table/TableHeaderCell.tsx +142 -0
  412. package/src/ui/organisms/Table/TableHeaderRow.tsx +72 -0
  413. package/src/ui/organisms/Table/TablePagination/TablePagination.stories.tsx +87 -0
  414. package/src/ui/organisms/Table/TablePagination/TablePagination.test.tsx +90 -0
  415. package/src/ui/organisms/Table/TablePagination/TablePagination.tsx +207 -0
  416. package/src/ui/organisms/Table/TablePagination.tsx +48 -0
  417. package/src/ui/organisms/Table/TableProvider.tsx +429 -0
  418. package/src/ui/organisms/Table/TableRow.tsx +85 -0
  419. package/src/ui/organisms/Table/TableTypes.ts +11 -0
  420. package/src/ui/organisms/Table/index.ts +55 -0
  421. package/src/ui/organisms/Table/useColumnResizing.ts +134 -0
  422. package/src/ui/organisms/Table/useVirtualScrolling.ts +116 -0
  423. package/src/ui/organisms/Timeline/Timeline.stories.tsx +230 -0
  424. package/src/ui/organisms/Timeline/Timeline.test.tsx +47 -0
  425. package/src/ui/organisms/Timeline/Timeline.tsx +179 -0
  426. package/src/ui/organisms/Timeline/index.ts +2 -0
  427. package/src/ui/organisms/Toast/Toast.stories.tsx +169 -0
  428. package/src/ui/organisms/Toast/Toast.test.tsx +537 -0
  429. package/src/ui/organisms/Toast/Toast.tsx +144 -0
  430. package/src/ui/organisms/Toast/ToastContainer.tsx +54 -0
  431. package/src/ui/organisms/Toast/ToastContext.tsx +38 -0
  432. package/src/ui/organisms/Toast/ToastProvider.tsx +56 -0
  433. package/src/ui/organisms/Toast/index.ts +17 -0
  434. package/src/ui/organisms/Toast/useToast.ts +70 -0
  435. package/src/ui/organisms/index.ts +17 -2
  436. package/src/ui/providers/AdvancedThemeProvider.tsx +229 -0
  437. package/src/ui/providers/index.ts +14 -0
  438. package/src/ui/themes/README.md +281 -0
  439. package/src/ui/themes/ThemeBuilder.ts +149 -0
  440. package/src/ui/themes/ThemeRegistry.ts +187 -0
  441. package/src/ui/themes/index.ts +20 -0
  442. package/src/ui/themes/types.ts +53 -0
  443. package/src/ui/themes/utils.ts +70 -0
  444. package/src/ui/tokens/README.md +212 -0
  445. package/src/ui/tokens/TokenVisualizations.tsx +273 -0
  446. package/src/ui/tokens/Tokens.mdx +348 -0
  447. package/src/ui/tokens/animations.ts +157 -0
  448. package/src/ui/tokens/borders.ts +121 -0
  449. package/src/ui/tokens/gradients.ts +154 -0
  450. package/src/ui/tokens/index.ts +57 -0
  451. package/src/ui/tokens/opacity.ts +107 -0
  452. package/src/ui/tokens/radius.ts +107 -0
  453. package/src/ui/tokens/shadows.ts +92 -0
  454. package/src/ui/tokens/tokens.factory.ts +124 -0
  455. package/src/ui/tokens/z-index.ts +113 -0
  456. package/src/ui/utils/index.ts +10 -0
  457. package/src/App.css +0 -42
  458. package/src/App.tsx +0 -35
  459. package/src/index.css +0 -68
  460. package/src/main.tsx +0 -15
@@ -0,0 +1,45 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { render, screen } from '@testing-library/react';
3
+ import Info from './Info';
4
+
5
+ describe('Info', () => {
6
+ it('renders info message', () => {
7
+ render(<Info>This is an info message</Info>);
8
+ expect(screen.getByText('This is an info message')).toBeInTheDocument();
9
+ });
10
+
11
+ it('has role="alert"', () => {
12
+ render(<Info>Info message</Info>);
13
+ const info = screen.getByRole('alert');
14
+ expect(info).toBeInTheDocument();
15
+ });
16
+
17
+ it('applies info variant classes by default', () => {
18
+ const { container } = render(<Info>Info message</Info>);
19
+ const info = container.querySelector('div[role="alert"]');
20
+ expect(info).toHaveClass('bg-blue-100', 'text-blue-800', 'border-blue-500');
21
+ });
22
+
23
+ it('applies warning variant classes', () => {
24
+ const { container } = render(<Info variant="warning">Warning message</Info>);
25
+ const info = container.querySelector('div[role="alert"]');
26
+ expect(info).toHaveClass('bg-yellow-100', 'text-yellow-800', 'border-yellow-500');
27
+ });
28
+
29
+ it('applies error variant classes', () => {
30
+ const { container } = render(<Info variant="error">Error message</Info>);
31
+ const info = container.querySelector('div[role="alert"]');
32
+ expect(info).toHaveClass('bg-red-100', 'text-red-800', 'border-red-500');
33
+ });
34
+
35
+ it('applies custom className', () => {
36
+ const { container } = render(<Info className="custom-class">Info</Info>);
37
+ const info = container.querySelector('div[role="alert"]');
38
+ expect(info).toHaveClass('custom-class');
39
+ });
40
+
41
+ it('passes through HTML attributes', () => {
42
+ render(<Info data-testid="info">Info message</Info>);
43
+ expect(screen.getByTestId('info')).toBeInTheDocument();
44
+ });
45
+ });
@@ -1,10 +1,10 @@
1
1
  import type { HTMLAttributes } from "react";
2
2
 
3
- interface Props extends HTMLAttributes<HTMLDivElement> {
3
+ export interface InfoProps extends HTMLAttributes<HTMLDivElement> {
4
4
  variant?: "info" | "warning" | "error";
5
5
  }
6
6
 
7
- export default function Info({ variant = "info", className, ...props }: Props) {
7
+ export default function Info({ variant = "info", className, ...props }: InfoProps) {
8
8
  const cls: string[] = [className || ""];
9
9
 
10
10
  switch (variant) {
@@ -1,5 +1,6 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
2
  import Input from './Input';
3
+ import { Mail, Search, Lock, User } from 'lucide-react';
3
4
 
4
5
  const meta: Meta<typeof Input> = {
5
6
  title: 'Atoms/Input',
@@ -12,6 +13,9 @@ const meta: Meta<typeof Input> = {
12
13
  error: {
13
14
  control: 'boolean',
14
15
  },
16
+ success: {
17
+ control: 'boolean',
18
+ },
15
19
  size: {
16
20
  control: 'select',
17
21
  options: ['sm', 'md', 'lg'],
@@ -23,6 +27,9 @@ const meta: Meta<typeof Input> = {
23
27
  disabled: {
24
28
  control: 'boolean',
25
29
  },
30
+ showClearButton: {
31
+ control: 'boolean',
32
+ },
26
33
  },
27
34
  };
28
35
 
@@ -47,6 +54,18 @@ export const WithError: Story = {
47
54
  },
48
55
  };
49
56
 
57
+ export const WithSuccess: Story = {
58
+ args: {
59
+ label: 'Email',
60
+ type: 'email',
61
+ placeholder: 'Enter your email',
62
+ success: true,
63
+ helperText: 'Email is valid',
64
+ value: 'user@example.com',
65
+ onChange: () => {},
66
+ },
67
+ };
68
+
50
69
  export const Sizes: Story = {
51
70
  render: () => (
52
71
  <div className="space-y-4">
@@ -67,6 +86,56 @@ export const Variants: Story = {
67
86
  ),
68
87
  };
69
88
 
89
+ export const WithIcons: Story = {
90
+ render: () => (
91
+ <div className="space-y-4">
92
+ <Input
93
+ label="Email"
94
+ leftIcon={<Mail className="h-4 w-4" />}
95
+ placeholder="Enter your email"
96
+ />
97
+ <Input
98
+ label="Search"
99
+ rightIcon={<Search className="h-4 w-4" />}
100
+ placeholder="Search..."
101
+ />
102
+ <Input
103
+ label="Username"
104
+ leftIcon={<User className="h-4 w-4" />}
105
+ rightIcon={<Search className="h-4 w-4" />}
106
+ placeholder="Enter username"
107
+ />
108
+ </div>
109
+ ),
110
+ };
111
+
112
+ export const WithClearButton: Story = {
113
+ args: {
114
+ label: 'Search',
115
+ placeholder: 'Type to search...',
116
+ showClearButton: true,
117
+ value: 'Search term',
118
+ onChange: () => {},
119
+ },
120
+ };
121
+
122
+ export const Password: Story = {
123
+ args: {
124
+ label: 'Password',
125
+ type: 'password',
126
+ placeholder: 'Enter your password',
127
+ },
128
+ };
129
+
130
+ export const PasswordWithIcon: Story = {
131
+ args: {
132
+ label: 'Password',
133
+ type: 'password',
134
+ placeholder: 'Enter your password',
135
+ leftIcon: <Lock className="h-4 w-4" />,
136
+ },
137
+ };
138
+
70
139
  export const Disabled: Story = {
71
140
  args: {
72
141
  label: 'Disabled Input',
@@ -74,3 +143,14 @@ export const Disabled: Story = {
74
143
  disabled: true,
75
144
  },
76
145
  };
146
+
147
+ export const AllStates: Story = {
148
+ render: () => (
149
+ <div className="space-y-4">
150
+ <Input label="Default" placeholder="Default state" />
151
+ <Input label="Error" error helperText="This field has an error" />
152
+ <Input label="Success" success helperText="This field is valid" value="Valid value" onChange={() => {}} />
153
+ <Input label="Disabled" disabled placeholder="Disabled input" />
154
+ </div>
155
+ ),
156
+ };
@@ -1,54 +1,208 @@
1
- import type { ChangeEvent } from "react";
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import Input from './Input';
4
+ import { Mail, Search } from 'lucide-react';
2
5
 
3
- import { fireEvent, render } from "@testing-library/react";
6
+ describe('Input', () => {
7
+ it('renders input', () => {
8
+ render(<Input placeholder="Enter text" />);
9
+ expect(screen.getByPlaceholderText('Enter text')).toBeInTheDocument();
10
+ });
11
+
12
+ it('renders with label', () => {
13
+ render(<Input label="Email" />);
14
+ expect(screen.getByText('Email')).toBeInTheDocument();
15
+ });
16
+
17
+ it('shows error state', () => {
18
+ const { container } = render(<Input error helperText="Error message" />);
19
+ const input = container.querySelector('input');
20
+ expect(input).toHaveAttribute('aria-invalid', 'true');
21
+ expect(screen.getByText('Error message')).toBeInTheDocument();
22
+ });
23
+
24
+ it('shows success state', () => {
25
+ render(<Input success helperText="Valid" />);
26
+ expect(screen.getByText('Valid')).toBeInTheDocument();
27
+ });
28
+
29
+ it('renders left icon', () => {
30
+ render(<Input leftIcon={<Mail data-testid="mail-icon" />} />);
31
+ expect(screen.getByTestId('mail-icon')).toBeInTheDocument();
32
+ });
4
33
 
5
- import Input from "./Input";
34
+ it('renders right icon', () => {
35
+ render(<Input rightIcon={<Search data-testid="search-icon" />} />);
36
+ expect(screen.getByTestId('search-icon')).toBeInTheDocument();
37
+ });
6
38
 
7
- describe("UI / atom / Input", () => {
8
- const PLACEHOLDER = "Hello from Input";
39
+ it('shows clear button when showClearButton and has value', () => {
40
+ render(<Input showClearButton value="test" onChange={vi.fn()} />);
41
+ const clearButton = screen.getByLabelText('Clear input');
42
+ expect(clearButton).toBeInTheDocument();
43
+ });
9
44
 
10
- it("should properly render", () => {
11
- const { getByPlaceholderText } = render(
12
- <Input placeholder={PLACEHOLDER} />,
13
- );
45
+ it('calls onClear when clear button clicked', () => {
46
+ const handleClear = vi.fn();
47
+ render(<Input showClearButton value="test" onChange={vi.fn()} onClear={handleClear} />);
48
+ fireEvent.click(screen.getByLabelText('Clear input'));
49
+ expect(handleClear).toHaveBeenCalledTimes(1);
50
+ });
14
51
 
15
- expect(getByPlaceholderText(PLACEHOLDER)).toBeInTheDocument();
52
+ it('toggles password visibility', () => {
53
+ const { container } = render(<Input type="password" />);
54
+ const input = container.querySelector('input');
55
+ const toggleButton = screen.getByLabelText('Show password');
56
+
57
+ expect(input).toHaveAttribute('type', 'password');
58
+ fireEvent.click(toggleButton);
59
+ expect(input).toHaveAttribute('type', 'text');
60
+ expect(screen.getByLabelText('Hide password')).toBeInTheDocument();
16
61
  });
17
62
 
18
- it("should display a default value", () => {
19
- const VALUE = "Hello from Input";
20
- const { getByDisplayValue } = render(<Input defaultValue={VALUE} />);
21
- expect(getByDisplayValue(VALUE)).toBeInTheDocument();
63
+ it('applies size classes', () => {
64
+ const { container } = render(<Input size="sm" />);
65
+ const input = container.querySelector('input');
66
+ expect(input).toHaveClass('h-8', 'text-sm');
67
+ });
68
+
69
+ it('applies variant classes', () => {
70
+ const { container } = render(<Input variant="filled" />);
71
+ const input = container.querySelector('input');
72
+ expect(input).toHaveClass('bg-gray-100');
73
+ });
74
+
75
+ describe('Keyboard Navigation', () => {
76
+ it('handles Enter key', () => {
77
+ const handleKeyDown = vi.fn();
78
+ render(<Input onKeyDown={handleKeyDown} />);
79
+ const input = screen.getByRole('textbox');
80
+ fireEvent.keyDown(input, { key: 'Enter' });
81
+ expect(handleKeyDown).toHaveBeenCalled();
82
+ });
83
+
84
+ it('handles Escape key to clear', () => {
85
+ const handleClear = vi.fn();
86
+ render(<Input showClearButton value="test" onChange={vi.fn()} onClear={handleClear} />);
87
+ const input = screen.getByRole('textbox');
88
+ fireEvent.keyDown(input, { key: 'Escape' });
89
+ // Escape might trigger clear if implemented
90
+ expect(input).toBeInTheDocument();
91
+ });
92
+
93
+ it('is focusable', () => {
94
+ render(<Input />);
95
+ const input = screen.getByRole('textbox');
96
+ input.focus();
97
+ expect(document.activeElement).toBe(input);
98
+ });
99
+
100
+ it('is not focusable when disabled', () => {
101
+ render(<Input disabled />);
102
+ const input = screen.getByRole('textbox');
103
+ expect(input).toBeDisabled();
104
+ // Disabled inputs are not focusable by default
105
+ input.focus();
106
+ expect(document.activeElement).not.toBe(input);
107
+ });
22
108
  });
23
109
 
24
- it("should emit an event when the value is changed", () => {
25
- const changeFn: (input: string) => void = vitest.fn();
26
- const CHANGE_TEXT = "Hello";
110
+ describe('Accessibility', () => {
111
+ it('has correct role', () => {
112
+ render(<Input />);
113
+ const input = screen.getByRole('textbox');
114
+ expect(input).toBeInTheDocument();
115
+ });
116
+
117
+ it('associates label with input', () => {
118
+ render(<Input label="Email" id="email" />);
119
+ const input = screen.getByLabelText('Email');
120
+ expect(input).toBeInTheDocument();
121
+ expect(input).toHaveAttribute('id', 'email');
122
+ });
123
+
124
+ it('has aria-invalid when error', () => {
125
+ render(<Input error />);
126
+ const input = screen.getByRole('textbox');
127
+ expect(input).toHaveAttribute('aria-invalid', 'true');
128
+ });
129
+
130
+ it('has aria-describedby when helperText is provided', () => {
131
+ render(<Input helperText="Helper text" id="test-input" />);
132
+ const input = screen.getByRole('textbox');
133
+ expect(input).toHaveAttribute('aria-describedby', 'test-input-helper');
134
+ });
135
+
136
+ it('has aria-describedby for error message', () => {
137
+ render(<Input error helperText="Error message" id="test-input" />);
138
+ const input = screen.getByRole('textbox');
139
+ expect(input).toHaveAttribute('aria-describedby', 'test-input-error');
140
+ });
27
141
 
28
- const { getByPlaceholderText } = render(
29
- <Input
30
- placeholder={PLACEHOLDER}
31
- onChange={(e: ChangeEvent<HTMLInputElement>) =>
32
- changeFn(e.target.value)
33
- }
34
- />,
35
- );
142
+ it('has aria-required when required', () => {
143
+ render(<Input required />);
144
+ const input = screen.getByRole('textbox');
145
+ expect(input).toHaveAttribute('aria-required', 'true');
146
+ expect(input).toHaveAttribute('required');
147
+ });
148
+
149
+ it('has aria-label when provided', () => {
150
+ render(<Input aria-label="Search input" />);
151
+ const input = screen.getByLabelText('Search input');
152
+ expect(input).toBeInTheDocument();
153
+ });
36
154
 
37
- fireEvent.change(getByPlaceholderText(PLACEHOLDER), {
38
- target: { value: CHANGE_TEXT },
155
+ it('has aria-placeholder when placeholder is provided', () => {
156
+ render(<Input placeholder="Enter text" />);
157
+ const input = screen.getByPlaceholderText('Enter text');
158
+ expect(input).toBeInTheDocument();
39
159
  });
40
160
 
41
- expect(changeFn).toHaveBeenCalledWith(CHANGE_TEXT);
161
+ it('has correct type attribute', () => {
162
+ const { container } = render(<Input type="email" />);
163
+ const input = container.querySelector('input');
164
+ expect(input).toHaveAttribute('type', 'email');
165
+ });
166
+
167
+ it('has correct autocomplete attribute', () => {
168
+ const { container } = render(<Input autoComplete="email" />);
169
+ const input = container.querySelector('input');
170
+ expect(input).toHaveAttribute('autocomplete', 'email');
171
+ });
42
172
  });
43
173
 
44
- it("should allow for change of the type", () => {
45
- const { getByPlaceholderText } = render(
46
- <Input placeholder={PLACEHOLDER} type="password" />,
47
- );
174
+ describe('Edge Cases', () => {
175
+ it('handles controlled input', () => {
176
+ const { rerender } = render(<Input value="initial" onChange={vi.fn()} />);
177
+ const input = screen.getByRole('textbox') as HTMLInputElement;
178
+ expect(input.value).toBe('initial');
179
+
180
+ rerender(<Input value="updated" onChange={vi.fn()} />);
181
+ expect(input.value).toBe('updated');
182
+ });
183
+
184
+ it('handles uncontrolled input', () => {
185
+ render(<Input defaultValue="default" />);
186
+ const input = screen.getByRole('textbox') as HTMLInputElement;
187
+ expect(input.value).toBe('default');
188
+ });
48
189
 
49
- expect(getByPlaceholderText(PLACEHOLDER)).toHaveAttribute(
50
- "type",
51
- "password",
52
- );
190
+ it('handles maxLength', () => {
191
+ render(<Input maxLength={10} />);
192
+ const input = screen.getByRole('textbox');
193
+ expect(input).toHaveAttribute('maxLength', '10');
194
+ });
195
+
196
+ it('handles minLength', () => {
197
+ render(<Input minLength={2} />);
198
+ const input = screen.getByRole('textbox');
199
+ expect(input).toHaveAttribute('minLength', '2');
200
+ });
201
+
202
+ it('handles pattern validation', () => {
203
+ render(<Input pattern="[0-9]*" />);
204
+ const input = screen.getByRole('textbox');
205
+ expect(input).toHaveAttribute('pattern', '[0-9]*');
206
+ });
53
207
  });
54
208
  });
@@ -1,21 +1,33 @@
1
1
  'use client';
2
2
 
3
+ import { forwardRef, useState } from 'react';
3
4
  import type { InputHTMLAttributes, ReactNode } from 'react';
4
5
  import { getTypographyClasses } from '../../tokens/typography';
5
6
  import { getColorClass } from '../../tokens/colors';
7
+ import { X, Eye, EyeOff } from 'lucide-react';
8
+ import Button from '../Button/Button';
9
+
10
+ export type InputSize = 'sm' | 'md' | 'lg';
11
+ export type InputVariant = 'default' | 'outlined' | 'filled';
12
+ export type InputState = 'default' | 'error' | 'success';
6
13
 
7
14
  export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {
8
15
  label?: ReactNode;
9
16
  error?: boolean;
17
+ success?: boolean;
10
18
  helperText?: string;
11
- size?: 'sm' | 'md' | 'lg';
12
- variant?: 'default' | 'outlined' | 'filled';
19
+ size?: InputSize;
20
+ variant?: InputVariant;
21
+ leftIcon?: ReactNode;
22
+ rightIcon?: ReactNode;
23
+ showClearButton?: boolean;
24
+ onClear?: () => void;
13
25
  }
14
26
 
15
27
  /**
16
28
  * Input Component
17
29
  *
18
- * A styled text input component with label and error support.
30
+ * A styled text input component with label, error/success states, icons, and clear button.
19
31
  * Follows Atomic Design principles as an Atom component.
20
32
  * Uses Composite Pattern when combined with Label and ErrorMessage.
21
33
  *
@@ -28,38 +40,66 @@ export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>,
28
40
  * placeholder="Enter your email"
29
41
  * error={hasError}
30
42
  * helperText={errorMessage}
43
+ * leftIcon={<MailIcon />}
31
44
  * />
32
45
  * ```
33
46
  */
34
- export default function Input({
47
+ const Input = forwardRef<HTMLInputElement, InputProps>(function Input({
35
48
  id,
36
49
  label,
37
50
  error = false,
51
+ success = false,
38
52
  helperText,
39
53
  size = 'md',
40
54
  variant = 'outlined',
55
+ leftIcon,
56
+ rightIcon,
57
+ showClearButton = false,
58
+ onClear,
41
59
  className = '',
42
60
  disabled = false,
61
+ type = 'text',
62
+ value,
63
+ onChange,
43
64
  ...props
44
- }: InputProps) {
65
+ }, ref) {
45
66
  const inputId = id || `input-${Math.random().toString(36).substr(2, 9)}`;
46
67
  const errorId = error ? `${inputId}-error` : undefined;
47
68
  const helperId = helperText ? `${inputId}-helper` : undefined;
69
+
70
+ // Password toggle state
71
+ const [showPassword, setShowPassword] = useState(false);
72
+ const isPassword = type === 'password';
73
+ const inputType = isPassword && showPassword ? 'text' : type;
74
+
75
+ // Determine state
76
+ const state: InputState = error ? 'error' : success ? 'success' : 'default';
77
+
78
+ // Determine if we should show clear button
79
+ const hasValue = value !== undefined && value !== null && value !== '';
80
+ const shouldShowClear = showClearButton && hasValue && !disabled;
48
81
 
49
82
  // Size classes
50
- const sizeClasses = {
83
+ const sizeClasses: Record<InputSize, string> = {
51
84
  sm: 'h-8 text-sm px-3',
52
85
  md: 'h-10 text-base px-4',
53
86
  lg: 'h-12 text-lg px-5',
54
87
  };
55
88
 
56
89
  // Variant classes
57
- const variantClasses = {
90
+ const variantClasses: Record<InputVariant, string> = {
58
91
  default: 'border-0 border-b-2 border-gray-300 focus:border-indigo-500',
59
92
  outlined: 'border border-gray-300 focus:border-indigo-500',
60
93
  filled: 'bg-gray-100 border-0 focus:bg-white focus:ring-2 focus:ring-indigo-500',
61
94
  };
62
95
 
96
+ // State classes
97
+ const stateClasses: Record<InputState, string> = {
98
+ default: '',
99
+ error: 'border-red-500 focus:border-red-500 focus:ring-red-500',
100
+ success: 'border-green-500 focus:border-green-500 focus:ring-green-500',
101
+ };
102
+
63
103
  const baseClasses = [
64
104
  'w-full',
65
105
  'rounded-md',
@@ -71,25 +111,47 @@ export default function Input({
71
111
  'disabled:cursor-not-allowed',
72
112
  sizeClasses[size],
73
113
  variantClasses[variant],
114
+ stateClasses[state],
74
115
  ];
75
116
 
76
- const errorClasses = error
77
- ? 'border-red-500 focus:border-red-500 focus:ring-red-500'
78
- : '';
117
+ // Add padding for icons
118
+ if (leftIcon) {
119
+ baseClasses.push(size === 'sm' ? 'pl-9' : size === 'lg' ? 'pl-12' : 'pl-10');
120
+ }
121
+ if (rightIcon || shouldShowClear || isPassword) {
122
+ baseClasses.push(size === 'sm' ? 'pr-9' : size === 'lg' ? 'pr-12' : 'pr-10');
123
+ }
79
124
 
80
125
  const inputClasses = [
81
126
  ...baseClasses,
82
- errorClasses,
83
127
  className,
84
128
  ].filter(Boolean).join(' ');
85
129
 
86
130
  const labelClasses = [
87
131
  'block',
88
132
  getTypographyClasses('label'),
89
- 'mb-1',
133
+ 'mb-1',
90
134
  disabled ? 'opacity-50' : '',
91
135
  ].filter(Boolean).join(' ');
92
136
 
137
+ const iconSize = size === 'sm' ? 'h-4 w-4' : size === 'lg' ? 'h-5 w-5' : 'h-4 w-4';
138
+ const iconPosition = size === 'sm' ? 'top-2' : size === 'lg' ? 'top-3.5' : 'top-2.5';
139
+
140
+ const handleClear = (e: React.MouseEvent) => {
141
+ e.stopPropagation();
142
+ if (onClear) {
143
+ onClear();
144
+ } else if (onChange) {
145
+ // Create synthetic event to clear input
146
+ const syntheticEvent = {
147
+ ...e,
148
+ target: { ...e.target, value: '' },
149
+ currentTarget: { ...e.currentTarget, value: '' },
150
+ } as React.ChangeEvent<HTMLInputElement>;
151
+ onChange(syntheticEvent);
152
+ }
153
+ };
154
+
93
155
  return (
94
156
  <div className="w-full">
95
157
  {label && (
@@ -100,23 +162,80 @@ export default function Input({
100
162
  {label}
101
163
  </label>
102
164
  )}
103
- <input
104
- id={inputId}
105
- className={inputClasses}
106
- disabled={disabled}
107
- aria-invalid={error}
108
- aria-describedby={errorId || helperId}
109
- {...props}
110
- />
111
- {(error || helperText) && (
165
+ <div className="relative">
166
+ {leftIcon && (
167
+ <div className={`absolute left-3 ${iconPosition} text-gray-400 pointer-events-none`}>
168
+ <div className={iconSize}>
169
+ {leftIcon}
170
+ </div>
171
+ </div>
172
+ )}
173
+ <input
174
+ id={inputId}
175
+ ref={ref}
176
+ type={inputType}
177
+ className={inputClasses}
178
+ disabled={disabled}
179
+ value={value}
180
+ onChange={onChange}
181
+ aria-invalid={error}
182
+ aria-required={props.required}
183
+ aria-describedby={errorId || helperId}
184
+ {...props}
185
+ />
186
+ <div className="absolute right-3 top-0 bottom-0 flex items-center gap-1">
187
+ {shouldShowClear && (
188
+ <Button
189
+ variant="ghost"
190
+ size="sm"
191
+ onClick={handleClear}
192
+ className="h-auto p-1"
193
+ aria-label="Clear input"
194
+ >
195
+ <X className={iconSize} />
196
+ </Button>
197
+ )}
198
+ {isPassword && (
199
+ <Button
200
+ variant="ghost"
201
+ size="sm"
202
+ onClick={() => setShowPassword(!showPassword)}
203
+ className="h-auto p-1"
204
+ aria-label={showPassword ? 'Hide password' : 'Show password'}
205
+ >
206
+ {showPassword ? (
207
+ <EyeOff className={iconSize} />
208
+ ) : (
209
+ <Eye className={iconSize} />
210
+ )}
211
+ </Button>
212
+ )}
213
+ {rightIcon && !shouldShowClear && !isPassword && (
214
+ <div className={`text-gray-400 pointer-events-none ${iconSize}`}>
215
+ {rightIcon}
216
+ </div>
217
+ )}
218
+ </div>
219
+ </div>
220
+ {(error || success || helperText) && (
112
221
  <div
113
222
  id={errorId || helperId}
114
- className={`mt-1 ${getTypographyClasses('caption')} ${error ? getColorClass('error', 'DEFAULT', 'text') : 'text-gray-500'}`}
115
- role={error ? 'alert' : undefined}
223
+ className={`mt-1 ${getTypographyClasses('caption')} ${
224
+ error
225
+ ? getColorClass('error', 'DEFAULT', 'text')
226
+ : success
227
+ ? getColorClass('success', 'DEFAULT', 'text')
228
+ : 'text-gray-500'
229
+ }`}
230
+ role={error || success ? 'alert' : undefined}
116
231
  >
117
- {error || helperText}
232
+ {helperText || (error ? 'Error' : success ? 'Success' : '')}
118
233
  </div>
119
234
  )}
120
235
  </div>
121
236
  );
122
- }
237
+ });
238
+
239
+ Input.displayName = 'Input';
240
+
241
+ export default Input;
@@ -3,7 +3,7 @@ import Label from "./Label";
3
3
  import { Input } from "../../atoms";
4
4
 
5
5
  const meta: Meta<typeof Label> = {
6
- title: "UI/Atoms/Label",
6
+ title: "Atoms/Label",
7
7
  component: Label,
8
8
  parameters: {
9
9
  docs: {