taro-uno-ui 0.9.0-beta
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/LICENSE +100 -0
- package/README.md +273 -0
- package/dist/js/index-6NJ3A1Dn.js +26535 -0
- package/dist/js/index-6NJ3A1Dn.js.map +1 -0
- package/dist/js/index-DFdcksbe.js +1165 -0
- package/dist/js/index-DFdcksbe.js.map +1 -0
- package/dist/js/index-DXRIkWX1.js +1148 -0
- package/dist/js/index-DXRIkWX1.js.map +1 -0
- package/dist/js/index-DffLRSro.js +26519 -0
- package/dist/js/index-DffLRSro.js.map +1 -0
- package/package.json +119 -0
- package/src/app.config.ts +55 -0
- package/src/app.scss +508 -0
- package/src/app.tsx +44 -0
- package/src/components/basic/Button/Button.styles.ts +130 -0
- package/src/components/basic/Button/Button.test.tsx +154 -0
- package/src/components/basic/Button/Button.tsx +93 -0
- package/src/components/basic/Button/Button.types.ts +45 -0
- package/src/components/basic/Button/index.tsx +6 -0
- package/src/components/basic/Divider/Divider.styles.ts +488 -0
- package/src/components/basic/Divider/Divider.test.tsx +551 -0
- package/src/components/basic/Divider/Divider.tsx +361 -0
- package/src/components/basic/Divider/Divider.types.ts +261 -0
- package/src/components/basic/Divider/index.tsx +25 -0
- package/src/components/basic/Icon/Icon.styles.ts +359 -0
- package/src/components/basic/Icon/Icon.test.tsx +357 -0
- package/src/components/basic/Icon/Icon.tsx +154 -0
- package/src/components/basic/Icon/Icon.types.ts +210 -0
- package/src/components/basic/Icon/index.tsx +22 -0
- package/src/components/basic/Text/Text.styles.ts +500 -0
- package/src/components/basic/Text/Text.test.tsx +299 -0
- package/src/components/basic/Text/Text.tsx +340 -0
- package/src/components/basic/Text/Text.types.ts +319 -0
- package/src/components/basic/Text/index.tsx +27 -0
- package/src/components/basic/Typography/Typography.styles.ts +346 -0
- package/src/components/basic/Typography/Typography.tsx +205 -0
- package/src/components/basic/Typography/Typography.types.ts +296 -0
- package/src/components/basic/Typography/index.tsx +14 -0
- package/src/components/basic/index.tsx +302 -0
- package/src/components/common/ErrorBoundary.tsx +87 -0
- package/src/components/common/LazyComponent.tsx +246 -0
- package/src/components/common/ResponsiveContainer.tsx +93 -0
- package/src/components/common/ResponsiveGrid.tsx +183 -0
- package/src/components/common/SecurityProvider.tsx +110 -0
- package/src/components/common/ThemeProvider.tsx +128 -0
- package/src/components/common/VirtualList.tsx +368 -0
- package/src/components/common/__tests__/ErrorBoundary.test.tsx +249 -0
- package/src/components/common/index.tsx +7 -0
- package/src/components/display/Avatar/Avatar.styles.ts +62 -0
- package/src/components/display/Avatar/Avatar.test.tsx +390 -0
- package/src/components/display/Avatar/Avatar.tsx +79 -0
- package/src/components/display/Avatar/Avatar.types.ts +40 -0
- package/src/components/display/Avatar/index.ts +3 -0
- package/src/components/display/Badge/Badge.tsx +42 -0
- package/src/components/display/Badge/Badge.types.ts +29 -0
- package/src/components/display/Badge/index.ts +2 -0
- package/src/components/display/Calendar/Calendar.styles.ts +255 -0
- package/src/components/display/Calendar/Calendar.test.tsx +30 -0
- package/src/components/display/Calendar/Calendar.tsx +337 -0
- package/src/components/display/Calendar/Calendar.types.ts +91 -0
- package/src/components/display/Calendar/index.ts +3 -0
- package/src/components/display/Card/Card.styles.ts +89 -0
- package/src/components/display/Card/Card.test.tsx +180 -0
- package/src/components/display/Card/Card.tsx +135 -0
- package/src/components/display/Card/Card.types.ts +55 -0
- package/src/components/display/Card/index.ts +3 -0
- package/src/components/display/Carousel/Carousel.styles.ts +206 -0
- package/src/components/display/Carousel/Carousel.tsx +295 -0
- package/src/components/display/Carousel/Carousel.types.ts +57 -0
- package/src/components/display/Carousel/index.ts +3 -0
- package/src/components/display/List/List.styles.ts +79 -0
- package/src/components/display/List/List.tsx +115 -0
- package/src/components/display/List/List.types.ts +68 -0
- package/src/components/display/List/index.ts +3 -0
- package/src/components/display/Rate/Rate.styles.ts +266 -0
- package/src/components/display/Rate/Rate.tsx +332 -0
- package/src/components/display/Rate/Rate.types.ts +111 -0
- package/src/components/display/Rate/index.ts +28 -0
- package/src/components/display/Table/Table.styles.ts +269 -0
- package/src/components/display/Table/Table.test.tsx +343 -0
- package/src/components/display/Table/Table.tsx +430 -0
- package/src/components/display/Table/Table.types.ts +255 -0
- package/src/components/display/Table/index.tsx +16 -0
- package/src/components/display/Tag/Tag.styles.ts +197 -0
- package/src/components/display/Tag/Tag.test.tsx +541 -0
- package/src/components/display/Tag/Tag.tsx +156 -0
- package/src/components/display/Tag/Tag.types.ts +49 -0
- package/src/components/display/Tag/index.ts +3 -0
- package/src/components/display/Timeline/Timeline.styles.ts +211 -0
- package/src/components/display/Timeline/Timeline.tsx +239 -0
- package/src/components/display/Timeline/Timeline.types.ts +56 -0
- package/src/components/display/Timeline/index.ts +3 -0
- package/src/components/display/index.tsx +143 -0
- package/src/components/feedback/Loading/Loading.styles.ts +117 -0
- package/src/components/feedback/Loading/Loading.test.tsx +534 -0
- package/src/components/feedback/Loading/Loading.tsx +127 -0
- package/src/components/feedback/Loading/Loading.types.ts +33 -0
- package/src/components/feedback/Loading/index.ts +9 -0
- package/src/components/feedback/Message/Message.styles.ts +41 -0
- package/src/components/feedback/Message/Message.test.tsx +234 -0
- package/src/components/feedback/Message/Message.tsx +96 -0
- package/src/components/feedback/Message/Message.types.ts +37 -0
- package/src/components/feedback/Message/index.ts +9 -0
- package/src/components/feedback/Modal/Modal.styles.ts +21 -0
- package/src/components/feedback/Modal/Modal.test.tsx +11 -0
- package/src/components/feedback/Modal/Modal.tsx +291 -0
- package/src/components/feedback/Modal/Modal.types.ts +141 -0
- package/src/components/feedback/Modal/index.tsx +11 -0
- package/src/components/feedback/Notification/Notification.styles.ts +443 -0
- package/src/components/feedback/Notification/Notification.test.tsx +401 -0
- package/src/components/feedback/Notification/Notification.tsx +370 -0
- package/src/components/feedback/Notification/Notification.types.ts +336 -0
- package/src/components/feedback/Notification/NotificationManager.tsx +376 -0
- package/src/components/feedback/Notification/index.ts +33 -0
- package/src/components/feedback/Notification/index.tsx +164 -0
- package/src/components/feedback/Progress/Progress.styles.ts +460 -0
- package/src/components/feedback/Progress/Progress.test.simple.tsx +14 -0
- package/src/components/feedback/Progress/Progress.test.tsx +312 -0
- package/src/components/feedback/Progress/Progress.tsx +508 -0
- package/src/components/feedback/Progress/Progress.types.ts +163 -0
- package/src/components/feedback/Progress/index.ts +3 -0
- package/src/components/feedback/Progress/index.tsx +38 -0
- package/src/components/feedback/Progress/utils/animation.ts +204 -0
- package/src/components/feedback/Progress/utils/index.ts +26 -0
- package/src/components/feedback/Progress/utils/progress-calculator.ts +217 -0
- package/src/components/feedback/Result/Result.styles.ts +139 -0
- package/src/components/feedback/Result/Result.tsx +233 -0
- package/src/components/feedback/Result/Result.types.ts +128 -0
- package/src/components/feedback/Result/index.tsx +3 -0
- package/src/components/feedback/Toast/Toast.styles.ts +17 -0
- package/src/components/feedback/Toast/Toast.test.tsx +10 -0
- package/src/components/feedback/Toast/Toast.tsx +372 -0
- package/src/components/feedback/Toast/Toast.types.ts +86 -0
- package/src/components/feedback/Toast/index.tsx +3 -0
- package/src/components/feedback/Tooltip/Tooltip.examples.tsx +458 -0
- package/src/components/feedback/Tooltip/Tooltip.styles.ts +346 -0
- package/src/components/feedback/Tooltip/Tooltip.test.tsx +446 -0
- package/src/components/feedback/Tooltip/Tooltip.tsx +283 -0
- package/src/components/feedback/Tooltip/Tooltip.types.ts +172 -0
- package/src/components/feedback/Tooltip/index.ts +3 -0
- package/src/components/feedback/Tooltip/index.tsx +258 -0
- package/src/components/feedback/index.tsx +164 -0
- package/src/components/form/Cascader/Cascader.styles.ts +526 -0
- package/src/components/form/Cascader/Cascader.test.tsx +77 -0
- package/src/components/form/Cascader/Cascader.tsx +585 -0
- package/src/components/form/Cascader/Cascader.types.ts +582 -0
- package/src/components/form/Cascader/hooks/index.ts +3 -0
- package/src/components/form/Cascader/hooks/useCascaderFieldNames.ts +16 -0
- package/src/components/form/Cascader/hooks/useCascaderOptions.ts +109 -0
- package/src/components/form/Cascader/hooks/useCascaderState.ts +133 -0
- package/src/components/form/Cascader/index.ts +25 -0
- package/src/components/form/Cascader/utils/formatDisplayValue.ts +19 -0
- package/src/components/form/Cascader/utils/index.ts +1 -0
- package/src/components/form/Checkbox/Checkbox.styles.ts +608 -0
- package/src/components/form/Checkbox/Checkbox.test.tsx +1140 -0
- package/src/components/form/Checkbox/Checkbox.tsx +496 -0
- package/src/components/form/Checkbox/Checkbox.types.ts +472 -0
- package/src/components/form/Checkbox/CheckboxGroup.tsx +444 -0
- package/src/components/form/Checkbox/index.tsx +27 -0
- package/src/components/form/DatePicker/DatePicker.styles.ts +393 -0
- package/src/components/form/DatePicker/DatePicker.test.tsx +407 -0
- package/src/components/form/DatePicker/DatePicker.tsx +360 -0
- package/src/components/form/DatePicker/DatePicker.types.ts +247 -0
- package/src/components/form/DatePicker/index.tsx +15 -0
- package/src/components/form/Form/Form.styles.ts +357 -0
- package/src/components/form/Form/Form.test.tsx +122 -0
- package/src/components/form/Form/Form.tsx +695 -0
- package/src/components/form/Form/Form.types.ts +407 -0
- package/src/components/form/Form/index.tsx +31 -0
- package/src/components/form/Input/Input.enhanced.tsx +732 -0
- package/src/components/form/Input/Input.styles.ts +438 -0
- package/src/components/form/Input/Input.test.tsx +494 -0
- package/src/components/form/Input/Input.tsx +541 -0
- package/src/components/form/Input/Input.types.ts +285 -0
- package/src/components/form/Input/index.tsx +26 -0
- package/src/components/form/InputNumber/InputNumber.styles.ts +665 -0
- package/src/components/form/InputNumber/InputNumber.tsx +370 -0
- package/src/components/form/InputNumber/InputNumber.types.ts +318 -0
- package/src/components/form/InputNumber/components/InputNumberClearButton.tsx +32 -0
- package/src/components/form/InputNumber/components/InputNumberControls.tsx +42 -0
- package/src/components/form/InputNumber/components/index.ts +2 -0
- package/src/components/form/InputNumber/hooks/index.ts +4 -0
- package/src/components/form/InputNumber/hooks/useInputNumberState.ts +315 -0
- package/src/components/form/InputNumber/hooks/useInputNumberValidation.ts +147 -0
- package/src/components/form/InputNumber/index.ts +25 -0
- package/src/components/form/Radio/Radio.styles.ts +458 -0
- package/src/components/form/Radio/Radio.test.tsx +547 -0
- package/src/components/form/Radio/Radio.tsx +283 -0
- package/src/components/form/Radio/Radio.types.ts +410 -0
- package/src/components/form/Radio/index.tsx +21 -0
- package/src/components/form/Select/Select.styles.ts +514 -0
- package/src/components/form/Select/Select.test.tsx +648 -0
- package/src/components/form/Select/Select.tsx +474 -0
- package/src/components/form/Select/Select.types.ts +428 -0
- package/src/components/form/Select/index.tsx +30 -0
- package/src/components/form/Slider/Slider.styles.ts +139 -0
- package/src/components/form/Slider/Slider.test.tsx +553 -0
- package/src/components/form/Slider/Slider.tsx +326 -0
- package/src/components/form/Slider/Slider.types.ts +108 -0
- package/src/components/form/Slider/index.tsx +10 -0
- package/src/components/form/Switch/Switch.styles.ts +540 -0
- package/src/components/form/Switch/Switch.test.tsx +345 -0
- package/src/components/form/Switch/Switch.tsx +464 -0
- package/src/components/form/Switch/Switch.types.ts +386 -0
- package/src/components/form/Switch/index.tsx +26 -0
- package/src/components/form/Textarea/Textarea.styles.ts +592 -0
- package/src/components/form/Textarea/Textarea.test.tsx +1075 -0
- package/src/components/form/Textarea/Textarea.tsx +602 -0
- package/src/components/form/Textarea/Textarea.types.ts +371 -0
- package/src/components/form/Textarea/index.tsx +26 -0
- package/src/components/form/TimePicker/TimePicker.styles.ts +438 -0
- package/src/components/form/TimePicker/TimePicker.test.tsx +306 -0
- package/src/components/form/TimePicker/TimePicker.tsx +228 -0
- package/src/components/form/TimePicker/TimePicker.types.ts +385 -0
- package/src/components/form/TimePicker/index.ts +21 -0
- package/src/components/form/Transfer/Transfer.styles.ts +502 -0
- package/src/components/form/Transfer/Transfer.test.tsx +316 -0
- package/src/components/form/Transfer/Transfer.tsx +402 -0
- package/src/components/form/Transfer/Transfer.types.ts +557 -0
- package/src/components/form/Transfer/components/TransferItem.tsx +101 -0
- package/src/components/form/Transfer/components/TransferList.tsx +285 -0
- package/src/components/form/Transfer/components/TransferOperations.tsx +84 -0
- package/src/components/form/Transfer/components/TransferPagination.tsx +135 -0
- package/src/components/form/Transfer/components/TransferSearch.tsx +88 -0
- package/src/components/form/Transfer/components/index.ts +6 -0
- package/src/components/form/Transfer/hooks/index.ts +3 -0
- package/src/components/form/Transfer/hooks/useTransferData.ts +192 -0
- package/src/components/form/Transfer/hooks/useTransferState.ts +114 -0
- package/src/components/form/Transfer/index.ts +33 -0
- package/src/components/form/Upload/Upload.styles.ts +145 -0
- package/src/components/form/Upload/Upload.test.tsx +10 -0
- package/src/components/form/Upload/Upload.tsx +451 -0
- package/src/components/form/Upload/Upload.types.ts +200 -0
- package/src/components/form/Upload/index.tsx +12 -0
- package/src/components/form/index.tsx +121 -0
- package/src/components/index.tsx +146 -0
- package/src/components/layout/Affix/Affix.styles.ts +37 -0
- package/src/components/layout/Affix/Affix.test.tsx +10 -0
- package/src/components/layout/Affix/Affix.tsx +91 -0
- package/src/components/layout/Affix/Affix.types.ts +29 -0
- package/src/components/layout/Affix/index.tsx +3 -0
- package/src/components/layout/Col/Col.styles.ts +185 -0
- package/src/components/layout/Col/Col.test.tsx +535 -0
- package/src/components/layout/Col/Col.tsx +115 -0
- package/src/components/layout/Col/Col.types.ts +59 -0
- package/src/components/layout/Col/index.tsx +3 -0
- package/src/components/layout/Container/Container.styles.ts +161 -0
- package/src/components/layout/Container/Container.test.tsx +380 -0
- package/src/components/layout/Container/Container.tsx +132 -0
- package/src/components/layout/Container/Container.types.ts +63 -0
- package/src/components/layout/Container/index.tsx +3 -0
- package/src/components/layout/Grid/Grid.styles.ts +183 -0
- package/src/components/layout/Grid/Grid.test.tsx +637 -0
- package/src/components/layout/Grid/Grid.tsx +173 -0
- package/src/components/layout/Grid/Grid.types.ts +78 -0
- package/src/components/layout/Grid/index.tsx +3 -0
- package/src/components/layout/Layout/Content.tsx +38 -0
- package/src/components/layout/Layout/Footer.tsx +38 -0
- package/src/components/layout/Layout/Header.tsx +38 -0
- package/src/components/layout/Layout/Layout.styles.ts +84 -0
- package/src/components/layout/Layout/Layout.test.tsx +10 -0
- package/src/components/layout/Layout/Layout.tsx +39 -0
- package/src/components/layout/Layout/Layout.types.ts +58 -0
- package/src/components/layout/Layout/Sider.tsx +56 -0
- package/src/components/layout/Layout/index.tsx +8 -0
- package/src/components/layout/Row/Row.styles.ts +159 -0
- package/src/components/layout/Row/Row.test.tsx +467 -0
- package/src/components/layout/Row/Row.tsx +139 -0
- package/src/components/layout/Row/Row.types.ts +60 -0
- package/src/components/layout/Row/index.tsx +3 -0
- package/src/components/layout/Space/Space.styles.ts +255 -0
- package/src/components/layout/Space/Space.test.tsx +682 -0
- package/src/components/layout/Space/Space.tsx +211 -0
- package/src/components/layout/Space/Space.types.ts +92 -0
- package/src/components/layout/Space/index.tsx +12 -0
- package/src/components/layout/index.tsx +68 -0
- package/src/components/navigation/Menu/Menu.styles.ts +779 -0
- package/src/components/navigation/Menu/Menu.tsx +355 -0
- package/src/components/navigation/Menu/Menu.types.ts +231 -0
- package/src/components/navigation/Menu/Menu.utils.ts +187 -0
- package/src/components/navigation/Menu/MenuItem.tsx +126 -0
- package/src/components/navigation/Menu/SubMenu.tsx +148 -0
- package/src/components/navigation/Menu/__tests__/Menu.test.tsx +687 -0
- package/src/components/navigation/Menu/index.tsx +124 -0
- package/src/components/navigation/NavBar/NavBar.styles.ts +129 -0
- package/src/components/navigation/NavBar/NavBar.test.tsx +287 -0
- package/src/components/navigation/NavBar/NavBar.tsx +231 -0
- package/src/components/navigation/NavBar/NavBar.types.ts +54 -0
- package/src/components/navigation/NavBar/index.tsx +3 -0
- package/src/components/navigation/Pagination/Pagination.styles.ts +187 -0
- package/src/components/navigation/Pagination/Pagination.test.tsx +673 -0
- package/src/components/navigation/Pagination/Pagination.tsx +395 -0
- package/src/components/navigation/Pagination/Pagination.types.ts +86 -0
- package/src/components/navigation/Pagination/index.ts +18 -0
- package/src/components/navigation/Pagination/index.tsx +9 -0
- package/src/components/navigation/Steps/Step.tsx +56 -0
- package/src/components/navigation/Steps/Steps.styles.ts +154 -0
- package/src/components/navigation/Steps/Steps.test.tsx +12 -0
- package/src/components/navigation/Steps/Steps.tsx +113 -0
- package/src/components/navigation/Steps/Steps.types.ts +47 -0
- package/src/components/navigation/Steps/index.tsx +3 -0
- package/src/components/navigation/Tabs/Tabs.styles.ts +199 -0
- package/src/components/navigation/Tabs/Tabs.test.tsx +661 -0
- package/src/components/navigation/Tabs/Tabs.tsx +253 -0
- package/src/components/navigation/Tabs/Tabs.types.ts +114 -0
- package/src/components/navigation/Tabs/index.tsx +3 -0
- package/src/components/navigation/Tree/Tree.styles.ts +553 -0
- package/src/components/navigation/Tree/Tree.test.basic.tsx +7 -0
- package/src/components/navigation/Tree/Tree.test.functional.tsx +496 -0
- package/src/components/navigation/Tree/Tree.test.import.check.tsx +6 -0
- package/src/components/navigation/Tree/Tree.test.import.tsx +6 -0
- package/src/components/navigation/Tree/Tree.test.minimal.tsx +5 -0
- package/src/components/navigation/Tree/Tree.test.simple.tsx +30 -0
- package/src/components/navigation/Tree/Tree.test.tsx +908 -0
- package/src/components/navigation/Tree/Tree.test.working.tsx +673 -0
- package/src/components/navigation/Tree/Tree.tsx +600 -0
- package/src/components/navigation/Tree/Tree.types.ts +909 -0
- package/src/components/navigation/Tree/Tree.utils.ts +452 -0
- package/src/components/navigation/Tree/index.ts +33 -0
- package/src/components/navigation/Tree/index.tsx +23 -0
- package/src/components/navigation/index.tsx +83 -0
- package/src/constants/index.ts +785 -0
- package/src/hooks/index.ts +110 -0
- package/src/hooks/types.ts +10 -0
- package/src/hooks/useAsync.ts +65 -0
- package/src/hooks/useEventHandling.ts +444 -0
- package/src/hooks/useLifecycle.ts +399 -0
- package/src/hooks/usePerformance.ts +441 -0
- package/src/hooks/usePerformanceMonitor.ts +348 -0
- package/src/hooks/usePlatform.ts +62 -0
- package/src/hooks/useRequest.test.ts +11 -0
- package/src/hooks/useRequest.ts +135 -0
- package/src/hooks/useStateManagement.ts +300 -0
- package/src/hooks/useStyle.ts +537 -0
- package/src/hooks/useTheme.ts +347 -0
- package/src/hooks/useVirtualScroll.ts +331 -0
- package/src/index.ts +298 -0
- package/src/platform/index.ts +1188 -0
- package/src/providers/AppProvider.test.tsx +63 -0
- package/src/providers/AppProvider.tsx +155 -0
- package/src/providers/index.ts +1 -0
- package/src/theme/ThemeProvider.tsx +283 -0
- package/src/theme/ThemeProvider.types.ts +26 -0
- package/src/theme/animations.tsx +660 -0
- package/src/theme/defaults.ts +188 -0
- package/src/theme/design-system.ts +562 -0
- package/src/theme/design-tokens.ts +1136 -0
- package/src/theme/generated/dark-theme.scss +120 -0
- package/src/theme/generated/tokens.css +441 -0
- package/src/theme/generated/tokens.scss +320 -0
- package/src/theme/index.ts +120 -0
- package/src/theme/responsive.tsx +193 -0
- package/src/theme/styles/mixins.scss +612 -0
- package/src/theme/styles/variables.scss +295 -0
- package/src/theme/styles.ts +403 -0
- package/src/theme/tokens/colors.ts +256 -0
- package/src/theme/tokens/effects.ts +260 -0
- package/src/theme/tokens/index.ts +217 -0
- package/src/theme/tokens/spacing.ts +137 -0
- package/src/theme/tokens/typography.ts +186 -0
- package/src/theme/types.ts +188 -0
- package/src/theme/useThemeUtils.ts +313 -0
- package/src/theme/utils.ts +501 -0
- package/src/theme/variables.ts +583 -0
- package/src/types/accessibility.ts +51 -0
- package/src/types/button.ts +562 -0
- package/src/types/component-props.ts +317 -0
- package/src/types/env.d.ts +20 -0
- package/src/types/index.ts +427 -0
- package/src/types/modules.d.ts +40 -0
- package/src/types/standardized-components.ts +544 -0
- package/src/types/taro-adapter.d.ts +174 -0
- package/src/types/taro-components.d.ts +73 -0
- package/src/types/utils.ts +410 -0
- package/src/utils/__tests__/inputValidator.test.ts +338 -0
- package/src/utils/__tests__/responsiveUtils.test.ts +310 -0
- package/src/utils/__tests__/xssProtection.test.ts +268 -0
- package/src/utils/cache.ts +83 -0
- package/src/utils/createNamespace.ts +24 -0
- package/src/utils/environment.ts +95 -0
- package/src/utils/error-handler.ts +88 -0
- package/src/utils/errorLogger.ts +197 -0
- package/src/utils/formatUtils.ts +444 -0
- package/src/utils/index.ts +115 -0
- package/src/utils/inputValidator.ts +261 -0
- package/src/utils/network/http-client.test.ts +18 -0
- package/src/utils/network/http-client.ts +151 -0
- package/src/utils/performance/performance.ts +850 -0
- package/src/utils/responsiveUtils.ts +357 -0
- package/src/utils/rtl-support.ts +344 -0
- package/src/utils/security/api-security.ts +386 -0
- package/src/utils/security/xss-protection.ts +69 -0
- package/src/utils/securityHeaders.ts +314 -0
- package/src/utils/typeHelpers.ts +16 -0
- package/src/utils/types/dataProcessing.ts +543 -0
- package/src/utils/types/typeHelpers.ts +187 -0
- package/src/utils/xssProtection.ts +420 -0
|
@@ -0,0 +1,661 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { render, screen, fireEvent, act } from '@testing-library/react'
|
|
3
|
+
import { Tabs } from '../Tabs'
|
|
4
|
+
import type { TabsProps, TabsRef, TabItem } from '../Tabs.types'
|
|
5
|
+
|
|
6
|
+
// Mock Taro components
|
|
7
|
+
vi.mock('@tarojs/components', () => ({
|
|
8
|
+
View: 'div',
|
|
9
|
+
Text: 'span'
|
|
10
|
+
}))
|
|
11
|
+
|
|
12
|
+
// Mock styles
|
|
13
|
+
vi.mock('../Tabs.styles', () => ({
|
|
14
|
+
tabsStyles: {
|
|
15
|
+
getBaseStyle: (props: any) => ({
|
|
16
|
+
display: 'flex',
|
|
17
|
+
flexDirection: props.position === 'left' || props.position === 'right' ? 'row' : 'column',
|
|
18
|
+
alignItems: props.centered ? 'center' : 'stretch'
|
|
19
|
+
}),
|
|
20
|
+
getTabBarStyle: (position: any, type: any) => ({
|
|
21
|
+
display: 'flex',
|
|
22
|
+
flexDirection: position === 'top' || position === 'bottom' ? 'row' : 'column',
|
|
23
|
+
borderBottom: type === 'line' ? '2px solid #f0f0f0' : 'none',
|
|
24
|
+
backgroundColor: type === 'card' ? '#f5f5f5' : 'transparent'
|
|
25
|
+
}),
|
|
26
|
+
getTabItemStyle: (props: any) => ({
|
|
27
|
+
padding: props.size === 'small' ? '8px 16px' : props.size === 'large' ? '16px 32px' : '12px 24px',
|
|
28
|
+
fontSize: props.size === 'small' ? '12px' : props.size === 'large' ? '16px' : '14px',
|
|
29
|
+
fontWeight: props.active ? 'bold' : 'normal',
|
|
30
|
+
color: props.active ? '#1890ff' : '#666',
|
|
31
|
+
borderBottom: props.active && props.type === 'line' ? '2px solid #1890ff' : 'none',
|
|
32
|
+
backgroundColor: props.active && props.type === 'card' ? 'white' : 'transparent',
|
|
33
|
+
opacity: props.disabled ? 0.5 : 1,
|
|
34
|
+
cursor: props.disabled ? 'not-allowed' : 'pointer'
|
|
35
|
+
}),
|
|
36
|
+
getBadgeStyle: () => ({
|
|
37
|
+
backgroundColor: '#ff4d4f',
|
|
38
|
+
color: 'white',
|
|
39
|
+
borderRadius: '10px',
|
|
40
|
+
padding: '2px 6px',
|
|
41
|
+
fontSize: '12px',
|
|
42
|
+
marginLeft: '4px'
|
|
43
|
+
}),
|
|
44
|
+
getRemoveButtonStyle: () => ({
|
|
45
|
+
marginLeft: '8px',
|
|
46
|
+
cursor: 'pointer',
|
|
47
|
+
fontSize: '12px',
|
|
48
|
+
color: '#999'
|
|
49
|
+
}),
|
|
50
|
+
getAddButtonStyle: (size: any) => ({
|
|
51
|
+
padding: size === 'small' ? '8px 16px' : size === 'large' ? '16px 32px' : '12px 24px',
|
|
52
|
+
fontSize: size === 'small' ? '12px' : size === 'large' ? '16px' : '14px',
|
|
53
|
+
cursor: 'pointer',
|
|
54
|
+
color: '#1890ff'
|
|
55
|
+
}),
|
|
56
|
+
getContentStyle: (position: any, animated: any) => ({
|
|
57
|
+
flex: 1,
|
|
58
|
+
position: 'relative',
|
|
59
|
+
overflow: 'hidden'
|
|
60
|
+
}),
|
|
61
|
+
getTabContentStyle: (active: any, animated: any) => ({
|
|
62
|
+
display: active ? 'block' : 'none',
|
|
63
|
+
transition: animated ? 'all 0.3s ease' : 'none'
|
|
64
|
+
}),
|
|
65
|
+
getClassName: (props: any) => `taro-uno-tabs taro-uno-tabs--${props.position} taro-uno-tabs--${props.type} taro-uno-tabs--${props.size} ${props.centered ? 'taro-uno-tabs--centered' : ''} ${props.className || ''}`
|
|
66
|
+
}
|
|
67
|
+
}))
|
|
68
|
+
|
|
69
|
+
describe('Tabs Component', () => {
|
|
70
|
+
const mockRef = React.createRef<TabsRef>()
|
|
71
|
+
const mockItems: TabItem[] = [
|
|
72
|
+
{ key: 'tab1', title: 'Tab 1', content: 'Content 1' },
|
|
73
|
+
{ key: 'tab2', title: 'Tab 2', content: 'Content 2' },
|
|
74
|
+
{ key: 'tab3', title: 'Tab 3', content: 'Content 3' }
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
beforeEach(() => {
|
|
78
|
+
vi.clearAllMocks()
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
describe('Rendering', () => {
|
|
82
|
+
it('renders tabs with default props', () => {
|
|
83
|
+
render(<Tabs items={mockItems} data-testid="tabs" />)
|
|
84
|
+
const tabs = screen.getByTestId('tabs')
|
|
85
|
+
expect(tabs).toBeInTheDocument()
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('renders tabs with custom active key', () => {
|
|
89
|
+
render(<Tabs items={mockItems} activeKey="tab2" data-testid="tabs" />)
|
|
90
|
+
const tabs = screen.getByTestId('tabs')
|
|
91
|
+
expect(tabs).toBeInTheDocument()
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('renders tabs with default active key', () => {
|
|
95
|
+
render(<Tabs items={mockItems} defaultActiveKey="tab2" data-testid="tabs" />)
|
|
96
|
+
const tabs = screen.getByTestId('tabs')
|
|
97
|
+
expect(tabs).toBeInTheDocument()
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('renders tabs with custom position', () => {
|
|
101
|
+
render(<Tabs items={mockItems} position="bottom" data-testid="tabs" />)
|
|
102
|
+
const tabs = screen.getByTestId('tabs')
|
|
103
|
+
expect(tabs).toBeInTheDocument()
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('renders tabs with custom type', () => {
|
|
107
|
+
render(<Tabs items={mockItems} type="card" data-testid="tabs" />)
|
|
108
|
+
const tabs = screen.getByTestId('tabs')
|
|
109
|
+
expect(tabs).toBeInTheDocument()
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('renders tabs with custom size', () => {
|
|
113
|
+
render(<Tabs items={mockItems} size="small" data-testid="tabs" />)
|
|
114
|
+
const tabs = screen.getByTestId('tabs')
|
|
115
|
+
expect(tabs).toBeInTheDocument()
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('renders tabs with centered alignment', () => {
|
|
119
|
+
render(<Tabs items={mockItems} centered data-testid="tabs" />)
|
|
120
|
+
const tabs = screen.getByTestId('tabs')
|
|
121
|
+
expect(tabs).toBeInTheDocument()
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('renders tabs with animation', () => {
|
|
125
|
+
render(<Tabs items={mockItems} animated={false} data-testid="tabs" />)
|
|
126
|
+
const tabs = screen.getByTestId('tabs')
|
|
127
|
+
expect(tabs).toBeInTheDocument()
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('renders tabs with editable mode', () => {
|
|
131
|
+
render(<Tabs items={mockItems} editable data-testid="tabs" />)
|
|
132
|
+
const tabs = screen.getByTestId('tabs')
|
|
133
|
+
expect(tabs).toBeInTheDocument()
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('renders tabs with addable mode', () => {
|
|
137
|
+
render(<Tabs items={mockItems} addable data-testid="tabs" />)
|
|
138
|
+
const tabs = screen.getByTestId('tabs')
|
|
139
|
+
expect(tabs).toBeInTheDocument()
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('renders tabs with force render', () => {
|
|
143
|
+
render(<Tabs items={mockItems} forceRender data-testid="tabs" />)
|
|
144
|
+
const tabs = screen.getByTestId('tabs')
|
|
145
|
+
expect(tabs).toBeInTheDocument()
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('renders tabs with destroy inactive tab pane', () => {
|
|
149
|
+
render(<Tabs items={mockItems} destroyInactiveTabPane data-testid="tabs" />)
|
|
150
|
+
const tabs = screen.getByTestId('tabs')
|
|
151
|
+
expect(tabs).toBeInTheDocument()
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
it('renders tabs with custom className', () => {
|
|
155
|
+
render(<Tabs items={mockItems} className="custom-tabs" data-testid="tabs" />)
|
|
156
|
+
const tabs = screen.getByTestId('tabs')
|
|
157
|
+
expect(tabs).toBeInTheDocument()
|
|
158
|
+
expect(tabs).toHaveClass('custom-tabs')
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
it('renders tabs with custom style', () => {
|
|
162
|
+
const customStyle = { backgroundColor: '#f0f0f0', padding: '16px' }
|
|
163
|
+
render(<Tabs items={mockItems} style={customStyle} data-testid="tabs" />)
|
|
164
|
+
const tabs = screen.getByTestId('tabs')
|
|
165
|
+
expect(tabs).toBeInTheDocument()
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('renders tabs with disabled tab', () => {
|
|
169
|
+
const itemsWithDisabled = [
|
|
170
|
+
...mockItems,
|
|
171
|
+
{ key: 'tab4', title: 'Tab 4', content: 'Content 4', disabled: true }
|
|
172
|
+
]
|
|
173
|
+
render(<Tabs items={itemsWithDisabled} data-testid="tabs" />)
|
|
174
|
+
const tabs = screen.getByTestId('tabs')
|
|
175
|
+
expect(tabs).toBeInTheDocument()
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it('renders tabs with tab icons', () => {
|
|
179
|
+
const itemsWithIcons = [
|
|
180
|
+
{ key: 'tab1', title: 'Tab 1', content: 'Content 1', icon: '📄' },
|
|
181
|
+
{ key: 'tab2', title: 'Tab 2', content: 'Content 2', icon: '📊' }
|
|
182
|
+
]
|
|
183
|
+
render(<Tabs items={itemsWithIcons} data-testid="tabs" />)
|
|
184
|
+
const tabs = screen.getByTestId('tabs')
|
|
185
|
+
expect(tabs).toBeInTheDocument()
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('renders tabs with tab badges', () => {
|
|
189
|
+
const itemsWithBadges = [
|
|
190
|
+
{ key: 'tab1', title: 'Tab 1', content: 'Content 1', badge: '3' },
|
|
191
|
+
{ key: 'tab2', title: 'Tab 2', content: 'Content 2', badge: 'New' }
|
|
192
|
+
]
|
|
193
|
+
render(<Tabs items={itemsWithBadges} data-testid="tabs" />)
|
|
194
|
+
const tabs = screen.getByTestId('tabs')
|
|
195
|
+
expect(tabs).toBeInTheDocument()
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('handles empty items gracefully', () => {
|
|
199
|
+
render(<Tabs items={[]} data-testid="tabs" />)
|
|
200
|
+
const tabs = screen.getByTestId('tabs')
|
|
201
|
+
expect(tabs).toBeInTheDocument()
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it('handles single tab gracefully', () => {
|
|
205
|
+
const singleItem = [{ key: 'tab1', title: 'Tab 1', content: 'Content 1' }]
|
|
206
|
+
render(<Tabs items={singleItem} data-testid="tabs" />)
|
|
207
|
+
const tabs = screen.getByTestId('tabs')
|
|
208
|
+
expect(tabs).toBeInTheDocument()
|
|
209
|
+
})
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
describe('Tab Navigation', () => {
|
|
213
|
+
it('switches tab on click', () => {
|
|
214
|
+
const handleChange = vi.fn()
|
|
215
|
+
render(<Tabs items={mockItems} onChange={handleChange} data-testid="tabs" />)
|
|
216
|
+
|
|
217
|
+
const tab2 = screen.getByText('Tab 2')
|
|
218
|
+
fireEvent.click(tab2)
|
|
219
|
+
|
|
220
|
+
expect(handleChange).toHaveBeenCalledWith('tab2')
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
it('calls onTabClick when tab is clicked', () => {
|
|
224
|
+
const handleTabClick = vi.fn()
|
|
225
|
+
render(<Tabs items={mockItems} onTabClick={handleTabClick} data-testid="tabs" />)
|
|
226
|
+
|
|
227
|
+
const tab2 = screen.getByText('Tab 2')
|
|
228
|
+
fireEvent.click(tab2)
|
|
229
|
+
|
|
230
|
+
expect(handleTabClick).toHaveBeenCalledWith('tab2', expect.any(Object))
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
it('does not switch to disabled tab', () => {
|
|
234
|
+
const handleChange = vi.fn()
|
|
235
|
+
const itemsWithDisabled = [
|
|
236
|
+
...mockItems,
|
|
237
|
+
{ key: 'tab4', title: 'Tab 4', content: 'Content 4', disabled: true }
|
|
238
|
+
]
|
|
239
|
+
render(<Tabs items={itemsWithDisabled} onChange={handleChange} data-testid="tabs" />)
|
|
240
|
+
|
|
241
|
+
const disabledTab = screen.getByText('Tab 4')
|
|
242
|
+
fireEvent.click(disabledTab)
|
|
243
|
+
|
|
244
|
+
expect(handleChange).not.toHaveBeenCalled()
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
it('does not call onTabClick for disabled tab', () => {
|
|
248
|
+
const handleTabClick = vi.fn()
|
|
249
|
+
const itemsWithDisabled = [
|
|
250
|
+
...mockItems,
|
|
251
|
+
{ key: 'tab4', title: 'Tab 4', content: 'Content 4', disabled: true }
|
|
252
|
+
]
|
|
253
|
+
render(<Tabs items={itemsWithDisabled} onTabClick={handleTabClick} data-testid="tabs" />)
|
|
254
|
+
|
|
255
|
+
const disabledTab = screen.getByText('Tab 4')
|
|
256
|
+
fireEvent.click(disabledTab)
|
|
257
|
+
|
|
258
|
+
expect(handleTabClick).not.toHaveBeenCalled()
|
|
259
|
+
})
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
describe('Tab Management', () => {
|
|
263
|
+
it('adds new tab when add button is clicked', () => {
|
|
264
|
+
const handleAdd = vi.fn()
|
|
265
|
+
const handleEdit = vi.fn()
|
|
266
|
+
render(<Tabs items={mockItems} addable onAdd={handleAdd} onEdit={handleEdit} data-testid="tabs" />)
|
|
267
|
+
|
|
268
|
+
const addButton = screen.getByText('+')
|
|
269
|
+
fireEvent.click(addButton)
|
|
270
|
+
|
|
271
|
+
expect(handleAdd).toHaveBeenCalled()
|
|
272
|
+
expect(handleEdit).toHaveBeenCalledWith('', 'add')
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
it('removes tab when remove button is clicked', () => {
|
|
276
|
+
const handleRemove = vi.fn()
|
|
277
|
+
const handleEdit = vi.fn()
|
|
278
|
+
render(<Tabs items={mockItems} editable onRemove={handleRemove} onEdit={handleEdit} data-testid="tabs" />)
|
|
279
|
+
|
|
280
|
+
const removeButtons = screen.getAllByText('×')
|
|
281
|
+
fireEvent.click(removeButtons[0])
|
|
282
|
+
|
|
283
|
+
expect(handleRemove).toHaveBeenCalledWith('tab1')
|
|
284
|
+
expect(handleEdit).toHaveBeenCalledWith('tab1', 'remove')
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
it('stops event propagation when removing tab', () => {
|
|
288
|
+
const handleChange = vi.fn()
|
|
289
|
+
const handleRemove = vi.fn()
|
|
290
|
+
render(<Tabs items={mockItems} editable onRemove={handleRemove} onChange={handleChange} data-testid="tabs" />)
|
|
291
|
+
|
|
292
|
+
const removeButtons = screen.getAllByText('×')
|
|
293
|
+
fireEvent.click(removeButtons[0])
|
|
294
|
+
|
|
295
|
+
expect(handleRemove).toHaveBeenCalled()
|
|
296
|
+
expect(handleChange).not.toHaveBeenCalled()
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
it('does not show remove button for single tab', () => {
|
|
300
|
+
const singleItem = [{ key: 'tab1', title: 'Tab 1', content: 'Content 1' }]
|
|
301
|
+
render(<Tabs items={singleItem} editable data-testid="tabs" />)
|
|
302
|
+
|
|
303
|
+
const removeButtons = screen.queryAllByText('×')
|
|
304
|
+
expect(removeButtons).toHaveLength(0)
|
|
305
|
+
})
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
describe('Custom Rendering', () => {
|
|
309
|
+
it('uses custom tab bar render', () => {
|
|
310
|
+
const customTabBar = (props: TabsProps) => (
|
|
311
|
+
<div data-testid="custom-tab-bar">Custom Tab Bar</div>
|
|
312
|
+
)
|
|
313
|
+
render(<Tabs items={mockItems} renderTabBar={customTabBar} data-testid="tabs" />)
|
|
314
|
+
|
|
315
|
+
expect(screen.getByTestId('custom-tab-bar')).toBeInTheDocument()
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
it('uses custom tab render', () => {
|
|
319
|
+
const customTab = (item: TabItem, index: number) => (
|
|
320
|
+
<div data-testid={`custom-tab-${index}`}>Custom {item.title}</div>
|
|
321
|
+
)
|
|
322
|
+
render(<Tabs items={mockItems} renderTab={customTab} data-testid="tabs" />)
|
|
323
|
+
|
|
324
|
+
expect(screen.getByTestId('custom-tab-0')).toBeInTheDocument()
|
|
325
|
+
expect(screen.getByTestId('custom-tab-1')).toBeInTheDocument()
|
|
326
|
+
expect(screen.getByTestId('custom-tab-2')).toBeInTheDocument()
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
it('uses custom content render', () => {
|
|
330
|
+
const customContent = (item: TabItem, index: number) => (
|
|
331
|
+
<div data-testid={`custom-content-${index}`}>Custom Content {index + 1}</div>
|
|
332
|
+
)
|
|
333
|
+
render(<Tabs items={mockItems} renderContent={customContent} data-testid="tabs" />)
|
|
334
|
+
|
|
335
|
+
expect(screen.getByTestId('custom-content-0')).toBeInTheDocument()
|
|
336
|
+
})
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
describe('Props Updates', () => {
|
|
340
|
+
it('updates items correctly', () => {
|
|
341
|
+
const { rerender } = render(<Tabs items={mockItems} data-testid="tabs" />)
|
|
342
|
+
const tabs = screen.getByTestId('tabs')
|
|
343
|
+
|
|
344
|
+
const newItems = [
|
|
345
|
+
{ key: 'tab1', title: 'Updated Tab 1', content: 'Updated Content 1' },
|
|
346
|
+
{ key: 'tab2', title: 'Updated Tab 2', content: 'Updated Content 2' }
|
|
347
|
+
]
|
|
348
|
+
|
|
349
|
+
rerender(<Tabs items={newItems} data-testid="tabs" />)
|
|
350
|
+
expect(tabs).toBeInTheDocument()
|
|
351
|
+
expect(screen.getByText('Updated Tab 1')).toBeInTheDocument()
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
it('updates active key correctly', () => {
|
|
355
|
+
const { rerender } = render(<Tabs items={mockItems} activeKey="tab1" data-testid="tabs" />)
|
|
356
|
+
const tabs = screen.getByTestId('tabs')
|
|
357
|
+
|
|
358
|
+
rerender(<Tabs items={mockItems} activeKey="tab2" data-testid="tabs" />)
|
|
359
|
+
expect(tabs).toBeInTheDocument()
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
it('updates position correctly', () => {
|
|
363
|
+
const { rerender } = render(<Tabs items={mockItems} position="top" data-testid="tabs" />)
|
|
364
|
+
const tabs = screen.getByTestId('tabs')
|
|
365
|
+
|
|
366
|
+
rerender(<Tabs items={mockItems} position="bottom" data-testid="tabs" />)
|
|
367
|
+
expect(tabs).toBeInTheDocument()
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
it('updates type correctly', () => {
|
|
371
|
+
const { rerender } = render(<Tabs items={mockItems} type="line" data-testid="tabs" />)
|
|
372
|
+
const tabs = screen.getByTestId('tabs')
|
|
373
|
+
|
|
374
|
+
rerender(<Tabs items={mockItems} type="card" data-testid="tabs" />)
|
|
375
|
+
expect(tabs).toBeInTheDocument()
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
it('updates size correctly', () => {
|
|
379
|
+
const { rerender } = render(<Tabs items={mockItems} size="default" data-testid="tabs" />)
|
|
380
|
+
const tabs = screen.getByTestId('tabs')
|
|
381
|
+
|
|
382
|
+
rerender(<Tabs items={mockItems} size="small" data-testid="tabs" />)
|
|
383
|
+
expect(tabs).toBeInTheDocument()
|
|
384
|
+
})
|
|
385
|
+
|
|
386
|
+
it('updates centered correctly', () => {
|
|
387
|
+
const { rerender } = render(<Tabs items={mockItems} centered={false} data-testid="tabs" />)
|
|
388
|
+
const tabs = screen.getByTestId('tabs')
|
|
389
|
+
|
|
390
|
+
rerender(<Tabs items={mockItems} centered={true} data-testid="tabs" />)
|
|
391
|
+
expect(tabs).toBeInTheDocument()
|
|
392
|
+
})
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
describe('Ref API', () => {
|
|
396
|
+
it('exposes ref methods correctly', () => {
|
|
397
|
+
render(<Tabs items={mockItems} ref={mockRef} data-testid="tabs" />)
|
|
398
|
+
|
|
399
|
+
act(() => {
|
|
400
|
+
expect(mockRef.current).toBeDefined()
|
|
401
|
+
expect(mockRef.current?.getActiveKey()).toBe('tab1')
|
|
402
|
+
expect(mockRef.current?.getItems()).toEqual(mockItems)
|
|
403
|
+
})
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
it('sets active key via ref method', () => {
|
|
407
|
+
render(<Tabs items={mockItems} defaultActiveKey="tab1" ref={mockRef} data-testid="tabs" />)
|
|
408
|
+
|
|
409
|
+
act(() => {
|
|
410
|
+
if (mockRef.current) {
|
|
411
|
+
expect(() => mockRef.current.setActiveKey('tab2')).not.toThrow()
|
|
412
|
+
}
|
|
413
|
+
})
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
it('adds item via ref method', () => {
|
|
417
|
+
render(<Tabs items={mockItems} ref={mockRef} data-testid="tabs" />)
|
|
418
|
+
|
|
419
|
+
act(() => {
|
|
420
|
+
if (mockRef.current) {
|
|
421
|
+
const newItem = { key: 'tab4', title: 'Tab 4', content: 'Content 4' }
|
|
422
|
+
expect(() => mockRef.current.addItem(newItem)).not.toThrow()
|
|
423
|
+
}
|
|
424
|
+
})
|
|
425
|
+
})
|
|
426
|
+
|
|
427
|
+
it('adds item at specific index via ref method', () => {
|
|
428
|
+
render(<Tabs items={mockItems} ref={mockRef} data-testid="tabs" />)
|
|
429
|
+
|
|
430
|
+
act(() => {
|
|
431
|
+
if (mockRef.current) {
|
|
432
|
+
const newItem = { key: 'tab4', title: 'Tab 4', content: 'Content 4' }
|
|
433
|
+
expect(() => mockRef.current.addItem(newItem, 1)).not.toThrow()
|
|
434
|
+
}
|
|
435
|
+
})
|
|
436
|
+
})
|
|
437
|
+
|
|
438
|
+
it('removes item via ref method', () => {
|
|
439
|
+
render(<Tabs items={mockItems} ref={mockRef} data-testid="tabs" />)
|
|
440
|
+
|
|
441
|
+
act(() => {
|
|
442
|
+
if (mockRef.current) {
|
|
443
|
+
expect(() => mockRef.current.removeItem('tab2')).not.toThrow()
|
|
444
|
+
}
|
|
445
|
+
})
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
it('updates item via ref method', () => {
|
|
449
|
+
render(<Tabs items={mockItems} ref={mockRef} data-testid="tabs" />)
|
|
450
|
+
|
|
451
|
+
act(() => {
|
|
452
|
+
if (mockRef.current) {
|
|
453
|
+
const updatedItem = { title: 'Updated Tab 2' }
|
|
454
|
+
expect(() => mockRef.current.updateItem('tab2', updatedItem)).not.toThrow()
|
|
455
|
+
}
|
|
456
|
+
})
|
|
457
|
+
})
|
|
458
|
+
|
|
459
|
+
it('provides element access via ref', () => {
|
|
460
|
+
render(<Tabs items={mockItems} ref={mockRef} data-testid="tabs" />)
|
|
461
|
+
|
|
462
|
+
act(() => {
|
|
463
|
+
expect(mockRef.current?.element).toBeDefined()
|
|
464
|
+
})
|
|
465
|
+
})
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
describe('Controlled vs Uncontrolled', () => {
|
|
469
|
+
it('works in controlled mode', () => {
|
|
470
|
+
const handleChange = vi.fn()
|
|
471
|
+
render(<Tabs items={mockItems} activeKey="tab2" onChange={handleChange} data-testid="tabs" />)
|
|
472
|
+
|
|
473
|
+
const tab1 = screen.getByText('Tab 1')
|
|
474
|
+
fireEvent.click(tab1)
|
|
475
|
+
|
|
476
|
+
expect(handleChange).toHaveBeenCalledWith('tab1')
|
|
477
|
+
})
|
|
478
|
+
|
|
479
|
+
it('works in uncontrolled mode', () => {
|
|
480
|
+
const handleChange = vi.fn()
|
|
481
|
+
render(<Tabs items={mockItems} defaultActiveKey="tab2" onChange={handleChange} data-testid="tabs" />)
|
|
482
|
+
|
|
483
|
+
const tab1 = screen.getByText('Tab 1')
|
|
484
|
+
fireEvent.click(tab1)
|
|
485
|
+
|
|
486
|
+
expect(handleChange).toHaveBeenCalledWith('tab1')
|
|
487
|
+
})
|
|
488
|
+
})
|
|
489
|
+
|
|
490
|
+
describe('Content Rendering', () => {
|
|
491
|
+
it('renders active tab content', () => {
|
|
492
|
+
render(<Tabs items={mockItems} activeKey="tab2" data-testid="tabs" />)
|
|
493
|
+
expect(screen.getByText('Content 2')).toBeInTheDocument()
|
|
494
|
+
})
|
|
495
|
+
|
|
496
|
+
it('does not render inactive tab content when destroyInactiveTabPane is true', () => {
|
|
497
|
+
render(<Tabs items={mockItems} activeKey="tab2" destroyInactiveTabPane data-testid="tabs" />)
|
|
498
|
+
expect(screen.getByText('Content 2')).toBeInTheDocument()
|
|
499
|
+
expect(screen.queryByText('Content 1')).not.toBeInTheDocument()
|
|
500
|
+
expect(screen.queryByText('Content 3')).not.toBeInTheDocument()
|
|
501
|
+
})
|
|
502
|
+
|
|
503
|
+
it('renders all tab content when forceRender is true', () => {
|
|
504
|
+
render(<Tabs items={mockItems} forceRender data-testid="tabs" />)
|
|
505
|
+
expect(screen.getByText('Content 1')).toBeInTheDocument()
|
|
506
|
+
expect(screen.getByText('Content 2')).toBeInTheDocument()
|
|
507
|
+
expect(screen.getByText('Content 3')).toBeInTheDocument()
|
|
508
|
+
})
|
|
509
|
+
|
|
510
|
+
it('only renders active tab content by default', () => {
|
|
511
|
+
render(<Tabs items={mockItems} data-testid="tabs" />)
|
|
512
|
+
expect(screen.getByText('Content 1')).toBeInTheDocument()
|
|
513
|
+
// Note: The actual implementation may render all tabs but hide inactive ones
|
|
514
|
+
// This test verifies the active tab content is visible
|
|
515
|
+
})
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
describe('Accessibility', () => {
|
|
519
|
+
it('has proper role attribute', () => {
|
|
520
|
+
render(<Tabs items={mockItems} role="tablist" data-testid="tabs" />)
|
|
521
|
+
const tabs = screen.getByTestId('tabs')
|
|
522
|
+
expect(tabs).toHaveAttribute('role', 'tablist')
|
|
523
|
+
})
|
|
524
|
+
|
|
525
|
+
it('supports aria-label', () => {
|
|
526
|
+
render(<Tabs items={mockItems} aria-label="Tab navigation" data-testid="tabs" />)
|
|
527
|
+
const tabs = screen.getByTestId('tabs')
|
|
528
|
+
expect(tabs).toHaveAttribute('aria-label', 'Tab navigation')
|
|
529
|
+
})
|
|
530
|
+
|
|
531
|
+
it('supports aria-labelledby', () => {
|
|
532
|
+
render(<Tabs items={mockItems} aria-labelledby="tabs-title" data-testid="tabs" />)
|
|
533
|
+
const tabs = screen.getByTestId('tabs')
|
|
534
|
+
expect(tabs).toHaveAttribute('aria-labelledby', 'tabs-title')
|
|
535
|
+
})
|
|
536
|
+
|
|
537
|
+
it('supports data attributes', () => {
|
|
538
|
+
render(<Tabs items={mockItems} data-testid="tabs" data-custom="value" />)
|
|
539
|
+
const tabs = screen.getByTestId('tabs')
|
|
540
|
+
expect(tabs).toHaveAttribute('data-custom', 'value')
|
|
541
|
+
})
|
|
542
|
+
})
|
|
543
|
+
|
|
544
|
+
describe('Edge Cases', () => {
|
|
545
|
+
it('handles items with duplicate keys', () => {
|
|
546
|
+
const itemsWithDuplicateKeys = [
|
|
547
|
+
{ key: 'tab1', title: 'Tab 1', content: 'Content 1' },
|
|
548
|
+
{ key: 'tab1', title: 'Tab 1 Duplicate', content: 'Content 1 Duplicate' }
|
|
549
|
+
]
|
|
550
|
+
render(<Tabs items={itemsWithDuplicateKeys} data-testid="tabs" />)
|
|
551
|
+
const tabs = screen.getByTestId('tabs')
|
|
552
|
+
expect(tabs).toBeInTheDocument()
|
|
553
|
+
})
|
|
554
|
+
|
|
555
|
+
it('handles items without content', () => {
|
|
556
|
+
const itemsWithoutContent = [
|
|
557
|
+
{ key: 'tab1', title: 'Tab 1' },
|
|
558
|
+
{ key: 'tab2', title: 'Tab 2' }
|
|
559
|
+
]
|
|
560
|
+
render(<Tabs items={itemsWithoutContent} data-testid="tabs" />)
|
|
561
|
+
const tabs = screen.getByTestId('tabs')
|
|
562
|
+
expect(tabs).toBeInTheDocument()
|
|
563
|
+
})
|
|
564
|
+
|
|
565
|
+
it('handles items with undefined title', () => {
|
|
566
|
+
const itemsWithUndefinedTitle = [
|
|
567
|
+
{ key: 'tab1', title: undefined, content: 'Content 1' },
|
|
568
|
+
{ key: 'tab2', title: 'Tab 2', content: 'Content 2' }
|
|
569
|
+
]
|
|
570
|
+
render(<Tabs items={itemsWithUndefinedTitle as any} data-testid="tabs" />)
|
|
571
|
+
const tabs = screen.getByTestId('tabs')
|
|
572
|
+
expect(tabs).toBeInTheDocument()
|
|
573
|
+
})
|
|
574
|
+
|
|
575
|
+
it('handles items with complex content', () => {
|
|
576
|
+
const itemsWithComplexContent = [
|
|
577
|
+
{ key: 'tab1', title: 'Tab 1', content: <div data-testid="complex-content">Complex Content</div> },
|
|
578
|
+
{ key: 'tab2', title: 'Tab 2', content: 'Simple Content' }
|
|
579
|
+
]
|
|
580
|
+
render(<Tabs items={itemsWithComplexContent} data-testid="tabs" />)
|
|
581
|
+
const tabs = screen.getByTestId('tabs')
|
|
582
|
+
expect(tabs).toBeInTheDocument()
|
|
583
|
+
expect(screen.getByTestId('complex-content')).toBeInTheDocument()
|
|
584
|
+
})
|
|
585
|
+
|
|
586
|
+
it('handles items with complex title', () => {
|
|
587
|
+
const itemsWithComplexTitle = [
|
|
588
|
+
{ key: 'tab1', title: <div data-testid="complex-title">Complex Title</div>, content: 'Content 1' },
|
|
589
|
+
{ key: 'tab2', title: 'Simple Title', content: 'Content 2' }
|
|
590
|
+
]
|
|
591
|
+
render(<Tabs items={itemsWithComplexTitle} data-testid="tabs" />)
|
|
592
|
+
const tabs = screen.getByTestId('tabs')
|
|
593
|
+
expect(tabs).toBeInTheDocument()
|
|
594
|
+
expect(screen.getByTestId('complex-title')).toBeInTheDocument()
|
|
595
|
+
})
|
|
596
|
+
})
|
|
597
|
+
|
|
598
|
+
describe('Performance', () => {
|
|
599
|
+
it('renders efficiently with many tabs', () => {
|
|
600
|
+
const manyItems = Array.from({ length: 50 }, (_, i) => ({
|
|
601
|
+
key: `tab${i}`,
|
|
602
|
+
title: `Tab ${i}`,
|
|
603
|
+
content: `Content ${i}`
|
|
604
|
+
}))
|
|
605
|
+
render(<Tabs items={manyItems} data-testid="tabs" />)
|
|
606
|
+
const tabs = screen.getByTestId('tabs')
|
|
607
|
+
expect(tabs).toBeInTheDocument()
|
|
608
|
+
expect(screen.getAllByText(/^Tab \d+$/)).toHaveLength(50)
|
|
609
|
+
})
|
|
610
|
+
|
|
611
|
+
it('handles frequent prop updates efficiently', () => {
|
|
612
|
+
const { rerender } = render(<Tabs items={mockItems} activeKey="tab1" data-testid="tabs" />)
|
|
613
|
+
|
|
614
|
+
for (let i = 0; i < 10; i++) {
|
|
615
|
+
rerender(<Tabs items={mockItems} activeKey={mockItems[i % 3].key} data-testid="tabs" />)
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
expect(screen.getByTestId('tabs')).toBeInTheDocument()
|
|
619
|
+
})
|
|
620
|
+
})
|
|
621
|
+
|
|
622
|
+
describe('Position Variants', () => {
|
|
623
|
+
it('renders tabs in left position', () => {
|
|
624
|
+
render(<Tabs items={mockItems} position="left" data-testid="tabs" />)
|
|
625
|
+
const tabs = screen.getByTestId('tabs')
|
|
626
|
+
expect(tabs).toBeInTheDocument()
|
|
627
|
+
})
|
|
628
|
+
|
|
629
|
+
it('renders tabs in right position', () => {
|
|
630
|
+
render(<Tabs items={mockItems} position="right" data-testid="tabs" />)
|
|
631
|
+
const tabs = screen.getByTestId('tabs')
|
|
632
|
+
expect(tabs).toBeInTheDocument()
|
|
633
|
+
})
|
|
634
|
+
|
|
635
|
+
it('renders tabs in bottom position', () => {
|
|
636
|
+
render(<Tabs items={mockItems} position="bottom" data-testid="tabs" />)
|
|
637
|
+
const tabs = screen.getByTestId('tabs')
|
|
638
|
+
expect(tabs).toBeInTheDocument()
|
|
639
|
+
})
|
|
640
|
+
})
|
|
641
|
+
|
|
642
|
+
describe('Type Variants', () => {
|
|
643
|
+
it('renders line type tabs', () => {
|
|
644
|
+
render(<Tabs items={mockItems} type="line" data-testid="tabs" />)
|
|
645
|
+
const tabs = screen.getByTestId('tabs')
|
|
646
|
+
expect(tabs).toBeInTheDocument()
|
|
647
|
+
})
|
|
648
|
+
|
|
649
|
+
it('renders card type tabs', () => {
|
|
650
|
+
render(<Tabs items={mockItems} type="card" data-testid="tabs" />)
|
|
651
|
+
const tabs = screen.getByTestId('tabs')
|
|
652
|
+
expect(tabs).toBeInTheDocument()
|
|
653
|
+
})
|
|
654
|
+
|
|
655
|
+
it('renders segment type tabs', () => {
|
|
656
|
+
render(<Tabs items={mockItems} type="segment" data-testid="tabs" />)
|
|
657
|
+
const tabs = screen.getByTestId('tabs')
|
|
658
|
+
expect(tabs).toBeInTheDocument()
|
|
659
|
+
})
|
|
660
|
+
})
|
|
661
|
+
})
|