@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.
- package/package.json +17 -9
- package/src/button/index.ts +1 -0
- package/src/social-login-button/index.ts +1 -0
- package/src/{social-login-button.tsx → social-login-button/social-login-button.tsx} +5 -5
- package/src/user-avatar/index.ts +1 -0
- package/src/{user-avatar.tsx → user-avatar/user-avatar.tsx} +4 -4
- package/eslint.config.js +0 -89
- package/src/__tests__/data-grid.test.tsx +0 -125
- package/src/__tests__/package-wiring.test.ts +0 -41
- package/src/__tests__/setup.ts +0 -1
- package/src/__tests__/social-login-button.test.tsx +0 -186
- package/src/__tests__/user-avatar.test.tsx +0 -195
- package/src/__tests__/viewport.test.ts +0 -50
- package/src/components/command/__tests__/command.test.tsx +0 -112
- package/src/components/data-display/Alert/Alert.stories.tsx +0 -559
- package/src/components/data-display/Alert/Alert.test.stories.tsx +0 -707
- package/src/components/data-display/AlertDialog/AlertDialog.stories.tsx +0 -362
- package/src/components/data-display/AlertDialog/AlertDialog.test.stories.tsx +0 -763
- package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.stories.tsx +0 -230
- package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.test.stories.tsx +0 -270
- package/src/components/data-display/Avatar/Avatar.stories.tsx +0 -721
- package/src/components/data-display/Avatar/Avatar.test.stories.tsx +0 -822
- package/src/components/data-display/Badge/Badge.stories.tsx +0 -1134
- package/src/components/data-display/Badge/Badge.test.stories.tsx +0 -1194
- package/src/components/data-display/Banner/Banner.stories.tsx +0 -528
- package/src/components/data-display/Banner/Banner.test.stories.tsx +0 -511
- package/src/components/data-display/Carousel/Carousel.stories.tsx +0 -607
- package/src/components/data-display/Carousel/Carousel.test.stories.tsx +0 -701
- package/src/components/data-display/Chart/Chart.stories.tsx +0 -568
- package/src/components/data-display/Chart/Chart.test.stories.tsx +0 -174
- package/src/components/data-display/Chart/SpecChart.stories.tsx +0 -150
- package/src/components/data-display/Chart/SpecChart.test.stories.tsx +0 -56
- package/src/components/data-display/Chart/__tests__/SpecChart.test.tsx +0 -60
- package/src/components/data-display/Chart/__tests__/chart-format.test.ts +0 -43
- package/src/components/data-display/Chart/__tests__/chart-spec.test.ts +0 -119
- package/src/components/data-display/Chip/Chip.stories.tsx +0 -252
- package/src/components/data-display/Chip/Chip.test.stories.tsx +0 -266
- package/src/components/data-display/DataGrid/DataGrid.stories.tsx +0 -515
- package/src/components/data-display/DataGrid/DataGrid.test.stories.tsx +0 -653
- package/src/components/data-display/DataViews/__tests__/DataViewsGrid.test.tsx +0 -271
- package/src/components/data-display/DataViews/__tests__/DataViewsTableBase.test.tsx +0 -134
- package/src/components/data-display/DataViews/__tests__/SaveViewModal.test.tsx +0 -83
- package/src/components/data-display/DataViews/__tests__/data-views-filter.test.ts +0 -115
- package/src/components/data-display/DataViews/__tests__/data-views-preview.test.ts +0 -53
- package/src/components/data-display/DataViews/__tests__/range-pill.test.tsx +0 -114
- package/src/components/data-display/DataViews/base-card.stories.tsx +0 -136
- package/src/components/data-display/DescriptionItem/DescriptionItem.stories.tsx +0 -254
- package/src/components/data-display/EmptyState/EmptyState.stories.tsx +0 -282
- package/src/components/data-display/EmptyState/EmptyState.test.stories.tsx +0 -441
- package/src/components/data-display/ErrorState/ErrorState.stories.tsx +0 -110
- package/src/components/data-display/ErrorState/ErrorState.test.stories.tsx +0 -133
- package/src/components/data-display/HoverCard/HoverCard.stories.tsx +0 -650
- package/src/components/data-display/HoverCard/HoverCard.test.stories.tsx +0 -929
- package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.stories.tsx +0 -411
- package/src/components/data-display/LazyImage/LazyImage.stories.tsx +0 -387
- package/src/components/data-display/Lightbox/Lightbox.stories.tsx +0 -315
- package/src/components/data-display/Lightbox/Lightbox.test.stories.tsx +0 -598
- package/src/components/data-display/LoadingState/LoadingState.stories.tsx +0 -124
- package/src/components/data-display/LoadingState/LoadingState.test.stories.tsx +0 -99
- package/src/components/data-display/MapPreview/MapPreview.stories.tsx +0 -458
- package/src/components/data-display/MapPreview/MapPreview.test.stories.tsx +0 -394
- package/src/components/data-display/Popover/Popover.stories.tsx +0 -295
- package/src/components/data-display/Popover/Popover.test.stories.tsx +0 -1162
- package/src/components/data-display/Progress/Progress.stories.tsx +0 -481
- package/src/components/data-display/Progress/Progress.test.stories.tsx +0 -2070
- package/src/components/data-display/SectionOnboarding/SectionOnboarding.stories.tsx +0 -160
- package/src/components/data-display/SectionOnboarding/SectionOnboarding.test.stories.tsx +0 -190
- package/src/components/data-display/Sheet/Sheet.stories.tsx +0 -883
- package/src/components/data-display/Sheet/Sheet.test.stories.tsx +0 -1105
- package/src/components/data-display/StatCard/StatCard.stories.tsx +0 -167
- package/src/components/data-display/StatCard/StatCard.test.stories.tsx +0 -240
- package/src/components/data-display/Stepper/Stepper.stories.tsx +0 -540
- package/src/components/data-display/Stepper/Stepper.test.stories.tsx +0 -600
- package/src/components/data-display/Table/Table.stories.tsx +0 -803
- package/src/components/data-display/Table/Table.test.stories.tsx +0 -369
- package/src/components/data-display/Timeline/Timeline.stories.tsx +0 -442
- package/src/components/data-display/Timeline/Timeline.test.stories.tsx +0 -930
- package/src/components/data-display/TimingDiagram/TimingDiagram.stories.tsx +0 -475
- package/src/components/data-display/TimingDiagram/TimingDiagram.test.stories.tsx +0 -734
- package/src/components/data-display/Tooltip/Tooltip.stories.tsx +0 -468
- package/src/components/data-display/Tooltip/Tooltip.test.stories.tsx +0 -1154
- package/src/components/feedback/ConfirmAction/ConfirmAction.stories.tsx +0 -146
- package/src/components/feedback/ConfirmAction/ConfirmAction.test.stories.tsx +0 -114
- package/src/components/feedback/ConfirmAction/__tests__/confirm-action.test.tsx +0 -288
- package/src/components/feedback/Dialog/Dialog.stories.tsx +0 -833
- package/src/components/feedback/Dialog/Dialog.test.stories.tsx +0 -964
- package/src/components/feedback/Dialog/__tests__/dialog-content-spacing.test.tsx +0 -66
- package/src/components/feedback/Modal/Modal.stories.tsx +0 -765
- package/src/components/feedback/Modal/Modal.test.stories.tsx +0 -251
- package/src/components/feedback/Sonner/Sonner.stories.tsx +0 -698
- package/src/components/feedback/Sonner/Sonner.test.stories.tsx +0 -896
- package/src/components/feedback/StackedModal/StackedModal.stories.tsx +0 -1783
- package/src/components/feedback/StackedModal/StackedModal.test.stories.tsx +0 -973
- package/src/components/feedback/StackedModal/__tests__/stacked-modal-viewport.test.tsx +0 -104
- package/src/components/feedback/Toast/Toast.stories.tsx +0 -548
- package/src/components/feedback/Toast/Toast.test.stories.tsx +0 -834
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.stories.tsx +0 -691
- package/src/components/feedback/TutorialOverlay/TutorialOverlay.test.stories.tsx +0 -634
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.stories.tsx +0 -665
- package/src/components/form/AddressAutocomplete/AddressAutocomplete.test.stories.tsx +0 -727
- package/src/components/form/Autocomplete/Autocomplete.stories.tsx +0 -715
- package/src/components/form/Autocomplete/Autocomplete.test.stories.tsx +0 -825
- package/src/components/form/Button/Button.stories.tsx +0 -365
- package/src/components/form/Button/Button.test.stories.tsx +0 -607
- package/src/components/form/Calendar/Calendar.stories.tsx +0 -349
- package/src/components/form/Calendar/Calendar.test.stories.tsx +0 -464
- package/src/components/form/CepField/CepField.stories.tsx +0 -138
- package/src/components/form/CepField/CepField.test.stories.tsx +0 -281
- package/src/components/form/Checkbox/Checkbox.stories.tsx +0 -265
- package/src/components/form/Checkbox/Checkbox.test.stories.tsx +0 -181
- package/src/components/form/CodeEditor/CodeEditor.stories.tsx +0 -871
- package/src/components/form/CodeEditor/CodeEditor.test.stories.tsx +0 -574
- package/src/components/form/Command/Command.stories.tsx +0 -580
- package/src/components/form/Command/Command.test.stories.tsx +0 -215
- package/src/components/form/CreatableSelect/CreatableSelect.stories.tsx +0 -112
- package/src/components/form/CreatableSelect/CreatableSelect.test.stories.tsx +0 -134
- package/src/components/form/Form/Form.stories.tsx +0 -687
- package/src/components/form/Form/Form.test.stories.tsx +0 -601
- package/src/components/form/Input/Input.stories.tsx +0 -260
- package/src/components/form/Input/Input.test.stories.tsx +0 -1191
- package/src/components/form/InputOTP/InputOTP.stories.tsx +0 -774
- package/src/components/form/InputOTP/InputOTP.test.stories.tsx +0 -588
- package/src/components/form/Label/Label.stories.tsx +0 -473
- package/src/components/form/Label/Label.test.stories.tsx +0 -915
- package/src/components/form/Menubar/Menubar.stories.tsx +0 -472
- package/src/components/form/Menubar/Menubar.test.stories.tsx +0 -584
- package/src/components/form/PasswordStrength/PasswordStrength.stories.tsx +0 -562
- package/src/components/form/PasswordStrength/PasswordStrength.test.stories.tsx +0 -327
- package/src/components/form/PhoneInput/PhoneInput.stories.tsx +0 -495
- package/src/components/form/PhoneInput/PhoneInput.test.stories.tsx +0 -666
- package/src/components/form/RadioGroup/RadioGroup.stories.tsx +0 -518
- package/src/components/form/RadioGroup/RadioGroup.test.stories.tsx +0 -685
- package/src/components/form/RichTextEditor/RichTextEditor.stories.tsx +0 -261
- package/src/components/form/RichTextEditor/RichTextEditor.test.stories.tsx +0 -491
- package/src/components/form/SearchPalette/SearchPalette.stories.tsx +0 -135
- package/src/components/form/Select/Select.stories.tsx +0 -364
- package/src/components/form/Select/Select.test.stories.tsx +0 -985
- package/src/components/form/Slider/Slider.stories.tsx +0 -418
- package/src/components/form/Slider/Slider.test.stories.tsx +0 -1042
- package/src/components/form/Switch/Switch.stories.tsx +0 -697
- package/src/components/form/Switch/Switch.test.stories.tsx +0 -597
- package/src/components/form/Textarea/Textarea.stories.tsx +0 -473
- package/src/components/form/Textarea/Textarea.test.stories.tsx +0 -702
- package/src/components/form/Toggle/Toggle.stories.tsx +0 -638
- package/src/components/form/Toggle/Toggle.test.stories.tsx +0 -1159
- package/src/components/form/ToggleGroup/ToggleGroup.stories.tsx +0 -617
- package/src/components/form/ToggleGroup/ToggleGroup.test.stories.tsx +0 -915
- package/src/components/form/UploadButton/UploadButton.stories.tsx +0 -367
- package/src/components/form/UploadButton/UploadButton.test.stories.tsx +0 -573
- package/src/components/form/total-form/total-form.test.tsx +0 -214
- package/src/components/layout/Accordion/Accordion.stories.tsx +0 -694
- package/src/components/layout/Accordion/Accordion.test.stories.tsx +0 -1344
- package/src/components/layout/Card/Card.stories.tsx +0 -591
- package/src/components/layout/Card/Card.test.stories.tsx +0 -1134
- package/src/components/layout/CardGrid/__tests__/CardGrid.test.tsx +0 -92
- package/src/components/layout/Collapsible/Collapsible.stories.tsx +0 -180
- package/src/components/layout/Collapsible/Collapsible.test.stories.tsx +0 -308
- package/src/components/layout/Container/Container.stories.tsx +0 -557
- package/src/components/layout/Container/Container.test.stories.tsx +0 -745
- package/src/components/layout/ContentToolbar/ContentToolbar.stories.tsx +0 -129
- package/src/components/layout/ContentToolbar/ContentToolbar.test.stories.tsx +0 -251
- package/src/components/layout/ContentToolbar/__tests__/ColumnsMenu.test.tsx +0 -39
- package/src/components/layout/ContentToolbar/__tests__/MultiSelectDropdown.test.tsx +0 -131
- package/src/components/layout/ContentToolbar/__tests__/SavedViewsMenu.test.tsx +0 -99
- package/src/components/layout/Dashboard/Dashboard.stories.tsx +0 -104
- package/src/components/layout/Dashboard/Dashboard.test.stories.tsx +0 -190
- package/src/components/layout/Drawer/Drawer.stories.tsx +0 -658
- package/src/components/layout/Drawer/Drawer.test.stories.tsx +0 -1343
- package/src/components/layout/Resizable/Resizable.stories.tsx +0 -1039
- package/src/components/layout/Resizable/Resizable.test.stories.tsx +0 -754
- package/src/components/layout/ScrollArea/ScrollArea.stories.tsx +0 -651
- package/src/components/layout/ScrollArea/ScrollArea.test.stories.tsx +0 -619
- package/src/components/layout/Separator/Separator.stories.tsx +0 -563
- package/src/components/layout/Separator/Separator.test.stories.tsx +0 -518
- package/src/components/layout/SettingsLayout/SettingsLayout.stories.tsx +0 -192
- package/src/components/layout/SettingsLayout/SettingsLayout.test.stories.tsx +0 -175
- package/src/components/layout/Sidebar/Sidebar.stories.tsx +0 -609
- package/src/components/layout/Sidebar/Sidebar.test.stories.tsx +0 -1324
- package/src/components/layout/Skeleton/Skeleton.stories.tsx +0 -689
- package/src/components/layout/Skeleton/Skeleton.test.stories.tsx +0 -521
- package/src/components/layout/Spacer/Spacer.stories.tsx +0 -502
- package/src/components/layout/Spacer/Spacer.test.stories.tsx +0 -474
- package/src/components/layout/TableFilter/TableFilter.stories.tsx +0 -82
- package/src/components/layout/TableFilter/TableFilter.test.stories.tsx +0 -87
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.stories.tsx +0 -638
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.test.stories.tsx +0 -1008
- package/src/components/navigation/CommandPalette/CommandPalette.stories.tsx +0 -386
- package/src/components/navigation/CommandPalette/CommandPalette.test.stories.tsx +0 -435
- package/src/components/navigation/ContextMenu/ContextMenu.stories.tsx +0 -614
- package/src/components/navigation/ContextMenu/ContextMenu.test.stories.tsx +0 -845
- package/src/components/navigation/DropdownMenu/DropdownMenu.stories.tsx +0 -578
- package/src/components/navigation/DropdownMenu/DropdownMenu.test.stories.tsx +0 -660
- package/src/components/navigation/NavigationMenu/stories/NavigationMenu.stories.tsx +0 -516
- package/src/components/navigation/NavigationMenu/stories/NavigationMenu.test.stories.tsx +0 -935
- package/src/components/navigation/NavigationMenu/stories/NavigationMenuHorizontal.stories.tsx +0 -501
- package/src/components/navigation/NavigationMenu/stories/NavigationMenuVertical.stories.tsx +0 -453
- package/src/components/navigation/Pagination/Pagination.stories.tsx +0 -602
- package/src/components/navigation/Pagination/Pagination.test.stories.tsx +0 -520
- package/src/components/navigation/Tabs/Tabs.stories.tsx +0 -1004
- package/src/components/navigation/Tabs/Tabs.test.stories.tsx +0 -1225
- package/src/components/typography/Blockquote/Blockquote.stories.tsx +0 -359
- package/src/components/typography/Blockquote/Blockquote.test.stories.tsx +0 -432
- package/src/components/typography/Code/Code.stories.tsx +0 -686
- package/src/components/typography/Code/Code.test.stories.tsx +0 -703
- package/src/components/typography/Heading/Heading.stories.tsx +0 -451
- package/src/components/typography/Heading/Heading.test.stories.tsx +0 -830
- package/src/components/typography/Paragraph/Paragraph.stories.tsx +0 -280
- package/src/components/typography/Paragraph/Paragraph.test.stories.tsx +0 -231
- package/src/components/typography/Text/Text.stories.tsx +0 -359
- package/src/components/typography/Text/Text.test.stories.tsx +0 -759
- package/src/components/utility/AnimatedIcon/AnimatedIcon.stories.tsx +0 -945
- package/src/components/utility/AnimatedIcon/AnimatedIcon.test.stories.tsx +0 -603
- package/src/components/utility/AspectRatio/AspectRatio.stories.tsx +0 -414
- package/src/components/utility/AspectRatio/AspectRatio.test.stories.tsx +0 -859
- package/src/components/utility/InfiniteScroll/InfiniteScroll.stories.tsx +0 -620
- package/src/components/utility/InfiniteScroll/InfiniteScroll.test.stories.tsx +0 -361
- package/src/components/utility/InstallPrompt/InstallPrompt.stories.tsx +0 -118
- package/src/components/utility/InstallPrompt/InstallPrompt.test.stories.tsx +0 -83
- package/src/components/utility/InstallPrompt/__tests__/InstallPrompt.ios.test.tsx +0 -65
- package/src/components/utility/InstallPrompt/__tests__/usePwaInstall.test.tsx +0 -271
- package/src/components/utility/Portal/Portal.stories.tsx +0 -340
- package/src/components/utility/Portal/Portal.test.stories.tsx +0 -2086
- package/src/components/utility/Transition/Transition.stories.tsx +0 -493
- package/src/components/utility/Transition/Transition.test.stories.tsx +0 -854
- package/src/components/utility/VirtualList/VirtualList.stories.tsx +0 -1073
- package/src/components/utility/VirtualList/VirtualList.test.stories.tsx +0 -1085
- package/src/components/utility/WorkflowStep/WorkflowStep.stories.tsx +0 -431
- package/src/components/utility/WorkflowStep/WorkflowStep.test.stories.tsx +0 -364
- /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
|
-
};
|