@shohojdhara/atomix 0.4.0 → 0.4.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/dist/atomix.css +9231 -9337
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +2 -2
- package/dist/atomix.min.css.map +1 -1
- package/dist/charts.js +4 -5
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +87 -10
- package/dist/core.js +673 -480
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +15 -3
- package/dist/forms.js +530 -97
- package/dist/forms.js.map +1 -1
- package/dist/heavy.js +5 -6
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +495 -254
- package/dist/index.esm.js +1269 -723
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1273 -723
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +2 -2
- package/scripts/atomix-cli.js +10 -1
- package/scripts/cli/__tests__/utils.test.js +6 -2
- package/scripts/cli/migration-tools.js +2 -2
- package/scripts/cli/theme-bridge.js +7 -9
- package/scripts/cli/utils.js +2 -1
- package/src/components/Accordion/Accordion.stories.tsx +40 -0
- package/src/components/Accordion/Accordion.tsx +174 -56
- package/src/components/Accordion/AccordionCompound.test.tsx +70 -0
- package/src/components/Breadcrumb/Breadcrumb.tsx +156 -50
- package/src/components/Breadcrumb/BreadcrumbCompound.test.tsx +84 -0
- package/src/components/Callout/Callout.stories.tsx +166 -1011
- package/src/components/Callout/Callout.tsx +196 -84
- package/src/components/Callout/CalloutCompound.test.tsx +72 -0
- package/src/components/Dropdown/Dropdown.tsx +133 -20
- package/src/components/Dropdown/DropdownCompound.test.tsx +64 -0
- package/src/components/EdgePanel/EdgePanel.tsx +164 -112
- package/src/components/EdgePanel/EdgePanelCompound.test.tsx +53 -0
- package/src/components/Form/Select.stories.tsx +23 -0
- package/src/components/Form/Select.test.tsx +99 -0
- package/src/components/Form/Select.tsx +144 -93
- package/src/components/Form/SelectOption.tsx +88 -0
- package/src/components/Hero/Hero.stories.tsx +37 -0
- package/src/components/Hero/Hero.test.tsx +142 -0
- package/src/components/Hero/Hero.tsx +142 -3
- package/src/components/List/List.test.tsx +62 -0
- package/src/components/List/List.tsx +16 -5
- package/src/components/List/ListItem.tsx +20 -0
- package/src/components/Modal/Modal.stories.tsx +65 -1
- package/src/components/Modal/Modal.tsx +115 -35
- package/src/components/Modal/ModalCompound.test.tsx +94 -0
- package/src/components/Steps/Steps.tsx +124 -21
- package/src/components/Steps/StepsCompound.test.tsx +81 -0
- package/src/components/Tabs/Tabs.tsx +197 -44
- package/src/components/Tabs/TabsCompound.test.tsx +64 -0
- package/src/lib/composables/index.ts +0 -4
- package/src/lib/composables/useAtomixGlass.ts +0 -15
- package/src/lib/theme/devtools/CLI.ts +2 -10
- package/src/lib/types/components.ts +8 -2
- package/src/lib/utils/__tests__/componentUtils.test.ts +57 -2
- package/src/lib/utils/__tests__/themeNaming.test.ts +117 -0
- package/src/lib/utils/themeNaming.ts +1 -1
- package/src/styles/02-tools/_tools.breakpoints.scss +1 -1
- package/src/styles/02-tools/_tools.utility-api.scss +6 -6
- package/src/styles/99-utilities/_utilities.text.scss +0 -1
|
@@ -1,10 +1,53 @@
|
|
|
1
|
-
import React, { useRef, useEffect } from 'react';
|
|
1
|
+
import React, { useRef, useEffect, memo, forwardRef } from 'react';
|
|
2
2
|
import { EdgePanelProps } from '../../lib/types/components';
|
|
3
3
|
import { useEdgePanel } from '../../lib/composables/useEdgePanel';
|
|
4
4
|
import { EDGE_PANEL } from '../../lib/constants/components';
|
|
5
5
|
import { Icon } from '../Icon/Icon';
|
|
6
6
|
import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
|
|
7
7
|
|
|
8
|
+
// Subcomponents
|
|
9
|
+
export const EdgePanelHeader = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
10
|
+
({ children, className = '', ...props }, ref) => (
|
|
11
|
+
<div ref={ref} className={`c-edge-panel__header ${className}`.trim()} {...props}>
|
|
12
|
+
{children}
|
|
13
|
+
</div>
|
|
14
|
+
)
|
|
15
|
+
);
|
|
16
|
+
EdgePanelHeader.displayName = 'EdgePanelHeader';
|
|
17
|
+
|
|
18
|
+
export const EdgePanelBody = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
19
|
+
({ children, className = '', ...props }, ref) => (
|
|
20
|
+
<div ref={ref} className={`c-edge-panel__body ${className}`.trim()} {...props}>
|
|
21
|
+
{children}
|
|
22
|
+
</div>
|
|
23
|
+
)
|
|
24
|
+
);
|
|
25
|
+
EdgePanelBody.displayName = 'EdgePanelBody';
|
|
26
|
+
|
|
27
|
+
export const EdgePanelFooter = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
28
|
+
({ children, className = '', ...props }, ref) => (
|
|
29
|
+
<div ref={ref} className={`c-edge-panel__footer ${className}`.trim()} {...props}>
|
|
30
|
+
{children}
|
|
31
|
+
</div>
|
|
32
|
+
)
|
|
33
|
+
);
|
|
34
|
+
EdgePanelFooter.displayName = 'EdgePanelFooter';
|
|
35
|
+
|
|
36
|
+
export const EdgePanelCloseButton = forwardRef<HTMLButtonElement, React.ButtonHTMLAttributes<HTMLButtonElement>>(
|
|
37
|
+
({ className = '', onClick, ...props }, ref) => (
|
|
38
|
+
<button
|
|
39
|
+
ref={ref}
|
|
40
|
+
className={`c-edge-panel__close c-btn c-btn--icon ${className}`.trim()}
|
|
41
|
+
onClick={onClick}
|
|
42
|
+
aria-label="Close panel"
|
|
43
|
+
{...props}
|
|
44
|
+
>
|
|
45
|
+
<Icon name="X" />
|
|
46
|
+
</button>
|
|
47
|
+
)
|
|
48
|
+
);
|
|
49
|
+
EdgePanelCloseButton.displayName = 'EdgePanelCloseButton';
|
|
50
|
+
|
|
8
51
|
/**
|
|
9
52
|
* EdgePanel - A sliding panel component that appears from any screen edge
|
|
10
53
|
*
|
|
@@ -21,129 +64,138 @@ import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
|
|
|
21
64
|
* <p>Panel content</p>
|
|
22
65
|
* </EdgePanel>
|
|
23
66
|
*
|
|
24
|
-
* //
|
|
25
|
-
* <EdgePanel
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* <p>Panel with glass morphism</p>
|
|
33
|
-
* </EdgePanel>
|
|
34
|
-
*
|
|
35
|
-
* // With custom glass configuration
|
|
36
|
-
* <EdgePanel
|
|
37
|
-
* title="Custom Glass"
|
|
38
|
-
* isOpen={isOpen}
|
|
39
|
-
* onOpenChange={setIsOpen}
|
|
40
|
-
* position="start"
|
|
41
|
-
* glass={{
|
|
42
|
-
* mode: 'shader',
|
|
43
|
-
* shaderVariant: 'liquidGlass',
|
|
44
|
-
* displacementScale: 70,
|
|
45
|
-
* blurAmount: 1.8,
|
|
46
|
-
* saturation: 170,
|
|
47
|
-
* }}
|
|
48
|
-
* >
|
|
49
|
-
* <p>Panel with custom glass effect</p>
|
|
67
|
+
* // Compound Usage
|
|
68
|
+
* <EdgePanel isOpen={isOpen} onOpenChange={setIsOpen}>
|
|
69
|
+
* <EdgePanel.Header>
|
|
70
|
+
* <h4>Title</h4>
|
|
71
|
+
* <EdgePanel.CloseButton onClick={() => setIsOpen(false)} />
|
|
72
|
+
* </EdgePanel.Header>
|
|
73
|
+
* <EdgePanel.Body>Content</EdgePanel.Body>
|
|
74
|
+
* <EdgePanel.Footer>Footer</EdgePanel.Footer>
|
|
50
75
|
* </EdgePanel>
|
|
51
76
|
* ```
|
|
52
77
|
*/
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const {
|
|
68
|
-
isOpen: isOpenState,
|
|
69
|
-
containerRef,
|
|
70
|
-
backdropRef,
|
|
71
|
-
generateEdgePanelClass,
|
|
72
|
-
closePanel,
|
|
73
|
-
handleBackdropClick,
|
|
74
|
-
} = useEdgePanel({
|
|
75
|
-
position,
|
|
76
|
-
mode,
|
|
77
|
-
isOpen,
|
|
78
|
+
type EdgePanelComponent = React.FC<EdgePanelProps> & {
|
|
79
|
+
Header: typeof EdgePanelHeader;
|
|
80
|
+
Body: typeof EdgePanelBody;
|
|
81
|
+
Footer: typeof EdgePanelFooter;
|
|
82
|
+
CloseButton: typeof EdgePanelCloseButton;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const EdgePanel: EdgePanelComponent = memo(
|
|
86
|
+
({
|
|
87
|
+
title,
|
|
88
|
+
children,
|
|
89
|
+
position = 'start',
|
|
90
|
+
mode = 'slide',
|
|
91
|
+
isOpen = false,
|
|
78
92
|
onOpenChange,
|
|
79
|
-
backdrop,
|
|
80
|
-
closeOnBackdropClick,
|
|
81
|
-
closeOnEscape,
|
|
93
|
+
backdrop = true,
|
|
94
|
+
closeOnBackdropClick = true,
|
|
95
|
+
closeOnEscape = true,
|
|
96
|
+
className = '',
|
|
97
|
+
style,
|
|
82
98
|
glass,
|
|
83
|
-
})
|
|
99
|
+
}: EdgePanelProps) => {
|
|
100
|
+
const {
|
|
101
|
+
isOpen: isOpenState,
|
|
102
|
+
containerRef,
|
|
103
|
+
backdropRef,
|
|
104
|
+
generateEdgePanelClass,
|
|
105
|
+
closePanel,
|
|
106
|
+
handleBackdropClick,
|
|
107
|
+
} = useEdgePanel({
|
|
108
|
+
position,
|
|
109
|
+
mode,
|
|
110
|
+
isOpen,
|
|
111
|
+
onOpenChange,
|
|
112
|
+
backdrop,
|
|
113
|
+
closeOnBackdropClick,
|
|
114
|
+
closeOnEscape,
|
|
115
|
+
glass,
|
|
116
|
+
});
|
|
84
117
|
|
|
85
|
-
|
|
86
|
-
|
|
118
|
+
// Moved useRef outside of conditional rendering to fix hook order issue
|
|
119
|
+
const glassContentRef = useRef<HTMLDivElement>(null);
|
|
87
120
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
121
|
+
const panelClass = generateEdgePanelClass({
|
|
122
|
+
position,
|
|
123
|
+
isOpen,
|
|
124
|
+
className: glass ? `${className} c-edge-panel--glass` : className,
|
|
125
|
+
});
|
|
93
126
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return
|
|
97
|
-
|
|
127
|
+
// If not open and not controlled by parent, don't render
|
|
128
|
+
// Note: useEdgePanel manages internal state if onOpenChange is not provided?
|
|
129
|
+
// Looking at useEdgePanel (implied): it seems to return isOpenState.
|
|
130
|
+
// If we return null here, animations might be cut off.
|
|
131
|
+
// Usually EdgePanel/Drawer should stay mounted but hidden or conditionally mounted.
|
|
132
|
+
// The original code returned null if !isOpenState && isOpen === false.
|
|
133
|
+
// Let's keep that logic.
|
|
134
|
+
if (!isOpenState && isOpen === false) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
98
137
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
>
|
|
134
|
-
{panelContent}
|
|
135
|
-
</div>
|
|
136
|
-
</AtomixGlass>
|
|
137
|
-
) : (
|
|
138
|
-
panelContent
|
|
138
|
+
const defaultGlassProps = {
|
|
139
|
+
elasticity: 0,
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
|
|
143
|
+
|
|
144
|
+
// Check for compound components
|
|
145
|
+
const hasCompoundComponents = React.Children.toArray(children).some((child) =>
|
|
146
|
+
React.isValidElement(child) &&
|
|
147
|
+
['EdgePanelHeader', 'EdgePanelBody', 'EdgePanelFooter'].includes((child.type as any).displayName)
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const panelContent = hasCompoundComponents ? (
|
|
151
|
+
children
|
|
152
|
+
) : (
|
|
153
|
+
<>
|
|
154
|
+
<div className="c-edge-panel__header">
|
|
155
|
+
<h4>{title}</h4>
|
|
156
|
+
<button
|
|
157
|
+
className="c-edge-panel__close c-btn c-btn--icon"
|
|
158
|
+
onClick={() => closePanel()}
|
|
159
|
+
aria-label="Close panel"
|
|
160
|
+
>
|
|
161
|
+
<Icon name="X" />
|
|
162
|
+
</button>
|
|
163
|
+
</div>
|
|
164
|
+
<div className="c-edge-panel__body">{children}</div>
|
|
165
|
+
</>
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<div className={panelClass} data-position={position} data-mode={mode} style={style}>
|
|
170
|
+
{backdrop && (
|
|
171
|
+
<div ref={backdropRef} className="c-edge-panel__backdrop" onClick={handleBackdropClick} />
|
|
139
172
|
)}
|
|
173
|
+
<div ref={containerRef} className="c-edge-panel__container">
|
|
174
|
+
{glass ? (
|
|
175
|
+
<AtomixGlass {...glassProps}>
|
|
176
|
+
<div
|
|
177
|
+
ref={glassContentRef}
|
|
178
|
+
className="c-edge-panel__glass-content"
|
|
179
|
+
style={{ borderRadius: containerRef.current?.style.borderRadius }}
|
|
180
|
+
>
|
|
181
|
+
{panelContent}
|
|
182
|
+
</div>
|
|
183
|
+
</AtomixGlass>
|
|
184
|
+
) : (
|
|
185
|
+
panelContent
|
|
186
|
+
)}
|
|
187
|
+
</div>
|
|
140
188
|
</div>
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
export type { EdgePanelProps };
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
) as unknown as EdgePanelComponent;
|
|
146
192
|
|
|
147
193
|
EdgePanel.displayName = 'EdgePanel';
|
|
194
|
+
EdgePanel.Header = EdgePanelHeader;
|
|
195
|
+
EdgePanel.Body = EdgePanelBody;
|
|
196
|
+
EdgePanel.Footer = EdgePanelFooter;
|
|
197
|
+
EdgePanel.CloseButton = EdgePanelCloseButton;
|
|
198
|
+
|
|
199
|
+
export type { EdgePanelProps };
|
|
148
200
|
|
|
149
201
|
export default EdgePanel;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
2
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
3
|
+
import { EdgePanel } from './EdgePanel';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
|
|
6
|
+
describe('EdgePanel Component', () => {
|
|
7
|
+
it('renders correctly with legacy props', () => {
|
|
8
|
+
render(
|
|
9
|
+
<EdgePanel isOpen={true} title="Legacy Title">
|
|
10
|
+
Legacy Content
|
|
11
|
+
</EdgePanel>
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
expect(screen.getByText('Legacy Title')).toBeInTheDocument();
|
|
15
|
+
expect(screen.getByText('Legacy Content')).toBeInTheDocument();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('renders correctly with compound components', () => {
|
|
19
|
+
render(
|
|
20
|
+
<EdgePanel isOpen={true}>
|
|
21
|
+
<EdgePanel.Header>
|
|
22
|
+
<h4>Compound Title</h4>
|
|
23
|
+
</EdgePanel.Header>
|
|
24
|
+
<EdgePanel.Body>
|
|
25
|
+
Compound Content
|
|
26
|
+
</EdgePanel.Body>
|
|
27
|
+
<EdgePanel.Footer>
|
|
28
|
+
Footer
|
|
29
|
+
</EdgePanel.Footer>
|
|
30
|
+
</EdgePanel>
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
expect(screen.getByText('Compound Title')).toBeInTheDocument();
|
|
34
|
+
expect(screen.getByText('Compound Content')).toBeInTheDocument();
|
|
35
|
+
expect(screen.getByText('Footer')).toBeInTheDocument();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('uses close button in compound mode', () => {
|
|
39
|
+
const onClose = vi.fn();
|
|
40
|
+
render(
|
|
41
|
+
<EdgePanel isOpen={true} onOpenChange={onClose}>
|
|
42
|
+
<EdgePanel.Header>
|
|
43
|
+
<EdgePanel.CloseButton onClick={() => onClose(false)} />
|
|
44
|
+
</EdgePanel.Header>
|
|
45
|
+
<EdgePanel.Body>Content</EdgePanel.Body>
|
|
46
|
+
</EdgePanel>
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const closeBtn = screen.getByLabelText('Close panel');
|
|
50
|
+
fireEvent.click(closeBtn);
|
|
51
|
+
expect(onClose).toHaveBeenCalledWith(false);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -256,6 +256,29 @@ export const Sizes: Story = {
|
|
|
256
256
|
),
|
|
257
257
|
};
|
|
258
258
|
|
|
259
|
+
// Compound usage
|
|
260
|
+
export const Compound: Story = {
|
|
261
|
+
render: () => (
|
|
262
|
+
<div style={{ width: '300px' }}>
|
|
263
|
+
<Select placeholder="Select a framework">
|
|
264
|
+
<Select.Option value="react">React</Select.Option>
|
|
265
|
+
<Select.Option value="vue">Vue</Select.Option>
|
|
266
|
+
<Select.Option value="angular">Angular</Select.Option>
|
|
267
|
+
<Select.Option value="svelte" disabled>
|
|
268
|
+
Svelte (Disabled)
|
|
269
|
+
</Select.Option>
|
|
270
|
+
</Select>
|
|
271
|
+
</div>
|
|
272
|
+
),
|
|
273
|
+
parameters: {
|
|
274
|
+
docs: {
|
|
275
|
+
description: {
|
|
276
|
+
story: 'Select component using Compound Component Pattern.',
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
|
|
259
282
|
// Select states
|
|
260
283
|
export const States: Story = {
|
|
261
284
|
args: {
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
3
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
4
|
+
import { Select } from './Select';
|
|
5
|
+
|
|
6
|
+
describe('Select Component', () => {
|
|
7
|
+
it('renders legacy options correctly', () => {
|
|
8
|
+
const options = [
|
|
9
|
+
{ value: '1', label: 'Option 1' },
|
|
10
|
+
{ value: '2', label: 'Option 2' },
|
|
11
|
+
];
|
|
12
|
+
render(<Select options={options} value="" onChange={() => {}} />);
|
|
13
|
+
|
|
14
|
+
// Check custom UI items
|
|
15
|
+
const items = document.querySelectorAll('.c-select__item');
|
|
16
|
+
expect(items).toHaveLength(2);
|
|
17
|
+
expect(items[0]).toHaveTextContent('Option 1');
|
|
18
|
+
expect(items[1]).toHaveTextContent('Option 2');
|
|
19
|
+
|
|
20
|
+
// Check native select options
|
|
21
|
+
const select = document.querySelector('select');
|
|
22
|
+
expect(select).not.toBeNull();
|
|
23
|
+
expect(select?.options).toHaveLength(3); // Placeholder + 2
|
|
24
|
+
expect(select?.options[1].value).toBe('1');
|
|
25
|
+
expect(select?.options[2].value).toBe('2');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('renders compound options correctly', async () => {
|
|
29
|
+
render(
|
|
30
|
+
<Select value="" onChange={() => {}}>
|
|
31
|
+
<Select.Option value="1">Compound Option 1</Select.Option>
|
|
32
|
+
<Select.Option value="2">Compound Option 2</Select.Option>
|
|
33
|
+
</Select>
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// Check custom UI items
|
|
37
|
+
const items = document.querySelectorAll('.c-select__item');
|
|
38
|
+
expect(items).toHaveLength(2);
|
|
39
|
+
expect(items[0]).toHaveTextContent('Compound Option 1');
|
|
40
|
+
expect(items[1]).toHaveTextContent('Compound Option 2');
|
|
41
|
+
|
|
42
|
+
// Check native select options
|
|
43
|
+
await waitFor(() => {
|
|
44
|
+
const select = document.querySelector('select');
|
|
45
|
+
expect(select).not.toBeNull();
|
|
46
|
+
expect(select?.options).toHaveLength(3); // Placeholder + 2
|
|
47
|
+
expect(select?.options[1].value).toBe('1');
|
|
48
|
+
expect(select?.options[2].value).toBe('2');
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('handles selection in legacy mode', () => {
|
|
53
|
+
const handleChange = vi.fn();
|
|
54
|
+
const options = [
|
|
55
|
+
{ value: '1', label: 'Option 1' },
|
|
56
|
+
{ value: '2', label: 'Option 2' },
|
|
57
|
+
];
|
|
58
|
+
render(<Select options={options} value="" onChange={handleChange} />);
|
|
59
|
+
|
|
60
|
+
// Open dropdown
|
|
61
|
+
const trigger = document.querySelector('.c-select__selected');
|
|
62
|
+
fireEvent.click(trigger!);
|
|
63
|
+
|
|
64
|
+
// Click item
|
|
65
|
+
const item = document.querySelector('.c-select__item[data-value="1"]');
|
|
66
|
+
fireEvent.click(item!);
|
|
67
|
+
|
|
68
|
+
expect(handleChange).toHaveBeenCalled();
|
|
69
|
+
// Check event value
|
|
70
|
+
expect(handleChange.mock.calls[0][0].target.value).toBe('1');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('handles selection in compound mode', async () => {
|
|
74
|
+
const handleChange = vi.fn();
|
|
75
|
+
render(
|
|
76
|
+
<Select value="" onChange={handleChange}>
|
|
77
|
+
<Select.Option value="1">Option 1</Select.Option>
|
|
78
|
+
<Select.Option value="2">Option 2</Select.Option>
|
|
79
|
+
</Select>
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Wait for options to be registered
|
|
83
|
+
await waitFor(() => {
|
|
84
|
+
const select = document.querySelector('select');
|
|
85
|
+
expect(select?.options).toHaveLength(3);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Open dropdown
|
|
89
|
+
const trigger = document.querySelector('.c-select__selected');
|
|
90
|
+
fireEvent.click(trigger!);
|
|
91
|
+
|
|
92
|
+
// Click item
|
|
93
|
+
const item = document.querySelector('.c-select__item[data-value="2"]');
|
|
94
|
+
fireEvent.click(item!);
|
|
95
|
+
|
|
96
|
+
expect(handleChange).toHaveBeenCalled();
|
|
97
|
+
expect(handleChange.mock.calls[0][0].target.value).toBe('2');
|
|
98
|
+
});
|
|
99
|
+
});
|