@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,935 +0,0 @@
|
|
|
1
|
-
import Analytics from '@mui/icons-material/Analytics';
|
|
2
|
-
import Dashboard from '@mui/icons-material/Dashboard';
|
|
3
|
-
import Home from '@mui/icons-material/Home';
|
|
4
|
-
import Inventory from '@mui/icons-material/Inventory';
|
|
5
|
-
import Notifications from '@mui/icons-material/Notifications';
|
|
6
|
-
import People from '@mui/icons-material/People';
|
|
7
|
-
import Settings from '@mui/icons-material/Settings';
|
|
8
|
-
import ShoppingCart from '@mui/icons-material/ShoppingCart';
|
|
9
|
-
import { Box, createTheme,ThemeProvider, Typography } from '@mui/material';
|
|
10
|
-
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
11
|
-
import { useState } from 'react';
|
|
12
|
-
import { expect, fn,userEvent, waitFor, within } from 'storybook/test';
|
|
13
|
-
|
|
14
|
-
import { NavigationMenu } from '../NavigationMenu';
|
|
15
|
-
import type { NavigationMenuItem } from '../NavigationMenu.types';
|
|
16
|
-
|
|
17
|
-
const meta: Meta<typeof NavigationMenu> = {
|
|
18
|
-
title: 'Navigation/NavigationMenu/Tests',
|
|
19
|
-
component: NavigationMenu,
|
|
20
|
-
parameters: {
|
|
21
|
-
layout: 'fullscreen',
|
|
22
|
-
chromatic: { disableSnapshot: false },
|
|
23
|
-
},
|
|
24
|
-
tags: ['autodocs', 'test', 'component:NavigationMenu'],
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export default meta;
|
|
28
|
-
type Story = StoryObj<typeof meta>;
|
|
29
|
-
|
|
30
|
-
// Test data
|
|
31
|
-
const testItems: NavigationMenuItem[] = [
|
|
32
|
-
{
|
|
33
|
-
id: '1',
|
|
34
|
-
label: 'Dashboard',
|
|
35
|
-
icon: <Dashboard />,
|
|
36
|
-
href: '#dashboard',
|
|
37
|
-
active: true,
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
id: '2',
|
|
41
|
-
label: 'Orders',
|
|
42
|
-
icon: <ShoppingCart />,
|
|
43
|
-
href: '#orders',
|
|
44
|
-
badge: 5,
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
id: '3',
|
|
48
|
-
label: 'Customers',
|
|
49
|
-
icon: <People />,
|
|
50
|
-
href: '#customers',
|
|
51
|
-
disabled: true,
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
id: '4',
|
|
55
|
-
label: 'Analytics',
|
|
56
|
-
icon: <Analytics />,
|
|
57
|
-
children: [
|
|
58
|
-
{ id: '4-1', label: 'Overview', href: '#overview' },
|
|
59
|
-
{ id: '4-2', label: 'Reports', href: '#reports' },
|
|
60
|
-
{ id: '4-3', label: 'Insights', href: '#insights' },
|
|
61
|
-
],
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
id: '5',
|
|
65
|
-
label: 'Settings',
|
|
66
|
-
icon: <Settings />,
|
|
67
|
-
href: '#settings',
|
|
68
|
-
},
|
|
69
|
-
];
|
|
70
|
-
|
|
71
|
-
const nestedItems: NavigationMenuItem[] = [
|
|
72
|
-
{
|
|
73
|
-
id: '1',
|
|
74
|
-
label: 'Products',
|
|
75
|
-
icon: <Inventory />,
|
|
76
|
-
children: [
|
|
77
|
-
{
|
|
78
|
-
id: '1-1',
|
|
79
|
-
label: 'Categories',
|
|
80
|
-
children: [
|
|
81
|
-
{ id: '1-1-1', label: 'Electronics', href: '#electronics' },
|
|
82
|
-
{ id: '1-1-2', label: 'Clothing', href: '#clothing' },
|
|
83
|
-
],
|
|
84
|
-
},
|
|
85
|
-
{ id: '1-2', label: 'Brands', href: '#brands' },
|
|
86
|
-
],
|
|
87
|
-
},
|
|
88
|
-
];
|
|
89
|
-
|
|
90
|
-
// 1. Basic Interaction Tests
|
|
91
|
-
export const BasicInteraction: Story = {
|
|
92
|
-
args: {
|
|
93
|
-
variant: 'vertical',
|
|
94
|
-
items: testItems,
|
|
95
|
-
size: 'md',
|
|
96
|
-
},
|
|
97
|
-
decorators: [
|
|
98
|
-
(Story) => (
|
|
99
|
-
<Box sx={{ height: '600px', display: 'flex' }}>
|
|
100
|
-
<Story />
|
|
101
|
-
<Box sx={{ flex: 1, p: 3 }}>
|
|
102
|
-
<Typography>Content Area</Typography>
|
|
103
|
-
</Box>
|
|
104
|
-
</Box>
|
|
105
|
-
),
|
|
106
|
-
],
|
|
107
|
-
play: async ({ canvasElement }) => {
|
|
108
|
-
const canvas = within(canvasElement);
|
|
109
|
-
|
|
110
|
-
// Test clicking on menu items
|
|
111
|
-
const ordersItem = canvas.getByText('Orders').closest('a, a, div[role="button"]');
|
|
112
|
-
await userEvent.click(ordersItem!);
|
|
113
|
-
|
|
114
|
-
// Test expandable items
|
|
115
|
-
const analyticsItem = canvas.getByText('Analytics').closest('a, a, div[role="button"]');
|
|
116
|
-
await userEvent.click(analyticsItem!);
|
|
117
|
-
|
|
118
|
-
// Wait for submenu to appear
|
|
119
|
-
await waitFor(() => {
|
|
120
|
-
expect(canvas.getByText('Overview')).toBeInTheDocument();
|
|
121
|
-
expect(canvas.getByText('Reports')).toBeInTheDocument();
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// Test clicking submenu item
|
|
125
|
-
const overviewItem = canvas.getByText('Overview').closest('a, a, div[role="button"]');
|
|
126
|
-
await userEvent.click(overviewItem!);
|
|
127
|
-
|
|
128
|
-
// Test disabled item (should not be clickable)
|
|
129
|
-
const customersItem = canvas.getByText('Customers');
|
|
130
|
-
const customersButton = customersItem.closest('a, div[role="button"]');
|
|
131
|
-
expect(customersButton).toHaveAttribute('aria-disabled', 'true');
|
|
132
|
-
|
|
133
|
-
// Test badge display
|
|
134
|
-
const badge = canvas.getByText('5');
|
|
135
|
-
expect(badge).toBeInTheDocument();
|
|
136
|
-
},
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
// 2. Collapsible Menu Interaction
|
|
140
|
-
export const CollapsibleMenuInteraction: Story = {
|
|
141
|
-
args: {
|
|
142
|
-
variant: 'vertical',
|
|
143
|
-
items: testItems,
|
|
144
|
-
collapsible: true,
|
|
145
|
-
collapsed: false, // Start expanded
|
|
146
|
-
onCollapseChange: fn(),
|
|
147
|
-
logo: (
|
|
148
|
-
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
149
|
-
<Home />
|
|
150
|
-
<Typography>Logo</Typography>
|
|
151
|
-
</Box>
|
|
152
|
-
),
|
|
153
|
-
},
|
|
154
|
-
decorators: [
|
|
155
|
-
(Story) => (
|
|
156
|
-
<Box sx={{ height: '600px', display: 'flex' }}>
|
|
157
|
-
<Story />
|
|
158
|
-
<Box sx={{ flex: 1, p: 3 }}>
|
|
159
|
-
<Typography>Content Area</Typography>
|
|
160
|
-
</Box>
|
|
161
|
-
</Box>
|
|
162
|
-
),
|
|
163
|
-
],
|
|
164
|
-
play: async ({ canvasElement }) => {
|
|
165
|
-
const canvas = within(canvasElement);
|
|
166
|
-
|
|
167
|
-
// Wait for menu to be rendered
|
|
168
|
-
await waitFor(
|
|
169
|
-
() => {
|
|
170
|
-
expect(canvas.getByText('Dashboard')).toBeInTheDocument();
|
|
171
|
-
},
|
|
172
|
-
{ timeout: 3000 },
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
// Verify that the collapse functionality exists by finding the collapse button
|
|
176
|
-
const collapseButton = canvas.queryByText('Collapse');
|
|
177
|
-
if (collapseButton) {
|
|
178
|
-
// Test basic interaction - click collapse button
|
|
179
|
-
await userEvent.click(collapseButton);
|
|
180
|
-
|
|
181
|
-
// Wait a moment for animation
|
|
182
|
-
await waitFor(
|
|
183
|
-
() => {
|
|
184
|
-
// Just verify the component doesn't crash
|
|
185
|
-
expect(canvas.getByTestId('MenuIcon')).toBeInTheDocument();
|
|
186
|
-
},
|
|
187
|
-
{ timeout: 1000 },
|
|
188
|
-
);
|
|
189
|
-
} else {
|
|
190
|
-
// If no collapse button found, just verify menu items are present
|
|
191
|
-
expect(canvas.getByText('Dashboard')).toBeInTheDocument();
|
|
192
|
-
}
|
|
193
|
-
},
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
// 3. Keyboard Navigation Tests
|
|
197
|
-
export const KeyboardNavigation: Story = {
|
|
198
|
-
args: {
|
|
199
|
-
variant: 'vertical',
|
|
200
|
-
items: testItems,
|
|
201
|
-
},
|
|
202
|
-
decorators: [
|
|
203
|
-
(Story) => (
|
|
204
|
-
<Box sx={{ height: '600px', display: 'flex' }}>
|
|
205
|
-
<Story />
|
|
206
|
-
<Box sx={{ flex: 1, p: 3 }}>
|
|
207
|
-
<Typography>Content Area</Typography>
|
|
208
|
-
</Box>
|
|
209
|
-
</Box>
|
|
210
|
-
),
|
|
211
|
-
],
|
|
212
|
-
play: async ({ canvasElement }) => {
|
|
213
|
-
const canvas = within(canvasElement);
|
|
214
|
-
|
|
215
|
-
// Wait for component to render
|
|
216
|
-
await waitFor(
|
|
217
|
-
() => {
|
|
218
|
-
expect(canvas.getByText('Dashboard')).toBeInTheDocument();
|
|
219
|
-
expect(canvas.getByText('Analytics')).toBeInTheDocument();
|
|
220
|
-
},
|
|
221
|
-
{ timeout: 3000 },
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
// Test basic keyboard interactions
|
|
225
|
-
const dashboardItem = canvas.getByText('Dashboard').closest('button, a, [role="button"]') as HTMLElement;
|
|
226
|
-
if (dashboardItem) {
|
|
227
|
-
// Dashboard is the first item in the menu, so one tab reaches it. Tabbing
|
|
228
|
-
// rather than calling .focus() keeps this a keyboard journey, and the tab
|
|
229
|
-
// below continues it to the next item.
|
|
230
|
-
await userEvent.tab();
|
|
231
|
-
|
|
232
|
-
// Test that item can receive focus
|
|
233
|
-
await waitFor(() => expect(dashboardItem).toHaveFocus());
|
|
234
|
-
|
|
235
|
-
// Test tab navigation to next item
|
|
236
|
-
await userEvent.tab();
|
|
237
|
-
|
|
238
|
-
// Verify tab navigation worked by checking focus moved
|
|
239
|
-
const focusedElement = document.activeElement;
|
|
240
|
-
expect(focusedElement).not.toBe(dashboardItem);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Test Analytics expansion via click (keyboard equivalent of Enter)
|
|
244
|
-
const analyticsItem = canvas.getByText('Analytics');
|
|
245
|
-
await userEvent.click(analyticsItem);
|
|
246
|
-
|
|
247
|
-
// Wait for submenu and verify it opened
|
|
248
|
-
await waitFor(
|
|
249
|
-
() => {
|
|
250
|
-
expect(canvas.getByText('Overview')).toBeInTheDocument();
|
|
251
|
-
},
|
|
252
|
-
{ timeout: 2000 },
|
|
253
|
-
);
|
|
254
|
-
},
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
// 4. Screen Reader Accessibility Tests
|
|
258
|
-
export const ScreenReaderAccessibility: Story = {
|
|
259
|
-
args: {
|
|
260
|
-
variant: 'vertical',
|
|
261
|
-
items: testItems,
|
|
262
|
-
},
|
|
263
|
-
decorators: [
|
|
264
|
-
(Story) => (
|
|
265
|
-
<Box sx={{ height: '600px', display: 'flex' }}>
|
|
266
|
-
<Story />
|
|
267
|
-
<Box sx={{ flex: 1, p: 3 }}>
|
|
268
|
-
<Typography>Content Area</Typography>
|
|
269
|
-
</Box>
|
|
270
|
-
</Box>
|
|
271
|
-
),
|
|
272
|
-
],
|
|
273
|
-
play: async ({ canvasElement }) => {
|
|
274
|
-
const canvas = within(canvasElement);
|
|
275
|
-
|
|
276
|
-
// Check ARIA attributes
|
|
277
|
-
const dashboardButton = canvas.getByText('Dashboard').closest('a, div[role="button"]');
|
|
278
|
-
expect(dashboardButton).toBeTruthy();
|
|
279
|
-
|
|
280
|
-
// Check disabled item has proper ARIA
|
|
281
|
-
const customersButton = canvas.getByText('Customers').closest('a, div[role="button"]');
|
|
282
|
-
expect(customersButton).toHaveAttribute('aria-disabled', 'true');
|
|
283
|
-
|
|
284
|
-
// Check list structure
|
|
285
|
-
const list = canvas.getByRole('list');
|
|
286
|
-
expect(list).toBeInTheDocument();
|
|
287
|
-
|
|
288
|
-
// Check badge accessibility
|
|
289
|
-
const badge = canvas.getByText('5');
|
|
290
|
-
expect(badge).toBeInTheDocument();
|
|
291
|
-
// Badge should be in a MUI Badge component (class contains MuiBadge)
|
|
292
|
-
expect(badge.closest('[class*="MuiBadge"]')).toBeInTheDocument();
|
|
293
|
-
},
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
// 5. Focus Management Tests
|
|
297
|
-
export const FocusManagement: Story = {
|
|
298
|
-
args: {
|
|
299
|
-
variant: 'vertical',
|
|
300
|
-
items: testItems,
|
|
301
|
-
},
|
|
302
|
-
decorators: [
|
|
303
|
-
(Story) => (
|
|
304
|
-
<Box sx={{ height: '600px', display: 'flex' }}>
|
|
305
|
-
<Story />
|
|
306
|
-
<Box sx={{ flex: 1, p: 3 }}>
|
|
307
|
-
<Typography>Content Area</Typography>
|
|
308
|
-
</Box>
|
|
309
|
-
</Box>
|
|
310
|
-
),
|
|
311
|
-
],
|
|
312
|
-
play: async ({ canvasElement }) => {
|
|
313
|
-
const canvas = within(canvasElement);
|
|
314
|
-
|
|
315
|
-
// Test focus ring visibility
|
|
316
|
-
const dashboardItem = canvas.getByText('Dashboard').closest('a, div[role="button"]') as HTMLElement;
|
|
317
|
-
// Dashboard is the first focusable item in the menu.
|
|
318
|
-
await userEvent.tab();
|
|
319
|
-
|
|
320
|
-
// Check focus is visible
|
|
321
|
-
await waitFor(() => expect(dashboardItem).toHaveFocus());
|
|
322
|
-
|
|
323
|
-
// Test focus trap in expanded menu
|
|
324
|
-
const analyticsItem = canvas.getByText('Analytics');
|
|
325
|
-
await userEvent.click(analyticsItem);
|
|
326
|
-
|
|
327
|
-
await waitFor(() => {
|
|
328
|
-
expect(canvas.getByText('Overview')).toBeInTheDocument();
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
// The submenu item is reachable and rendered. Whether focus moves into the
|
|
332
|
-
// submenu automatically is deliberately NOT asserted here: the previous
|
|
333
|
-
// version called .focus() itself and then checked the element was focused,
|
|
334
|
-
// which proved only that the DOM honours focus(), never that the component
|
|
335
|
-
// moved it.
|
|
336
|
-
const overviewItem = canvas.getByText('Overview').closest('a, div[role="button"]') as HTMLElement;
|
|
337
|
-
expect(overviewItem).toBeVisible();
|
|
338
|
-
},
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
// 6. Responsive Design Tests
|
|
342
|
-
export const ResponsiveDesign: Story = {
|
|
343
|
-
args: {
|
|
344
|
-
variant: 'horizontal',
|
|
345
|
-
items: testItems.slice(0, 5),
|
|
346
|
-
},
|
|
347
|
-
parameters: {
|
|
348
|
-
viewport: {
|
|
349
|
-
defaultViewport: 'mobile1',
|
|
350
|
-
},
|
|
351
|
-
},
|
|
352
|
-
decorators: [
|
|
353
|
-
(Story) => (
|
|
354
|
-
<Box>
|
|
355
|
-
<Story />
|
|
356
|
-
<Box sx={{ p: 3 }}>
|
|
357
|
-
<Typography>Mobile View Content</Typography>
|
|
358
|
-
</Box>
|
|
359
|
-
</Box>
|
|
360
|
-
),
|
|
361
|
-
],
|
|
362
|
-
play: async ({ canvasElement }) => {
|
|
363
|
-
const canvas = within(canvasElement);
|
|
364
|
-
|
|
365
|
-
// Check horizontal layout renders properly
|
|
366
|
-
const nav = canvas.getByRole('list');
|
|
367
|
-
expect(nav).toBeInTheDocument();
|
|
368
|
-
|
|
369
|
-
// Check items are visible
|
|
370
|
-
expect(canvas.getByText('Dashboard')).toBeInTheDocument();
|
|
371
|
-
expect(canvas.getByText('Orders')).toBeInTheDocument();
|
|
372
|
-
},
|
|
373
|
-
};
|
|
374
|
-
|
|
375
|
-
// 7. Theme Variations Tests
|
|
376
|
-
export const ThemeVariations: Story = {
|
|
377
|
-
args: {
|
|
378
|
-
variant: 'vertical',
|
|
379
|
-
items: testItems,
|
|
380
|
-
color: 'primary',
|
|
381
|
-
size: 'lg',
|
|
382
|
-
},
|
|
383
|
-
decorators: [
|
|
384
|
-
(Story) => (
|
|
385
|
-
<Box sx={{ height: '600px', display: 'flex' }}>
|
|
386
|
-
<Story />
|
|
387
|
-
<Box sx={{ flex: 1, p: 3 }}>
|
|
388
|
-
<Typography>Large Size Theme</Typography>
|
|
389
|
-
</Box>
|
|
390
|
-
</Box>
|
|
391
|
-
),
|
|
392
|
-
],
|
|
393
|
-
play: async ({ canvasElement }) => {
|
|
394
|
-
const canvas = within(canvasElement);
|
|
395
|
-
|
|
396
|
-
// Check size variations are applied
|
|
397
|
-
const dashboardText = canvas.getByText('Dashboard');
|
|
398
|
-
const dashboardButton = dashboardText.closest('a, div[role="button"]');
|
|
399
|
-
|
|
400
|
-
// Large size should have more padding
|
|
401
|
-
const styles = window.getComputedStyle(dashboardButton!);
|
|
402
|
-
expect(parseInt(styles.padding)).toBeGreaterThan(8);
|
|
403
|
-
|
|
404
|
-
// Check active state styling
|
|
405
|
-
const computedStyles = window.getComputedStyle(dashboardButton!);
|
|
406
|
-
expect(computedStyles.color).toBeTruthy();
|
|
407
|
-
|
|
408
|
-
// Verify active state background is applied
|
|
409
|
-
expect(computedStyles.backgroundColor).toBeTruthy();
|
|
410
|
-
},
|
|
411
|
-
};
|
|
412
|
-
|
|
413
|
-
// 8. Visual States Tests
|
|
414
|
-
export const VisualStates: Story = {
|
|
415
|
-
args: {
|
|
416
|
-
variant: 'vertical',
|
|
417
|
-
items: [
|
|
418
|
-
{ id: '1', label: 'Active Item', icon: <Dashboard />, active: true, href: '#' },
|
|
419
|
-
{ id: '2', label: 'Hover Item', icon: <ShoppingCart />, href: '#' },
|
|
420
|
-
{ id: '3', label: 'Disabled Item', icon: <People />, disabled: true, href: '#' },
|
|
421
|
-
{ id: '4', label: 'With Badge', icon: <Notifications />, badge: '99+', href: '#' },
|
|
422
|
-
],
|
|
423
|
-
},
|
|
424
|
-
decorators: [
|
|
425
|
-
(Story) => (
|
|
426
|
-
<Box sx={{ height: '400px', display: 'flex' }}>
|
|
427
|
-
<Story />
|
|
428
|
-
<Box sx={{ flex: 1, p: 3 }}>
|
|
429
|
-
<Typography>Visual States Demo</Typography>
|
|
430
|
-
</Box>
|
|
431
|
-
</Box>
|
|
432
|
-
),
|
|
433
|
-
],
|
|
434
|
-
play: async ({ canvasElement }) => {
|
|
435
|
-
const canvas = within(canvasElement);
|
|
436
|
-
|
|
437
|
-
// Test hover state
|
|
438
|
-
const hoverItem = canvas.getByText('Hover Item');
|
|
439
|
-
await userEvent.hover(hoverItem);
|
|
440
|
-
|
|
441
|
-
// Test active state is visible
|
|
442
|
-
const activeItem = canvas.getByText('Active Item').closest('a, div[role="button"]');
|
|
443
|
-
expect(activeItem).toHaveStyle({
|
|
444
|
-
backgroundColor: expect.stringContaining('rgba'),
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
// Test disabled state
|
|
448
|
-
const disabledItem = canvas.getByText('Disabled Item').closest('a, div[role="button"]');
|
|
449
|
-
expect(disabledItem).toHaveAttribute('aria-disabled', 'true');
|
|
450
|
-
|
|
451
|
-
// Test badge display
|
|
452
|
-
const badge = canvas.getByText('99+');
|
|
453
|
-
expect(badge).toBeInTheDocument();
|
|
454
|
-
},
|
|
455
|
-
};
|
|
456
|
-
|
|
457
|
-
// 9. Performance Tests
|
|
458
|
-
export const PerformanceTest: Story = {
|
|
459
|
-
args: {
|
|
460
|
-
variant: 'vertical',
|
|
461
|
-
items: Array.from({ length: 50 }, (_, i) => ({
|
|
462
|
-
id: `item-${i}`,
|
|
463
|
-
label: `Menu Item ${i + 1}`,
|
|
464
|
-
icon: i % 3 === 0 ? <Dashboard /> : i % 3 === 1 ? <ShoppingCart /> : <People />,
|
|
465
|
-
href: `#item-${i}`,
|
|
466
|
-
badge: i % 5 === 0 ? i : undefined,
|
|
467
|
-
})),
|
|
468
|
-
},
|
|
469
|
-
decorators: [
|
|
470
|
-
(Story) => (
|
|
471
|
-
<Box sx={{ height: '600px', display: 'flex' }}>
|
|
472
|
-
<Story />
|
|
473
|
-
<Box sx={{ flex: 1, p: 3 }}>
|
|
474
|
-
<Typography>Performance Test - 50 Items</Typography>
|
|
475
|
-
</Box>
|
|
476
|
-
</Box>
|
|
477
|
-
),
|
|
478
|
-
],
|
|
479
|
-
play: async ({ canvasElement }) => {
|
|
480
|
-
const canvas = within(canvasElement);
|
|
481
|
-
|
|
482
|
-
// Check all items render
|
|
483
|
-
await waitFor(
|
|
484
|
-
() => {
|
|
485
|
-
expect(canvas.getByText('Menu Item 1')).toBeInTheDocument();
|
|
486
|
-
expect(canvas.getByText('Menu Item 50')).toBeInTheDocument();
|
|
487
|
-
},
|
|
488
|
-
{ timeout: 5000 },
|
|
489
|
-
);
|
|
490
|
-
|
|
491
|
-
// Verify performance by checking if items are properly rendered
|
|
492
|
-
const menuItems = canvas.getAllByText(/Menu Item \d+/);
|
|
493
|
-
expect(menuItems.length).toBeGreaterThan(20);
|
|
494
|
-
|
|
495
|
-
// Test scrolling functionality exists
|
|
496
|
-
const menuContainer =
|
|
497
|
-
canvas.getByRole('list').closest('[role="list"]') || canvas.getByRole('list');
|
|
498
|
-
expect(menuContainer).toBeTruthy();
|
|
499
|
-
|
|
500
|
-
// Scrollability is not asserted: scrollHeight >= clientHeight holds for every
|
|
501
|
-
// element that has ever been laid out, so the check could not fail, and both
|
|
502
|
-
// figures move with the runner's window size.
|
|
503
|
-
},
|
|
504
|
-
};
|
|
505
|
-
|
|
506
|
-
// 10. Edge Cases Tests
|
|
507
|
-
export const EdgeCases: Story = {
|
|
508
|
-
args: {
|
|
509
|
-
variant: 'vertical',
|
|
510
|
-
items: [
|
|
511
|
-
{
|
|
512
|
-
id: '1',
|
|
513
|
-
label: 'Very Long Menu Item Label That Should Wrap or Truncate Properly',
|
|
514
|
-
icon: <Dashboard />,
|
|
515
|
-
href: '#',
|
|
516
|
-
},
|
|
517
|
-
{
|
|
518
|
-
id: '2',
|
|
519
|
-
label: 'Item with Very Long Description',
|
|
520
|
-
description:
|
|
521
|
-
'This is a very long description that should be handled properly without breaking the layout',
|
|
522
|
-
icon: <ShoppingCart />,
|
|
523
|
-
href: '#',
|
|
524
|
-
},
|
|
525
|
-
{
|
|
526
|
-
id: '3',
|
|
527
|
-
label: 'Deep Nesting',
|
|
528
|
-
icon: <Inventory />,
|
|
529
|
-
children: nestedItems,
|
|
530
|
-
},
|
|
531
|
-
{
|
|
532
|
-
id: '4',
|
|
533
|
-
label: '', // Empty label
|
|
534
|
-
icon: <Settings />,
|
|
535
|
-
href: '#',
|
|
536
|
-
},
|
|
537
|
-
{
|
|
538
|
-
id: '5',
|
|
539
|
-
label: 'No Icon Item',
|
|
540
|
-
href: '#',
|
|
541
|
-
},
|
|
542
|
-
],
|
|
543
|
-
},
|
|
544
|
-
decorators: [
|
|
545
|
-
(Story) => (
|
|
546
|
-
<Box sx={{ height: '600px', display: 'flex' }}>
|
|
547
|
-
<Story />
|
|
548
|
-
<Box sx={{ flex: 1, p: 3 }}>
|
|
549
|
-
<Typography>Edge Cases Test</Typography>
|
|
550
|
-
</Box>
|
|
551
|
-
</Box>
|
|
552
|
-
),
|
|
553
|
-
],
|
|
554
|
-
play: async ({ canvasElement }) => {
|
|
555
|
-
const canvas = within(canvasElement);
|
|
556
|
-
|
|
557
|
-
// Test long text handling
|
|
558
|
-
const longLabel = canvas.getByText(/Very Long Menu Item Label/);
|
|
559
|
-
expect(longLabel).toBeInTheDocument();
|
|
560
|
-
|
|
561
|
-
// Test description rendering
|
|
562
|
-
const description = canvas.getByText(/This is a very long description/);
|
|
563
|
-
expect(description).toBeInTheDocument();
|
|
564
|
-
|
|
565
|
-
// Test deep nesting
|
|
566
|
-
const deepNestingItem = canvas.getByText('Deep Nesting');
|
|
567
|
-
await userEvent.click(deepNestingItem);
|
|
568
|
-
|
|
569
|
-
await waitFor(() => {
|
|
570
|
-
expect(canvas.getByText('Products')).toBeInTheDocument();
|
|
571
|
-
});
|
|
572
|
-
|
|
573
|
-
const productsItem = canvas.getByText('Products');
|
|
574
|
-
await userEvent.click(productsItem);
|
|
575
|
-
|
|
576
|
-
await waitFor(() => {
|
|
577
|
-
expect(canvas.getByText('Categories')).toBeInTheDocument();
|
|
578
|
-
});
|
|
579
|
-
|
|
580
|
-
// Test item without icon
|
|
581
|
-
const noIconItem = canvas.getByText('No Icon Item');
|
|
582
|
-
expect(noIconItem).toBeInTheDocument();
|
|
583
|
-
},
|
|
584
|
-
};
|
|
585
|
-
|
|
586
|
-
// 11. Theme Integration Tests
|
|
587
|
-
export const ThemeIntegration: Story = {
|
|
588
|
-
render: function ThemeIntegrationRender() {
|
|
589
|
-
const [darkMode, setDarkMode] = useState(false);
|
|
590
|
-
|
|
591
|
-
const theme = createTheme({
|
|
592
|
-
palette: {
|
|
593
|
-
mode: darkMode ? 'dark' : 'light',
|
|
594
|
-
primary: {
|
|
595
|
-
main: darkMode ? '#90caf9' : '#1976d2',
|
|
596
|
-
},
|
|
597
|
-
},
|
|
598
|
-
});
|
|
599
|
-
|
|
600
|
-
return (
|
|
601
|
-
<div data-testid="theme-container">
|
|
602
|
-
<button
|
|
603
|
-
data-testid="toggle-theme"
|
|
604
|
-
onClick={() => setDarkMode(!darkMode)}
|
|
605
|
-
style={{ marginBottom: 16 }}
|
|
606
|
-
>
|
|
607
|
-
Toggle {darkMode ? 'Light' : 'Dark'} Mode
|
|
608
|
-
</button>
|
|
609
|
-
<ThemeProvider theme={theme}>
|
|
610
|
-
<Box
|
|
611
|
-
sx={{
|
|
612
|
-
display: 'flex',
|
|
613
|
-
height: '400px',
|
|
614
|
-
bgcolor: 'background.default',
|
|
615
|
-
color: 'text.primary',
|
|
616
|
-
}}
|
|
617
|
-
>
|
|
618
|
-
<NavigationMenu
|
|
619
|
-
variant="vertical"
|
|
620
|
-
items={testItems.map((item) => ({ ...item, active: item.id === '1' }))}
|
|
621
|
-
data-testid="themed-menu"
|
|
622
|
-
/>
|
|
623
|
-
<Box sx={{ flex: 1, p: 2, bgcolor: 'background.paper' }}>
|
|
624
|
-
<div data-testid="theme-mode">Mode: {darkMode ? 'dark' : 'light'}</div>
|
|
625
|
-
</Box>
|
|
626
|
-
</Box>
|
|
627
|
-
</ThemeProvider>
|
|
628
|
-
</div>
|
|
629
|
-
);
|
|
630
|
-
},
|
|
631
|
-
play: async ({ canvasElement }) => {
|
|
632
|
-
const canvas = within(canvasElement);
|
|
633
|
-
|
|
634
|
-
// Test light mode
|
|
635
|
-
await expect(canvas.getByTestId('theme-mode')).toHaveTextContent('Mode: light');
|
|
636
|
-
|
|
637
|
-
// Toggle to dark mode
|
|
638
|
-
await userEvent.click(canvas.getByTestId('toggle-theme'));
|
|
639
|
-
await expect(canvas.getByTestId('theme-mode')).toHaveTextContent('Mode: dark');
|
|
640
|
-
|
|
641
|
-
// Verify menu still functions in dark mode
|
|
642
|
-
const menuItem = canvas.getByText('Orders');
|
|
643
|
-
await userEvent.click(menuItem);
|
|
644
|
-
expect(menuItem).toBeInTheDocument();
|
|
645
|
-
},
|
|
646
|
-
};
|
|
647
|
-
|
|
648
|
-
// 12. Controlled vs Uncontrolled Tests
|
|
649
|
-
export const ControlledUncontrolled: Story = {
|
|
650
|
-
render: function ControlledUncontrolledRender() {
|
|
651
|
-
const [controlledCollapsed, setControlledCollapsed] = useState<boolean | undefined>(undefined);
|
|
652
|
-
const [isControlled, setIsControlled] = useState(false);
|
|
653
|
-
|
|
654
|
-
return (
|
|
655
|
-
<div data-testid="controlled-container">
|
|
656
|
-
<Box sx={{ mb: 2 }}>
|
|
657
|
-
<button data-testid="toggle-controlled" onClick={() => setIsControlled(!isControlled)}>
|
|
658
|
-
{isControlled ? 'Switch to Uncontrolled' : 'Switch to Controlled'}
|
|
659
|
-
</button>
|
|
660
|
-
{isControlled && (
|
|
661
|
-
<button
|
|
662
|
-
data-testid="control-collapse"
|
|
663
|
-
onClick={() => setControlledCollapsed(!controlledCollapsed)}
|
|
664
|
-
style={{ marginLeft: 8 }}
|
|
665
|
-
>
|
|
666
|
-
Toggle Collapse (Controlled)
|
|
667
|
-
</button>
|
|
668
|
-
)}
|
|
669
|
-
</Box>
|
|
670
|
-
<Box sx={{ display: 'flex', height: '400px' }}>
|
|
671
|
-
<NavigationMenu
|
|
672
|
-
variant="vertical"
|
|
673
|
-
items={testItems}
|
|
674
|
-
collapsible
|
|
675
|
-
collapsed={isControlled ? controlledCollapsed : undefined}
|
|
676
|
-
onCollapseChange={(collapsed) => {
|
|
677
|
-
if (isControlled) {
|
|
678
|
-
setControlledCollapsed(collapsed);
|
|
679
|
-
}
|
|
680
|
-
}}
|
|
681
|
-
data-testid="controlled-menu"
|
|
682
|
-
/>
|
|
683
|
-
<Box sx={{ flex: 1, p: 2 }}>
|
|
684
|
-
<div data-testid="control-state">
|
|
685
|
-
Mode: {isControlled ? 'Controlled' : 'Uncontrolled'}
|
|
686
|
-
</div>
|
|
687
|
-
<div data-testid="collapse-state">
|
|
688
|
-
Collapsed: {controlledCollapsed?.toString() ?? 'undefined'}
|
|
689
|
-
</div>
|
|
690
|
-
</Box>
|
|
691
|
-
</Box>
|
|
692
|
-
</div>
|
|
693
|
-
);
|
|
694
|
-
},
|
|
695
|
-
play: async ({ canvasElement }) => {
|
|
696
|
-
const canvas = within(canvasElement);
|
|
697
|
-
|
|
698
|
-
// Test uncontrolled mode
|
|
699
|
-
await expect(canvas.getByTestId('control-state')).toHaveTextContent('Mode: Uncontrolled');
|
|
700
|
-
|
|
701
|
-
// Switch to controlled mode
|
|
702
|
-
await userEvent.click(canvas.getByTestId('toggle-controlled'));
|
|
703
|
-
await expect(canvas.getByTestId('control-state')).toHaveTextContent('Mode: Controlled');
|
|
704
|
-
|
|
705
|
-
// Test controlled collapse
|
|
706
|
-
await userEvent.click(canvas.getByTestId('control-collapse'));
|
|
707
|
-
await expect(canvas.getByTestId('collapse-state')).toHaveTextContent('Collapsed: true');
|
|
708
|
-
},
|
|
709
|
-
};
|
|
710
|
-
|
|
711
|
-
// 13. Large Dataset Performance Test
|
|
712
|
-
export const LargeDatasetPerformance: Story = {
|
|
713
|
-
args: {
|
|
714
|
-
variant: 'vertical',
|
|
715
|
-
items: Array.from({ length: 100 }, (_, i) => ({
|
|
716
|
-
id: `item-${i}`,
|
|
717
|
-
label: `Menu Item ${i + 1}`,
|
|
718
|
-
icon: <Dashboard />,
|
|
719
|
-
href: `#item-${i}`,
|
|
720
|
-
...(i % 10 === 0 && {
|
|
721
|
-
children: Array.from({ length: 5 }, (_, j) => ({
|
|
722
|
-
id: `item-${i}-${j}`,
|
|
723
|
-
label: `Subitem ${j + 1}`,
|
|
724
|
-
href: `#item-${i}-${j}`,
|
|
725
|
-
})),
|
|
726
|
-
}),
|
|
727
|
-
})),
|
|
728
|
-
},
|
|
729
|
-
decorators: [
|
|
730
|
-
(Story) => (
|
|
731
|
-
<Box sx={{ height: '600px', display: 'flex' }}>
|
|
732
|
-
<Story />
|
|
733
|
-
<Box sx={{ flex: 1, p: 3 }}>
|
|
734
|
-
<Typography>Large Dataset - 100 Items with Nested Structure</Typography>
|
|
735
|
-
</Box>
|
|
736
|
-
</Box>
|
|
737
|
-
),
|
|
738
|
-
],
|
|
739
|
-
play: async ({ canvasElement }) => {
|
|
740
|
-
const startTime = window.performance.now();
|
|
741
|
-
const canvas = within(canvasElement);
|
|
742
|
-
|
|
743
|
-
// Verify large dataset renders
|
|
744
|
-
await waitFor(() => {
|
|
745
|
-
expect(canvas.getByText('Menu Item 1')).toBeInTheDocument();
|
|
746
|
-
expect(canvas.getByText('Menu Item 100')).toBeInTheDocument();
|
|
747
|
-
});
|
|
748
|
-
|
|
749
|
-
const renderTime = window.performance.now() - startTime;
|
|
750
|
-
expect(renderTime).toBeLessThan(5000); // Should render within 5 seconds
|
|
751
|
-
|
|
752
|
-
// Test expanding nested items in large dataset
|
|
753
|
-
const expandableItem = canvas.getByText('Menu Item 1');
|
|
754
|
-
await userEvent.click(expandableItem);
|
|
755
|
-
|
|
756
|
-
await waitFor(() => {
|
|
757
|
-
expect(canvas.getByText('Subitem 1')).toBeInTheDocument();
|
|
758
|
-
});
|
|
759
|
-
},
|
|
760
|
-
};
|
|
761
|
-
|
|
762
|
-
// 14. Integration Tests
|
|
763
|
-
export const IntegrationTest: Story = {
|
|
764
|
-
args: {
|
|
765
|
-
variant: 'mega',
|
|
766
|
-
items: [
|
|
767
|
-
{
|
|
768
|
-
id: 'section1',
|
|
769
|
-
label: 'Products',
|
|
770
|
-
children: [
|
|
771
|
-
{ id: 's1-1', label: 'All Products', icon: <Inventory />, href: '#' },
|
|
772
|
-
{ id: 's1-2', label: 'Categories', icon: <Dashboard />, href: '#' },
|
|
773
|
-
],
|
|
774
|
-
},
|
|
775
|
-
{
|
|
776
|
-
id: 'section2',
|
|
777
|
-
label: 'Analytics',
|
|
778
|
-
children: [
|
|
779
|
-
{ id: 's2-1', label: 'Reports', icon: <Analytics />, href: '#' },
|
|
780
|
-
{ id: 's2-2', label: 'Insights', icon: <Settings />, href: '#' },
|
|
781
|
-
],
|
|
782
|
-
},
|
|
783
|
-
],
|
|
784
|
-
logo: (
|
|
785
|
-
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
|
786
|
-
<Home />
|
|
787
|
-
<Typography variant="h6">MegaMenu</Typography>
|
|
788
|
-
</Box>
|
|
789
|
-
),
|
|
790
|
-
},
|
|
791
|
-
decorators: [
|
|
792
|
-
(Story) => (
|
|
793
|
-
<Box>
|
|
794
|
-
<Story />
|
|
795
|
-
<Box sx={{ p: 3, bgcolor: 'grey.50', minHeight: '400px' }}>
|
|
796
|
-
<Typography variant="h4">Integration Test - Mega Menu</Typography>
|
|
797
|
-
</Box>
|
|
798
|
-
</Box>
|
|
799
|
-
),
|
|
800
|
-
],
|
|
801
|
-
play: async ({ canvasElement }) => {
|
|
802
|
-
const canvas = within(canvasElement);
|
|
803
|
-
|
|
804
|
-
// Test mega menu sections render
|
|
805
|
-
expect(canvas.getByText('Products')).toBeInTheDocument();
|
|
806
|
-
expect(canvas.getByText('Analytics')).toBeInTheDocument();
|
|
807
|
-
|
|
808
|
-
// Test logo renders
|
|
809
|
-
expect(canvas.getByText('MegaMenu')).toBeInTheDocument();
|
|
810
|
-
|
|
811
|
-
// Test section items
|
|
812
|
-
expect(canvas.getByText('All Products')).toBeInTheDocument();
|
|
813
|
-
expect(canvas.getByText('Reports')).toBeInTheDocument();
|
|
814
|
-
|
|
815
|
-
// Test clicking items in mega menu
|
|
816
|
-
const allProductsItem = canvas.getByText('All Products');
|
|
817
|
-
await userEvent.click(allProductsItem);
|
|
818
|
-
|
|
819
|
-
// Check grid layout - should have section headings
|
|
820
|
-
const sections = canvas.getAllByRole('heading', { level: 6 });
|
|
821
|
-
expect(sections.length).toBeGreaterThanOrEqual(2);
|
|
822
|
-
},
|
|
823
|
-
};
|
|
824
|
-
|
|
825
|
-
// 15. Accessibility Compliance (WCAG 2.1 AA) Tests
|
|
826
|
-
export const AccessibilityCompliance: Story = {
|
|
827
|
-
render: () => (
|
|
828
|
-
<div data-testid="a11y-container">
|
|
829
|
-
<Box sx={{ display: 'flex', height: '400px' }}>
|
|
830
|
-
<nav aria-label="Main navigation" data-testid="a11y-nav">
|
|
831
|
-
<NavigationMenu variant="vertical" items={testItems} data-testid="a11y-menu" />
|
|
832
|
-
</nav>
|
|
833
|
-
<Box sx={{ flex: 1, p: 2 }} role="main">
|
|
834
|
-
<Typography variant="h6" id="content-heading">
|
|
835
|
-
Content Area
|
|
836
|
-
</Typography>
|
|
837
|
-
<p>Testing WCAG compliance</p>
|
|
838
|
-
</Box>
|
|
839
|
-
</Box>
|
|
840
|
-
</div>
|
|
841
|
-
),
|
|
842
|
-
play: async ({ canvasElement }) => {
|
|
843
|
-
const canvas = within(canvasElement);
|
|
844
|
-
|
|
845
|
-
// Test navigation landmark
|
|
846
|
-
const nav = canvas.getByTestId('a11y-nav');
|
|
847
|
-
expect(nav).toHaveAttribute('aria-label', 'Main navigation');
|
|
848
|
-
|
|
849
|
-
// Test menu items have proper roles
|
|
850
|
-
const menuButtons = canvas.queryAllByRole('button');
|
|
851
|
-
const menuLinks = canvas.queryAllByRole('link');
|
|
852
|
-
const totalInteractiveElements = menuButtons.length + menuLinks.length;
|
|
853
|
-
expect(totalInteractiveElements).toBeGreaterThan(0);
|
|
854
|
-
|
|
855
|
-
// Test disabled item has proper attributes
|
|
856
|
-
const disabledButton = canvas.getByText('Customers').closest('[role="button"], button, a');
|
|
857
|
-
expect(disabledButton).toBeTruthy();
|
|
858
|
-
if (disabledButton) {
|
|
859
|
-
expect(disabledButton).toHaveAttribute('aria-disabled', 'true');
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
// Test expandable items have proper ARIA attributes
|
|
863
|
-
const expandableItem = canvas.getByText('Analytics');
|
|
864
|
-
await userEvent.click(expandableItem);
|
|
865
|
-
|
|
866
|
-
await waitFor(() => {
|
|
867
|
-
const subItems = canvas.getAllByText(/Overview|Reports|Insights/);
|
|
868
|
-
expect(subItems.length).toBeGreaterThan(0);
|
|
869
|
-
});
|
|
870
|
-
},
|
|
871
|
-
};
|
|
872
|
-
|
|
873
|
-
// 16. Advanced Keyboard Navigation Tests
|
|
874
|
-
export const AdvancedKeyboardNavigation: Story = {
|
|
875
|
-
render: function AdvancedKeyboardNavigationRender() {
|
|
876
|
-
const [selectedItem, setSelectedItem] = useState<string>('');
|
|
877
|
-
|
|
878
|
-
return (
|
|
879
|
-
<div data-testid="keyboard-nav-container">
|
|
880
|
-
<Box sx={{ display: 'flex', height: '400px' }}>
|
|
881
|
-
<NavigationMenu
|
|
882
|
-
variant="vertical"
|
|
883
|
-
items={testItems.map((item) => ({
|
|
884
|
-
...item,
|
|
885
|
-
onClick: (e) => {
|
|
886
|
-
e.preventDefault();
|
|
887
|
-
setSelectedItem(item.id);
|
|
888
|
-
},
|
|
889
|
-
}))}
|
|
890
|
-
data-testid="keyboard-menu"
|
|
891
|
-
/>
|
|
892
|
-
<Box sx={{ flex: 1, p: 2 }}>
|
|
893
|
-
<div data-testid="focused-item">Focused: (tracked via DOM)</div>
|
|
894
|
-
<div data-testid="selected-via-keyboard">Selected: {selectedItem}</div>
|
|
895
|
-
<p>Use Tab, Arrow keys, Enter, Escape, and Space to navigate</p>
|
|
896
|
-
</Box>
|
|
897
|
-
</Box>
|
|
898
|
-
</div>
|
|
899
|
-
);
|
|
900
|
-
},
|
|
901
|
-
play: async ({ canvasElement }) => {
|
|
902
|
-
const canvas = within(canvasElement);
|
|
903
|
-
|
|
904
|
-
// Focus first menu item
|
|
905
|
-
// No pre-focus needed: the step below clicks this button directly.
|
|
906
|
-
const firstMenuItem = canvas.getByText('Dashboard').closest('button');
|
|
907
|
-
expect(firstMenuItem).toBeInTheDocument();
|
|
908
|
-
|
|
909
|
-
// Test Enter key activation - click the focused button directly
|
|
910
|
-
if (firstMenuItem) {
|
|
911
|
-
await userEvent.click(firstMenuItem);
|
|
912
|
-
await waitFor(() => {
|
|
913
|
-
expect(canvas.getByTestId('selected-via-keyboard')).toHaveTextContent('Selected: 1');
|
|
914
|
-
});
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
// Test Tab navigation
|
|
918
|
-
await userEvent.keyboard('{Tab}');
|
|
919
|
-
|
|
920
|
-
// Test Arrow key navigation
|
|
921
|
-
await userEvent.keyboard('{ArrowDown}');
|
|
922
|
-
|
|
923
|
-
// Test expanding submenu with Enter
|
|
924
|
-
const analyticsItem = canvas.getByText('Analytics').closest('button');
|
|
925
|
-
if (analyticsItem) {
|
|
926
|
-
await userEvent.click(analyticsItem);
|
|
927
|
-
await waitFor(() => {
|
|
928
|
-
expect(canvas.getByText('Overview')).toBeInTheDocument();
|
|
929
|
-
});
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
// Test Escape to collapse submenu
|
|
933
|
-
await userEvent.keyboard('{Escape}');
|
|
934
|
-
},
|
|
935
|
-
};
|