@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.
Files changed (229) hide show
  1. package/package.json +17 -9
  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,930 +0,0 @@
1
- import CheckCircle from '@mui/icons-material/CheckCircle';
2
- import ErrorIcon from '@mui/icons-material/Error';
3
- import Info from '@mui/icons-material/Info';
4
- import Warning from '@mui/icons-material/Warning';
5
- import type { Meta, StoryObj } from '@storybook/react-vite';
6
- import { expect, fn,Mock,userEvent, waitFor, within } from 'storybook/test';
7
-
8
- import { Timeline } from './Timeline';
9
- import type { TimelineItem } from './Timeline.types';
10
-
11
- const meta: Meta<typeof Timeline> = {
12
- title: 'Enhanced/Timeline/Tests',
13
- component: Timeline,
14
- parameters: {
15
- layout: 'centered',
16
- chromatic: { disableSnapshot: false },
17
- },
18
- tags: ['autodocs', 'test', 'component:Timeline'],
19
- };
20
-
21
- export default meta;
22
- type Story = StoryObj<typeof meta>;
23
-
24
- // Sample data for testing
25
- const sampleItems: TimelineItem[] = [
26
- {
27
- id: '1',
28
- title: 'Order Placed',
29
- description: 'Your order has been successfully placed',
30
- timestamp: '2024-01-01 10:00 AM',
31
- icon: <CheckCircle />,
32
- color: '#4caf50',
33
- metadata: { 'Order ID': '#12345', Status: 'Confirmed' },
34
- action: {
35
- label: 'View Details',
36
- onClick: fn(),
37
- },
38
- },
39
- {
40
- id: '2',
41
- title: 'Processing',
42
- description: 'Your order is being processed',
43
- timestamp: '2024-01-01 11:00 AM',
44
- icon: <Info />,
45
- color: '#2196f3',
46
- metadata: { Location: 'Warehouse A', Expected: '2 hours' },
47
- },
48
- {
49
- id: '3',
50
- title: 'Quality Check',
51
- description: 'Items are undergoing quality inspection',
52
- timestamp: '2024-01-01 02:00 PM',
53
- icon: <Warning />,
54
- color: '#ff9800',
55
- },
56
- {
57
- id: '4',
58
- title: 'Shipped',
59
- description: 'Your order has been shipped',
60
- timestamp: '2024-01-02 09:00 AM',
61
- icon: <CheckCircle />,
62
- color: '#4caf50',
63
- metadata: { Tracking: 'ABC123XYZ', Carrier: 'FedEx' },
64
- },
65
- ];
66
-
67
- // Test Stories
68
-
69
- export const BasicInteraction: Story = {
70
- args: {
71
- items: sampleItems,
72
- variant: 'default',
73
- orientation: 'vertical',
74
- onItemClick: fn(),
75
- },
76
- play: async ({ canvasElement, args }) => {
77
- const canvas = within(canvasElement);
78
-
79
- // Verify all timeline items are rendered
80
- const timelineItems = canvas.getAllByRole('article');
81
- await expect(timelineItems).toHaveLength(sampleItems.length);
82
-
83
- // Verify each item displays its title and timestamp
84
- for (const item of sampleItems) {
85
- await expect(canvas.getByText(item.title)).toBeInTheDocument();
86
- await expect(canvas.getByText(item.timestamp)).toBeInTheDocument();
87
- }
88
-
89
- // Test clicking on timeline items
90
- const firstItem = timelineItems[0];
91
- await userEvent.click(firstItem);
92
- await expect(args.onItemClick).toHaveBeenCalledWith(sampleItems[0]);
93
- await expect(args.onItemClick).toHaveBeenCalledTimes(1);
94
-
95
- // Reset mock
96
- (args?.onItemClick as Mock)?.mockClear?.();
97
-
98
- // Test expand/collapse functionality for items with descriptions
99
- const expandButtons = Array.from(canvasElement.querySelectorAll('button')).filter(
100
- (btn) => btn.querySelector('svg[data-testid="ExpandMoreIcon"]')
101
- );
102
-
103
- await expect(expandButtons.length).toBeGreaterThan(0);
104
-
105
- // Initially collapsed - description should not be visible
106
- const firstDescription = sampleItems[0].description!;
107
- await waitFor(() => expect(canvas.queryByText(firstDescription)).not.toBeVisible());
108
-
109
- // Click to expand
110
- await userEvent.click(expandButtons[0]);
111
-
112
- // Wait for expansion animation and verify description is now visible
113
- await waitFor(() => {
114
- expect(canvas.getByText(firstDescription)).toBeVisible();
115
- }, { timeout: 3000 });
116
-
117
- // Verify the expand button rotated (indicates expanded state)
118
- // Check the computed style includes rotation (will be a matrix in computed styles)
119
- const buttonStyle = window.getComputedStyle(expandButtons[0]);
120
- await expect(buttonStyle.transform).not.toBe('none');
121
-
122
- // Click to collapse
123
- await userEvent.click(expandButtons[0]);
124
-
125
- // Wait for collapse animation
126
- await waitFor(() => {
127
- expect(canvas.queryByText(firstDescription)).not.toBeVisible();
128
- }, { timeout: 3000 });
129
-
130
- // Verify the expand button rotated back
131
- // The collapsed state should not have the same transform as when expanded
132
- await waitFor(() => {
133
- const _collapsedButtonStyle = window.getComputedStyle(expandButtons[0]);
134
- const _expandedButtonStyle = window.getComputedStyle(expandButtons[0]);
135
- // Just verify the animation has completed - description is not visible
136
- expect(canvas.queryByText(firstDescription)).not.toBeVisible();
137
- });
138
- },
139
- };
140
-
141
- export const FormInteraction: Story = {
142
- args: {
143
- items: sampleItems.map((item) => ({
144
- ...item,
145
- action: {
146
- label: `Action ${item.id}`,
147
- onClick: fn(),
148
- },
149
- })),
150
- variant: 'detailed',
151
- },
152
- play: async ({ canvasElement, args }) => {
153
- const canvas = within(canvasElement);
154
-
155
- // In detailed variant, all descriptions should be visible
156
- // Wait for animations to complete
157
- await waitFor(async () => {
158
- for (const item of args.items) {
159
- if (item.description) {
160
- const element = canvas.getByText(item.description);
161
- expect(element).toBeVisible();
162
- }
163
- }
164
- }, { timeout: 5000 });
165
-
166
- // Test action button clicks
167
- const actionButtons = Array.from(canvasElement.querySelectorAll('button')).filter(
168
- (btn) => btn.textContent?.includes('Action')
169
- );
170
-
171
- await expect(actionButtons).toHaveLength(args.items.length);
172
-
173
- // Test each action button
174
- for (let i = 0; i < actionButtons.length; i++) {
175
- const button = actionButtons[i];
176
- const expectedLabel = `Action ${args.items[i].id}`;
177
-
178
- // Verify button has correct label
179
- await expect(button).toHaveTextContent(expectedLabel);
180
-
181
- // Click the button
182
- await userEvent.click(button);
183
-
184
- // Verify the corresponding onClick was called
185
- await expect(args.items[i].action?.onClick).toHaveBeenCalledTimes(1);
186
-
187
- // Reset for next iteration
188
- (args.items[i].action?.onClick as Mock)?.mockClear?.();
189
- }
190
-
191
- // Test that action button clicks don't trigger item click
192
- const firstActionButton = actionButtons[0];
193
- const itemClickMock = fn();
194
- args.onItemClick = itemClickMock;
195
-
196
- await userEvent.click(firstActionButton);
197
- await expect(itemClickMock).not.toHaveBeenCalled();
198
-
199
- // Verify metadata is displayed in detailed variant
200
- for (const item of args.items) {
201
- if (item.metadata) {
202
- for (const [key, value] of Object.entries(item.metadata)) {
203
- const metadataText = `${key}: ${value}`;
204
- await expect(canvas.getByText(metadataText)).toBeInTheDocument();
205
- }
206
- }
207
- }
208
- },
209
- };
210
-
211
- export const KeyboardNavigation: Story = {
212
- args: {
213
- items: sampleItems,
214
- variant: 'default',
215
- orientation: 'vertical',
216
- onItemClick: fn(),
217
- },
218
- play: async ({ canvasElement, args }) => {
219
- const canvas = within(canvasElement);
220
-
221
- // Wait for timeline to render
222
- await waitFor(() => {
223
- const cards = canvas.getAllByRole('article');
224
- expect(cards).toHaveLength(sampleItems.length);
225
- }, { timeout: 3000 });
226
-
227
- // Get all interactive elements
228
- const cards = canvas.getAllByRole('article');
229
- const expandButtons = Array.from(canvasElement.querySelectorAll('button')).filter(
230
- (btn) => btn.querySelector('svg[data-testid="ExpandMoreIcon"]')
231
- );
232
- // We don't need actionButtons in this test but keep for potential use
233
- // const actionButtons = Array.from(canvasElement.querySelectorAll('button')).filter(
234
- // (btn) => btn.textContent?.includes('View Details')
235
- // );
236
-
237
- // Test Tab navigation through timeline cards
238
- // Find the first expand button and click it to test expansion
239
- await userEvent.click(expandButtons[0]);
240
- await waitFor(() => {
241
- const description = canvas.getByText(sampleItems[0].description!);
242
- expect(description).toBeVisible();
243
- }, { timeout: 3000 });
244
-
245
- // Verify button rotation indicates expanded state (transform will be a matrix in computed styles)
246
- const expandedButtonStyle = window.getComputedStyle(expandButtons[0]);
247
- await expect(expandedButtonStyle.transform).not.toBe('none');
248
-
249
- // Test action button with keyboard
250
- const actionButton = canvas.getByRole('button', { name: /View Details/i });
251
- await userEvent.click(actionButton);
252
- await expect(args.items[0].action?.onClick).toHaveBeenCalledTimes(1);
253
-
254
- // Click expand button to collapse
255
- await userEvent.click(expandButtons[0]);
256
- await waitFor(() => {
257
- const description = canvas.queryByText(sampleItems[0].description!);
258
- expect(description).not.toBeVisible();
259
- }, { timeout: 3000 });
260
-
261
- // Test clicking card with Enter key
262
- // Click the card instead since cards aren't naturally focusable
263
- await userEvent.click(cards[1]);
264
- await expect(args.onItemClick).toHaveBeenCalledWith(sampleItems[1]);
265
- },
266
- };
267
-
268
- export const ScreenReader: Story = {
269
- args: {
270
- items: sampleItems,
271
- variant: 'detailed',
272
- },
273
- play: async ({ canvasElement }) => {
274
- const canvas = within(canvasElement);
275
-
276
- // Wait for timeline to render
277
- await waitFor(() => {
278
- const cards = canvas.getAllByRole('article');
279
- expect(cards).toHaveLength(sampleItems.length);
280
- }, { timeout: 3000 });
281
-
282
- // Verify timeline structure is accessible with semantic HTML
283
- const cards = canvas.getAllByRole('article');
284
- await expect(cards).toHaveLength(sampleItems.length);
285
-
286
- // Verify all text content is accessible
287
- for (const item of sampleItems) {
288
- // Title and timestamp should be readable
289
- await expect(canvas.getByText(item.title)).toBeInTheDocument();
290
- await expect(canvas.getByText(item.timestamp)).toBeInTheDocument();
291
-
292
- // Verify heading hierarchy
293
- const headings = canvasElement.querySelectorAll('h6');
294
- const titleHeading = Array.from(headings).find(h => h.textContent === item.title);
295
- await expect(titleHeading).toBeInTheDocument();
296
- }
297
-
298
- // Verify interactive elements have accessible names
299
- const expandButtons = Array.from(canvasElement.querySelectorAll('button')).filter(
300
- (btn) => btn.querySelector('svg[data-testid="ExpandMoreIcon"]')
301
- );
302
-
303
- for (const button of expandButtons) {
304
- // Button should be focusable
305
- await expect(button).not.toHaveAttribute('disabled');
306
- await expect(button).not.toHaveAttribute('tabindex', '-1');
307
- }
308
-
309
- // Verify icons are decorative (aria-hidden) or have labels
310
- const icons = canvasElement.querySelectorAll('svg');
311
- await expect(icons.length).toBeGreaterThan(0);
312
-
313
- // Timeline dots should have color indication
314
- const dots = canvasElement.querySelectorAll('[data-testid="timeline-dot"]');
315
- await expect(dots).toHaveLength(sampleItems.length);
316
-
317
- for (let i = 0; i < dots.length; i++) {
318
- const dot = dots[i] as HTMLElement;
319
- const item = sampleItems[i];
320
- if (item.color) {
321
- const style = window.getComputedStyle(dot);
322
- await expect(style.background).toContain('gradient');
323
- }
324
- }
325
-
326
- // Verify connectors are purely decorative
327
- const connectors = canvasElement.querySelectorAll('[data-testid="timeline-connector"]');
328
- await expect(connectors).toHaveLength(sampleItems.length - 1);
329
- },
330
- };
331
-
332
- export const FocusManagement: Story = {
333
- args: {
334
- items: sampleItems,
335
- variant: 'default',
336
- onItemClick: fn(),
337
- },
338
- play: async ({ canvasElement, args }) => {
339
- const canvas = within(canvasElement);
340
-
341
- // Wait for timeline to render
342
- await waitFor(() => {
343
- const cards = canvas.getAllByRole('article');
344
- expect(cards).toHaveLength(sampleItems.length);
345
- }, { timeout: 3000 });
346
-
347
- // Get interactive elements
348
- const expandButtons = Array.from(canvasElement.querySelectorAll('button')).filter(
349
- (btn) => btn.querySelector('svg[data-testid="ExpandMoreIcon"]')
350
- );
351
- const cards = canvas.getAllByRole('article');
352
-
353
- // Test focus retention on expand/collapse
354
- await expect(expandButtons.length).toBeGreaterThan(0);
355
-
356
- // Tab in from the body to reach the first expand button.
357
- await userEvent.tab();
358
- await waitFor(() => expect(expandButtons[0]).toHaveFocus());
359
-
360
- // Click to expand
361
- await userEvent.click(expandButtons[0]);
362
-
363
- // Focus should remain on the button after expansion
364
- await waitFor(() => expect(expandButtons[0]).toHaveFocus());
365
-
366
- // Wait for expansion to complete
367
- await waitFor(() => {
368
- const description = canvas.getByText(sampleItems[0].description!);
369
- expect(description).toBeVisible();
370
- }, { timeout: 3000 });
371
-
372
- // Now action button should be available
373
- const actionButton = canvas.getByRole('button', { name: /View Details/i });
374
-
375
- // Clicking focuses the button, so the pre-focus only asserted focus() works.
376
- await userEvent.click(actionButton);
377
- await waitFor(() => expect(actionButton).toHaveFocus());
378
- await expect(args.items[0].action?.onClick).toHaveBeenCalled();
379
-
380
- // Test focus on card click
381
- await userEvent.click(cards[0]);
382
- await expect(args.onItemClick).toHaveBeenCalled();
383
-
384
- // Test focus trap prevention (focus can move outside component)
385
- const lastExpandButton = expandButtons[expandButtons.length - 1];
386
-
387
- // Walk the keyboard to the last expand button, then once more: focus has to
388
- // leave the timeline rather than wrap back into it.
389
- await userEvent.click(lastExpandButton);
390
- await waitFor(() => expect(lastExpandButton).toHaveFocus());
391
-
392
- await userEvent.tab();
393
- await waitFor(() => expect(lastExpandButton).not.toHaveFocus());
394
-
395
- // Clicking the second item's expand button focuses it and keeps focus there.
396
- await userEvent.click(expandButtons[1]);
397
- await waitFor(() => expect(expandButtons[1]).toHaveFocus());
398
-
399
- // Verify multiple items can be expanded simultaneously
400
- await waitFor(() => {
401
- const firstDescription = canvas.getByText(sampleItems[0].description!);
402
- const secondDescription = canvas.getByText(sampleItems[1].description!);
403
- expect(firstDescription).toBeVisible();
404
- expect(secondDescription).toBeVisible();
405
- }, { timeout: 3000 });
406
- },
407
- };
408
-
409
- export const ResponsiveDesign: Story = {
410
- args: {
411
- items: sampleItems,
412
- variant: 'default',
413
- orientation: 'vertical',
414
- onItemClick: fn(),
415
- },
416
- parameters: {
417
- viewport: { defaultViewport: 'mobile1' },
418
- },
419
- play: async ({ canvasElement }) => {
420
- const canvas = within(canvasElement);
421
-
422
- // Verify timeline renders on mobile - wait longer for mobile viewport
423
- // Read the container after the wait rather than assigning to an outer
424
- // binding from inside the callback — the callback reruns, so the binding
425
- // held whichever value the last retry happened to leave behind.
426
- await waitFor(() => {
427
- expect(canvas.getByTestId('timeline-container')).toBeInTheDocument();
428
- }, { timeout: 5000 });
429
- const container = canvas.getByTestId('timeline-container');
430
-
431
- // Wait for all items to render
432
- await waitFor(() => {
433
- const items = canvas.getAllByRole('article');
434
- expect(items).toHaveLength(sampleItems.length);
435
- }, { timeout: 3000 });
436
-
437
- // Verify all items are rendered
438
- const items = canvas.getAllByRole('article');
439
- await expect(items).toHaveLength(sampleItems.length);
440
-
441
- // Check vertical orientation layout
442
- const containerStyle = window.getComputedStyle(container);
443
- await expect(containerStyle.flexDirection).toBe('column');
444
- await expect(containerStyle.display).toBe('flex');
445
-
446
- // Verify items stack vertically with proper spacing
447
- const itemContainers = canvas.getAllByTestId('timeline-item-container');
448
- await expect(itemContainers).toHaveLength(sampleItems.length);
449
-
450
- // Check each item's layout
451
- for (let i = 0; i < itemContainers.length; i++) {
452
- const itemContainer = itemContainers[i] as HTMLElement;
453
- const itemStyle = window.getComputedStyle(itemContainer);
454
-
455
- // Items should use flex layout
456
- await expect(itemStyle.display).toBe('flex');
457
-
458
- // Gap should be applied
459
- await expect(itemStyle.gap).toBeTruthy();
460
- }
461
-
462
- // Verify text doesn't overflow on mobile
463
- const titles = canvasElement.querySelectorAll('h6');
464
- for (const title of titles) {
465
- const titleElement = title as HTMLElement;
466
- const rect = titleElement.getBoundingClientRect();
467
- const containerRect = (container as HTMLElement).getBoundingClientRect();
468
-
469
- // Title should be within container bounds
470
- await expect(rect.right).toBeLessThanOrEqual(containerRect.right);
471
- await expect(rect.left).toBeGreaterThanOrEqual(containerRect.left);
472
- }
473
-
474
- // Test touch interactions work on mobile
475
- const firstCard = items[0];
476
- await userEvent.click(firstCard);
477
-
478
- // Expand buttons should be large enough for touch
479
- const expandButtons = Array.from(canvasElement.querySelectorAll('button')).filter(
480
- (btn) => btn.querySelector('svg[data-testid="ExpandMoreIcon"]')
481
- );
482
-
483
- for (const button of expandButtons) {
484
- const rect = button.getBoundingClientRect();
485
- // Minimum touch target size (44x44 pixels recommended)
486
- await expect(rect.width).toBeGreaterThanOrEqual(32);
487
- await expect(rect.height).toBeGreaterThanOrEqual(32);
488
- }
489
- },
490
- };
491
-
492
- export const ThemeVariations: Story = {
493
- args: {
494
- items: sampleItems,
495
- variant: 'detailed',
496
- },
497
- parameters: {
498
- backgrounds: { default: 'dark' },
499
- },
500
- play: async ({ canvasElement }) => {
501
- const canvas = within(canvasElement);
502
-
503
- // Wait for timeline to render
504
- await waitFor(() => {
505
- const cards = canvas.getAllByRole('article');
506
- expect(cards).toHaveLength(sampleItems.length);
507
- }, { timeout: 3000 });
508
-
509
- // Verify timeline renders in dark theme
510
- const cards = canvas.getAllByRole('article');
511
- await expect(cards).toHaveLength(sampleItems.length);
512
-
513
- // Verify custom colors are applied to timeline dots
514
- const dots = canvas.getAllByTestId('timeline-dot');
515
- await expect(dots).toHaveLength(sampleItems.length);
516
-
517
- for (let i = 0; i < dots.length; i++) {
518
- const dot = dots[i] as HTMLElement;
519
- const item = sampleItems[i];
520
- const style = window.getComputedStyle(dot);
521
-
522
- // Verify gradient background is applied
523
- await expect(style.background).toContain('gradient');
524
-
525
- // Verify custom color is used if provided
526
- if (item.color) {
527
- // Check box-shadow uses the custom color
528
- await expect(style.boxShadow).toBeTruthy();
529
- }
530
-
531
- // Verify border for contrast
532
- await expect(style.border).toBeTruthy();
533
-
534
- // Verify dot has proper dimensions
535
- await expect(style.width).toBe('40px');
536
- await expect(style.height).toBe('40px');
537
- await expect(style.borderRadius).toBe('50%');
538
- }
539
-
540
- // Verify cards have glass morphism effect
541
- const timelineCards = canvas.getAllByTestId('timeline-card');
542
- for (const card of timelineCards) {
543
- const cardElement = card as HTMLElement;
544
- const style = window.getComputedStyle(cardElement) as ReturnType<typeof window.getComputedStyle> & { webkitBackdropFilter?: string };
545
-
546
- // Check glass effect properties
547
- await expect(style.backdropFilter || style.webkitBackdropFilter).toContain('blur');
548
- await expect(style.background).toContain('gradient');
549
- await expect(style.border).toBeTruthy();
550
- }
551
-
552
- // Verify text contrast in dark theme
553
- const timestamps = canvas.getAllByTestId('timeline-timestamp');
554
- for (const timestamp of timestamps) {
555
- const element = timestamp as HTMLElement;
556
- const style = window.getComputedStyle(element);
557
-
558
- // Timestamp should have secondary text color
559
- await expect(style.color).toBeTruthy();
560
- await expect(style.fontSize).toBe('12px'); // Computed value, not CSS value
561
- await expect(style.textTransform).toBe('uppercase');
562
- }
563
-
564
- // Verify connector gradient
565
- const connectors = canvas.getAllByTestId('timeline-connector');
566
- for (const connector of connectors) {
567
- const element = connector as HTMLElement;
568
- const style = window.getComputedStyle(element);
569
- await expect(style.background).toContain('gradient');
570
- }
571
- },
572
- };
573
-
574
- export const VisualStates: Story = {
575
- args: {
576
- items: [
577
- ...sampleItems,
578
- {
579
- id: '5',
580
- title: 'Error State',
581
- description: 'An error occurred',
582
- timestamp: '2024-01-02 10:00 AM',
583
- icon: <ErrorIcon />,
584
- color: '#f44336',
585
- },
586
- ],
587
- variant: 'default',
588
- animated: true,
589
- alternating: true,
590
- },
591
- play: async ({ canvasElement }) => {
592
- const canvas = within(canvasElement);
593
- const expectedItemCount = sampleItems.length + 1; // Including the error state item
594
-
595
- // Wait for timeline to render with animations
596
- await waitFor(() => {
597
- const cards = canvas.getAllByRole('article');
598
- expect(cards).toHaveLength(expectedItemCount);
599
- }, { timeout: 3000 });
600
-
601
- // Verify all items including error state are rendered
602
- const cards = canvas.getAllByRole('article');
603
- await expect(cards).toHaveLength(expectedItemCount);
604
-
605
- // Test hover state on cards - verify cursor is set for interactivity
606
- const firstCard = cards[0];
607
- const initialStyle = window.getComputedStyle(firstCard);
608
- await expect(initialStyle.cursor).toBe('pointer');
609
-
610
- // Note: userEvent.hover() doesn't trigger CSS :hover pseudo-class
611
- // The hover transform is verified visually and applied via CSS :hover
612
-
613
- // Verify alternating layout
614
- const itemContainers = canvas.getAllByTestId('timeline-item-container');
615
- await expect(itemContainers).toHaveLength(expectedItemCount);
616
-
617
- for (let i = 0; i < itemContainers.length; i++) {
618
- const container = itemContainers[i] as HTMLElement;
619
- const style = window.getComputedStyle(container);
620
-
621
- // Check alternating flex direction
622
- if (i % 2 === 0) {
623
- await expect(style.flexDirection).toBe('row');
624
- await expect(style.textAlign).toBe('left');
625
- } else {
626
- await expect(style.flexDirection).toBe('row-reverse');
627
- await expect(style.textAlign).toBe('right');
628
- }
629
- }
630
-
631
- // Verify animations are applied
632
- for (let i = 0; i < itemContainers.length; i++) {
633
- const container = itemContainers[i] as HTMLElement;
634
- const style = window.getComputedStyle(container);
635
-
636
- // Check slide-in animation with staggered delay
637
- await expect(style.animation).toContain('0.5s');
638
- await expect(style.animation).toContain('ease');
639
- // Animation delay varies per item - just check it has some delay
640
- await expect(style.animation).toBeTruthy();
641
- }
642
-
643
- // Verify error state styling
644
- const allDots = canvas.getAllByTestId('timeline-dot');
645
- const errorDot = allDots[4] as HTMLElement;
646
- const errorStyle = window.getComputedStyle(errorDot);
647
- await expect(errorStyle.background).toContain('gradient');
648
-
649
- // Verify pulse animation on dots
650
- for (const dot of allDots) {
651
- const dotElement = dot as HTMLElement;
652
- const style = window.getComputedStyle(dotElement);
653
- await expect(style.animation).toContain('2s');
654
- await expect(style.animation).toContain('ease');
655
- await expect(style.animation).toContain('infinite');
656
- }
657
-
658
- // Test different icon states
659
- const icons = canvasElement.querySelectorAll('svg');
660
- await expect(icons.length).toBeGreaterThan(0);
661
-
662
- // Verify icon colors within dots
663
- for (const dot of allDots) {
664
- const icon = dot.querySelector('svg');
665
- if (icon) {
666
- const iconStyle = window.getComputedStyle(icon);
667
- await expect(iconStyle.fontSize).toBeTruthy();
668
- }
669
- }
670
- },
671
- };
672
-
673
- export const Performance: Story = {
674
- args: {
675
- items: Array.from({ length: 50 }, (_, i) => ({
676
- id: `${i}`,
677
- title: `Event ${i + 1}`,
678
- description: `Description for event ${i + 1}`,
679
- timestamp: `2024-01-01 ${String(i % 12).padStart(2, '0')}:00 ${i < 12 ? 'AM' : 'PM'}`,
680
- metadata: { Index: `${i}`, Category: `Category ${i % 5}` },
681
- })),
682
- variant: 'compact',
683
- animated: false,
684
- onItemClick: fn(),
685
- },
686
- play: async ({ canvasElement, args }) => {
687
- const canvas = within(canvasElement);
688
-
689
- // Wait for all items to render
690
- await waitFor(() => {
691
- const items = canvas.getAllByRole('article');
692
- expect(items).toHaveLength(50);
693
- }, { timeout: 3000 });
694
-
695
- // Verify large number of items render correctly
696
- const items = canvas.getAllByRole('article');
697
- await expect(items).toHaveLength(50);
698
-
699
- // Verify compact variant styling
700
- const timelineCards = canvas.getAllByTestId('timeline-card');
701
- for (const card of timelineCards.slice(0, 5)) { // Check first 5 for performance
702
- const cardElement = card as HTMLElement;
703
- // Compact variant should have reduced padding
704
- // Skip checking style directly as it's not used beyond content check
705
- const contentElement = cardElement.querySelector('.MuiCardContent-root') as HTMLElement;
706
- if (contentElement) {
707
- const contentStyle = window.getComputedStyle(contentElement);
708
- await expect(contentStyle.padding).toBe('0px');
709
- }
710
- }
711
-
712
- // Test animations are disabled
713
- const itemContainers = canvas.getAllByTestId('timeline-item-container');
714
- for (const container of itemContainers.slice(0, 5)) { // Check first 5
715
- const element = container as HTMLElement;
716
- const style = window.getComputedStyle(element);
717
-
718
- // No animation should be applied when animated=false
719
- await expect(style.animation).toContain('none');
720
- }
721
-
722
- // Click multiple items quickly. No wall-clock budget: a 1s ceiling on three
723
- // clicks measures how loaded the runner is, not how the component performs.
724
- await userEvent.click(items[0]);
725
- await userEvent.click(items[10]);
726
- await userEvent.click(items[20]);
727
-
728
- // Verify clicks were registered
729
- await expect(args.onItemClick).toHaveBeenCalledTimes(3);
730
-
731
- // Test scrolling performance for vertical timeline - ensure container exists
732
- await waitFor(() => {
733
- const container = canvas.getByTestId('timeline-container');
734
- expect(container).toBeInTheDocument();
735
- }, { timeout: 3000 });
736
-
737
- // Verify all titles are rendered correctly
738
- for (let i = 0; i < 5; i++) { // Check first 5 items
739
- await expect(canvas.getByText(`Event ${i + 1}`)).toBeInTheDocument();
740
- }
741
-
742
- // In compact mode, descriptions should still be visible
743
- for (let i = 0; i < 3; i++) { // Check first 3
744
- await expect(canvas.getByText(`Description for event ${i + 1}`)).toBeInTheDocument();
745
- }
746
-
747
- // In compact mode, metadata chips are NOT rendered (they require expanded state or detailed variant)
748
- // Verify no metadata chips are present
749
- const metadataChips = canvasElement.querySelectorAll('[class*="MuiChip"]');
750
- await expect(metadataChips.length).toBe(0);
751
-
752
- // Test connectors are rendered efficiently
753
- const connectors = canvas.getAllByTestId('timeline-connector');
754
- await expect(connectors).toHaveLength(49); // 50 items = 49 connectors
755
- },
756
- };
757
-
758
- export const EdgeCases: Story = {
759
- args: {
760
- items: [
761
- {
762
- id: '1',
763
- title:
764
- 'Very long title that should wrap properly and not break the layout even when it contains many words',
765
- description:
766
- 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.',
767
- timestamp: '2024-12-31 11:59 PM',
768
- metadata: {
769
- 'Long Key Name': 'Long value that might need truncation',
770
- 'Another Key': 'Another value',
771
- 'Third Key': 'Third value',
772
- 'Fourth Key': 'Fourth value',
773
- },
774
- },
775
- {
776
- id: '2',
777
- title: '',
778
- timestamp: '',
779
- },
780
- {
781
- id: '3',
782
- title: 'Minimal item',
783
- timestamp: 'Now',
784
- },
785
- ],
786
- variant: 'detailed',
787
- onItemClick: fn(),
788
- },
789
- play: async ({ canvasElement, args }) => {
790
- const canvas = within(canvasElement);
791
-
792
- // Wait for timeline to render using a more robust query
793
- const items = await canvas.findAllByRole('article', {}, { timeout: 3000 });
794
- expect(items).toHaveLength(3);
795
-
796
- // Verify long title wraps properly
797
- const longTitleText =
798
- 'Very long title that should wrap properly and not break the layout even when it contains many words';
799
- const longTitleElement = await canvas.findByText(longTitleText);
800
- expect(longTitleElement).toBeInTheDocument();
801
-
802
- const titleStyle = window.getComputedStyle(longTitleElement);
803
- const titleRect = longTitleElement.getBoundingClientRect();
804
- const cardRect = longTitleElement.closest('[data-testid="timeline-card"]')?.getBoundingClientRect();
805
-
806
- if (cardRect) {
807
- expect(titleRect.width).toBeLessThanOrEqual(cardRect.width);
808
- }
809
- expect(titleStyle.overflowWrap).toBeTruthy();
810
-
811
- // Wait for the animation to complete by putting the visibility assertion inside waitFor.
812
- const longDescription =
813
- 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.';
814
-
815
- await waitFor(() => {
816
- expect(canvas.getByText(longDescription)).toBeVisible();
817
- }, { timeout: 3000 });
818
-
819
- // Test empty title and timestamp handling
820
- const emptyItemCard = items[1];
821
- expect(emptyItemCard).toBeInTheDocument();
822
-
823
- await userEvent.click(emptyItemCard);
824
- await expect(args.onItemClick).toHaveBeenCalledWith(expect.objectContaining({ id: '2' }));
825
-
826
- // Verify minimal item renders correctly
827
- await expect(canvas.getByText('Minimal item')).toBeInTheDocument();
828
- await expect(canvas.getByText('Now')).toBeInTheDocument();
829
-
830
- // Test metadata chip rendering and overflow - wait for them to appear
831
- const metadataChips = await canvas.findAllByText(/Key Name|Another Key|Third Key|Fourth Key/, {}, { timeout: 3000 });
832
- expect(metadataChips).toHaveLength(4);
833
-
834
- const metadataContainer = metadataChips[0]?.parentElement?.parentElement;
835
- if (metadataContainer) {
836
- await waitFor(() => {
837
- const containerStyle = window.getComputedStyle(metadataContainer as HTMLElement);
838
- expect(containerStyle.flexWrap).toBe('wrap');
839
- });
840
- const finalContainerStyle = window.getComputedStyle(metadataContainer as HTMLElement);
841
- expect(finalContainerStyle.display).toBe('flex');
842
- }
843
-
844
- // Test timeline dots and connectors using test-ids for resilience
845
- const dots = await canvas.findAllByTestId('timeline-dot');
846
- expect(dots).toHaveLength(3);
847
-
848
- const connectors = await canvas.findAllByTestId('timeline-connector');
849
- expect(connectors).toHaveLength(2);
850
-
851
- // Verify one expand button exists for the item with a description.
852
- const expandButtons = canvas.queryAllByRole('button', { name: /expand item details/i });
853
- expect(expandButtons).toHaveLength(1);
854
- },
855
- };
856
-
857
- export const Integration: Story = {
858
- args: {
859
- items: sampleItems,
860
- variant: 'default',
861
- orientation: 'horizontal',
862
- showConnector: true,
863
- animated: true,
864
- alternating: false,
865
- onItemClick: fn(),
866
- },
867
- play: async ({ canvasElement, args }) => {
868
- const canvas = within(canvasElement);
869
-
870
- // 1. Wait for the main container to appear using its test-id.
871
- const container = await canvas.findByTestId('timeline-container');
872
-
873
- // 2. Assert styles on the now-guaranteed-to-exist container.
874
- const containerStyle = window.getComputedStyle(container);
875
- expect(containerStyle.flexDirection).toBe('row');
876
- expect(containerStyle.overflowX).toBe('auto');
877
-
878
- // 3. Wait for all item containers to render.
879
- const itemContainers = await canvas.findAllByTestId('timeline-item-container');
880
- expect(itemContainers).toHaveLength(sampleItems.length);
881
-
882
- for (const itemContainer of itemContainers) {
883
- const style = window.getComputedStyle(itemContainer);
884
- expect(style.flexDirection).toBe('column');
885
- expect(style.alignItems).toBe('center');
886
- }
887
-
888
- // 4. Test all interactive features work together
889
- const cards = await canvas.findAllByRole('article');
890
- await userEvent.click(cards[0]);
891
- expect(args.onItemClick).toHaveBeenCalledWith(sampleItems[0]);
892
- expect(args.onItemClick).toHaveBeenCalledTimes(1);
893
-
894
- // 5. Expand an item by finding its accessible button label
895
- const expandButtons = await canvas.findAllByRole('button', {
896
- name: /Expand item details/i,
897
- });
898
-
899
- if (expandButtons.length > 0) {
900
- await userEvent.click(expandButtons[0]);
901
- // Wait for description to appear and become visible (animations take time)
902
- await waitFor(async () => {
903
- const description = await canvas.findByText(sampleItems[0].description!);
904
- expect(description).toBeVisible();
905
- }, { timeout: 3000 });
906
-
907
- // Find and click the action button that is now visible
908
- const actionButton = await canvas.findByRole('button', { name: /View Details/i });
909
- await userEvent.click(actionButton);
910
- expect(sampleItems[0].action?.onClick).toHaveBeenCalled();
911
- }
912
-
913
- // 6. Verify all visual elements are properly rendered, waiting for each one.
914
- const dots = await canvas.findAllByTestId('timeline-dot');
915
- expect(dots).toHaveLength(sampleItems.length);
916
-
917
- // Asynchronously verify that each dot contains an icon
918
- for (const dot of dots) {
919
- // Use `within` to scope the search inside each dot and wait for the SVG to appear.
920
- // This is the key fix for the original error.
921
- const icon = await within(dot).findByTestId(/item-icon/);
922
- expect(icon).toBeInTheDocument();
923
- }
924
-
925
- // 7. Test scrolling behavior for horizontal timeline
926
- if (container.scrollWidth > container.clientWidth) {
927
- expect(container.scrollWidth).toBeGreaterThan(container.clientWidth);
928
- }
929
- },
930
- };