@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.
- package/README.md +48 -3
- package/dist/docs/components/ComponentStatusTable.d.ts +11 -0
- package/dist/ui/atoms/Accordion/Accordion.d.ts +34 -0
- package/dist/ui/atoms/Accordion/Accordion.stories.d.ts +11 -0
- package/dist/ui/atoms/Accordion/Accordion.test.d.ts +1 -0
- package/dist/ui/atoms/Accordion/index.d.ts +2 -0
- package/dist/ui/atoms/Avatar/Avatar.d.ts +30 -0
- package/dist/ui/atoms/Avatar/Avatar.stories.d.ts +13 -0
- package/dist/ui/atoms/Avatar/Avatar.test.d.ts +1 -0
- package/dist/ui/atoms/Avatar/AvatarGroup.d.ts +26 -0
- package/dist/ui/atoms/Avatar/index.d.ts +9 -0
- package/dist/ui/atoms/Badge/Badge.d.ts +14 -6
- package/dist/ui/atoms/Badge/Badge.stories.d.ts +8 -9
- package/dist/ui/atoms/BoxWrapper/BoxWrapper.d.ts +3 -3
- package/dist/ui/atoms/BoxWrapper/BoxWrapper.test.d.ts +1 -0
- package/dist/ui/atoms/Button/Button.d.ts +32 -10
- package/dist/ui/atoms/Button/Button.stories.d.ts +7 -0
- package/dist/ui/atoms/Button/Button.test.d.ts +1 -0
- package/dist/ui/atoms/Checkbox/Checkbox.d.ts +2 -1
- package/dist/ui/atoms/Collapsible/Collapsible.stories.d.ts +2 -0
- package/dist/ui/atoms/Info/Info.d.ts +2 -3
- package/dist/ui/atoms/Info/Info.test.d.ts +1 -0
- package/dist/ui/atoms/Input/Input.d.ts +14 -4
- package/dist/ui/atoms/Input/Input.stories.d.ts +6 -0
- package/dist/ui/atoms/Popover/Popover.d.ts +35 -0
- package/dist/ui/atoms/Popover/Popover.stories.d.ts +11 -0
- package/dist/ui/atoms/Popover/Popover.test.d.ts +1 -0
- package/dist/ui/atoms/Popover/index.d.ts +2 -0
- package/dist/ui/atoms/Progress/Progress.d.ts +33 -0
- package/dist/ui/atoms/Progress/Progress.stories.d.ts +12 -0
- package/dist/ui/atoms/Progress/Progress.test.d.ts +1 -0
- package/dist/ui/atoms/Select/Select.d.ts +18 -6
- package/dist/ui/atoms/Select/Select.stories.d.ts +11 -8
- package/dist/ui/atoms/Separator/Separator.d.ts +23 -0
- package/dist/ui/atoms/Separator/Separator.stories.d.ts +10 -0
- package/dist/ui/atoms/Separator/Separator.test.d.ts +1 -0
- package/dist/ui/atoms/Separator/index.d.ts +2 -0
- package/dist/ui/atoms/Skeleton/Skeleton.d.ts +1 -1
- package/dist/ui/atoms/Skeleton/Skeleton.stories.d.ts +24 -0
- package/dist/ui/atoms/Slider/Slider.d.ts +45 -0
- package/dist/ui/atoms/Slider/Slider.stories.d.ts +13 -0
- package/dist/ui/atoms/Slider/Slider.test.d.ts +1 -0
- package/dist/ui/atoms/Slider/index.d.ts +2 -0
- package/dist/ui/atoms/Spinner/Spinner.d.ts +22 -0
- package/dist/ui/atoms/Spinner/Spinner.stories.d.ts +9 -0
- package/dist/ui/atoms/Spinner/Spinner.test.d.ts +1 -0
- package/dist/ui/atoms/Switch/Switch.d.ts +28 -0
- package/dist/ui/atoms/Switch/Switch.stories.d.ts +11 -0
- package/dist/ui/atoms/Switch/Switch.test.d.ts +1 -0
- package/dist/ui/atoms/Switch/index.d.ts +2 -0
- package/dist/ui/atoms/Tooltip/Tooltip.d.ts +2 -1
- package/dist/ui/atoms/Tooltip/Tooltip.stories.d.ts +17 -0
- package/dist/ui/atoms/index.d.ts +20 -3
- package/dist/ui/index.d.ts +6 -1
- package/dist/ui/molecules/Card/Card.d.ts +6 -2
- package/dist/ui/molecules/Card/Card.stories.d.ts +2 -0
- package/dist/ui/molecules/ColorPicker/ColorPicker.d.ts +28 -0
- package/dist/ui/molecules/ColorPicker/ColorPicker.stories.d.ts +12 -0
- package/dist/ui/molecules/ColorPicker/ColorPicker.test.d.ts +1 -0
- package/dist/ui/molecules/ColorPicker/index.d.ts +2 -0
- package/dist/ui/molecules/DatePicker/DatePicker.d.ts +74 -0
- package/dist/ui/molecules/DatePicker/DatePicker.stories.d.ts +12 -0
- package/dist/ui/molecules/DatePicker/DatePicker.test.d.ts +1 -0
- package/dist/ui/molecules/DatePicker/DatePickerCalendar.d.ts +6 -0
- package/dist/ui/molecules/DatePicker/DatePickerContext.d.ts +28 -0
- package/dist/ui/molecules/DatePicker/DatePickerInput.d.ts +9 -0
- package/dist/ui/molecules/DatePicker/DatePickerProvider.d.ts +23 -0
- package/dist/ui/molecules/DatePicker/index.d.ts +14 -0
- package/dist/ui/molecules/Dropdown/Dropdown.d.ts +2 -1
- package/dist/ui/molecules/Dropdown/Dropdown.stories.d.ts +13 -0
- package/dist/ui/molecules/EmptyState/EmptyState.stories.d.ts +22 -0
- package/dist/ui/molecules/FileUpload/FileUpload.d.ts +37 -0
- package/dist/ui/molecules/FileUpload/FileUpload.stories.d.ts +12 -0
- package/dist/ui/molecules/FileUpload/FileUpload.test.d.ts +1 -0
- package/dist/ui/molecules/FileUpload/index.d.ts +2 -0
- package/dist/ui/molecules/Form/Form.d.ts +29 -4
- package/dist/ui/molecules/Form/Form.stories.d.ts +2 -0
- package/dist/ui/molecules/Form/FormContext.d.ts +17 -0
- package/dist/ui/molecules/Form/FormField.d.ts +36 -0
- package/dist/ui/molecules/Form/FormProvider.d.ts +14 -0
- package/dist/ui/molecules/Form/index.d.ts +13 -0
- package/dist/ui/molecules/Form/useFormFieldArray.d.ts +28 -0
- package/dist/ui/molecules/InputWithLabel/InputWithLabel.test.d.ts +1 -0
- package/dist/ui/molecules/Rating/Rating.d.ts +33 -0
- package/dist/ui/molecules/Rating/Rating.stories.d.ts +13 -0
- package/dist/ui/molecules/Rating/Rating.test.d.ts +1 -0
- package/dist/ui/molecules/Rating/index.d.ts +2 -0
- package/dist/ui/molecules/SearchInput/SearchInput.d.ts +24 -0
- package/dist/ui/molecules/SearchInput/SearchInput.stories.d.ts +10 -0
- package/dist/ui/molecules/SearchInput/SearchInput.test.d.ts +1 -0
- package/dist/ui/molecules/SearchInput/index.d.ts +2 -0
- package/dist/ui/molecules/SidebarHeader/SidebarHeader.test.d.ts +1 -0
- package/dist/ui/molecules/TableActions/TableActions.d.ts +31 -0
- package/dist/ui/molecules/TableActions/TableActions.stories.d.ts +7 -0
- package/dist/ui/molecules/TableActions/TableActions.test.d.ts +1 -0
- package/dist/ui/molecules/TableFilters/TableFilters.d.ts +37 -0
- package/dist/ui/molecules/TableFilters/TableFilters.stories.d.ts +7 -0
- package/dist/ui/molecules/TableFilters/TableFilters.test.d.ts +1 -0
- package/dist/ui/molecules/TablePagination/TablePagination.d.ts +29 -0
- package/dist/ui/molecules/TablePagination/TablePagination.stories.d.ts +8 -0
- package/dist/ui/molecules/TablePagination/TablePagination.test.d.ts +1 -0
- package/dist/ui/molecules/Tabs/Tabs.d.ts +15 -0
- package/dist/ui/molecules/Tabs/Tabs.stories.d.ts +10 -0
- package/dist/ui/molecules/Tabs/Tabs.test.d.ts +1 -0
- package/dist/ui/molecules/Tabs/TabsContent.d.ts +14 -0
- package/dist/ui/molecules/Tabs/TabsContext.d.ts +18 -0
- package/dist/ui/molecules/Tabs/TabsList.d.ts +12 -0
- package/dist/ui/molecules/Tabs/TabsProvider.d.ts +16 -0
- package/dist/ui/molecules/Tabs/TabsTrigger.d.ts +13 -0
- package/dist/ui/molecules/Tabs/index.d.ts +17 -0
- package/dist/ui/molecules/TimePicker/TimePicker.d.ts +29 -0
- package/dist/ui/molecules/TimePicker/TimePicker.stories.d.ts +12 -0
- package/dist/ui/molecules/TimePicker/TimePicker.test.d.ts +1 -0
- package/dist/ui/molecules/TimePicker/index.d.ts +2 -0
- package/dist/ui/molecules/index.d.ts +13 -5
- package/dist/ui/organisms/CommandPalette/CommandPalette.d.ts +37 -0
- package/dist/ui/organisms/CommandPalette/CommandPalette.stories.d.ts +11 -0
- package/dist/ui/organisms/CommandPalette/CommandPalette.test.d.ts +1 -0
- package/dist/ui/organisms/CommandPalette/index.d.ts +2 -0
- package/dist/ui/organisms/DataGrid/DataGrid.d.ts +84 -0
- package/dist/ui/organisms/DataGrid/DataGrid.stories.d.ts +14 -0
- package/dist/ui/organisms/DataGrid/DataGrid.test.d.ts +1 -0
- package/dist/ui/organisms/DataGrid/index.d.ts +2 -0
- package/dist/ui/organisms/Dialog/AlertDialog.d.ts +34 -0
- package/dist/ui/organisms/Dialog/Dialog.d.ts +58 -0
- package/dist/ui/organisms/Dialog/Dialog.stories.d.ts +13 -0
- package/dist/ui/organisms/Dialog/Dialog.test.d.ts +1 -0
- package/dist/ui/organisms/Dialog/DialogClose.d.ts +8 -0
- package/dist/ui/organisms/Dialog/DialogContent.d.ts +8 -0
- package/dist/ui/organisms/Dialog/DialogContext.d.ts +10 -0
- package/dist/ui/organisms/Dialog/DialogDescription.d.ts +4 -0
- package/dist/ui/organisms/Dialog/DialogFooter.d.ts +5 -0
- package/dist/ui/organisms/Dialog/DialogHeader.d.ts +5 -0
- package/dist/ui/organisms/Dialog/DialogProvider.d.ts +10 -0
- package/dist/ui/organisms/Dialog/DialogTitle.d.ts +5 -0
- package/dist/ui/organisms/Dialog/DialogTrigger.d.ts +6 -0
- package/dist/ui/organisms/Dialog/index.d.ts +22 -0
- package/dist/ui/organisms/Sidebar/Sidebar.d.ts +7 -4
- package/dist/ui/organisms/Sidebar/SidebarGroup/SidebarGroup.d.ts +27 -0
- package/dist/ui/organisms/Sidebar/SidebarGroup/SidebarGroup.stories.d.ts +11 -0
- package/dist/ui/organisms/Sidebar/SidebarGroup/SidebarGroup.test.d.ts +1 -0
- package/dist/ui/organisms/Sidebar/SidebarHeader/SidebarHeader.d.ts +19 -0
- package/dist/ui/organisms/Sidebar/SidebarHeader/SidebarHeader.test.d.ts +1 -0
- package/dist/ui/organisms/Sidebar/SidebarItem/SidebarItem.d.ts +23 -0
- package/dist/ui/organisms/Sidebar/SidebarItem/SidebarItem.stories.d.ts +10 -0
- package/dist/ui/organisms/Sidebar/SidebarItem/SidebarItem.test.d.ts +1 -0
- package/dist/ui/organisms/Sidebar/index.d.ts +8 -0
- package/dist/ui/organisms/Stepper/Stepper.d.ts +40 -0
- package/dist/ui/organisms/Stepper/Stepper.stories.d.ts +12 -0
- package/dist/ui/organisms/Stepper/Stepper.test.d.ts +1 -0
- package/dist/ui/organisms/Stepper/index.d.ts +2 -0
- package/dist/ui/organisms/Table/Table.d.ts +84 -16
- package/dist/ui/organisms/Table/Table.stories.d.ts +15 -0
- package/dist/ui/organisms/Table/TableActions/TableActions.d.ts +31 -0
- package/dist/ui/organisms/Table/TableActions/TableActions.stories.d.ts +7 -0
- package/dist/ui/organisms/Table/TableActions/TableActions.test.d.ts +1 -0
- package/dist/ui/organisms/Table/TableActions.d.ts +13 -0
- package/dist/ui/organisms/Table/TableBody.d.ts +12 -0
- package/dist/ui/organisms/Table/TableCell.d.ts +14 -0
- package/dist/ui/organisms/Table/TableContext.d.ts +75 -0
- package/dist/ui/organisms/Table/TableEmptyState.d.ts +11 -0
- package/dist/ui/organisms/Table/TableFilters/TableFilters.d.ts +37 -0
- package/dist/ui/organisms/Table/TableFilters/TableFilters.stories.d.ts +7 -0
- package/dist/ui/organisms/Table/TableFilters/TableFilters.test.d.ts +1 -0
- package/dist/ui/organisms/Table/TableFilters.d.ts +12 -0
- package/dist/ui/organisms/Table/TableHeader.d.ts +10 -0
- package/dist/ui/organisms/Table/TableHeaderCell.d.ts +18 -0
- package/dist/ui/organisms/Table/TableHeaderRow.d.ts +10 -0
- package/dist/ui/organisms/Table/TablePagination/TablePagination.d.ts +29 -0
- package/dist/ui/organisms/Table/TablePagination/TablePagination.stories.d.ts +8 -0
- package/dist/ui/organisms/Table/TablePagination/TablePagination.test.d.ts +1 -0
- package/dist/ui/organisms/Table/TablePagination.d.ts +14 -0
- package/dist/ui/organisms/Table/TableProvider.d.ts +55 -0
- package/dist/ui/organisms/Table/TableRow.d.ts +14 -0
- package/dist/ui/organisms/Table/TableTypes.d.ts +8 -0
- package/dist/ui/organisms/Table/index.d.ts +30 -0
- package/dist/ui/organisms/Table/useColumnResizing.d.ts +39 -0
- package/dist/ui/organisms/Table/useVirtualScrolling.d.ts +35 -0
- package/dist/ui/organisms/Timeline/Timeline.d.ts +34 -0
- package/dist/ui/organisms/Timeline/Timeline.stories.d.ts +12 -0
- package/dist/ui/organisms/Timeline/Timeline.test.d.ts +1 -0
- package/dist/ui/organisms/Timeline/index.d.ts +2 -0
- package/dist/ui/organisms/Toast/Toast.d.ts +8 -0
- package/dist/ui/organisms/Toast/Toast.stories.d.ts +14 -0
- package/dist/ui/organisms/Toast/Toast.test.d.ts +1 -0
- package/dist/ui/organisms/Toast/ToastContainer.d.ts +5 -0
- package/dist/ui/organisms/Toast/ToastContext.d.ts +21 -0
- package/dist/ui/organisms/Toast/ToastProvider.d.ts +7 -0
- package/dist/ui/organisms/Toast/index.d.ts +15 -0
- package/dist/ui/organisms/Toast/useToast.d.ts +35 -0
- package/dist/ui/organisms/index.d.ts +12 -2
- package/dist/ui/providers/AdvancedThemeProvider.d.ts +52 -0
- package/dist/ui/providers/index.d.ts +9 -0
- package/dist/ui/themes/ThemeBuilder.d.ts +28 -0
- package/dist/ui/themes/ThemeRegistry.d.ts +55 -0
- package/dist/ui/themes/index.d.ts +9 -0
- package/dist/ui/themes/types.d.ts +48 -0
- package/dist/ui/themes/utils.d.ts +21 -0
- package/dist/ui/tokens/TokenVisualizations.d.ts +41 -0
- package/dist/ui/tokens/animations.d.ts +65 -0
- package/dist/ui/tokens/borders.d.ts +61 -0
- package/dist/ui/tokens/gradients.d.ts +55 -0
- package/dist/ui/tokens/index.d.ts +31 -0
- package/dist/ui/tokens/opacity.d.ts +51 -0
- package/dist/ui/tokens/radius.d.ts +45 -0
- package/dist/ui/tokens/shadows.d.ts +42 -0
- package/dist/ui/tokens/themes/dark.d.ts +26 -26
- package/dist/ui/tokens/themes/light.d.ts +26 -26
- package/dist/ui/tokens/tokens.factory.d.ts +42 -0
- package/dist/ui/tokens/z-index.d.ts +44 -0
- package/dist/ui/utils/index.d.ts +6 -0
- package/package.json +50 -6
- package/src/docs/Accessibility.mdx +402 -0
- package/src/docs/BestPractices.mdx +315 -0
- package/src/docs/ComponentComposition.mdx +381 -0
- package/src/docs/ComponentStatus.mdx +177 -0
- package/src/docs/DesignSystem.mdx +121 -0
- package/src/docs/GettingStarted.mdx +284 -0
- package/src/docs/MigrationGuide.mdx +297 -0
- package/src/docs/Performance.mdx +206 -0
- package/src/docs/components/ComponentStatusTable.tsx +184 -0
- package/src/setupTests.ts +32 -0
- package/src/ui/atoms/Accordion/Accordion.stories.tsx +147 -0
- package/src/ui/atoms/Accordion/Accordion.test.tsx +86 -0
- package/src/ui/atoms/Accordion/Accordion.tsx +147 -0
- package/src/ui/atoms/Accordion/index.ts +2 -0
- package/src/ui/atoms/Avatar/Avatar.stories.tsx +226 -0
- package/src/ui/atoms/Avatar/Avatar.test.tsx +233 -0
- package/src/ui/atoms/Avatar/Avatar.tsx +128 -0
- package/src/ui/atoms/Avatar/AvatarGroup.tsx +96 -0
- package/src/ui/atoms/Avatar/index.ts +11 -0
- package/src/ui/atoms/Badge/Badge.stories.tsx +65 -56
- package/src/ui/atoms/Badge/Badge.test.tsx +27 -50
- package/src/ui/atoms/Badge/Badge.tsx +70 -27
- package/src/ui/atoms/BoxWrapper/BoxWrapper.stories.tsx +1 -1
- package/src/ui/atoms/BoxWrapper/BoxWrapper.test.tsx +27 -0
- package/src/ui/atoms/BoxWrapper/BoxWrapper.tsx +5 -2
- package/src/ui/atoms/Button/Button.stories.tsx +130 -1
- package/src/ui/atoms/Button/Button.test.tsx +233 -0
- package/src/ui/atoms/Button/Button.tsx +160 -53
- package/src/ui/atoms/Checkbox/Checkbox.tsx +14 -1
- package/src/ui/atoms/Collapsible/Collapsible.stories.tsx +47 -1
- package/src/ui/atoms/Collapsible/Collapsible.test.tsx +36 -24
- package/src/ui/atoms/Collapsible/Collapsible.tsx +9 -1
- package/src/ui/atoms/ErrorMessage/ErrorMessage.stories.tsx +1 -1
- package/src/ui/atoms/Info/Info.stories.tsx +1 -1
- package/src/ui/atoms/Info/Info.test.tsx +45 -0
- package/src/ui/atoms/Info/Info.tsx +2 -2
- package/src/ui/atoms/Input/Input.stories.tsx +80 -0
- package/src/ui/atoms/Input/Input.test.tsx +190 -36
- package/src/ui/atoms/Input/Input.tsx +144 -25
- package/src/ui/atoms/Label/Label.stories.tsx +1 -1
- package/src/ui/atoms/NavLink/NavLink.stories.tsx +1 -1
- package/src/ui/atoms/Popover/Popover.stories.tsx +157 -0
- package/src/ui/atoms/Popover/Popover.test.tsx +80 -0
- package/src/ui/atoms/Popover/Popover.tsx +256 -0
- package/src/ui/atoms/Popover/index.ts +2 -0
- package/src/ui/atoms/Progress/Progress.css +17 -0
- package/src/ui/atoms/Progress/Progress.stories.tsx +170 -0
- package/src/ui/atoms/Progress/Progress.test.tsx +134 -0
- package/src/ui/atoms/Progress/Progress.tsx +138 -0
- package/src/ui/atoms/Radio/Radio.tsx +1 -1
- package/src/ui/atoms/Select/Select.stories.tsx +93 -58
- package/src/ui/atoms/Select/Select.test.tsx +162 -46
- package/src/ui/atoms/Select/Select.tsx +142 -44
- package/src/ui/atoms/Separator/Separator.stories.tsx +88 -0
- package/src/ui/atoms/Separator/Separator.test.tsx +34 -0
- package/src/ui/atoms/Separator/Separator.tsx +81 -0
- package/src/ui/atoms/Separator/index.ts +2 -0
- package/src/ui/atoms/Skeleton/Skeleton.stories.tsx +62 -0
- package/src/ui/atoms/Skeleton/Skeleton.tsx +19 -2
- package/src/ui/atoms/Slider/Slider.stories.tsx +205 -0
- package/src/ui/atoms/Slider/Slider.test.tsx +53 -0
- package/src/ui/atoms/Slider/Slider.tsx +307 -0
- package/src/ui/atoms/Slider/index.ts +2 -0
- package/src/ui/atoms/Spinner/Spinner.stories.tsx +56 -0
- package/src/ui/atoms/Spinner/Spinner.test.tsx +35 -0
- package/src/ui/atoms/Spinner/Spinner.tsx +88 -0
- package/src/ui/atoms/Switch/Switch.stories.tsx +182 -0
- package/src/ui/atoms/Switch/Switch.test.tsx +90 -0
- package/src/ui/atoms/Switch/Switch.tsx +181 -0
- package/src/ui/atoms/Switch/index.ts +2 -0
- package/src/ui/atoms/Text/Text.stories.tsx +1 -1
- package/src/ui/atoms/Text/Text.test.tsx +48 -32
- package/src/ui/atoms/Textarea/Textarea.stories.tsx +1 -1
- package/src/ui/atoms/Tooltip/Tooltip.stories.tsx +44 -0
- package/src/ui/atoms/Tooltip/Tooltip.tsx +94 -6
- package/src/ui/atoms/index.ts +27 -4
- package/src/ui/index.ts +6 -1
- package/src/ui/molecules/Breadcrumb/Breadcrumb.stories.tsx +1 -1
- package/src/ui/molecules/Breadcrumb/Breadcrumb.tsx +1 -1
- package/src/ui/molecules/Card/Card.stories.tsx +49 -1
- package/src/ui/molecules/Card/Card.tsx +40 -5
- package/src/ui/molecules/ColorPicker/ColorPicker.stories.tsx +156 -0
- package/src/ui/molecules/ColorPicker/ColorPicker.test.tsx +47 -0
- package/src/ui/molecules/ColorPicker/ColorPicker.tsx +271 -0
- package/src/ui/molecules/ColorPicker/index.ts +2 -0
- package/src/ui/molecules/DatePicker/DatePicker.mdx +150 -0
- package/src/ui/molecules/DatePicker/DatePicker.stories.tsx +188 -0
- package/src/ui/molecules/DatePicker/DatePicker.test.tsx +381 -0
- package/src/ui/molecules/DatePicker/DatePicker.tsx +231 -0
- package/src/ui/molecules/DatePicker/DatePickerCalendar.tsx +277 -0
- package/src/ui/molecules/DatePicker/DatePickerContext.tsx +39 -0
- package/src/ui/molecules/DatePicker/DatePickerInput.tsx +147 -0
- package/src/ui/molecules/DatePicker/DatePickerProvider.tsx +100 -0
- package/src/ui/molecules/DatePicker/index.ts +16 -0
- package/src/ui/molecules/Dropdown/Dropdown.stories.tsx +50 -8
- package/src/ui/molecules/Dropdown/Dropdown.test.tsx +272 -12
- package/src/ui/molecules/Dropdown/Dropdown.tsx +176 -10
- package/src/ui/molecules/EmptyState/EmptyState.stories.tsx +24 -2
- package/src/ui/molecules/EmptyState/EmptyState.tsx +9 -3
- package/src/ui/molecules/FileUpload/FileUpload.stories.tsx +177 -0
- package/src/ui/molecules/FileUpload/FileUpload.test.tsx +114 -0
- package/src/ui/molecules/FileUpload/FileUpload.tsx +312 -0
- package/src/ui/molecules/FileUpload/index.ts +2 -0
- package/src/ui/molecules/Form/Form.mdx +145 -0
- package/src/ui/molecules/Form/Form.stories.tsx +121 -1
- package/src/ui/molecules/Form/Form.test.tsx +1 -3
- package/src/ui/molecules/Form/Form.tsx +95 -15
- package/src/ui/molecules/Form/FormContext.tsx +35 -0
- package/src/ui/molecules/Form/FormField.tsx +83 -0
- package/src/ui/molecules/Form/FormProvider.tsx +34 -0
- package/src/ui/molecules/Form/index.ts +21 -0
- package/src/ui/molecules/Form/useFormFieldArray.ts +46 -0
- package/src/ui/molecules/InputWithLabel/InputWithLabel.stories.tsx +1 -1
- package/src/ui/molecules/InputWithLabel/InputWithLabel.test.tsx +44 -0
- package/src/ui/molecules/InputWithLabel/InputWithLabel.tsx +3 -1
- package/src/ui/molecules/NavbarGroup/NavbarGroup.stories.tsx +1 -1
- package/src/ui/molecules/Pagination/Pagination.stories.tsx +1 -1
- package/src/ui/molecules/Rating/Rating.stories.tsx +206 -0
- package/src/ui/molecules/Rating/Rating.test.tsx +60 -0
- package/src/ui/molecules/Rating/Rating.tsx +173 -0
- package/src/ui/molecules/Rating/index.ts +2 -0
- package/src/ui/molecules/SearchInput/SearchInput.stories.tsx +146 -0
- package/src/ui/molecules/SearchInput/SearchInput.test.tsx +82 -0
- package/src/ui/molecules/SearchInput/SearchInput.tsx +133 -0
- package/src/ui/molecules/SearchInput/index.ts +2 -0
- package/src/ui/molecules/Tabs/Tabs.stories.tsx +229 -0
- package/src/ui/molecules/Tabs/Tabs.test.tsx +497 -0
- package/src/ui/molecules/Tabs/Tabs.tsx +58 -0
- package/src/ui/molecules/Tabs/TabsContent.tsx +50 -0
- package/src/ui/molecules/Tabs/TabsContext.tsx +36 -0
- package/src/ui/molecules/Tabs/TabsList.tsx +98 -0
- package/src/ui/molecules/Tabs/TabsProvider.tsx +53 -0
- package/src/ui/molecules/Tabs/TabsTrigger.tsx +111 -0
- package/src/ui/molecules/Tabs/index.ts +23 -0
- package/src/ui/molecules/TimePicker/TimePicker.stories.tsx +145 -0
- package/src/ui/molecules/TimePicker/TimePicker.test.tsx +41 -0
- package/src/ui/molecules/TimePicker/TimePicker.tsx +264 -0
- package/src/ui/molecules/TimePicker/index.ts +2 -0
- package/src/ui/molecules/index.ts +20 -7
- package/src/ui/organisms/CommandPalette/CommandPalette.stories.tsx +218 -0
- package/src/ui/organisms/CommandPalette/CommandPalette.test.tsx +85 -0
- package/src/ui/organisms/CommandPalette/CommandPalette.tsx +333 -0
- package/src/ui/organisms/CommandPalette/index.ts +2 -0
- package/src/ui/organisms/DataGrid/DataGrid.stories.tsx +196 -0
- package/src/ui/organisms/DataGrid/DataGrid.test.tsx +53 -0
- package/src/ui/organisms/DataGrid/DataGrid.tsx +294 -0
- package/src/ui/organisms/DataGrid/index.ts +2 -0
- package/src/ui/organisms/Dialog/AlertDialog.tsx +92 -0
- package/src/ui/organisms/Dialog/Dialog.mdx +200 -0
- package/src/ui/organisms/Dialog/Dialog.stories.tsx +226 -0
- package/src/ui/organisms/Dialog/Dialog.test.tsx +435 -0
- package/src/ui/organisms/Dialog/Dialog.tsx +79 -0
- package/src/ui/organisms/Dialog/DialogClose.tsx +45 -0
- package/src/ui/organisms/Dialog/DialogContent.tsx +149 -0
- package/src/ui/organisms/Dialog/DialogContext.tsx +25 -0
- package/src/ui/organisms/Dialog/DialogDescription.tsx +28 -0
- package/src/ui/organisms/Dialog/DialogFooter.tsx +18 -0
- package/src/ui/organisms/Dialog/DialogHeader.tsx +18 -0
- package/src/ui/organisms/Dialog/DialogProvider.tsx +73 -0
- package/src/ui/organisms/Dialog/DialogTitle.tsx +31 -0
- package/src/ui/organisms/Dialog/DialogTrigger.tsx +34 -0
- package/src/ui/organisms/Dialog/index.ts +24 -0
- package/src/ui/organisms/LoginBox/LoginBox.stories.tsx +1 -1
- package/src/ui/organisms/Modal/Modal.stories.tsx +2 -2
- package/src/ui/organisms/Modal/Modal.test.tsx +1 -1
- package/src/ui/organisms/Sidebar/Sidebar.stories.tsx +1 -1
- package/src/ui/organisms/Sidebar/Sidebar.test.tsx +5 -3
- package/src/ui/organisms/Sidebar/Sidebar.tsx +21 -6
- package/src/ui/{molecules → organisms/Sidebar}/SidebarGroup/SidebarGroup.stories.tsx +2 -2
- package/src/ui/{molecules → organisms/Sidebar}/SidebarGroup/SidebarGroup.test.tsx +32 -9
- package/src/ui/{molecules → organisms/Sidebar}/SidebarGroup/SidebarGroup.tsx +7 -7
- package/src/ui/organisms/Sidebar/SidebarHeader/SidebarHeader.test.tsx +66 -0
- package/src/ui/{molecules → organisms/Sidebar}/SidebarHeader/SidebarHeader.tsx +1 -2
- package/src/ui/{atoms → organisms/Sidebar}/SidebarItem/SidebarItem.stories.tsx +1 -1
- package/src/ui/{atoms → organisms/Sidebar}/SidebarItem/SidebarItem.test.tsx +9 -8
- package/src/ui/{atoms → organisms/Sidebar}/SidebarItem/SidebarItem.tsx +9 -3
- package/src/ui/organisms/Sidebar/index.ts +13 -0
- package/src/ui/organisms/Stepper/Stepper.stories.tsx +253 -0
- package/src/ui/organisms/Stepper/Stepper.test.tsx +76 -0
- package/src/ui/organisms/Stepper/Stepper.tsx +323 -0
- package/src/ui/organisms/Stepper/index.ts +2 -0
- package/src/ui/organisms/Table/Table.mdx +154 -0
- package/src/ui/organisms/Table/Table.stories.tsx +614 -4
- package/src/ui/organisms/Table/Table.test.tsx +86 -4
- package/src/ui/organisms/Table/Table.tsx +215 -99
- package/src/ui/organisms/Table/TableActions/TableActions.stories.tsx +88 -0
- package/src/ui/organisms/Table/TableActions/TableActions.test.tsx +64 -0
- package/src/ui/organisms/Table/TableActions/TableActions.tsx +71 -0
- package/src/ui/organisms/Table/TableActions.tsx +46 -0
- package/src/ui/organisms/Table/TableBody.tsx +137 -0
- package/src/ui/organisms/Table/TableCell.tsx +36 -0
- package/src/ui/organisms/Table/TableContext.tsx +111 -0
- package/src/ui/organisms/Table/TableEmptyState.tsx +51 -0
- package/src/ui/organisms/Table/TableFilters/TableFilters.stories.tsx +111 -0
- package/src/ui/organisms/Table/TableFilters/TableFilters.test.tsx +104 -0
- package/src/ui/organisms/Table/TableFilters/TableFilters.tsx +191 -0
- package/src/ui/organisms/Table/TableFilters.tsx +39 -0
- package/src/ui/organisms/Table/TableHeader.tsx +29 -0
- package/src/ui/organisms/Table/TableHeaderCell.tsx +142 -0
- package/src/ui/organisms/Table/TableHeaderRow.tsx +72 -0
- package/src/ui/organisms/Table/TablePagination/TablePagination.stories.tsx +87 -0
- package/src/ui/organisms/Table/TablePagination/TablePagination.test.tsx +90 -0
- package/src/ui/organisms/Table/TablePagination/TablePagination.tsx +207 -0
- package/src/ui/organisms/Table/TablePagination.tsx +48 -0
- package/src/ui/organisms/Table/TableProvider.tsx +429 -0
- package/src/ui/organisms/Table/TableRow.tsx +85 -0
- package/src/ui/organisms/Table/TableTypes.ts +11 -0
- package/src/ui/organisms/Table/index.ts +55 -0
- package/src/ui/organisms/Table/useColumnResizing.ts +134 -0
- package/src/ui/organisms/Table/useVirtualScrolling.ts +116 -0
- package/src/ui/organisms/Timeline/Timeline.stories.tsx +230 -0
- package/src/ui/organisms/Timeline/Timeline.test.tsx +47 -0
- package/src/ui/organisms/Timeline/Timeline.tsx +179 -0
- package/src/ui/organisms/Timeline/index.ts +2 -0
- package/src/ui/organisms/Toast/Toast.stories.tsx +169 -0
- package/src/ui/organisms/Toast/Toast.test.tsx +537 -0
- package/src/ui/organisms/Toast/Toast.tsx +144 -0
- package/src/ui/organisms/Toast/ToastContainer.tsx +54 -0
- package/src/ui/organisms/Toast/ToastContext.tsx +38 -0
- package/src/ui/organisms/Toast/ToastProvider.tsx +56 -0
- package/src/ui/organisms/Toast/index.ts +17 -0
- package/src/ui/organisms/Toast/useToast.ts +70 -0
- package/src/ui/organisms/index.ts +17 -2
- package/src/ui/providers/AdvancedThemeProvider.tsx +229 -0
- package/src/ui/providers/index.ts +14 -0
- package/src/ui/themes/README.md +281 -0
- package/src/ui/themes/ThemeBuilder.ts +149 -0
- package/src/ui/themes/ThemeRegistry.ts +187 -0
- package/src/ui/themes/index.ts +20 -0
- package/src/ui/themes/types.ts +53 -0
- package/src/ui/themes/utils.ts +70 -0
- package/src/ui/tokens/README.md +212 -0
- package/src/ui/tokens/TokenVisualizations.tsx +273 -0
- package/src/ui/tokens/Tokens.mdx +348 -0
- package/src/ui/tokens/animations.ts +157 -0
- package/src/ui/tokens/borders.ts +121 -0
- package/src/ui/tokens/gradients.ts +154 -0
- package/src/ui/tokens/index.ts +57 -0
- package/src/ui/tokens/opacity.ts +107 -0
- package/src/ui/tokens/radius.ts +107 -0
- package/src/ui/tokens/shadows.ts +92 -0
- package/src/ui/tokens/tokens.factory.ts +124 -0
- package/src/ui/tokens/z-index.ts +113 -0
- package/src/ui/utils/index.ts +10 -0
- package/src/App.css +0 -42
- package/src/App.tsx +0 -35
- package/src/index.css +0 -68
- 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
|
|
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 }:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8
|
-
|
|
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(
|
|
11
|
-
const
|
|
12
|
-
|
|
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
|
-
|
|
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(
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
expect(
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
29
|
-
<Input
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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?:
|
|
12
|
-
variant?:
|
|
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
|
|
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
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
77
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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')} ${
|
|
115
|
-
|
|
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
|
-
{
|
|
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;
|