@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,1162 +0,0 @@
|
|
|
1
|
-
import { Button, List, ListItem, ListItemText,Stack, Typography } from '@mui/material';
|
|
2
|
-
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { expect, fn, userEvent, waitFor, within } from 'storybook/test';
|
|
5
|
-
|
|
6
|
-
import { Popover } from './Popover';
|
|
7
|
-
|
|
8
|
-
const meta: Meta<typeof Popover> = {
|
|
9
|
-
title: 'DataDisplay/Popover/Tests',
|
|
10
|
-
component: Popover,
|
|
11
|
-
parameters: {
|
|
12
|
-
layout: 'centered',
|
|
13
|
-
chromatic: { disableSnapshot: false },
|
|
14
|
-
},
|
|
15
|
-
tags: ['autodocs', 'test', 'component:Popover'],
|
|
16
|
-
};
|
|
17
|
-
export default meta;
|
|
18
|
-
export type Story = StoryObj<typeof meta>;
|
|
19
|
-
|
|
20
|
-
// Helper function to get popover elements from document (since MUI renders in portal)
|
|
21
|
-
const getPopoverElement = (testId: string) => document.querySelector(`[data-testid="${testId}"]`);
|
|
22
|
-
const queryPopoverElement = (testId: string) => document.querySelector(`[data-testid="${testId}"]`);
|
|
23
|
-
|
|
24
|
-
// Helper component for tests
|
|
25
|
-
const PopoverTestWrapper = ({
|
|
26
|
-
children,
|
|
27
|
-
onOpen = fn(),
|
|
28
|
-
onClose = fn(),
|
|
29
|
-
...props
|
|
30
|
-
}: {
|
|
31
|
-
children?: React.ReactNode;
|
|
32
|
-
onOpen?: () => void;
|
|
33
|
-
onClose?: () => void;
|
|
34
|
-
variant?: 'default' | 'glass' | 'arrow';
|
|
35
|
-
glow?: boolean;
|
|
36
|
-
pulse?: boolean;
|
|
37
|
-
maxWidth?: number;
|
|
38
|
-
}) => {
|
|
39
|
-
const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null);
|
|
40
|
-
const open = Boolean(anchorEl);
|
|
41
|
-
|
|
42
|
-
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
43
|
-
setAnchorEl(event.currentTarget);
|
|
44
|
-
onOpen();
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const handleClose = () => {
|
|
48
|
-
setAnchorEl(null);
|
|
49
|
-
onClose();
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
return (
|
|
53
|
-
<>
|
|
54
|
-
<Button variant="contained" onClick={handleClick} data-testid="popover-trigger">
|
|
55
|
-
Open Popover
|
|
56
|
-
</Button>
|
|
57
|
-
<Popover
|
|
58
|
-
open={open}
|
|
59
|
-
anchorEl={anchorEl}
|
|
60
|
-
onClose={handleClose}
|
|
61
|
-
anchorOrigin={{
|
|
62
|
-
vertical: 'bottom',
|
|
63
|
-
horizontal: 'left',
|
|
64
|
-
}}
|
|
65
|
-
data-testid="popover"
|
|
66
|
-
{...props}
|
|
67
|
-
>
|
|
68
|
-
{children}
|
|
69
|
-
</Popover>
|
|
70
|
-
</>
|
|
71
|
-
);
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
// 1. Basic Interaction Tests
|
|
75
|
-
export const BasicInteraction: Story = {
|
|
76
|
-
render: () => (
|
|
77
|
-
<>
|
|
78
|
-
<PopoverTestWrapper>
|
|
79
|
-
<Typography sx={{ p: 2 }} data-testid="popover-content">
|
|
80
|
-
This is basic popover content.
|
|
81
|
-
</Typography>
|
|
82
|
-
</PopoverTestWrapper>
|
|
83
|
-
<div aria-label="Status of the test run" style={{ marginTop: 20 }}>
|
|
84
|
-
PASS
|
|
85
|
-
</div>
|
|
86
|
-
</>
|
|
87
|
-
),
|
|
88
|
-
play: async ({ canvasElement, step }) => {
|
|
89
|
-
const canvas = within(canvasElement);
|
|
90
|
-
const statusElement = canvas.getByLabelText('Status of the test run');
|
|
91
|
-
statusElement.textContent = 'RUNS';
|
|
92
|
-
|
|
93
|
-
await step('Initial state should be closed', async () => {
|
|
94
|
-
const trigger = canvas.getByTestId('popover-trigger');
|
|
95
|
-
expect(trigger).toBeInTheDocument();
|
|
96
|
-
|
|
97
|
-
// Popover should not be visible initially
|
|
98
|
-
await waitFor(() => expect(document.querySelector('[data-testid="popover-content"]')).not.toBeInTheDocument());
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
await step('Should open popover on trigger click', async () => {
|
|
102
|
-
const trigger = canvas.getByTestId('popover-trigger');
|
|
103
|
-
await userEvent.click(trigger);
|
|
104
|
-
|
|
105
|
-
await waitFor(() => {
|
|
106
|
-
// MUI Popover renders content in a portal (document body), not in canvas
|
|
107
|
-
const content = document.querySelector('[data-testid="popover-content"]');
|
|
108
|
-
expect(content).toBeInTheDocument();
|
|
109
|
-
expect(content).toHaveTextContent('This is basic popover content.');
|
|
110
|
-
|
|
111
|
-
// Verify popover is positioned relative to trigger
|
|
112
|
-
const popoverContainer = document.querySelector('[role="presentation"]');
|
|
113
|
-
expect(popoverContainer).toBeInTheDocument();
|
|
114
|
-
|
|
115
|
-
// Test that popover Paper element exists with proper styling
|
|
116
|
-
const paper = document.querySelector('.MuiPaper-root');
|
|
117
|
-
expect(paper).toBeInTheDocument();
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
await step('Should close popover on outside click', async () => {
|
|
122
|
-
// Click outside the popover (on backdrop)
|
|
123
|
-
const backdrop = document.querySelector('.MuiBackdrop-root');
|
|
124
|
-
if (backdrop) {
|
|
125
|
-
await userEvent.click(backdrop as HTMLElement);
|
|
126
|
-
} else {
|
|
127
|
-
await userEvent.click(canvasElement);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
await waitFor(() => {
|
|
131
|
-
expect(document.querySelector('[data-testid="popover-content"]')).not.toBeInTheDocument();
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
// Mark test as passed
|
|
136
|
-
statusElement.textContent = 'PASS';
|
|
137
|
-
},
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
// 2. Form Interaction Tests
|
|
141
|
-
export const FormInteraction: Story = {
|
|
142
|
-
render: () => {
|
|
143
|
-
const [clickedItem, setClickedItem] = React.useState('');
|
|
144
|
-
return (
|
|
145
|
-
<>
|
|
146
|
-
<PopoverTestWrapper>
|
|
147
|
-
<div style={{ padding: 16 }}>
|
|
148
|
-
<Typography variant="h6" gutterBottom>
|
|
149
|
-
User Menu
|
|
150
|
-
</Typography>
|
|
151
|
-
<List dense>
|
|
152
|
-
<ListItem
|
|
153
|
-
component="button"
|
|
154
|
-
data-testid="menu-item-profile"
|
|
155
|
-
onClick={() => setClickedItem('profile')}
|
|
156
|
-
>
|
|
157
|
-
<ListItemText primary="Profile Settings" />
|
|
158
|
-
</ListItem>
|
|
159
|
-
<ListItem
|
|
160
|
-
component="button"
|
|
161
|
-
data-testid="menu-item-account"
|
|
162
|
-
onClick={() => setClickedItem('account')}
|
|
163
|
-
>
|
|
164
|
-
<ListItemText primary="Account Security" />
|
|
165
|
-
</ListItem>
|
|
166
|
-
<ListItem
|
|
167
|
-
component="button"
|
|
168
|
-
data-testid="menu-item-notifications"
|
|
169
|
-
onClick={() => setClickedItem('notifications')}
|
|
170
|
-
>
|
|
171
|
-
<ListItemText primary="Notifications" />
|
|
172
|
-
</ListItem>
|
|
173
|
-
</List>
|
|
174
|
-
</div>
|
|
175
|
-
</PopoverTestWrapper>
|
|
176
|
-
<div data-testid="clicked-item" style={{ display: 'none' }}>
|
|
177
|
-
{clickedItem}
|
|
178
|
-
</div>
|
|
179
|
-
<div aria-label="Status of the test run" style={{ marginTop: 20 }}>
|
|
180
|
-
PASS
|
|
181
|
-
</div>
|
|
182
|
-
</>
|
|
183
|
-
);
|
|
184
|
-
},
|
|
185
|
-
play: async ({ canvasElement, step }) => {
|
|
186
|
-
const canvas = within(canvasElement);
|
|
187
|
-
const statusElement = canvas.getByLabelText('Status of the test run');
|
|
188
|
-
statusElement.textContent = 'RUNS';
|
|
189
|
-
|
|
190
|
-
await step('Should open popover with menu items', async () => {
|
|
191
|
-
const trigger = canvas.getByTestId('popover-trigger');
|
|
192
|
-
await userEvent.click(trigger);
|
|
193
|
-
|
|
194
|
-
await waitFor(() => {
|
|
195
|
-
const profileItem = getPopoverElement('menu-item-profile');
|
|
196
|
-
const accountItem = getPopoverElement('menu-item-account');
|
|
197
|
-
const notificationsItem = getPopoverElement('menu-item-notifications');
|
|
198
|
-
|
|
199
|
-
expect(profileItem).toBeInTheDocument();
|
|
200
|
-
expect(accountItem).toBeInTheDocument();
|
|
201
|
-
expect(notificationsItem).toBeInTheDocument();
|
|
202
|
-
|
|
203
|
-
// Verify menu items have proper text content
|
|
204
|
-
expect(profileItem).toHaveTextContent('Profile Settings');
|
|
205
|
-
expect(accountItem).toHaveTextContent('Account Security');
|
|
206
|
-
expect(notificationsItem).toHaveTextContent('Notifications');
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
await step('Should be able to click menu items and trigger actions', async () => {
|
|
211
|
-
const profileItem = getPopoverElement('menu-item-profile') as HTMLElement;
|
|
212
|
-
await userEvent.click(profileItem);
|
|
213
|
-
|
|
214
|
-
// Verify click handler was called
|
|
215
|
-
await waitFor(() => {
|
|
216
|
-
const clickedItemElement = canvas.getByTestId('clicked-item');
|
|
217
|
-
expect(clickedItemElement).toHaveTextContent('profile');
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
// Menu item should still be accessible after click
|
|
221
|
-
expect(profileItem).toBeInTheDocument();
|
|
222
|
-
|
|
223
|
-
// Test clicking another item
|
|
224
|
-
const accountItem = getPopoverElement('menu-item-account') as HTMLElement;
|
|
225
|
-
await userEvent.click(accountItem);
|
|
226
|
-
|
|
227
|
-
await waitFor(() => {
|
|
228
|
-
const clickedItemElement = canvas.getByTestId('clicked-item');
|
|
229
|
-
expect(clickedItemElement).toHaveTextContent('account');
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
statusElement.textContent = 'PASS';
|
|
234
|
-
},
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
// 3. Keyboard Navigation Tests
|
|
238
|
-
export const KeyboardNavigation: Story = {
|
|
239
|
-
render: () => (
|
|
240
|
-
<>
|
|
241
|
-
<PopoverTestWrapper>
|
|
242
|
-
<div style={{ padding: 16 }}>
|
|
243
|
-
<Typography variant="h6" gutterBottom>
|
|
244
|
-
Keyboard Navigation Test
|
|
245
|
-
</Typography>
|
|
246
|
-
<List dense>
|
|
247
|
-
<ListItem component="button" tabIndex={0} data-testid="item-1">
|
|
248
|
-
<ListItemText primary="First Item" />
|
|
249
|
-
</ListItem>
|
|
250
|
-
<ListItem component="button" tabIndex={0} data-testid="item-2">
|
|
251
|
-
<ListItemText primary="Second Item" />
|
|
252
|
-
</ListItem>
|
|
253
|
-
<ListItem component="button" tabIndex={0} data-testid="item-3">
|
|
254
|
-
<ListItemText primary="Third Item" />
|
|
255
|
-
</ListItem>
|
|
256
|
-
</List>
|
|
257
|
-
</div>
|
|
258
|
-
</PopoverTestWrapper>
|
|
259
|
-
<div aria-label="Status of the test run" style={{ marginTop: 20 }}>
|
|
260
|
-
PASS
|
|
261
|
-
</div>
|
|
262
|
-
</>
|
|
263
|
-
),
|
|
264
|
-
play: async ({ canvasElement, step }) => {
|
|
265
|
-
const canvas = within(canvasElement);
|
|
266
|
-
const statusElement = canvas.getByLabelText('Status of the test run');
|
|
267
|
-
statusElement.textContent = 'RUNS';
|
|
268
|
-
|
|
269
|
-
await step('Should open popover with Enter key', async () => {
|
|
270
|
-
const trigger = canvas.getByTestId('popover-trigger');
|
|
271
|
-
await userEvent.click(trigger);
|
|
272
|
-
await waitFor(() => expect(trigger).toHaveFocus());
|
|
273
|
-
|
|
274
|
-
await userEvent.keyboard('{Enter}');
|
|
275
|
-
|
|
276
|
-
await waitFor(() => {
|
|
277
|
-
const firstItem = getPopoverElement('item-1');
|
|
278
|
-
expect(firstItem).toBeInTheDocument();
|
|
279
|
-
|
|
280
|
-
// Verify all items are present and focusable
|
|
281
|
-
const secondItem = getPopoverElement('item-2');
|
|
282
|
-
const thirdItem = getPopoverElement('item-3');
|
|
283
|
-
expect(secondItem).toBeInTheDocument();
|
|
284
|
-
expect(thirdItem).toBeInTheDocument();
|
|
285
|
-
|
|
286
|
-
// Verify items have proper tabIndex for keyboard navigation
|
|
287
|
-
expect(firstItem).toHaveAttribute('tabIndex', '0');
|
|
288
|
-
expect(secondItem).toHaveAttribute('tabIndex', '0');
|
|
289
|
-
expect(thirdItem).toHaveAttribute('tabIndex', '0');
|
|
290
|
-
});
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
await step('Should navigate between items with Tab key', async () => {
|
|
294
|
-
// Tab through items
|
|
295
|
-
const firstItem = getPopoverElement('item-1') as HTMLElement;
|
|
296
|
-
await userEvent.click(firstItem);
|
|
297
|
-
await waitFor(() => expect(firstItem).toHaveFocus());
|
|
298
|
-
|
|
299
|
-
await userEvent.keyboard('{Tab}');
|
|
300
|
-
const secondItem = getPopoverElement('item-2') as HTMLElement;
|
|
301
|
-
await waitFor(() => expect(secondItem).toHaveFocus());
|
|
302
|
-
|
|
303
|
-
await userEvent.keyboard('{Tab}');
|
|
304
|
-
const thirdItem = getPopoverElement('item-3') as HTMLElement;
|
|
305
|
-
await waitFor(() => expect(thirdItem).toHaveFocus());
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
await step('Should close popover with Escape key', async () => {
|
|
309
|
-
await userEvent.keyboard('{Escape}');
|
|
310
|
-
|
|
311
|
-
await waitFor(() => {
|
|
312
|
-
expect(queryPopoverElement('item-1')).not.toBeInTheDocument();
|
|
313
|
-
expect(queryPopoverElement('item-2')).not.toBeInTheDocument();
|
|
314
|
-
expect(queryPopoverElement('item-3')).not.toBeInTheDocument();
|
|
315
|
-
|
|
316
|
-
// Verify focus returns to trigger
|
|
317
|
-
const trigger = canvas.getByTestId('popover-trigger');
|
|
318
|
-
expect(trigger).toHaveFocus();
|
|
319
|
-
});
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
statusElement.textContent = 'PASS';
|
|
323
|
-
},
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
// 4. Screen Reader Tests
|
|
327
|
-
export const ScreenReader: Story = {
|
|
328
|
-
render: () => (
|
|
329
|
-
<>
|
|
330
|
-
<PopoverTestWrapper>
|
|
331
|
-
<div
|
|
332
|
-
role="dialog"
|
|
333
|
-
aria-label="User menu popover"
|
|
334
|
-
aria-describedby="popover-desc"
|
|
335
|
-
style={{ padding: 16 }}
|
|
336
|
-
>
|
|
337
|
-
<Typography variant="h6" gutterBottom id="popover-title">
|
|
338
|
-
Accessible Menu
|
|
339
|
-
</Typography>
|
|
340
|
-
<Typography variant="body2" id="popover-desc" sx={{ mb: 1 }}>
|
|
341
|
-
Navigate menu options using arrow keys
|
|
342
|
-
</Typography>
|
|
343
|
-
<List dense aria-labelledby="popover-title">
|
|
344
|
-
<ListItem
|
|
345
|
-
component="button"
|
|
346
|
-
role="menuitem"
|
|
347
|
-
data-testid="accessible-item-1"
|
|
348
|
-
aria-label="Navigate to profile settings"
|
|
349
|
-
>
|
|
350
|
-
<ListItemText primary="Accessible Item 1" />
|
|
351
|
-
</ListItem>
|
|
352
|
-
<ListItem
|
|
353
|
-
component="button"
|
|
354
|
-
role="menuitem"
|
|
355
|
-
data-testid="accessible-item-2"
|
|
356
|
-
aria-label="Navigate to account settings"
|
|
357
|
-
>
|
|
358
|
-
<ListItemText primary="Accessible Item 2" />
|
|
359
|
-
</ListItem>
|
|
360
|
-
</List>
|
|
361
|
-
</div>
|
|
362
|
-
</PopoverTestWrapper>
|
|
363
|
-
<div aria-label="Status of the test run" style={{ marginTop: 20 }}>
|
|
364
|
-
PASS
|
|
365
|
-
</div>
|
|
366
|
-
</>
|
|
367
|
-
),
|
|
368
|
-
play: async ({ canvasElement, step }) => {
|
|
369
|
-
const canvas = within(canvasElement);
|
|
370
|
-
const statusElement = canvas.getByLabelText('Status of the test run');
|
|
371
|
-
statusElement.textContent = 'RUNS';
|
|
372
|
-
|
|
373
|
-
await step('Should have proper ARIA attributes for screen readers', async () => {
|
|
374
|
-
const trigger = canvas.getByTestId('popover-trigger');
|
|
375
|
-
await userEvent.click(trigger);
|
|
376
|
-
|
|
377
|
-
await waitFor(() => {
|
|
378
|
-
// Use document queries for portal-rendered content
|
|
379
|
-
const dialog = document.querySelector('[role="dialog"]');
|
|
380
|
-
expect(dialog).toBeInTheDocument();
|
|
381
|
-
expect(dialog).toHaveAttribute('aria-label', 'User menu popover');
|
|
382
|
-
expect(dialog).toHaveAttribute('aria-describedby', 'popover-desc');
|
|
383
|
-
|
|
384
|
-
// Verify menu items have proper roles and labels
|
|
385
|
-
const menuItems = document.querySelectorAll('[role="menuitem"]');
|
|
386
|
-
expect(menuItems).toHaveLength(2);
|
|
387
|
-
expect(menuItems[0]).toHaveAttribute('aria-label', 'Navigate to profile settings');
|
|
388
|
-
expect(menuItems[1]).toHaveAttribute('aria-label', 'Navigate to account settings');
|
|
389
|
-
});
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
await step('Should have proper heading structure for screen readers', async () => {
|
|
393
|
-
// Use document queries for portal-rendered content
|
|
394
|
-
const title = document.querySelector('#popover-title');
|
|
395
|
-
expect(title).toBeInTheDocument();
|
|
396
|
-
expect(title).toHaveTextContent('Accessible Menu');
|
|
397
|
-
|
|
398
|
-
const description = document.querySelector('#popover-desc');
|
|
399
|
-
expect(description).toBeInTheDocument();
|
|
400
|
-
expect(description).toHaveTextContent('Navigate menu options using arrow keys');
|
|
401
|
-
|
|
402
|
-
const list = document.querySelector('[aria-labelledby="popover-title"]');
|
|
403
|
-
expect(list).toBeInTheDocument();
|
|
404
|
-
|
|
405
|
-
// Verify MUI Popover backdrop has proper role
|
|
406
|
-
const backdrop = document.querySelector('.MuiBackdrop-root');
|
|
407
|
-
if (backdrop) {
|
|
408
|
-
expect(backdrop).toHaveAttribute('aria-hidden', 'true');
|
|
409
|
-
}
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
statusElement.textContent = 'PASS';
|
|
413
|
-
},
|
|
414
|
-
};
|
|
415
|
-
|
|
416
|
-
// 5. Focus Management Tests
|
|
417
|
-
export const FocusManagement: Story = {
|
|
418
|
-
render: () => (
|
|
419
|
-
<>
|
|
420
|
-
<div>
|
|
421
|
-
<Button data-testid="before-popover">Before Popover</Button>
|
|
422
|
-
<PopoverTestWrapper>
|
|
423
|
-
<div style={{ padding: 16 }}>
|
|
424
|
-
<Button data-testid="first-focusable" autoFocus>
|
|
425
|
-
First Focusable
|
|
426
|
-
</Button>
|
|
427
|
-
<Button data-testid="second-focusable">Second Focusable</Button>
|
|
428
|
-
<Button data-testid="third-focusable">Third Focusable</Button>
|
|
429
|
-
</div>
|
|
430
|
-
</PopoverTestWrapper>
|
|
431
|
-
<Button data-testid="after-popover">After Popover</Button>
|
|
432
|
-
</div>
|
|
433
|
-
<div aria-label="Status of the test run" style={{ marginTop: 20 }}>
|
|
434
|
-
PASS
|
|
435
|
-
</div>
|
|
436
|
-
</>
|
|
437
|
-
),
|
|
438
|
-
play: async ({ canvasElement, step }) => {
|
|
439
|
-
const canvas = within(canvasElement);
|
|
440
|
-
const statusElement = canvas.getByLabelText('Status of the test run');
|
|
441
|
-
statusElement.textContent = 'RUNS';
|
|
442
|
-
|
|
443
|
-
await step('Should manage focus when popover opens', async () => {
|
|
444
|
-
const trigger = canvas.getByTestId('popover-trigger');
|
|
445
|
-
|
|
446
|
-
// Set initial focus to before button
|
|
447
|
-
const beforeButton = canvas.getByTestId('before-popover');
|
|
448
|
-
await userEvent.click(beforeButton);
|
|
449
|
-
await waitFor(() => expect(beforeButton).toHaveFocus());
|
|
450
|
-
|
|
451
|
-
// Open popover
|
|
452
|
-
await userEvent.click(trigger);
|
|
453
|
-
|
|
454
|
-
await waitFor(() => {
|
|
455
|
-
// Use document queries for portal-rendered content
|
|
456
|
-
const firstFocusable = document.querySelector('[data-testid="first-focusable"]');
|
|
457
|
-
expect(firstFocusable).toBeInTheDocument();
|
|
458
|
-
|
|
459
|
-
// Verify autoFocus worked
|
|
460
|
-
expect(firstFocusable).toHaveFocus();
|
|
461
|
-
|
|
462
|
-
// Verify all focusable elements are present
|
|
463
|
-
const secondFocusable = document.querySelector('[data-testid="second-focusable"]');
|
|
464
|
-
const thirdFocusable = document.querySelector('[data-testid="third-focusable"]');
|
|
465
|
-
expect(secondFocusable).toBeInTheDocument();
|
|
466
|
-
expect(thirdFocusable).toBeInTheDocument();
|
|
467
|
-
});
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
await step('Should cycle focus within popover', async () => {
|
|
471
|
-
// Tab through elements (use document queries for portal content)
|
|
472
|
-
await userEvent.keyboard('{Tab}');
|
|
473
|
-
const secondFocusable = document.querySelector('[data-testid="second-focusable"]');
|
|
474
|
-
await waitFor(() => expect(secondFocusable).toHaveFocus());
|
|
475
|
-
|
|
476
|
-
await userEvent.keyboard('{Tab}');
|
|
477
|
-
const thirdFocusable = document.querySelector('[data-testid="third-focusable"]');
|
|
478
|
-
await waitFor(() => expect(thirdFocusable).toHaveFocus());
|
|
479
|
-
|
|
480
|
-
// Shift+Tab to go back
|
|
481
|
-
await userEvent.keyboard('{Shift>}{Tab}{/Shift}');
|
|
482
|
-
await waitFor(() => expect(secondFocusable).toHaveFocus());
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
await step('Should return focus to trigger when closed', async () => {
|
|
486
|
-
await userEvent.keyboard('{Escape}');
|
|
487
|
-
|
|
488
|
-
await waitFor(() => {
|
|
489
|
-
const trigger = canvas.getByTestId('popover-trigger');
|
|
490
|
-
expect(trigger).toHaveFocus();
|
|
491
|
-
|
|
492
|
-
// Verify popover content is removed (check in document, not canvas)
|
|
493
|
-
expect(document.querySelector('[data-testid="first-focusable"]')).not.toBeInTheDocument();
|
|
494
|
-
expect(document.querySelector('[data-testid="second-focusable"]')).not.toBeInTheDocument();
|
|
495
|
-
});
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
statusElement.textContent = 'PASS';
|
|
499
|
-
},
|
|
500
|
-
};
|
|
501
|
-
|
|
502
|
-
// 6. Responsive Design Tests
|
|
503
|
-
export const ResponsiveDesign: Story = {
|
|
504
|
-
parameters: {
|
|
505
|
-
viewport: {
|
|
506
|
-
defaultViewport: 'mobile1',
|
|
507
|
-
},
|
|
508
|
-
},
|
|
509
|
-
render: () => (
|
|
510
|
-
<>
|
|
511
|
-
<PopoverTestWrapper maxWidth={280}>
|
|
512
|
-
<div style={{ padding: 16 }}>
|
|
513
|
-
<Typography variant="h6" gutterBottom>
|
|
514
|
-
Mobile Responsive Content
|
|
515
|
-
</Typography>
|
|
516
|
-
<Typography variant="body2" data-testid="mobile-content">
|
|
517
|
-
This content should adapt to mobile viewport sizes and maintain readability.
|
|
518
|
-
</Typography>
|
|
519
|
-
<Button data-testid="mobile-button" size="small" fullWidth>
|
|
520
|
-
Mobile Action
|
|
521
|
-
</Button>
|
|
522
|
-
</div>
|
|
523
|
-
</PopoverTestWrapper>
|
|
524
|
-
<div aria-label="Status of the test run" style={{ marginTop: 20 }}>
|
|
525
|
-
PASS
|
|
526
|
-
</div>
|
|
527
|
-
</>
|
|
528
|
-
),
|
|
529
|
-
play: async ({ canvasElement, step }) => {
|
|
530
|
-
const canvas = within(canvasElement);
|
|
531
|
-
const statusElement = canvas.getByLabelText('Status of the test run');
|
|
532
|
-
statusElement.textContent = 'RUNS';
|
|
533
|
-
|
|
534
|
-
await step('Should display properly on mobile viewport with constrained width', async () => {
|
|
535
|
-
const trigger = canvas.getByTestId('popover-trigger');
|
|
536
|
-
await userEvent.click(trigger);
|
|
537
|
-
|
|
538
|
-
await waitFor(() => {
|
|
539
|
-
const content = getPopoverElement('mobile-content');
|
|
540
|
-
expect(content).toBeInTheDocument();
|
|
541
|
-
expect(content).toHaveTextContent(
|
|
542
|
-
'This content should adapt to mobile viewport sizes and maintain readability.',
|
|
543
|
-
);
|
|
544
|
-
|
|
545
|
-
// Verify mobile button is full width
|
|
546
|
-
const mobileButton = getPopoverElement('mobile-button');
|
|
547
|
-
expect(mobileButton).toBeInTheDocument();
|
|
548
|
-
expect(mobileButton).toHaveClass('MuiButton-fullWidth');
|
|
549
|
-
|
|
550
|
-
// Verify popover Paper respects maxWidth
|
|
551
|
-
const paper = document.querySelector('.MuiPaper-root') as HTMLElement;
|
|
552
|
-
if (paper) {
|
|
553
|
-
const computedStyle = window.getComputedStyle(paper);
|
|
554
|
-
const maxWidth = computedStyle.maxWidth;
|
|
555
|
-
expect(maxWidth).toBe('280px');
|
|
556
|
-
}
|
|
557
|
-
});
|
|
558
|
-
});
|
|
559
|
-
|
|
560
|
-
await step('Should position correctly on mobile screens', async () => {
|
|
561
|
-
// Verify popover is visible within viewport
|
|
562
|
-
const popoverRoot = document.querySelector('[role="presentation"]') as HTMLElement;
|
|
563
|
-
if (popoverRoot) {
|
|
564
|
-
const rect = popoverRoot.getBoundingClientRect();
|
|
565
|
-
expect(rect.left).toBeGreaterThanOrEqual(0);
|
|
566
|
-
expect(rect.top).toBeGreaterThanOrEqual(0);
|
|
567
|
-
}
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
statusElement.textContent = 'PASS';
|
|
571
|
-
},
|
|
572
|
-
};
|
|
573
|
-
|
|
574
|
-
// 7. Theme Variations Tests
|
|
575
|
-
export const ThemeVariations: Story = {
|
|
576
|
-
render: () => (
|
|
577
|
-
<>
|
|
578
|
-
<Stack spacing={2} direction="row">
|
|
579
|
-
<PopoverTestWrapper variant="default" data-testid="default-popover">
|
|
580
|
-
<Typography sx={{ p: 2 }} data-testid="default-content">
|
|
581
|
-
Default Theme
|
|
582
|
-
</Typography>
|
|
583
|
-
</PopoverTestWrapper>
|
|
584
|
-
<PopoverTestWrapper variant="glass" data-testid="glass-popover">
|
|
585
|
-
<Typography sx={{ p: 2 }} data-testid="glass-content">
|
|
586
|
-
Glass Theme
|
|
587
|
-
</Typography>
|
|
588
|
-
</PopoverTestWrapper>
|
|
589
|
-
<PopoverTestWrapper variant="arrow" data-testid="arrow-popover">
|
|
590
|
-
<Typography sx={{ p: 2 }} data-testid="arrow-content">
|
|
591
|
-
Arrow Theme
|
|
592
|
-
</Typography>
|
|
593
|
-
</PopoverTestWrapper>
|
|
594
|
-
</Stack>
|
|
595
|
-
<div aria-label="Status of the test run" style={{ marginTop: 20 }}>
|
|
596
|
-
PASS
|
|
597
|
-
</div>
|
|
598
|
-
</>
|
|
599
|
-
),
|
|
600
|
-
play: async ({ canvasElement, step }) => {
|
|
601
|
-
const canvas = within(canvasElement);
|
|
602
|
-
const statusElement = canvas.getByLabelText('Status of the test run');
|
|
603
|
-
statusElement.textContent = 'RUNS';
|
|
604
|
-
|
|
605
|
-
await step('Should render and style all theme variants correctly', async () => {
|
|
606
|
-
const triggers = canvas.getAllByTestId('popover-trigger');
|
|
607
|
-
expect(triggers).toHaveLength(3);
|
|
608
|
-
|
|
609
|
-
// Test default variant
|
|
610
|
-
await userEvent.click(triggers[0]);
|
|
611
|
-
await waitFor(() => {
|
|
612
|
-
// Use document queries for portal-rendered content
|
|
613
|
-
const content = document.querySelector('[data-testid="default-content"]');
|
|
614
|
-
expect(content).toBeInTheDocument();
|
|
615
|
-
expect(content).toHaveTextContent('Default Theme');
|
|
616
|
-
|
|
617
|
-
// Verify default variant styling
|
|
618
|
-
const paper = content?.closest('.MuiPaper-root') as HTMLElement;
|
|
619
|
-
if (paper) {
|
|
620
|
-
const computedStyle = window.getComputedStyle(paper);
|
|
621
|
-
expect(computedStyle.borderRadius).toBeTruthy();
|
|
622
|
-
expect(computedStyle.backgroundColor).toBeTruthy();
|
|
623
|
-
}
|
|
624
|
-
});
|
|
625
|
-
await userEvent.keyboard('{Escape}');
|
|
626
|
-
|
|
627
|
-
// Wait for animation
|
|
628
|
-
await waitFor(() => {
|
|
629
|
-
expect(document.querySelector('[data-testid="default-content"]')).not.toBeInTheDocument();
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
// Test glass variant
|
|
633
|
-
await userEvent.click(triggers[1]);
|
|
634
|
-
await waitFor(() => {
|
|
635
|
-
// Use document queries for portal-rendered content
|
|
636
|
-
const content = document.querySelector('[data-testid="glass-content"]');
|
|
637
|
-
expect(content).toBeInTheDocument();
|
|
638
|
-
expect(content).toHaveTextContent('Glass Theme');
|
|
639
|
-
|
|
640
|
-
// Verify glass variant has backdrop filter
|
|
641
|
-
const paper = content?.closest('.MuiPaper-root') as HTMLElement;
|
|
642
|
-
if (paper) {
|
|
643
|
-
const computedStyle = window.getComputedStyle(paper);
|
|
644
|
-
// Glass variant should have backdrop-filter
|
|
645
|
-
expect(computedStyle.backdropFilter || computedStyle.webkitBackdropFilter).toBeTruthy();
|
|
646
|
-
}
|
|
647
|
-
});
|
|
648
|
-
await userEvent.keyboard('{Escape}');
|
|
649
|
-
|
|
650
|
-
// Wait for animation
|
|
651
|
-
await waitFor(() => {
|
|
652
|
-
expect(document.querySelector('[data-testid="glass-content"]')).not.toBeInTheDocument();
|
|
653
|
-
});
|
|
654
|
-
|
|
655
|
-
// Test arrow variant
|
|
656
|
-
await userEvent.click(triggers[2]);
|
|
657
|
-
await waitFor(() => {
|
|
658
|
-
// Use document queries for portal-rendered content
|
|
659
|
-
const content = document.querySelector('[data-testid="arrow-content"]');
|
|
660
|
-
expect(content).toBeInTheDocument();
|
|
661
|
-
expect(content).toHaveTextContent('Arrow Theme');
|
|
662
|
-
|
|
663
|
-
// Verify arrow variant styling
|
|
664
|
-
const paper = content?.closest('.MuiPaper-root') as HTMLElement;
|
|
665
|
-
if (paper) {
|
|
666
|
-
const computedStyle = window.getComputedStyle(paper);
|
|
667
|
-
expect(computedStyle.boxShadow).toBeTruthy();
|
|
668
|
-
}
|
|
669
|
-
});
|
|
670
|
-
await userEvent.keyboard('{Escape}');
|
|
671
|
-
});
|
|
672
|
-
|
|
673
|
-
statusElement.textContent = 'PASS';
|
|
674
|
-
},
|
|
675
|
-
};
|
|
676
|
-
|
|
677
|
-
// 8. Visual States Tests
|
|
678
|
-
export const VisualStates: Story = {
|
|
679
|
-
render: () => (
|
|
680
|
-
<>
|
|
681
|
-
<Stack spacing={2} direction="row">
|
|
682
|
-
<PopoverTestWrapper glow data-testid="glow-popover">
|
|
683
|
-
<Typography sx={{ p: 2 }} data-testid="glow-content">
|
|
684
|
-
Glow Effect
|
|
685
|
-
</Typography>
|
|
686
|
-
</PopoverTestWrapper>
|
|
687
|
-
<PopoverTestWrapper pulse data-testid="pulse-popover">
|
|
688
|
-
<Typography sx={{ p: 2 }} data-testid="pulse-content">
|
|
689
|
-
Pulse Effect
|
|
690
|
-
</Typography>
|
|
691
|
-
</PopoverTestWrapper>
|
|
692
|
-
<PopoverTestWrapper glow pulse data-testid="both-effects-popover">
|
|
693
|
-
<Typography sx={{ p: 2 }} data-testid="both-effects-content">
|
|
694
|
-
Both Effects
|
|
695
|
-
</Typography>
|
|
696
|
-
</PopoverTestWrapper>
|
|
697
|
-
</Stack>
|
|
698
|
-
<div aria-label="Status of the test run" style={{ marginTop: 20 }}>
|
|
699
|
-
PASS
|
|
700
|
-
</div>
|
|
701
|
-
</>
|
|
702
|
-
),
|
|
703
|
-
play: async ({ canvasElement, step }) => {
|
|
704
|
-
const canvas = within(canvasElement);
|
|
705
|
-
const statusElement = canvas.getByLabelText('Status of the test run');
|
|
706
|
-
statusElement.textContent = 'RUNS';
|
|
707
|
-
|
|
708
|
-
await step('Should render and apply visual effect variants', async () => {
|
|
709
|
-
const triggers = canvas.getAllByTestId('popover-trigger');
|
|
710
|
-
expect(triggers).toHaveLength(3);
|
|
711
|
-
|
|
712
|
-
// Test glow effect
|
|
713
|
-
await userEvent.click(triggers[0]);
|
|
714
|
-
await waitFor(() => {
|
|
715
|
-
// Use document queries for portal-rendered content
|
|
716
|
-
const content = document.querySelector('[data-testid="glow-content"]');
|
|
717
|
-
expect(content).toBeInTheDocument();
|
|
718
|
-
|
|
719
|
-
// Verify glow effect is applied (enhanced box-shadow)
|
|
720
|
-
const paper = content?.closest('.MuiPaper-root') as HTMLElement;
|
|
721
|
-
if (paper) {
|
|
722
|
-
const computedStyle = window.getComputedStyle(paper);
|
|
723
|
-
expect(computedStyle.boxShadow).toContain('rgb');
|
|
724
|
-
expect(computedStyle.filter).toContain('brightness');
|
|
725
|
-
}
|
|
726
|
-
});
|
|
727
|
-
await userEvent.keyboard('{Escape}');
|
|
728
|
-
|
|
729
|
-
// Wait for close animation
|
|
730
|
-
await waitFor(() => {
|
|
731
|
-
expect(document.querySelector('[data-testid="glow-content"]')).not.toBeInTheDocument();
|
|
732
|
-
});
|
|
733
|
-
|
|
734
|
-
// Test pulse effect
|
|
735
|
-
await userEvent.click(triggers[1]);
|
|
736
|
-
await waitFor(() => {
|
|
737
|
-
// Use document queries for portal-rendered content
|
|
738
|
-
const content = document.querySelector('[data-testid="pulse-content"]');
|
|
739
|
-
expect(content).toBeInTheDocument();
|
|
740
|
-
|
|
741
|
-
// Verify pulse animation is present
|
|
742
|
-
const paper = content?.closest('.MuiPaper-root') as HTMLElement;
|
|
743
|
-
if (paper) {
|
|
744
|
-
// Check for ::after pseudo element that creates pulse
|
|
745
|
-
const computedStyle = window.getComputedStyle(paper, '::after');
|
|
746
|
-
expect(computedStyle.animation || computedStyle.animationName).toBeTruthy();
|
|
747
|
-
}
|
|
748
|
-
});
|
|
749
|
-
await userEvent.keyboard('{Escape}');
|
|
750
|
-
|
|
751
|
-
// Wait for close animation
|
|
752
|
-
await waitFor(() => {
|
|
753
|
-
expect(document.querySelector('[data-testid="pulse-content"]')).not.toBeInTheDocument();
|
|
754
|
-
});
|
|
755
|
-
|
|
756
|
-
// Test both effects combined
|
|
757
|
-
await userEvent.click(triggers[2]);
|
|
758
|
-
await waitFor(() => {
|
|
759
|
-
// Use document queries for portal-rendered content
|
|
760
|
-
const content = document.querySelector('[data-testid="both-effects-content"]');
|
|
761
|
-
expect(content).toBeInTheDocument();
|
|
762
|
-
|
|
763
|
-
// Verify both effects are applied
|
|
764
|
-
const paper = content?.closest('.MuiPaper-root') as HTMLElement;
|
|
765
|
-
if (paper) {
|
|
766
|
-
const computedStyle = window.getComputedStyle(paper);
|
|
767
|
-
// Should have glow shadow
|
|
768
|
-
expect(computedStyle.boxShadow).toContain('rgb');
|
|
769
|
-
expect(computedStyle.filter).toContain('brightness');
|
|
770
|
-
// Should have pulse animation
|
|
771
|
-
const afterStyle = window.getComputedStyle(paper, '::after');
|
|
772
|
-
expect(afterStyle.animation || afterStyle.animationName).toBeTruthy();
|
|
773
|
-
}
|
|
774
|
-
});
|
|
775
|
-
await userEvent.keyboard('{Escape}');
|
|
776
|
-
});
|
|
777
|
-
|
|
778
|
-
statusElement.textContent = 'PASS';
|
|
779
|
-
},
|
|
780
|
-
};
|
|
781
|
-
|
|
782
|
-
// 9. Performance Tests
|
|
783
|
-
const PerformanceTestComponent = () => {
|
|
784
|
-
const [renderCount, setRenderCount] = React.useState(0);
|
|
785
|
-
const [openCount, setOpenCount] = React.useState(0);
|
|
786
|
-
|
|
787
|
-
React.useEffect(() => {
|
|
788
|
-
setRenderCount((c) => c + 1);
|
|
789
|
-
}, []);
|
|
790
|
-
|
|
791
|
-
return (
|
|
792
|
-
<div>
|
|
793
|
-
<div data-testid="render-count">Renders: {renderCount}</div>
|
|
794
|
-
<div data-testid="open-count" style={{ display: 'none' }}>
|
|
795
|
-
Opens: {openCount}
|
|
796
|
-
</div>
|
|
797
|
-
<PopoverTestWrapper onOpen={() => setOpenCount((c) => c + 1)}>
|
|
798
|
-
<div style={{ padding: 16 }}>
|
|
799
|
-
<Typography variant="h6">Performance Test</Typography>
|
|
800
|
-
<Typography data-testid="performance-content">
|
|
801
|
-
This tests if the popover triggers excessive re-renders.
|
|
802
|
-
</Typography>
|
|
803
|
-
</div>
|
|
804
|
-
</PopoverTestWrapper>
|
|
805
|
-
<div aria-label="Status of the test run" style={{ marginTop: 20 }}>
|
|
806
|
-
PASS
|
|
807
|
-
</div>
|
|
808
|
-
</div>
|
|
809
|
-
);
|
|
810
|
-
};
|
|
811
|
-
|
|
812
|
-
export const Performance: Story = {
|
|
813
|
-
render: () => <PerformanceTestComponent />,
|
|
814
|
-
play: async ({ canvasElement, step }) => {
|
|
815
|
-
const canvas = within(canvasElement);
|
|
816
|
-
const statusElement = canvas.getByLabelText('Status of the test run');
|
|
817
|
-
statusElement.textContent = 'RUNS';
|
|
818
|
-
|
|
819
|
-
await step('Should not cause excessive re-renders during interactions', async () => {
|
|
820
|
-
const renderCountElement = canvas.getByTestId('render-count');
|
|
821
|
-
const initialRenders = parseInt(renderCountElement.textContent?.split(': ')[1] || '0');
|
|
822
|
-
|
|
823
|
-
// Open and close popover multiple times
|
|
824
|
-
const trigger = canvas.getByTestId('popover-trigger');
|
|
825
|
-
|
|
826
|
-
// First open/close cycle
|
|
827
|
-
await userEvent.click(trigger);
|
|
828
|
-
await waitFor(() => {
|
|
829
|
-
expect(document.querySelector('[data-testid="performance-content"]')).toBeInTheDocument();
|
|
830
|
-
});
|
|
831
|
-
await userEvent.keyboard('{Escape}');
|
|
832
|
-
await waitFor(() => {
|
|
833
|
-
expect(
|
|
834
|
-
document.querySelector('[data-testid="performance-content"]'),
|
|
835
|
-
).not.toBeInTheDocument();
|
|
836
|
-
});
|
|
837
|
-
|
|
838
|
-
// Second open/close cycle
|
|
839
|
-
await userEvent.click(trigger);
|
|
840
|
-
await waitFor(() => {
|
|
841
|
-
expect(document.querySelector('[data-testid="performance-content"]')).toBeInTheDocument();
|
|
842
|
-
});
|
|
843
|
-
await userEvent.keyboard('{Escape}');
|
|
844
|
-
await waitFor(() => {
|
|
845
|
-
expect(
|
|
846
|
-
document.querySelector('[data-testid="performance-content"]'),
|
|
847
|
-
).not.toBeInTheDocument();
|
|
848
|
-
});
|
|
849
|
-
|
|
850
|
-
// Third open/close cycle
|
|
851
|
-
await userEvent.click(trigger);
|
|
852
|
-
await waitFor(() => {
|
|
853
|
-
expect(document.querySelector('[data-testid="performance-content"]')).toBeInTheDocument();
|
|
854
|
-
});
|
|
855
|
-
await userEvent.keyboard('{Escape}');
|
|
856
|
-
await waitFor(() => {
|
|
857
|
-
expect(
|
|
858
|
-
document.querySelector('[data-testid="performance-content"]'),
|
|
859
|
-
).not.toBeInTheDocument();
|
|
860
|
-
});
|
|
861
|
-
|
|
862
|
-
// Check that renders didn't increase dramatically
|
|
863
|
-
const finalRenders = parseInt(
|
|
864
|
-
canvas.getByTestId('render-count').textContent?.split(': ')[1] || '0',
|
|
865
|
-
);
|
|
866
|
-
expect(finalRenders - initialRenders).toBeLessThan(10);
|
|
867
|
-
|
|
868
|
-
// Verify open count matches expected
|
|
869
|
-
const openCount = parseInt(
|
|
870
|
-
canvas.getByTestId('open-count').textContent?.split(': ')[1] || '0',
|
|
871
|
-
);
|
|
872
|
-
expect(openCount).toBe(3);
|
|
873
|
-
});
|
|
874
|
-
|
|
875
|
-
await step('Should handle rapid open/close without performance degradation', async () => {
|
|
876
|
-
const trigger = canvas.getByTestId('popover-trigger');
|
|
877
|
-
|
|
878
|
-
// Rapid open/close
|
|
879
|
-
for (let i = 0; i < 5; i++) {
|
|
880
|
-
await userEvent.click(trigger);
|
|
881
|
-
await userEvent.keyboard('{Escape}');
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
});
|
|
885
|
-
|
|
886
|
-
statusElement.textContent = 'PASS';
|
|
887
|
-
},
|
|
888
|
-
};
|
|
889
|
-
|
|
890
|
-
// 10. Edge Cases Tests
|
|
891
|
-
export const EdgeCases: Story = {
|
|
892
|
-
render: () => (
|
|
893
|
-
<>
|
|
894
|
-
<Stack spacing={2}>
|
|
895
|
-
{/* Empty content */}
|
|
896
|
-
<PopoverTestWrapper data-testid="empty-popover">
|
|
897
|
-
<div style={{ padding: 16, minHeight: 50 }} data-testid="empty-content" />
|
|
898
|
-
</PopoverTestWrapper>
|
|
899
|
-
|
|
900
|
-
{/* Very long content */}
|
|
901
|
-
<PopoverTestWrapper maxWidth={200} data-testid="long-content-popover">
|
|
902
|
-
<Typography sx={{ p: 2 }} data-testid="long-content">
|
|
903
|
-
This is a very long text content that should wrap properly and not break the popover
|
|
904
|
-
layout. It should handle overflow gracefully and maintain proper spacing and typography.
|
|
905
|
-
Additional text to ensure wrapping behavior is tested thoroughly.
|
|
906
|
-
</Typography>
|
|
907
|
-
</PopoverTestWrapper>
|
|
908
|
-
|
|
909
|
-
{/* No maxWidth */}
|
|
910
|
-
<PopoverTestWrapper maxWidth={undefined} data-testid="no-max-width-popover">
|
|
911
|
-
<Typography sx={{ p: 2 }} data-testid="no-max-width-content">
|
|
912
|
-
Content without maxWidth constraint should expand naturally
|
|
913
|
-
</Typography>
|
|
914
|
-
</PopoverTestWrapper>
|
|
915
|
-
|
|
916
|
-
{/* Zero maxWidth edge case */}
|
|
917
|
-
<PopoverTestWrapper maxWidth={0} data-testid="zero-width-popover">
|
|
918
|
-
<Typography sx={{ p: 2 }} data-testid="zero-width-content">
|
|
919
|
-
Zero width test
|
|
920
|
-
</Typography>
|
|
921
|
-
</PopoverTestWrapper>
|
|
922
|
-
</Stack>
|
|
923
|
-
<div aria-label="Status of the test run" style={{ marginTop: 20 }}>
|
|
924
|
-
PASS
|
|
925
|
-
</div>
|
|
926
|
-
</>
|
|
927
|
-
),
|
|
928
|
-
play: async ({ canvasElement, step }) => {
|
|
929
|
-
const canvas = within(canvasElement);
|
|
930
|
-
const statusElement = canvas.getByLabelText('Status of the test run');
|
|
931
|
-
statusElement.textContent = 'RUNS';
|
|
932
|
-
|
|
933
|
-
await step('Should handle empty content gracefully', async () => {
|
|
934
|
-
const triggers = canvas.getAllByTestId('popover-trigger');
|
|
935
|
-
await userEvent.click(triggers[0]);
|
|
936
|
-
|
|
937
|
-
await waitFor(() => {
|
|
938
|
-
// Use document queries for portal-rendered content
|
|
939
|
-
const emptyContent = document.querySelector('[data-testid="empty-content"]');
|
|
940
|
-
expect(emptyContent).toBeInTheDocument();
|
|
941
|
-
|
|
942
|
-
// Verify minimum height is applied
|
|
943
|
-
const computedStyle = window.getComputedStyle(emptyContent as HTMLElement);
|
|
944
|
-
expect(computedStyle.minHeight).toBe('50px');
|
|
945
|
-
|
|
946
|
-
// Verify popover renders even with empty content
|
|
947
|
-
const paper = emptyContent?.closest('.MuiPaper-root');
|
|
948
|
-
expect(paper).toBeInTheDocument();
|
|
949
|
-
});
|
|
950
|
-
await userEvent.keyboard('{Escape}');
|
|
951
|
-
});
|
|
952
|
-
|
|
953
|
-
await step('Should handle long content with proper text wrapping', async () => {
|
|
954
|
-
const triggers = canvas.getAllByTestId('popover-trigger');
|
|
955
|
-
await userEvent.click(triggers[1]);
|
|
956
|
-
|
|
957
|
-
await waitFor(() => {
|
|
958
|
-
// Use document queries for portal-rendered content
|
|
959
|
-
const content = document.querySelector('[data-testid="long-content"]') as HTMLElement;
|
|
960
|
-
expect(content).toBeInTheDocument();
|
|
961
|
-
|
|
962
|
-
// Verify maxWidth is applied
|
|
963
|
-
const paper = content?.closest('.MuiPaper-root') as HTMLElement;
|
|
964
|
-
if (paper) {
|
|
965
|
-
const computedStyle = window.getComputedStyle(paper);
|
|
966
|
-
expect(computedStyle.maxWidth).toBe('200px');
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
// Verify text wraps properly
|
|
970
|
-
const contentHeight = content?.offsetHeight;
|
|
971
|
-
expect(contentHeight).toBeGreaterThan(50); // Should be taller due to wrapping
|
|
972
|
-
});
|
|
973
|
-
await userEvent.keyboard('{Escape}');
|
|
974
|
-
});
|
|
975
|
-
|
|
976
|
-
await step('Should handle content without maxWidth constraint', async () => {
|
|
977
|
-
const triggers = canvas.getAllByTestId('popover-trigger');
|
|
978
|
-
await userEvent.click(triggers[2]);
|
|
979
|
-
|
|
980
|
-
await waitFor(() => {
|
|
981
|
-
// Use document queries for portal-rendered content
|
|
982
|
-
const content = document.querySelector('[data-testid="no-max-width-content"]');
|
|
983
|
-
expect(content).toBeInTheDocument();
|
|
984
|
-
|
|
985
|
-
// Verify no maxWidth constraint is applied (should use default)
|
|
986
|
-
const paper = content?.closest('.MuiPaper-root') as HTMLElement;
|
|
987
|
-
if (paper) {
|
|
988
|
-
const computedStyle = window.getComputedStyle(paper);
|
|
989
|
-
// When undefined, should use default maxWidth of 400px
|
|
990
|
-
expect(computedStyle.maxWidth).toBe('400px');
|
|
991
|
-
}
|
|
992
|
-
});
|
|
993
|
-
await userEvent.keyboard('{Escape}');
|
|
994
|
-
});
|
|
995
|
-
|
|
996
|
-
await step('Should handle zero maxWidth edge case', async () => {
|
|
997
|
-
const triggers = canvas.getAllByTestId('popover-trigger');
|
|
998
|
-
await userEvent.click(triggers[3]);
|
|
999
|
-
|
|
1000
|
-
await waitFor(() => {
|
|
1001
|
-
// Use document queries for portal-rendered content
|
|
1002
|
-
const content = document.querySelector('[data-testid="zero-width-content"]');
|
|
1003
|
-
expect(content).toBeInTheDocument();
|
|
1004
|
-
|
|
1005
|
-
// Verify zero maxWidth is handled
|
|
1006
|
-
const paper = content?.closest('.MuiPaper-root') as HTMLElement;
|
|
1007
|
-
if (paper) {
|
|
1008
|
-
const computedStyle = window.getComputedStyle(paper);
|
|
1009
|
-
expect(computedStyle.maxWidth).toBe('0px');
|
|
1010
|
-
}
|
|
1011
|
-
});
|
|
1012
|
-
await userEvent.keyboard('{Escape}');
|
|
1013
|
-
});
|
|
1014
|
-
|
|
1015
|
-
statusElement.textContent = 'PASS';
|
|
1016
|
-
},
|
|
1017
|
-
};
|
|
1018
|
-
|
|
1019
|
-
// 11. Integration Tests
|
|
1020
|
-
const IntegrationTestComponent = () => {
|
|
1021
|
-
const [selectedItem, setSelectedItem] = React.useState<string>('');
|
|
1022
|
-
const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null);
|
|
1023
|
-
const isOpen = Boolean(anchorEl);
|
|
1024
|
-
|
|
1025
|
-
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
1026
|
-
setAnchorEl(event.currentTarget);
|
|
1027
|
-
};
|
|
1028
|
-
|
|
1029
|
-
const handleClose = () => {
|
|
1030
|
-
setAnchorEl(null);
|
|
1031
|
-
};
|
|
1032
|
-
|
|
1033
|
-
const handleItemClick = (option: string) => {
|
|
1034
|
-
setSelectedItem(option);
|
|
1035
|
-
handleClose(); // Properly close popover after selection
|
|
1036
|
-
};
|
|
1037
|
-
|
|
1038
|
-
return (
|
|
1039
|
-
<>
|
|
1040
|
-
<div>
|
|
1041
|
-
<Typography data-testid="selected-item">Selected: {selectedItem || 'None'}</Typography>
|
|
1042
|
-
<Button variant="contained" onClick={handleClick} data-testid="popover-trigger">
|
|
1043
|
-
Open Popover
|
|
1044
|
-
</Button>
|
|
1045
|
-
<Popover
|
|
1046
|
-
open={isOpen}
|
|
1047
|
-
anchorEl={anchorEl}
|
|
1048
|
-
onClose={handleClose}
|
|
1049
|
-
anchorOrigin={{
|
|
1050
|
-
vertical: 'bottom',
|
|
1051
|
-
horizontal: 'left',
|
|
1052
|
-
}}
|
|
1053
|
-
data-testid="popover"
|
|
1054
|
-
>
|
|
1055
|
-
<div style={{ padding: 16 }}>
|
|
1056
|
-
<Typography variant="h6" gutterBottom>
|
|
1057
|
-
Select an Option
|
|
1058
|
-
</Typography>
|
|
1059
|
-
<List dense>
|
|
1060
|
-
{['Option 1', 'Option 2', 'Option 3'].map((option) => (
|
|
1061
|
-
<ListItem
|
|
1062
|
-
key={option}
|
|
1063
|
-
component="button"
|
|
1064
|
-
data-testid={`option-${option.toLowerCase().replace(' ', '-')}`}
|
|
1065
|
-
onClick={() => handleItemClick(option)}
|
|
1066
|
-
>
|
|
1067
|
-
<ListItemText primary={option} />
|
|
1068
|
-
</ListItem>
|
|
1069
|
-
))}
|
|
1070
|
-
</List>
|
|
1071
|
-
</div>
|
|
1072
|
-
</Popover>
|
|
1073
|
-
<Typography data-testid="popover-state" style={{ display: 'none' }}>
|
|
1074
|
-
{isOpen ? 'open' : 'closed'}
|
|
1075
|
-
</Typography>
|
|
1076
|
-
</div>
|
|
1077
|
-
<div aria-label="Status of the test run" style={{ marginTop: 20 }}>
|
|
1078
|
-
PASS
|
|
1079
|
-
</div>
|
|
1080
|
-
</>
|
|
1081
|
-
);
|
|
1082
|
-
};
|
|
1083
|
-
|
|
1084
|
-
export const Integration: Story = {
|
|
1085
|
-
render: () => <IntegrationTestComponent />,
|
|
1086
|
-
play: async ({ canvasElement, step }) => {
|
|
1087
|
-
const canvas = within(canvasElement);
|
|
1088
|
-
const statusElement = canvas.getByLabelText('Status of the test run');
|
|
1089
|
-
statusElement.textContent = 'RUNS';
|
|
1090
|
-
|
|
1091
|
-
await step('Should integrate with external state management', async () => {
|
|
1092
|
-
// Verify initial state
|
|
1093
|
-
expect(canvas.getByTestId('selected-item')).toHaveTextContent('Selected: None');
|
|
1094
|
-
expect(canvas.getByTestId('popover-state')).toHaveTextContent('closed');
|
|
1095
|
-
|
|
1096
|
-
const trigger = canvas.getByTestId('popover-trigger');
|
|
1097
|
-
await userEvent.click(trigger);
|
|
1098
|
-
|
|
1099
|
-
await waitFor(() => {
|
|
1100
|
-
expect(canvas.getByTestId('popover-state')).toHaveTextContent('open');
|
|
1101
|
-
// Use document queries for portal-rendered content
|
|
1102
|
-
expect(document.querySelector('[data-testid="option-option-1"]')).toBeInTheDocument();
|
|
1103
|
-
expect(document.querySelector('[data-testid="option-option-2"]')).toBeInTheDocument();
|
|
1104
|
-
expect(document.querySelector('[data-testid="option-option-3"]')).toBeInTheDocument();
|
|
1105
|
-
});
|
|
1106
|
-
|
|
1107
|
-
// Click an option
|
|
1108
|
-
const option2 = document.querySelector('[data-testid="option-option-2"]') as HTMLElement;
|
|
1109
|
-
await userEvent.click(option2);
|
|
1110
|
-
|
|
1111
|
-
// Should update external state and close popover
|
|
1112
|
-
await waitFor(() => {
|
|
1113
|
-
expect(canvas.getByTestId('selected-item')).toHaveTextContent('Selected: Option 2');
|
|
1114
|
-
expect(canvas.getByTestId('popover-state')).toHaveTextContent('closed');
|
|
1115
|
-
});
|
|
1116
|
-
|
|
1117
|
-
// Wait for popover content to be removed from DOM
|
|
1118
|
-
await waitFor(
|
|
1119
|
-
() => {
|
|
1120
|
-
expect(document.querySelector('[data-testid="option-option-1"]')).not.toBeInTheDocument();
|
|
1121
|
-
},
|
|
1122
|
-
{ timeout: 2000 },
|
|
1123
|
-
);
|
|
1124
|
-
});
|
|
1125
|
-
|
|
1126
|
-
await step('Should maintain state across multiple interactions', async () => {
|
|
1127
|
-
// Open again and select different option
|
|
1128
|
-
const trigger = canvas.getByTestId('popover-trigger');
|
|
1129
|
-
await userEvent.click(trigger);
|
|
1130
|
-
|
|
1131
|
-
await waitFor(() => {
|
|
1132
|
-
expect(canvas.getByTestId('popover-state')).toHaveTextContent('open');
|
|
1133
|
-
});
|
|
1134
|
-
|
|
1135
|
-
const option3 = document.querySelector('[data-testid="option-option-3"]') as HTMLElement;
|
|
1136
|
-
await userEvent.click(option3);
|
|
1137
|
-
|
|
1138
|
-
await waitFor(() => {
|
|
1139
|
-
expect(canvas.getByTestId('selected-item')).toHaveTextContent('Selected: Option 3');
|
|
1140
|
-
expect(canvas.getByTestId('popover-state')).toHaveTextContent('closed');
|
|
1141
|
-
});
|
|
1142
|
-
|
|
1143
|
-
// Open again and select first option
|
|
1144
|
-
await userEvent.click(trigger);
|
|
1145
|
-
|
|
1146
|
-
await waitFor(() => {
|
|
1147
|
-
// Use document query for portal-rendered content
|
|
1148
|
-
expect(document.querySelector('[data-testid="option-option-1"]')).toBeInTheDocument();
|
|
1149
|
-
});
|
|
1150
|
-
|
|
1151
|
-
const option1 = document.querySelector('[data-testid="option-option-1"]') as HTMLElement;
|
|
1152
|
-
await userEvent.click(option1);
|
|
1153
|
-
|
|
1154
|
-
await waitFor(() => {
|
|
1155
|
-
expect(canvas.getByTestId('selected-item')).toHaveTextContent('Selected: Option 1');
|
|
1156
|
-
expect(canvas.getByTestId('popover-state')).toHaveTextContent('closed');
|
|
1157
|
-
});
|
|
1158
|
-
});
|
|
1159
|
-
|
|
1160
|
-
statusElement.textContent = 'PASS';
|
|
1161
|
-
},
|
|
1162
|
-
};
|