@12-apps/ui 1.0.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/components-catalog.md +806 -0
- package/components-guidelines.md +347 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/eslint.config.js +77 -0
- package/package.json +247 -0
- package/src/__tests__/package-wiring.test.ts +37 -0
- package/src/__tests__/setup.ts +1 -0
- package/src/__tests__/social-login-button.test.tsx +186 -0
- package/src/__tests__/user-avatar.test.tsx +195 -0
- package/src/button.tsx +22 -0
- package/src/components/command/__tests__/command.test.tsx +112 -0
- package/src/components/data-display/Alert/Alert.md +209 -0
- package/src/components/data-display/Alert/Alert.stories.tsx +561 -0
- package/src/components/data-display/Alert/Alert.test.stories.tsx +709 -0
- package/src/components/data-display/Alert/Alert.tsx +449 -0
- package/src/components/data-display/Alert/Alert.types.ts +79 -0
- package/src/components/data-display/Alert/index.ts +2 -0
- package/src/components/data-display/AlertDialog/AlertDialog.md +408 -0
- package/src/components/data-display/AlertDialog/AlertDialog.stories.tsx +360 -0
- package/src/components/data-display/AlertDialog/AlertDialog.test.stories.tsx +753 -0
- package/src/components/data-display/AlertDialog/AlertDialog.tsx +273 -0
- package/src/components/data-display/AlertDialog/AlertDialog.types.ts +74 -0
- package/src/components/data-display/AlertDialog/index.ts +2 -0
- package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.stories.tsx +230 -0
- package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.test.stories.tsx +270 -0
- package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.tsx +116 -0
- package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.types.ts +70 -0
- package/src/components/data-display/AsyncStateContainer/index.ts +2 -0
- package/src/components/data-display/Avatar/Avatar.md +568 -0
- package/src/components/data-display/Avatar/Avatar.stories.tsx +718 -0
- package/src/components/data-display/Avatar/Avatar.test.stories.tsx +820 -0
- package/src/components/data-display/Avatar/Avatar.tsx +507 -0
- package/src/components/data-display/Avatar/Avatar.types.ts +88 -0
- package/src/components/data-display/Avatar/index.ts +2 -0
- package/src/components/data-display/Badge/Badge.md +469 -0
- package/src/components/data-display/Badge/Badge.stories.tsx +1136 -0
- package/src/components/data-display/Badge/Badge.test.stories.tsx +1193 -0
- package/src/components/data-display/Badge/Badge.tsx +606 -0
- package/src/components/data-display/Badge/Badge.types.ts +118 -0
- package/src/components/data-display/Badge/index.ts +2 -0
- package/src/components/data-display/Banner/Banner.stories.tsx +524 -0
- package/src/components/data-display/Banner/Banner.test.stories.tsx +503 -0
- package/src/components/data-display/Banner/Banner.tsx +350 -0
- package/src/components/data-display/Banner/Banner.types.ts +41 -0
- package/src/components/data-display/Banner/banner.md +141 -0
- package/src/components/data-display/Banner/index.ts +2 -0
- package/src/components/data-display/Carousel/Carousel.md +815 -0
- package/src/components/data-display/Carousel/Carousel.stories.tsx +607 -0
- package/src/components/data-display/Carousel/Carousel.test.stories.tsx +691 -0
- package/src/components/data-display/Carousel/Carousel.tsx +765 -0
- package/src/components/data-display/Carousel/Carousel.types.ts +79 -0
- package/src/components/data-display/Carousel/index.ts +8 -0
- package/src/components/data-display/Chart/Chart.md +352 -0
- package/src/components/data-display/Chart/Chart.stories.tsx +568 -0
- package/src/components/data-display/Chart/Chart.test.stories.tsx +174 -0
- package/src/components/data-display/Chart/Chart.tsx +520 -0
- package/src/components/data-display/Chart/Chart.types.ts +65 -0
- package/src/components/data-display/Chart/index.ts +2 -0
- package/src/components/data-display/Chip/Chip.stories.tsx +251 -0
- package/src/components/data-display/Chip/Chip.test.stories.tsx +263 -0
- package/src/components/data-display/Chip/Chip.tsx +134 -0
- package/src/components/data-display/Chip/Chip.types.ts +51 -0
- package/src/components/data-display/Chip/chip.md +395 -0
- package/src/components/data-display/Chip/index.ts +2 -0
- package/src/components/data-display/DataGrid/DataGrid.md +602 -0
- package/src/components/data-display/DataGrid/DataGrid.stories.tsx +515 -0
- package/src/components/data-display/DataGrid/DataGrid.test.stories.tsx +659 -0
- package/src/components/data-display/DataGrid/DataGrid.tsx +743 -0
- package/src/components/data-display/DataGrid/DataGrid.types.ts +171 -0
- package/src/components/data-display/DataGrid/index.ts +21 -0
- package/src/components/data-display/DateTimeDisplay/DateTimeDisplay.tsx +197 -0
- package/src/components/data-display/DateTimeDisplay/DateTimeDisplay.types.ts +46 -0
- package/src/components/data-display/DateTimeDisplay/index.ts +2 -0
- package/src/components/data-display/DescriptionItem/DescriptionItem.md +226 -0
- package/src/components/data-display/DescriptionItem/DescriptionItem.stories.tsx +254 -0
- package/src/components/data-display/DescriptionItem/DescriptionItem.tsx +100 -0
- package/src/components/data-display/DescriptionItem/DescriptionItem.types.ts +1 -0
- package/src/components/data-display/DescriptionItem/index.ts +2 -0
- package/src/components/data-display/EmptyState/EmptyState.md +265 -0
- package/src/components/data-display/EmptyState/EmptyState.stories.tsx +282 -0
- package/src/components/data-display/EmptyState/EmptyState.test.stories.tsx +441 -0
- package/src/components/data-display/EmptyState/EmptyState.tsx +180 -0
- package/src/components/data-display/EmptyState/EmptyState.types.ts +31 -0
- package/src/components/data-display/EmptyState/index.ts +2 -0
- package/src/components/data-display/ErrorState/ErrorState.stories.tsx +110 -0
- package/src/components/data-display/ErrorState/ErrorState.test.stories.tsx +133 -0
- package/src/components/data-display/ErrorState/ErrorState.tsx +118 -0
- package/src/components/data-display/ErrorState/ErrorState.types.ts +45 -0
- package/src/components/data-display/ErrorState/index.ts +2 -0
- package/src/components/data-display/HoverCard/HoverCard.md +389 -0
- package/src/components/data-display/HoverCard/HoverCard.stories.tsx +648 -0
- package/src/components/data-display/HoverCard/HoverCard.test.stories.tsx +929 -0
- package/src/components/data-display/HoverCard/HoverCard.tsx +657 -0
- package/src/components/data-display/HoverCard/HoverCard.types.ts +116 -0
- package/src/components/data-display/HoverCard/index.ts +2 -0
- package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.md +532 -0
- package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.stories.tsx +411 -0
- package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.tsx +245 -0
- package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.types.ts +64 -0
- package/src/components/data-display/InteractiveTooltip/index.ts +2 -0
- package/src/components/data-display/LazyImage/LazyImage.md +356 -0
- package/src/components/data-display/LazyImage/LazyImage.stories.tsx +387 -0
- package/src/components/data-display/LazyImage/LazyImage.tsx +323 -0
- package/src/components/data-display/LazyImage/LazyImage.types.ts +205 -0
- package/src/components/data-display/LazyImage/index.ts +2 -0
- package/src/components/data-display/Lightbox/Lightbox.stories.tsx +315 -0
- package/src/components/data-display/Lightbox/Lightbox.test.stories.tsx +605 -0
- package/src/components/data-display/Lightbox/Lightbox.tsx +666 -0
- package/src/components/data-display/Lightbox/Lightbox.types.ts +49 -0
- package/src/components/data-display/Lightbox/index.ts +2 -0
- package/src/components/data-display/Lightbox/lightbox.md +298 -0
- package/src/components/data-display/LoadingState/LoadingState.stories.tsx +124 -0
- package/src/components/data-display/LoadingState/LoadingState.test.stories.tsx +99 -0
- package/src/components/data-display/LoadingState/LoadingState.tsx +103 -0
- package/src/components/data-display/LoadingState/LoadingState.types.ts +37 -0
- package/src/components/data-display/LoadingState/index.ts +2 -0
- package/src/components/data-display/MapPreview/MapPreview.md +154 -0
- package/src/components/data-display/MapPreview/MapPreview.stories.tsx +458 -0
- package/src/components/data-display/MapPreview/MapPreview.test.stories.tsx +390 -0
- package/src/components/data-display/MapPreview/MapPreview.tsx +922 -0
- package/src/components/data-display/MapPreview/MapPreview.types.ts +56 -0
- package/src/components/data-display/MapPreview/index.ts +2 -0
- package/src/components/data-display/Popover/Popover.md +245 -0
- package/src/components/data-display/Popover/Popover.stories.tsx +295 -0
- package/src/components/data-display/Popover/Popover.test.stories.tsx +1168 -0
- package/src/components/data-display/Popover/Popover.tsx +132 -0
- package/src/components/data-display/Popover/Popover.types.ts +33 -0
- package/src/components/data-display/Popover/index.ts +2 -0
- package/src/components/data-display/Progress/Progress.md +450 -0
- package/src/components/data-display/Progress/Progress.stories.tsx +481 -0
- package/src/components/data-display/Progress/Progress.test.stories.tsx +2070 -0
- package/src/components/data-display/Progress/Progress.tsx +332 -0
- package/src/components/data-display/Progress/Progress.types.ts +61 -0
- package/src/components/data-display/Progress/index.ts +2 -0
- package/src/components/data-display/SectionOnboarding/SectionOnboarding.md +108 -0
- package/src/components/data-display/SectionOnboarding/SectionOnboarding.stories.tsx +160 -0
- package/src/components/data-display/SectionOnboarding/SectionOnboarding.test.stories.tsx +190 -0
- package/src/components/data-display/SectionOnboarding/SectionOnboarding.tsx +224 -0
- package/src/components/data-display/SectionOnboarding/SectionOnboarding.types.ts +101 -0
- package/src/components/data-display/SectionOnboarding/index.ts +7 -0
- package/src/components/data-display/Sheet/Sheet.md +475 -0
- package/src/components/data-display/Sheet/Sheet.stories.tsx +883 -0
- package/src/components/data-display/Sheet/Sheet.test.stories.tsx +1101 -0
- package/src/components/data-display/Sheet/Sheet.tsx +978 -0
- package/src/components/data-display/Sheet/Sheet.types.ts +93 -0
- package/src/components/data-display/Sheet/index.ts +8 -0
- package/src/components/data-display/Stepper/Stepper.md +542 -0
- package/src/components/data-display/Stepper/Stepper.stories.tsx +540 -0
- package/src/components/data-display/Stepper/Stepper.test.stories.tsx +607 -0
- package/src/components/data-display/Stepper/Stepper.tsx +328 -0
- package/src/components/data-display/Stepper/Stepper.types.ts +97 -0
- package/src/components/data-display/Stepper/index.ts +2 -0
- package/src/components/data-display/Table/Table.md +243 -0
- package/src/components/data-display/Table/Table.stories.tsx +801 -0
- package/src/components/data-display/Table/Table.test.stories.tsx +369 -0
- package/src/components/data-display/Table/Table.tsx +831 -0
- package/src/components/data-display/Table/Table.types.ts +335 -0
- package/src/components/data-display/Table/index.ts +2 -0
- package/src/components/data-display/Timeline/Timeline.md +201 -0
- package/src/components/data-display/Timeline/Timeline.stories.tsx +444 -0
- package/src/components/data-display/Timeline/Timeline.test.stories.tsx +944 -0
- package/src/components/data-display/Timeline/Timeline.tsx +348 -0
- package/src/components/data-display/Timeline/Timeline.types.ts +26 -0
- package/src/components/data-display/Timeline/index.ts +2 -0
- package/src/components/data-display/TimingDiagram/TimingDiagram.md +162 -0
- package/src/components/data-display/TimingDiagram/TimingDiagram.stories.tsx +475 -0
- package/src/components/data-display/TimingDiagram/TimingDiagram.test.stories.tsx +734 -0
- package/src/components/data-display/TimingDiagram/TimingDiagram.tsx +399 -0
- package/src/components/data-display/TimingDiagram/TimingDiagram.types.ts +19 -0
- package/src/components/data-display/TimingDiagram/index.ts +2 -0
- package/src/components/data-display/Tooltip/Tooltip.md +397 -0
- package/src/components/data-display/Tooltip/Tooltip.stories.tsx +465 -0
- package/src/components/data-display/Tooltip/Tooltip.test.stories.tsx +1168 -0
- package/src/components/data-display/Tooltip/Tooltip.tsx +161 -0
- package/src/components/data-display/Tooltip/Tooltip.types.ts +33 -0
- package/src/components/data-display/Tooltip/index.ts +2 -0
- package/src/components/data-display/index.ts +29 -0
- package/src/components/feedback/Dialog/Dialog.md +527 -0
- package/src/components/feedback/Dialog/Dialog.stories.tsx +833 -0
- package/src/components/feedback/Dialog/Dialog.styles.ts +171 -0
- package/src/components/feedback/Dialog/Dialog.test.stories.tsx +955 -0
- package/src/components/feedback/Dialog/Dialog.tsx +256 -0
- package/src/components/feedback/Dialog/Dialog.types.ts +46 -0
- package/src/components/feedback/Dialog/index.ts +9 -0
- package/src/components/feedback/Modal/Modal.md +629 -0
- package/src/components/feedback/Modal/Modal.stories.tsx +765 -0
- package/src/components/feedback/Modal/Modal.test.stories.tsx +251 -0
- package/src/components/feedback/Modal/Modal.tsx +279 -0
- package/src/components/feedback/Modal/Modal.types.ts +26 -0
- package/src/components/feedback/Modal/index.ts +7 -0
- package/src/components/feedback/Sonner/Sonner.stories.tsx +698 -0
- package/src/components/feedback/Sonner/Sonner.test.stories.tsx +896 -0
- package/src/components/feedback/Sonner/Sonner.tsx +493 -0
- package/src/components/feedback/Sonner/Sonner.types.ts +65 -0
- package/src/components/feedback/Sonner/index.ts +8 -0
- package/src/components/feedback/StackedModal/StackedModal.md +273 -0
- package/src/components/feedback/StackedModal/StackedModal.stories.tsx +1785 -0
- package/src/components/feedback/StackedModal/StackedModal.test.stories.tsx +975 -0
- package/src/components/feedback/StackedModal/StackedModal.tsx +664 -0
- package/src/components/feedback/StackedModal/StackedModal.types.ts +70 -0
- package/src/components/feedback/StackedModal/index.ts +11 -0
- package/src/components/feedback/Toast/Toast.md +547 -0
- package/src/components/feedback/Toast/Toast.stories.tsx +548 -0
- package/src/components/feedback/Toast/Toast.test.stories.tsx +834 -0
- package/src/components/feedback/Toast/Toast.tsx +279 -0
- package/src/components/feedback/Toast/Toast.types.ts +59 -0
- package/src/components/feedback/Toast/index.ts +8 -0
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.md +610 -0
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.stories.tsx +691 -0
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.test.stories.tsx +634 -0
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.tsx +554 -0
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.types.ts +30 -0
- package/src/components/feedback/TutorialOverlay/index.ts +2 -0
- package/src/components/feedback/index.ts +6 -0
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.md +633 -0
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.stories.tsx +663 -0
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.test.stories.tsx +723 -0
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.tsx +836 -0
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.types.ts +37 -0
- package/src/components/form/AddressAutocomplete/index.ts +2 -0
- package/src/components/form/Autocomplete/Autocomplete.helpers.ts +115 -0
- package/src/components/form/Autocomplete/Autocomplete.keys.ts +134 -0
- package/src/components/form/Autocomplete/Autocomplete.parts.tsx +337 -0
- package/src/components/form/Autocomplete/Autocomplete.stories.tsx +715 -0
- package/src/components/form/Autocomplete/Autocomplete.test.stories.tsx +825 -0
- package/src/components/form/Autocomplete/Autocomplete.tsx +166 -0
- package/src/components/form/Autocomplete/Autocomplete.types.ts +116 -0
- package/src/components/form/Autocomplete/autocomplete.md +789 -0
- package/src/components/form/Autocomplete/index.ts +8 -0
- package/src/components/form/Autocomplete/useAutocomplete.ts +311 -0
- package/src/components/form/Button/Button.md +411 -0
- package/src/components/form/Button/Button.stories.tsx +362 -0
- package/src/components/form/Button/Button.test.stories.tsx +603 -0
- package/src/components/form/Button/Button.tsx +326 -0
- package/src/components/form/Button/Button.types.ts +74 -0
- package/src/components/form/Button/index.ts +2 -0
- package/src/components/form/Calendar/Calendar.md +807 -0
- package/src/components/form/Calendar/Calendar.stories.tsx +349 -0
- package/src/components/form/Calendar/Calendar.test.stories.tsx +465 -0
- package/src/components/form/Calendar/Calendar.tsx +571 -0
- package/src/components/form/Calendar/Calendar.types.ts +81 -0
- package/src/components/form/Calendar/index.ts +11 -0
- package/src/components/form/Checkbox/Checkbox.md +200 -0
- package/src/components/form/Checkbox/Checkbox.stories.tsx +265 -0
- package/src/components/form/Checkbox/Checkbox.test.stories.tsx +181 -0
- package/src/components/form/Checkbox/Checkbox.tsx +170 -0
- package/src/components/form/Checkbox/Checkbox.types.ts +20 -0
- package/src/components/form/Checkbox/index.ts +2 -0
- package/src/components/form/CodeEditor/CodeEditor.md +350 -0
- package/src/components/form/CodeEditor/CodeEditor.stories.tsx +871 -0
- package/src/components/form/CodeEditor/CodeEditor.test.stories.tsx +575 -0
- package/src/components/form/CodeEditor/CodeEditor.tsx +415 -0
- package/src/components/form/CodeEditor/CodeEditor.types.ts +38 -0
- package/src/components/form/CodeEditor/index.ts +2 -0
- package/src/components/form/Command/Command.md +468 -0
- package/src/components/form/Command/Command.stories.tsx +580 -0
- package/src/components/form/Command/Command.test.stories.tsx +215 -0
- package/src/components/form/Command/Command.tsx +545 -0
- package/src/components/form/Command/Command.types.ts +116 -0
- package/src/components/form/Command/index.ts +12 -0
- package/src/components/form/CreatableSelect/CreatableSelect.md +114 -0
- package/src/components/form/CreatableSelect/CreatableSelect.stories.tsx +112 -0
- package/src/components/form/CreatableSelect/CreatableSelect.test.stories.tsx +134 -0
- package/src/components/form/CreatableSelect/CreatableSelect.tsx +149 -0
- package/src/components/form/CreatableSelect/CreatableSelect.types.ts +37 -0
- package/src/components/form/CreatableSelect/index.ts +2 -0
- package/src/components/form/Form/Form.md +325 -0
- package/src/components/form/Form/Form.stories.tsx +687 -0
- package/src/components/form/Form/Form.test.stories.tsx +601 -0
- package/src/components/form/Form/Form.tsx +136 -0
- package/src/components/form/Form/Form.types.ts +42 -0
- package/src/components/form/Form/index.ts +8 -0
- package/src/components/form/Input/Input.md +252 -0
- package/src/components/form/Input/Input.stories.tsx +256 -0
- package/src/components/form/Input/Input.test.stories.tsx +1184 -0
- package/src/components/form/Input/Input.tsx +263 -0
- package/src/components/form/Input/Input.types.ts +24 -0
- package/src/components/form/Input/index.ts +2 -0
- package/src/components/form/InputOTP/InputOTP.md +419 -0
- package/src/components/form/InputOTP/InputOTP.stories.tsx +774 -0
- package/src/components/form/InputOTP/InputOTP.test.stories.tsx +596 -0
- package/src/components/form/InputOTP/InputOTP.tsx +231 -0
- package/src/components/form/InputOTP/InputOTP.types.ts +68 -0
- package/src/components/form/InputOTP/index.ts +2 -0
- package/src/components/form/Label/Label.md +426 -0
- package/src/components/form/Label/Label.stories.tsx +473 -0
- package/src/components/form/Label/Label.test.stories.tsx +909 -0
- package/src/components/form/Label/Label.tsx +367 -0
- package/src/components/form/Label/Label.types.ts +35 -0
- package/src/components/form/Label/index.ts +2 -0
- package/src/components/form/Menubar/Menubar.md +275 -0
- package/src/components/form/Menubar/Menubar.stories.tsx +472 -0
- package/src/components/form/Menubar/Menubar.test.stories.tsx +588 -0
- package/src/components/form/Menubar/Menubar.tsx +467 -0
- package/src/components/form/Menubar/Menubar.types.ts +71 -0
- package/src/components/form/Menubar/index.ts +8 -0
- package/src/components/form/PasswordStrength/PasswordStrength.md +139 -0
- package/src/components/form/PasswordStrength/PasswordStrength.stories.tsx +562 -0
- package/src/components/form/PasswordStrength/PasswordStrength.test.stories.tsx +327 -0
- package/src/components/form/PasswordStrength/PasswordStrength.tsx +419 -0
- package/src/components/form/PasswordStrength/PasswordStrength.types.ts +18 -0
- package/src/components/form/PasswordStrength/index.ts +2 -0
- package/src/components/form/PhoneInput/PhoneInput.md +578 -0
- package/src/components/form/PhoneInput/PhoneInput.stories.tsx +495 -0
- package/src/components/form/PhoneInput/PhoneInput.test.stories.tsx +666 -0
- package/src/components/form/PhoneInput/PhoneInput.tsx +376 -0
- package/src/components/form/PhoneInput/PhoneInput.types.ts +25 -0
- package/src/components/form/PhoneInput/index.ts +2 -0
- package/src/components/form/RadioGroup/RadioGroup.md +477 -0
- package/src/components/form/RadioGroup/RadioGroup.stories.tsx +518 -0
- package/src/components/form/RadioGroup/RadioGroup.test.stories.tsx +685 -0
- package/src/components/form/RadioGroup/RadioGroup.tsx +664 -0
- package/src/components/form/RadioGroup/RadioGroup.types.ts +82 -0
- package/src/components/form/RadioGroup/index.ts +2 -0
- package/src/components/form/RichTextEditor/RichTextEditor.md +710 -0
- package/src/components/form/RichTextEditor/RichTextEditor.stories.tsx +261 -0
- package/src/components/form/RichTextEditor/RichTextEditor.test.stories.tsx +498 -0
- package/src/components/form/RichTextEditor/RichTextEditor.tsx +468 -0
- package/src/components/form/RichTextEditor/RichTextEditor.types.ts +72 -0
- package/src/components/form/RichTextEditor/index.ts +2 -0
- package/src/components/form/SearchPalette/SearchPalette.md +69 -0
- package/src/components/form/SearchPalette/SearchPalette.parts.tsx +358 -0
- package/src/components/form/SearchPalette/SearchPalette.stories.tsx +135 -0
- package/src/components/form/SearchPalette/SearchPalette.tsx +141 -0
- package/src/components/form/SearchPalette/SearchPalette.types.ts +82 -0
- package/src/components/form/SearchPalette/index.ts +7 -0
- package/src/components/form/Select/Select.md +254 -0
- package/src/components/form/Select/Select.stories.tsx +364 -0
- package/src/components/form/Select/Select.test.stories.tsx +977 -0
- package/src/components/form/Select/Select.tsx +239 -0
- package/src/components/form/Select/Select.types.ts +57 -0
- package/src/components/form/Select/index.ts +2 -0
- package/src/components/form/Slider/Slider.md +326 -0
- package/src/components/form/Slider/Slider.stories.tsx +418 -0
- package/src/components/form/Slider/Slider.test.stories.tsx +1044 -0
- package/src/components/form/Slider/Slider.tsx +374 -0
- package/src/components/form/Slider/Slider.types.ts +113 -0
- package/src/components/form/Slider/index.ts +2 -0
- package/src/components/form/Switch/Switch.md +565 -0
- package/src/components/form/Switch/Switch.stories.tsx +697 -0
- package/src/components/form/Switch/Switch.test.stories.tsx +597 -0
- package/src/components/form/Switch/Switch.tsx +578 -0
- package/src/components/form/Switch/Switch.types.ts +130 -0
- package/src/components/form/Switch/index.ts +2 -0
- package/src/components/form/Textarea/Textarea.stories.tsx +473 -0
- package/src/components/form/Textarea/Textarea.test.stories.tsx +708 -0
- package/src/components/form/Textarea/Textarea.tsx +678 -0
- package/src/components/form/Textarea/Textarea.types.ts +84 -0
- package/src/components/form/Textarea/index.ts +2 -0
- package/src/components/form/Toggle/Toggle.md +234 -0
- package/src/components/form/Toggle/Toggle.stories.tsx +638 -0
- package/src/components/form/Toggle/Toggle.test.stories.tsx +1167 -0
- package/src/components/form/Toggle/Toggle.tsx +265 -0
- package/src/components/form/Toggle/Toggle.types.ts +79 -0
- package/src/components/form/Toggle/index.ts +2 -0
- package/src/components/form/ToggleGroup/ToggleGroup.md +329 -0
- package/src/components/form/ToggleGroup/ToggleGroup.stories.tsx +617 -0
- package/src/components/form/ToggleGroup/ToggleGroup.test.stories.tsx +918 -0
- package/src/components/form/ToggleGroup/ToggleGroup.tsx +129 -0
- package/src/components/form/ToggleGroup/ToggleGroup.types.ts +53 -0
- package/src/components/form/ToggleGroup/index.ts +2 -0
- package/src/components/form/UploadButton/UploadButton.md +44 -0
- package/src/components/form/UploadButton/UploadButton.stories.tsx +366 -0
- package/src/components/form/UploadButton/UploadButton.test.stories.tsx +454 -0
- package/src/components/form/UploadButton/UploadButton.tsx +312 -0
- package/src/components/form/UploadButton/UploadButton.types.ts +58 -0
- package/src/components/form/UploadButton/index.ts +2 -0
- package/src/components/form/index.ts +25 -0
- package/src/components/form/total-form/Fields/SelectField.tsx +57 -0
- package/src/components/form/total-form/Fields/TextField.tsx +58 -0
- package/src/components/form/total-form/Fields/index.ts +4 -0
- package/src/components/form/total-form/FormContainer.tsx +104 -0
- package/src/components/form/total-form/FormContext.tsx +38 -0
- package/src/components/form/total-form/FormErrorSnackbar.tsx +35 -0
- package/src/components/form/total-form/SubmitButton.tsx +27 -0
- package/src/components/form/total-form/index.ts +12 -0
- package/src/components/form/total-form/total-form.test.tsx +97 -0
- package/src/components/index.ts +7 -0
- package/src/components/layout/Accordion/Accordion.md +725 -0
- package/src/components/layout/Accordion/Accordion.stories.tsx +690 -0
- package/src/components/layout/Accordion/Accordion.test.stories.tsx +1362 -0
- package/src/components/layout/Accordion/Accordion.tsx +242 -0
- package/src/components/layout/Accordion/Accordion.types.ts +32 -0
- package/src/components/layout/Accordion/index.ts +8 -0
- package/src/components/layout/Card/Card.md +533 -0
- package/src/components/layout/Card/Card.stories.tsx +593 -0
- package/src/components/layout/Card/Card.test.stories.tsx +1134 -0
- package/src/components/layout/Card/Card.tsx +404 -0
- package/src/components/layout/Card/Card.types.ts +57 -0
- package/src/components/layout/Card/index.ts +8 -0
- package/src/components/layout/Collapsible/Collapsible.md +135 -0
- package/src/components/layout/Collapsible/Collapsible.stories.tsx +180 -0
- package/src/components/layout/Collapsible/Collapsible.test.stories.tsx +304 -0
- package/src/components/layout/Collapsible/Collapsible.tsx +198 -0
- package/src/components/layout/Collapsible/Collapsible.types.ts +35 -0
- package/src/components/layout/Collapsible/index.ts +6 -0
- package/src/components/layout/Container/Container.md +420 -0
- package/src/components/layout/Container/Container.stories.tsx +557 -0
- package/src/components/layout/Container/Container.test.stories.tsx +750 -0
- package/src/components/layout/Container/Container.tsx +72 -0
- package/src/components/layout/Container/Container.types.ts +11 -0
- package/src/components/layout/Container/index.ts +2 -0
- package/src/components/layout/ContentToolbar/ColumnsMenu.tsx +99 -0
- package/src/components/layout/ContentToolbar/ContentToolbar.md +75 -0
- package/src/components/layout/ContentToolbar/ContentToolbar.stories.tsx +129 -0
- package/src/components/layout/ContentToolbar/ContentToolbar.test.stories.tsx +251 -0
- package/src/components/layout/ContentToolbar/ContentToolbar.tsx +108 -0
- package/src/components/layout/ContentToolbar/ContentToolbar.types.ts +229 -0
- package/src/components/layout/ContentToolbar/FilterTrigger.tsx +71 -0
- package/src/components/layout/ContentToolbar/MultiSelectDropdown.tsx +297 -0
- package/src/components/layout/ContentToolbar/SavedViewsMenu.tsx +334 -0
- package/src/components/layout/ContentToolbar/SortByDropdown.tsx +213 -0
- package/src/components/layout/ContentToolbar/ViewSelector.tsx +98 -0
- package/src/components/layout/ContentToolbar/__tests__/ColumnsMenu.test.tsx +39 -0
- package/src/components/layout/ContentToolbar/__tests__/MultiSelectDropdown.test.tsx +110 -0
- package/src/components/layout/ContentToolbar/__tests__/SavedViewsMenu.test.tsx +99 -0
- package/src/components/layout/ContentToolbar/index.ts +24 -0
- package/src/components/layout/ContentToolbar/tests.md +42 -0
- package/src/components/layout/ContentToolbar/track.md +41 -0
- package/src/components/layout/Dashboard/Dashboard.md +71 -0
- package/src/components/layout/Dashboard/Dashboard.stories.tsx +104 -0
- package/src/components/layout/Dashboard/Dashboard.test.stories.tsx +164 -0
- package/src/components/layout/Dashboard/Dashboard.tsx +297 -0
- package/src/components/layout/Dashboard/Dashboard.types.ts +157 -0
- package/src/components/layout/Dashboard/DashboardContext.tsx +47 -0
- package/src/components/layout/Dashboard/DashboardParts.tsx +216 -0
- package/src/components/layout/Dashboard/index.ts +18 -0
- package/src/components/layout/Dashboard/tests.md +46 -0
- package/src/components/layout/Dashboard/track.md +50 -0
- package/src/components/layout/Drawer/Drawer.md +479 -0
- package/src/components/layout/Drawer/Drawer.stories.tsx +660 -0
- package/src/components/layout/Drawer/Drawer.test.stories.tsx +1339 -0
- package/src/components/layout/Drawer/Drawer.tsx +150 -0
- package/src/components/layout/Drawer/Drawer.types.ts +34 -0
- package/src/components/layout/Drawer/index.ts +2 -0
- package/src/components/layout/Resizable/Resizable.md +308 -0
- package/src/components/layout/Resizable/Resizable.stories.tsx +1039 -0
- package/src/components/layout/Resizable/Resizable.test.stories.tsx +750 -0
- package/src/components/layout/Resizable/Resizable.tsx +190 -0
- package/src/components/layout/Resizable/Resizable.types.ts +34 -0
- package/src/components/layout/Resizable/index.ts +2 -0
- package/src/components/layout/ScrollArea/ScrollArea.md +122 -0
- package/src/components/layout/ScrollArea/ScrollArea.stories.tsx +651 -0
- package/src/components/layout/ScrollArea/ScrollArea.test.stories.tsx +619 -0
- package/src/components/layout/ScrollArea/ScrollArea.tsx +449 -0
- package/src/components/layout/ScrollArea/ScrollArea.types.ts +80 -0
- package/src/components/layout/ScrollArea/index.ts +7 -0
- package/src/components/layout/Separator/Separator.stories.tsx +563 -0
- package/src/components/layout/Separator/Separator.test.stories.tsx +527 -0
- package/src/components/layout/Separator/Separator.tsx +187 -0
- package/src/components/layout/Separator/Separator.types.ts +17 -0
- package/src/components/layout/Separator/index.ts +2 -0
- package/src/components/layout/SettingsLayout/SettingsLayout.md +139 -0
- package/src/components/layout/SettingsLayout/SettingsLayout.parts.tsx +357 -0
- package/src/components/layout/SettingsLayout/SettingsLayout.stories.tsx +194 -0
- package/src/components/layout/SettingsLayout/SettingsLayout.test.stories.tsx +175 -0
- package/src/components/layout/SettingsLayout/SettingsLayout.tsx +69 -0
- package/src/components/layout/SettingsLayout/SettingsLayout.types.ts +61 -0
- package/src/components/layout/SettingsLayout/index.ts +6 -0
- package/src/components/layout/Sidebar/Sidebar.md +367 -0
- package/src/components/layout/Sidebar/Sidebar.stories.tsx +611 -0
- package/src/components/layout/Sidebar/Sidebar.test.stories.tsx +1326 -0
- package/src/components/layout/Sidebar/Sidebar.tsx +131 -0
- package/src/components/layout/Sidebar/Sidebar.types.ts +30 -0
- package/src/components/layout/Sidebar/index.ts +7 -0
- package/src/components/layout/Skeleton/Skeleton.md +240 -0
- package/src/components/layout/Skeleton/Skeleton.stories.tsx +689 -0
- package/src/components/layout/Skeleton/Skeleton.test.stories.tsx +521 -0
- package/src/components/layout/Skeleton/Skeleton.tsx +166 -0
- package/src/components/layout/Skeleton/Skeleton.types.ts +19 -0
- package/src/components/layout/Skeleton/index.ts +2 -0
- package/src/components/layout/Spacer/Spacer.md +84 -0
- package/src/components/layout/Spacer/Spacer.stories.tsx +502 -0
- package/src/components/layout/Spacer/Spacer.test.stories.tsx +472 -0
- package/src/components/layout/Spacer/Spacer.tsx +68 -0
- package/src/components/layout/Spacer/Spacer.types.ts +12 -0
- package/src/components/layout/Spacer/index.ts +2 -0
- package/src/components/layout/TableFilter/TableFilter.md +55 -0
- package/src/components/layout/TableFilter/TableFilter.stories.tsx +82 -0
- package/src/components/layout/TableFilter/TableFilter.test.stories.tsx +87 -0
- package/src/components/layout/TableFilter/TableFilter.tsx +383 -0
- package/src/components/layout/TableFilter/TableFilter.types.ts +87 -0
- package/src/components/layout/TableFilter/TableFilterContext.tsx +29 -0
- package/src/components/layout/TableFilter/index.ts +15 -0
- package/src/components/layout/TableFilter/tests.md +37 -0
- package/src/components/layout/TableFilter/track.md +36 -0
- package/src/components/layout/index.ts +14 -0
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.md +558 -0
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.stories.tsx +640 -0
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.test.stories.tsx +1007 -0
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.tsx +516 -0
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.types.ts +132 -0
- package/src/components/navigation/Breadcrumbs/index.ts +2 -0
- package/src/components/navigation/CommandPalette/CommandPalette.md +201 -0
- package/src/components/navigation/CommandPalette/CommandPalette.stories.tsx +384 -0
- package/src/components/navigation/CommandPalette/CommandPalette.test.stories.tsx +437 -0
- package/src/components/navigation/CommandPalette/CommandPalette.tsx +434 -0
- package/src/components/navigation/CommandPalette/CommandPalette.types.ts +25 -0
- package/src/components/navigation/CommandPalette/index.ts +2 -0
- package/src/components/navigation/ContextMenu/ContextMenu.md +535 -0
- package/src/components/navigation/ContextMenu/ContextMenu.stories.tsx +611 -0
- package/src/components/navigation/ContextMenu/ContextMenu.test.stories.tsx +850 -0
- package/src/components/navigation/ContextMenu/ContextMenu.tsx +254 -0
- package/src/components/navigation/ContextMenu/ContextMenu.types.ts +96 -0
- package/src/components/navigation/ContextMenu/index.ts +2 -0
- package/src/components/navigation/DropdownMenu/DropdownMenu.md +514 -0
- package/src/components/navigation/DropdownMenu/DropdownMenu.stories.tsx +580 -0
- package/src/components/navigation/DropdownMenu/DropdownMenu.test.stories.tsx +660 -0
- package/src/components/navigation/DropdownMenu/DropdownMenu.tsx +305 -0
- package/src/components/navigation/DropdownMenu/DropdownMenu.types.ts +116 -0
- package/src/components/navigation/DropdownMenu/index.ts +2 -0
- package/src/components/navigation/NavigationMenu/NavigationMenu.md +580 -0
- package/src/components/navigation/NavigationMenu/NavigationMenu.tsx +874 -0
- package/src/components/navigation/NavigationMenu/NavigationMenu.types.ts +135 -0
- package/src/components/navigation/NavigationMenu/index.ts +2 -0
- package/src/components/navigation/NavigationMenu/stories/NavigationMenu.stories.tsx +518 -0
- package/src/components/navigation/NavigationMenu/stories/NavigationMenu.test.stories.tsx +931 -0
- package/src/components/navigation/NavigationMenu/stories/NavigationMenuHorizontal.stories.tsx +503 -0
- package/src/components/navigation/NavigationMenu/stories/NavigationMenuVertical.stories.tsx +455 -0
- package/src/components/navigation/Pagination/Pagination.md +699 -0
- package/src/components/navigation/Pagination/Pagination.stories.tsx +604 -0
- package/src/components/navigation/Pagination/Pagination.test.stories.tsx +520 -0
- package/src/components/navigation/Pagination/Pagination.tsx +356 -0
- package/src/components/navigation/Pagination/Pagination.types.ts +129 -0
- package/src/components/navigation/Pagination/index.ts +2 -0
- package/src/components/navigation/Tabs/Tabs.md +774 -0
- package/src/components/navigation/Tabs/Tabs.stories.tsx +1006 -0
- package/src/components/navigation/Tabs/Tabs.test.stories.tsx +1225 -0
- package/src/components/navigation/Tabs/Tabs.tsx +433 -0
- package/src/components/navigation/Tabs/Tabs.types.ts +239 -0
- package/src/components/navigation/Tabs/index.ts +2 -0
- package/src/components/navigation/index.ts +7 -0
- package/src/components/typography/Blockquote/Blockquote.md +130 -0
- package/src/components/typography/Blockquote/Blockquote.stories.tsx +359 -0
- package/src/components/typography/Blockquote/Blockquote.test.stories.tsx +432 -0
- package/src/components/typography/Blockquote/Blockquote.tsx +166 -0
- package/src/components/typography/Blockquote/Blockquote.types.ts +11 -0
- package/src/components/typography/Blockquote/index.ts +2 -0
- package/src/components/typography/Code/Code.md +182 -0
- package/src/components/typography/Code/Code.stories.tsx +686 -0
- package/src/components/typography/Code/Code.test.stories.tsx +703 -0
- package/src/components/typography/Code/Code.tsx +228 -0
- package/src/components/typography/Code/Code.types.ts +12 -0
- package/src/components/typography/Code/index.ts +2 -0
- package/src/components/typography/Heading/Heading.md +143 -0
- package/src/components/typography/Heading/Heading.stories.tsx +451 -0
- package/src/components/typography/Heading/Heading.test.stories.tsx +830 -0
- package/src/components/typography/Heading/Heading.tsx +178 -0
- package/src/components/typography/Heading/Heading.types.ts +11 -0
- package/src/components/typography/Heading/index.ts +2 -0
- package/src/components/typography/Paragraph/Paragraph.md +80 -0
- package/src/components/typography/Paragraph/Paragraph.stories.tsx +280 -0
- package/src/components/typography/Paragraph/Paragraph.test.stories.tsx +238 -0
- package/src/components/typography/Paragraph/Paragraph.tsx +99 -0
- package/src/components/typography/Paragraph/Paragraph.types.ts +10 -0
- package/src/components/typography/Paragraph/index.ts +2 -0
- package/src/components/typography/Text/Text.md +129 -0
- package/src/components/typography/Text/Text.stories.tsx +359 -0
- package/src/components/typography/Text/Text.test.stories.tsx +762 -0
- package/src/components/typography/Text/Text.tsx +159 -0
- package/src/components/typography/Text/Text.types.ts +15 -0
- package/src/components/typography/Text/index.ts +2 -0
- package/src/components/typography/index.ts +5 -0
- package/src/components/utility/AnimatedIcon/AnimatedIcon.md +359 -0
- package/src/components/utility/AnimatedIcon/AnimatedIcon.stories.tsx +947 -0
- package/src/components/utility/AnimatedIcon/AnimatedIcon.test.stories.tsx +596 -0
- package/src/components/utility/AnimatedIcon/AnimatedIcon.tsx +595 -0
- package/src/components/utility/AnimatedIcon/AnimatedIcon.types.ts +98 -0
- package/src/components/utility/AnimatedIcon/index.ts +2 -0
- package/src/components/utility/AspectRatio/AspectRatio.md +200 -0
- package/src/components/utility/AspectRatio/AspectRatio.stories.tsx +414 -0
- package/src/components/utility/AspectRatio/AspectRatio.test.stories.tsx +859 -0
- package/src/components/utility/AspectRatio/AspectRatio.tsx +76 -0
- package/src/components/utility/AspectRatio/AspectRatio.types.ts +13 -0
- package/src/components/utility/AspectRatio/index.ts +2 -0
- package/src/components/utility/InfiniteScroll/InfiniteScroll.md +531 -0
- package/src/components/utility/InfiniteScroll/InfiniteScroll.stories.tsx +620 -0
- package/src/components/utility/InfiniteScroll/InfiniteScroll.test.stories.tsx +358 -0
- package/src/components/utility/InfiniteScroll/InfiniteScroll.tsx +204 -0
- package/src/components/utility/InfiniteScroll/InfiniteScroll.types.ts +39 -0
- package/src/components/utility/InfiniteScroll/index.ts +6 -0
- package/src/components/utility/Portal/Portal.button-container.example.tsx +99 -0
- package/src/components/utility/Portal/Portal.md +593 -0
- package/src/components/utility/Portal/Portal.stories.tsx +340 -0
- package/src/components/utility/Portal/Portal.test.stories.tsx +2086 -0
- package/src/components/utility/Portal/Portal.tsx +39 -0
- package/src/components/utility/Portal/Portal.types.ts +7 -0
- package/src/components/utility/Portal/index.ts +2 -0
- package/src/components/utility/Transition/Transition.md +684 -0
- package/src/components/utility/Transition/Transition.stories.tsx +493 -0
- package/src/components/utility/Transition/Transition.test.stories.tsx +854 -0
- package/src/components/utility/Transition/Transition.tsx +140 -0
- package/src/components/utility/Transition/Transition.types.ts +14 -0
- package/src/components/utility/Transition/index.ts +6 -0
- package/src/components/utility/VirtualList/VirtualList.md +520 -0
- package/src/components/utility/VirtualList/VirtualList.stories.tsx +1073 -0
- package/src/components/utility/VirtualList/VirtualList.test.stories.tsx +1074 -0
- package/src/components/utility/VirtualList/VirtualList.tsx +369 -0
- package/src/components/utility/VirtualList/VirtualList.types.ts +56 -0
- package/src/components/utility/VirtualList/index.ts +7 -0
- package/src/components/utility/WorkflowStep/WorkflowStep.md +187 -0
- package/src/components/utility/WorkflowStep/WorkflowStep.stories.tsx +433 -0
- package/src/components/utility/WorkflowStep/WorkflowStep.test.stories.tsx +364 -0
- package/src/components/utility/WorkflowStep/WorkflowStep.tsx +506 -0
- package/src/components/utility/WorkflowStep/WorkflowStep.types.ts +133 -0
- package/src/components/utility/WorkflowStep/index.ts +13 -0
- package/src/components/utility/index.ts +7 -0
- package/src/index.ts +17 -0
- package/src/mui/AppBar.ts +2 -0
- package/src/mui/Box.ts +2 -0
- package/src/mui/CssBaseline.ts +2 -0
- package/src/mui/FormControl.ts +2 -0
- package/src/mui/FormControlLabel.ts +2 -0
- package/src/mui/Grid2.ts +2 -0
- package/src/mui/InputLabel.ts +2 -0
- package/src/mui/List.ts +2 -0
- package/src/mui/ListItem.ts +2 -0
- package/src/mui/ListItemButton.ts +2 -0
- package/src/mui/ListItemText.ts +2 -0
- package/src/mui/Stack.ts +2 -0
- package/src/mui/SvgIcon.ts +2 -0
- package/src/mui/Toolbar.ts +2 -0
- package/src/mui/styles.ts +2 -0
- package/src/mui/useMediaQuery.ts +1 -0
- package/src/social-login-button.tsx +199 -0
- package/src/user-avatar.tsx +102 -0
- package/src/utils/cn.ts +9 -0
- package/src/utils/index.ts +1 -0
|
@@ -0,0 +1,774 @@
|
|
|
1
|
+
# Tabs Component
|
|
2
|
+
|
|
3
|
+
A highly customizable tabs component for organizing content into separate views accessible through tab navigation.
|
|
4
|
+
|
|
5
|
+
## Purpose and Use Cases
|
|
6
|
+
|
|
7
|
+
The Tabs component provides an intuitive way to organize related content into separate panels, allowing users to switch between different views without leaving the current page context. It supports multiple visual styles, orientations, and advanced features like closable tabs, badges, and loading states.
|
|
8
|
+
|
|
9
|
+
### Common Use Cases
|
|
10
|
+
|
|
11
|
+
- Settings panels with different configuration sections
|
|
12
|
+
- Dashboard views with multiple data perspectives
|
|
13
|
+
- Product information organized by categories (Overview, Specs, Reviews)
|
|
14
|
+
- Documentation with different sections
|
|
15
|
+
- Form wizards with multiple steps
|
|
16
|
+
- Profile pages with different content areas (Posts, About, Photos)
|
|
17
|
+
- Admin panels with multiple management sections
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- **Multiple Variants**: Default, pills, underline, and enclosed styles
|
|
22
|
+
- **Size Options**: Small, medium, and large sizes
|
|
23
|
+
- **Closable Tabs**: Optional close buttons for dynamic tabs
|
|
24
|
+
- **Badge Support**: Display notification counts or status indicators
|
|
25
|
+
- **Icon Support**: Add icons to tab labels
|
|
26
|
+
- **Loading States**: Built-in loading indicators for async content
|
|
27
|
+
- **Content Animation**: Smooth transitions between tab panels
|
|
28
|
+
- **Vertical/Horizontal**: Support for both orientations
|
|
29
|
+
- **Scrollable Tabs**: Automatic scrolling for many tabs
|
|
30
|
+
- **Accessibility**: Full ARIA support and keyboard navigation
|
|
31
|
+
- **Content Persistence**: Option to keep inactive panels in DOM
|
|
32
|
+
- **Theme Integration**: Supports light/dark themes and custom colors
|
|
33
|
+
|
|
34
|
+
## Props Documentation
|
|
35
|
+
|
|
36
|
+
### Core Props
|
|
37
|
+
|
|
38
|
+
| Prop | Type | Default | Description |
|
|
39
|
+
| ---------- | -------------------------------------- | ---------- | ----------------------------------- |
|
|
40
|
+
| `items` | `TabItem[]` | Required | Array of tab configuration objects |
|
|
41
|
+
| `value` | `string` | Required | Currently active tab ID |
|
|
42
|
+
| `onChange` | `(event, tabId: string) => void` | Required | Callback when active tab changes |
|
|
43
|
+
|
|
44
|
+
### TabItem Interface
|
|
45
|
+
|
|
46
|
+
| Property | Type | Required | Description |
|
|
47
|
+
| ---------- | ----------- | -------- | ----------------------------------- |
|
|
48
|
+
| `id` | `string` | Yes | Unique identifier for the tab |
|
|
49
|
+
| `label` | `string` | Yes | Tab label text |
|
|
50
|
+
| `content` | `ReactNode` | Yes | Content to display in panel |
|
|
51
|
+
| `icon` | `ReactNode` | No | Icon to display before label |
|
|
52
|
+
| `badge` | `string \| number` | No | Badge content (notification count) |
|
|
53
|
+
| `disabled` | `boolean` | No | Whether tab is disabled |
|
|
54
|
+
| `closable` | `boolean` | No | Whether tab can be closed |
|
|
55
|
+
| `className`| `string` | No | Custom CSS class for the tab |
|
|
56
|
+
|
|
57
|
+
### Style Props
|
|
58
|
+
|
|
59
|
+
| Prop | Type | Default | Description |
|
|
60
|
+
| ---------- | ------------------------------------------------- | ----------- | -------------------------- |
|
|
61
|
+
| `variant` | `'default' \| 'pills' \| 'underline' \| 'enclosed'` | `'default'` | Visual style variant |
|
|
62
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size of tabs |
|
|
63
|
+
| `color` | `'primary' \| 'secondary'` | `'primary'` | Color theme |
|
|
64
|
+
| `indicatorColor` | `string` | `undefined` | Custom indicator color |
|
|
65
|
+
|
|
66
|
+
### Layout Props
|
|
67
|
+
|
|
68
|
+
| Prop | Type | Default | Description |
|
|
69
|
+
| ------------- | ----------------------------- | -------------- | -------------------------------- |
|
|
70
|
+
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Tab bar orientation |
|
|
71
|
+
| `fullWidth` | `boolean` | `false` | Tabs fill full container width |
|
|
72
|
+
| `centered` | `boolean` | `false` | Center tabs horizontally |
|
|
73
|
+
| `showDividers`| `boolean` | `false` | Show dividers between tabs |
|
|
74
|
+
|
|
75
|
+
### Scrolling Props
|
|
76
|
+
|
|
77
|
+
| Prop | Type | Default | Description |
|
|
78
|
+
| ---------------- | --------------------------------------- | -------- | ------------------------------ |
|
|
79
|
+
| `scrollable` | `boolean` | `false` | Enable scrolling for many tabs |
|
|
80
|
+
| `scrollButtons` | `'auto' \| 'desktop' \| 'on' \| 'off'` | `'auto'` | Scroll button behavior |
|
|
81
|
+
| `sticky` | `boolean` | `false` | Pin the tab list while panels scroll beneath it |
|
|
82
|
+
| `stickyOffset` | `number` | `0` | Top offset (px) when `sticky` — e.g. a fixed header height |
|
|
83
|
+
|
|
84
|
+
### Content Props
|
|
85
|
+
|
|
86
|
+
| Prop | Type | Default | Description |
|
|
87
|
+
| ------------------- | ----------- | ------- | ---------------------------------------- |
|
|
88
|
+
| `animateContent` | `boolean` | `false` | Animate transitions between panels |
|
|
89
|
+
| `animationDuration` | `number` | `300` | Animation duration in milliseconds |
|
|
90
|
+
| `persistContent` | `boolean` | `false` | Keep inactive panels in DOM |
|
|
91
|
+
| `loading` | `boolean` | `false` | Show loading state in panels |
|
|
92
|
+
| `loadingComponent` | `ReactNode` | `undefined` | Custom loading component |
|
|
93
|
+
|
|
94
|
+
### Interaction Props
|
|
95
|
+
|
|
96
|
+
| Prop | Type | Default | Description |
|
|
97
|
+
| ----------- | ----------------------- | ----------- | ---------------------------------- |
|
|
98
|
+
| `onTabClose`| `(tabId: string) => void` | `undefined` | Callback when closable tab closed |
|
|
99
|
+
| `disabled` | `boolean` | `false` | Disable all tab interactions |
|
|
100
|
+
|
|
101
|
+
### Other Props
|
|
102
|
+
|
|
103
|
+
| Prop | Type | Default | Description |
|
|
104
|
+
| -------------- | ------------------------- | ----------- | ------------------------------- |
|
|
105
|
+
| `className` | `string` | `undefined` | Additional CSS class name |
|
|
106
|
+
| `dataTestId` | `string` | `undefined` | Test ID for testing purposes |
|
|
107
|
+
| `tabPanelProps`| `{ className?: string }` | `undefined` | Props for tab panel container |
|
|
108
|
+
|
|
109
|
+
## Usage Examples
|
|
110
|
+
|
|
111
|
+
### Basic Tabs
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
import { Tabs } from '@/components/navigation/Tabs';
|
|
115
|
+
import { useState } from 'react';
|
|
116
|
+
|
|
117
|
+
function BasicExample() {
|
|
118
|
+
const [activeTab, setActiveTab] = useState('tab1');
|
|
119
|
+
|
|
120
|
+
const items = [
|
|
121
|
+
{
|
|
122
|
+
id: 'tab1',
|
|
123
|
+
label: 'Overview',
|
|
124
|
+
content: <div>Overview content goes here</div>,
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
id: 'tab2',
|
|
128
|
+
label: 'Details',
|
|
129
|
+
content: <div>Details content goes here</div>,
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
id: 'tab3',
|
|
133
|
+
label: 'Settings',
|
|
134
|
+
content: <div>Settings content goes here</div>,
|
|
135
|
+
},
|
|
136
|
+
];
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<Tabs
|
|
140
|
+
items={items}
|
|
141
|
+
value={activeTab}
|
|
142
|
+
onChange={(event, newValue) => setActiveTab(newValue)}
|
|
143
|
+
/>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Tabs with Icons and Badges
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
import { Home, Settings, Notifications } from '@mui/icons-material';
|
|
152
|
+
|
|
153
|
+
function TabsWithIconsAndBadges() {
|
|
154
|
+
const [activeTab, setActiveTab] = useState('home');
|
|
155
|
+
|
|
156
|
+
const items = [
|
|
157
|
+
{
|
|
158
|
+
id: 'home',
|
|
159
|
+
label: 'Home',
|
|
160
|
+
icon: <Home />,
|
|
161
|
+
content: <div>Home content</div>,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
id: 'notifications',
|
|
165
|
+
label: 'Notifications',
|
|
166
|
+
icon: <Notifications />,
|
|
167
|
+
badge: 5,
|
|
168
|
+
content: <div>Notifications content</div>,
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
id: 'settings',
|
|
172
|
+
label: 'Settings',
|
|
173
|
+
icon: <Settings />,
|
|
174
|
+
content: <div>Settings content</div>,
|
|
175
|
+
},
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
return (
|
|
179
|
+
<Tabs
|
|
180
|
+
items={items}
|
|
181
|
+
value={activeTab}
|
|
182
|
+
onChange={(event, newValue) => setActiveTab(newValue)}
|
|
183
|
+
/>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Different Variants
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
// Default variant (Material-UI style with indicator)
|
|
192
|
+
<Tabs items={items} value={activeTab} onChange={handleChange} variant="default" />
|
|
193
|
+
|
|
194
|
+
// Pills variant (rounded tabs with filled background)
|
|
195
|
+
<Tabs items={items} value={activeTab} onChange={handleChange} variant="pills" />
|
|
196
|
+
|
|
197
|
+
// Underline variant (minimal with bottom border)
|
|
198
|
+
<Tabs items={items} value={activeTab} onChange={handleChange} variant="underline" />
|
|
199
|
+
|
|
200
|
+
// Enclosed variant (bordered tabs like browser tabs)
|
|
201
|
+
<Tabs items={items} value={activeTab} onChange={handleChange} variant="enclosed" />
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Closable Tabs
|
|
205
|
+
|
|
206
|
+
```tsx
|
|
207
|
+
function ClosableTabsExample() {
|
|
208
|
+
const [tabs, setTabs] = useState([
|
|
209
|
+
{ id: '1', label: 'Tab 1', content: <div>Content 1</div>, closable: true },
|
|
210
|
+
{ id: '2', label: 'Tab 2', content: <div>Content 2</div>, closable: true },
|
|
211
|
+
{ id: '3', label: 'Tab 3', content: <div>Content 3</div>, closable: true },
|
|
212
|
+
]);
|
|
213
|
+
const [activeTab, setActiveTab] = useState('1');
|
|
214
|
+
|
|
215
|
+
const handleTabClose = (tabId: string) => {
|
|
216
|
+
const newTabs = tabs.filter((tab) => tab.id !== tabId);
|
|
217
|
+
setTabs(newTabs);
|
|
218
|
+
|
|
219
|
+
// Switch to first tab if closing active tab
|
|
220
|
+
if (tabId === activeTab && newTabs.length > 0) {
|
|
221
|
+
setActiveTab(newTabs[0].id);
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
return (
|
|
226
|
+
<Tabs
|
|
227
|
+
items={tabs}
|
|
228
|
+
value={activeTab}
|
|
229
|
+
onChange={(event, newValue) => setActiveTab(newValue)}
|
|
230
|
+
onTabClose={handleTabClose}
|
|
231
|
+
/>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Vertical Tabs
|
|
237
|
+
|
|
238
|
+
```tsx
|
|
239
|
+
<Tabs
|
|
240
|
+
items={items}
|
|
241
|
+
value={activeTab}
|
|
242
|
+
onChange={handleChange}
|
|
243
|
+
orientation="vertical"
|
|
244
|
+
sx={{ display: 'flex', height: 400 }}
|
|
245
|
+
/>
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Scrollable Tabs
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
function ScrollableTabsExample() {
|
|
252
|
+
const items = Array.from({ length: 20 }, (_, i) => ({
|
|
253
|
+
id: `tab-${i}`,
|
|
254
|
+
label: `Tab ${i + 1}`,
|
|
255
|
+
content: <div>Content for tab {i + 1}</div>,
|
|
256
|
+
}));
|
|
257
|
+
|
|
258
|
+
return (
|
|
259
|
+
<Tabs
|
|
260
|
+
items={items}
|
|
261
|
+
value={activeTab}
|
|
262
|
+
onChange={handleChange}
|
|
263
|
+
scrollable
|
|
264
|
+
scrollButtons="auto"
|
|
265
|
+
/>
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Sticky, Scrollable Tabs
|
|
271
|
+
|
|
272
|
+
Pin the tab list under a fixed header while the panels scroll, and let the row
|
|
273
|
+
scroll horizontally when there are more tabs than fit:
|
|
274
|
+
|
|
275
|
+
```tsx
|
|
276
|
+
<Tabs
|
|
277
|
+
items={items}
|
|
278
|
+
value={activeTab}
|
|
279
|
+
onChange={handleChange}
|
|
280
|
+
variant="underline"
|
|
281
|
+
scrollable
|
|
282
|
+
sticky
|
|
283
|
+
stickyOffset={64} // height of your fixed header
|
|
284
|
+
/>
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### With Loading States
|
|
288
|
+
|
|
289
|
+
```tsx
|
|
290
|
+
function LoadingTabsExample() {
|
|
291
|
+
const [loading, setLoading] = useState(false);
|
|
292
|
+
const [activeTab, setActiveTab] = useState('tab1');
|
|
293
|
+
|
|
294
|
+
const handleChange = async (event, newValue) => {
|
|
295
|
+
setLoading(true);
|
|
296
|
+
setActiveTab(newValue);
|
|
297
|
+
|
|
298
|
+
// Simulate async content loading
|
|
299
|
+
await fetchTabContent(newValue);
|
|
300
|
+
setLoading(false);
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
return (
|
|
304
|
+
<Tabs
|
|
305
|
+
items={items}
|
|
306
|
+
value={activeTab}
|
|
307
|
+
onChange={handleChange}
|
|
308
|
+
loading={loading}
|
|
309
|
+
animateContent
|
|
310
|
+
/>
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### With Custom Content Animation
|
|
316
|
+
|
|
317
|
+
```tsx
|
|
318
|
+
<Tabs
|
|
319
|
+
items={items}
|
|
320
|
+
value={activeTab}
|
|
321
|
+
onChange={handleChange}
|
|
322
|
+
animateContent
|
|
323
|
+
animationDuration={500}
|
|
324
|
+
persistContent={false}
|
|
325
|
+
/>
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Different Sizes
|
|
329
|
+
|
|
330
|
+
```tsx
|
|
331
|
+
// Small size (compact layouts)
|
|
332
|
+
<Tabs items={items} value={activeTab} onChange={handleChange} size="sm" />
|
|
333
|
+
|
|
334
|
+
// Medium size (default)
|
|
335
|
+
<Tabs items={items} value={activeTab} onChange={handleChange} size="md" />
|
|
336
|
+
|
|
337
|
+
// Large size (touch-friendly)
|
|
338
|
+
<Tabs items={items} value={activeTab} onChange={handleChange} size="lg" />
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Accessibility
|
|
342
|
+
|
|
343
|
+
The Tabs component is fully accessible with comprehensive ARIA support:
|
|
344
|
+
|
|
345
|
+
- **Keyboard Navigation**: Full support for Tab, arrow keys, Home, and End
|
|
346
|
+
- **ARIA Attributes**: Proper `role`, `aria-controls`, `aria-labelledby`, `aria-selected`
|
|
347
|
+
- **Screen Reader Support**: Clear announcements for tab changes
|
|
348
|
+
- **Focus Management**: Logical focus order and visible focus indicators
|
|
349
|
+
- **Disabled States**: Proper `aria-disabled` attributes
|
|
350
|
+
- **WCAG Compliance**: Meets WCAG 2.1 AA standards
|
|
351
|
+
|
|
352
|
+
### Keyboard Shortcuts
|
|
353
|
+
|
|
354
|
+
- `Tab`: Move focus to next focusable element
|
|
355
|
+
- `Shift+Tab`: Move focus to previous focusable element
|
|
356
|
+
- `Arrow Left/Up`: Navigate to previous tab (when focused on tab)
|
|
357
|
+
- `Arrow Right/Down`: Navigate to next tab (when focused on tab)
|
|
358
|
+
- `Home`: Navigate to first tab (when focused on tab)
|
|
359
|
+
- `End`: Navigate to last tab (when focused on tab)
|
|
360
|
+
- `Enter/Space`: Activate focused tab
|
|
361
|
+
- `Escape`: Close focused closable tab (when close button focused)
|
|
362
|
+
|
|
363
|
+
## Testing
|
|
364
|
+
|
|
365
|
+
### Test IDs
|
|
366
|
+
|
|
367
|
+
The component provides comprehensive test IDs for automated testing:
|
|
368
|
+
|
|
369
|
+
| Element | Default Test ID | With Custom dataTestId | Description |
|
|
370
|
+
| ------------ | ------------------------ | --------------------------- | ------------------------------ |
|
|
371
|
+
| Container | `tabs` | `{dataTestId}` | Main tabs container |
|
|
372
|
+
| Tab List | `tabs-list` | `{dataTestId}-list` | Tabs navigation bar |
|
|
373
|
+
| Individual Tab | `tabs-tab-{index}` | `{dataTestId}-tab-{index}` | Each tab button (0-indexed) |
|
|
374
|
+
| Tab Panel | `tabs-panel-{index}` | `{dataTestId}-panel-{index}`| Each tab panel (0-indexed) |
|
|
375
|
+
|
|
376
|
+
### Testing Best Practices
|
|
377
|
+
|
|
378
|
+
1. **Use stable test IDs**: Always provide a custom `dataTestId` for tabs used in critical flows
|
|
379
|
+
2. **Test tab switching**: Verify content changes when clicking tabs
|
|
380
|
+
3. **Test keyboard navigation**: Ensure arrow key navigation works
|
|
381
|
+
4. **Test disabled states**: Verify disabled tabs cannot be activated
|
|
382
|
+
5. **Test closable tabs**: Verify close functionality and state updates
|
|
383
|
+
6. **Test loading states**: Verify loading indicators appear correctly
|
|
384
|
+
7. **Test accessibility**: Use axe-core or similar tools to verify ARIA attributes
|
|
385
|
+
|
|
386
|
+
### Common Test Scenarios
|
|
387
|
+
|
|
388
|
+
#### Testing Tab Selection
|
|
389
|
+
|
|
390
|
+
```tsx
|
|
391
|
+
import { render, screen } from '@testing-library/react';
|
|
392
|
+
import userEvent from '@testing-library/user-event';
|
|
393
|
+
|
|
394
|
+
test('switches tabs on click', async () => {
|
|
395
|
+
const handleChange = jest.fn();
|
|
396
|
+
|
|
397
|
+
render(
|
|
398
|
+
<Tabs
|
|
399
|
+
items={items}
|
|
400
|
+
value="tab1"
|
|
401
|
+
onChange={handleChange}
|
|
402
|
+
dataTestId="product-tabs"
|
|
403
|
+
/>
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
const secondTab = screen.getByTestId('product-tabs-tab-1');
|
|
407
|
+
await userEvent.click(secondTab);
|
|
408
|
+
|
|
409
|
+
expect(handleChange).toHaveBeenCalledWith(expect.anything(), 'tab2');
|
|
410
|
+
});
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
#### Testing Tab Panel Content
|
|
414
|
+
|
|
415
|
+
```tsx
|
|
416
|
+
test('displays correct content for active tab', () => {
|
|
417
|
+
const items = [
|
|
418
|
+
{ id: 'tab1', label: 'Tab 1', content: <div>Content 1</div> },
|
|
419
|
+
{ id: 'tab2', label: 'Tab 2', content: <div>Content 2</div> },
|
|
420
|
+
];
|
|
421
|
+
|
|
422
|
+
render(
|
|
423
|
+
<Tabs
|
|
424
|
+
items={items}
|
|
425
|
+
value="tab1"
|
|
426
|
+
onChange={jest.fn()}
|
|
427
|
+
dataTestId="content-tabs"
|
|
428
|
+
/>
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
const firstPanel = screen.getByTestId('content-tabs-panel-0');
|
|
432
|
+
expect(firstPanel).toHaveTextContent('Content 1');
|
|
433
|
+
expect(firstPanel).toBeVisible();
|
|
434
|
+
|
|
435
|
+
const secondPanel = screen.getByTestId('content-tabs-panel-1');
|
|
436
|
+
expect(secondPanel).not.toBeVisible();
|
|
437
|
+
});
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
#### Testing Closable Tabs
|
|
441
|
+
|
|
442
|
+
```tsx
|
|
443
|
+
test('closes tab when close button clicked', async () => {
|
|
444
|
+
const handleClose = jest.fn();
|
|
445
|
+
|
|
446
|
+
const items = [
|
|
447
|
+
{ id: 'tab1', label: 'Tab 1', content: <div>Content</div>, closable: true },
|
|
448
|
+
];
|
|
449
|
+
|
|
450
|
+
render(
|
|
451
|
+
<Tabs
|
|
452
|
+
items={items}
|
|
453
|
+
value="tab1"
|
|
454
|
+
onChange={jest.fn()}
|
|
455
|
+
onTabClose={handleClose}
|
|
456
|
+
dataTestId="closable-tabs"
|
|
457
|
+
/>
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
const closeButton = screen.getByLabelText('Close tab');
|
|
461
|
+
await userEvent.click(closeButton);
|
|
462
|
+
|
|
463
|
+
expect(handleClose).toHaveBeenCalledWith('tab1');
|
|
464
|
+
});
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
#### Testing Keyboard Navigation
|
|
468
|
+
|
|
469
|
+
```tsx
|
|
470
|
+
test('navigates tabs with arrow keys', async () => {
|
|
471
|
+
const handleChange = jest.fn();
|
|
472
|
+
|
|
473
|
+
render(
|
|
474
|
+
<Tabs
|
|
475
|
+
items={items}
|
|
476
|
+
value="tab1"
|
|
477
|
+
onChange={handleChange}
|
|
478
|
+
dataTestId="nav-tabs"
|
|
479
|
+
/>
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
const firstTab = screen.getByTestId('nav-tabs-tab-0');
|
|
483
|
+
firstTab.focus();
|
|
484
|
+
|
|
485
|
+
await userEvent.keyboard('{ArrowRight}');
|
|
486
|
+
|
|
487
|
+
// Should focus second tab
|
|
488
|
+
const secondTab = screen.getByTestId('nav-tabs-tab-1');
|
|
489
|
+
expect(secondTab).toHaveFocus();
|
|
490
|
+
});
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
#### Testing Loading States
|
|
494
|
+
|
|
495
|
+
```tsx
|
|
496
|
+
test('shows loading indicator when loading', () => {
|
|
497
|
+
render(
|
|
498
|
+
<Tabs
|
|
499
|
+
items={items}
|
|
500
|
+
value="tab1"
|
|
501
|
+
onChange={jest.fn()}
|
|
502
|
+
loading={true}
|
|
503
|
+
dataTestId="loading-tabs"
|
|
504
|
+
/>
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
const panel = screen.getByTestId('loading-tabs-panel-0');
|
|
508
|
+
expect(panel).toContainElement(screen.getByRole('progressbar'));
|
|
509
|
+
});
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
#### Testing Accessibility
|
|
513
|
+
|
|
514
|
+
```tsx
|
|
515
|
+
test('has proper ARIA attributes', () => {
|
|
516
|
+
render(
|
|
517
|
+
<Tabs
|
|
518
|
+
items={items}
|
|
519
|
+
value="tab1"
|
|
520
|
+
onChange={jest.fn()}
|
|
521
|
+
dataTestId="aria-tabs"
|
|
522
|
+
/>
|
|
523
|
+
);
|
|
524
|
+
|
|
525
|
+
const firstTab = screen.getByTestId('aria-tabs-tab-0');
|
|
526
|
+
expect(firstTab).toHaveAttribute('role', 'tab');
|
|
527
|
+
expect(firstTab).toHaveAttribute('aria-selected', 'true');
|
|
528
|
+
expect(firstTab).toHaveAttribute('aria-controls');
|
|
529
|
+
|
|
530
|
+
const firstPanel = screen.getByTestId('aria-tabs-panel-0');
|
|
531
|
+
expect(firstPanel).toHaveAttribute('role', 'tabpanel');
|
|
532
|
+
expect(firstPanel).toHaveAttribute('aria-labelledby');
|
|
533
|
+
});
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
## Best Practices
|
|
537
|
+
|
|
538
|
+
### Design Guidelines
|
|
539
|
+
|
|
540
|
+
1. **Choose appropriate variants**:
|
|
541
|
+
- `default` for standard interfaces
|
|
542
|
+
- `pills` for modern, rounded aesthetics
|
|
543
|
+
- `underline` for minimal, text-focused designs
|
|
544
|
+
- `enclosed` for browser-like tab interfaces
|
|
545
|
+
|
|
546
|
+
2. **Limit number of tabs**:
|
|
547
|
+
- Ideal: 3-7 tabs for easy scanning
|
|
548
|
+
- Use scrollable tabs for more than 7
|
|
549
|
+
- Consider alternative navigation for 10+ tabs
|
|
550
|
+
|
|
551
|
+
3. **Use clear, concise labels**:
|
|
552
|
+
- Keep labels short (1-2 words)
|
|
553
|
+
- Use consistent capitalization
|
|
554
|
+
- Add icons for visual recognition
|
|
555
|
+
|
|
556
|
+
4. **Provide visual feedback**:
|
|
557
|
+
- Use badges for notifications
|
|
558
|
+
- Show loading states for async content
|
|
559
|
+
- Maintain clear active tab indicators
|
|
560
|
+
|
|
561
|
+
### Performance Tips
|
|
562
|
+
|
|
563
|
+
1. **Use `persistContent={false}`** (default) to unmount inactive panels
|
|
564
|
+
2. **Lazy load tab content** that requires heavy computation
|
|
565
|
+
3. **Use `React.memo`** for complex tab panel content
|
|
566
|
+
4. **Debounce async operations** triggered by tab changes
|
|
567
|
+
5. **Avoid re-creating items array** on every render
|
|
568
|
+
|
|
569
|
+
### UX Recommendations
|
|
570
|
+
|
|
571
|
+
1. **Persist tab selection** in URL or state for deep linking
|
|
572
|
+
2. **Show progress indicators** for loading content
|
|
573
|
+
3. **Disable tabs** that require prerequisites
|
|
574
|
+
4. **Provide context** with icons and badges
|
|
575
|
+
5. **Test keyboard navigation** thoroughly
|
|
576
|
+
6. **Maintain consistent tab order** throughout the session
|
|
577
|
+
7. **Use animations sparingly** to avoid motion sickness
|
|
578
|
+
|
|
579
|
+
### Code Organization
|
|
580
|
+
|
|
581
|
+
```tsx
|
|
582
|
+
// Good: Define items outside component or memoize
|
|
583
|
+
const TAB_ITEMS = [
|
|
584
|
+
{ id: 'tab1', label: 'Overview', content: <Overview /> },
|
|
585
|
+
{ id: 'tab2', label: 'Details', content: <Details /> },
|
|
586
|
+
];
|
|
587
|
+
|
|
588
|
+
// Or use useMemo for dynamic items
|
|
589
|
+
const items = useMemo(() => [
|
|
590
|
+
{ id: 'tab1', label: 'Overview', content: <Overview data={data} /> },
|
|
591
|
+
], [data]);
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
## Theme Customization
|
|
595
|
+
|
|
596
|
+
The component respects your MUI theme configuration:
|
|
597
|
+
|
|
598
|
+
```tsx
|
|
599
|
+
import { createTheme } from '@mui/material/styles';
|
|
600
|
+
|
|
601
|
+
const theme = createTheme({
|
|
602
|
+
palette: {
|
|
603
|
+
primary: {
|
|
604
|
+
main: '#1976d2',
|
|
605
|
+
},
|
|
606
|
+
secondary: {
|
|
607
|
+
main: '#dc004e',
|
|
608
|
+
},
|
|
609
|
+
},
|
|
610
|
+
components: {
|
|
611
|
+
MuiTabs: {
|
|
612
|
+
styleOverrides: {
|
|
613
|
+
root: {
|
|
614
|
+
// Custom styles for tabs
|
|
615
|
+
},
|
|
616
|
+
},
|
|
617
|
+
},
|
|
618
|
+
},
|
|
619
|
+
});
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
## Common Patterns
|
|
623
|
+
|
|
624
|
+
### Settings Tabs
|
|
625
|
+
|
|
626
|
+
```tsx
|
|
627
|
+
function SettingsTabs() {
|
|
628
|
+
const [activeTab, setActiveTab] = useState('general');
|
|
629
|
+
|
|
630
|
+
const items = [
|
|
631
|
+
{
|
|
632
|
+
id: 'general',
|
|
633
|
+
label: 'General',
|
|
634
|
+
icon: <Settings />,
|
|
635
|
+
content: <GeneralSettings />,
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
id: 'security',
|
|
639
|
+
label: 'Security',
|
|
640
|
+
icon: <Security />,
|
|
641
|
+
content: <SecuritySettings />,
|
|
642
|
+
},
|
|
643
|
+
{
|
|
644
|
+
id: 'notifications',
|
|
645
|
+
label: 'Notifications',
|
|
646
|
+
icon: <Notifications />,
|
|
647
|
+
badge: hasUnreadNotifications ? '!' : undefined,
|
|
648
|
+
content: <NotificationSettings />,
|
|
649
|
+
},
|
|
650
|
+
];
|
|
651
|
+
|
|
652
|
+
return (
|
|
653
|
+
<Tabs
|
|
654
|
+
items={items}
|
|
655
|
+
value={activeTab}
|
|
656
|
+
onChange={(_, newValue) => setActiveTab(newValue)}
|
|
657
|
+
variant="pills"
|
|
658
|
+
fullWidth
|
|
659
|
+
/>
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
### Dynamic Tabs with URL Sync
|
|
665
|
+
|
|
666
|
+
```tsx
|
|
667
|
+
import { useSearchParams } from 'react-router-dom';
|
|
668
|
+
|
|
669
|
+
function DynamicTabs() {
|
|
670
|
+
const [searchParams, setSearchParams] = useSearchParams();
|
|
671
|
+
const activeTab = searchParams.get('tab') || 'overview';
|
|
672
|
+
|
|
673
|
+
const handleChange = (event, newValue) => {
|
|
674
|
+
setSearchParams({ tab: newValue });
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
return (
|
|
678
|
+
<Tabs
|
|
679
|
+
items={items}
|
|
680
|
+
value={activeTab}
|
|
681
|
+
onChange={handleChange}
|
|
682
|
+
/>
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
### Tabs with Lazy Loading
|
|
688
|
+
|
|
689
|
+
```tsx
|
|
690
|
+
function LazyLoadedTabs() {
|
|
691
|
+
const [activeTab, setActiveTab] = useState('tab1');
|
|
692
|
+
const [loadedTabs, setLoadedTabs] = useState(new Set(['tab1']));
|
|
693
|
+
|
|
694
|
+
const handleChange = (event, newValue) => {
|
|
695
|
+
setActiveTab(newValue);
|
|
696
|
+
setLoadedTabs((prev) => new Set([...prev, newValue]));
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
const items = [
|
|
700
|
+
{
|
|
701
|
+
id: 'tab1',
|
|
702
|
+
label: 'Tab 1',
|
|
703
|
+
content: <Content1 />,
|
|
704
|
+
},
|
|
705
|
+
{
|
|
706
|
+
id: 'tab2',
|
|
707
|
+
label: 'Tab 2',
|
|
708
|
+
content: loadedTabs.has('tab2') ? <Content2 /> : <LoadingSpinner />,
|
|
709
|
+
},
|
|
710
|
+
{
|
|
711
|
+
id: 'tab3',
|
|
712
|
+
label: 'Tab 3',
|
|
713
|
+
content: loadedTabs.has('tab3') ? <Content3 /> : <LoadingSpinner />,
|
|
714
|
+
},
|
|
715
|
+
];
|
|
716
|
+
|
|
717
|
+
return (
|
|
718
|
+
<Tabs
|
|
719
|
+
items={items}
|
|
720
|
+
value={activeTab}
|
|
721
|
+
onChange={handleChange}
|
|
722
|
+
animateContent
|
|
723
|
+
/>
|
|
724
|
+
);
|
|
725
|
+
}
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
## Troubleshooting
|
|
729
|
+
|
|
730
|
+
### Tab content not updating
|
|
731
|
+
|
|
732
|
+
Ensure you're using controlled state properly:
|
|
733
|
+
|
|
734
|
+
```tsx
|
|
735
|
+
// Bad: Not updating state
|
|
736
|
+
<Tabs items={items} value={activeTab} onChange={() => {}} />
|
|
737
|
+
|
|
738
|
+
// Good: Properly updating state
|
|
739
|
+
<Tabs items={items} value={activeTab} onChange={(e, v) => setActiveTab(v)} />
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
### Tabs overflowing container
|
|
743
|
+
|
|
744
|
+
Enable scrollable mode:
|
|
745
|
+
|
|
746
|
+
```tsx
|
|
747
|
+
<Tabs items={items} value={activeTab} onChange={handleChange} scrollable />
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
### Performance issues with many tabs
|
|
751
|
+
|
|
752
|
+
Use `persistContent={false}` and lazy load content:
|
|
753
|
+
|
|
754
|
+
```tsx
|
|
755
|
+
<Tabs
|
|
756
|
+
items={items}
|
|
757
|
+
value={activeTab}
|
|
758
|
+
onChange={handleChange}
|
|
759
|
+
persistContent={false}
|
|
760
|
+
/>
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
### Close button not working
|
|
764
|
+
|
|
765
|
+
Provide `onTabClose` callback:
|
|
766
|
+
|
|
767
|
+
```tsx
|
|
768
|
+
<Tabs
|
|
769
|
+
items={items}
|
|
770
|
+
value={activeTab}
|
|
771
|
+
onChange={handleChange}
|
|
772
|
+
onTabClose={(tabId) => handleRemoveTab(tabId)}
|
|
773
|
+
/>
|
|
774
|
+
```
|