@12-apps/ui 1.4.1 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/package.json +17 -8
  2. package/src/button/index.ts +1 -0
  3. package/src/social-login-button/index.ts +1 -0
  4. package/src/{social-login-button.tsx → social-login-button/social-login-button.tsx} +5 -5
  5. package/src/user-avatar/index.ts +1 -0
  6. package/src/{user-avatar.tsx → user-avatar/user-avatar.tsx} +4 -4
  7. package/eslint.config.js +0 -77
  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 -753
  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 -503
  27. package/src/components/data-display/Carousel/Carousel.stories.tsx +0 -607
  28. package/src/components/data-display/Carousel/Carousel.test.stories.tsx +0 -691
  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 -1101
  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 -955
  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 -975
  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 -909
  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 -918
  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 -750
  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 -472
  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 -762
  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,1194 +0,0 @@
1
- import CheckCircle from '@mui/icons-material/CheckCircle';
2
- import Mail from '@mui/icons-material/Mail';
3
- import Notifications from '@mui/icons-material/Notifications';
4
- import Person from '@mui/icons-material/Person';
5
- import Settings from '@mui/icons-material/Settings';
6
- import ShoppingCart from '@mui/icons-material/ShoppingCart';
7
- import Star from '@mui/icons-material/Star';
8
- import { Box, Button, IconButton } from '@mui/material';
9
- import type { Meta, StoryObj } from '@storybook/react-vite';
10
- import React, { useState } from 'react';
11
- import { expect, fn, userEvent, waitFor, within } from 'storybook/test';
12
-
13
- import { Badge } from './Badge';
14
-
15
- const meta: Meta<typeof Badge> = {
16
- title: 'DataDisplay/Badge/Tests',
17
- component: Badge,
18
- parameters: {
19
- layout: 'centered',
20
- chromatic: { disableSnapshot: false },
21
- },
22
- tags: ['autodocs', 'test', 'component:Badge'],
23
- };
24
-
25
- export default meta;
26
- type Story = StoryObj<typeof meta>;
27
-
28
- // Interaction Tests
29
- export const BasicInteraction: Story = {
30
- name: '🧪 Basic Interaction Test',
31
- args: {
32
- badgeContent: 5,
33
- onClick: fn(),
34
- children: (
35
- <IconButton data-testid="badge-button">
36
- <Mail />
37
- </IconButton>
38
- ),
39
- },
40
- play: async ({ canvasElement, step, args }) => {
41
- const canvas = within(canvasElement);
42
-
43
- await step('Initial render verification', async () => {
44
- const badge = canvas.getByText('5');
45
- await expect(badge).toBeInTheDocument();
46
- });
47
-
48
- await step('Click interaction on child element', async () => {
49
- const button = canvas.getByTestId('badge-button');
50
- await userEvent.click(button);
51
- await expect(args.onClick).toHaveBeenCalledTimes(1);
52
- });
53
-
54
- await step('Hover interaction', async () => {
55
- const badge = canvas.getByText('5');
56
- await userEvent.hover(badge);
57
- // Badge should maintain visibility on hover
58
- await expect(badge).toBeInTheDocument();
59
- });
60
- },
61
- };
62
-
63
- // Create a test component that demonstrates showZero behavior
64
- const CountVariantTestComponent = () => {
65
- const [count, setCount] = useState(0);
66
- const [showZero, setShowZero] = useState(false);
67
-
68
- return (
69
- <Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
70
- <Badge variant="count" badgeContent={count} showZero={showZero} max={99}>
71
- <ShoppingCart data-testid="cart-icon" />
72
- </Badge>
73
- <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
74
- <Button data-testid="toggle-show-zero" size="small" onClick={() => setShowZero(!showZero)}>
75
- Toggle Show Zero
76
- </Button>
77
- <Button data-testid="set-count-150" size="small" onClick={() => setCount(150)}>
78
- Set Count to 150
79
- </Button>
80
- <Button data-testid="reset-count" size="small" onClick={() => setCount(0)}>
81
- Reset Count
82
- </Button>
83
- </Box>
84
- </Box>
85
- );
86
- };
87
-
88
- export const CountVariantTest: Story = {
89
- name: '📊 Count Variant Test',
90
- render: () => <CountVariantTestComponent />,
91
- play: async ({ canvasElement, step }) => {
92
- const canvas = within(canvasElement);
93
-
94
- await step('Zero count should be hidden when showZero is false', async () => {
95
- // Initially showZero is false and count is 0
96
- await waitFor(() => expect(canvas.queryByText('0')).not.toBeInTheDocument());
97
- });
98
-
99
- await step('Toggle to show zero', async () => {
100
- const toggleButton = canvas.getByTestId('toggle-show-zero');
101
- await userEvent.click(toggleButton);
102
-
103
- // Now badge should show '0'
104
- const badge = await canvas.findByText('0');
105
- await expect(badge).toBeInTheDocument();
106
- });
107
-
108
- await step('Test max count formatting', async () => {
109
- const setCountButton = canvas.getByTestId('set-count-150');
110
- await userEvent.click(setCountButton);
111
-
112
- // Should show '99+' because 150 > 99
113
- const badge = await canvas.findByText('99+');
114
- await expect(badge).toBeInTheDocument();
115
- });
116
- },
117
- };
118
-
119
- export const DotVariantTest: Story = {
120
- name: '🔴 Dot Variant Test',
121
- args: {
122
- variant: 'dot',
123
- color: 'success',
124
- pulse: false,
125
- children: <Notifications data-testid="notification-icon" />,
126
- },
127
- play: async ({ canvasElement, step }) => {
128
- const canvas = within(canvasElement);
129
-
130
- await step('Verify dot variant rendering', async () => {
131
- // Dot variant shouldn't show any text content
132
- const badges = canvas.queryAllByText(/\d+/);
133
- await expect(badges).toHaveLength(0);
134
-
135
- // Should have the notification icon
136
- const icon = canvas.getByTestId('notification-icon');
137
- await expect(icon).toBeInTheDocument();
138
- });
139
-
140
- await step('Verify dot badge is rendered', async () => {
141
- const badge = canvasElement.querySelector('.MuiBadge-dot');
142
- await expect(badge).toBeInTheDocument();
143
- // Dot badge should have the success color applied
144
- if (badge) {
145
- const badgeStyle = window.getComputedStyle(badge);
146
- // Check that it has some background color (not transparent)
147
- await expect(badgeStyle.backgroundColor).not.toBe('transparent');
148
- }
149
- });
150
- },
151
- };
152
-
153
- // Accessibility Tests
154
- export const KeyboardNavigation: Story = {
155
- name: '⌨️ Keyboard Navigation Test',
156
- args: {
157
- badgeContent: 'NEW',
158
- color: 'primary',
159
- children: (
160
- <Box sx={{ display: 'flex', gap: 2 }}>
161
- <IconButton data-testid="first-focusable" aria-label="First button with badge">
162
- <Mail />
163
- </IconButton>
164
- <IconButton data-testid="second-focusable" aria-label="Second button with badge">
165
- <Notifications />
166
- </IconButton>
167
- </Box>
168
- ),
169
- },
170
- parameters: {
171
- a11y: {
172
- element: '#storybook-root',
173
- config: {
174
- rules: [
175
- { id: 'color-contrast', enabled: true },
176
- { id: 'aria-required-attr', enabled: true },
177
- { id: 'aria-roles', enabled: true },
178
- { id: 'aria-valid-attr-value', enabled: true },
179
- { id: 'button-name', enabled: true },
180
- ],
181
- },
182
- },
183
- },
184
- play: async ({ canvasElement, step }) => {
185
- const canvas = within(canvasElement);
186
-
187
- await step('Tab navigation forward', async () => {
188
- const firstButton = canvas.getByTestId('first-focusable');
189
- const secondButton = canvas.getByTestId('second-focusable');
190
-
191
- // Tab in from the body — the old .focus() call plus assertion only proved
192
- // the DOM honours focus(), not that this button is the first tab stop.
193
- await userEvent.tab();
194
- await waitFor(() => expect(firstButton).toHaveFocus());
195
-
196
- // Tab to next element
197
- await userEvent.tab();
198
- await waitFor(() => expect(secondButton).toHaveFocus());
199
- });
200
-
201
- await step('Tab navigation backward', async () => {
202
- await userEvent.tab({ shift: true });
203
- const firstButton = canvas.getByTestId('first-focusable');
204
- await waitFor(() => expect(firstButton).toHaveFocus());
205
- });
206
-
207
- await step('Enter key activation on focused button', async () => {
208
- const button = canvas.getByTestId('first-focusable');
209
- // Focus is already back on this button from the shift-tab above.
210
- await waitFor(() => expect(button).toHaveFocus());
211
-
212
- await userEvent.keyboard('{Enter}');
213
- // Focus must survive activation rather than jump elsewhere.
214
- await waitFor(() => expect(button).toHaveFocus());
215
- });
216
- },
217
- };
218
-
219
- export const ScreenReaderTest: Story = {
220
- name: '🔊 Screen Reader Test',
221
- args: {
222
- badgeContent: 12,
223
- color: 'error',
224
- 'aria-label': '12 new notifications',
225
- children: (
226
- <IconButton aria-label="Notifications">
227
- <Notifications />
228
- </IconButton>
229
- ),
230
- },
231
- play: async ({ canvasElement, step }) => {
232
- const canvas = within(canvasElement);
233
-
234
- await step('Verify ARIA labels on button', async () => {
235
- const button = canvas.getByLabelText('Notifications');
236
- await expect(button).toBeInTheDocument();
237
- });
238
-
239
- await step('Verify badge content is readable', async () => {
240
- const badge = canvas.getByText('12');
241
- await expect(badge).toBeInTheDocument();
242
- });
243
-
244
- await step('Verify role attributes', async () => {
245
- const button = canvas.getByRole('button');
246
- await expect(button).toBeInTheDocument();
247
- });
248
- },
249
- };
250
-
251
- // Visual Tests
252
- export const ResponsiveDesign: Story = {
253
- name: '📱 Responsive Design Test',
254
- args: {
255
- badgeContent: 99,
256
- size: 'md',
257
- children: <Mail data-testid="responsive-icon" />,
258
- },
259
- parameters: {
260
- viewport: {
261
- viewports: {
262
- mobile: {
263
- name: 'Mobile',
264
- styles: { width: '375px', height: '667px' },
265
- type: 'mobile',
266
- },
267
- tablet: {
268
- name: 'Tablet',
269
- styles: { width: '768px', height: '1024px' },
270
- type: 'tablet',
271
- },
272
- desktop: {
273
- name: 'Desktop',
274
- styles: { width: '1920px', height: '1080px' },
275
- type: 'desktop',
276
- },
277
- },
278
- defaultViewport: 'mobile',
279
- },
280
- chromatic: {
281
- viewports: [375, 768, 1920],
282
- delay: 300,
283
- },
284
- },
285
- play: async ({ canvasElement, step }) => {
286
- const canvas = within(canvasElement);
287
-
288
- await step('Verify badge scales appropriately', async () => {
289
- const badge = canvas.getByText('99');
290
- await expect(badge).toBeInTheDocument();
291
-
292
- const computedStyle = window.getComputedStyle(badge);
293
- const fontSize = parseFloat(computedStyle.fontSize);
294
-
295
- // Font size should be reasonable for the viewport
296
- await expect(fontSize).toBeGreaterThan(8);
297
- await expect(fontSize).toBeLessThan(20);
298
- });
299
- },
300
- };
301
-
302
- export const ThemeVariations: Story = {
303
- name: '🎨 Theme Variations Test',
304
- args: {
305
- badgeContent: 'HOT',
306
- color: 'error',
307
- variant: 'gradient',
308
- glow: true,
309
- children: <ShoppingCart />,
310
- },
311
- parameters: {
312
- backgrounds: {
313
- default: 'light',
314
- values: [
315
- { name: 'light', value: '#ffffff' },
316
- { name: 'dark', value: '#1a1a1a' },
317
- ],
318
- },
319
- },
320
- play: async ({ canvasElement, step }) => {
321
- const canvas = within(canvasElement);
322
-
323
- await step('Verify theme colors', async () => {
324
- const badgeText = canvas.getByText('HOT');
325
- // Get the parent badge element that has the gradient
326
- const badgeElement = badgeText.closest('.MuiBadge-badge');
327
-
328
- if (badgeElement) {
329
- const computedStyle = window.getComputedStyle(badgeElement);
330
- // Check if gradient is applied
331
- const background = computedStyle.background || computedStyle.backgroundImage;
332
- await expect(background).toMatch(/gradient|linear/i);
333
- }
334
- });
335
-
336
- await step('Verify glow effect', async () => {
337
- const badgeText = canvas.getByText('HOT');
338
- const badgeElement = badgeText.closest('.MuiBadge-badge');
339
-
340
- if (badgeElement) {
341
- const computedStyle = window.getComputedStyle(badgeElement);
342
- // Check for box shadow (glow effect)
343
- await expect(computedStyle.boxShadow).not.toBe('none');
344
- }
345
- });
346
- },
347
- };
348
-
349
- export const VisualStates: Story = {
350
- name: '👁️ Visual States Test',
351
- args: {
352
- badgeContent: 5,
353
- color: 'primary',
354
- children: (
355
- <Box sx={{ display: 'flex', gap: 3 }}>
356
- <IconButton data-testid="normal-button">
357
- <Badge badgeContent={5}>
358
- <Mail />
359
- </Badge>
360
- </IconButton>
361
- <IconButton data-testid="disabled-button" disabled>
362
- <Badge badgeContent={5}>
363
- <Mail />
364
- </Badge>
365
- </IconButton>
366
- </Box>
367
- ),
368
- },
369
- play: async ({ canvasElement, step }) => {
370
- const canvas = within(canvasElement);
371
-
372
- await step('Default state', async () => {
373
- const normalButton = canvas.getByTestId('normal-button');
374
- await expect(normalButton).not.toBeDisabled();
375
- await expect(normalButton).toHaveStyle({ opacity: '1' });
376
- });
377
-
378
- await step('Hover state', async () => {
379
- const normalButton = canvas.getByTestId('normal-button');
380
- await userEvent.hover(normalButton);
381
- // Button should respond to hover
382
- await expect(normalButton).toBeInTheDocument();
383
- });
384
-
385
- await step('Disabled state', async () => {
386
- const disabledButton = canvas.getByTestId('disabled-button');
387
- await expect(disabledButton).toBeDisabled();
388
- const computedStyle = window.getComputedStyle(disabledButton);
389
- const opacity = parseFloat(computedStyle.opacity);
390
- // Disabled buttons should have reduced opacity or be 1 (theme dependent)
391
- await expect(opacity).toBeLessThanOrEqual(1);
392
- });
393
- },
394
- };
395
-
396
- // Performance Tests
397
- export const PerformanceTest: Story = {
398
- name: '⚡ Performance Test',
399
- args: {
400
- children: (
401
- <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 2 }} data-testid="badge-container">
402
- {Array.from({ length: 20 }, (_, i) => (
403
- <Box key={i} data-testid={`badge-wrapper-${i}`}>
404
- <Badge badgeContent={i + 1}>
405
- <Mail />
406
- </Badge>
407
- </Box>
408
- ))}
409
- </Box>
410
- ),
411
- },
412
- parameters: {
413
- chromatic: { disableSnapshot: false, delay: 500 },
414
- },
415
- play: async ({ canvasElement, step }) => {
416
- const canvas = within(canvasElement);
417
-
418
- await step('Verify all badges render', async () => {
419
- const container = canvas.getByTestId('badge-container');
420
- await expect(container).toBeInTheDocument();
421
-
422
- // Count wrapper elements to ensure correct number of badges
423
- const wrappers = container.querySelectorAll('[data-testid^="badge-wrapper-"]');
424
- expect(wrappers.length).toBe(20);
425
- });
426
-
427
- await step('Verify badge content integrity', async () => {
428
- // Check that badge content renders correctly
429
- for (let i = 0; i < 5; i++) {
430
- const wrapper = canvas.getByTestId(`badge-wrapper-${i}`);
431
- await expect(wrapper).toBeInTheDocument();
432
- // Verify the badge content exists
433
- const badgeContent = canvas.getByText(String(i + 1));
434
- await expect(badgeContent).toBeInTheDocument();
435
- }
436
- });
437
-
438
- await step('Verify container is visible', async () => {
439
- const container = canvas.getByTestId('badge-container');
440
- await expect(container).toBeVisible();
441
- });
442
- },
443
- };
444
-
445
- // Edge Cases Tests
446
- export const EdgeCases: Story = {
447
- name: '🔧 Edge Cases Test',
448
- args: {
449
- badgeContent: '',
450
- children: <Mail data-testid="mail-icon" />,
451
- },
452
- play: async ({ canvasElement, step }) => {
453
- const canvas = within(canvasElement);
454
-
455
- await step('Empty content handling', async () => {
456
- // Badge with empty content should not crash
457
- const icon = canvas.getByTestId('mail-icon');
458
- await expect(icon).toBeInTheDocument();
459
- });
460
-
461
- await step('Long text overflow', async () => {
462
- // We can't modify args mid-test. Instead check the current badge renders
463
- const icon = canvas.getByTestId('mail-icon');
464
- await expect(icon).toBeInTheDocument();
465
- // The badge has empty content from args, so it won't have visible badge element
466
- });
467
-
468
- await step('Special characters handling', async () => {
469
- // We need to find a way to test special characters without modifying args mid-test
470
- // Instead, let's check if the component can render without errors
471
- const icon = canvas.getByTestId('mail-icon');
472
- await expect(icon).toBeInTheDocument();
473
- // The test needs to be structured to render the special chars in the args from the start
474
- });
475
-
476
- await step('Maximum number handling', async () => {
477
- // Like the previous step, we can't modify args mid-test
478
- // Let's just check that the component renders properly
479
- const icon = canvas.getByTestId('mail-icon');
480
- await expect(icon).toBeInTheDocument();
481
- });
482
- },
483
- };
484
-
485
- // Animation Tests
486
- export const AnimationTest: Story = {
487
- name: '🎬 Animation Test',
488
- args: {
489
- variant: 'dot',
490
- color: 'success',
491
- pulse: true,
492
- glow: false,
493
- children: <Notifications data-testid="animated-icon" />,
494
- },
495
- play: async ({ canvasElement, step }) => {
496
- within(canvasElement);
497
-
498
- await step('Verify pulse animation', async () => {
499
- const badge = canvasElement.querySelector('.MuiBadge-dot');
500
- await expect(badge).not.toBeNull();
501
- await expect(badge).toBeInTheDocument();
502
-
503
- const computedStyle = window.getComputedStyle(badge as Element);
504
- // Check if animation is applied
505
- await expect(computedStyle.animationName).not.toBe('none');
506
- await expect(computedStyle.animationDuration).toBe('2s');
507
- });
508
-
509
- await step('Check initial pulse styling', async () => {
510
- // Since glow is false in args, we only have pulse animation
511
- const badge = canvasElement.querySelector('.MuiBadge-dot');
512
- if (badge) {
513
- const computedStyle = window.getComputedStyle(badge);
514
- // Check if some styling is applied (pulse animation should be active)
515
- await expect(computedStyle.animationName).not.toBe('none');
516
- }
517
- });
518
- },
519
- };
520
-
521
- // Special Characters Test (as separate story)
522
- export const SpecialCharactersTest: Story = {
523
- name: '🔤 Special Characters Test',
524
- args: {
525
- badgeContent: '!@#$%',
526
- children: <Mail data-testid="special-chars-icon" />,
527
- },
528
- play: async ({ canvasElement, step }) => {
529
- const canvas = within(canvasElement);
530
-
531
- await step('Verify special characters render', async () => {
532
- const badge = canvas.getByText('!@#$%');
533
- await expect(badge).toBeInTheDocument();
534
- });
535
-
536
- await step('Verify icon is present', async () => {
537
- const icon = canvas.getByTestId('special-chars-icon');
538
- await expect(icon).toBeInTheDocument();
539
- });
540
- },
541
- };
542
-
543
- // Long Text Test (as separate story)
544
- export const LongTextTest: Story = {
545
- name: '📝 Long Text Test',
546
- args: {
547
- badgeContent: 'VERYLONGTEXTCONTENT',
548
- children: <Mail data-testid="long-text-icon" />,
549
- },
550
- play: async ({ canvasElement, step }) => {
551
- const canvas = within(canvasElement);
552
-
553
- await step('Verify long text renders', async () => {
554
- const badge = canvas.getByText('VERYLONGTEXTCONTENT');
555
- await expect(badge).toBeInTheDocument();
556
-
557
- const computedStyle = window.getComputedStyle(badge);
558
- // Badge should handle long text appropriately - width should be at least 20px
559
- expect(parseFloat(computedStyle.width)).toBeGreaterThanOrEqual(20);
560
- });
561
- },
562
- };
563
-
564
- // Max Number Test (as separate story)
565
- export const MaxNumberTest: Story = {
566
- name: '🔢 Max Number Test',
567
- args: {
568
- badgeContent: 9999,
569
- max: 999,
570
- variant: 'count',
571
- children: <Mail data-testid="max-number-icon" />,
572
- },
573
- play: async ({ canvasElement, step }) => {
574
- const canvas = within(canvasElement);
575
-
576
- await step('Verify max number format', async () => {
577
- const badge = canvas.getByText('999+');
578
- await expect(badge).toBeInTheDocument();
579
- });
580
- },
581
- };
582
-
583
- // Position Tests
584
- export const PositionTest: Story = {
585
- name: '📍 Position Test',
586
- args: {
587
- badgeContent: 1,
588
- position: 'top-right',
589
- children: (
590
- <Box
591
- sx={{ width: 60, height: 60, bgcolor: 'grey.300', borderRadius: 1 }}
592
- data-testid="position-box"
593
- />
594
- ),
595
- },
596
- play: async ({ canvasElement, step }) => {
597
- const canvas = within(canvasElement);
598
-
599
- await step('Test initial position renders correctly', async () => {
600
- const badge = canvas.getByText('1');
601
- await expect(badge).toBeInTheDocument();
602
-
603
- const box = canvas.getByTestId('position-box');
604
- await expect(box).toBeInTheDocument();
605
-
606
- // Badge should be positioned relative to the box
607
- const badgeRect = badge.getBoundingClientRect();
608
- const boxRect = box.getBoundingClientRect();
609
-
610
- // For top-right position, badge should be positioned relative to the box
611
- // Allow for edge cases where positions might be equal due to styling
612
- expect(badgeRect.left).toBeGreaterThanOrEqual(boxRect.left);
613
- expect(badgeRect.top).toBeLessThanOrEqual(boxRect.bottom);
614
- });
615
- },
616
- };
617
-
618
- // Integration Test
619
- export const IntegrationTest: Story = {
620
- name: '🔗 Integration Test',
621
- render: (args) => (
622
- <Box sx={{ display: 'flex', gap: 3, alignItems: 'center' }}>
623
- <Badge {...args} badgeContent={3} color="primary">
624
- <IconButton data-testid="primary-button">
625
- <Mail />
626
- </IconButton>
627
- </Badge>
628
- <Badge {...args} variant="dot" color="success">
629
- <IconButton data-testid="success-button">
630
- <Notifications />
631
- </IconButton>
632
- </Badge>
633
- <Badge {...args} badgeContent={99} color="error" max={99}>
634
- <IconButton data-testid="error-button">
635
- <ShoppingCart />
636
- </IconButton>
637
- </Badge>
638
- </Box>
639
- ),
640
- play: async ({ canvasElement, step }) => {
641
- const canvas = within(canvasElement);
642
-
643
- await step('Verify multiple badges render correctly', async () => {
644
- const primaryBadge = canvas.getByText('3');
645
- const errorBadge = canvas.getByText('99');
646
-
647
- await expect(primaryBadge).toBeInTheDocument();
648
- await expect(errorBadge).toBeInTheDocument();
649
- });
650
-
651
- await step('Interact with different badged buttons', async () => {
652
- const buttons = ['primary-button', 'success-button', 'error-button'];
653
-
654
- for (const buttonId of buttons) {
655
- const button = canvas.getByTestId(buttonId);
656
- await userEvent.click(button);
657
- await expect(button).toBeInTheDocument();
658
- }
659
- });
660
- },
661
- };
662
-
663
- // Test for new variants
664
- export const NewVariantsTest: Story = {
665
- name: '🎨 New Variants Test',
666
- render: () => (
667
- <Box sx={{ display: 'flex', gap: 2, flexWrap: 'wrap' }}>
668
- <Badge variant="outline" badgeContent="Outline" color="primary">
669
- <Box sx={{ p: 2, bgcolor: 'grey.100' }}>Outline</Box>
670
- </Badge>
671
- <Badge variant="secondary" badgeContent="Secondary" color="secondary">
672
- <Box sx={{ p: 2, bgcolor: 'grey.100' }}>Secondary</Box>
673
- </Badge>
674
- <Badge variant="destructive" badgeContent="!" color="error">
675
- <Box sx={{ p: 2, bgcolor: 'grey.100' }}>Destructive</Box>
676
- </Badge>
677
- <Badge variant="success" badgeContent="✓" color="success">
678
- <Box sx={{ p: 2, bgcolor: 'grey.100' }}>Success</Box>
679
- </Badge>
680
- <Badge variant="warning" badgeContent="⚠" color="warning">
681
- <Box sx={{ p: 2, bgcolor: 'grey.100' }}>Warning</Box>
682
- </Badge>
683
- </Box>
684
- ),
685
- play: async ({ canvasElement, step }) => {
686
- const canvas = within(canvasElement);
687
-
688
- await step('Verify all new variants render correctly', async () => {
689
- // Use more specific selectors to avoid ambiguity
690
- const badges = canvasElement.querySelectorAll('.MuiBadge-badge');
691
-
692
- // Check that we have the expected number of badges
693
- expect(badges).toHaveLength(5);
694
-
695
- // Check specific variant content by finding within badge elements
696
- const outlineBadge = Array.from(badges).find((badge) => badge.textContent === 'Outline');
697
- const secondaryBadge = Array.from(badges).find((badge) => badge.textContent === 'Secondary');
698
- const destructiveBadge = canvas.getByText('!');
699
- const successBadge = canvas.getByText('✓');
700
- const warningBadge = canvas.getByText('⚠');
701
-
702
- await expect(outlineBadge).toBeTruthy();
703
- await expect(secondaryBadge).toBeTruthy();
704
- await expect(destructiveBadge).toBeInTheDocument();
705
- await expect(successBadge).toBeInTheDocument();
706
- await expect(warningBadge).toBeInTheDocument();
707
- });
708
- },
709
- };
710
-
711
- // Test closable badges
712
- export const ClosableBadgeTest: Story = {
713
- name: '❌ Closable Badge Test',
714
- args: {
715
- badgeContent: 'Close Me',
716
- closable: true,
717
- onClose: fn(),
718
- color: 'primary',
719
- variant: 'secondary',
720
- children: <Box sx={{ p: 2, bgcolor: 'grey.200' }}>Content</Box>,
721
- },
722
- play: async ({ canvasElement, step, args }) => {
723
- const canvas = within(canvasElement);
724
-
725
- await step('Verify closable badge renders with close button', async () => {
726
- const badge = canvas.getByText('Close Me');
727
- await expect(badge).toBeInTheDocument();
728
-
729
- // Check for close button
730
- const closeButtons = canvasElement.querySelectorAll('button');
731
- const closeButton = Array.from(closeButtons).find((btn) =>
732
- btn.querySelector('svg[data-testid="CloseIcon"]'),
733
- );
734
- await expect(closeButton).toBeTruthy();
735
- });
736
-
737
- await step('Click close button', async () => {
738
- const closeButtons = canvasElement.querySelectorAll('button');
739
- const closeButton = Array.from(closeButtons).find((btn) =>
740
- btn.querySelector('svg[data-testid="CloseIcon"]'),
741
- ) as HTMLButtonElement | undefined;
742
-
743
- if (closeButton) {
744
- await userEvent.click(closeButton);
745
- // Wait for fade out animation
746
- await waitFor(
747
- () => {
748
- expect(args.onClose).toHaveBeenCalledTimes(1);
749
- },
750
- { timeout: 500 },
751
- );
752
- }
753
- });
754
- },
755
- };
756
-
757
- // Test badges with icons
758
- export const BadgeWithIconTest: Story = {
759
- name: '🎯 Badge with Icon Test',
760
- args: {
761
- icon: <CheckCircle sx={{ fontSize: 'inherit' }} />,
762
- badgeContent: 'Success',
763
- color: 'success',
764
- variant: 'success',
765
- children: <Box sx={{ p: 2, bgcolor: 'grey.100' }}>Task</Box>,
766
- },
767
- play: async ({ canvasElement, step }) => {
768
- const canvas = within(canvasElement);
769
-
770
- await step('Verify badge renders with icon and text', async () => {
771
- const badge = canvas.getByText('Success');
772
- await expect(badge).toBeInTheDocument();
773
-
774
- // Check for icon presence
775
- const icon = canvasElement.querySelector('svg[data-testid="CheckCircleIcon"]');
776
- await expect(icon).toBeTruthy();
777
- });
778
- },
779
- };
780
-
781
- // Test shimmer effect
782
- export const ShimmerEffectTest: Story = {
783
- name: '✨ Shimmer Effect Test',
784
- args: {
785
- badgeContent: 'PREMIUM',
786
- shimmer: true,
787
- variant: 'gradient',
788
- color: 'primary',
789
- children: <Star data-testid="star-icon" />,
790
- },
791
- play: async ({ canvasElement, step }) => {
792
- const canvas = within(canvasElement);
793
-
794
- await step('Verify shimmer effect is applied', async () => {
795
- const badge = canvas.getByText('PREMIUM');
796
- await expect(badge).toBeInTheDocument();
797
-
798
- // Check for shimmer animation (pseudo-element will have animation)
799
- const badgeElement = badge.closest('.MuiBadge-badge');
800
- await expect(badgeElement).toBeTruthy();
801
- });
802
- },
803
- };
804
-
805
- // Test bounce animation
806
- export const BounceAnimationTest: Story = {
807
- name: '🏀 Bounce Animation Test',
808
- args: {
809
- badgeContent: 'NEW',
810
- bounce: true,
811
- color: 'success',
812
- children: <Box sx={{ p: 2, bgcolor: 'grey.100' }}>Item</Box>,
813
- },
814
- play: async ({ canvasElement, step }) => {
815
- const canvas = within(canvasElement);
816
-
817
- await step('Verify bounce animation is applied', async () => {
818
- const badge = canvas.getByText('NEW');
819
- await expect(badge).toBeInTheDocument();
820
-
821
- const computedStyle = window.getComputedStyle(badge);
822
- // Check if animation is applied
823
- await expect(computedStyle.animationName).toBeTruthy();
824
- });
825
- },
826
- };
827
-
828
- // Test extra small size
829
- export const ExtraSmallSizeTest: Story = {
830
- name: '🔬 Extra Small Size Test',
831
- args: {
832
- size: 'xs',
833
- badgeContent: 2,
834
- color: 'primary',
835
- children: <Mail data-testid="mail-icon" />,
836
- },
837
- play: async ({ canvasElement, step }) => {
838
- const canvas = within(canvasElement);
839
-
840
- await step('Verify extra small size renders correctly', async () => {
841
- const badge = canvas.getByText('2');
842
- await expect(badge).toBeInTheDocument();
843
-
844
- const computedStyle = window.getComputedStyle(badge);
845
- const fontSize = parseFloat(computedStyle.fontSize);
846
-
847
- // Extra small should have smallest font size
848
- await expect(fontSize).toBeLessThan(10);
849
- });
850
- },
851
- };
852
-
853
- // State Management Test
854
- const StateManagementComponent = () => {
855
- const [count, setCount] = useState(0);
856
- const [isVisible, setIsVisible] = useState(true);
857
-
858
- return (
859
- <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, alignItems: 'center' }}>
860
- <Box sx={{ display: 'flex', gap: 2 }}>
861
- <Button onClick={() => setCount((c) => c + 1)} data-testid="increment-button">
862
- Increment ({count})
863
- </Button>
864
- <Button onClick={() => setCount((c) => Math.max(0, c - 1))} data-testid="decrement-button">
865
- Decrement
866
- </Button>
867
- <Button onClick={() => setIsVisible((v) => !v)} data-testid="toggle-visibility">
868
- Toggle Visibility
869
- </Button>
870
- </Box>
871
- <Badge
872
- badgeContent={count}
873
- color="primary"
874
- invisible={!isVisible}
875
- showZero
876
- data-testid="dynamic-badge"
877
- >
878
- <Mail data-testid="mail-with-badge" />
879
- </Badge>
880
- <Box data-testid="count-display">Count: {count}</Box>
881
- <Box data-testid="visibility-display">Visible: {isVisible.toString()}</Box>
882
- </Box>
883
- );
884
- };
885
-
886
- export const StateManagementTest: Story = {
887
- name: '🔄 State Management Test',
888
- render: () => <StateManagementComponent />,
889
- play: async ({ canvasElement, step }) => {
890
- const canvas = within(canvasElement);
891
-
892
- await step('Initial state verification', async () => {
893
- const countDisplay = canvas.getByTestId('count-display');
894
- const visibilityDisplay = canvas.getByTestId('visibility-display');
895
-
896
- await expect(countDisplay).toHaveTextContent('Count: 0');
897
- await expect(visibilityDisplay).toHaveTextContent('Visible: true');
898
- });
899
-
900
- await step('Increment count updates badge', async () => {
901
- const incrementButton = canvas.getByTestId('increment-button');
902
- await userEvent.click(incrementButton);
903
-
904
- const badge = canvas.getByText('1');
905
- await expect(badge).toBeInTheDocument();
906
-
907
- const countDisplay = canvas.getByTestId('count-display');
908
- await expect(countDisplay).toHaveTextContent('Count: 1');
909
- });
910
-
911
- await step('Multiple increments', async () => {
912
- const incrementButton = canvas.getByTestId('increment-button');
913
- await userEvent.click(incrementButton);
914
- await userEvent.click(incrementButton);
915
- await userEvent.click(incrementButton);
916
-
917
- const badge = canvas.getByText('4');
918
- await expect(badge).toBeInTheDocument();
919
- });
920
-
921
- await step('Decrement count', async () => {
922
- const decrementButton = canvas.getByTestId('decrement-button');
923
- await userEvent.click(decrementButton);
924
- await userEvent.click(decrementButton);
925
-
926
- const badge = canvas.getByText('2');
927
- await expect(badge).toBeInTheDocument();
928
- });
929
-
930
- await step('Toggle visibility', async () => {
931
- const toggleButton = canvas.getByTestId('toggle-visibility');
932
- await userEvent.click(toggleButton);
933
-
934
- const visibilityDisplay = canvas.getByTestId('visibility-display');
935
- await expect(visibilityDisplay).toHaveTextContent('Visible: false');
936
-
937
- // Badge should be hidden - just verify the visibility state is updated
938
- await waitFor(
939
- () => {
940
- const visibilityDisplay = canvas.getByTestId('visibility-display');
941
- expect(visibilityDisplay).toHaveTextContent('Visible: false');
942
- },
943
- { timeout: 1000 },
944
- );
945
- });
946
-
947
- await step('Toggle visibility back', async () => {
948
- const toggleButton = canvas.getByTestId('toggle-visibility');
949
- await userEvent.click(toggleButton);
950
-
951
- const badge = canvas.getByText('2');
952
- await expect(badge).toBeInTheDocument();
953
- });
954
- },
955
- };
956
-
957
- // Focus Management Enhanced Test
958
- const FocusManagementComponent = () => {
959
- const [activeIndex, setActiveIndex] = useState(0);
960
- const items = [
961
- { label: 'Messages', icon: <Mail />, count: 5 },
962
- { label: 'Notifications', icon: <Notifications />, count: 12 },
963
- { label: 'Settings', icon: <Settings />, count: 3 },
964
- { label: 'Profile', icon: <Person />, count: 0 },
965
- ];
966
-
967
- return (
968
- <Box sx={{ display: 'flex', gap: 2 }} role="tablist">
969
- {items.map((item, index) => (
970
- <Badge
971
- key={item.label}
972
- badgeContent={item.count}
973
- color="primary"
974
- showZero={index === 3} // Show zero for profile
975
- >
976
- <Button
977
- data-testid={`focus-item-${index}`}
978
- onFocus={() => setActiveIndex(index)}
979
- sx={{
980
- minWidth: 80,
981
- border: activeIndex === index ? 2 : 1,
982
- borderColor: activeIndex === index ? 'primary.main' : 'grey.300',
983
- borderStyle: 'solid',
984
- }}
985
- aria-label={`${item.label}, ${item.count} notifications`}
986
- >
987
- {item.icon}
988
- </Button>
989
- </Badge>
990
- ))}
991
- <Box data-testid="active-index" aria-live="polite">
992
- Active: {activeIndex}
993
- </Box>
994
- </Box>
995
- );
996
- };
997
-
998
- export const FocusManagementTest: Story = {
999
- name: '🔍 Focus Management Test',
1000
- render: () => <FocusManagementComponent />,
1001
- play: async ({ canvasElement, step }) => {
1002
- const canvas = within(canvasElement);
1003
-
1004
- await step('Initial focus state', async () => {
1005
- const activeDisplay = canvas.getByTestId('active-index');
1006
- await expect(activeDisplay).toHaveTextContent('Active: 0');
1007
- });
1008
-
1009
- await step('Tab through focusable elements', async () => {
1010
- // Tab in from the body to reach the first item.
1011
- const firstItem = canvas.getByTestId('focus-item-0');
1012
- await userEvent.tab();
1013
- await waitFor(() => expect(firstItem).toHaveFocus());
1014
-
1015
- // Tab to second item
1016
- await userEvent.tab();
1017
- const secondItem = canvas.getByTestId('focus-item-1');
1018
- await waitFor(() => expect(secondItem).toHaveFocus());
1019
-
1020
- const activeDisplay = canvas.getByTestId('active-index');
1021
- await expect(activeDisplay).toHaveTextContent('Active: 1');
1022
-
1023
- // Tab to third item
1024
- await userEvent.tab();
1025
- const thirdItem = canvas.getByTestId('focus-item-2');
1026
- await waitFor(() => expect(thirdItem).toHaveFocus());
1027
-
1028
- await expect(canvas.getByTestId('active-index')).toHaveTextContent('Active: 2');
1029
- });
1030
-
1031
- await step('Reverse tab navigation', async () => {
1032
- await userEvent.tab({ shift: true });
1033
- const secondItem = canvas.getByTestId('focus-item-1');
1034
- await waitFor(() => expect(secondItem).toHaveFocus());
1035
-
1036
- const activeDisplay = canvas.getByTestId('active-index');
1037
- await expect(activeDisplay).toHaveTextContent('Active: 1');
1038
- });
1039
-
1040
- await step('Verify badge content accessibility', async () => {
1041
- const button = canvas.getByLabelText(/Messages, 5 notifications/);
1042
- await expect(button).toBeInTheDocument();
1043
-
1044
- const profileButton = canvas.getByLabelText(/Profile, 0 notifications/);
1045
- await expect(profileButton).toBeInTheDocument();
1046
- });
1047
- },
1048
- };
1049
-
1050
- // WCAG Compliance Test
1051
- export const WCAGComplianceTest: Story = {
1052
- name: '♿ WCAG Compliance Test',
1053
- args: {
1054
- badgeContent: 'Alert',
1055
- color: 'error',
1056
- 'aria-label': 'Error alert notification',
1057
- 'aria-live': 'assertive',
1058
- 'aria-atomic': true,
1059
- children: (
1060
- <Button aria-describedby="badge-description" data-testid="wcag-button">
1061
- System Status
1062
- </Button>
1063
- ),
1064
- },
1065
- parameters: {
1066
- a11y: {
1067
- element: '#storybook-root',
1068
- config: {
1069
- rules: [
1070
- { id: 'color-contrast', enabled: true },
1071
- { id: 'aria-required-attr', enabled: true },
1072
- { id: 'aria-roles', enabled: true },
1073
- { id: 'aria-valid-attr-value', enabled: true },
1074
- { id: 'button-name', enabled: true },
1075
- { id: 'link-name', enabled: true },
1076
- { id: 'label', enabled: true },
1077
- ],
1078
- },
1079
- },
1080
- },
1081
- render: (args) => (
1082
- <Box>
1083
- <Badge {...args} />
1084
- <div id="badge-description" style={{ position: 'absolute', left: '-10000px' }}>
1085
- This badge indicates system status alerts
1086
- </div>
1087
- </Box>
1088
- ),
1089
- play: async ({ canvasElement, step }) => {
1090
- const canvas = within(canvasElement);
1091
-
1092
- await step('Verify ARIA attributes', async () => {
1093
- const badge = canvas.getByText('Alert');
1094
- await expect(badge).toBeInTheDocument();
1095
-
1096
- // Check that badge has proper ARIA live region
1097
- const badgeElement = badge.closest('[aria-live]');
1098
- await expect(badgeElement).toHaveAttribute('aria-live', 'assertive');
1099
- await expect(badgeElement).toHaveAttribute('aria-atomic', 'true');
1100
- });
1101
-
1102
- await step('Verify button accessibility', async () => {
1103
- const button = canvas.getByTestId('wcag-button');
1104
- await expect(button).toHaveAttribute('aria-describedby', 'badge-description');
1105
-
1106
- // Button should be focusable — reached by keyboard, not by calling focus().
1107
- await userEvent.tab();
1108
- await waitFor(() => expect(button).toHaveFocus());
1109
- });
1110
-
1111
- await step('Color contrast verification', async () => {
1112
- const badge = canvas.getByText('Alert');
1113
- const computedStyle = window.getComputedStyle(badge);
1114
-
1115
- // Error color should provide good contrast
1116
- const backgroundColor = computedStyle.backgroundColor;
1117
- const color = computedStyle.color;
1118
-
1119
- await expect(backgroundColor).toBeTruthy();
1120
- await expect(color).toBeTruthy();
1121
- });
1122
-
1123
- await step('Keyboard activation', async () => {
1124
- const button = canvas.getByTestId('wcag-button');
1125
- // Focus is already on the button from the previous step.
1126
- await waitFor(() => expect(button).toHaveFocus());
1127
-
1128
- // Should be activatable with Enter
1129
- await userEvent.keyboard('{Enter}');
1130
- await waitFor(() => expect(button).toHaveFocus());
1131
-
1132
- // Should be activatable with Space
1133
- await userEvent.keyboard(' ');
1134
- await waitFor(() => expect(button).toHaveFocus());
1135
- });
1136
- },
1137
- };
1138
-
1139
- // Cross Browser Compatibility Test
1140
- export const CrossBrowserTest: Story = {
1141
- name: '🌐 Cross Browser Test',
1142
- args: {
1143
- variant: 'glass',
1144
- badgeContent: 'BETA',
1145
- color: 'primary',
1146
- glow: true,
1147
- shimmer: true,
1148
- children: <Box sx={{ p: 3, bgcolor: 'grey.100', borderRadius: 1 }}>Feature</Box>,
1149
- },
1150
- play: async ({ canvasElement, step }) => {
1151
- const canvas = within(canvasElement);
1152
-
1153
- await step('Verify backdrop-filter support fallback', async () => {
1154
- const badge = canvas.getByText('BETA');
1155
- const computedStyle = window.getComputedStyle(badge);
1156
-
1157
- // Check if backdrop-filter is supported or has fallback
1158
- const hasBackdropFilter = computedStyle.backdropFilter !== undefined;
1159
- const hasWebkitBackdropFilter =
1160
- 'webkitBackdropFilter' in computedStyle &&
1161
- (computedStyle as Record<string, unknown>).webkitBackdropFilter !== undefined;
1162
- const hasBackground = computedStyle.backgroundColor !== 'rgba(0, 0, 0, 0)';
1163
-
1164
- // At least one should be true for cross-browser compatibility
1165
- const isCompatible = hasBackdropFilter || hasWebkitBackdropFilter || hasBackground;
1166
- await expect(isCompatible).toBe(true);
1167
- });
1168
-
1169
- await step('CSS Grid/Flexbox fallback', async () => {
1170
- // Check if badges work with various layout systems
1171
- const badge = canvas.getByText('BETA');
1172
- await expect(badge).toBeInTheDocument();
1173
-
1174
- const computedStyle = window.getComputedStyle(badge);
1175
- const position = computedStyle.position;
1176
-
1177
- // Should have valid position value (including static)
1178
- await expect(position).toMatch(/static|absolute|relative|fixed/);
1179
- });
1180
-
1181
- await step('Animation performance', async () => {
1182
- const badge = canvas.getByText('BETA');
1183
- const computedStyle = window.getComputedStyle(badge);
1184
-
1185
- // Check for hardware acceleration hints
1186
- const willChange = computedStyle.willChange;
1187
- const backfaceVisibility = computedStyle.backfaceVisibility;
1188
-
1189
- // Modern browsers should support these optimization properties
1190
- await expect(willChange).toBeTruthy();
1191
- await expect(backfaceVisibility).toBeTruthy();
1192
- });
1193
- },
1194
- };