@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,822 +0,0 @@
1
- import Person from '@mui/icons-material/Person';
2
- import { Box,Stack } from '@mui/material';
3
- import type { Meta, StoryObj } from '@storybook/react-vite';
4
- import React from 'react';
5
- import { expect, fn,userEvent, waitFor, within } from 'storybook/test';
6
-
7
- import { Avatar, AvatarGroup } from './Avatar';
8
-
9
- const meta: Meta<typeof Avatar> = {
10
- title: 'DataDisplay/Avatar/Tests',
11
- component: Avatar,
12
- parameters: {
13
- layout: 'centered',
14
- chromatic: { disableSnapshot: false },
15
- },
16
- tags: ['autodocs', 'test', 'component:Avatar'],
17
- };
18
-
19
- export default meta;
20
- type Story = StoryObj<typeof meta>;
21
-
22
- // Interaction Tests
23
- export const BasicInteraction: Story = {
24
- name: '🧪 Basic Interaction Test',
25
- args: {
26
- fallback: 'TEST',
27
- interactive: true,
28
- onClick: fn(),
29
- },
30
- play: async ({ canvasElement, step, args }) => {
31
- const canvas = within(canvasElement);
32
-
33
- await step('Initial render verification', async () => {
34
- // Wait for the fade animation to complete
35
- await waitFor(() => {
36
- const avatar = canvas.getByRole('button');
37
- expect(avatar).toBeInTheDocument();
38
- });
39
- const avatar = canvas.getByRole('button');
40
- await expect(avatar).toHaveTextContent('TEST');
41
- });
42
-
43
- await step('Click interaction', async () => {
44
- const avatar = canvas.getByRole('button');
45
- await userEvent.click(avatar);
46
- await expect(args.onClick).toHaveBeenCalledTimes(1);
47
- });
48
-
49
- await step('Hover interaction', async () => {
50
- const avatar = canvas.getByRole('button');
51
- await userEvent.hover(avatar);
52
- // Check that the element has interactive styles
53
- await expect(avatar).toHaveStyle({ cursor: 'pointer' });
54
- });
55
- },
56
- };
57
-
58
- export const ImageLoadingTest: Story = {
59
- name: '📷 Image Loading Test',
60
- args: {
61
- src: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=150&h=150&fit=crop&crop=face',
62
- alt: 'Test User',
63
- fallback: 'TU',
64
- onError: fn(),
65
- },
66
- play: async ({ canvasElement, step }) => {
67
- const canvas = within(canvasElement);
68
-
69
- await step('Check image loading', async () => {
70
- const avatar = canvas.getByRole('img', { hidden: true });
71
- await expect(avatar).toBeInTheDocument();
72
- await expect(avatar).toHaveAttribute('alt', 'Test User');
73
- });
74
-
75
- await step('Image should have src attribute', async () => {
76
- const avatar = canvas.getByRole('img', { hidden: true });
77
- await expect(avatar).toHaveAttribute('src');
78
- });
79
- },
80
- };
81
-
82
- export const StatusIndicatorTest: Story = {
83
- name: '🔵 Status Indicator Test',
84
- render: () => (
85
- <Stack direction="row" spacing={2}>
86
- <Avatar variant="status" status="online" fallback="ON" dataTestId="online-avatar" />
87
- <Avatar variant="status" status="offline" fallback="OF" dataTestId="offline-avatar" />
88
- <Avatar variant="status" status="away" fallback="AW" dataTestId="away-avatar" />
89
- <Avatar variant="status" status="busy" fallback="BS" dataTestId="busy-avatar" />
90
- </Stack>
91
- ),
92
- play: async ({ canvasElement, step }) => {
93
- const canvas = within(canvasElement);
94
-
95
- await step('Verify all status variants render', async () => {
96
- const onlineAvatar = canvas.getByTestId('online-avatar');
97
- const offlineAvatar = canvas.getByTestId('offline-avatar');
98
- const awayAvatar = canvas.getByTestId('away-avatar');
99
- const busyAvatar = canvas.getByTestId('busy-avatar');
100
-
101
- await expect(onlineAvatar).toBeInTheDocument();
102
- await expect(offlineAvatar).toBeInTheDocument();
103
- await expect(awayAvatar).toBeInTheDocument();
104
- await expect(busyAvatar).toBeInTheDocument();
105
- });
106
-
107
- await step('Check status badge presence', async () => {
108
- // Verify the avatars are wrapped in badge components
109
- const onlineAvatar = canvas.getByTestId('online-avatar');
110
- const offlineAvatar = canvas.getByTestId('offline-avatar');
111
- const awayAvatar = canvas.getByTestId('away-avatar');
112
- const busyAvatar = canvas.getByTestId('busy-avatar');
113
-
114
- // All status avatars should have their parent badge element
115
- await expect(onlineAvatar.parentElement).toBeInTheDocument();
116
- await expect(offlineAvatar.parentElement).toBeInTheDocument();
117
- await expect(awayAvatar.parentElement).toBeInTheDocument();
118
- await expect(busyAvatar.parentElement).toBeInTheDocument();
119
- });
120
- },
121
- };
122
-
123
- // Accessibility Tests
124
- export const KeyboardNavigation: Story = {
125
- name: '⌨️ Keyboard Navigation Test',
126
- render: () => (
127
- <Stack direction="row" spacing={2}>
128
- <Avatar
129
- fallback="KB1"
130
- interactive
131
- onClick={fn()}
132
- dataTestId="first-avatar"
133
- aria-label="First Avatar"
134
- />
135
- <Avatar
136
- fallback="KB2"
137
- interactive
138
- onClick={fn()}
139
- dataTestId="second-avatar"
140
- aria-label="Second Avatar"
141
- />
142
- <Avatar
143
- fallback="KB3"
144
- interactive
145
- onClick={fn()}
146
- dataTestId="third-avatar"
147
- aria-label="Third Avatar"
148
- />
149
- </Stack>
150
- ),
151
- parameters: {
152
- a11y: {
153
- element: '#storybook-root',
154
- config: {
155
- rules: [
156
- { id: 'color-contrast', enabled: true },
157
- { id: 'aria-required-attr', enabled: true },
158
- { id: 'aria-roles', enabled: true },
159
- { id: 'button-name', enabled: true },
160
- ],
161
- },
162
- },
163
- },
164
- play: async ({ canvasElement, step }) => {
165
- const canvas = within(canvasElement);
166
-
167
- await step('Tab navigation verification', async () => {
168
- const firstAvatar = canvas.getByTestId('first-avatar');
169
- const secondAvatar = canvas.getByTestId('second-avatar');
170
- const thirdAvatar = canvas.getByTestId('third-avatar');
171
-
172
- // Verify all avatars are interactive and have proper attributes
173
- await expect(firstAvatar).toHaveAttribute('role', 'button');
174
- await expect(firstAvatar).toHaveAttribute('tabindex', '0');
175
- await expect(secondAvatar).toHaveAttribute('role', 'button');
176
- await expect(secondAvatar).toHaveAttribute('tabindex', '0');
177
- await expect(thirdAvatar).toHaveAttribute('role', 'button');
178
- await expect(thirdAvatar).toHaveAttribute('tabindex', '0');
179
-
180
- // Test basic click functionality (focus not reliable in test environment)
181
- await userEvent.click(firstAvatar);
182
- // Focus assertion removed due to Storybook test environment limitations
183
- });
184
-
185
- await step('Keyboard activation', async () => {
186
- const firstAvatar = canvas.getByTestId('first-avatar');
187
-
188
- // Test keyboard activation (focus assertions removed)
189
- await userEvent.click(firstAvatar);
190
- await userEvent.keyboard('{Enter}');
191
- await userEvent.keyboard(' ');
192
- });
193
-
194
- await step('Sequential focus navigation', async () => {
195
- // Start from first avatar
196
- const firstAvatar = canvas.getByTestId('first-avatar');
197
- await userEvent.click(firstAvatar);
198
-
199
- // Tab through elements (test tab order)
200
- await userEvent.tab();
201
- await userEvent.tab();
202
- await userEvent.tab({ shift: true }); // Go back
203
-
204
- // Verify tab navigation works at basic level
205
- const allButtons = canvas.getAllByRole('button');
206
- await expect(allButtons.length).toBeGreaterThanOrEqual(3);
207
- });
208
- },
209
- };
210
-
211
- export const ScreenReaderTest: Story = {
212
- name: '🔊 Screen Reader Test',
213
- args: {
214
- fallback: 'SR',
215
- alt: 'Screen reader test avatar',
216
- interactive: true,
217
- },
218
- play: async ({ canvasElement, step }) => {
219
- const canvas = within(canvasElement);
220
-
221
- await step('Verify ARIA labels', async () => {
222
- // Wait for the fade animation to complete
223
- await waitFor(() => {
224
- const avatar = canvas.getByRole('button');
225
- expect(avatar).toBeInTheDocument();
226
- });
227
- const avatar = canvas.getByRole('button');
228
- await expect(avatar).toHaveAttribute('aria-label', 'Screen reader test avatar');
229
- });
230
-
231
- await step('Verify role attributes', async () => {
232
- const avatar = canvas.getByRole('button');
233
- await expect(avatar).toHaveAttribute('role', 'button');
234
- await expect(avatar).toHaveAttribute('tabindex', '0');
235
- });
236
- },
237
- };
238
-
239
- // Visual Tests
240
- export const ResponsiveDesign: Story = {
241
- name: '📱 Responsive Design Test',
242
- render: () => (
243
- <Box
244
- sx={{
245
- display: 'grid',
246
- gridTemplateColumns: {
247
- xs: 'repeat(3, 1fr)',
248
- sm: 'repeat(4, 1fr)',
249
- md: 'repeat(6, 1fr)',
250
- },
251
- gap: 2,
252
- width: '100%',
253
- }}
254
- >
255
- {Array.from({ length: 12 }, (_, i) => (
256
- <Avatar key={i} fallback={`R${i + 1}`} dataTestId={`responsive-avatar-${i}`} />
257
- ))}
258
- </Box>
259
- ),
260
- parameters: {
261
- viewport: {
262
- viewports: {
263
- mobile: {
264
- name: 'Mobile',
265
- styles: { width: '375px', height: '667px' },
266
- type: 'mobile',
267
- },
268
- tablet: {
269
- name: 'Tablet',
270
- styles: { width: '768px', height: '1024px' },
271
- type: 'tablet',
272
- },
273
- desktop: {
274
- name: 'Desktop',
275
- styles: { width: '1920px', height: '1080px' },
276
- type: 'desktop',
277
- },
278
- },
279
- defaultViewport: 'mobile',
280
- },
281
- chromatic: {
282
- viewports: [375, 768, 1920],
283
- delay: 300,
284
- },
285
- },
286
- play: async ({ canvasElement, step }) => {
287
- const canvas = within(canvasElement);
288
-
289
- await step('Verify responsive grid layout', async () => {
290
- // Wait for components to render
291
- const avatar0 = await canvas.findByTestId('responsive-avatar-0');
292
- await expect(avatar0).toBeInTheDocument();
293
-
294
- // Verify all avatars are rendered
295
- for (let i = 0; i < 12; i++) {
296
- const avatar = canvas.getByTestId(`responsive-avatar-${i}`);
297
- await expect(avatar).toBeInTheDocument();
298
- }
299
-
300
- // Find the Box container with the grid styling
301
- const container = avatar0.parentElement;
302
-
303
- if (!container) throw new Error('Container not found');
304
-
305
- // Verify basic layout - simplified test due to MUI rendering complexity
306
- const computedStyle = window.getComputedStyle(container as HTMLElement);
307
-
308
- // Check that the container exists and has some layout styling
309
- await expect(container).toBeInTheDocument();
310
-
311
- // At minimum, verify it's not using default display: inline
312
- const hasLayoutDisplay = computedStyle.display !== 'inline';
313
- await expect(hasLayoutDisplay).toBe(true);
314
- });
315
- },
316
- };
317
-
318
- export const VisualStates: Story = {
319
- name: '👁️ Visual States Test',
320
- render: () => (
321
- <Stack spacing={2}>
322
- <Stack direction="row" spacing={2} alignItems="center">
323
- <Avatar fallback="DF" dataTestId="default-avatar" />
324
- <Avatar fallback="GL" glow dataTestId="glow-avatar" />
325
- <Avatar fallback="PL" pulse dataTestId="pulse-avatar" />
326
- <Avatar fallback="BD" bordered dataTestId="bordered-avatar" />
327
- </Stack>
328
- <Stack direction="row" spacing={2} alignItems="center">
329
- <Avatar fallback="IN" interactive dataTestId="interactive-avatar" />
330
- <Avatar fallback="LD" loading dataTestId="loading-avatar" />
331
- </Stack>
332
- </Stack>
333
- ),
334
- play: async ({ canvasElement, step }) => {
335
- const canvas = within(canvasElement);
336
-
337
- await step('Default state', async () => {
338
- const avatar = canvas.getByTestId('default-avatar');
339
- await expect(avatar).toBeInTheDocument();
340
- });
341
-
342
- await step('Glow effect', async () => {
343
- const avatar = canvas.getByTestId('glow-avatar');
344
- const computedStyle = window.getComputedStyle(avatar);
345
- // Glow effect adds box-shadow
346
- await expect(computedStyle.boxShadow).not.toBe('none');
347
- });
348
-
349
- await step('Hover state on interactive', async () => {
350
- const avatar = canvas.getByTestId('interactive-avatar');
351
- await userEvent.hover(avatar);
352
- // Interactive avatars transform on hover
353
- await waitFor(() => {
354
- const computedStyle = window.getComputedStyle(avatar);
355
- expect(computedStyle.transform).not.toBe('none');
356
- });
357
- });
358
-
359
- await step('Loading state', async () => {
360
- const _loadingAvatar = canvas.getByTestId('loading-avatar');
361
- // Check for loading overlay and spinner by testid
362
- const loadingOverlay = canvas.getByTestId('loading-avatar-loading');
363
- await expect(loadingOverlay).toBeInTheDocument();
364
- const spinner = canvas.getByTestId('loading-avatar-loading-spinner');
365
- await expect(spinner).toBeInTheDocument();
366
- });
367
- },
368
- };
369
-
370
- // Performance Tests
371
- export const PerformanceTest: Story = {
372
- name: '⚡ Performance Test',
373
- render: () => (
374
- <Box
375
- sx={{
376
- display: 'flex',
377
- flexWrap: 'wrap',
378
- gap: 1,
379
- maxWidth: 600,
380
- maxHeight: 400,
381
- overflow: 'auto',
382
- }}
383
- >
384
- {Array.from({ length: 100 }, (_, i) => (
385
- <Avatar key={i} fallback={`${i}`} size="sm" dataTestId={`perf-avatar-${i}`} />
386
- ))}
387
- </Box>
388
- ),
389
- play: async ({ canvasElement, step }) => {
390
- const canvas = within(canvasElement);
391
-
392
- await step('Measure render time', async () => {
393
- const startTime = window.performance.now();
394
- // Get only the exact testid matches, not the content children
395
- const avatars = Array.from({ length: 100 }, (_, i) =>
396
- canvas.getByTestId(`perf-avatar-${i}`)
397
- );
398
- const endTime = window.performance.now();
399
-
400
- const renderTime = endTime - startTime;
401
- // Log render time for debugging if needed
402
- // console.log(`Render time for ${avatars.length} avatars: ${renderTime}ms`);
403
-
404
- // Assert reasonable render time
405
- await expect(renderTime).toBeLessThan(1000);
406
- await expect(avatars).toHaveLength(100);
407
- });
408
- },
409
- };
410
-
411
- // Edge Cases Tests
412
- export const EdgeCases: Story = {
413
- name: '🔧 Edge Cases Test',
414
- render: () => (
415
- <Stack spacing={2}>
416
- <Stack direction="row" spacing={2} alignItems="center">
417
- <Avatar fallback="" dataTestId="empty-fallback" />
418
- <Avatar fallback="VERYLONGTEXTTHATWILLOVERFLOW" dataTestId="long-text" />
419
- <Avatar fallback="🎉" dataTestId="emoji" />
420
- <Avatar fallback="中文" dataTestId="unicode" />
421
- </Stack>
422
- <Stack direction="row" spacing={2} alignItems="center">
423
- <Avatar src="https://invalid-url.com/image.jpg" fallback="ERR" dataTestId="error-image" />
424
- </Stack>
425
- </Stack>
426
- ),
427
- play: async ({ canvasElement, step }) => {
428
- const canvas = within(canvasElement);
429
-
430
- await step('Empty fallback handling', async () => {
431
- const avatar = canvas.getByTestId('empty-fallback');
432
- await expect(avatar).toBeInTheDocument();
433
- // Should show default icon when fallback is empty (Person icon is default)
434
- // The icon is rendered by MUI and has dataTestId="PersonIcon"
435
- const personIcon = canvas.getByTestId('PersonIcon');
436
- await expect(personIcon).toBeInTheDocument();
437
- });
438
-
439
- await step('Long text overflow', async () => {
440
- const avatar = canvas.getByTestId('long-text');
441
- // MuiAvatar uses overflow: hidden internally, check parent for overflow handling
442
- await expect(avatar).toBeInTheDocument();
443
- await expect(avatar).toHaveTextContent('VERYLONGTEXTTHATWILLOVERFLOW');
444
- });
445
-
446
- await step('Emoji support', async () => {
447
- const avatar = canvas.getByTestId('emoji');
448
- await expect(avatar).toHaveTextContent('🎉');
449
- });
450
-
451
- await step('Unicode support', async () => {
452
- const avatar = canvas.getByTestId('unicode');
453
- await expect(avatar).toHaveTextContent('中文');
454
- });
455
-
456
- await step('Image error handling', async () => {
457
- const avatar = canvas.getByTestId('error-image');
458
- // Wait for error to occur and fallback to show
459
- await waitFor(
460
- () => {
461
- expect(avatar).toHaveTextContent('ERR');
462
- },
463
- { timeout: 3000 },
464
- );
465
- });
466
- },
467
- };
468
-
469
- // Integration Tests
470
- export const AvatarGroupTest: Story = {
471
- name: '🔗 Avatar Group Test',
472
- render: () => (
473
- <Stack spacing={2}>
474
- <AvatarGroup max={3}>
475
- <Avatar fallback="A1" dataTestId="group-avatar-1" />
476
- <Avatar fallback="A2" dataTestId="group-avatar-2" />
477
- <Avatar fallback="A3" dataTestId="group-avatar-3" />
478
- <Avatar fallback="A4" dataTestId="group-avatar-4" />
479
- <Avatar fallback="A5" dataTestId="group-avatar-5" />
480
- </AvatarGroup>
481
- </Stack>
482
- ),
483
- play: async ({ canvasElement, step }) => {
484
- const canvas = within(canvasElement);
485
-
486
- await step('Verify max avatars displayed', async () => {
487
- // Only first 3 should be visible plus the overflow indicator
488
- const avatar1 = canvas.getByTestId('group-avatar-1');
489
- const avatar2 = canvas.getByTestId('group-avatar-2');
490
- const avatar3 = canvas.getByTestId('group-avatar-3');
491
-
492
- await expect(avatar1).toBeInTheDocument();
493
- await expect(avatar2).toBeInTheDocument();
494
- await expect(avatar3).toBeInTheDocument();
495
-
496
- // Avatar 4 and 5 should not be in the document (hidden by max=3)
497
- await waitFor(() => {
498
- expect(canvas.queryByTestId('group-avatar-4')).not.toBeInTheDocument();
499
- expect(canvas.queryByTestId('group-avatar-5')).not.toBeInTheDocument();
500
- });
501
- });
502
-
503
- await step('Verify overflow indicator', async () => {
504
- // Look for the overflow indicator text
505
- const overflowIndicator = canvas.getByText('+2');
506
- await expect(overflowIndicator).toBeInTheDocument();
507
- });
508
-
509
- await step('Hover effect on group avatars', async () => {
510
- const firstAvatar = canvas.getByTestId('group-avatar-1');
511
-
512
- // Test hover interaction (style checks simplified)
513
- await userEvent.hover(firstAvatar);
514
-
515
- // Verify element is still present and interactive after hover
516
- await expect(firstAvatar).toBeInTheDocument();
517
- await expect(firstAvatar).toHaveTextContent('A1');
518
- });
519
- },
520
- };
521
-
522
- // Animation Tests
523
- export const AnimationTest: Story = {
524
- name: '🎬 Animation Test',
525
- render: () => (
526
- <Stack direction="row" spacing={2}>
527
- <Avatar fallback="A0" animationDelay={0} dataTestId="delay-0" />
528
- <Avatar fallback="A200" animationDelay={200} dataTestId="delay-200" />
529
- <Avatar fallback="A400" animationDelay={400} dataTestId="delay-400" />
530
- </Stack>
531
- ),
532
- play: async ({ canvasElement, step }) => {
533
- const canvas = within(canvasElement);
534
-
535
- await step('Verify animation delays', async () => {
536
- const avatar0 = canvas.getByTestId('delay-0');
537
- const avatar200 = canvas.getByTestId('delay-200');
538
- const avatar400 = canvas.getByTestId('delay-400');
539
-
540
- // All avatars should be in the document
541
- await expect(avatar0).toBeInTheDocument();
542
- await expect(avatar200).toBeInTheDocument();
543
- await expect(avatar400).toBeInTheDocument();
544
-
545
- // Check they have proper fade-in applied
546
- await waitFor(() => {
547
- const opacity0 = window.getComputedStyle(avatar0.parentElement!).opacity;
548
- expect(parseFloat(opacity0)).toBeGreaterThan(0);
549
- });
550
- });
551
- },
552
- };
553
-
554
- // Focus Management Test
555
- export const FocusManagement: Story = {
556
- name: '🎯 Focus Management Test',
557
- render: () => (
558
- <Stack spacing={2}>
559
- <Avatar fallback="FC" interactive dataTestId="focus-avatar" aria-label="Focus test avatar" />
560
- </Stack>
561
- ),
562
- play: async ({ canvasElement, step }) => {
563
- const canvas = within(canvasElement);
564
-
565
- await step('Focus visible state', async () => {
566
- const avatar = canvas.getByTestId('focus-avatar');
567
-
568
- // Test basic interaction (focus not reliable in test environment)
569
- await userEvent.click(avatar);
570
-
571
- // Check element has focusable attributes
572
- await expect(avatar).toHaveAttribute('role', 'button');
573
- await expect(avatar).toHaveAttribute('tabindex', '0');
574
-
575
- // Check for focus-visible styles (simplified)
576
- const computedStyle = window.getComputedStyle(avatar);
577
- // Check that element has some kind of styling applied
578
- await expect(computedStyle.cursor).toBe('pointer');
579
- });
580
-
581
- await step('Focus restoration after click', async () => {
582
- const avatar = canvas.getByTestId('focus-avatar');
583
-
584
- // Test repeated clicks work (focus assertions removed)
585
- await userEvent.click(avatar);
586
- await userEvent.click(avatar);
587
-
588
- // Verify the element is still interactive
589
- await expect(avatar).toHaveAttribute('role', 'button');
590
- });
591
- },
592
- };
593
-
594
- // Theme Integration Tests
595
- export const ThemeIntegration: Story = {
596
- name: '🎨 Theme Integration Test',
597
- render: function ThemeIntegrationRender() {
598
- const [theme, setTheme] = React.useState<'light' | 'dark'>('light');
599
-
600
- return (
601
- <Box sx={{ p: 2, backgroundColor: theme === 'dark' ? '#303030' : '#f5f5f5' }}>
602
- <Stack spacing={2}>
603
- <button
604
- onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
605
- data-testid="theme-toggle"
606
- style={{ padding: '8px 16px', marginBottom: '16px' }}
607
- >
608
- Switch to {theme === 'light' ? 'dark' : 'light'} theme
609
- </button>
610
- <Stack direction="row" spacing={2}>
611
- <Avatar fallback="LT" color="primary" dataTestId="theme-avatar-primary" />
612
- <Avatar fallback="SC" color="secondary" dataTestId="theme-avatar-secondary" />
613
- <Avatar fallback="ER" color="error" dataTestId="theme-avatar-error" />
614
- <Avatar fallback="WN" color="warning" dataTestId="theme-avatar-warning" />
615
- <Avatar fallback="SU" color="success" dataTestId="theme-avatar-success" />
616
- </Stack>
617
- </Stack>
618
- </Box>
619
- );
620
- },
621
- play: async ({ canvasElement, step }) => {
622
- const canvas = within(canvasElement);
623
-
624
- await step('Test light theme colors', async () => {
625
- const primaryAvatar = canvas.getByTestId('theme-avatar-primary');
626
- await expect(primaryAvatar).toBeInTheDocument();
627
-
628
- const computedStyle = window.getComputedStyle(primaryAvatar);
629
- // In light theme, should have specific color values
630
- await expect(computedStyle.backgroundColor).toBeDefined();
631
- });
632
-
633
- await step('Switch to dark theme', async () => {
634
- const themeToggle = canvas.getByTestId('theme-toggle');
635
- await userEvent.click(themeToggle);
636
-
637
- await waitFor(() => {
638
- expect(themeToggle).toHaveTextContent('Switch to light theme');
639
- });
640
- });
641
-
642
- await step('Test dark theme colors', async () => {
643
- const primaryAvatar = canvas.getByTestId('theme-avatar-primary');
644
- const computedStyle = window.getComputedStyle(primaryAvatar);
645
-
646
- // Colors should adapt to dark theme
647
- await expect(computedStyle.backgroundColor).toBeDefined();
648
- await expect(primaryAvatar).toBeInTheDocument();
649
- });
650
- },
651
- };
652
-
653
- // Enhanced Accessibility Compliance Tests
654
- export const AccessibilityCompliance: Story = {
655
- name: '♿ Accessibility Compliance Test (WCAG)',
656
- render: () => (
657
- <Stack spacing={3}>
658
- {/* Color Contrast Tests */}
659
- <Stack direction="row" spacing={2}>
660
- <Avatar
661
- fallback="AA"
662
- color="primary"
663
- dataTestId="contrast-primary"
664
- aria-label="Primary color avatar"
665
- />
666
- <Avatar
667
- fallback="BB"
668
- color="error"
669
- dataTestId="contrast-error"
670
- aria-label="Error color avatar"
671
- />
672
- <Avatar
673
- fallback="CC"
674
- color="warning"
675
- dataTestId="contrast-warning"
676
- aria-label="Warning color avatar"
677
- />
678
- </Stack>
679
-
680
- {/* Focus Indicators */}
681
- <Stack direction="row" spacing={2}>
682
- <Avatar
683
- fallback="F1"
684
- interactive
685
- dataTestId="focus-interactive"
686
- aria-label="Focusable interactive avatar"
687
- />
688
- <Avatar
689
- fallback="F2"
690
- onClick={fn()}
691
- dataTestId="focus-clickable"
692
- aria-label="Focusable clickable avatar"
693
- />
694
- </Stack>
695
-
696
- {/* Screen Reader Support */}
697
- <Stack direction="row" spacing={2}>
698
- <Avatar
699
- src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=150&h=150&fit=crop&crop=face"
700
- alt="Profile photo of John Doe"
701
- dataTestId="sr-image"
702
- />
703
- <Avatar fallback="SR" aria-label="User initials: S.R." dataTestId="sr-initials" />
704
- <Avatar icon={<Person />} aria-label="Generic user profile" dataTestId="sr-icon" />
705
- </Stack>
706
-
707
- {/* Status Indicators with Labels */}
708
- <Stack direction="row" spacing={2}>
709
- <Avatar
710
- variant="status"
711
- status="online"
712
- fallback="ON"
713
- dataTestId="status-online"
714
- aria-label="User is online"
715
- />
716
- <Avatar
717
- variant="status"
718
- status="busy"
719
- fallback="BS"
720
- dataTestId="status-busy"
721
- aria-label="User is busy"
722
- />
723
- </Stack>
724
- </Stack>
725
- ),
726
- parameters: {
727
- a11y: {
728
- element: '#storybook-root',
729
- config: {
730
- rules: [
731
- // WCAG 2.1 AA compliance rules
732
- { id: 'color-contrast', enabled: true },
733
- { id: 'aria-required-attr', enabled: true },
734
- { id: 'aria-roles', enabled: true },
735
- { id: 'button-name', enabled: true },
736
- { id: 'focus-order-semantics', enabled: true },
737
- { id: 'image-alt', enabled: true },
738
- { id: 'keyboard-navigation', enabled: true },
739
- { id: 'aria-valid-attr-value', enabled: true },
740
- { id: 'aria-valid-attr', enabled: true },
741
- ],
742
- },
743
- },
744
- },
745
- play: async ({ canvasElement, step }) => {
746
- const canvas = within(canvasElement);
747
-
748
- await step('Color contrast verification', async () => {
749
- const primaryAvatar = canvas.getByTestId('contrast-primary');
750
- const errorAvatar = canvas.getByTestId('contrast-error');
751
-
752
- // Check that avatars have sufficient contrast
753
- const primaryStyle = window.getComputedStyle(primaryAvatar);
754
- const errorStyle = window.getComputedStyle(errorAvatar);
755
-
756
- await expect(primaryStyle.backgroundColor).not.toBe(primaryStyle.color);
757
- await expect(errorStyle.backgroundColor).not.toBe(errorStyle.color);
758
- });
759
-
760
- await step('Focus indicators compliance', async () => {
761
- const interactiveAvatar = canvas.getByTestId('focus-interactive');
762
- const clickableAvatar = canvas.getByTestId('focus-clickable');
763
-
764
- // Test click interactions (focus not reliable in test environment)
765
- await userEvent.click(interactiveAvatar);
766
- await userEvent.click(clickableAvatar);
767
-
768
- // Verify elements are interactive after clicks
769
- await expect(interactiveAvatar).toHaveAttribute('role', 'button');
770
- await expect(clickableAvatar).toHaveAttribute('role', 'button');
771
- });
772
-
773
- await step('Screen reader support', async () => {
774
- const _imageAvatar = canvas.getByTestId('sr-image');
775
- const initialsAvatar = canvas.getByTestId('sr-initials');
776
- const iconAvatar = canvas.getByTestId('sr-icon');
777
-
778
- // Verify proper ARIA labels and alt text
779
- // For image avatars, find the img element by alt text
780
- const imgElement = await canvas.findByAltText('Profile photo of John Doe');
781
- await expect(imgElement).toBeInTheDocument();
782
-
783
- await expect(initialsAvatar).toHaveAttribute('aria-label', 'User initials: S.R.');
784
- await expect(iconAvatar).toHaveAttribute('aria-label', 'Generic user profile');
785
- });
786
-
787
- await step('Status indicators accessibility', async () => {
788
- const onlineAvatar = canvas.getByTestId('status-online');
789
- const busyAvatar = canvas.getByTestId('status-busy');
790
-
791
- await expect(onlineAvatar).toHaveAttribute('aria-label', 'User is online');
792
- await expect(busyAvatar).toHaveAttribute('aria-label', 'User is busy');
793
-
794
- // Verify the avatars are present and wrapped correctly
795
- // Both avatars should exist and have the correct labels
796
- await expect(onlineAvatar).toBeInTheDocument();
797
- await expect(busyAvatar).toBeInTheDocument();
798
- });
799
-
800
- await step('Keyboard navigation compliance', async () => {
801
- const interactiveAvatar = canvas.getByTestId('focus-interactive');
802
- const clickableAvatar = canvas.getByTestId('focus-clickable');
803
-
804
- // Verify basic interactivity attributes
805
- await expect(interactiveAvatar).toHaveAttribute('role', 'button');
806
- await expect(interactiveAvatar).toHaveAttribute('tabindex', '0');
807
- await expect(clickableAvatar).toHaveAttribute('role', 'button');
808
- await expect(clickableAvatar).toHaveAttribute('tabindex', '0');
809
-
810
- // Test basic click capability (focus not reliable in test environment)
811
- await userEvent.click(interactiveAvatar);
812
-
813
- // Test keyboard activation
814
- await userEvent.keyboard('{Enter}');
815
- await userEvent.keyboard(' ');
816
-
817
- // Test that navigation works at basic level
818
- await userEvent.tab();
819
- // Don't assert specific focus, just test that tabbing works
820
- });
821
- },
822
- };