@12-apps/ui 1.5.0 → 1.5.1

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.
Files changed (229) hide show
  1. package/package.json +16 -8
  2. package/src/button/index.ts +1 -0
  3. package/src/social-login-button/index.ts +1 -0
  4. package/src/{social-login-button.tsx → social-login-button/social-login-button.tsx} +5 -5
  5. package/src/user-avatar/index.ts +1 -0
  6. package/src/{user-avatar.tsx → user-avatar/user-avatar.tsx} +4 -4
  7. package/eslint.config.js +0 -89
  8. package/src/__tests__/data-grid.test.tsx +0 -125
  9. package/src/__tests__/package-wiring.test.ts +0 -41
  10. package/src/__tests__/setup.ts +0 -1
  11. package/src/__tests__/social-login-button.test.tsx +0 -186
  12. package/src/__tests__/user-avatar.test.tsx +0 -195
  13. package/src/__tests__/viewport.test.ts +0 -50
  14. package/src/components/command/__tests__/command.test.tsx +0 -112
  15. package/src/components/data-display/Alert/Alert.stories.tsx +0 -559
  16. package/src/components/data-display/Alert/Alert.test.stories.tsx +0 -707
  17. package/src/components/data-display/AlertDialog/AlertDialog.stories.tsx +0 -362
  18. package/src/components/data-display/AlertDialog/AlertDialog.test.stories.tsx +0 -763
  19. package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.stories.tsx +0 -230
  20. package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.test.stories.tsx +0 -270
  21. package/src/components/data-display/Avatar/Avatar.stories.tsx +0 -721
  22. package/src/components/data-display/Avatar/Avatar.test.stories.tsx +0 -822
  23. package/src/components/data-display/Badge/Badge.stories.tsx +0 -1134
  24. package/src/components/data-display/Badge/Badge.test.stories.tsx +0 -1194
  25. package/src/components/data-display/Banner/Banner.stories.tsx +0 -528
  26. package/src/components/data-display/Banner/Banner.test.stories.tsx +0 -511
  27. package/src/components/data-display/Carousel/Carousel.stories.tsx +0 -607
  28. package/src/components/data-display/Carousel/Carousel.test.stories.tsx +0 -701
  29. package/src/components/data-display/Chart/Chart.stories.tsx +0 -568
  30. package/src/components/data-display/Chart/Chart.test.stories.tsx +0 -174
  31. package/src/components/data-display/Chart/SpecChart.stories.tsx +0 -150
  32. package/src/components/data-display/Chart/SpecChart.test.stories.tsx +0 -56
  33. package/src/components/data-display/Chart/__tests__/SpecChart.test.tsx +0 -60
  34. package/src/components/data-display/Chart/__tests__/chart-format.test.ts +0 -43
  35. package/src/components/data-display/Chart/__tests__/chart-spec.test.ts +0 -119
  36. package/src/components/data-display/Chip/Chip.stories.tsx +0 -252
  37. package/src/components/data-display/Chip/Chip.test.stories.tsx +0 -266
  38. package/src/components/data-display/DataGrid/DataGrid.stories.tsx +0 -515
  39. package/src/components/data-display/DataGrid/DataGrid.test.stories.tsx +0 -653
  40. package/src/components/data-display/DataViews/__tests__/DataViewsGrid.test.tsx +0 -271
  41. package/src/components/data-display/DataViews/__tests__/DataViewsTableBase.test.tsx +0 -134
  42. package/src/components/data-display/DataViews/__tests__/SaveViewModal.test.tsx +0 -83
  43. package/src/components/data-display/DataViews/__tests__/data-views-filter.test.ts +0 -115
  44. package/src/components/data-display/DataViews/__tests__/data-views-preview.test.ts +0 -53
  45. package/src/components/data-display/DataViews/__tests__/range-pill.test.tsx +0 -114
  46. package/src/components/data-display/DataViews/base-card.stories.tsx +0 -136
  47. package/src/components/data-display/DescriptionItem/DescriptionItem.stories.tsx +0 -254
  48. package/src/components/data-display/EmptyState/EmptyState.stories.tsx +0 -282
  49. package/src/components/data-display/EmptyState/EmptyState.test.stories.tsx +0 -441
  50. package/src/components/data-display/ErrorState/ErrorState.stories.tsx +0 -110
  51. package/src/components/data-display/ErrorState/ErrorState.test.stories.tsx +0 -133
  52. package/src/components/data-display/HoverCard/HoverCard.stories.tsx +0 -650
  53. package/src/components/data-display/HoverCard/HoverCard.test.stories.tsx +0 -929
  54. package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.stories.tsx +0 -411
  55. package/src/components/data-display/LazyImage/LazyImage.stories.tsx +0 -387
  56. package/src/components/data-display/Lightbox/Lightbox.stories.tsx +0 -315
  57. package/src/components/data-display/Lightbox/Lightbox.test.stories.tsx +0 -598
  58. package/src/components/data-display/LoadingState/LoadingState.stories.tsx +0 -124
  59. package/src/components/data-display/LoadingState/LoadingState.test.stories.tsx +0 -99
  60. package/src/components/data-display/MapPreview/MapPreview.stories.tsx +0 -458
  61. package/src/components/data-display/MapPreview/MapPreview.test.stories.tsx +0 -394
  62. package/src/components/data-display/Popover/Popover.stories.tsx +0 -295
  63. package/src/components/data-display/Popover/Popover.test.stories.tsx +0 -1162
  64. package/src/components/data-display/Progress/Progress.stories.tsx +0 -481
  65. package/src/components/data-display/Progress/Progress.test.stories.tsx +0 -2070
  66. package/src/components/data-display/SectionOnboarding/SectionOnboarding.stories.tsx +0 -160
  67. package/src/components/data-display/SectionOnboarding/SectionOnboarding.test.stories.tsx +0 -190
  68. package/src/components/data-display/Sheet/Sheet.stories.tsx +0 -883
  69. package/src/components/data-display/Sheet/Sheet.test.stories.tsx +0 -1105
  70. package/src/components/data-display/StatCard/StatCard.stories.tsx +0 -167
  71. package/src/components/data-display/StatCard/StatCard.test.stories.tsx +0 -240
  72. package/src/components/data-display/Stepper/Stepper.stories.tsx +0 -540
  73. package/src/components/data-display/Stepper/Stepper.test.stories.tsx +0 -600
  74. package/src/components/data-display/Table/Table.stories.tsx +0 -803
  75. package/src/components/data-display/Table/Table.test.stories.tsx +0 -369
  76. package/src/components/data-display/Timeline/Timeline.stories.tsx +0 -442
  77. package/src/components/data-display/Timeline/Timeline.test.stories.tsx +0 -930
  78. package/src/components/data-display/TimingDiagram/TimingDiagram.stories.tsx +0 -475
  79. package/src/components/data-display/TimingDiagram/TimingDiagram.test.stories.tsx +0 -734
  80. package/src/components/data-display/Tooltip/Tooltip.stories.tsx +0 -468
  81. package/src/components/data-display/Tooltip/Tooltip.test.stories.tsx +0 -1154
  82. package/src/components/feedback/ConfirmAction/ConfirmAction.stories.tsx +0 -146
  83. package/src/components/feedback/ConfirmAction/ConfirmAction.test.stories.tsx +0 -114
  84. package/src/components/feedback/ConfirmAction/__tests__/confirm-action.test.tsx +0 -288
  85. package/src/components/feedback/Dialog/Dialog.stories.tsx +0 -833
  86. package/src/components/feedback/Dialog/Dialog.test.stories.tsx +0 -964
  87. package/src/components/feedback/Dialog/__tests__/dialog-content-spacing.test.tsx +0 -66
  88. package/src/components/feedback/Modal/Modal.stories.tsx +0 -765
  89. package/src/components/feedback/Modal/Modal.test.stories.tsx +0 -251
  90. package/src/components/feedback/Sonner/Sonner.stories.tsx +0 -698
  91. package/src/components/feedback/Sonner/Sonner.test.stories.tsx +0 -896
  92. package/src/components/feedback/StackedModal/StackedModal.stories.tsx +0 -1783
  93. package/src/components/feedback/StackedModal/StackedModal.test.stories.tsx +0 -973
  94. package/src/components/feedback/StackedModal/__tests__/stacked-modal-viewport.test.tsx +0 -104
  95. package/src/components/feedback/Toast/Toast.stories.tsx +0 -548
  96. package/src/components/feedback/Toast/Toast.test.stories.tsx +0 -834
  97. package/src/components/feedback/TutorialOverlay/TutorialOverlay.stories.tsx +0 -691
  98. package/src/components/feedback/TutorialOverlay/TutorialOverlay.test.stories.tsx +0 -634
  99. package/src/components/form/AddressAutocomplete/AddressAutocomplete.stories.tsx +0 -665
  100. package/src/components/form/AddressAutocomplete/AddressAutocomplete.test.stories.tsx +0 -727
  101. package/src/components/form/Autocomplete/Autocomplete.stories.tsx +0 -715
  102. package/src/components/form/Autocomplete/Autocomplete.test.stories.tsx +0 -825
  103. package/src/components/form/Button/Button.stories.tsx +0 -365
  104. package/src/components/form/Button/Button.test.stories.tsx +0 -607
  105. package/src/components/form/Calendar/Calendar.stories.tsx +0 -349
  106. package/src/components/form/Calendar/Calendar.test.stories.tsx +0 -464
  107. package/src/components/form/CepField/CepField.stories.tsx +0 -138
  108. package/src/components/form/CepField/CepField.test.stories.tsx +0 -281
  109. package/src/components/form/Checkbox/Checkbox.stories.tsx +0 -265
  110. package/src/components/form/Checkbox/Checkbox.test.stories.tsx +0 -181
  111. package/src/components/form/CodeEditor/CodeEditor.stories.tsx +0 -871
  112. package/src/components/form/CodeEditor/CodeEditor.test.stories.tsx +0 -574
  113. package/src/components/form/Command/Command.stories.tsx +0 -580
  114. package/src/components/form/Command/Command.test.stories.tsx +0 -215
  115. package/src/components/form/CreatableSelect/CreatableSelect.stories.tsx +0 -112
  116. package/src/components/form/CreatableSelect/CreatableSelect.test.stories.tsx +0 -134
  117. package/src/components/form/Form/Form.stories.tsx +0 -687
  118. package/src/components/form/Form/Form.test.stories.tsx +0 -601
  119. package/src/components/form/Input/Input.stories.tsx +0 -260
  120. package/src/components/form/Input/Input.test.stories.tsx +0 -1191
  121. package/src/components/form/InputOTP/InputOTP.stories.tsx +0 -774
  122. package/src/components/form/InputOTP/InputOTP.test.stories.tsx +0 -588
  123. package/src/components/form/Label/Label.stories.tsx +0 -473
  124. package/src/components/form/Label/Label.test.stories.tsx +0 -915
  125. package/src/components/form/Menubar/Menubar.stories.tsx +0 -472
  126. package/src/components/form/Menubar/Menubar.test.stories.tsx +0 -584
  127. package/src/components/form/PasswordStrength/PasswordStrength.stories.tsx +0 -562
  128. package/src/components/form/PasswordStrength/PasswordStrength.test.stories.tsx +0 -327
  129. package/src/components/form/PhoneInput/PhoneInput.stories.tsx +0 -495
  130. package/src/components/form/PhoneInput/PhoneInput.test.stories.tsx +0 -666
  131. package/src/components/form/RadioGroup/RadioGroup.stories.tsx +0 -518
  132. package/src/components/form/RadioGroup/RadioGroup.test.stories.tsx +0 -685
  133. package/src/components/form/RichTextEditor/RichTextEditor.stories.tsx +0 -261
  134. package/src/components/form/RichTextEditor/RichTextEditor.test.stories.tsx +0 -491
  135. package/src/components/form/SearchPalette/SearchPalette.stories.tsx +0 -135
  136. package/src/components/form/Select/Select.stories.tsx +0 -364
  137. package/src/components/form/Select/Select.test.stories.tsx +0 -985
  138. package/src/components/form/Slider/Slider.stories.tsx +0 -418
  139. package/src/components/form/Slider/Slider.test.stories.tsx +0 -1042
  140. package/src/components/form/Switch/Switch.stories.tsx +0 -697
  141. package/src/components/form/Switch/Switch.test.stories.tsx +0 -597
  142. package/src/components/form/Textarea/Textarea.stories.tsx +0 -473
  143. package/src/components/form/Textarea/Textarea.test.stories.tsx +0 -702
  144. package/src/components/form/Toggle/Toggle.stories.tsx +0 -638
  145. package/src/components/form/Toggle/Toggle.test.stories.tsx +0 -1159
  146. package/src/components/form/ToggleGroup/ToggleGroup.stories.tsx +0 -617
  147. package/src/components/form/ToggleGroup/ToggleGroup.test.stories.tsx +0 -915
  148. package/src/components/form/UploadButton/UploadButton.stories.tsx +0 -367
  149. package/src/components/form/UploadButton/UploadButton.test.stories.tsx +0 -573
  150. package/src/components/form/total-form/total-form.test.tsx +0 -214
  151. package/src/components/layout/Accordion/Accordion.stories.tsx +0 -694
  152. package/src/components/layout/Accordion/Accordion.test.stories.tsx +0 -1344
  153. package/src/components/layout/Card/Card.stories.tsx +0 -591
  154. package/src/components/layout/Card/Card.test.stories.tsx +0 -1134
  155. package/src/components/layout/CardGrid/__tests__/CardGrid.test.tsx +0 -92
  156. package/src/components/layout/Collapsible/Collapsible.stories.tsx +0 -180
  157. package/src/components/layout/Collapsible/Collapsible.test.stories.tsx +0 -308
  158. package/src/components/layout/Container/Container.stories.tsx +0 -557
  159. package/src/components/layout/Container/Container.test.stories.tsx +0 -745
  160. package/src/components/layout/ContentToolbar/ContentToolbar.stories.tsx +0 -129
  161. package/src/components/layout/ContentToolbar/ContentToolbar.test.stories.tsx +0 -251
  162. package/src/components/layout/ContentToolbar/__tests__/ColumnsMenu.test.tsx +0 -39
  163. package/src/components/layout/ContentToolbar/__tests__/MultiSelectDropdown.test.tsx +0 -131
  164. package/src/components/layout/ContentToolbar/__tests__/SavedViewsMenu.test.tsx +0 -99
  165. package/src/components/layout/Dashboard/Dashboard.stories.tsx +0 -104
  166. package/src/components/layout/Dashboard/Dashboard.test.stories.tsx +0 -190
  167. package/src/components/layout/Drawer/Drawer.stories.tsx +0 -658
  168. package/src/components/layout/Drawer/Drawer.test.stories.tsx +0 -1343
  169. package/src/components/layout/Resizable/Resizable.stories.tsx +0 -1039
  170. package/src/components/layout/Resizable/Resizable.test.stories.tsx +0 -754
  171. package/src/components/layout/ScrollArea/ScrollArea.stories.tsx +0 -651
  172. package/src/components/layout/ScrollArea/ScrollArea.test.stories.tsx +0 -619
  173. package/src/components/layout/Separator/Separator.stories.tsx +0 -563
  174. package/src/components/layout/Separator/Separator.test.stories.tsx +0 -518
  175. package/src/components/layout/SettingsLayout/SettingsLayout.stories.tsx +0 -192
  176. package/src/components/layout/SettingsLayout/SettingsLayout.test.stories.tsx +0 -175
  177. package/src/components/layout/Sidebar/Sidebar.stories.tsx +0 -609
  178. package/src/components/layout/Sidebar/Sidebar.test.stories.tsx +0 -1324
  179. package/src/components/layout/Skeleton/Skeleton.stories.tsx +0 -689
  180. package/src/components/layout/Skeleton/Skeleton.test.stories.tsx +0 -521
  181. package/src/components/layout/Spacer/Spacer.stories.tsx +0 -502
  182. package/src/components/layout/Spacer/Spacer.test.stories.tsx +0 -474
  183. package/src/components/layout/TableFilter/TableFilter.stories.tsx +0 -82
  184. package/src/components/layout/TableFilter/TableFilter.test.stories.tsx +0 -87
  185. package/src/components/navigation/Breadcrumbs/Breadcrumbs.stories.tsx +0 -638
  186. package/src/components/navigation/Breadcrumbs/Breadcrumbs.test.stories.tsx +0 -1008
  187. package/src/components/navigation/CommandPalette/CommandPalette.stories.tsx +0 -386
  188. package/src/components/navigation/CommandPalette/CommandPalette.test.stories.tsx +0 -435
  189. package/src/components/navigation/ContextMenu/ContextMenu.stories.tsx +0 -614
  190. package/src/components/navigation/ContextMenu/ContextMenu.test.stories.tsx +0 -845
  191. package/src/components/navigation/DropdownMenu/DropdownMenu.stories.tsx +0 -578
  192. package/src/components/navigation/DropdownMenu/DropdownMenu.test.stories.tsx +0 -660
  193. package/src/components/navigation/NavigationMenu/stories/NavigationMenu.stories.tsx +0 -516
  194. package/src/components/navigation/NavigationMenu/stories/NavigationMenu.test.stories.tsx +0 -935
  195. package/src/components/navigation/NavigationMenu/stories/NavigationMenuHorizontal.stories.tsx +0 -501
  196. package/src/components/navigation/NavigationMenu/stories/NavigationMenuVertical.stories.tsx +0 -453
  197. package/src/components/navigation/Pagination/Pagination.stories.tsx +0 -602
  198. package/src/components/navigation/Pagination/Pagination.test.stories.tsx +0 -520
  199. package/src/components/navigation/Tabs/Tabs.stories.tsx +0 -1004
  200. package/src/components/navigation/Tabs/Tabs.test.stories.tsx +0 -1225
  201. package/src/components/typography/Blockquote/Blockquote.stories.tsx +0 -359
  202. package/src/components/typography/Blockquote/Blockquote.test.stories.tsx +0 -432
  203. package/src/components/typography/Code/Code.stories.tsx +0 -686
  204. package/src/components/typography/Code/Code.test.stories.tsx +0 -703
  205. package/src/components/typography/Heading/Heading.stories.tsx +0 -451
  206. package/src/components/typography/Heading/Heading.test.stories.tsx +0 -830
  207. package/src/components/typography/Paragraph/Paragraph.stories.tsx +0 -280
  208. package/src/components/typography/Paragraph/Paragraph.test.stories.tsx +0 -231
  209. package/src/components/typography/Text/Text.stories.tsx +0 -359
  210. package/src/components/typography/Text/Text.test.stories.tsx +0 -759
  211. package/src/components/utility/AnimatedIcon/AnimatedIcon.stories.tsx +0 -945
  212. package/src/components/utility/AnimatedIcon/AnimatedIcon.test.stories.tsx +0 -603
  213. package/src/components/utility/AspectRatio/AspectRatio.stories.tsx +0 -414
  214. package/src/components/utility/AspectRatio/AspectRatio.test.stories.tsx +0 -859
  215. package/src/components/utility/InfiniteScroll/InfiniteScroll.stories.tsx +0 -620
  216. package/src/components/utility/InfiniteScroll/InfiniteScroll.test.stories.tsx +0 -361
  217. package/src/components/utility/InstallPrompt/InstallPrompt.stories.tsx +0 -118
  218. package/src/components/utility/InstallPrompt/InstallPrompt.test.stories.tsx +0 -83
  219. package/src/components/utility/InstallPrompt/__tests__/InstallPrompt.ios.test.tsx +0 -65
  220. package/src/components/utility/InstallPrompt/__tests__/usePwaInstall.test.tsx +0 -271
  221. package/src/components/utility/Portal/Portal.stories.tsx +0 -340
  222. package/src/components/utility/Portal/Portal.test.stories.tsx +0 -2086
  223. package/src/components/utility/Transition/Transition.stories.tsx +0 -493
  224. package/src/components/utility/Transition/Transition.test.stories.tsx +0 -854
  225. package/src/components/utility/VirtualList/VirtualList.stories.tsx +0 -1073
  226. package/src/components/utility/VirtualList/VirtualList.test.stories.tsx +0 -1085
  227. package/src/components/utility/WorkflowStep/WorkflowStep.stories.tsx +0 -431
  228. package/src/components/utility/WorkflowStep/WorkflowStep.test.stories.tsx +0 -364
  229. /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
- };