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