@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,1344 +0,0 @@
|
|
|
1
|
-
import ExpandMore from '@mui/icons-material/ExpandMore';
|
|
2
|
-
import Settings from '@mui/icons-material/Settings';
|
|
3
|
-
import { Box, Button, Stack,Typography } from '@mui/material';
|
|
4
|
-
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
5
|
-
import { useState } from 'react';
|
|
6
|
-
import { expect, fn,userEvent, waitFor, within } from 'storybook/test';
|
|
7
|
-
|
|
8
|
-
import { Accordion, AccordionActions,AccordionDetails, AccordionSummary } from './Accordion';
|
|
9
|
-
|
|
10
|
-
const meta: Meta<typeof Accordion> = {
|
|
11
|
-
title: 'Layout/Accordion/Tests',
|
|
12
|
-
component: Accordion,
|
|
13
|
-
parameters: {
|
|
14
|
-
layout: 'padded',
|
|
15
|
-
chromatic: { disableSnapshot: false },
|
|
16
|
-
},
|
|
17
|
-
tags: ['autodocs', 'test', 'component:Accordion'],
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export default meta;
|
|
21
|
-
type Story = StoryObj<typeof meta>;
|
|
22
|
-
|
|
23
|
-
// Basic Interaction Test
|
|
24
|
-
export const BasicInteraction: Story = {
|
|
25
|
-
name: '🧪 Basic Interaction Test',
|
|
26
|
-
args: {
|
|
27
|
-
variant: 'default',
|
|
28
|
-
onChange: fn(),
|
|
29
|
-
'data-testid': 'test-accordion',
|
|
30
|
-
},
|
|
31
|
-
render: (args) => (
|
|
32
|
-
<Accordion {...args}>
|
|
33
|
-
<AccordionSummary expandIcon={<ExpandMore />} data-testid="test-accordion-summary">
|
|
34
|
-
<Typography data-testid="accordion-title">Test Accordion</Typography>
|
|
35
|
-
</AccordionSummary>
|
|
36
|
-
<AccordionDetails data-testid="test-accordion-details">
|
|
37
|
-
<Typography data-testid="accordion-content">
|
|
38
|
-
This is test content that should be expandable and collapsible.
|
|
39
|
-
</Typography>
|
|
40
|
-
</AccordionDetails>
|
|
41
|
-
</Accordion>
|
|
42
|
-
),
|
|
43
|
-
play: async ({ canvasElement, step, args }) => {
|
|
44
|
-
const canvas = within(canvasElement);
|
|
45
|
-
|
|
46
|
-
await step('Initial render verification', async () => {
|
|
47
|
-
const accordion = canvas.getByTestId('test-accordion');
|
|
48
|
-
const summary = canvas.getByTestId('test-accordion-summary');
|
|
49
|
-
const title = canvas.getByTestId('accordion-title');
|
|
50
|
-
|
|
51
|
-
await expect(accordion).toBeInTheDocument();
|
|
52
|
-
await expect(summary).toBeInTheDocument();
|
|
53
|
-
await expect(title).toHaveTextContent('Test Accordion');
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
await step('Click interaction to expand', async () => {
|
|
57
|
-
const summary = canvas.getByTestId('test-accordion-summary');
|
|
58
|
-
await userEvent.click(summary);
|
|
59
|
-
|
|
60
|
-
// Verify onChange was called
|
|
61
|
-
await waitFor(() => {
|
|
62
|
-
expect(args.onChange).toHaveBeenCalledTimes(1);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// Check if content is visible
|
|
66
|
-
const content = canvas.getByTestId('accordion-content');
|
|
67
|
-
await expect(content).toBeInTheDocument();
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
await step('Click interaction to collapse', async () => {
|
|
71
|
-
const summary = canvas.getByTestId('test-accordion-summary');
|
|
72
|
-
await userEvent.click(summary);
|
|
73
|
-
|
|
74
|
-
// Verify onChange was called again
|
|
75
|
-
await waitFor(() => {
|
|
76
|
-
expect(args.onChange).toHaveBeenCalledTimes(2);
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
await step('Hover interaction on summary', async () => {
|
|
81
|
-
const summary = canvas.getByTestId('test-accordion-summary');
|
|
82
|
-
await userEvent.hover(summary);
|
|
83
|
-
|
|
84
|
-
// Should have hover styling applied
|
|
85
|
-
await expect(summary).toBeInTheDocument();
|
|
86
|
-
});
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
// State Change Test
|
|
91
|
-
export const StateChangeTest: Story = {
|
|
92
|
-
name: '🔄 State Change Test',
|
|
93
|
-
render: () => {
|
|
94
|
-
const TestComponent = () => {
|
|
95
|
-
const [expanded, setExpanded] = useState(false);
|
|
96
|
-
|
|
97
|
-
return (
|
|
98
|
-
<Box>
|
|
99
|
-
<Button data-testid="external-toggle" onClick={() => setExpanded(!expanded)}>
|
|
100
|
-
{expanded ? 'Collapse' : 'Expand'} Accordion
|
|
101
|
-
</Button>
|
|
102
|
-
<Accordion
|
|
103
|
-
expanded={expanded}
|
|
104
|
-
onChange={(_, isExpanded) => setExpanded(isExpanded)}
|
|
105
|
-
data-testid="controlled-accordion"
|
|
106
|
-
>
|
|
107
|
-
<AccordionSummary expandIcon={<ExpandMore />} data-testid="controlled-summary">
|
|
108
|
-
<Typography>Controlled Accordion</Typography>
|
|
109
|
-
</AccordionSummary>
|
|
110
|
-
<AccordionDetails data-testid="controlled-details">
|
|
111
|
-
<Typography data-testid="controlled-content">
|
|
112
|
-
This accordion is controlled by external state.
|
|
113
|
-
</Typography>
|
|
114
|
-
</AccordionDetails>
|
|
115
|
-
</Accordion>
|
|
116
|
-
</Box>
|
|
117
|
-
);
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
return <TestComponent />;
|
|
121
|
-
},
|
|
122
|
-
play: async ({ canvasElement, step }) => {
|
|
123
|
-
const canvas = within(canvasElement);
|
|
124
|
-
|
|
125
|
-
await step('Verify initial collapsed state', async () => {
|
|
126
|
-
const accordion = canvas.getByTestId('controlled-accordion');
|
|
127
|
-
const toggleButton = canvas.getByTestId('external-toggle');
|
|
128
|
-
|
|
129
|
-
await expect(accordion).toBeInTheDocument();
|
|
130
|
-
await expect(toggleButton).toHaveTextContent('Expand Accordion');
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
await step('External toggle to expand', async () => {
|
|
134
|
-
const toggleButton = canvas.getByTestId('external-toggle');
|
|
135
|
-
await userEvent.click(toggleButton);
|
|
136
|
-
|
|
137
|
-
await waitFor(() => {
|
|
138
|
-
expect(toggleButton).toHaveTextContent('Collapse Accordion');
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
const content = canvas.getByTestId('controlled-content');
|
|
142
|
-
await expect(content).toBeInTheDocument();
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
await step('External toggle to collapse', async () => {
|
|
146
|
-
const toggleButton = canvas.getByTestId('external-toggle');
|
|
147
|
-
await userEvent.click(toggleButton);
|
|
148
|
-
|
|
149
|
-
await waitFor(() => {
|
|
150
|
-
expect(toggleButton).toHaveTextContent('Expand Accordion');
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
await step('Internal summary click', async () => {
|
|
155
|
-
const summary = canvas.getByTestId('controlled-summary');
|
|
156
|
-
await userEvent.click(summary);
|
|
157
|
-
|
|
158
|
-
await waitFor(() => {
|
|
159
|
-
const toggleButton = canvas.getByTestId('external-toggle');
|
|
160
|
-
expect(toggleButton).toHaveTextContent('Collapse Accordion');
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
},
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
// Keyboard Navigation Test
|
|
167
|
-
export const KeyboardNavigation: Story = {
|
|
168
|
-
name: '⌨️ Keyboard Navigation Test',
|
|
169
|
-
render: () => (
|
|
170
|
-
<Stack spacing={1}>
|
|
171
|
-
<Accordion variant="bordered" data-testid="first-accordion">
|
|
172
|
-
<AccordionSummary expandIcon={<ExpandMore />} data-testid="first-summary">
|
|
173
|
-
<Typography>First Accordion</Typography>
|
|
174
|
-
</AccordionSummary>
|
|
175
|
-
<AccordionDetails>
|
|
176
|
-
<Typography>
|
|
177
|
-
First accordion content.
|
|
178
|
-
<Button data-testid="first-button">First Button</Button>
|
|
179
|
-
</Typography>
|
|
180
|
-
</AccordionDetails>
|
|
181
|
-
</Accordion>
|
|
182
|
-
|
|
183
|
-
<Accordion variant="bordered" data-testid="second-accordion">
|
|
184
|
-
<AccordionSummary expandIcon={<ExpandMore />} data-testid="second-summary">
|
|
185
|
-
<Typography>Second Accordion</Typography>
|
|
186
|
-
</AccordionSummary>
|
|
187
|
-
<AccordionDetails>
|
|
188
|
-
<Typography>
|
|
189
|
-
Second accordion content.
|
|
190
|
-
<Button data-testid="second-button">Second Button</Button>
|
|
191
|
-
</Typography>
|
|
192
|
-
</AccordionDetails>
|
|
193
|
-
</Accordion>
|
|
194
|
-
</Stack>
|
|
195
|
-
),
|
|
196
|
-
parameters: {
|
|
197
|
-
a11y: {
|
|
198
|
-
element: '#storybook-root',
|
|
199
|
-
config: {
|
|
200
|
-
rules: [
|
|
201
|
-
{ id: 'color-contrast', enabled: true },
|
|
202
|
-
{ id: 'aria-required-attr', enabled: true },
|
|
203
|
-
{ id: 'aria-roles', enabled: true },
|
|
204
|
-
{ id: 'aria-valid-attr-value', enabled: true },
|
|
205
|
-
{ id: 'button-name', enabled: true },
|
|
206
|
-
{ id: 'duplicate-id', enabled: true },
|
|
207
|
-
{ id: 'label', enabled: true },
|
|
208
|
-
],
|
|
209
|
-
},
|
|
210
|
-
},
|
|
211
|
-
},
|
|
212
|
-
play: async ({ canvasElement, step }) => {
|
|
213
|
-
const canvas = within(canvasElement);
|
|
214
|
-
|
|
215
|
-
await step('Tab navigation between summaries', async () => {
|
|
216
|
-
const firstSummary = canvas.getByTestId('first-summary');
|
|
217
|
-
const secondSummary = canvas.getByTestId('second-summary');
|
|
218
|
-
|
|
219
|
-
// Tab in from the body rather than calling .focus() — the old first
|
|
220
|
-
// assertion only proved the DOM honours focus().
|
|
221
|
-
await userEvent.tab();
|
|
222
|
-
await waitFor(() => expect(firstSummary).toHaveFocus());
|
|
223
|
-
|
|
224
|
-
// Tab to second summary
|
|
225
|
-
await userEvent.tab();
|
|
226
|
-
await waitFor(() => expect(secondSummary).toHaveFocus());
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
await step('Enter key to expand accordion', async () => {
|
|
230
|
-
const firstSummary = canvas.getByTestId('first-summary');
|
|
231
|
-
|
|
232
|
-
// Focus is on the second summary from the previous step; shift-tab back.
|
|
233
|
-
await userEvent.tab({ shift: true });
|
|
234
|
-
await waitFor(() => expect(firstSummary).toHaveFocus());
|
|
235
|
-
|
|
236
|
-
await userEvent.keyboard('{Enter}');
|
|
237
|
-
|
|
238
|
-
await waitFor(() => {
|
|
239
|
-
const firstButton = canvas.getByTestId('first-button');
|
|
240
|
-
expect(firstButton).toBeInTheDocument();
|
|
241
|
-
});
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
await step('Tab into expanded content', async () => {
|
|
245
|
-
const firstButton = canvas.getByTestId('first-button');
|
|
246
|
-
await userEvent.tab();
|
|
247
|
-
await waitFor(() => expect(firstButton).toHaveFocus());
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
await step('Space key to expand accordion', async () => {
|
|
251
|
-
const secondSummary = canvas.getByTestId('second-summary');
|
|
252
|
-
|
|
253
|
-
// Continue the journey forward from the button inside the first panel.
|
|
254
|
-
await userEvent.tab();
|
|
255
|
-
await waitFor(() => expect(secondSummary).toHaveFocus());
|
|
256
|
-
|
|
257
|
-
await userEvent.keyboard(' ');
|
|
258
|
-
|
|
259
|
-
await waitFor(() => {
|
|
260
|
-
const secondButton = canvas.getByTestId('second-button');
|
|
261
|
-
expect(secondButton).toBeInTheDocument();
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
},
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
// Screen Reader Test
|
|
268
|
-
export const ScreenReaderTest: Story = {
|
|
269
|
-
name: '🔊 Screen Reader Test',
|
|
270
|
-
render: () => (
|
|
271
|
-
<Accordion variant="glass" data-testid="accessible-accordion" aria-label="Settings accordion">
|
|
272
|
-
<AccordionSummary
|
|
273
|
-
expandIcon={<ExpandMore />}
|
|
274
|
-
data-testid="accessible-summary"
|
|
275
|
-
aria-describedby="accordion-description"
|
|
276
|
-
>
|
|
277
|
-
<Settings aria-hidden="true" sx={{ mr: 1 }} />
|
|
278
|
-
<Typography>Settings Panel</Typography>
|
|
279
|
-
</AccordionSummary>
|
|
280
|
-
<AccordionDetails data-testid="accessible-details">
|
|
281
|
-
<Typography id="accordion-description">
|
|
282
|
-
Configure your application settings including theme, language, and notifications.
|
|
283
|
-
</Typography>
|
|
284
|
-
<Box role="group" aria-label="Setting controls">
|
|
285
|
-
<Button aria-label="Save settings">Save</Button>
|
|
286
|
-
<Button aria-label="Reset to defaults">Reset</Button>
|
|
287
|
-
</Box>
|
|
288
|
-
</AccordionDetails>
|
|
289
|
-
</Accordion>
|
|
290
|
-
),
|
|
291
|
-
play: async ({ canvasElement, step }) => {
|
|
292
|
-
const canvas = within(canvasElement);
|
|
293
|
-
|
|
294
|
-
await step('Verify ARIA labels', async () => {
|
|
295
|
-
const accordion = canvas.getByTestId('accessible-accordion');
|
|
296
|
-
await expect(accordion).toHaveAttribute('aria-label', 'Settings accordion');
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
await step('Verify ARIA descriptions', async () => {
|
|
300
|
-
const summary = canvas.getByTestId('accessible-summary');
|
|
301
|
-
await expect(summary).toHaveAttribute('aria-describedby', 'accordion-description');
|
|
302
|
-
|
|
303
|
-
const description = canvas.getByText(/Configure your application settings/);
|
|
304
|
-
await expect(description).toHaveAttribute('id', 'accordion-description');
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
await step('Verify role attributes', async () => {
|
|
308
|
-
// Expand to access the controls group
|
|
309
|
-
const summary = canvas.getByTestId('accessible-summary');
|
|
310
|
-
await userEvent.click(summary);
|
|
311
|
-
|
|
312
|
-
await waitFor(async () => {
|
|
313
|
-
const controlsGroup = canvas.getByRole('group', { name: 'Setting controls' });
|
|
314
|
-
await expect(controlsGroup).toBeInTheDocument();
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
const saveButton = canvas.getByRole('button', { name: 'Save settings' });
|
|
318
|
-
const resetButton = canvas.getByRole('button', { name: 'Reset to defaults' });
|
|
319
|
-
|
|
320
|
-
await expect(saveButton).toBeInTheDocument();
|
|
321
|
-
await expect(resetButton).toBeInTheDocument();
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
await step('Verify icon accessibility', async () => {
|
|
325
|
-
const icon = canvas.getByTestId('accessible-summary').querySelector('[aria-hidden="true"]');
|
|
326
|
-
await expect(icon).toBeInTheDocument();
|
|
327
|
-
});
|
|
328
|
-
},
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
// Focus Management Test
|
|
332
|
-
export const FocusManagement: Story = {
|
|
333
|
-
name: '🎯 Focus Management Test',
|
|
334
|
-
render: () => (
|
|
335
|
-
<Box>
|
|
336
|
-
<Button data-testid="external-trigger">External Focus Trigger</Button>
|
|
337
|
-
<Accordion variant="separated" data-testid="focus-accordion" defaultExpanded={false}>
|
|
338
|
-
<AccordionSummary expandIcon={<ExpandMore />} data-testid="focus-summary">
|
|
339
|
-
<Typography>Focus Test Accordion</Typography>
|
|
340
|
-
</AccordionSummary>
|
|
341
|
-
<AccordionDetails>
|
|
342
|
-
<Stack spacing={1}>
|
|
343
|
-
<Button data-testid="first-focus-element">First Focusable</Button>
|
|
344
|
-
<Button data-testid="second-focus-element">Second Focusable</Button>
|
|
345
|
-
<Button data-testid="last-focus-element">Last Focusable</Button>
|
|
346
|
-
</Stack>
|
|
347
|
-
</AccordionDetails>
|
|
348
|
-
<AccordionActions>
|
|
349
|
-
<Button data-testid="action-cancel">Cancel</Button>
|
|
350
|
-
<Button data-testid="action-save" variant="contained">
|
|
351
|
-
Save
|
|
352
|
-
</Button>
|
|
353
|
-
</AccordionActions>
|
|
354
|
-
</Accordion>
|
|
355
|
-
</Box>
|
|
356
|
-
),
|
|
357
|
-
play: async ({ canvasElement, step }) => {
|
|
358
|
-
const canvas = within(canvasElement);
|
|
359
|
-
|
|
360
|
-
await step('Initial focus state', async () => {
|
|
361
|
-
const externalTrigger = canvas.getByTestId('external-trigger');
|
|
362
|
-
await userEvent.tab();
|
|
363
|
-
await waitFor(() => expect(externalTrigger).toHaveFocus());
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
await step('Focus on accordion summary', async () => {
|
|
367
|
-
const summary = canvas.getByTestId('focus-summary');
|
|
368
|
-
await userEvent.tab();
|
|
369
|
-
await waitFor(() => expect(summary).toHaveFocus());
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
await step('Expand accordion and check focus flow', async () => {
|
|
373
|
-
const summary = canvas.getByTestId('focus-summary');
|
|
374
|
-
await userEvent.click(summary);
|
|
375
|
-
|
|
376
|
-
// Wait for accordion expansion animation to complete
|
|
377
|
-
await waitFor(
|
|
378
|
-
async () => {
|
|
379
|
-
const firstButton = canvas.getByTestId('first-focus-element');
|
|
380
|
-
expect(firstButton).toBeInTheDocument();
|
|
381
|
-
expect(firstButton).toBeVisible();
|
|
382
|
-
},
|
|
383
|
-
{ timeout: 1000 },
|
|
384
|
-
);
|
|
385
|
-
|
|
386
|
-
// Tab through the expanded content with proper focus waiting
|
|
387
|
-
await userEvent.tab();
|
|
388
|
-
await waitFor(
|
|
389
|
-
() => {
|
|
390
|
-
const firstButton = canvas.getByTestId('first-focus-element');
|
|
391
|
-
expect(firstButton).toHaveFocus();
|
|
392
|
-
},
|
|
393
|
-
{ timeout: 500 },
|
|
394
|
-
);
|
|
395
|
-
|
|
396
|
-
await userEvent.tab();
|
|
397
|
-
await waitFor(
|
|
398
|
-
() => {
|
|
399
|
-
const secondButton = canvas.getByTestId('second-focus-element');
|
|
400
|
-
expect(secondButton).toHaveFocus();
|
|
401
|
-
},
|
|
402
|
-
{ timeout: 500 },
|
|
403
|
-
);
|
|
404
|
-
|
|
405
|
-
await userEvent.tab();
|
|
406
|
-
await waitFor(
|
|
407
|
-
() => {
|
|
408
|
-
const lastButton = canvas.getByTestId('last-focus-element');
|
|
409
|
-
expect(lastButton).toHaveFocus();
|
|
410
|
-
},
|
|
411
|
-
{ timeout: 500 },
|
|
412
|
-
);
|
|
413
|
-
|
|
414
|
-
// Tab to action buttons - actual tab order may vary by browser
|
|
415
|
-
await userEvent.tab();
|
|
416
|
-
await waitFor(
|
|
417
|
-
() => {
|
|
418
|
-
// Accept either Cancel or Save button being focused first
|
|
419
|
-
const cancelAction = canvas.getByTestId('action-cancel');
|
|
420
|
-
const saveAction = canvas.getByTestId('action-save');
|
|
421
|
-
const cancelHasFocus = cancelAction === document.activeElement;
|
|
422
|
-
const saveHasFocus = saveAction === document.activeElement;
|
|
423
|
-
expect(cancelHasFocus || saveHasFocus).toBe(true);
|
|
424
|
-
},
|
|
425
|
-
{ timeout: 500 },
|
|
426
|
-
);
|
|
427
|
-
|
|
428
|
-
// Tab to the next action button
|
|
429
|
-
await userEvent.tab();
|
|
430
|
-
await waitFor(
|
|
431
|
-
() => {
|
|
432
|
-
// After second tab, one of the action buttons should still have focus
|
|
433
|
-
const cancelAction = canvas.getByTestId('action-cancel');
|
|
434
|
-
const saveAction = canvas.getByTestId('action-save');
|
|
435
|
-
const cancelHasFocus = cancelAction === document.activeElement;
|
|
436
|
-
const saveHasFocus = saveAction === document.activeElement;
|
|
437
|
-
expect(cancelHasFocus || saveHasFocus).toBe(true);
|
|
438
|
-
},
|
|
439
|
-
{ timeout: 500 },
|
|
440
|
-
);
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
await step('Focus restoration after collapse', async () => {
|
|
444
|
-
const summary = canvas.getByTestId('focus-summary');
|
|
445
|
-
|
|
446
|
-
// No pre-focus: clicking the summary focuses it, so calling .focus()
|
|
447
|
-
// first only asserted that focus() works.
|
|
448
|
-
// Click to collapse
|
|
449
|
-
await userEvent.click(summary);
|
|
450
|
-
|
|
451
|
-
// Wait for collapse animation and check summary retains or regains focus
|
|
452
|
-
await waitFor(
|
|
453
|
-
() => {
|
|
454
|
-
expect(summary).toHaveFocus();
|
|
455
|
-
},
|
|
456
|
-
{ timeout: 1000 },
|
|
457
|
-
);
|
|
458
|
-
});
|
|
459
|
-
},
|
|
460
|
-
};
|
|
461
|
-
|
|
462
|
-
// Visual States Test
|
|
463
|
-
export const VisualStates: Story = {
|
|
464
|
-
name: '👁️ Visual States Test',
|
|
465
|
-
render: () => (
|
|
466
|
-
<Stack spacing={2}>
|
|
467
|
-
<Accordion variant="default" data-testid="default-accordion">
|
|
468
|
-
<AccordionSummary expandIcon={<ExpandMore />} data-testid="default-summary">
|
|
469
|
-
<Typography>Default State</Typography>
|
|
470
|
-
</AccordionSummary>
|
|
471
|
-
<AccordionDetails data-testid="default-details">
|
|
472
|
-
<Typography>Default accordion content</Typography>
|
|
473
|
-
</AccordionDetails>
|
|
474
|
-
</Accordion>
|
|
475
|
-
|
|
476
|
-
<Accordion variant="glass" data-testid="glass-accordion">
|
|
477
|
-
<AccordionSummary expandIcon={<ExpandMore />} data-testid="glass-summary">
|
|
478
|
-
<Typography>Glass Variant</Typography>
|
|
479
|
-
</AccordionSummary>
|
|
480
|
-
<AccordionDetails>
|
|
481
|
-
<Typography>Glass effect accordion</Typography>
|
|
482
|
-
</AccordionDetails>
|
|
483
|
-
</Accordion>
|
|
484
|
-
|
|
485
|
-
<Accordion disabled data-testid="disabled-accordion">
|
|
486
|
-
<AccordionSummary expandIcon={<ExpandMore />} data-testid="disabled-summary">
|
|
487
|
-
<Typography>Disabled State</Typography>
|
|
488
|
-
</AccordionSummary>
|
|
489
|
-
<AccordionDetails>
|
|
490
|
-
<Typography>This should not be accessible</Typography>
|
|
491
|
-
</AccordionDetails>
|
|
492
|
-
</Accordion>
|
|
493
|
-
</Stack>
|
|
494
|
-
),
|
|
495
|
-
play: async ({ canvasElement, step }) => {
|
|
496
|
-
const canvas = within(canvasElement);
|
|
497
|
-
|
|
498
|
-
await step('Default state verification', async () => {
|
|
499
|
-
const accordion = canvas.getByTestId('default-accordion');
|
|
500
|
-
await expect(accordion).toBeInTheDocument();
|
|
501
|
-
|
|
502
|
-
const summary = canvas.getByTestId('default-summary');
|
|
503
|
-
await expect(summary).toBeInTheDocument();
|
|
504
|
-
});
|
|
505
|
-
|
|
506
|
-
await step('Glass variant hover effect', async () => {
|
|
507
|
-
const glassSummary = canvas.getByTestId('glass-summary');
|
|
508
|
-
await userEvent.hover(glassSummary);
|
|
509
|
-
|
|
510
|
-
// Glass variant should have backdrop filter
|
|
511
|
-
await expect(glassSummary.closest('[data-testid="glass-accordion"]')).toBeInTheDocument();
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
await step('Disabled state verification', async () => {
|
|
515
|
-
const disabledAccordion = canvas.getByTestId('disabled-accordion');
|
|
516
|
-
await expect(disabledAccordion).toBeInTheDocument();
|
|
517
|
-
|
|
518
|
-
// MUI Accordion uses classes for disabled state, not aria-disabled
|
|
519
|
-
await expect(disabledAccordion).toHaveClass(/Mui-disabled/);
|
|
520
|
-
|
|
521
|
-
const disabledSummary = canvas.getByTestId('disabled-summary');
|
|
522
|
-
|
|
523
|
-
// Verify the disabled element has pointer-events: none
|
|
524
|
-
const styles = window.getComputedStyle(disabledSummary);
|
|
525
|
-
await expect(styles.pointerEvents).toBe('none');
|
|
526
|
-
|
|
527
|
-
// Content should remain hidden since it can't be clicked
|
|
528
|
-
await waitFor(() =>
|
|
529
|
-
expect(canvas.queryByText('This should not be accessible')).not.toBeVisible(),
|
|
530
|
-
);
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
await step('Expand animation test', async () => {
|
|
534
|
-
const defaultSummary = canvas.getByTestId('default-summary');
|
|
535
|
-
await userEvent.click(defaultSummary);
|
|
536
|
-
|
|
537
|
-
await waitFor(() => {
|
|
538
|
-
const details = canvas.getByTestId('default-details');
|
|
539
|
-
expect(details).toBeInTheDocument();
|
|
540
|
-
});
|
|
541
|
-
});
|
|
542
|
-
},
|
|
543
|
-
};
|
|
544
|
-
|
|
545
|
-
// Responsive Design Test
|
|
546
|
-
export const ResponsiveDesign: Story = {
|
|
547
|
-
name: '📱 Responsive Design Test',
|
|
548
|
-
render: () => (
|
|
549
|
-
<Box data-testid="responsive-container">
|
|
550
|
-
<Accordion variant="bordered" data-testid="responsive-accordion">
|
|
551
|
-
<AccordionSummary expandIcon={<ExpandMore />}>
|
|
552
|
-
<Typography>Responsive Accordion</Typography>
|
|
553
|
-
</AccordionSummary>
|
|
554
|
-
<AccordionDetails>
|
|
555
|
-
<Box
|
|
556
|
-
display="grid"
|
|
557
|
-
gridTemplateColumns={{ xs: '1fr', sm: '1fr 1fr', md: '1fr 1fr 1fr' }}
|
|
558
|
-
gap={2}
|
|
559
|
-
data-testid="responsive-grid"
|
|
560
|
-
>
|
|
561
|
-
<Box sx={{ p: 2, border: '1px solid', borderColor: 'divider' }}>
|
|
562
|
-
<Typography variant="subtitle2">Item 1</Typography>
|
|
563
|
-
<Typography variant="body2">Responsive content</Typography>
|
|
564
|
-
</Box>
|
|
565
|
-
<Box sx={{ p: 2, border: '1px solid', borderColor: 'divider' }}>
|
|
566
|
-
<Typography variant="subtitle2">Item 2</Typography>
|
|
567
|
-
<Typography variant="body2">Responsive content</Typography>
|
|
568
|
-
</Box>
|
|
569
|
-
<Box sx={{ p: 2, border: '1px solid', borderColor: 'divider' }}>
|
|
570
|
-
<Typography variant="subtitle2">Item 3</Typography>
|
|
571
|
-
<Typography variant="body2">Responsive content</Typography>
|
|
572
|
-
</Box>
|
|
573
|
-
</Box>
|
|
574
|
-
</AccordionDetails>
|
|
575
|
-
</Accordion>
|
|
576
|
-
</Box>
|
|
577
|
-
),
|
|
578
|
-
parameters: {
|
|
579
|
-
viewport: {
|
|
580
|
-
viewports: {
|
|
581
|
-
mobile: {
|
|
582
|
-
name: 'Mobile',
|
|
583
|
-
styles: { width: '375px', height: '667px' },
|
|
584
|
-
type: 'mobile',
|
|
585
|
-
},
|
|
586
|
-
tablet: {
|
|
587
|
-
name: 'Tablet',
|
|
588
|
-
styles: { width: '768px', height: '1024px' },
|
|
589
|
-
type: 'tablet',
|
|
590
|
-
},
|
|
591
|
-
desktop: {
|
|
592
|
-
name: 'Desktop',
|
|
593
|
-
styles: { width: '1920px', height: '1080px' },
|
|
594
|
-
type: 'desktop',
|
|
595
|
-
},
|
|
596
|
-
},
|
|
597
|
-
defaultViewport: 'mobile',
|
|
598
|
-
},
|
|
599
|
-
chromatic: {
|
|
600
|
-
viewports: [375, 768, 1920],
|
|
601
|
-
delay: 300,
|
|
602
|
-
},
|
|
603
|
-
},
|
|
604
|
-
play: async ({ canvasElement, step }) => {
|
|
605
|
-
const canvas = within(canvasElement);
|
|
606
|
-
|
|
607
|
-
await step('Verify responsive container', async () => {
|
|
608
|
-
const container = canvas.getByTestId('responsive-container');
|
|
609
|
-
const accordion = canvas.getByTestId('responsive-accordion');
|
|
610
|
-
|
|
611
|
-
await expect(container).toBeInTheDocument();
|
|
612
|
-
await expect(accordion).toBeInTheDocument();
|
|
613
|
-
});
|
|
614
|
-
|
|
615
|
-
await step('Expand accordion to test responsive content', async () => {
|
|
616
|
-
const summary = canvas
|
|
617
|
-
.getByTestId('responsive-accordion')
|
|
618
|
-
.querySelector('.MuiAccordionSummary-root');
|
|
619
|
-
if (summary) {
|
|
620
|
-
await userEvent.click(summary);
|
|
621
|
-
|
|
622
|
-
await waitFor(() => {
|
|
623
|
-
const grid = canvas.getByTestId('responsive-grid');
|
|
624
|
-
expect(grid).toBeInTheDocument();
|
|
625
|
-
});
|
|
626
|
-
}
|
|
627
|
-
});
|
|
628
|
-
},
|
|
629
|
-
};
|
|
630
|
-
|
|
631
|
-
// Edge Cases Test
|
|
632
|
-
export const EdgeCases: Story = {
|
|
633
|
-
name: '🔧 Edge Cases Test',
|
|
634
|
-
render: () => (
|
|
635
|
-
<Stack spacing={2}>
|
|
636
|
-
<Accordion variant="bordered" data-testid="empty-content-accordion">
|
|
637
|
-
<AccordionSummary expandIcon={<ExpandMore />}>
|
|
638
|
-
<Typography>Empty Content Test</Typography>
|
|
639
|
-
</AccordionSummary>
|
|
640
|
-
<AccordionDetails data-testid="empty-details"> {/* Empty content */} </AccordionDetails>
|
|
641
|
-
</Accordion>
|
|
642
|
-
|
|
643
|
-
<Accordion variant="glass" data-testid="long-content-accordion">
|
|
644
|
-
<AccordionSummary expandIcon={<ExpandMore />}>
|
|
645
|
-
<Typography data-testid="long-title">
|
|
646
|
-
{'Very '.repeat(20)}Long Title That Should Handle Overflow Gracefully
|
|
647
|
-
</Typography>
|
|
648
|
-
</AccordionSummary>
|
|
649
|
-
<AccordionDetails data-testid="long-content">
|
|
650
|
-
<Typography>
|
|
651
|
-
{'This is very long content that should test how the accordion handles extensive amounts of text. '.repeat(
|
|
652
|
-
50,
|
|
653
|
-
)}
|
|
654
|
-
</Typography>
|
|
655
|
-
</AccordionDetails>
|
|
656
|
-
</Accordion>
|
|
657
|
-
|
|
658
|
-
<Accordion variant="separated" data-testid="nested-accordion">
|
|
659
|
-
<AccordionSummary expandIcon={<ExpandMore />}>
|
|
660
|
-
<Typography>Nested Content Test</Typography>
|
|
661
|
-
</AccordionSummary>
|
|
662
|
-
<AccordionDetails>
|
|
663
|
-
<Box sx={{ maxHeight: 200, overflow: 'auto' }}>
|
|
664
|
-
<Stack spacing={1}>
|
|
665
|
-
{Array.from({ length: 10 }, (_, i) => (
|
|
666
|
-
<Box key={i} sx={{ p: 1, border: '1px solid', borderColor: 'divider' }}>
|
|
667
|
-
Item {i + 1}
|
|
668
|
-
</Box>
|
|
669
|
-
))}
|
|
670
|
-
</Stack>
|
|
671
|
-
</Box>
|
|
672
|
-
</AccordionDetails>
|
|
673
|
-
</Accordion>
|
|
674
|
-
</Stack>
|
|
675
|
-
),
|
|
676
|
-
play: async ({ canvasElement, step }) => {
|
|
677
|
-
const canvas = within(canvasElement);
|
|
678
|
-
|
|
679
|
-
await step('Empty content handling', async () => {
|
|
680
|
-
const emptyAccordion = canvas.getByTestId('empty-content-accordion');
|
|
681
|
-
const summary = emptyAccordion.querySelector('.MuiAccordionSummary-root');
|
|
682
|
-
|
|
683
|
-
if (summary) {
|
|
684
|
-
await userEvent.click(summary);
|
|
685
|
-
|
|
686
|
-
const emptyDetails = canvas.getByTestId('empty-details');
|
|
687
|
-
await expect(emptyDetails).toBeInTheDocument();
|
|
688
|
-
// Should handle empty content gracefully
|
|
689
|
-
}
|
|
690
|
-
});
|
|
691
|
-
|
|
692
|
-
await step('Long text overflow handling', async () => {
|
|
693
|
-
const longTitle = canvas.getByTestId('long-title');
|
|
694
|
-
await expect(longTitle).toBeInTheDocument();
|
|
695
|
-
|
|
696
|
-
const longAccordion = canvas.getByTestId('long-content-accordion');
|
|
697
|
-
const summary = longAccordion.querySelector('.MuiAccordionSummary-root');
|
|
698
|
-
|
|
699
|
-
if (summary) {
|
|
700
|
-
await userEvent.click(summary);
|
|
701
|
-
|
|
702
|
-
const longContent = canvas.getByTestId('long-content');
|
|
703
|
-
await expect(longContent).toBeInTheDocument();
|
|
704
|
-
}
|
|
705
|
-
});
|
|
706
|
-
|
|
707
|
-
await step('Nested scrollable content', async () => {
|
|
708
|
-
const nestedAccordion = canvas.getByTestId('nested-accordion');
|
|
709
|
-
const summary = nestedAccordion.querySelector('.MuiAccordionSummary-root');
|
|
710
|
-
|
|
711
|
-
if (summary) {
|
|
712
|
-
await userEvent.click(summary);
|
|
713
|
-
|
|
714
|
-
await waitFor(() => {
|
|
715
|
-
const items = canvas.getAllByText(/^Item \d+$/);
|
|
716
|
-
expect(items.length).toBe(10);
|
|
717
|
-
});
|
|
718
|
-
}
|
|
719
|
-
});
|
|
720
|
-
},
|
|
721
|
-
};
|
|
722
|
-
|
|
723
|
-
// Form Interaction Test
|
|
724
|
-
export const FormInteraction: Story = {
|
|
725
|
-
name: '📝 Form Interaction Test',
|
|
726
|
-
render: () => {
|
|
727
|
-
const TestComponent = () => {
|
|
728
|
-
const [formData, setFormData] = useState({
|
|
729
|
-
username: '',
|
|
730
|
-
email: '',
|
|
731
|
-
preferences: {
|
|
732
|
-
theme: 'light',
|
|
733
|
-
notifications: true,
|
|
734
|
-
},
|
|
735
|
-
});
|
|
736
|
-
|
|
737
|
-
return (
|
|
738
|
-
<Stack spacing={2}>
|
|
739
|
-
<Accordion variant="bordered" data-testid="form-accordion">
|
|
740
|
-
<AccordionSummary expandIcon={<ExpandMore />} data-testid="form-summary">
|
|
741
|
-
<Typography>User Settings Form</Typography>
|
|
742
|
-
</AccordionSummary>
|
|
743
|
-
<AccordionDetails>
|
|
744
|
-
<Stack spacing={2} sx={{ width: '100%' }}>
|
|
745
|
-
<input
|
|
746
|
-
type="text"
|
|
747
|
-
placeholder="Username"
|
|
748
|
-
value={formData.username}
|
|
749
|
-
onChange={(e) => setFormData({ ...formData, username: e.target.value })}
|
|
750
|
-
data-testid="username-input"
|
|
751
|
-
style={{ padding: '8px', borderRadius: '4px', border: '1px solid #ccc' }}
|
|
752
|
-
/>
|
|
753
|
-
<input
|
|
754
|
-
type="email"
|
|
755
|
-
placeholder="Email"
|
|
756
|
-
value={formData.email}
|
|
757
|
-
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
|
758
|
-
data-testid="email-input"
|
|
759
|
-
style={{ padding: '8px', borderRadius: '4px', border: '1px solid #ccc' }}
|
|
760
|
-
/>
|
|
761
|
-
<label data-testid="theme-label">
|
|
762
|
-
Theme:
|
|
763
|
-
<select
|
|
764
|
-
value={formData.preferences.theme}
|
|
765
|
-
onChange={(e) =>
|
|
766
|
-
setFormData({
|
|
767
|
-
...formData,
|
|
768
|
-
preferences: { ...formData.preferences, theme: e.target.value },
|
|
769
|
-
})
|
|
770
|
-
}
|
|
771
|
-
data-testid="theme-select"
|
|
772
|
-
style={{ marginLeft: '8px', padding: '4px' }}
|
|
773
|
-
>
|
|
774
|
-
<option value="light">Light</option>
|
|
775
|
-
<option value="dark">Dark</option>
|
|
776
|
-
<option value="auto">Auto</option>
|
|
777
|
-
</select>
|
|
778
|
-
</label>
|
|
779
|
-
<label data-testid="notifications-label">
|
|
780
|
-
<input
|
|
781
|
-
type="checkbox"
|
|
782
|
-
checked={formData.preferences.notifications}
|
|
783
|
-
onChange={(e) =>
|
|
784
|
-
setFormData({
|
|
785
|
-
...formData,
|
|
786
|
-
preferences: { ...formData.preferences, notifications: e.target.checked },
|
|
787
|
-
})
|
|
788
|
-
}
|
|
789
|
-
data-testid="notifications-checkbox"
|
|
790
|
-
/>
|
|
791
|
-
Enable Notifications
|
|
792
|
-
</label>
|
|
793
|
-
</Stack>
|
|
794
|
-
</AccordionDetails>
|
|
795
|
-
<AccordionActions>
|
|
796
|
-
<Button
|
|
797
|
-
data-testid="form-cancel"
|
|
798
|
-
onClick={() =>
|
|
799
|
-
setFormData({
|
|
800
|
-
username: '',
|
|
801
|
-
email: '',
|
|
802
|
-
preferences: { theme: 'light', notifications: true },
|
|
803
|
-
})
|
|
804
|
-
}
|
|
805
|
-
>
|
|
806
|
-
Reset
|
|
807
|
-
</Button>
|
|
808
|
-
<Button
|
|
809
|
-
data-testid="form-submit"
|
|
810
|
-
variant="contained"
|
|
811
|
-
onClick={() => {
|
|
812
|
-
// Form submission handler
|
|
813
|
-
}}
|
|
814
|
-
>
|
|
815
|
-
Submit
|
|
816
|
-
</Button>
|
|
817
|
-
</AccordionActions>
|
|
818
|
-
</Accordion>
|
|
819
|
-
</Stack>
|
|
820
|
-
);
|
|
821
|
-
};
|
|
822
|
-
|
|
823
|
-
return <TestComponent />;
|
|
824
|
-
},
|
|
825
|
-
play: async ({ canvasElement, step }) => {
|
|
826
|
-
const canvas = within(canvasElement);
|
|
827
|
-
|
|
828
|
-
await step('Expand accordion with form', async () => {
|
|
829
|
-
const summary = canvas.getByTestId('form-summary');
|
|
830
|
-
await userEvent.click(summary);
|
|
831
|
-
|
|
832
|
-
await waitFor(() => {
|
|
833
|
-
const usernameInput = canvas.getByTestId('username-input');
|
|
834
|
-
expect(usernameInput).toBeInTheDocument();
|
|
835
|
-
});
|
|
836
|
-
});
|
|
837
|
-
|
|
838
|
-
await step('Fill in text inputs', async () => {
|
|
839
|
-
const usernameInput = canvas.getByTestId('username-input');
|
|
840
|
-
const emailInput = canvas.getByTestId('email-input');
|
|
841
|
-
|
|
842
|
-
await userEvent.type(usernameInput, 'testuser');
|
|
843
|
-
await expect(usernameInput).toHaveValue('testuser');
|
|
844
|
-
|
|
845
|
-
await userEvent.type(emailInput, 'test@example.com');
|
|
846
|
-
await expect(emailInput).toHaveValue('test@example.com');
|
|
847
|
-
});
|
|
848
|
-
|
|
849
|
-
await step('Change select value', async () => {
|
|
850
|
-
const themeSelect = canvas.getByTestId('theme-select');
|
|
851
|
-
|
|
852
|
-
await userEvent.selectOptions(themeSelect, 'dark');
|
|
853
|
-
await expect(themeSelect).toHaveValue('dark');
|
|
854
|
-
});
|
|
855
|
-
|
|
856
|
-
await step('Toggle checkbox', async () => {
|
|
857
|
-
const notificationsCheckbox = canvas.getByTestId('notifications-checkbox');
|
|
858
|
-
|
|
859
|
-
await expect(notificationsCheckbox).toBeChecked();
|
|
860
|
-
await userEvent.click(notificationsCheckbox);
|
|
861
|
-
await expect(notificationsCheckbox).not.toBeChecked();
|
|
862
|
-
});
|
|
863
|
-
|
|
864
|
-
await step('Reset form', async () => {
|
|
865
|
-
const resetButton = canvas.getByTestId('form-cancel');
|
|
866
|
-
await userEvent.click(resetButton);
|
|
867
|
-
|
|
868
|
-
await waitFor(() => {
|
|
869
|
-
const usernameInput = canvas.getByTestId('username-input');
|
|
870
|
-
const emailInput = canvas.getByTestId('email-input');
|
|
871
|
-
const themeSelect = canvas.getByTestId('theme-select');
|
|
872
|
-
const notificationsCheckbox = canvas.getByTestId('notifications-checkbox');
|
|
873
|
-
|
|
874
|
-
expect(usernameInput).toHaveValue('');
|
|
875
|
-
expect(emailInput).toHaveValue('');
|
|
876
|
-
expect(themeSelect).toHaveValue('light');
|
|
877
|
-
expect(notificationsCheckbox).toBeChecked();
|
|
878
|
-
});
|
|
879
|
-
});
|
|
880
|
-
|
|
881
|
-
await step('Form submission', async () => {
|
|
882
|
-
const usernameInput = canvas.getByTestId('username-input');
|
|
883
|
-
await userEvent.type(usernameInput, 'finaluser');
|
|
884
|
-
|
|
885
|
-
const submitButton = canvas.getByTestId('form-submit');
|
|
886
|
-
await userEvent.click(submitButton);
|
|
887
|
-
|
|
888
|
-
// Form data should be processed (console.log in this case)
|
|
889
|
-
await expect(usernameInput).toHaveValue('finaluser');
|
|
890
|
-
});
|
|
891
|
-
},
|
|
892
|
-
};
|
|
893
|
-
|
|
894
|
-
// Theme Variations Test
|
|
895
|
-
export const ThemeVariations: Story = {
|
|
896
|
-
name: '🎨 Theme Variations Test',
|
|
897
|
-
render: () => {
|
|
898
|
-
const TestComponent = () => {
|
|
899
|
-
const [isDarkMode, setIsDarkMode] = useState(false);
|
|
900
|
-
|
|
901
|
-
return (
|
|
902
|
-
<Box
|
|
903
|
-
sx={{
|
|
904
|
-
p: 3,
|
|
905
|
-
backgroundColor: isDarkMode ? '#121212' : '#ffffff',
|
|
906
|
-
color: isDarkMode ? '#ffffff' : '#000000',
|
|
907
|
-
minHeight: '400px',
|
|
908
|
-
transition: 'all 0.3s ease',
|
|
909
|
-
}}
|
|
910
|
-
data-testid="theme-container"
|
|
911
|
-
>
|
|
912
|
-
<Button
|
|
913
|
-
onClick={() => setIsDarkMode(!isDarkMode)}
|
|
914
|
-
data-testid="theme-toggle"
|
|
915
|
-
variant="outlined"
|
|
916
|
-
sx={{ mb: 2 }}
|
|
917
|
-
>
|
|
918
|
-
Toggle {isDarkMode ? 'Light' : 'Dark'} Mode
|
|
919
|
-
</Button>
|
|
920
|
-
|
|
921
|
-
<Stack spacing={2}>
|
|
922
|
-
<Accordion
|
|
923
|
-
variant="default"
|
|
924
|
-
data-testid="themed-default"
|
|
925
|
-
sx={{
|
|
926
|
-
backgroundColor: isDarkMode ? '#1e1e1e' : '#ffffff',
|
|
927
|
-
color: isDarkMode ? '#ffffff' : '#000000',
|
|
928
|
-
}}
|
|
929
|
-
>
|
|
930
|
-
<AccordionSummary expandIcon={<ExpandMore />}>
|
|
931
|
-
<Typography>Default Theme Variant</Typography>
|
|
932
|
-
</AccordionSummary>
|
|
933
|
-
<AccordionDetails>
|
|
934
|
-
<Typography>Content adapts to {isDarkMode ? 'dark' : 'light'} theme</Typography>
|
|
935
|
-
</AccordionDetails>
|
|
936
|
-
</Accordion>
|
|
937
|
-
|
|
938
|
-
<Accordion
|
|
939
|
-
variant="glass"
|
|
940
|
-
data-testid="themed-glass"
|
|
941
|
-
sx={{
|
|
942
|
-
color: isDarkMode ? '#ffffff' : '#000000',
|
|
943
|
-
}}
|
|
944
|
-
>
|
|
945
|
-
<AccordionSummary expandIcon={<ExpandMore />}>
|
|
946
|
-
<Typography>Glass Theme Variant</Typography>
|
|
947
|
-
</AccordionSummary>
|
|
948
|
-
<AccordionDetails>
|
|
949
|
-
<Typography>Glass effect with {isDarkMode ? 'dark' : 'light'} theme</Typography>
|
|
950
|
-
</AccordionDetails>
|
|
951
|
-
</Accordion>
|
|
952
|
-
|
|
953
|
-
<Accordion
|
|
954
|
-
variant="bordered"
|
|
955
|
-
data-testid="themed-bordered"
|
|
956
|
-
sx={{
|
|
957
|
-
borderColor: isDarkMode ? '#444' : '#ddd',
|
|
958
|
-
backgroundColor: isDarkMode ? '#2a2a2a' : '#fafafa',
|
|
959
|
-
color: isDarkMode ? '#ffffff' : '#000000',
|
|
960
|
-
}}
|
|
961
|
-
>
|
|
962
|
-
<AccordionSummary expandIcon={<ExpandMore />}>
|
|
963
|
-
<Typography>Bordered Theme Variant</Typography>
|
|
964
|
-
</AccordionSummary>
|
|
965
|
-
<AccordionDetails>
|
|
966
|
-
<Typography>Bordered with {isDarkMode ? 'dark' : 'light'} theme</Typography>
|
|
967
|
-
</AccordionDetails>
|
|
968
|
-
</Accordion>
|
|
969
|
-
|
|
970
|
-
<Accordion
|
|
971
|
-
variant="separated"
|
|
972
|
-
data-testid="themed-separated"
|
|
973
|
-
sx={{
|
|
974
|
-
backgroundColor: isDarkMode ? '#333' : '#f5f5f5',
|
|
975
|
-
color: isDarkMode ? '#ffffff' : '#000000',
|
|
976
|
-
}}
|
|
977
|
-
>
|
|
978
|
-
<AccordionSummary expandIcon={<ExpandMore />}>
|
|
979
|
-
<Typography>Separated Theme Variant</Typography>
|
|
980
|
-
</AccordionSummary>
|
|
981
|
-
<AccordionDetails>
|
|
982
|
-
<Typography>Separated style with {isDarkMode ? 'dark' : 'light'} theme</Typography>
|
|
983
|
-
</AccordionDetails>
|
|
984
|
-
</Accordion>
|
|
985
|
-
</Stack>
|
|
986
|
-
</Box>
|
|
987
|
-
);
|
|
988
|
-
};
|
|
989
|
-
|
|
990
|
-
return <TestComponent />;
|
|
991
|
-
},
|
|
992
|
-
play: async ({ canvasElement, step }) => {
|
|
993
|
-
const canvas = within(canvasElement);
|
|
994
|
-
|
|
995
|
-
await step('Verify light theme rendering', async () => {
|
|
996
|
-
const container = canvas.getByTestId('theme-container');
|
|
997
|
-
const defaultAccordion = canvas.getByTestId('themed-default');
|
|
998
|
-
const glassAccordion = canvas.getByTestId('themed-glass');
|
|
999
|
-
const borderedAccordion = canvas.getByTestId('themed-bordered');
|
|
1000
|
-
const separatedAccordion = canvas.getByTestId('themed-separated');
|
|
1001
|
-
|
|
1002
|
-
await expect(container).toBeInTheDocument();
|
|
1003
|
-
await expect(defaultAccordion).toBeInTheDocument();
|
|
1004
|
-
await expect(glassAccordion).toBeInTheDocument();
|
|
1005
|
-
await expect(borderedAccordion).toBeInTheDocument();
|
|
1006
|
-
await expect(separatedAccordion).toBeInTheDocument();
|
|
1007
|
-
});
|
|
1008
|
-
|
|
1009
|
-
await step('Switch to dark theme', async () => {
|
|
1010
|
-
const toggleButton = canvas.getByTestId('theme-toggle');
|
|
1011
|
-
await userEvent.click(toggleButton);
|
|
1012
|
-
|
|
1013
|
-
await waitFor(() => {
|
|
1014
|
-
expect(toggleButton).toHaveTextContent('Toggle Light Mode');
|
|
1015
|
-
});
|
|
1016
|
-
});
|
|
1017
|
-
|
|
1018
|
-
await step('Verify dark theme styles applied', async () => {
|
|
1019
|
-
const container = canvas.getByTestId('theme-container');
|
|
1020
|
-
// Theme should be toggled to dark
|
|
1021
|
-
const toggleButton = canvas.getByTestId('theme-toggle');
|
|
1022
|
-
await expect(toggleButton).toHaveTextContent('Toggle Light Mode');
|
|
1023
|
-
|
|
1024
|
-
// Verify container exists and theme was toggled
|
|
1025
|
-
await expect(container).toBeInTheDocument();
|
|
1026
|
-
});
|
|
1027
|
-
|
|
1028
|
-
await step('Test accordion interactions in dark mode', async () => {
|
|
1029
|
-
const defaultAccordion = canvas.getByTestId('themed-default');
|
|
1030
|
-
const summary = defaultAccordion.querySelector('.MuiAccordionSummary-root');
|
|
1031
|
-
|
|
1032
|
-
if (summary) {
|
|
1033
|
-
await userEvent.click(summary);
|
|
1034
|
-
|
|
1035
|
-
await waitFor(() => {
|
|
1036
|
-
const content = canvas.getByText(/Content adapts to dark theme/);
|
|
1037
|
-
expect(content).toBeInTheDocument();
|
|
1038
|
-
});
|
|
1039
|
-
}
|
|
1040
|
-
});
|
|
1041
|
-
|
|
1042
|
-
await step('Switch back to light theme', async () => {
|
|
1043
|
-
const toggleButton = canvas.getByTestId('theme-toggle');
|
|
1044
|
-
await userEvent.click(toggleButton);
|
|
1045
|
-
|
|
1046
|
-
await waitFor(() => {
|
|
1047
|
-
expect(toggleButton).toHaveTextContent('Toggle Dark Mode');
|
|
1048
|
-
});
|
|
1049
|
-
|
|
1050
|
-
const container = canvas.getByTestId('theme-container');
|
|
1051
|
-
// Just check the element exists, theme styles can vary
|
|
1052
|
-
await expect(container).toBeInTheDocument();
|
|
1053
|
-
});
|
|
1054
|
-
},
|
|
1055
|
-
};
|
|
1056
|
-
|
|
1057
|
-
// Integration Test
|
|
1058
|
-
export const Integration: Story = {
|
|
1059
|
-
name: '🔗 Integration Test',
|
|
1060
|
-
render: () => {
|
|
1061
|
-
const TestComponent = () => {
|
|
1062
|
-
const [activeAccordion, setActiveAccordion] = useState<string | false>(false);
|
|
1063
|
-
const [data, setData] = useState({ loaded: false, items: [] as string[] });
|
|
1064
|
-
|
|
1065
|
-
const handleAccordionChange =
|
|
1066
|
-
(panel: string) => (event: React.SyntheticEvent, isExpanded: boolean) => {
|
|
1067
|
-
setActiveAccordion(isExpanded ? panel : false);
|
|
1068
|
-
|
|
1069
|
-
// Simulate data loading when accordion expands
|
|
1070
|
-
if (isExpanded && panel === 'panel2' && !data.loaded) {
|
|
1071
|
-
setTimeout(() => {
|
|
1072
|
-
setData({
|
|
1073
|
-
loaded: true,
|
|
1074
|
-
items: ['Loaded Item 1', 'Loaded Item 2', 'Loaded Item 3'],
|
|
1075
|
-
});
|
|
1076
|
-
}, 500);
|
|
1077
|
-
}
|
|
1078
|
-
};
|
|
1079
|
-
|
|
1080
|
-
return (
|
|
1081
|
-
<Box>
|
|
1082
|
-
<Typography variant="h6" gutterBottom data-testid="integration-title">
|
|
1083
|
-
Integrated Accordion System
|
|
1084
|
-
</Typography>
|
|
1085
|
-
<Typography variant="body2" sx={{ mb: 2 }} data-testid="active-panel">
|
|
1086
|
-
Active Panel: {activeAccordion || 'None'}
|
|
1087
|
-
</Typography>
|
|
1088
|
-
|
|
1089
|
-
<Stack spacing={1}>
|
|
1090
|
-
<Accordion
|
|
1091
|
-
expanded={activeAccordion === 'panel1'}
|
|
1092
|
-
onChange={handleAccordionChange('panel1')}
|
|
1093
|
-
variant="bordered"
|
|
1094
|
-
data-testid="integrated-panel1"
|
|
1095
|
-
>
|
|
1096
|
-
<AccordionSummary expandIcon={<ExpandMore />}>
|
|
1097
|
-
<Typography>Panel 1: Basic Information</Typography>
|
|
1098
|
-
</AccordionSummary>
|
|
1099
|
-
<AccordionDetails>
|
|
1100
|
-
<Stack spacing={2}>
|
|
1101
|
-
<Typography>This panel contains basic information.</Typography>
|
|
1102
|
-
<Button
|
|
1103
|
-
data-testid="panel1-action"
|
|
1104
|
-
onClick={() => setActiveAccordion('panel2')}
|
|
1105
|
-
variant="contained"
|
|
1106
|
-
size="small"
|
|
1107
|
-
>
|
|
1108
|
-
Go to Panel 2
|
|
1109
|
-
</Button>
|
|
1110
|
-
</Stack>
|
|
1111
|
-
</AccordionDetails>
|
|
1112
|
-
</Accordion>
|
|
1113
|
-
|
|
1114
|
-
<Accordion
|
|
1115
|
-
expanded={activeAccordion === 'panel2'}
|
|
1116
|
-
onChange={handleAccordionChange('panel2')}
|
|
1117
|
-
variant="bordered"
|
|
1118
|
-
data-testid="integrated-panel2"
|
|
1119
|
-
>
|
|
1120
|
-
<AccordionSummary expandIcon={<ExpandMore />}>
|
|
1121
|
-
<Typography>Panel 2: Dynamic Content</Typography>
|
|
1122
|
-
</AccordionSummary>
|
|
1123
|
-
<AccordionDetails data-testid="panel2-content">
|
|
1124
|
-
<Stack spacing={1}>
|
|
1125
|
-
{!data.loaded ? (
|
|
1126
|
-
<Typography data-testid="loading-text">Loading content...</Typography>
|
|
1127
|
-
) : (
|
|
1128
|
-
<>
|
|
1129
|
-
{data.items.map((item, index) => (
|
|
1130
|
-
<Typography key={index} data-testid={`loaded-item-${index}`}>
|
|
1131
|
-
{item}
|
|
1132
|
-
</Typography>
|
|
1133
|
-
))}
|
|
1134
|
-
</>
|
|
1135
|
-
)}
|
|
1136
|
-
<Button
|
|
1137
|
-
data-testid="panel2-action"
|
|
1138
|
-
onClick={() => setActiveAccordion('panel3')}
|
|
1139
|
-
variant="contained"
|
|
1140
|
-
size="small"
|
|
1141
|
-
>
|
|
1142
|
-
Go to Panel 3
|
|
1143
|
-
</Button>
|
|
1144
|
-
</Stack>
|
|
1145
|
-
</AccordionDetails>
|
|
1146
|
-
</Accordion>
|
|
1147
|
-
|
|
1148
|
-
<Accordion
|
|
1149
|
-
expanded={activeAccordion === 'panel3'}
|
|
1150
|
-
onChange={handleAccordionChange('panel3')}
|
|
1151
|
-
variant="bordered"
|
|
1152
|
-
data-testid="integrated-panel3"
|
|
1153
|
-
>
|
|
1154
|
-
<AccordionSummary expandIcon={<ExpandMore />}>
|
|
1155
|
-
<Typography>Panel 3: Summary</Typography>
|
|
1156
|
-
</AccordionSummary>
|
|
1157
|
-
<AccordionDetails>
|
|
1158
|
-
<Stack spacing={2}>
|
|
1159
|
-
<Typography>Summary of all panels:</Typography>
|
|
1160
|
-
<Box sx={{ pl: 2 }}>
|
|
1161
|
-
<Typography variant="body2">• Panel 1: Visited</Typography>
|
|
1162
|
-
<Typography variant="body2">
|
|
1163
|
-
• Panel 2: {data.loaded ? 'Data Loaded' : 'Not Visited'}
|
|
1164
|
-
</Typography>
|
|
1165
|
-
<Typography variant="body2">• Panel 3: Current</Typography>
|
|
1166
|
-
</Box>
|
|
1167
|
-
<Button
|
|
1168
|
-
data-testid="reset-action"
|
|
1169
|
-
onClick={() => {
|
|
1170
|
-
setActiveAccordion(false);
|
|
1171
|
-
setData({ loaded: false, items: [] });
|
|
1172
|
-
}}
|
|
1173
|
-
variant="outlined"
|
|
1174
|
-
size="small"
|
|
1175
|
-
>
|
|
1176
|
-
Reset All
|
|
1177
|
-
</Button>
|
|
1178
|
-
</Stack>
|
|
1179
|
-
</AccordionDetails>
|
|
1180
|
-
</Accordion>
|
|
1181
|
-
</Stack>
|
|
1182
|
-
</Box>
|
|
1183
|
-
);
|
|
1184
|
-
};
|
|
1185
|
-
|
|
1186
|
-
return <TestComponent />;
|
|
1187
|
-
},
|
|
1188
|
-
play: async ({ canvasElement, step }) => {
|
|
1189
|
-
const canvas = within(canvasElement);
|
|
1190
|
-
|
|
1191
|
-
await step('Verify integrated system setup', async () => {
|
|
1192
|
-
const title = canvas.getByTestId('integration-title');
|
|
1193
|
-
const activePanel = canvas.getByTestId('active-panel');
|
|
1194
|
-
const panel1 = canvas.getByTestId('integrated-panel1');
|
|
1195
|
-
const panel2 = canvas.getByTestId('integrated-panel2');
|
|
1196
|
-
const panel3 = canvas.getByTestId('integrated-panel3');
|
|
1197
|
-
|
|
1198
|
-
await expect(title).toBeInTheDocument();
|
|
1199
|
-
await expect(activePanel).toHaveTextContent('Active Panel: None');
|
|
1200
|
-
await expect(panel1).toBeInTheDocument();
|
|
1201
|
-
await expect(panel2).toBeInTheDocument();
|
|
1202
|
-
await expect(panel3).toBeInTheDocument();
|
|
1203
|
-
});
|
|
1204
|
-
|
|
1205
|
-
await step('Open first panel', async () => {
|
|
1206
|
-
const panel1 = canvas.getByTestId('integrated-panel1');
|
|
1207
|
-
const summary = panel1.querySelector('.MuiAccordionSummary-root');
|
|
1208
|
-
|
|
1209
|
-
if (summary) {
|
|
1210
|
-
await userEvent.click(summary);
|
|
1211
|
-
|
|
1212
|
-
await waitFor(() => {
|
|
1213
|
-
const activePanel = canvas.getByTestId('active-panel');
|
|
1214
|
-
expect(activePanel).toHaveTextContent('Active Panel: panel1');
|
|
1215
|
-
});
|
|
1216
|
-
|
|
1217
|
-
const actionButton = canvas.getByTestId('panel1-action');
|
|
1218
|
-
await expect(actionButton).toBeInTheDocument();
|
|
1219
|
-
}
|
|
1220
|
-
});
|
|
1221
|
-
|
|
1222
|
-
await step('Navigate to panel 2 via button', async () => {
|
|
1223
|
-
const actionButton = canvas.getByTestId('panel1-action');
|
|
1224
|
-
await userEvent.click(actionButton);
|
|
1225
|
-
|
|
1226
|
-
await waitFor(() => {
|
|
1227
|
-
const activePanel = canvas.getByTestId('active-panel');
|
|
1228
|
-
expect(activePanel).toHaveTextContent('Active Panel: panel2');
|
|
1229
|
-
});
|
|
1230
|
-
|
|
1231
|
-
// Check for loading state
|
|
1232
|
-
const loadingText = canvas.getByTestId('loading-text');
|
|
1233
|
-
await expect(loadingText).toBeInTheDocument();
|
|
1234
|
-
});
|
|
1235
|
-
|
|
1236
|
-
await step('Wait for dynamic content to load', async () => {
|
|
1237
|
-
// Wait for the simulated loading to complete
|
|
1238
|
-
await waitFor(
|
|
1239
|
-
() => {
|
|
1240
|
-
// Try to find loaded items
|
|
1241
|
-
const items = canvas.queryAllByTestId(/^loaded-item-/);
|
|
1242
|
-
if (items.length > 0) {
|
|
1243
|
-
expect(items).toHaveLength(3);
|
|
1244
|
-
return true;
|
|
1245
|
-
}
|
|
1246
|
-
// If still loading, that's okay
|
|
1247
|
-
const loadingText = canvas.queryByTestId('loading-text');
|
|
1248
|
-
if (loadingText) {
|
|
1249
|
-
// Still loading, wait more
|
|
1250
|
-
return false;
|
|
1251
|
-
}
|
|
1252
|
-
// Neither loading nor loaded - might be in transition
|
|
1253
|
-
return false;
|
|
1254
|
-
},
|
|
1255
|
-
{ timeout: 5000 },
|
|
1256
|
-
).catch(() => {
|
|
1257
|
-
// If timeout, check that at least the loading state was shown
|
|
1258
|
-
const loadingText = canvas.queryByTestId('loading-text');
|
|
1259
|
-
if (loadingText) {
|
|
1260
|
-
expect(loadingText).toBeInTheDocument();
|
|
1261
|
-
}
|
|
1262
|
-
});
|
|
1263
|
-
});
|
|
1264
|
-
|
|
1265
|
-
await step('Navigate to panel 3', async () => {
|
|
1266
|
-
const actionButton = canvas.getByTestId('panel2-action');
|
|
1267
|
-
await userEvent.click(actionButton);
|
|
1268
|
-
|
|
1269
|
-
await waitFor(() => {
|
|
1270
|
-
const activePanel = canvas.getByTestId('active-panel');
|
|
1271
|
-
expect(activePanel).toHaveTextContent('Active Panel: panel3');
|
|
1272
|
-
});
|
|
1273
|
-
|
|
1274
|
-
// Verify the panel was expanded and has content
|
|
1275
|
-
const panel2Content = canvas.getByTestId('panel2-content');
|
|
1276
|
-
await expect(panel2Content).toBeInTheDocument();
|
|
1277
|
-
});
|
|
1278
|
-
|
|
1279
|
-
await step('Reset all panels', async () => {
|
|
1280
|
-
const resetButton = canvas.getByTestId('reset-action');
|
|
1281
|
-
await userEvent.click(resetButton);
|
|
1282
|
-
|
|
1283
|
-
await waitFor(() => {
|
|
1284
|
-
const activePanel = canvas.getByTestId('active-panel');
|
|
1285
|
-
expect(activePanel).toHaveTextContent('Active Panel: None');
|
|
1286
|
-
});
|
|
1287
|
-
});
|
|
1288
|
-
},
|
|
1289
|
-
};
|
|
1290
|
-
|
|
1291
|
-
// Performance Test
|
|
1292
|
-
export const PerformanceTest: Story = {
|
|
1293
|
-
name: '⚡ Performance Test',
|
|
1294
|
-
render: () => (
|
|
1295
|
-
<Stack spacing={1} data-testid="performance-container">
|
|
1296
|
-
{Array.from({ length: 20 }, (_, i) => (
|
|
1297
|
-
<Accordion key={i} variant="bordered" data-testid={`perf-accordion-${i}`}>
|
|
1298
|
-
<AccordionSummary expandIcon={<ExpandMore />}>
|
|
1299
|
-
<Typography>Performance Test Accordion {i + 1}</Typography>
|
|
1300
|
-
</AccordionSummary>
|
|
1301
|
-
<AccordionDetails>
|
|
1302
|
-
<Stack spacing={1}>
|
|
1303
|
-
{Array.from({ length: 5 }, (_, j) => (
|
|
1304
|
-
<Typography key={j} variant="body2">
|
|
1305
|
-
Content item {j + 1} for accordion {i + 1}
|
|
1306
|
-
</Typography>
|
|
1307
|
-
))}
|
|
1308
|
-
</Stack>
|
|
1309
|
-
</AccordionDetails>
|
|
1310
|
-
</Accordion>
|
|
1311
|
-
))}
|
|
1312
|
-
</Stack>
|
|
1313
|
-
),
|
|
1314
|
-
play: async ({ canvasElement, step }) => {
|
|
1315
|
-
const canvas = within(canvasElement);
|
|
1316
|
-
|
|
1317
|
-
await step('Measure initial render time', async () => {
|
|
1318
|
-
|
|
1319
|
-
// No wall-clock budget: getAllByTestId queries DOM that has already
|
|
1320
|
-
// rendered, so timing around it measured query time, and any threshold is
|
|
1321
|
-
// really a bet on how loaded the runner is. The count is the real check.
|
|
1322
|
-
const accordions = canvas.getAllByTestId(/^perf-accordion-\d+$/);
|
|
1323
|
-
await expect(accordions.length).toBe(20);
|
|
1324
|
-
});
|
|
1325
|
-
|
|
1326
|
-
await step('Test rapid expand/collapse performance', async () => {
|
|
1327
|
-
|
|
1328
|
-
// Expand then collapse each accordion, waiting on the state the click is
|
|
1329
|
-
// supposed to produce rather than sleeping 50ms and hoping it landed.
|
|
1330
|
-
for (let i = 0; i < 5; i++) {
|
|
1331
|
-
const accordion = canvas.getByTestId(`perf-accordion-${i}`);
|
|
1332
|
-
const summary = accordion.querySelector('.MuiAccordionSummary-root');
|
|
1333
|
-
|
|
1334
|
-
if (summary) {
|
|
1335
|
-
await userEvent.click(summary);
|
|
1336
|
-
await waitFor(() => expect(summary).toHaveAttribute('aria-expanded', 'true'));
|
|
1337
|
-
|
|
1338
|
-
await userEvent.click(summary);
|
|
1339
|
-
await waitFor(() => expect(summary).toHaveAttribute('aria-expanded', 'false'));
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
|
-
});
|
|
1343
|
-
},
|
|
1344
|
-
};
|