@12-apps/ui 1.5.0 → 1.5.2
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/package.json +17 -9
- package/src/button/index.ts +1 -0
- package/src/social-login-button/index.ts +1 -0
- package/src/{social-login-button.tsx → social-login-button/social-login-button.tsx} +5 -5
- package/src/user-avatar/index.ts +1 -0
- package/src/{user-avatar.tsx → user-avatar/user-avatar.tsx} +4 -4
- package/eslint.config.js +0 -89
- package/src/__tests__/data-grid.test.tsx +0 -125
- package/src/__tests__/package-wiring.test.ts +0 -41
- package/src/__tests__/setup.ts +0 -1
- package/src/__tests__/social-login-button.test.tsx +0 -186
- package/src/__tests__/user-avatar.test.tsx +0 -195
- package/src/__tests__/viewport.test.ts +0 -50
- package/src/components/command/__tests__/command.test.tsx +0 -112
- package/src/components/data-display/Alert/Alert.stories.tsx +0 -559
- package/src/components/data-display/Alert/Alert.test.stories.tsx +0 -707
- package/src/components/data-display/AlertDialog/AlertDialog.stories.tsx +0 -362
- package/src/components/data-display/AlertDialog/AlertDialog.test.stories.tsx +0 -763
- package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.stories.tsx +0 -230
- package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.test.stories.tsx +0 -270
- package/src/components/data-display/Avatar/Avatar.stories.tsx +0 -721
- package/src/components/data-display/Avatar/Avatar.test.stories.tsx +0 -822
- package/src/components/data-display/Badge/Badge.stories.tsx +0 -1134
- package/src/components/data-display/Badge/Badge.test.stories.tsx +0 -1194
- package/src/components/data-display/Banner/Banner.stories.tsx +0 -528
- package/src/components/data-display/Banner/Banner.test.stories.tsx +0 -511
- package/src/components/data-display/Carousel/Carousel.stories.tsx +0 -607
- package/src/components/data-display/Carousel/Carousel.test.stories.tsx +0 -701
- package/src/components/data-display/Chart/Chart.stories.tsx +0 -568
- package/src/components/data-display/Chart/Chart.test.stories.tsx +0 -174
- package/src/components/data-display/Chart/SpecChart.stories.tsx +0 -150
- package/src/components/data-display/Chart/SpecChart.test.stories.tsx +0 -56
- package/src/components/data-display/Chart/__tests__/SpecChart.test.tsx +0 -60
- package/src/components/data-display/Chart/__tests__/chart-format.test.ts +0 -43
- package/src/components/data-display/Chart/__tests__/chart-spec.test.ts +0 -119
- package/src/components/data-display/Chip/Chip.stories.tsx +0 -252
- package/src/components/data-display/Chip/Chip.test.stories.tsx +0 -266
- package/src/components/data-display/DataGrid/DataGrid.stories.tsx +0 -515
- package/src/components/data-display/DataGrid/DataGrid.test.stories.tsx +0 -653
- package/src/components/data-display/DataViews/__tests__/DataViewsGrid.test.tsx +0 -271
- package/src/components/data-display/DataViews/__tests__/DataViewsTableBase.test.tsx +0 -134
- package/src/components/data-display/DataViews/__tests__/SaveViewModal.test.tsx +0 -83
- package/src/components/data-display/DataViews/__tests__/data-views-filter.test.ts +0 -115
- package/src/components/data-display/DataViews/__tests__/data-views-preview.test.ts +0 -53
- package/src/components/data-display/DataViews/__tests__/range-pill.test.tsx +0 -114
- package/src/components/data-display/DataViews/base-card.stories.tsx +0 -136
- package/src/components/data-display/DescriptionItem/DescriptionItem.stories.tsx +0 -254
- package/src/components/data-display/EmptyState/EmptyState.stories.tsx +0 -282
- package/src/components/data-display/EmptyState/EmptyState.test.stories.tsx +0 -441
- package/src/components/data-display/ErrorState/ErrorState.stories.tsx +0 -110
- package/src/components/data-display/ErrorState/ErrorState.test.stories.tsx +0 -133
- package/src/components/data-display/HoverCard/HoverCard.stories.tsx +0 -650
- package/src/components/data-display/HoverCard/HoverCard.test.stories.tsx +0 -929
- package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.stories.tsx +0 -411
- package/src/components/data-display/LazyImage/LazyImage.stories.tsx +0 -387
- package/src/components/data-display/Lightbox/Lightbox.stories.tsx +0 -315
- package/src/components/data-display/Lightbox/Lightbox.test.stories.tsx +0 -598
- package/src/components/data-display/LoadingState/LoadingState.stories.tsx +0 -124
- package/src/components/data-display/LoadingState/LoadingState.test.stories.tsx +0 -99
- package/src/components/data-display/MapPreview/MapPreview.stories.tsx +0 -458
- package/src/components/data-display/MapPreview/MapPreview.test.stories.tsx +0 -394
- package/src/components/data-display/Popover/Popover.stories.tsx +0 -295
- package/src/components/data-display/Popover/Popover.test.stories.tsx +0 -1162
- package/src/components/data-display/Progress/Progress.stories.tsx +0 -481
- package/src/components/data-display/Progress/Progress.test.stories.tsx +0 -2070
- package/src/components/data-display/SectionOnboarding/SectionOnboarding.stories.tsx +0 -160
- package/src/components/data-display/SectionOnboarding/SectionOnboarding.test.stories.tsx +0 -190
- package/src/components/data-display/Sheet/Sheet.stories.tsx +0 -883
- package/src/components/data-display/Sheet/Sheet.test.stories.tsx +0 -1105
- package/src/components/data-display/StatCard/StatCard.stories.tsx +0 -167
- package/src/components/data-display/StatCard/StatCard.test.stories.tsx +0 -240
- package/src/components/data-display/Stepper/Stepper.stories.tsx +0 -540
- package/src/components/data-display/Stepper/Stepper.test.stories.tsx +0 -600
- package/src/components/data-display/Table/Table.stories.tsx +0 -803
- package/src/components/data-display/Table/Table.test.stories.tsx +0 -369
- package/src/components/data-display/Timeline/Timeline.stories.tsx +0 -442
- package/src/components/data-display/Timeline/Timeline.test.stories.tsx +0 -930
- package/src/components/data-display/TimingDiagram/TimingDiagram.stories.tsx +0 -475
- package/src/components/data-display/TimingDiagram/TimingDiagram.test.stories.tsx +0 -734
- package/src/components/data-display/Tooltip/Tooltip.stories.tsx +0 -468
- package/src/components/data-display/Tooltip/Tooltip.test.stories.tsx +0 -1154
- package/src/components/feedback/ConfirmAction/ConfirmAction.stories.tsx +0 -146
- package/src/components/feedback/ConfirmAction/ConfirmAction.test.stories.tsx +0 -114
- package/src/components/feedback/ConfirmAction/__tests__/confirm-action.test.tsx +0 -288
- package/src/components/feedback/Dialog/Dialog.stories.tsx +0 -833
- package/src/components/feedback/Dialog/Dialog.test.stories.tsx +0 -964
- package/src/components/feedback/Dialog/__tests__/dialog-content-spacing.test.tsx +0 -66
- package/src/components/feedback/Modal/Modal.stories.tsx +0 -765
- package/src/components/feedback/Modal/Modal.test.stories.tsx +0 -251
- package/src/components/feedback/Sonner/Sonner.stories.tsx +0 -698
- package/src/components/feedback/Sonner/Sonner.test.stories.tsx +0 -896
- package/src/components/feedback/StackedModal/StackedModal.stories.tsx +0 -1783
- package/src/components/feedback/StackedModal/StackedModal.test.stories.tsx +0 -973
- package/src/components/feedback/StackedModal/__tests__/stacked-modal-viewport.test.tsx +0 -104
- package/src/components/feedback/Toast/Toast.stories.tsx +0 -548
- package/src/components/feedback/Toast/Toast.test.stories.tsx +0 -834
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.stories.tsx +0 -691
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.test.stories.tsx +0 -634
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.stories.tsx +0 -665
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.test.stories.tsx +0 -727
- package/src/components/form/Autocomplete/Autocomplete.stories.tsx +0 -715
- package/src/components/form/Autocomplete/Autocomplete.test.stories.tsx +0 -825
- package/src/components/form/Button/Button.stories.tsx +0 -365
- package/src/components/form/Button/Button.test.stories.tsx +0 -607
- package/src/components/form/Calendar/Calendar.stories.tsx +0 -349
- package/src/components/form/Calendar/Calendar.test.stories.tsx +0 -464
- package/src/components/form/CepField/CepField.stories.tsx +0 -138
- package/src/components/form/CepField/CepField.test.stories.tsx +0 -281
- package/src/components/form/Checkbox/Checkbox.stories.tsx +0 -265
- package/src/components/form/Checkbox/Checkbox.test.stories.tsx +0 -181
- package/src/components/form/CodeEditor/CodeEditor.stories.tsx +0 -871
- package/src/components/form/CodeEditor/CodeEditor.test.stories.tsx +0 -574
- package/src/components/form/Command/Command.stories.tsx +0 -580
- package/src/components/form/Command/Command.test.stories.tsx +0 -215
- package/src/components/form/CreatableSelect/CreatableSelect.stories.tsx +0 -112
- package/src/components/form/CreatableSelect/CreatableSelect.test.stories.tsx +0 -134
- package/src/components/form/Form/Form.stories.tsx +0 -687
- package/src/components/form/Form/Form.test.stories.tsx +0 -601
- package/src/components/form/Input/Input.stories.tsx +0 -260
- package/src/components/form/Input/Input.test.stories.tsx +0 -1191
- package/src/components/form/InputOTP/InputOTP.stories.tsx +0 -774
- package/src/components/form/InputOTP/InputOTP.test.stories.tsx +0 -588
- package/src/components/form/Label/Label.stories.tsx +0 -473
- package/src/components/form/Label/Label.test.stories.tsx +0 -915
- package/src/components/form/Menubar/Menubar.stories.tsx +0 -472
- package/src/components/form/Menubar/Menubar.test.stories.tsx +0 -584
- package/src/components/form/PasswordStrength/PasswordStrength.stories.tsx +0 -562
- package/src/components/form/PasswordStrength/PasswordStrength.test.stories.tsx +0 -327
- package/src/components/form/PhoneInput/PhoneInput.stories.tsx +0 -495
- package/src/components/form/PhoneInput/PhoneInput.test.stories.tsx +0 -666
- package/src/components/form/RadioGroup/RadioGroup.stories.tsx +0 -518
- package/src/components/form/RadioGroup/RadioGroup.test.stories.tsx +0 -685
- package/src/components/form/RichTextEditor/RichTextEditor.stories.tsx +0 -261
- package/src/components/form/RichTextEditor/RichTextEditor.test.stories.tsx +0 -491
- package/src/components/form/SearchPalette/SearchPalette.stories.tsx +0 -135
- package/src/components/form/Select/Select.stories.tsx +0 -364
- package/src/components/form/Select/Select.test.stories.tsx +0 -985
- package/src/components/form/Slider/Slider.stories.tsx +0 -418
- package/src/components/form/Slider/Slider.test.stories.tsx +0 -1042
- package/src/components/form/Switch/Switch.stories.tsx +0 -697
- package/src/components/form/Switch/Switch.test.stories.tsx +0 -597
- package/src/components/form/Textarea/Textarea.stories.tsx +0 -473
- package/src/components/form/Textarea/Textarea.test.stories.tsx +0 -702
- package/src/components/form/Toggle/Toggle.stories.tsx +0 -638
- package/src/components/form/Toggle/Toggle.test.stories.tsx +0 -1159
- package/src/components/form/ToggleGroup/ToggleGroup.stories.tsx +0 -617
- package/src/components/form/ToggleGroup/ToggleGroup.test.stories.tsx +0 -915
- package/src/components/form/UploadButton/UploadButton.stories.tsx +0 -367
- package/src/components/form/UploadButton/UploadButton.test.stories.tsx +0 -573
- package/src/components/form/total-form/total-form.test.tsx +0 -214
- package/src/components/layout/Accordion/Accordion.stories.tsx +0 -694
- package/src/components/layout/Accordion/Accordion.test.stories.tsx +0 -1344
- package/src/components/layout/Card/Card.stories.tsx +0 -591
- package/src/components/layout/Card/Card.test.stories.tsx +0 -1134
- package/src/components/layout/CardGrid/__tests__/CardGrid.test.tsx +0 -92
- package/src/components/layout/Collapsible/Collapsible.stories.tsx +0 -180
- package/src/components/layout/Collapsible/Collapsible.test.stories.tsx +0 -308
- package/src/components/layout/Container/Container.stories.tsx +0 -557
- package/src/components/layout/Container/Container.test.stories.tsx +0 -745
- package/src/components/layout/ContentToolbar/ContentToolbar.stories.tsx +0 -129
- package/src/components/layout/ContentToolbar/ContentToolbar.test.stories.tsx +0 -251
- package/src/components/layout/ContentToolbar/__tests__/ColumnsMenu.test.tsx +0 -39
- package/src/components/layout/ContentToolbar/__tests__/MultiSelectDropdown.test.tsx +0 -131
- package/src/components/layout/ContentToolbar/__tests__/SavedViewsMenu.test.tsx +0 -99
- package/src/components/layout/Dashboard/Dashboard.stories.tsx +0 -104
- package/src/components/layout/Dashboard/Dashboard.test.stories.tsx +0 -190
- package/src/components/layout/Drawer/Drawer.stories.tsx +0 -658
- package/src/components/layout/Drawer/Drawer.test.stories.tsx +0 -1343
- package/src/components/layout/Resizable/Resizable.stories.tsx +0 -1039
- package/src/components/layout/Resizable/Resizable.test.stories.tsx +0 -754
- package/src/components/layout/ScrollArea/ScrollArea.stories.tsx +0 -651
- package/src/components/layout/ScrollArea/ScrollArea.test.stories.tsx +0 -619
- package/src/components/layout/Separator/Separator.stories.tsx +0 -563
- package/src/components/layout/Separator/Separator.test.stories.tsx +0 -518
- package/src/components/layout/SettingsLayout/SettingsLayout.stories.tsx +0 -192
- package/src/components/layout/SettingsLayout/SettingsLayout.test.stories.tsx +0 -175
- package/src/components/layout/Sidebar/Sidebar.stories.tsx +0 -609
- package/src/components/layout/Sidebar/Sidebar.test.stories.tsx +0 -1324
- package/src/components/layout/Skeleton/Skeleton.stories.tsx +0 -689
- package/src/components/layout/Skeleton/Skeleton.test.stories.tsx +0 -521
- package/src/components/layout/Spacer/Spacer.stories.tsx +0 -502
- package/src/components/layout/Spacer/Spacer.test.stories.tsx +0 -474
- package/src/components/layout/TableFilter/TableFilter.stories.tsx +0 -82
- package/src/components/layout/TableFilter/TableFilter.test.stories.tsx +0 -87
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.stories.tsx +0 -638
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.test.stories.tsx +0 -1008
- package/src/components/navigation/CommandPalette/CommandPalette.stories.tsx +0 -386
- package/src/components/navigation/CommandPalette/CommandPalette.test.stories.tsx +0 -435
- package/src/components/navigation/ContextMenu/ContextMenu.stories.tsx +0 -614
- package/src/components/navigation/ContextMenu/ContextMenu.test.stories.tsx +0 -845
- package/src/components/navigation/DropdownMenu/DropdownMenu.stories.tsx +0 -578
- package/src/components/navigation/DropdownMenu/DropdownMenu.test.stories.tsx +0 -660
- package/src/components/navigation/NavigationMenu/stories/NavigationMenu.stories.tsx +0 -516
- package/src/components/navigation/NavigationMenu/stories/NavigationMenu.test.stories.tsx +0 -935
- package/src/components/navigation/NavigationMenu/stories/NavigationMenuHorizontal.stories.tsx +0 -501
- package/src/components/navigation/NavigationMenu/stories/NavigationMenuVertical.stories.tsx +0 -453
- package/src/components/navigation/Pagination/Pagination.stories.tsx +0 -602
- package/src/components/navigation/Pagination/Pagination.test.stories.tsx +0 -520
- package/src/components/navigation/Tabs/Tabs.stories.tsx +0 -1004
- package/src/components/navigation/Tabs/Tabs.test.stories.tsx +0 -1225
- package/src/components/typography/Blockquote/Blockquote.stories.tsx +0 -359
- package/src/components/typography/Blockquote/Blockquote.test.stories.tsx +0 -432
- package/src/components/typography/Code/Code.stories.tsx +0 -686
- package/src/components/typography/Code/Code.test.stories.tsx +0 -703
- package/src/components/typography/Heading/Heading.stories.tsx +0 -451
- package/src/components/typography/Heading/Heading.test.stories.tsx +0 -830
- package/src/components/typography/Paragraph/Paragraph.stories.tsx +0 -280
- package/src/components/typography/Paragraph/Paragraph.test.stories.tsx +0 -231
- package/src/components/typography/Text/Text.stories.tsx +0 -359
- package/src/components/typography/Text/Text.test.stories.tsx +0 -759
- package/src/components/utility/AnimatedIcon/AnimatedIcon.stories.tsx +0 -945
- package/src/components/utility/AnimatedIcon/AnimatedIcon.test.stories.tsx +0 -603
- package/src/components/utility/AspectRatio/AspectRatio.stories.tsx +0 -414
- package/src/components/utility/AspectRatio/AspectRatio.test.stories.tsx +0 -859
- package/src/components/utility/InfiniteScroll/InfiniteScroll.stories.tsx +0 -620
- package/src/components/utility/InfiniteScroll/InfiniteScroll.test.stories.tsx +0 -361
- package/src/components/utility/InstallPrompt/InstallPrompt.stories.tsx +0 -118
- package/src/components/utility/InstallPrompt/InstallPrompt.test.stories.tsx +0 -83
- package/src/components/utility/InstallPrompt/__tests__/InstallPrompt.ios.test.tsx +0 -65
- package/src/components/utility/InstallPrompt/__tests__/usePwaInstall.test.tsx +0 -271
- package/src/components/utility/Portal/Portal.stories.tsx +0 -340
- package/src/components/utility/Portal/Portal.test.stories.tsx +0 -2086
- package/src/components/utility/Transition/Transition.stories.tsx +0 -493
- package/src/components/utility/Transition/Transition.test.stories.tsx +0 -854
- package/src/components/utility/VirtualList/VirtualList.stories.tsx +0 -1073
- package/src/components/utility/VirtualList/VirtualList.test.stories.tsx +0 -1085
- package/src/components/utility/WorkflowStep/WorkflowStep.stories.tsx +0 -431
- package/src/components/utility/WorkflowStep/WorkflowStep.test.stories.tsx +0 -364
- /package/src/{button.tsx → button/button.tsx} +0 -0
|
@@ -1,1225 +0,0 @@
|
|
|
1
|
-
import Dashboard from '@mui/icons-material/Dashboard';
|
|
2
|
-
import Email from '@mui/icons-material/Email';
|
|
3
|
-
import Home from '@mui/icons-material/Home';
|
|
4
|
-
import Notifications from '@mui/icons-material/Notifications';
|
|
5
|
-
import Person from '@mui/icons-material/Person';
|
|
6
|
-
import Security from '@mui/icons-material/Security';
|
|
7
|
-
import Settings from '@mui/icons-material/Settings';
|
|
8
|
-
import { Box, Typography } from '@mui/material';
|
|
9
|
-
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
10
|
-
import { useState } from 'react';
|
|
11
|
-
import { expect, fn,userEvent, waitFor, within } from 'storybook/test';
|
|
12
|
-
|
|
13
|
-
import { Tabs } from './Tabs';
|
|
14
|
-
import type { TabItem, TabsProps } from './Tabs.types';
|
|
15
|
-
|
|
16
|
-
const meta: Meta<typeof Tabs> = {
|
|
17
|
-
title: 'Navigation/Tabs/Tests',
|
|
18
|
-
component: Tabs,
|
|
19
|
-
parameters: {
|
|
20
|
-
layout: 'centered',
|
|
21
|
-
chromatic: { disableSnapshot: false },
|
|
22
|
-
},
|
|
23
|
-
tags: ['autodocs', 'test', 'component:Tabs'],
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export default meta;
|
|
27
|
-
type Story = StoryObj<typeof meta>;
|
|
28
|
-
|
|
29
|
-
// Test wrapper component
|
|
30
|
-
interface TabsTestWrapperProps extends Partial<TabsProps> {
|
|
31
|
-
onChangeCallback?: typeof fn;
|
|
32
|
-
onTabCloseCallback?: typeof fn;
|
|
33
|
-
onFocusCallback?: typeof fn;
|
|
34
|
-
onBlurCallback?: typeof fn;
|
|
35
|
-
initialValue?: string;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const TabsTestWrapper = ({
|
|
39
|
-
onChangeCallback = fn(),
|
|
40
|
-
onTabCloseCallback = fn(),
|
|
41
|
-
onFocusCallback = fn(),
|
|
42
|
-
onBlurCallback = fn(),
|
|
43
|
-
initialValue = 'tab1',
|
|
44
|
-
...props
|
|
45
|
-
}: TabsTestWrapperProps) => {
|
|
46
|
-
const [value, setValue] = useState(initialValue);
|
|
47
|
-
|
|
48
|
-
const handleChange = (event: React.SyntheticEvent, tabId: string) => {
|
|
49
|
-
setValue(tabId);
|
|
50
|
-
onChangeCallback(event, tabId);
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const handleTabClose = (tabId: string) => {
|
|
54
|
-
onTabCloseCallback(tabId);
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
return (
|
|
58
|
-
<Box sx={{ width: 600, minHeight: 300 }}>
|
|
59
|
-
<Tabs
|
|
60
|
-
{...props}
|
|
61
|
-
value={value}
|
|
62
|
-
onChange={handleChange}
|
|
63
|
-
onTabClose={handleTabClose}
|
|
64
|
-
onFocus={onFocusCallback}
|
|
65
|
-
onBlur={onBlurCallback}
|
|
66
|
-
/>
|
|
67
|
-
</Box>
|
|
68
|
-
);
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
// Basic tab items for testing
|
|
72
|
-
const basicTestItems: TabItem[] = [
|
|
73
|
-
{
|
|
74
|
-
id: 'tab1',
|
|
75
|
-
label: 'Tab 1',
|
|
76
|
-
content: (
|
|
77
|
-
<Box sx={{ p: 2 }}>
|
|
78
|
-
<Typography>Content for Tab 1</Typography>
|
|
79
|
-
</Box>
|
|
80
|
-
),
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
id: 'tab2',
|
|
84
|
-
label: 'Tab 2',
|
|
85
|
-
content: (
|
|
86
|
-
<Box sx={{ p: 2 }}>
|
|
87
|
-
<Typography>Content for Tab 2</Typography>
|
|
88
|
-
</Box>
|
|
89
|
-
),
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
id: 'tab3',
|
|
93
|
-
label: 'Tab 3',
|
|
94
|
-
content: (
|
|
95
|
-
<Box sx={{ p: 2 }}>
|
|
96
|
-
<Typography>Content for Tab 3</Typography>
|
|
97
|
-
</Box>
|
|
98
|
-
),
|
|
99
|
-
},
|
|
100
|
-
];
|
|
101
|
-
|
|
102
|
-
const itemsWithIcons: TabItem[] = [
|
|
103
|
-
{
|
|
104
|
-
id: 'home',
|
|
105
|
-
label: 'Home',
|
|
106
|
-
icon: <Home />,
|
|
107
|
-
content: (
|
|
108
|
-
<Box sx={{ p: 2 }}>
|
|
109
|
-
<Typography>Home content</Typography>
|
|
110
|
-
</Box>
|
|
111
|
-
),
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
id: 'settings',
|
|
115
|
-
label: 'Settings',
|
|
116
|
-
icon: <Settings />,
|
|
117
|
-
content: (
|
|
118
|
-
<Box sx={{ p: 2 }}>
|
|
119
|
-
<Typography>Settings content</Typography>
|
|
120
|
-
</Box>
|
|
121
|
-
),
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
id: 'profile',
|
|
125
|
-
label: 'Profile',
|
|
126
|
-
icon: <Person />,
|
|
127
|
-
content: (
|
|
128
|
-
<Box sx={{ p: 2 }}>
|
|
129
|
-
<Typography>Profile content</Typography>
|
|
130
|
-
</Box>
|
|
131
|
-
),
|
|
132
|
-
},
|
|
133
|
-
];
|
|
134
|
-
|
|
135
|
-
// 1. Basic Interaction Test
|
|
136
|
-
export const BasicInteraction: Story = {
|
|
137
|
-
name: '🧪 Basic Interaction Test',
|
|
138
|
-
args: {
|
|
139
|
-
items: basicTestItems,
|
|
140
|
-
onChangeCallback: fn(),
|
|
141
|
-
onTabCloseCallback: fn(),
|
|
142
|
-
onFocusCallback: fn(),
|
|
143
|
-
onBlurCallback: fn(),
|
|
144
|
-
},
|
|
145
|
-
render: (args) => <TabsTestWrapper {...args} />,
|
|
146
|
-
parameters: {
|
|
147
|
-
test: {
|
|
148
|
-
dangerouslyIgnoreUnhandledErrors: true,
|
|
149
|
-
},
|
|
150
|
-
},
|
|
151
|
-
play: async ({ canvasElement, step }) => {
|
|
152
|
-
const canvas = within(canvasElement);
|
|
153
|
-
|
|
154
|
-
await step('Initial render verification', async () => {
|
|
155
|
-
// Check tabs are rendered
|
|
156
|
-
const tab1 = canvas.getByRole('tab', { name: /Tab 1/i });
|
|
157
|
-
const tab2 = canvas.getByRole('tab', { name: /Tab 2/i });
|
|
158
|
-
const tab3 = canvas.getByRole('tab', { name: /Tab 3/i });
|
|
159
|
-
|
|
160
|
-
await expect(tab1).toBeInTheDocument();
|
|
161
|
-
await expect(tab2).toBeInTheDocument();
|
|
162
|
-
await expect(tab3).toBeInTheDocument();
|
|
163
|
-
|
|
164
|
-
// Check first tab is selected
|
|
165
|
-
await expect(tab1).toHaveAttribute('aria-selected', 'true');
|
|
166
|
-
await expect(tab2).toHaveAttribute('aria-selected', 'false');
|
|
167
|
-
|
|
168
|
-
// Check first panel is visible
|
|
169
|
-
const panel1 = canvas.getByRole('tabpanel', { name: /Tab 1/i });
|
|
170
|
-
await expect(panel1).toBeInTheDocument();
|
|
171
|
-
await expect(panel1).toHaveTextContent('Content for Tab 1');
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
await step('Tab click interaction', async () => {
|
|
175
|
-
const tab2 = canvas.getByRole('tab', { name: /Tab 2/i });
|
|
176
|
-
await userEvent.click(tab2);
|
|
177
|
-
|
|
178
|
-
// Check tab2 is now selected
|
|
179
|
-
await expect(tab2).toHaveAttribute('aria-selected', 'true');
|
|
180
|
-
|
|
181
|
-
// Check panel2 is now visible
|
|
182
|
-
const panel2 = canvas.getByRole('tabpanel', { name: /Tab 2/i });
|
|
183
|
-
await expect(panel2).toBeInTheDocument();
|
|
184
|
-
await expect(panel2).toHaveTextContent('Content for Tab 2');
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
await step('Tab click on already selected tab', async () => {
|
|
188
|
-
const tab2 = canvas.getByRole('tab', { name: /Tab 2/i });
|
|
189
|
-
await userEvent.click(tab2);
|
|
190
|
-
|
|
191
|
-
// Should still be selected
|
|
192
|
-
await expect(tab2).toHaveAttribute('aria-selected', 'true');
|
|
193
|
-
});
|
|
194
|
-
},
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
// 2. Keyboard Navigation Test
|
|
198
|
-
export const KeyboardNavigation: Story = {
|
|
199
|
-
name: '⌨️ Keyboard Navigation Test',
|
|
200
|
-
args: {
|
|
201
|
-
items: basicTestItems,
|
|
202
|
-
onChangeCallback: fn(),
|
|
203
|
-
onTabCloseCallback: fn(),
|
|
204
|
-
onFocusCallback: fn(),
|
|
205
|
-
onBlurCallback: fn(),
|
|
206
|
-
},
|
|
207
|
-
render: (args) => <TabsTestWrapper {...args} />,
|
|
208
|
-
parameters: {
|
|
209
|
-
a11y: {
|
|
210
|
-
element: '#storybook-root',
|
|
211
|
-
config: {
|
|
212
|
-
rules: [
|
|
213
|
-
{ id: 'color-contrast', enabled: true },
|
|
214
|
-
{ id: 'aria-required-attr', enabled: true },
|
|
215
|
-
{ id: 'aria-roles', enabled: true },
|
|
216
|
-
{ id: 'aria-valid-attr-value', enabled: true },
|
|
217
|
-
],
|
|
218
|
-
},
|
|
219
|
-
},
|
|
220
|
-
},
|
|
221
|
-
play: async ({ canvasElement, step }) => {
|
|
222
|
-
const canvas = within(canvasElement);
|
|
223
|
-
|
|
224
|
-
await step('Tab navigation with arrow keys', async () => {
|
|
225
|
-
const tab1 = canvas.getByRole('tab', { name: /Tab 1/i });
|
|
226
|
-
const tab2 = canvas.getByRole('tab', { name: /Tab 2/i });
|
|
227
|
-
const tab3 = canvas.getByRole('tab', { name: /Tab 3/i });
|
|
228
|
-
|
|
229
|
-
// A tab list is a single tab stop (roving tabindex), so tabbing in from
|
|
230
|
-
// the body lands on the selected tab — the first one here.
|
|
231
|
-
await userEvent.tab();
|
|
232
|
-
await waitFor(() => expect(tab1).toHaveFocus());
|
|
233
|
-
|
|
234
|
-
// Navigate right with arrow key
|
|
235
|
-
await userEvent.keyboard('{ArrowRight}');
|
|
236
|
-
await waitFor(() => expect(tab2).toHaveFocus());
|
|
237
|
-
|
|
238
|
-
// Navigate right again
|
|
239
|
-
await userEvent.keyboard('{ArrowRight}');
|
|
240
|
-
await waitFor(() => expect(tab3).toHaveFocus());
|
|
241
|
-
|
|
242
|
-
// Navigate left
|
|
243
|
-
await userEvent.keyboard('{ArrowLeft}');
|
|
244
|
-
await waitFor(() => expect(tab2).toHaveFocus());
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
await step('Tab activation with Enter key', async () => {
|
|
248
|
-
const tab3 = canvas.getByRole('tab', { name: /Tab 3/i });
|
|
249
|
-
|
|
250
|
-
// Focus is on tab 2 from the previous step; arrow right to reach tab 3.
|
|
251
|
-
await userEvent.keyboard('{ArrowRight}');
|
|
252
|
-
await waitFor(() => expect(tab3).toHaveFocus());
|
|
253
|
-
|
|
254
|
-
// Activate with Enter
|
|
255
|
-
await userEvent.keyboard('{Enter}');
|
|
256
|
-
|
|
257
|
-
// Check tab3 is selected
|
|
258
|
-
await expect(tab3).toHaveAttribute('aria-selected', 'true');
|
|
259
|
-
|
|
260
|
-
// Check panel3 is visible
|
|
261
|
-
const panel3 = canvas.getByRole('tabpanel', { name: /Tab 3/i });
|
|
262
|
-
await expect(panel3).toBeInTheDocument();
|
|
263
|
-
await expect(panel3).toHaveTextContent('Content for Tab 3');
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
await step('Tab navigation with Tab key', async () => {
|
|
267
|
-
const tab3 = canvas.getByRole('tab', { name: /Tab 3/i });
|
|
268
|
-
|
|
269
|
-
// Focus is still on tab 3, activated in the previous step.
|
|
270
|
-
await waitFor(() => expect(tab3).toHaveFocus());
|
|
271
|
-
|
|
272
|
-
// Tab to content. Because the list is one tab stop, Tab has to leave the
|
|
273
|
-
// list entirely rather than step to the next tab.
|
|
274
|
-
await userEvent.tab();
|
|
275
|
-
await waitFor(() => expect(tab3).not.toHaveFocus());
|
|
276
|
-
});
|
|
277
|
-
},
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
// 3. Closable Tabs Test
|
|
281
|
-
export const ClosableTabsTest: Story = {
|
|
282
|
-
name: '❌ Closable Tabs Test',
|
|
283
|
-
args: {
|
|
284
|
-
onChange: fn(),
|
|
285
|
-
onTabClose: fn(),
|
|
286
|
-
onFocus: fn(),
|
|
287
|
-
onBlur: fn(),
|
|
288
|
-
items: [
|
|
289
|
-
{
|
|
290
|
-
id: 'tab1',
|
|
291
|
-
label: 'Tab 1',
|
|
292
|
-
closable: true,
|
|
293
|
-
content: (
|
|
294
|
-
<Box sx={{ p: 2 }}>
|
|
295
|
-
<Typography>Content 1</Typography>
|
|
296
|
-
</Box>
|
|
297
|
-
),
|
|
298
|
-
},
|
|
299
|
-
{
|
|
300
|
-
id: 'tab2',
|
|
301
|
-
label: 'Tab 2',
|
|
302
|
-
closable: true,
|
|
303
|
-
content: (
|
|
304
|
-
<Box sx={{ p: 2 }}>
|
|
305
|
-
<Typography>Content 2</Typography>
|
|
306
|
-
</Box>
|
|
307
|
-
),
|
|
308
|
-
},
|
|
309
|
-
{
|
|
310
|
-
id: 'tab3',
|
|
311
|
-
label: 'Tab 3',
|
|
312
|
-
closable: false,
|
|
313
|
-
content: (
|
|
314
|
-
<Box sx={{ p: 2 }}>
|
|
315
|
-
<Typography>Content 3</Typography>
|
|
316
|
-
</Box>
|
|
317
|
-
),
|
|
318
|
-
},
|
|
319
|
-
],
|
|
320
|
-
},
|
|
321
|
-
render: (args) => {
|
|
322
|
-
const ClosableTabsWrapper = () => {
|
|
323
|
-
const [tabs, setTabs] = useState(args.items);
|
|
324
|
-
const [value, setValue] = useState('tab1');
|
|
325
|
-
|
|
326
|
-
const handleChange = (event: React.SyntheticEvent, tabId: string) => {
|
|
327
|
-
setValue(tabId);
|
|
328
|
-
args.onChange(event, tabId);
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
const handleTabClose = (tabId: string) => {
|
|
332
|
-
const newTabs = tabs.filter((tab) => tab.id !== tabId);
|
|
333
|
-
setTabs(newTabs);
|
|
334
|
-
if (value === tabId && newTabs.length > 0) {
|
|
335
|
-
setValue(newTabs[0].id);
|
|
336
|
-
}
|
|
337
|
-
args.onTabClose(tabId);
|
|
338
|
-
};
|
|
339
|
-
|
|
340
|
-
return (
|
|
341
|
-
<Box sx={{ width: 600, minHeight: 300 }}>
|
|
342
|
-
<Tabs
|
|
343
|
-
{...args}
|
|
344
|
-
items={tabs}
|
|
345
|
-
value={value}
|
|
346
|
-
onChange={handleChange}
|
|
347
|
-
onTabClose={handleTabClose}
|
|
348
|
-
onFocus={args.onFocus}
|
|
349
|
-
onBlur={args.onBlur}
|
|
350
|
-
/>
|
|
351
|
-
</Box>
|
|
352
|
-
);
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
return <ClosableTabsWrapper />;
|
|
356
|
-
},
|
|
357
|
-
play: async ({ canvasElement, step }) => {
|
|
358
|
-
const canvas = within(canvasElement);
|
|
359
|
-
|
|
360
|
-
await step('Verify close buttons are rendered', async () => {
|
|
361
|
-
// Check close buttons for closable tabs
|
|
362
|
-
const closeButtons = canvas.getAllByRole('button', { name: 'Close tab' });
|
|
363
|
-
// Should have 2 close buttons (tab1 and tab2)
|
|
364
|
-
await expect(closeButtons).toHaveLength(2);
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
await step('Close a tab', async () => {
|
|
368
|
-
// Get the first close button
|
|
369
|
-
const closeButtons = canvas.getAllByRole('button', { name: 'Close tab' });
|
|
370
|
-
const firstCloseButton = closeButtons[0];
|
|
371
|
-
|
|
372
|
-
// Click close button
|
|
373
|
-
await userEvent.click(firstCloseButton);
|
|
374
|
-
|
|
375
|
-
// Wait for tab to be removed
|
|
376
|
-
await waitFor(() => {
|
|
377
|
-
const tabs = canvas.getAllByRole('tab');
|
|
378
|
-
expect(tabs).toHaveLength(2);
|
|
379
|
-
});
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
await step('Verify non-closable tab has no close button', async () => {
|
|
383
|
-
const tab3 = canvas.getByRole('tab', { name: /Tab 3/i });
|
|
384
|
-
|
|
385
|
-
// Should not have a close button within Tab 3
|
|
386
|
-
await waitFor(() =>
|
|
387
|
-
expect(within(tab3).queryByRole('button', { name: 'Close tab' })).not.toBeInTheDocument(),
|
|
388
|
-
);
|
|
389
|
-
});
|
|
390
|
-
},
|
|
391
|
-
};
|
|
392
|
-
|
|
393
|
-
// 4. Badge Test
|
|
394
|
-
export const BadgeTest: Story = {
|
|
395
|
-
name: '🔴 Badge Test',
|
|
396
|
-
args: {
|
|
397
|
-
items: [
|
|
398
|
-
{
|
|
399
|
-
id: 'messages',
|
|
400
|
-
label: 'Messages',
|
|
401
|
-
icon: <Email />,
|
|
402
|
-
badge: 5,
|
|
403
|
-
content: (
|
|
404
|
-
<Box sx={{ p: 2 }}>
|
|
405
|
-
<Typography>5 new messages</Typography>
|
|
406
|
-
</Box>
|
|
407
|
-
),
|
|
408
|
-
},
|
|
409
|
-
{
|
|
410
|
-
id: 'notifications',
|
|
411
|
-
label: 'Notifications',
|
|
412
|
-
icon: <Notifications />,
|
|
413
|
-
badge: 99,
|
|
414
|
-
content: (
|
|
415
|
-
<Box sx={{ p: 2 }}>
|
|
416
|
-
<Typography>99+ notifications</Typography>
|
|
417
|
-
</Box>
|
|
418
|
-
),
|
|
419
|
-
},
|
|
420
|
-
{
|
|
421
|
-
id: 'dashboard',
|
|
422
|
-
label: 'Dashboard',
|
|
423
|
-
icon: <Dashboard />,
|
|
424
|
-
content: (
|
|
425
|
-
<Box sx={{ p: 2 }}>
|
|
426
|
-
<Typography>Dashboard content</Typography>
|
|
427
|
-
</Box>
|
|
428
|
-
),
|
|
429
|
-
},
|
|
430
|
-
],
|
|
431
|
-
onChangeCallback: fn(),
|
|
432
|
-
onTabCloseCallback: fn(),
|
|
433
|
-
onFocusCallback: fn(),
|
|
434
|
-
onBlurCallback: fn(),
|
|
435
|
-
},
|
|
436
|
-
render: (args) => <TabsTestWrapper {...args} initialValue="messages" />,
|
|
437
|
-
play: async ({ canvasElement, step }) => {
|
|
438
|
-
const canvas = within(canvasElement);
|
|
439
|
-
|
|
440
|
-
await step('Verify badges are rendered', async () => {
|
|
441
|
-
// Check for badge elements
|
|
442
|
-
const badges = canvas.getAllByText(/^(5|99)$/);
|
|
443
|
-
await expect(badges).toHaveLength(2);
|
|
444
|
-
|
|
445
|
-
// Verify badge content
|
|
446
|
-
await expect(canvas.getByText('5')).toBeInTheDocument();
|
|
447
|
-
await expect(canvas.getByText('99')).toBeInTheDocument();
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
await step('Verify tab without badge', async () => {
|
|
451
|
-
const dashboardTab = canvas.getByRole('tab', { name: /Dashboard/i });
|
|
452
|
-
await expect(dashboardTab).toBeInTheDocument();
|
|
453
|
-
|
|
454
|
-
// Dashboard tab should not contain any badge numbers
|
|
455
|
-
const dashboardContainer = dashboardTab.closest('[role="tab"]');
|
|
456
|
-
const badgeInDashboard = dashboardContainer
|
|
457
|
-
? within(dashboardContainer).queryByText(/^(5|99)$/)
|
|
458
|
-
: null;
|
|
459
|
-
await expect(badgeInDashboard).toBeNull();
|
|
460
|
-
});
|
|
461
|
-
},
|
|
462
|
-
};
|
|
463
|
-
|
|
464
|
-
// 5. Disabled Tabs Test
|
|
465
|
-
export const DisabledTabsTest: Story = {
|
|
466
|
-
name: '🚫 Disabled Tabs Test',
|
|
467
|
-
args: {
|
|
468
|
-
items: [
|
|
469
|
-
{
|
|
470
|
-
id: 'tab1',
|
|
471
|
-
label: 'Active Tab',
|
|
472
|
-
content: (
|
|
473
|
-
<Box sx={{ p: 2 }}>
|
|
474
|
-
<Typography>Active content</Typography>
|
|
475
|
-
</Box>
|
|
476
|
-
),
|
|
477
|
-
},
|
|
478
|
-
{
|
|
479
|
-
id: 'tab2',
|
|
480
|
-
label: 'Disabled Tab',
|
|
481
|
-
disabled: true,
|
|
482
|
-
content: (
|
|
483
|
-
<Box sx={{ p: 2 }}>
|
|
484
|
-
<Typography>This should not be visible</Typography>
|
|
485
|
-
</Box>
|
|
486
|
-
),
|
|
487
|
-
},
|
|
488
|
-
{
|
|
489
|
-
id: 'tab3',
|
|
490
|
-
label: 'Another Active Tab',
|
|
491
|
-
content: (
|
|
492
|
-
<Box sx={{ p: 2 }}>
|
|
493
|
-
<Typography>Another active content</Typography>
|
|
494
|
-
</Box>
|
|
495
|
-
),
|
|
496
|
-
},
|
|
497
|
-
],
|
|
498
|
-
},
|
|
499
|
-
render: (args) => <TabsTestWrapper {...args} />,
|
|
500
|
-
play: async ({ canvasElement, step }) => {
|
|
501
|
-
const canvas = within(canvasElement);
|
|
502
|
-
|
|
503
|
-
await step('Verify disabled tab appearance', async () => {
|
|
504
|
-
const disabledTab = canvas.getByRole('tab', { name: /Disabled Tab/i });
|
|
505
|
-
await expect(disabledTab).toBeInTheDocument();
|
|
506
|
-
await expect(disabledTab).toHaveAttribute('aria-disabled', 'true');
|
|
507
|
-
});
|
|
508
|
-
|
|
509
|
-
await step('Attempt to click disabled tab', async () => {
|
|
510
|
-
const disabledTab = canvas.getByRole('tab', { name: /Disabled Tab/i });
|
|
511
|
-
|
|
512
|
-
// Try to click but expect it to fail gracefully due to pointer-events: none
|
|
513
|
-
try {
|
|
514
|
-
await userEvent.click(disabledTab, { skipPointerEventsCheck: true });
|
|
515
|
-
} catch {
|
|
516
|
-
// Expected to fail due to pointer-events: none
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
// Should not be selected
|
|
520
|
-
await expect(disabledTab).toHaveAttribute('aria-selected', 'false');
|
|
521
|
-
|
|
522
|
-
// First tab should still be selected
|
|
523
|
-
const firstTab = canvas.getByRole('tab', { name: 'Active Tab' });
|
|
524
|
-
await expect(firstTab).toHaveAttribute('aria-selected', 'true');
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
await step('Verify disabled content is not shown', async () => {
|
|
528
|
-
// The disabled tab's content should not be visible
|
|
529
|
-
await waitFor(() =>
|
|
530
|
-
expect(canvas.queryByText('This should not be visible')).not.toBeInTheDocument(),
|
|
531
|
-
);
|
|
532
|
-
});
|
|
533
|
-
},
|
|
534
|
-
};
|
|
535
|
-
|
|
536
|
-
// 6. Variant Test
|
|
537
|
-
export const VariantTest: Story = {
|
|
538
|
-
name: '🎨 Variant Test',
|
|
539
|
-
args: {
|
|
540
|
-
items: itemsWithIcons,
|
|
541
|
-
},
|
|
542
|
-
render: () => {
|
|
543
|
-
const variants = ['default', 'pills', 'underline', 'enclosed'] as const;
|
|
544
|
-
|
|
545
|
-
return (
|
|
546
|
-
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 4, width: 800 }}>
|
|
547
|
-
{variants.map((variant) => (
|
|
548
|
-
<Box key={variant}>
|
|
549
|
-
<Typography variant="h6" gutterBottom>
|
|
550
|
-
{variant}
|
|
551
|
-
</Typography>
|
|
552
|
-
<TabsTestWrapper items={itemsWithIcons} variant={variant} initialValue="home" />
|
|
553
|
-
</Box>
|
|
554
|
-
))}
|
|
555
|
-
</Box>
|
|
556
|
-
);
|
|
557
|
-
},
|
|
558
|
-
play: async ({ canvasElement, step }) => {
|
|
559
|
-
const canvas = within(canvasElement);
|
|
560
|
-
|
|
561
|
-
await step('Verify all variants are rendered', async () => {
|
|
562
|
-
// Check that all variant labels are present
|
|
563
|
-
await expect(canvas.getByText('default')).toBeInTheDocument();
|
|
564
|
-
await expect(canvas.getByText('pills')).toBeInTheDocument();
|
|
565
|
-
await expect(canvas.getByText('underline')).toBeInTheDocument();
|
|
566
|
-
await expect(canvas.getByText('enclosed')).toBeInTheDocument();
|
|
567
|
-
|
|
568
|
-
// Check that tabs are rendered for each variant
|
|
569
|
-
const allTabs = canvas.getAllByRole('tab');
|
|
570
|
-
// 3 tabs × 4 variants = 12 tabs
|
|
571
|
-
await expect(allTabs).toHaveLength(12);
|
|
572
|
-
});
|
|
573
|
-
},
|
|
574
|
-
};
|
|
575
|
-
|
|
576
|
-
// 7. Size Variation Test
|
|
577
|
-
export const SizeVariationTest: Story = {
|
|
578
|
-
name: '📏 Size Variation Test',
|
|
579
|
-
args: {
|
|
580
|
-
items: basicTestItems,
|
|
581
|
-
onChange: fn(),
|
|
582
|
-
onTabClose: fn(),
|
|
583
|
-
onFocus: fn(),
|
|
584
|
-
onBlur: fn(),
|
|
585
|
-
},
|
|
586
|
-
render: (args) => {
|
|
587
|
-
const sizes = ['sm', 'md', 'lg'] as const;
|
|
588
|
-
|
|
589
|
-
return (
|
|
590
|
-
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3, width: 600 }}>
|
|
591
|
-
{sizes.map((size) => (
|
|
592
|
-
<Box key={size}>
|
|
593
|
-
<Typography variant="subtitle2" gutterBottom>
|
|
594
|
-
Size: {size}
|
|
595
|
-
</Typography>
|
|
596
|
-
<TabsTestWrapper
|
|
597
|
-
items={basicTestItems}
|
|
598
|
-
size={size}
|
|
599
|
-
variant="default"
|
|
600
|
-
initialValue="tab1"
|
|
601
|
-
onChangeCallback={args.onChange}
|
|
602
|
-
onTabCloseCallback={args.onTabClose}
|
|
603
|
-
onFocusCallback={args.onFocus}
|
|
604
|
-
onBlurCallback={args.onBlur}
|
|
605
|
-
/>
|
|
606
|
-
</Box>
|
|
607
|
-
))}
|
|
608
|
-
</Box>
|
|
609
|
-
);
|
|
610
|
-
},
|
|
611
|
-
parameters: {
|
|
612
|
-
test: {
|
|
613
|
-
dangerouslyIgnoreUnhandledErrors: true,
|
|
614
|
-
},
|
|
615
|
-
},
|
|
616
|
-
play: async ({ canvasElement, step }) => {
|
|
617
|
-
const canvas = within(canvasElement);
|
|
618
|
-
|
|
619
|
-
await step('Verify all sizes are rendered', async () => {
|
|
620
|
-
// Check size labels
|
|
621
|
-
await expect(canvas.getByText('Size: sm')).toBeInTheDocument();
|
|
622
|
-
await expect(canvas.getByText('Size: md')).toBeInTheDocument();
|
|
623
|
-
await expect(canvas.getByText('Size: lg')).toBeInTheDocument();
|
|
624
|
-
|
|
625
|
-
// Check that tabs exist for each size
|
|
626
|
-
const allTabs = canvas.getAllByRole('tab');
|
|
627
|
-
// 3 tabs × 3 sizes = 9 tabs
|
|
628
|
-
await expect(allTabs).toHaveLength(9);
|
|
629
|
-
});
|
|
630
|
-
},
|
|
631
|
-
};
|
|
632
|
-
|
|
633
|
-
// 8. Scrollable Tabs Test
|
|
634
|
-
export const ScrollableTabsTest: Story = {
|
|
635
|
-
name: '📜 Scrollable Tabs Test',
|
|
636
|
-
args: {
|
|
637
|
-
scrollable: true,
|
|
638
|
-
items: [
|
|
639
|
-
...basicTestItems,
|
|
640
|
-
{
|
|
641
|
-
id: 'tab4',
|
|
642
|
-
label: 'Tab 4',
|
|
643
|
-
content: (
|
|
644
|
-
<Box sx={{ p: 2 }}>
|
|
645
|
-
<Typography>Content 4</Typography>
|
|
646
|
-
</Box>
|
|
647
|
-
),
|
|
648
|
-
},
|
|
649
|
-
{
|
|
650
|
-
id: 'tab5',
|
|
651
|
-
label: 'Tab 5',
|
|
652
|
-
content: (
|
|
653
|
-
<Box sx={{ p: 2 }}>
|
|
654
|
-
<Typography>Content 5</Typography>
|
|
655
|
-
</Box>
|
|
656
|
-
),
|
|
657
|
-
},
|
|
658
|
-
{
|
|
659
|
-
id: 'tab6',
|
|
660
|
-
label: 'Tab 6',
|
|
661
|
-
content: (
|
|
662
|
-
<Box sx={{ p: 2 }}>
|
|
663
|
-
<Typography>Content 6</Typography>
|
|
664
|
-
</Box>
|
|
665
|
-
),
|
|
666
|
-
},
|
|
667
|
-
{
|
|
668
|
-
id: 'tab7',
|
|
669
|
-
label: 'Tab 7',
|
|
670
|
-
content: (
|
|
671
|
-
<Box sx={{ p: 2 }}>
|
|
672
|
-
<Typography>Content 7</Typography>
|
|
673
|
-
</Box>
|
|
674
|
-
),
|
|
675
|
-
},
|
|
676
|
-
{
|
|
677
|
-
id: 'tab8',
|
|
678
|
-
label: 'Tab 8',
|
|
679
|
-
content: (
|
|
680
|
-
<Box sx={{ p: 2 }}>
|
|
681
|
-
<Typography>Content 8</Typography>
|
|
682
|
-
</Box>
|
|
683
|
-
),
|
|
684
|
-
},
|
|
685
|
-
],
|
|
686
|
-
},
|
|
687
|
-
render: (args) => (
|
|
688
|
-
<Box sx={{ width: 400 }}>
|
|
689
|
-
<TabsTestWrapper {...args} />
|
|
690
|
-
</Box>
|
|
691
|
-
),
|
|
692
|
-
play: async ({ canvasElement, step }) => {
|
|
693
|
-
const canvas = within(canvasElement);
|
|
694
|
-
|
|
695
|
-
await step('Verify scrollable tabs are rendered', async () => {
|
|
696
|
-
// Check that all tabs are rendered
|
|
697
|
-
const allTabs = canvas.getAllByRole('tab');
|
|
698
|
-
await expect(allTabs).toHaveLength(8);
|
|
699
|
-
});
|
|
700
|
-
|
|
701
|
-
await step('Check for scroll buttons', async () => {
|
|
702
|
-
// MUI adds scroll buttons when tabs overflow
|
|
703
|
-
// Note: Scroll buttons might not appear if all tabs fit
|
|
704
|
-
const tabsContainer = canvas.getByRole('tablist');
|
|
705
|
-
await expect(tabsContainer).toBeInTheDocument();
|
|
706
|
-
});
|
|
707
|
-
},
|
|
708
|
-
};
|
|
709
|
-
|
|
710
|
-
// 9. Animation Test
|
|
711
|
-
export const AnimationTest: Story = {
|
|
712
|
-
name: '🎬 Animation Test',
|
|
713
|
-
args: {
|
|
714
|
-
items: basicTestItems,
|
|
715
|
-
animateContent: true,
|
|
716
|
-
animationDuration: 300,
|
|
717
|
-
},
|
|
718
|
-
render: (args) => <TabsTestWrapper {...args} />,
|
|
719
|
-
play: async ({ canvasElement, step }) => {
|
|
720
|
-
const canvas = within(canvasElement);
|
|
721
|
-
|
|
722
|
-
await step('Switch tabs with animation', async () => {
|
|
723
|
-
const tab2 = canvas.getByRole('tab', { name: /Tab 2/i });
|
|
724
|
-
|
|
725
|
-
// Click tab2
|
|
726
|
-
await userEvent.click(tab2);
|
|
727
|
-
|
|
728
|
-
// Wait for animation to complete
|
|
729
|
-
await waitFor(
|
|
730
|
-
() => {
|
|
731
|
-
const panel2 = canvas.getByRole('tabpanel', { name: /Tab 2/i });
|
|
732
|
-
expect(panel2).toBeInTheDocument();
|
|
733
|
-
expect(panel2).toHaveTextContent('Content for Tab 2');
|
|
734
|
-
},
|
|
735
|
-
{ timeout: 500 },
|
|
736
|
-
);
|
|
737
|
-
});
|
|
738
|
-
|
|
739
|
-
await step('Switch back with animation', async () => {
|
|
740
|
-
const tab1 = canvas.getByRole('tab', { name: /Tab 1/i });
|
|
741
|
-
|
|
742
|
-
// Click tab1
|
|
743
|
-
await userEvent.click(tab1);
|
|
744
|
-
|
|
745
|
-
// Wait for animation
|
|
746
|
-
await waitFor(
|
|
747
|
-
() => {
|
|
748
|
-
const panel1 = canvas.getByRole('tabpanel', { name: /Tab 1/i });
|
|
749
|
-
expect(panel1).toBeInTheDocument();
|
|
750
|
-
expect(panel1).toHaveTextContent('Content for Tab 1');
|
|
751
|
-
},
|
|
752
|
-
{ timeout: 500 },
|
|
753
|
-
);
|
|
754
|
-
});
|
|
755
|
-
},
|
|
756
|
-
};
|
|
757
|
-
|
|
758
|
-
// 10. Persist Content Test
|
|
759
|
-
export const PersistContentTest: Story = {
|
|
760
|
-
name: '💾 Persist Content Test',
|
|
761
|
-
args: {
|
|
762
|
-
items: [
|
|
763
|
-
{
|
|
764
|
-
id: 'form',
|
|
765
|
-
label: 'Form',
|
|
766
|
-
content: (
|
|
767
|
-
<Box sx={{ p: 2 }}>
|
|
768
|
-
<input type="text" data-testid="form-input" placeholder="Enter text" />
|
|
769
|
-
</Box>
|
|
770
|
-
),
|
|
771
|
-
},
|
|
772
|
-
{
|
|
773
|
-
id: 'settings',
|
|
774
|
-
label: 'Settings',
|
|
775
|
-
content: (
|
|
776
|
-
<Box sx={{ p: 2 }}>
|
|
777
|
-
<Typography>Settings content</Typography>
|
|
778
|
-
</Box>
|
|
779
|
-
),
|
|
780
|
-
},
|
|
781
|
-
],
|
|
782
|
-
persistContent: true,
|
|
783
|
-
},
|
|
784
|
-
render: (args) => <TabsTestWrapper {...args} initialValue="form" />,
|
|
785
|
-
play: async ({ canvasElement, step }) => {
|
|
786
|
-
const canvas = within(canvasElement);
|
|
787
|
-
|
|
788
|
-
await step('Enter text in form', async () => {
|
|
789
|
-
const input = canvas.getByTestId('form-input');
|
|
790
|
-
await userEvent.type(input, 'Test value');
|
|
791
|
-
await expect(input).toHaveValue('Test value');
|
|
792
|
-
});
|
|
793
|
-
|
|
794
|
-
await step('Switch to another tab', async () => {
|
|
795
|
-
const settingsTab = canvas.getByRole('tab', { name: /Settings/i });
|
|
796
|
-
await userEvent.click(settingsTab);
|
|
797
|
-
|
|
798
|
-
// Settings panel should be visible
|
|
799
|
-
const settingsPanel = canvas.getByRole('tabpanel', { name: /Settings/i });
|
|
800
|
-
await expect(settingsPanel).toBeInTheDocument();
|
|
801
|
-
});
|
|
802
|
-
|
|
803
|
-
await step('Switch back and verify form persisted', async () => {
|
|
804
|
-
const formTab = canvas.getByRole('tab', { name: /Form/i });
|
|
805
|
-
await userEvent.click(formTab);
|
|
806
|
-
|
|
807
|
-
// Check that input value is preserved
|
|
808
|
-
const input = canvas.getByTestId('form-input');
|
|
809
|
-
await expect(input).toHaveValue('Test value');
|
|
810
|
-
});
|
|
811
|
-
},
|
|
812
|
-
};
|
|
813
|
-
|
|
814
|
-
// 11. Loading State Test
|
|
815
|
-
export const LoadingStateTest: Story = {
|
|
816
|
-
name: '⏳ Loading State Test',
|
|
817
|
-
args: {
|
|
818
|
-
items: basicTestItems,
|
|
819
|
-
loading: true,
|
|
820
|
-
},
|
|
821
|
-
render: (args) => <TabsTestWrapper {...args} />,
|
|
822
|
-
play: async ({ canvasElement, step }) => {
|
|
823
|
-
const canvas = within(canvasElement);
|
|
824
|
-
|
|
825
|
-
await step('Verify loading indicator is shown', async () => {
|
|
826
|
-
// Check for loading spinner (CircularProgress)
|
|
827
|
-
const loadingIndicator = canvas.getByRole('progressbar');
|
|
828
|
-
await expect(loadingIndicator).toBeInTheDocument();
|
|
829
|
-
});
|
|
830
|
-
|
|
831
|
-
await step('Verify content is not shown while loading', async () => {
|
|
832
|
-
// Content should not be visible
|
|
833
|
-
await waitFor(() =>
|
|
834
|
-
expect(canvas.queryByText('Content for Tab 1')).not.toBeInTheDocument(),
|
|
835
|
-
);
|
|
836
|
-
});
|
|
837
|
-
},
|
|
838
|
-
};
|
|
839
|
-
|
|
840
|
-
// 12. Accessibility Test
|
|
841
|
-
export const AccessibilityTest: Story = {
|
|
842
|
-
name: '♿ Accessibility Test',
|
|
843
|
-
args: {
|
|
844
|
-
items: itemsWithIcons,
|
|
845
|
-
},
|
|
846
|
-
render: (args) => <TabsTestWrapper {...args} initialValue="home" />,
|
|
847
|
-
parameters: {
|
|
848
|
-
a11y: {
|
|
849
|
-
element: '#storybook-root',
|
|
850
|
-
config: {
|
|
851
|
-
rules: [
|
|
852
|
-
{ id: 'color-contrast', enabled: true },
|
|
853
|
-
{ id: 'aria-required-attr', enabled: true },
|
|
854
|
-
{ id: 'aria-roles', enabled: true },
|
|
855
|
-
{ id: 'aria-valid-attr-value', enabled: true },
|
|
856
|
-
{ id: 'button-name', enabled: true },
|
|
857
|
-
{ id: 'tabindex', enabled: true },
|
|
858
|
-
],
|
|
859
|
-
},
|
|
860
|
-
},
|
|
861
|
-
},
|
|
862
|
-
play: async ({ canvasElement, step }) => {
|
|
863
|
-
const canvas = within(canvasElement);
|
|
864
|
-
|
|
865
|
-
await step('Verify ARIA attributes', async () => {
|
|
866
|
-
// Check tablist role
|
|
867
|
-
const tablist = canvas.getByRole('tablist');
|
|
868
|
-
await expect(tablist).toBeInTheDocument();
|
|
869
|
-
|
|
870
|
-
// Check tab roles and ARIA attributes
|
|
871
|
-
const tabs = canvas.getAllByRole('tab');
|
|
872
|
-
for (const tab of tabs) {
|
|
873
|
-
await expect(tab).toHaveAttribute('role', 'tab');
|
|
874
|
-
await expect(tab).toHaveAttribute('aria-controls');
|
|
875
|
-
await expect(tab).toHaveAttribute('id');
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
// Check tabpanel role and ARIA attributes
|
|
879
|
-
const panel = canvas.getByRole('tabpanel');
|
|
880
|
-
await expect(panel).toHaveAttribute('role', 'tabpanel');
|
|
881
|
-
await expect(panel).toHaveAttribute('aria-labelledby');
|
|
882
|
-
await expect(panel).toHaveAttribute('id');
|
|
883
|
-
});
|
|
884
|
-
|
|
885
|
-
await step('Verify keyboard accessibility', async () => {
|
|
886
|
-
const firstTab = canvas.getByRole('tab', { name: /Home/i });
|
|
887
|
-
|
|
888
|
-
// Tab in from the body — the selected tab is the list's single tab stop.
|
|
889
|
-
await userEvent.tab();
|
|
890
|
-
await waitFor(() => expect(firstTab).toHaveFocus());
|
|
891
|
-
|
|
892
|
-
// Tab key should move focus out of tablist
|
|
893
|
-
await userEvent.tab();
|
|
894
|
-
await waitFor(() => expect(firstTab).not.toHaveFocus());
|
|
895
|
-
|
|
896
|
-
// Shift+Tab should return focus to tablist
|
|
897
|
-
await userEvent.tab({ shift: true });
|
|
898
|
-
await waitFor(() => expect(firstTab).toHaveFocus());
|
|
899
|
-
});
|
|
900
|
-
},
|
|
901
|
-
};
|
|
902
|
-
|
|
903
|
-
// 13. Color Theme Test
|
|
904
|
-
export const ColorThemeTest: Story = {
|
|
905
|
-
name: '🎨 Color Theme Test',
|
|
906
|
-
args: {
|
|
907
|
-
items: basicTestItems,
|
|
908
|
-
},
|
|
909
|
-
render: () => (
|
|
910
|
-
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3, width: 600 }}>
|
|
911
|
-
<Box>
|
|
912
|
-
<Typography variant="subtitle2" gutterBottom>
|
|
913
|
-
Primary Color
|
|
914
|
-
</Typography>
|
|
915
|
-
<TabsTestWrapper items={basicTestItems} color="primary" variant="default" />
|
|
916
|
-
</Box>
|
|
917
|
-
<Box>
|
|
918
|
-
<Typography variant="subtitle2" gutterBottom>
|
|
919
|
-
Secondary Color
|
|
920
|
-
</Typography>
|
|
921
|
-
<TabsTestWrapper items={basicTestItems} color="secondary" variant="default" />
|
|
922
|
-
</Box>
|
|
923
|
-
</Box>
|
|
924
|
-
),
|
|
925
|
-
play: async ({ canvasElement, step }) => {
|
|
926
|
-
const canvas = within(canvasElement);
|
|
927
|
-
|
|
928
|
-
await step('Verify both color themes are rendered', async () => {
|
|
929
|
-
await expect(canvas.getByText('Primary Color')).toBeInTheDocument();
|
|
930
|
-
await expect(canvas.getByText('Secondary Color')).toBeInTheDocument();
|
|
931
|
-
|
|
932
|
-
// Check that tabs are rendered for both themes
|
|
933
|
-
const allTabs = canvas.getAllByRole('tab');
|
|
934
|
-
await expect(allTabs).toHaveLength(6); // 3 tabs × 2 themes
|
|
935
|
-
});
|
|
936
|
-
},
|
|
937
|
-
};
|
|
938
|
-
|
|
939
|
-
// 14. Full Width Test
|
|
940
|
-
export const FullWidthTest: Story = {
|
|
941
|
-
name: '↔️ Full Width Test',
|
|
942
|
-
args: {
|
|
943
|
-
items: basicTestItems.slice(0, 2),
|
|
944
|
-
fullWidth: true,
|
|
945
|
-
variant: 'default',
|
|
946
|
-
},
|
|
947
|
-
render: (args) => (
|
|
948
|
-
<Box sx={{ width: 800, border: '1px dashed #ccc', p: 2 }}>
|
|
949
|
-
<TabsTestWrapper {...args} />
|
|
950
|
-
</Box>
|
|
951
|
-
),
|
|
952
|
-
play: async ({ canvasElement, step }) => {
|
|
953
|
-
const canvas = within(canvasElement);
|
|
954
|
-
|
|
955
|
-
await step('Verify tabs stretch full width', async () => {
|
|
956
|
-
const tablist = canvas.getByRole('tablist');
|
|
957
|
-
await expect(tablist).toBeInTheDocument();
|
|
958
|
-
|
|
959
|
-
// Tabs should be present
|
|
960
|
-
const tabs = canvas.getAllByRole('tab');
|
|
961
|
-
await expect(tabs).toHaveLength(2);
|
|
962
|
-
});
|
|
963
|
-
},
|
|
964
|
-
};
|
|
965
|
-
|
|
966
|
-
// 15. Centered Tabs Test
|
|
967
|
-
export const CenteredTabsTest: Story = {
|
|
968
|
-
name: '🎯 Centered Tabs Test',
|
|
969
|
-
args: {
|
|
970
|
-
items: basicTestItems.slice(0, 2),
|
|
971
|
-
centered: true,
|
|
972
|
-
variant: 'default',
|
|
973
|
-
},
|
|
974
|
-
render: (args) => (
|
|
975
|
-
<Box sx={{ width: 800, border: '1px dashed #ccc', p: 2 }}>
|
|
976
|
-
<TabsTestWrapper {...args} />
|
|
977
|
-
</Box>
|
|
978
|
-
),
|
|
979
|
-
play: async ({ canvasElement, step }) => {
|
|
980
|
-
const canvas = within(canvasElement);
|
|
981
|
-
|
|
982
|
-
await step('Verify tabs are centered', async () => {
|
|
983
|
-
const tablist = canvas.getByRole('tablist');
|
|
984
|
-
await expect(tablist).toBeInTheDocument();
|
|
985
|
-
|
|
986
|
-
// Tabs should be present
|
|
987
|
-
const tabs = canvas.getAllByRole('tab');
|
|
988
|
-
await expect(tabs).toHaveLength(2);
|
|
989
|
-
});
|
|
990
|
-
},
|
|
991
|
-
};
|
|
992
|
-
|
|
993
|
-
// 16. Edge Cases Test
|
|
994
|
-
export const EdgeCasesTest: Story = {
|
|
995
|
-
name: '🔧 Edge Cases Test',
|
|
996
|
-
args: {
|
|
997
|
-
items: [
|
|
998
|
-
{
|
|
999
|
-
id: 'empty',
|
|
1000
|
-
label: '',
|
|
1001
|
-
content: <Box sx={{ p: 2 }}></Box>,
|
|
1002
|
-
},
|
|
1003
|
-
{
|
|
1004
|
-
id: 'long',
|
|
1005
|
-
label: 'This is a very long tab label that might overflow the container',
|
|
1006
|
-
content: (
|
|
1007
|
-
<Box sx={{ p: 2 }}>
|
|
1008
|
-
<Typography>Long label content</Typography>
|
|
1009
|
-
</Box>
|
|
1010
|
-
),
|
|
1011
|
-
},
|
|
1012
|
-
{
|
|
1013
|
-
id: 'special',
|
|
1014
|
-
label: 'Special @#$% Characters!',
|
|
1015
|
-
content: (
|
|
1016
|
-
<Box sx={{ p: 2 }}>
|
|
1017
|
-
<Typography>Special characters content</Typography>
|
|
1018
|
-
</Box>
|
|
1019
|
-
),
|
|
1020
|
-
},
|
|
1021
|
-
],
|
|
1022
|
-
},
|
|
1023
|
-
render: (args) => (
|
|
1024
|
-
<Box sx={{ width: 400 }}>
|
|
1025
|
-
<TabsTestWrapper {...args} initialValue="long" />
|
|
1026
|
-
</Box>
|
|
1027
|
-
),
|
|
1028
|
-
play: async ({ canvasElement, step }) => {
|
|
1029
|
-
const canvas = within(canvasElement);
|
|
1030
|
-
|
|
1031
|
-
await step('Verify edge case tabs are rendered', async () => {
|
|
1032
|
-
const tabs = canvas.getAllByRole('tab');
|
|
1033
|
-
await expect(tabs).toHaveLength(3);
|
|
1034
|
-
|
|
1035
|
-
// Check special characters tab
|
|
1036
|
-
const specialTab = canvas.getByRole('tab', { name: /Special.*Characters/i });
|
|
1037
|
-
await expect(specialTab).toBeInTheDocument();
|
|
1038
|
-
|
|
1039
|
-
// Handle empty label
|
|
1040
|
-
const emptyTab = tabs[0];
|
|
1041
|
-
await expect(emptyTab).toBeInTheDocument();
|
|
1042
|
-
// Even with empty label, tab should be clickable
|
|
1043
|
-
await userEvent.click(emptyTab);
|
|
1044
|
-
await expect(emptyTab).toHaveAttribute('aria-selected', 'true');
|
|
1045
|
-
});
|
|
1046
|
-
},
|
|
1047
|
-
};
|
|
1048
|
-
|
|
1049
|
-
// 17. Custom Indicator Color Test
|
|
1050
|
-
export const CustomIndicatorColorTest: Story = {
|
|
1051
|
-
name: '🖌️ Custom Indicator Color Test',
|
|
1052
|
-
args: {
|
|
1053
|
-
items: basicTestItems,
|
|
1054
|
-
variant: 'default',
|
|
1055
|
-
indicatorColor: '#ff6b6b',
|
|
1056
|
-
},
|
|
1057
|
-
render: (args) => <TabsTestWrapper {...args} />,
|
|
1058
|
-
play: async ({ canvasElement, step }) => {
|
|
1059
|
-
const canvas = within(canvasElement);
|
|
1060
|
-
|
|
1061
|
-
await step('Verify tabs with custom indicator are rendered', async () => {
|
|
1062
|
-
const tabs = canvas.getAllByRole('tab');
|
|
1063
|
-
await expect(tabs).toHaveLength(3);
|
|
1064
|
-
|
|
1065
|
-
// Click to switch tabs and verify indicator moves
|
|
1066
|
-
const tab2 = canvas.getByRole('tab', { name: /Tab 2/i });
|
|
1067
|
-
await userEvent.click(tab2);
|
|
1068
|
-
await expect(tab2).toHaveAttribute('aria-selected', 'true');
|
|
1069
|
-
});
|
|
1070
|
-
},
|
|
1071
|
-
};
|
|
1072
|
-
|
|
1073
|
-
// 18. Dividers Test
|
|
1074
|
-
export const DividersTest: Story = {
|
|
1075
|
-
name: '│ Dividers Test',
|
|
1076
|
-
args: {
|
|
1077
|
-
items: basicTestItems,
|
|
1078
|
-
showDividers: true,
|
|
1079
|
-
variant: 'underline',
|
|
1080
|
-
},
|
|
1081
|
-
render: (args) => <TabsTestWrapper {...args} />,
|
|
1082
|
-
play: async ({ canvasElement, step }) => {
|
|
1083
|
-
const canvas = within(canvasElement);
|
|
1084
|
-
|
|
1085
|
-
await step('Verify tabs with dividers are rendered', async () => {
|
|
1086
|
-
const tabs = canvas.getAllByRole('tab');
|
|
1087
|
-
await expect(tabs).toHaveLength(3);
|
|
1088
|
-
|
|
1089
|
-
// Tabs should be functional
|
|
1090
|
-
const tab2 = canvas.getByRole('tab', { name: /Tab 2/i });
|
|
1091
|
-
await userEvent.click(tab2);
|
|
1092
|
-
await expect(tab2).toHaveAttribute('aria-selected', 'true');
|
|
1093
|
-
});
|
|
1094
|
-
},
|
|
1095
|
-
};
|
|
1096
|
-
|
|
1097
|
-
// 19. Comprehensive Integration Test
|
|
1098
|
-
export const IntegrationTest: Story = {
|
|
1099
|
-
name: '🔗 Integration Test',
|
|
1100
|
-
args: {
|
|
1101
|
-
variant: 'pills',
|
|
1102
|
-
size: 'md',
|
|
1103
|
-
color: 'primary',
|
|
1104
|
-
animateContent: true,
|
|
1105
|
-
onChange: fn(),
|
|
1106
|
-
onTabClose: fn(),
|
|
1107
|
-
onFocus: fn(),
|
|
1108
|
-
onBlur: fn(),
|
|
1109
|
-
items: [
|
|
1110
|
-
{
|
|
1111
|
-
id: 'home',
|
|
1112
|
-
label: 'Home',
|
|
1113
|
-
icon: <Home />,
|
|
1114
|
-
badge: 2,
|
|
1115
|
-
content: (
|
|
1116
|
-
<Box sx={{ p: 2 }}>
|
|
1117
|
-
<Typography>Home with notifications</Typography>
|
|
1118
|
-
</Box>
|
|
1119
|
-
),
|
|
1120
|
-
},
|
|
1121
|
-
{
|
|
1122
|
-
id: 'security',
|
|
1123
|
-
label: 'Security',
|
|
1124
|
-
icon: <Security />,
|
|
1125
|
-
disabled: true,
|
|
1126
|
-
content: (
|
|
1127
|
-
<Box sx={{ p: 2 }}>
|
|
1128
|
-
<Typography>Security (disabled)</Typography>
|
|
1129
|
-
</Box>
|
|
1130
|
-
),
|
|
1131
|
-
},
|
|
1132
|
-
{
|
|
1133
|
-
id: 'settings',
|
|
1134
|
-
label: 'Settings',
|
|
1135
|
-
icon: <Settings />,
|
|
1136
|
-
closable: true,
|
|
1137
|
-
content: (
|
|
1138
|
-
<Box sx={{ p: 2 }}>
|
|
1139
|
-
<Typography>Closable settings</Typography>
|
|
1140
|
-
</Box>
|
|
1141
|
-
),
|
|
1142
|
-
},
|
|
1143
|
-
],
|
|
1144
|
-
},
|
|
1145
|
-
render: (args) => {
|
|
1146
|
-
const IntegrationWrapper = () => {
|
|
1147
|
-
const [items, setItems] = useState(args.items);
|
|
1148
|
-
const [value, setValue] = useState('home');
|
|
1149
|
-
|
|
1150
|
-
const handleChange = (event: React.SyntheticEvent, tabId: string) => {
|
|
1151
|
-
setValue(tabId);
|
|
1152
|
-
args.onChange(event, tabId);
|
|
1153
|
-
};
|
|
1154
|
-
|
|
1155
|
-
const handleTabClose = (tabId: string) => {
|
|
1156
|
-
const newItems = items.filter((item) => item.id !== tabId);
|
|
1157
|
-
setItems(newItems);
|
|
1158
|
-
if (value === tabId && newItems.length > 0) {
|
|
1159
|
-
setValue(newItems[0].id);
|
|
1160
|
-
}
|
|
1161
|
-
args.onTabClose(tabId);
|
|
1162
|
-
};
|
|
1163
|
-
|
|
1164
|
-
return (
|
|
1165
|
-
<Box sx={{ width: 600, minHeight: 300 }}>
|
|
1166
|
-
<Tabs
|
|
1167
|
-
{...args}
|
|
1168
|
-
items={items}
|
|
1169
|
-
value={value}
|
|
1170
|
-
onChange={handleChange}
|
|
1171
|
-
onTabClose={handleTabClose}
|
|
1172
|
-
onFocus={args.onFocus}
|
|
1173
|
-
onBlur={args.onBlur}
|
|
1174
|
-
/>
|
|
1175
|
-
</Box>
|
|
1176
|
-
);
|
|
1177
|
-
};
|
|
1178
|
-
|
|
1179
|
-
return <IntegrationWrapper />;
|
|
1180
|
-
},
|
|
1181
|
-
play: async ({ canvasElement, step }) => {
|
|
1182
|
-
const canvas = within(canvasElement);
|
|
1183
|
-
|
|
1184
|
-
await step('Verify all features work together', async () => {
|
|
1185
|
-
// Check badge
|
|
1186
|
-
await expect(canvas.getByText('2')).toBeInTheDocument();
|
|
1187
|
-
|
|
1188
|
-
// Check disabled tab
|
|
1189
|
-
const securityTab = canvas.getByRole('tab', { name: /Security/i });
|
|
1190
|
-
await expect(securityTab).toHaveAttribute('aria-disabled', 'true');
|
|
1191
|
-
|
|
1192
|
-
// Check closable tab has close button
|
|
1193
|
-
const closeButtons = canvas.getAllByRole('button', { name: 'Close tab' });
|
|
1194
|
-
await expect(closeButtons.length).toBeGreaterThan(0);
|
|
1195
|
-
});
|
|
1196
|
-
|
|
1197
|
-
await step('Test tab switching with animation', async () => {
|
|
1198
|
-
const settingsTab = canvas.getByRole('tab', { name: /Settings/i });
|
|
1199
|
-
await userEvent.click(settingsTab);
|
|
1200
|
-
|
|
1201
|
-
// Wait for animation
|
|
1202
|
-
await waitFor(() => {
|
|
1203
|
-
const panel = canvas.getByRole('tabpanel');
|
|
1204
|
-
expect(panel).toHaveTextContent('Closable settings');
|
|
1205
|
-
});
|
|
1206
|
-
});
|
|
1207
|
-
|
|
1208
|
-
await step('Close the settings tab', async () => {
|
|
1209
|
-
const closeButtons = canvas.getAllByRole('button', { name: 'Close tab' });
|
|
1210
|
-
const settingsCloseButton = closeButtons[0];
|
|
1211
|
-
|
|
1212
|
-
await userEvent.click(settingsCloseButton);
|
|
1213
|
-
|
|
1214
|
-
// Wait for tab to be removed
|
|
1215
|
-
await waitFor(() => {
|
|
1216
|
-
const settingsTab = canvas.queryByRole('tab', { name: /Settings/i });
|
|
1217
|
-
expect(settingsTab).not.toBeInTheDocument();
|
|
1218
|
-
});
|
|
1219
|
-
|
|
1220
|
-
// Should switch to home tab
|
|
1221
|
-
const homeTab = canvas.getByRole('tab', { name: /Home/i });
|
|
1222
|
-
await expect(homeTab).toHaveAttribute('aria-selected', 'true');
|
|
1223
|
-
});
|
|
1224
|
-
},
|
|
1225
|
-
};
|