@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,497 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { render, screen, fireEvent, waitFor, act } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import Tabs from './Tabs';
|
|
5
|
+
|
|
6
|
+
describe('Tabs', () => {
|
|
7
|
+
describe('Rendering', () => {
|
|
8
|
+
it('renders tabs with triggers and content', () => {
|
|
9
|
+
render(
|
|
10
|
+
<Tabs defaultValue="tab1">
|
|
11
|
+
<Tabs.List>
|
|
12
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
13
|
+
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
14
|
+
</Tabs.List>
|
|
15
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
16
|
+
<Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
17
|
+
</Tabs>
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
expect(screen.getByText('Tab 1')).toBeInTheDocument();
|
|
21
|
+
expect(screen.getByText('Tab 2')).toBeInTheDocument();
|
|
22
|
+
expect(screen.getByText('Content 1')).toBeInTheDocument();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('shows only active tab content', () => {
|
|
26
|
+
render(
|
|
27
|
+
<Tabs defaultValue="tab1">
|
|
28
|
+
<Tabs.List>
|
|
29
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
30
|
+
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
31
|
+
</Tabs.List>
|
|
32
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
33
|
+
<Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
34
|
+
</Tabs>
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
expect(screen.getByText('Content 1')).toBeInTheDocument();
|
|
38
|
+
expect(screen.queryByText('Content 2')).not.toBeInTheDocument();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('renders all content when forceMount is true', () => {
|
|
42
|
+
render(
|
|
43
|
+
<Tabs defaultValue="tab1">
|
|
44
|
+
<Tabs.List>
|
|
45
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
46
|
+
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
47
|
+
</Tabs.List>
|
|
48
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
49
|
+
<Tabs.Content value="tab2" forceMount>Content 2</Tabs.Content>
|
|
50
|
+
</Tabs>
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
expect(screen.getByText('Content 1')).toBeInTheDocument();
|
|
54
|
+
expect(screen.getByText('Content 2')).toBeInTheDocument();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('Tab Switching', () => {
|
|
59
|
+
it('switches to different tab when clicked', async () => {
|
|
60
|
+
const user = userEvent.setup();
|
|
61
|
+
render(
|
|
62
|
+
<Tabs defaultValue="tab1">
|
|
63
|
+
<Tabs.List>
|
|
64
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
65
|
+
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
66
|
+
</Tabs.List>
|
|
67
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
68
|
+
<Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
69
|
+
</Tabs>
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
expect(screen.getByText('Content 1')).toBeInTheDocument();
|
|
73
|
+
expect(screen.queryByText('Content 2')).not.toBeInTheDocument();
|
|
74
|
+
|
|
75
|
+
const tab2 = screen.getByText('Tab 2');
|
|
76
|
+
await user.click(tab2);
|
|
77
|
+
|
|
78
|
+
await waitFor(() => {
|
|
79
|
+
expect(screen.queryByText('Content 1')).not.toBeInTheDocument();
|
|
80
|
+
expect(screen.getByText('Content 2')).toBeInTheDocument();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('calls onValueChange when tab is switched', async () => {
|
|
85
|
+
const user = userEvent.setup();
|
|
86
|
+
const handleChange = vi.fn();
|
|
87
|
+
render(
|
|
88
|
+
<Tabs defaultValue="tab1" onValueChange={handleChange}>
|
|
89
|
+
<Tabs.List>
|
|
90
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
91
|
+
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
92
|
+
</Tabs.List>
|
|
93
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
94
|
+
<Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
95
|
+
</Tabs>
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const tab2 = screen.getByText('Tab 2');
|
|
99
|
+
await user.click(tab2);
|
|
100
|
+
|
|
101
|
+
await waitFor(() => {
|
|
102
|
+
expect(handleChange).toHaveBeenCalledWith('tab2');
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('Keyboard Navigation', () => {
|
|
108
|
+
it('navigates with ArrowRight in horizontal mode', async () => {
|
|
109
|
+
render(
|
|
110
|
+
<Tabs defaultValue="tab1">
|
|
111
|
+
<Tabs.List>
|
|
112
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
113
|
+
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
114
|
+
<Tabs.Trigger value="tab3">Tab 3</Tabs.Trigger>
|
|
115
|
+
</Tabs.List>
|
|
116
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
117
|
+
<Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
118
|
+
<Tabs.Content value="tab3">Content 3</Tabs.Content>
|
|
119
|
+
</Tabs>
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const tab1 = screen.getByText('Tab 1');
|
|
123
|
+
tab1.focus();
|
|
124
|
+
|
|
125
|
+
fireEvent.keyDown(tab1, { key: 'ArrowRight' });
|
|
126
|
+
|
|
127
|
+
await waitFor(() => {
|
|
128
|
+
const tab2 = screen.getByText('Tab 2');
|
|
129
|
+
expect(document.activeElement).toBe(tab2);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('navigates with ArrowLeft in horizontal mode', async () => {
|
|
134
|
+
render(
|
|
135
|
+
<Tabs defaultValue="tab2">
|
|
136
|
+
<Tabs.List>
|
|
137
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
138
|
+
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
139
|
+
<Tabs.Trigger value="tab3">Tab 3</Tabs.Trigger>
|
|
140
|
+
</Tabs.List>
|
|
141
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
142
|
+
<Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
143
|
+
<Tabs.Content value="tab3">Content 3</Tabs.Content>
|
|
144
|
+
</Tabs>
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const tab2 = screen.getByText('Tab 2');
|
|
148
|
+
tab2.focus();
|
|
149
|
+
|
|
150
|
+
fireEvent.keyDown(tab2, { key: 'ArrowLeft' });
|
|
151
|
+
|
|
152
|
+
await waitFor(() => {
|
|
153
|
+
const tab1 = screen.getByText('Tab 1');
|
|
154
|
+
expect(document.activeElement).toBe(tab1);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('navigates with ArrowDown in vertical mode', async () => {
|
|
159
|
+
render(
|
|
160
|
+
<Tabs defaultValue="tab1" orientation="vertical">
|
|
161
|
+
<Tabs.List>
|
|
162
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
163
|
+
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
164
|
+
<Tabs.Trigger value="tab3">Tab 3</Tabs.Trigger>
|
|
165
|
+
</Tabs.List>
|
|
166
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
167
|
+
<Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
168
|
+
<Tabs.Content value="tab3">Content 3</Tabs.Content>
|
|
169
|
+
</Tabs>
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
const tab1 = screen.getByText('Tab 1');
|
|
173
|
+
tab1.focus();
|
|
174
|
+
|
|
175
|
+
fireEvent.keyDown(tab1, { key: 'ArrowDown' });
|
|
176
|
+
|
|
177
|
+
await waitFor(() => {
|
|
178
|
+
const tab2 = screen.getByText('Tab 2');
|
|
179
|
+
expect(document.activeElement).toBe(tab2);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('navigates with ArrowUp in vertical mode', async () => {
|
|
184
|
+
render(
|
|
185
|
+
<Tabs defaultValue="tab2" orientation="vertical">
|
|
186
|
+
<Tabs.List>
|
|
187
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
188
|
+
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
189
|
+
<Tabs.Trigger value="tab3">Tab 3</Tabs.Trigger>
|
|
190
|
+
</Tabs.List>
|
|
191
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
192
|
+
<Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
193
|
+
<Tabs.Content value="tab3">Content 3</Tabs.Content>
|
|
194
|
+
</Tabs>
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
const tab2 = screen.getByText('Tab 2');
|
|
198
|
+
tab2.focus();
|
|
199
|
+
|
|
200
|
+
fireEvent.keyDown(tab2, { key: 'ArrowUp' });
|
|
201
|
+
|
|
202
|
+
await waitFor(() => {
|
|
203
|
+
const tab1 = screen.getByText('Tab 1');
|
|
204
|
+
expect(document.activeElement).toBe(tab1);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('navigates to first tab with Home key', async () => {
|
|
209
|
+
render(
|
|
210
|
+
<Tabs defaultValue="tab2">
|
|
211
|
+
<Tabs.List>
|
|
212
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
213
|
+
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
214
|
+
<Tabs.Trigger value="tab3">Tab 3</Tabs.Trigger>
|
|
215
|
+
</Tabs.List>
|
|
216
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
217
|
+
<Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
218
|
+
<Tabs.Content value="tab3">Content 3</Tabs.Content>
|
|
219
|
+
</Tabs>
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const tab2 = screen.getByText('Tab 2');
|
|
223
|
+
tab2.focus();
|
|
224
|
+
|
|
225
|
+
fireEvent.keyDown(tab2, { key: 'Home' });
|
|
226
|
+
|
|
227
|
+
await waitFor(() => {
|
|
228
|
+
const tab1 = screen.getByText('Tab 1');
|
|
229
|
+
expect(document.activeElement).toBe(tab1);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('navigates to last tab with End key', async () => {
|
|
234
|
+
render(
|
|
235
|
+
<Tabs defaultValue="tab1">
|
|
236
|
+
<Tabs.List>
|
|
237
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
238
|
+
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
239
|
+
<Tabs.Trigger value="tab3">Tab 3</Tabs.Trigger>
|
|
240
|
+
</Tabs.List>
|
|
241
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
242
|
+
<Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
243
|
+
<Tabs.Content value="tab3">Content 3</Tabs.Content>
|
|
244
|
+
</Tabs>
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
const tab1 = screen.getByText('Tab 1');
|
|
248
|
+
tab1.focus();
|
|
249
|
+
|
|
250
|
+
fireEvent.keyDown(tab1, { key: 'End' });
|
|
251
|
+
|
|
252
|
+
await waitFor(() => {
|
|
253
|
+
const tab3 = screen.getByText('Tab 3');
|
|
254
|
+
expect(document.activeElement).toBe(tab3);
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
describe('Activation Modes', () => {
|
|
260
|
+
it('activates tab automatically on focus in automatic mode', async () => {
|
|
261
|
+
render(
|
|
262
|
+
<Tabs defaultValue="tab1" activationMode="automatic">
|
|
263
|
+
<Tabs.List>
|
|
264
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
265
|
+
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
266
|
+
</Tabs.List>
|
|
267
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
268
|
+
<Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
269
|
+
</Tabs>
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
const tab2 = screen.getByText('Tab 2');
|
|
273
|
+
|
|
274
|
+
await act(async () => {
|
|
275
|
+
tab2.focus();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
await waitFor(() => {
|
|
279
|
+
expect(screen.getByText('Content 2')).toBeInTheDocument();
|
|
280
|
+
expect(screen.queryByText('Content 1')).not.toBeInTheDocument();
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('requires Enter or Space to activate in manual mode', async () => {
|
|
285
|
+
const _user = userEvent.setup();
|
|
286
|
+
render(
|
|
287
|
+
<Tabs defaultValue="tab1" activationMode="manual">
|
|
288
|
+
<Tabs.List>
|
|
289
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
290
|
+
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
291
|
+
</Tabs.List>
|
|
292
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
293
|
+
<Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
294
|
+
</Tabs>
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
const tab2 = screen.getByText('Tab 2');
|
|
298
|
+
tab2.focus();
|
|
299
|
+
|
|
300
|
+
// Content should not change on focus
|
|
301
|
+
expect(screen.getByText('Content 1')).toBeInTheDocument();
|
|
302
|
+
expect(screen.queryByText('Content 2')).not.toBeInTheDocument();
|
|
303
|
+
|
|
304
|
+
// Press Enter to activate
|
|
305
|
+
fireEvent.keyDown(tab2, { key: 'Enter' });
|
|
306
|
+
|
|
307
|
+
await waitFor(() => {
|
|
308
|
+
expect(screen.getByText('Content 2')).toBeInTheDocument();
|
|
309
|
+
expect(screen.queryByText('Content 1')).not.toBeInTheDocument();
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
describe('Controlled vs Uncontrolled', () => {
|
|
315
|
+
it('works in uncontrolled mode', async () => {
|
|
316
|
+
const user = userEvent.setup();
|
|
317
|
+
render(
|
|
318
|
+
<Tabs defaultValue="tab1">
|
|
319
|
+
<Tabs.List>
|
|
320
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
321
|
+
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
322
|
+
</Tabs.List>
|
|
323
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
324
|
+
<Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
325
|
+
</Tabs>
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
const tab2 = screen.getByText('Tab 2');
|
|
329
|
+
await user.click(tab2);
|
|
330
|
+
|
|
331
|
+
await waitFor(() => {
|
|
332
|
+
expect(screen.getByText('Content 2')).toBeInTheDocument();
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('works in controlled mode', () => {
|
|
337
|
+
const { rerender } = render(
|
|
338
|
+
<Tabs value="tab1">
|
|
339
|
+
<Tabs.List>
|
|
340
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
341
|
+
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
342
|
+
</Tabs.List>
|
|
343
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
344
|
+
<Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
345
|
+
</Tabs>
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
expect(screen.getByText('Content 1')).toBeInTheDocument();
|
|
349
|
+
|
|
350
|
+
rerender(
|
|
351
|
+
<Tabs value="tab2">
|
|
352
|
+
<Tabs.List>
|
|
353
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
354
|
+
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
355
|
+
</Tabs.List>
|
|
356
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
357
|
+
<Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
358
|
+
</Tabs>
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
expect(screen.getByText('Content 2')).toBeInTheDocument();
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
describe('Disabled Tabs', () => {
|
|
366
|
+
it('does not switch when disabled tab is clicked', async () => {
|
|
367
|
+
const user = userEvent.setup();
|
|
368
|
+
render(
|
|
369
|
+
<Tabs defaultValue="tab1">
|
|
370
|
+
<Tabs.List>
|
|
371
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
372
|
+
<Tabs.Trigger value="tab2" disabled>Tab 2</Tabs.Trigger>
|
|
373
|
+
</Tabs.List>
|
|
374
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
375
|
+
<Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
376
|
+
</Tabs>
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
const tab2 = screen.getByText('Tab 2');
|
|
380
|
+
expect(tab2).toBeDisabled();
|
|
381
|
+
|
|
382
|
+
await user.click(tab2);
|
|
383
|
+
|
|
384
|
+
// Content should not change
|
|
385
|
+
expect(screen.getByText('Content 1')).toBeInTheDocument();
|
|
386
|
+
expect(screen.queryByText('Content 2')).not.toBeInTheDocument();
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it('skips disabled tabs in keyboard navigation', async () => {
|
|
390
|
+
render(
|
|
391
|
+
<Tabs defaultValue="tab1">
|
|
392
|
+
<Tabs.List>
|
|
393
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
394
|
+
<Tabs.Trigger value="tab2" disabled>Tab 2</Tabs.Trigger>
|
|
395
|
+
<Tabs.Trigger value="tab3">Tab 3</Tabs.Trigger>
|
|
396
|
+
</Tabs.List>
|
|
397
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
398
|
+
<Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
399
|
+
<Tabs.Content value="tab3">Content 3</Tabs.Content>
|
|
400
|
+
</Tabs>
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
const tab1 = screen.getByText('Tab 1');
|
|
404
|
+
tab1.focus();
|
|
405
|
+
|
|
406
|
+
fireEvent.keyDown(tab1, { key: 'ArrowRight' });
|
|
407
|
+
|
|
408
|
+
await waitFor(() => {
|
|
409
|
+
const tab3 = screen.getByText('Tab 3');
|
|
410
|
+
expect(document.activeElement).toBe(tab3);
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
describe('Accessibility', () => {
|
|
416
|
+
it('has correct ARIA attributes on tablist', () => {
|
|
417
|
+
render(
|
|
418
|
+
<Tabs defaultValue="tab1">
|
|
419
|
+
<Tabs.List>
|
|
420
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
421
|
+
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
422
|
+
</Tabs.List>
|
|
423
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
424
|
+
<Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
425
|
+
</Tabs>
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
const tablist = screen.getByRole('tablist');
|
|
429
|
+
expect(tablist).toHaveAttribute('aria-orientation', 'horizontal');
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
it('has correct ARIA attributes on triggers', () => {
|
|
433
|
+
render(
|
|
434
|
+
<Tabs defaultValue="tab1">
|
|
435
|
+
<Tabs.List>
|
|
436
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
437
|
+
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
438
|
+
</Tabs.List>
|
|
439
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
440
|
+
<Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
441
|
+
</Tabs>
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
const tab1 = screen.getByRole('tab', { name: 'Tab 1' });
|
|
445
|
+
expect(tab1).toHaveAttribute('aria-selected', 'true');
|
|
446
|
+
expect(tab1).toHaveAttribute('aria-controls', 'tabpanel-tab1');
|
|
447
|
+
expect(tab1).toHaveAttribute('id', 'tab-tab1');
|
|
448
|
+
|
|
449
|
+
const tab2 = screen.getByRole('tab', { name: 'Tab 2' });
|
|
450
|
+
expect(tab2).toHaveAttribute('aria-selected', 'false');
|
|
451
|
+
expect(tab2).toHaveAttribute('aria-controls', 'tabpanel-tab2');
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it('has correct ARIA attributes on content', () => {
|
|
455
|
+
render(
|
|
456
|
+
<Tabs defaultValue="tab1">
|
|
457
|
+
<Tabs.List>
|
|
458
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
459
|
+
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
460
|
+
</Tabs.List>
|
|
461
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
462
|
+
<Tabs.Content value="tab2" forceMount>Content 2</Tabs.Content>
|
|
463
|
+
</Tabs>
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
const content1 = screen.getByRole('tabpanel', { name: 'Tab 1' });
|
|
467
|
+
expect(content1).toHaveAttribute('id', 'tabpanel-tab1');
|
|
468
|
+
expect(content1).toHaveAttribute('aria-labelledby', 'tab-tab1');
|
|
469
|
+
expect(content1).not.toHaveAttribute('hidden');
|
|
470
|
+
|
|
471
|
+
// Tab 2 content should be hidden (using forceMount to render it)
|
|
472
|
+
// When hidden, we need to query by text or id since getByRole may not find hidden elements
|
|
473
|
+
const content2 = screen.getByText('Content 2').closest('[role="tabpanel"]');
|
|
474
|
+
expect(content2).toBeInTheDocument();
|
|
475
|
+
expect(content2).toHaveAttribute('hidden');
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
it('has correct tabIndex on triggers', () => {
|
|
479
|
+
render(
|
|
480
|
+
<Tabs defaultValue="tab1">
|
|
481
|
+
<Tabs.List>
|
|
482
|
+
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
483
|
+
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
484
|
+
</Tabs.List>
|
|
485
|
+
<Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
486
|
+
<Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
487
|
+
</Tabs>
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
const tab1 = screen.getByRole('tab', { name: 'Tab 1' });
|
|
491
|
+
expect(tab1).toHaveAttribute('tabIndex', '0');
|
|
492
|
+
|
|
493
|
+
const tab2 = screen.getByRole('tab', { name: 'Tab 2' });
|
|
494
|
+
expect(tab2).toHaveAttribute('tabIndex', '-1');
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { type ReactNode } from 'react';
|
|
4
|
+
import { TabsProvider, type TabsProviderProps } from './TabsProvider';
|
|
5
|
+
import { TabsList } from './TabsList';
|
|
6
|
+
import { TabsTrigger } from './TabsTrigger';
|
|
7
|
+
import { TabsContent } from './TabsContent';
|
|
8
|
+
|
|
9
|
+
export interface TabsProps extends Omit<TabsProviderProps, 'children'> {
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Tabs Component
|
|
15
|
+
*
|
|
16
|
+
* A flexible tabs component with compound components pattern.
|
|
17
|
+
* Supports horizontal and vertical orientations, automatic and manual activation modes.
|
|
18
|
+
* Fully accessible with ARIA attributes and keyboard navigation.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* <Tabs defaultValue="tab1">
|
|
23
|
+
* <Tabs.List>
|
|
24
|
+
* <Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
|
|
25
|
+
* <Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
|
|
26
|
+
* </Tabs.List>
|
|
27
|
+
* <Tabs.Content value="tab1">Content 1</Tabs.Content>
|
|
28
|
+
* <Tabs.Content value="tab2">Content 2</Tabs.Content>
|
|
29
|
+
* </Tabs>
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
function TabsComponent({
|
|
33
|
+
children,
|
|
34
|
+
...providerProps
|
|
35
|
+
}: TabsProps) {
|
|
36
|
+
return (
|
|
37
|
+
<TabsProvider {...providerProps}>
|
|
38
|
+
{children}
|
|
39
|
+
</TabsProvider>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Compound components
|
|
44
|
+
TabsComponent.List = TabsList;
|
|
45
|
+
TabsComponent.Trigger = TabsTrigger;
|
|
46
|
+
TabsComponent.Content = TabsContent;
|
|
47
|
+
|
|
48
|
+
// Type declaration for compound components
|
|
49
|
+
export interface TabsComponentType extends React.FC<TabsProps> {
|
|
50
|
+
List: typeof TabsList;
|
|
51
|
+
Trigger: typeof TabsTrigger;
|
|
52
|
+
Content: typeof TabsContent;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Cast to include compound components
|
|
56
|
+
const Tabs: TabsComponentType = TabsComponent as TabsComponentType;
|
|
57
|
+
|
|
58
|
+
export default Tabs;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useTabsContext } from './TabsContext';
|
|
4
|
+
import type { HTMLAttributes, ReactNode } from 'react';
|
|
5
|
+
|
|
6
|
+
export interface TabsContentProps extends HTMLAttributes<HTMLDivElement> {
|
|
7
|
+
value: string;
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
forceMount?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* TabsContent Component
|
|
14
|
+
*
|
|
15
|
+
* Content panel for a tab.
|
|
16
|
+
* Only renders when the tab is active (unless forceMount is true).
|
|
17
|
+
* Must be used within a Tabs component.
|
|
18
|
+
*/
|
|
19
|
+
export function TabsContent({
|
|
20
|
+
value,
|
|
21
|
+
children,
|
|
22
|
+
forceMount = false,
|
|
23
|
+
className = '',
|
|
24
|
+
...props
|
|
25
|
+
}: TabsContentProps) {
|
|
26
|
+
const { value: activeValue } = useTabsContext();
|
|
27
|
+
|
|
28
|
+
const isActive = activeValue === value;
|
|
29
|
+
|
|
30
|
+
if (!isActive && !forceMount) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div
|
|
36
|
+
role="tabpanel"
|
|
37
|
+
id={`tabpanel-${value}`}
|
|
38
|
+
aria-labelledby={`tab-${value}`}
|
|
39
|
+
hidden={!isActive}
|
|
40
|
+
className={`
|
|
41
|
+
mt-2
|
|
42
|
+
focus:outline-none
|
|
43
|
+
${className}
|
|
44
|
+
`}
|
|
45
|
+
{...props}
|
|
46
|
+
>
|
|
47
|
+
{children}
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext } from 'react';
|
|
4
|
+
|
|
5
|
+
export interface TabsContextValue {
|
|
6
|
+
value: string;
|
|
7
|
+
onValueChange: (value: string) => void;
|
|
8
|
+
orientation?: 'horizontal' | 'vertical';
|
|
9
|
+
activationMode?: 'automatic' | 'manual';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const TabsContext = createContext<TabsContextValue | undefined>(undefined);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Hook to access Tabs context
|
|
16
|
+
*
|
|
17
|
+
* @throws Error if used outside of Tabs component
|
|
18
|
+
*/
|
|
19
|
+
export function useTabsContext(): TabsContextValue {
|
|
20
|
+
const context = useContext(TabsContext);
|
|
21
|
+
|
|
22
|
+
if (context === undefined) {
|
|
23
|
+
throw new Error('useTabsContext must be used within a Tabs component');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return context;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Hook to access Tabs context (optional, returns undefined if not in Tabs)
|
|
31
|
+
*/
|
|
32
|
+
export function useTabsContextOptional(): TabsContextValue | undefined {
|
|
33
|
+
return useContext(TabsContext);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { TabsContext };
|