@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,1154 +0,0 @@
1
- import Delete from '@mui/icons-material/Delete';
2
- import Help from '@mui/icons-material/Help';
3
- import Info from '@mui/icons-material/Info';
4
- import { Box, Button, IconButton, Stack, Typography } from '@mui/material';
5
- import type { Meta, StoryObj } from '@storybook/react-vite';
6
- import React from 'react';
7
- import { expect, userEvent, waitFor,within } from 'storybook/test';
8
-
9
- import { Tooltip } from './Tooltip';
10
-
11
- const meta: Meta<typeof Tooltip> = {
12
- title: 'DataDisplay/Tooltip/Tests',
13
- component: Tooltip,
14
- parameters: {
15
- layout: 'centered',
16
- chromatic: { disableSnapshot: false },
17
- },
18
- tags: ['autodocs', 'test', 'component:Tooltip'],
19
- };
20
-
21
- export default meta;
22
- type Story = StoryObj<typeof meta>;
23
-
24
- // Helper function to find tooltip by content or just the first visible one
25
- const findTooltipByContent = async (content?: string) => {
26
- const tooltips = await within(document.body).findAllByRole('tooltip');
27
- if (!content) {
28
- // Return the last tooltip (innermost div with actual content)
29
- return tooltips[tooltips.length - 1];
30
- }
31
- return tooltips.find((tip) => tip.textContent?.includes(content));
32
- };
33
-
34
- // 7.2 Interaction Tests
35
- export const BasicInteraction: Story = {
36
- name: '🧪 Basic Interaction Test',
37
- args: {
38
- title: 'Test tooltip content',
39
- variant: 'default',
40
- },
41
- render: (args) => (
42
- <Box sx={{ p: 4, display: 'flex', justifyContent: 'center' }}>
43
- <Tooltip {...args}>
44
- <Button variant="outlined" data-testid="trigger-button">
45
- Hover me
46
- </Button>
47
- </Tooltip>
48
- </Box>
49
- ),
50
- play: async ({ canvasElement, step }) => {
51
- const canvas = within(canvasElement);
52
-
53
- await step('Initial render verification', async () => {
54
- const button = canvas.getByTestId('trigger-button');
55
- await expect(button).toBeInTheDocument();
56
- await expect(button).toHaveTextContent('Hover me');
57
- });
58
-
59
- await step('Hover interaction shows tooltip', async () => {
60
- const button = canvas.getByTestId('trigger-button');
61
- await userEvent.hover(button);
62
-
63
- // Wait for tooltip to appear
64
- await waitFor(
65
- async () => {
66
- const tooltip = await findTooltipByContent('Test tooltip content');
67
- await expect(tooltip).toBeInTheDocument();
68
- await expect(tooltip).toHaveTextContent('Test tooltip content');
69
- },
70
- { timeout: 2000 },
71
- );
72
- });
73
-
74
- await step('Unhover hides tooltip', async () => {
75
- const button = canvas.getByTestId('trigger-button');
76
- await userEvent.unhover(button);
77
-
78
- // Wait for tooltip to disappear
79
- await waitFor(
80
- () => {
81
- const tooltip = within(document.body).queryByRole('tooltip');
82
- expect(tooltip).not.toBeInTheDocument();
83
- },
84
- { timeout: 2000 },
85
- );
86
- });
87
- },
88
- };
89
-
90
- export const ClickInteraction: Story = {
91
- name: '🖱️ Click Interaction Test',
92
- args: {
93
- title: 'Click tooltip',
94
- variant: 'light',
95
- },
96
- render: (args) => (
97
- <Box sx={{ p: 4, display: 'flex', justifyContent: 'center' }}>
98
- <Tooltip {...args}>
99
- <Button variant="contained" color="primary" data-testid="click-trigger">
100
- Click me
101
- </Button>
102
- </Tooltip>
103
- </Box>
104
- ),
105
- play: async ({ canvasElement, step }) => {
106
- const canvas = within(canvasElement);
107
-
108
- await step('Click shows tooltip', async () => {
109
- const button = canvas.getByTestId('click-trigger');
110
- await userEvent.click(button);
111
-
112
- await waitFor(
113
- async () => {
114
- const tooltip = await findTooltipByContent('Click tooltip');
115
- await expect(tooltip).toBeInTheDocument();
116
- await expect(tooltip).toHaveTextContent('Click tooltip');
117
- },
118
- { timeout: 2000 },
119
- );
120
- });
121
-
122
- await step('Click elsewhere hides tooltip', async () => {
123
- // Click outside the button
124
- await userEvent.click(canvasElement);
125
-
126
- await waitFor(
127
- () => {
128
- const tooltip = within(document.body).queryByRole('tooltip');
129
- expect(tooltip).not.toBeInTheDocument();
130
- },
131
- { timeout: 2000 },
132
- );
133
- });
134
- },
135
- };
136
-
137
- const StateChangeTestComponent = () => {
138
- const [variant, setVariant] = React.useState<'default' | 'dark' | 'light' | 'glass'>('default');
139
- const [glow, setGlow] = React.useState(false);
140
- const [pulse, setPulse] = React.useState(false);
141
-
142
- return (
143
- <Box sx={{ p: 4, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2 }}>
144
- <Stack direction="row" spacing={2} sx={{ mb: 2 }}>
145
- <Button
146
- variant="outlined"
147
- size="small"
148
- onClick={() => setVariant(variant === 'default' ? 'dark' : 'default')}
149
- data-testid="variant-toggle"
150
- >
151
- Toggle Variant
152
- </Button>
153
- <Button
154
- variant="outlined"
155
- size="small"
156
- onClick={() => setGlow(!glow)}
157
- data-testid="glow-toggle"
158
- >
159
- Toggle Glow
160
- </Button>
161
- <Button
162
- variant="outlined"
163
- size="small"
164
- onClick={() => setPulse(!pulse)}
165
- data-testid="pulse-toggle"
166
- >
167
- Toggle Pulse
168
- </Button>
169
- </Stack>
170
-
171
- <Tooltip
172
- title={`Variant: ${variant}, Glow: ${glow}, Pulse: ${pulse}`}
173
- variant={variant}
174
- glow={glow}
175
- pulse={pulse}
176
- >
177
- <Button variant="contained" data-testid="stateful-tooltip">
178
- Dynamic Tooltip
179
- </Button>
180
- </Tooltip>
181
- </Box>
182
- );
183
- };
184
-
185
- export const StateChangeTest: Story = {
186
- name: '🔄 State Change Test',
187
- render: () => <StateChangeTestComponent />,
188
- play: async ({ canvasElement, step }) => {
189
- const canvas = within(canvasElement);
190
-
191
- await step('Toggle variant changes tooltip style', async () => {
192
- const variantButton = canvas.getByTestId('variant-toggle');
193
- const tooltipTrigger = canvas.getByTestId('stateful-tooltip');
194
-
195
- await userEvent.click(variantButton);
196
- await userEvent.hover(tooltipTrigger);
197
-
198
- await waitFor(
199
- async () => {
200
- const tooltip = await findTooltipByContent();
201
- await expect(tooltip).toBeInTheDocument();
202
- await expect(tooltip).toHaveTextContent(/dark/);
203
- },
204
- { timeout: 2000 },
205
- );
206
-
207
- await userEvent.unhover(tooltipTrigger);
208
- });
209
-
210
- await step('Toggle glow adds visual effect', async () => {
211
- const glowButton = canvas.getByTestId('glow-toggle');
212
- const tooltipTrigger = canvas.getByTestId('stateful-tooltip');
213
-
214
- await userEvent.click(glowButton);
215
- await userEvent.hover(tooltipTrigger);
216
-
217
- await waitFor(
218
- async () => {
219
- const tooltip = await findTooltipByContent();
220
- await expect(tooltip).toHaveTextContent(/Glow: true/);
221
- },
222
- { timeout: 2000 },
223
- );
224
-
225
- await userEvent.unhover(tooltipTrigger);
226
- });
227
-
228
- await step('Toggle pulse adds animation', async () => {
229
- const pulseButton = canvas.getByTestId('pulse-toggle');
230
- const tooltipTrigger = canvas.getByTestId('stateful-tooltip');
231
-
232
- await userEvent.click(pulseButton);
233
- await userEvent.hover(tooltipTrigger);
234
-
235
- await waitFor(
236
- async () => {
237
- const tooltip = await findTooltipByContent();
238
- await expect(tooltip).toHaveTextContent(/Pulse: true/);
239
- },
240
- { timeout: 2000 },
241
- );
242
-
243
- await userEvent.unhover(tooltipTrigger);
244
- });
245
- },
246
- };
247
-
248
- // 7.3 Accessibility Tests
249
- export const KeyboardNavigation: Story = {
250
- name: '⌨️ Keyboard Navigation Test',
251
- parameters: {
252
- a11y: {
253
- element: '#storybook-root',
254
- config: {
255
- rules: [
256
- { id: 'color-contrast', enabled: true },
257
- { id: 'aria-required-attr', enabled: true },
258
- { id: 'aria-roles', enabled: true },
259
- { id: 'aria-valid-attr-value', enabled: true },
260
- { id: 'button-name', enabled: true },
261
- { id: 'duplicate-id', enabled: true },
262
- { id: 'label', enabled: true },
263
- ],
264
- },
265
- },
266
- },
267
- render: () => (
268
- <Box sx={{ p: 4, display: 'flex', justifyContent: 'center', gap: 2 }}>
269
- <Stack direction="row" spacing={2}>
270
- <Tooltip title="First tooltip" variant="default">
271
- <Button variant="outlined" data-testid="first-focusable">
272
- First
273
- </Button>
274
- </Tooltip>
275
-
276
- <Tooltip title="Second tooltip" variant="light">
277
- <Button variant="outlined" data-testid="second-focusable">
278
- Second
279
- </Button>
280
- </Tooltip>
281
-
282
- <Tooltip title="Third tooltip with keyboard navigation" variant="glass">
283
- <IconButton data-testid="third-focusable">
284
- <Help />
285
- </IconButton>
286
- </Tooltip>
287
- </Stack>
288
- </Box>
289
- ),
290
- play: async ({ canvasElement, step }) => {
291
- const canvas = within(canvasElement);
292
-
293
- await step('Tab navigation forward', async () => {
294
- const firstElement = canvas.getByTestId('first-focusable');
295
- const secondElement = canvas.getByTestId('second-focusable');
296
-
297
- // Tab in from the body rather than calling .focus().
298
- await userEvent.tab();
299
- await waitFor(() => expect(firstElement).toHaveFocus());
300
-
301
- // Tab to next element
302
- await userEvent.tab();
303
- await waitFor(() => expect(secondElement).toHaveFocus());
304
- });
305
-
306
- await step('Tab navigation backward', async () => {
307
- await userEvent.tab({ shift: true });
308
- const firstElement = canvas.getByTestId('first-focusable');
309
- await waitFor(() => expect(firstElement).toHaveFocus());
310
- });
311
-
312
- await step('Enter key maintains focus', async () => {
313
- const firstButton = canvas.getByTestId('first-focusable');
314
- // Focus is already back here from the shift-tab in the previous step.
315
- await waitFor(() => expect(firstButton).toHaveFocus());
316
- await userEvent.keyboard('{Enter}');
317
- await waitFor(() => expect(firstButton).toHaveFocus());
318
- });
319
-
320
- await step('Space key maintains focus', async () => {
321
- const secondButton = canvas.getByTestId('second-focusable');
322
- await userEvent.tab();
323
- await waitFor(() => expect(secondButton).toHaveFocus());
324
- await userEvent.keyboard(' ');
325
- await waitFor(() => expect(secondButton).toHaveFocus());
326
- });
327
- },
328
- };
329
-
330
- export const ScreenReaderTest: Story = {
331
- name: '🔊 Screen Reader Test',
332
- render: () => (
333
- <Box sx={{ p: 4, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3 }}>
334
- <Tooltip title="This tooltip provides additional context" variant="default">
335
- <Button data-testid="aria-component" aria-label="Action button with tooltip">
336
- Action
337
- </Button>
338
- </Tooltip>
339
-
340
- <Tooltip title="Delete this item permanently" variant="dark" placement="top">
341
- <IconButton data-testid="delete-button" aria-label="Delete item" color="error">
342
- <Delete />
343
- </IconButton>
344
- </Tooltip>
345
-
346
- <Box>
347
- <Typography variant="subtitle2" id="field-label" data-testid="field-label" sx={{ mb: 1 }}>
348
- Important Setting
349
- </Typography>
350
- <Tooltip
351
- title="This setting affects system performance and cannot be changed later"
352
- variant="light"
353
- placement="right"
354
- >
355
- <IconButton
356
- data-testid="help-icon"
357
- aria-label="Help information for important setting"
358
- aria-describedby="field-label"
359
- size="small"
360
- >
361
- <Info fontSize="small" />
362
- </IconButton>
363
- </Tooltip>
364
- </Box>
365
- </Box>
366
- ),
367
- play: async ({ canvasElement, step }) => {
368
- const canvas = within(canvasElement);
369
-
370
- await step('Verify ARIA labels', async () => {
371
- const actionButton = canvas.getByTestId('aria-component');
372
- await expect(actionButton).toHaveAttribute('aria-label', 'Action button with tooltip');
373
-
374
- const deleteButton = canvas.getByTestId('delete-button');
375
- await expect(deleteButton).toHaveAttribute('aria-label', 'Delete item');
376
- });
377
-
378
- await step('Verify ARIA descriptions', async () => {
379
- const helpIcon = canvas.getByTestId('help-icon');
380
- await expect(helpIcon).toHaveAttribute('aria-describedby', 'field-label');
381
-
382
- const label = canvas.getByTestId('field-label') || canvas.getByText('Important Setting');
383
- await expect(label).toBeInTheDocument();
384
- });
385
-
386
- await step('Verify tooltip content is accessible', async () => {
387
- const actionButton = canvas.getByTestId('aria-component');
388
- await userEvent.hover(actionButton);
389
-
390
- await waitFor(
391
- async () => {
392
- const tooltip = await findTooltipByContent();
393
- await expect(tooltip).toBeInTheDocument();
394
- await expect(tooltip).toHaveTextContent('This tooltip provides additional context');
395
- },
396
- { timeout: 2000 },
397
- );
398
-
399
- await userEvent.unhover(actionButton);
400
- });
401
- },
402
- };
403
-
404
- export const FocusManagement: Story = {
405
- name: '🎯 Focus Management Test',
406
- render: () => (
407
- <Box sx={{ p: 4, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3 }}>
408
- <Tooltip title="Focus is maintained on trigger" variant="default">
409
- <Button data-testid="focus-trigger" variant="contained" autoFocus>
410
- Auto Focus Button
411
- </Button>
412
- </Tooltip>
413
-
414
- <Stack direction="row" spacing={2}>
415
- <Tooltip title="First focusable element" variant="light">
416
- <Button data-testid="first-element">First</Button>
417
- </Tooltip>
418
-
419
- <Tooltip title="Second focusable element" variant="glass">
420
- <Button data-testid="second-element">Second</Button>
421
- </Tooltip>
422
-
423
- <Tooltip title="Last focusable element" variant="dark">
424
- <Button data-testid="last-element">Last</Button>
425
- </Tooltip>
426
- </Stack>
427
- </Box>
428
- ),
429
- play: async ({ canvasElement, step }) => {
430
- const canvas = within(canvasElement);
431
-
432
- await step('Auto focus on mount', async () => {
433
- const autoFocusElement = canvas.getByTestId('focus-trigger');
434
- await waitFor(() => {
435
- expect(autoFocusElement).toHaveFocus();
436
- });
437
- });
438
-
439
- await step('Focus is maintained during tooltip display', async () => {
440
- const focusButton = canvas.getByTestId('focus-trigger');
441
- await userEvent.tab();
442
- await waitFor(() => expect(focusButton).toHaveFocus());
443
-
444
- // Hover to show tooltip
445
- await userEvent.hover(focusButton);
446
-
447
- await waitFor(
448
- async () => {
449
- const tooltip = await findTooltipByContent();
450
- await expect(tooltip).toBeInTheDocument();
451
- // Focus should remain on trigger
452
- await waitFor(() => expect(focusButton).toHaveFocus());
453
- },
454
- { timeout: 2000 },
455
- );
456
-
457
- await userEvent.unhover(focusButton);
458
- });
459
-
460
- await step('Tab order is preserved', async () => {
461
- const firstElement = canvas.getByTestId('first-element');
462
- const secondElement = canvas.getByTestId('second-element');
463
- const lastElement = canvas.getByTestId('last-element');
464
-
465
- await userEvent.tab();
466
- await waitFor(() => expect(firstElement).toHaveFocus());
467
-
468
- await userEvent.tab();
469
- await waitFor(() => expect(secondElement).toHaveFocus());
470
-
471
- await userEvent.tab();
472
- await waitFor(() => expect(lastElement).toHaveFocus());
473
- });
474
- },
475
- };
476
-
477
- // 7.4 Visual Tests
478
- export const ResponsiveDesign: Story = {
479
- name: '📱 Responsive Design Test',
480
- parameters: {
481
- viewport: {
482
- viewports: {
483
- mobile: {
484
- name: 'Mobile',
485
- styles: { width: '375px', height: '667px' },
486
- type: 'mobile',
487
- },
488
- tablet: {
489
- name: 'Tablet',
490
- styles: { width: '768px', height: '1024px' },
491
- type: 'tablet',
492
- },
493
- desktop: {
494
- name: 'Desktop',
495
- styles: { width: '1920px', height: '1080px' },
496
- type: 'desktop',
497
- },
498
- },
499
- defaultViewport: 'mobile',
500
- },
501
- chromatic: {
502
- viewports: [375, 768, 1920],
503
- delay: 300,
504
- },
505
- },
506
- render: () => (
507
- <Box
508
- sx={{
509
- p: 2,
510
- display: 'flex',
511
- flexDirection: { xs: 'column', md: 'row' },
512
- gap: 2,
513
- justifyContent: 'center',
514
- alignItems: 'center',
515
- }}
516
- data-testid="responsive-container"
517
- >
518
- <Tooltip
519
- title="This tooltip adapts to different screen sizes and viewport widths"
520
- variant="default"
521
- maxWidth={200}
522
- >
523
- <Button variant="outlined" size="small">
524
- Mobile Tooltip
525
- </Button>
526
- </Tooltip>
527
-
528
- <Tooltip
529
- title="Tablet and desktop tooltip with more content that demonstrates responsive behavior"
530
- variant="light"
531
- maxWidth={300}
532
- >
533
- <Button variant="outlined">Responsive Tooltip</Button>
534
- </Tooltip>
535
- </Box>
536
- ),
537
- play: async ({ canvasElement, step }) => {
538
- const canvas = within(canvasElement);
539
-
540
- await step('Verify responsive container layout', async () => {
541
- const container = canvas.getByTestId('responsive-container');
542
- await expect(container).toBeInTheDocument();
543
-
544
- // Assert the container has an explicit flex direction, without branching
545
- // on the runner's window width. Reading innerWidth made the story assert a
546
- // different thing depending on the machine it ran on, so neither branch
547
- // was ever exercised in both directions.
548
- const computedStyle = window.getComputedStyle(container);
549
- await expect(['row', 'column']).toContain(computedStyle.flexDirection);
550
- });
551
-
552
- await step('Test tooltip rendering at different viewport sizes', async () => {
553
- const buttons = canvas.getAllByRole('button');
554
-
555
- for (const button of buttons) {
556
- await userEvent.hover(button);
557
-
558
- await waitFor(
559
- async () => {
560
- const tooltip = await findTooltipByContent();
561
- await expect(tooltip).toBeInTheDocument();
562
- },
563
- { timeout: 2000 },
564
- );
565
-
566
- await userEvent.unhover(button);
567
- }
568
- });
569
- },
570
- };
571
-
572
- export const ThemeVariations: Story = {
573
- name: '🎨 Theme Variations Test',
574
- parameters: {
575
- backgrounds: {
576
- default: 'light',
577
- values: [
578
- { name: 'light', value: '#ffffff' },
579
- { name: 'dark', value: '#1a1a1a' },
580
- { name: 'gray', value: '#f5f5f5' },
581
- ],
582
- },
583
- },
584
- render: () => (
585
- <Box sx={{ p: 4, display: 'flex', flexDirection: 'column', gap: 3, alignItems: 'center' }}>
586
- <Stack direction="row" spacing={2}>
587
- <Tooltip title="Default theme" variant="default">
588
- <Button variant="outlined" data-testid="default-theme">
589
- Default
590
- </Button>
591
- </Tooltip>
592
-
593
- <Tooltip title="Light theme" variant="light">
594
- <Button variant="outlined" data-testid="light-theme">
595
- Light
596
- </Button>
597
- </Tooltip>
598
-
599
- <Tooltip title="Dark theme" variant="dark">
600
- <Button variant="outlined" data-testid="dark-theme">
601
- Dark
602
- </Button>
603
- </Tooltip>
604
-
605
- <Tooltip title="Glass theme" variant="glass">
606
- <Button variant="outlined" data-testid="glass-theme">
607
- Glass
608
- </Button>
609
- </Tooltip>
610
- </Stack>
611
-
612
- <Stack direction="row" spacing={2}>
613
- <Tooltip title="Glow effect" variant="default" glow>
614
- <Button variant="contained" color="primary" data-testid="glow-effect">
615
- Glow
616
- </Button>
617
- </Tooltip>
618
-
619
- <Tooltip title="Pulse animation" variant="light" pulse>
620
- <Button variant="contained" color="secondary" data-testid="pulse-effect">
621
- Pulse
622
- </Button>
623
- </Tooltip>
624
-
625
- <Tooltip title="Combined effects" variant="glass" glow pulse>
626
- <Button variant="contained" color="success" data-testid="combined-effects">
627
- Combined
628
- </Button>
629
- </Tooltip>
630
- </Stack>
631
- </Box>
632
- ),
633
- play: async ({ canvasElement, step }) => {
634
- const canvas = within(canvasElement);
635
-
636
- await step('Verify all theme variants render', async () => {
637
- const buttons = ['default-theme', 'light-theme', 'dark-theme', 'glass-theme'];
638
-
639
- for (const buttonId of buttons) {
640
- const button = canvas.getByTestId(buttonId);
641
- await userEvent.hover(button);
642
-
643
- await waitFor(
644
- async () => {
645
- const tooltip = await findTooltipByContent();
646
- await expect(tooltip).toBeInTheDocument();
647
- },
648
- { timeout: 2000 },
649
- );
650
-
651
- await userEvent.unhover(button);
652
- }
653
- });
654
-
655
- await step('Verify visual effects render', async () => {
656
- const effectButtons = ['glow-effect', 'pulse-effect', 'combined-effects'];
657
-
658
- for (const buttonId of effectButtons) {
659
- const button = canvas.getByTestId(buttonId);
660
- await userEvent.hover(button);
661
-
662
- await waitFor(
663
- async () => {
664
- const tooltip = await findTooltipByContent();
665
- await expect(tooltip).toBeInTheDocument();
666
- },
667
- { timeout: 2000 },
668
- );
669
-
670
- await userEvent.unhover(button);
671
- }
672
- });
673
- },
674
- };
675
-
676
- export const VisualStates: Story = {
677
- name: '👁️ Visual States Test',
678
- render: () => (
679
- <Box sx={{ p: 4, display: 'flex', flexDirection: 'column', gap: 3, alignItems: 'center' }}>
680
- <Stack direction="row" spacing={2}>
681
- <Tooltip title="Normal state tooltip">
682
- <Button variant="outlined" data-testid="normal-state">
683
- Normal
684
- </Button>
685
- </Tooltip>
686
-
687
- <Tooltip title="This button is disabled" placement="top">
688
- <span>
689
- <Button variant="outlined" disabled data-testid="disabled-button">
690
- Disabled
691
- </Button>
692
- </span>
693
- </Tooltip>
694
- </Stack>
695
-
696
- <Stack direction="row" spacing={2}>
697
- <Tooltip title="Small size tooltip" size="sm">
698
- <Button variant="outlined" size="small" data-testid="small-tooltip">
699
- Small
700
- </Button>
701
- </Tooltip>
702
-
703
- <Tooltip title="Large size tooltip" size="lg">
704
- <Button variant="outlined" size="large" data-testid="large-tooltip">
705
- Large
706
- </Button>
707
- </Tooltip>
708
- </Stack>
709
-
710
- <Tooltip
711
- title="This is a very long tooltip text that should wrap properly within the maximum width constraints and demonstrate how the tooltip handles lengthy content gracefully"
712
- maxWidth={200}
713
- >
714
- <Button variant="outlined" data-testid="long-text-tooltip">
715
- Long Text
716
- </Button>
717
- </Tooltip>
718
- </Box>
719
- ),
720
- play: async ({ canvasElement, step }) => {
721
- const canvas = within(canvasElement);
722
-
723
- await step('Normal state tooltip', async () => {
724
- const button = canvas.getByTestId('normal-state');
725
- await userEvent.hover(button);
726
-
727
- await waitFor(
728
- async () => {
729
- const tooltip = await findTooltipByContent();
730
- await expect(tooltip).toBeInTheDocument();
731
- await expect(tooltip).toHaveTextContent('Normal state tooltip');
732
- },
733
- { timeout: 2000 },
734
- );
735
-
736
- await userEvent.unhover(button);
737
- });
738
-
739
- await step('Disabled state tooltip', async () => {
740
- const disabledButton = canvas.getByTestId('disabled-button');
741
- await expect(disabledButton).toBeDisabled();
742
-
743
- // Hover on the span wrapper since disabled buttons don't receive hover events
744
- const wrapper = disabledButton.parentElement;
745
- if (wrapper) {
746
- await userEvent.hover(wrapper);
747
-
748
- await waitFor(
749
- async () => {
750
- const tooltip = await findTooltipByContent();
751
- await expect(tooltip).toHaveTextContent('This button is disabled');
752
- },
753
- { timeout: 2000 },
754
- );
755
-
756
- await userEvent.unhover(wrapper);
757
- }
758
- });
759
-
760
- await step('Different sizes render correctly', async () => {
761
- const smallButton = canvas.getByTestId('small-tooltip');
762
- await userEvent.hover(smallButton);
763
-
764
- await waitFor(
765
- async () => {
766
- const tooltip = await findTooltipByContent();
767
- await expect(tooltip).toHaveTextContent('Small size tooltip');
768
- },
769
- { timeout: 2000 },
770
- );
771
-
772
- await userEvent.unhover(smallButton);
773
-
774
- const largeButton = canvas.getByTestId('large-tooltip');
775
- await userEvent.hover(largeButton);
776
-
777
- await waitFor(
778
- async () => {
779
- const tooltip = await findTooltipByContent();
780
- await expect(tooltip).toHaveTextContent('Large size tooltip');
781
- },
782
- { timeout: 2000 },
783
- );
784
-
785
- await userEvent.unhover(largeButton);
786
- });
787
-
788
- await step('Long text wrapping', async () => {
789
- const longTextButton = canvas.getByTestId('long-text-tooltip');
790
- await userEvent.hover(longTextButton);
791
-
792
- await waitFor(
793
- async () => {
794
- const tooltip = await findTooltipByContent();
795
- await expect(tooltip).toBeInTheDocument();
796
- // Check that tooltip contains the long text
797
- await expect(tooltip).toHaveTextContent(/very long tooltip text/);
798
- },
799
- { timeout: 2000 },
800
- );
801
-
802
- await userEvent.unhover(longTextButton);
803
- });
804
- },
805
- };
806
-
807
- // 7.5 Performance Tests
808
- export const PerformanceTest: Story = {
809
- name: '⚡ Performance Test',
810
- render: () => {
811
- const items = Array.from({ length: 20 }, (_, i) => ({
812
- id: i,
813
- name: `Item ${i}`,
814
- description: `Description for item ${i} with additional context information`,
815
- }));
816
-
817
- return (
818
- <Box sx={{ p: 2, maxHeight: 400, overflow: 'auto' }} data-testid="scroll-container">
819
- <Stack spacing={1}>
820
- {items.map((item) => (
821
- <Tooltip key={item.id} title={item.description} variant="default" placement="right">
822
- <Button
823
- variant="outlined"
824
- size="small"
825
- fullWidth
826
- data-testid={`item-${item.id}`}
827
- sx={{ justifyContent: 'flex-start' }}
828
- >
829
- {item.name}
830
- </Button>
831
- </Tooltip>
832
- ))}
833
- </Stack>
834
- </Box>
835
- );
836
- },
837
- play: async ({ canvasElement, step }) => {
838
- const canvas = within(canvasElement);
839
-
840
- await step('Measure render time for multiple tooltips', async () => {
841
- // No wall-clock budget: this queries DOM that has already rendered, so the
842
- // timing measured query cost and any threshold tracks runner load.
843
- const elements = canvas.getAllByTestId(/item-/);
844
- await expect(elements.length).toBe(20);
845
- });
846
-
847
- await step('Test rapid tooltip interactions', async () => {
848
- const elements = canvas.getAllByTestId(/item-/);
849
- const testElements = elements.slice(0, 5); // Test first 5 items
850
-
851
- // Hover and unhover each in turn. The 50ms sleep between them was a guess
852
- // at how long the tooltip needs; the elements surviving the cycle is the
853
- // property this step actually cares about.
854
- for (const element of testElements) {
855
- await userEvent.hover(element);
856
- await userEvent.unhover(element);
857
- await expect(element).toBeInTheDocument();
858
- }
859
- });
860
-
861
- await step('Tooltips still work inside a scroll container', async () => {
862
- const scrollContainer = canvas.getByTestId('scroll-container');
863
- await expect(scrollContainer).toBeInTheDocument();
864
-
865
- // Driving scrollTop is not asserted on here. Whether the container scrolls
866
- // at all depends on how tall the runner's window is, so a scrollTop check
867
- // reports window size rather than component behaviour — and the original
868
- // version wrapped it in a 500ms budget on top of that. What this step can
869
- // honestly assert is that a tooltip trigger inside the container still
870
- // responds to hover.
871
- const [firstItem] = canvas.getAllByTestId(/item-/);
872
- await userEvent.hover(firstItem);
873
- await userEvent.unhover(firstItem);
874
- await expect(firstItem).toBeInTheDocument();
875
- });
876
- },
877
- };
878
-
879
- // 7.6 Edge Cases Tests
880
- export const EdgeCases: Story = {
881
- name: '🔧 Edge Cases Test',
882
- render: () => (
883
- <Box sx={{ p: 4, display: 'flex', flexDirection: 'column', gap: 3, alignItems: 'center' }}>
884
- <Tooltip title="" variant="default">
885
- <Button variant="outlined" data-testid="empty-tooltip">
886
- Empty Tooltip
887
- </Button>
888
- </Tooltip>
889
-
890
- <Tooltip
891
- title="This is an extremely long tooltip text that contains a lot of information and should properly handle the maximum width constraints while maintaining readability and proper text wrapping behavior even with very lengthy content that might otherwise cause layout issues or performance problems in less robust implementations"
892
- maxWidth={150}
893
- variant="light"
894
- >
895
- <Button variant="outlined" data-testid="very-long-text">
896
- Very Long Text
897
- </Button>
898
- </Tooltip>
899
-
900
- <Tooltip title={'Special chars: !@#$%^&*()_+{}|:<>?[];\'"\\,./`~'} variant="glass">
901
- <Button variant="outlined" data-testid="special-chars">
902
- Special Characters
903
- </Button>
904
- </Tooltip>
905
-
906
- <Tooltip
907
- title="HTML content test: <script>alert('test')</script> & entities: &lt;&gt;&amp;"
908
- variant="dark"
909
- >
910
- <Button variant="outlined" data-testid="html-content">
911
- HTML Content
912
- </Button>
913
- </Tooltip>
914
-
915
- <Tooltip title={0 as unknown as string} variant="default">
916
- <Button variant="outlined" data-testid="invalid-props">
917
- Invalid Props
918
- </Button>
919
- </Tooltip>
920
- </Box>
921
- ),
922
- play: async ({ canvasElement, step }) => {
923
- const canvas = within(canvasElement);
924
-
925
- await step('Empty content handling', async () => {
926
- const emptyButton = canvas.getByTestId('empty-tooltip');
927
- await userEvent.hover(emptyButton);
928
-
929
- // Should handle empty tooltip gracefully (might not show or show empty)
930
- await new Promise((resolve) => window.setTimeout(resolve, 500));
931
-
932
- await userEvent.unhover(emptyButton);
933
- });
934
-
935
- await step('Very long text overflow handling', async () => {
936
- const longTextButton = canvas.getByTestId('very-long-text');
937
- await userEvent.hover(longTextButton);
938
-
939
- await waitFor(
940
- async () => {
941
- const tooltip = await findTooltipByContent();
942
- await expect(tooltip).toBeInTheDocument();
943
- // Should contain the long text but be constrained by maxWidth
944
- await expect(tooltip).toHaveTextContent(/extremely long tooltip text/);
945
- },
946
- { timeout: 2000 },
947
- );
948
-
949
- await userEvent.unhover(longTextButton);
950
- });
951
-
952
- await step('Special characters handling', async () => {
953
- const specialButton = canvas.getByTestId('special-chars');
954
- await userEvent.hover(specialButton);
955
-
956
- await waitFor(
957
- async () => {
958
- const tooltip = await findTooltipByContent();
959
- await expect(tooltip).toBeInTheDocument();
960
- await expect(tooltip).toHaveTextContent(/Special chars:/);
961
- },
962
- { timeout: 2000 },
963
- );
964
-
965
- await userEvent.unhover(specialButton);
966
- });
967
-
968
- await step('HTML content sanitization', async () => {
969
- const htmlButton = canvas.getByTestId('html-content');
970
- await userEvent.hover(htmlButton);
971
-
972
- await waitFor(
973
- async () => {
974
- const tooltip = await findTooltipByContent();
975
- await expect(tooltip).toBeInTheDocument();
976
- // Should contain the text but not execute HTML/scripts
977
- await expect(tooltip).toHaveTextContent(/HTML content test/);
978
- },
979
- { timeout: 2000 },
980
- );
981
-
982
- await userEvent.unhover(htmlButton);
983
- });
984
-
985
- await step('Invalid props handling', async () => {
986
- const invalidButton = canvas.getByTestId('invalid-props');
987
-
988
- // Component should handle invalid props gracefully
989
- await expect(invalidButton).toBeInTheDocument();
990
-
991
- await userEvent.hover(invalidButton);
992
- // Should not crash and either show nothing or handle gracefully
993
- await new Promise((resolve) => window.setTimeout(resolve, 500));
994
- await userEvent.unhover(invalidButton);
995
- });
996
- },
997
- };
998
-
999
- // 7.7 Integration Tests
1000
- const IntegrationTestComponent = () => {
1001
- const [count, setCount] = React.useState(0);
1002
- const [showTooltips, setShowTooltips] = React.useState(true);
1003
-
1004
- return (
1005
- <Box sx={{ p: 4, display: 'flex', flexDirection: 'column', gap: 3, alignItems: 'center' }}>
1006
- <Typography variant="h6" data-testid="counter-display">
1007
- Count: {count}
1008
- </Typography>
1009
-
1010
- <Stack direction="row" spacing={2}>
1011
- <Button
1012
- variant="outlined"
1013
- onClick={() => setShowTooltips(!showTooltips)}
1014
- data-testid="toggle-tooltips"
1015
- >
1016
- Toggle Tooltips
1017
- </Button>
1018
-
1019
- <Button variant="outlined" onClick={() => setCount(0)} data-testid="reset-counter">
1020
- Reset
1021
- </Button>
1022
- </Stack>
1023
-
1024
- <Stack direction="row" spacing={2}>
1025
- {showTooltips ? (
1026
- <>
1027
- <Tooltip title={`Increment counter (current: ${count})`} variant="default">
1028
- <Button
1029
- variant="contained"
1030
- color="primary"
1031
- onClick={() => setCount((c) => c + 1)}
1032
- data-testid="increment-with-tooltip"
1033
- >
1034
- +
1035
- </Button>
1036
- </Tooltip>
1037
-
1038
- <Tooltip title={`Decrement counter (current: ${count})`} variant="dark">
1039
- <Button
1040
- variant="contained"
1041
- color="secondary"
1042
- onClick={() => setCount((c) => c - 1)}
1043
- data-testid="decrement-with-tooltip"
1044
- >
1045
- -
1046
- </Button>
1047
- </Tooltip>
1048
- </>
1049
- ) : (
1050
- <>
1051
- <Button
1052
- variant="contained"
1053
- color="primary"
1054
- onClick={() => setCount((c) => c + 1)}
1055
- data-testid="increment-without-tooltip"
1056
- >
1057
- +
1058
- </Button>
1059
-
1060
- <Button
1061
- variant="contained"
1062
- color="secondary"
1063
- onClick={() => setCount((c) => c - 1)}
1064
- data-testid="decrement-without-tooltip"
1065
- >
1066
- -
1067
- </Button>
1068
- </>
1069
- )}
1070
- </Stack>
1071
- </Box>
1072
- );
1073
- };
1074
-
1075
- export const IntegrationTest: Story = {
1076
- name: '🔗 Integration Test',
1077
- render: () => <IntegrationTestComponent />,
1078
- play: async ({ canvasElement, step }) => {
1079
- const canvas = within(canvasElement);
1080
-
1081
- await step('Integration with state management', async () => {
1082
- const counterDisplay = canvas.getByTestId('counter-display');
1083
- const incrementButton = canvas.getByTestId('increment-with-tooltip');
1084
-
1085
- await expect(counterDisplay).toHaveTextContent('Count: 0');
1086
-
1087
- // Click increment button
1088
- await userEvent.click(incrementButton);
1089
-
1090
- await waitFor(() => {
1091
- expect(counterDisplay).toHaveTextContent('Count: 1');
1092
- });
1093
- });
1094
-
1095
- await step('Tooltip content updates with state', async () => {
1096
- const incrementButton = canvas.getByTestId('increment-with-tooltip');
1097
-
1098
- // Hover to see tooltip with updated count
1099
- await userEvent.hover(incrementButton);
1100
-
1101
- await waitFor(
1102
- async () => {
1103
- const tooltip = await findTooltipByContent();
1104
- await expect(tooltip).toHaveTextContent('current: 1');
1105
- },
1106
- { timeout: 2000 },
1107
- );
1108
-
1109
- await userEvent.unhover(incrementButton);
1110
- });
1111
-
1112
- await step('Conditional tooltip rendering', async () => {
1113
- const toggleButton = canvas.getByTestId('toggle-tooltips');
1114
-
1115
- // Toggle tooltips off
1116
- await userEvent.click(toggleButton);
1117
-
1118
- // Verify buttons without tooltips exist
1119
- await waitFor(() => {
1120
- const incrementWithoutTooltip = canvas.getByTestId('increment-without-tooltip');
1121
- expect(incrementWithoutTooltip).toBeInTheDocument();
1122
- });
1123
- });
1124
-
1125
- await step('State reset integration', async () => {
1126
- const resetButton = canvas.getByTestId('reset-counter');
1127
- const counterDisplay = canvas.getByTestId('counter-display');
1128
-
1129
- await userEvent.click(resetButton);
1130
-
1131
- await waitFor(() => {
1132
- expect(counterDisplay).toHaveTextContent('Count: 0');
1133
- });
1134
-
1135
- // Toggle tooltips back on
1136
- const toggleButton = canvas.getByTestId('toggle-tooltips');
1137
- await userEvent.click(toggleButton);
1138
-
1139
- // Verify tooltip shows reset state
1140
- const incrementButton = canvas.getByTestId('increment-with-tooltip');
1141
- await userEvent.hover(incrementButton);
1142
-
1143
- await waitFor(
1144
- async () => {
1145
- const tooltip = await findTooltipByContent();
1146
- await expect(tooltip).toHaveTextContent('current: 0');
1147
- },
1148
- { timeout: 2000 },
1149
- );
1150
-
1151
- await userEvent.unhover(incrementButton);
1152
- });
1153
- },
1154
- };