@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,7 +1,8 @@
|
|
|
1
|
-
import React, { ReactNode, memo } from 'react';
|
|
1
|
+
import React, { ReactNode, memo, forwardRef, Children, cloneElement, isValidElement } from 'react';
|
|
2
2
|
import { BREADCRUMB } from '../../lib/constants/components';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// Legacy Item Interface
|
|
5
|
+
export interface BreadcrumbItemData {
|
|
5
6
|
/**
|
|
6
7
|
* Text to display
|
|
7
8
|
*/
|
|
@@ -38,11 +39,108 @@ export interface BreadcrumbItem {
|
|
|
38
39
|
className?: string;
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
// Export legacy interface as type alias to preserve backward compatibility for type imports
|
|
43
|
+
export type { BreadcrumbItemData as BreadcrumbItem };
|
|
44
|
+
|
|
45
|
+
// Compound Component Props
|
|
46
|
+
export interface BreadcrumbItemProps extends React.HTMLAttributes<HTMLLIElement> {
|
|
47
|
+
/**
|
|
48
|
+
* URL for the breadcrumb item
|
|
49
|
+
*/
|
|
50
|
+
href?: string;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Whether this item is active (current page)
|
|
54
|
+
*/
|
|
55
|
+
active?: boolean;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Optional icon to display before the label
|
|
59
|
+
*/
|
|
60
|
+
icon?: ReactNode;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Optional click handler for the link
|
|
64
|
+
*/
|
|
65
|
+
onClick?: (event: React.MouseEvent<HTMLAnchorElement | HTMLSpanElement>) => void;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Optional custom link component
|
|
69
|
+
*/
|
|
70
|
+
linkAs?: React.ElementType;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Link props to pass to the underlying anchor or LinkComponent
|
|
74
|
+
*/
|
|
75
|
+
linkProps?: Record<string, any>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const BreadcrumbItem = forwardRef<HTMLLIElement, BreadcrumbItemProps>(
|
|
79
|
+
(
|
|
80
|
+
{
|
|
81
|
+
children,
|
|
82
|
+
href,
|
|
83
|
+
active,
|
|
84
|
+
icon,
|
|
85
|
+
onClick,
|
|
86
|
+
className = '',
|
|
87
|
+
style,
|
|
88
|
+
linkAs: LinkComponent,
|
|
89
|
+
linkProps = {},
|
|
90
|
+
...props
|
|
91
|
+
},
|
|
92
|
+
ref
|
|
93
|
+
) => {
|
|
94
|
+
const itemClasses = [BREADCRUMB.CLASSES.ITEM, active ? BREADCRUMB.CLASSES.ACTIVE : '', className]
|
|
95
|
+
.filter(Boolean)
|
|
96
|
+
.join(' ');
|
|
97
|
+
|
|
98
|
+
const linkContent = (
|
|
99
|
+
<>
|
|
100
|
+
{icon && <span className="c-breadcrumb__icon">{icon}</span>}
|
|
101
|
+
{children}
|
|
102
|
+
</>
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const commonLinkProps = {
|
|
106
|
+
className: BREADCRUMB.CLASSES.LINK,
|
|
107
|
+
onClick: onClick as any,
|
|
108
|
+
style, // Apply style to the link as per legacy behavior
|
|
109
|
+
...linkProps,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<li ref={ref} className={itemClasses} style={style} {...props}>
|
|
114
|
+
{href && !active ? (
|
|
115
|
+
LinkComponent ? (
|
|
116
|
+
(() => {
|
|
117
|
+
const Component = LinkComponent;
|
|
118
|
+
return (
|
|
119
|
+
<Component href={href} {...commonLinkProps}>
|
|
120
|
+
{linkContent}
|
|
121
|
+
</Component>
|
|
122
|
+
);
|
|
123
|
+
})()
|
|
124
|
+
) : (
|
|
125
|
+
<a href={href} {...(commonLinkProps as React.HTMLAttributes<HTMLAnchorElement>)}>
|
|
126
|
+
{linkContent}
|
|
127
|
+
</a>
|
|
128
|
+
)
|
|
129
|
+
) : (
|
|
130
|
+
<span className={BREADCRUMB.CLASSES.LINK}>{linkContent}</span>
|
|
131
|
+
)}
|
|
132
|
+
</li>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
BreadcrumbItem.displayName = 'BreadcrumbItem';
|
|
138
|
+
|
|
41
139
|
export interface BreadcrumbProps {
|
|
42
140
|
/**
|
|
43
|
-
* Array of breadcrumb items
|
|
141
|
+
* Array of breadcrumb items (Legacy)
|
|
44
142
|
*/
|
|
45
|
-
items
|
|
143
|
+
items?: BreadcrumbItemData[];
|
|
46
144
|
|
|
47
145
|
/**
|
|
48
146
|
* Custom divider character or element
|
|
@@ -68,8 +166,18 @@ export interface BreadcrumbProps {
|
|
|
68
166
|
* Custom style for the breadcrumb
|
|
69
167
|
*/
|
|
70
168
|
style?: React.CSSProperties;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Children (Compound)
|
|
172
|
+
*/
|
|
173
|
+
children?: ReactNode;
|
|
71
174
|
}
|
|
72
|
-
|
|
175
|
+
|
|
176
|
+
type BreadcrumbComponent = React.FC<BreadcrumbProps> & {
|
|
177
|
+
Item: typeof BreadcrumbItem;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export const Breadcrumb: BreadcrumbComponent = memo(
|
|
73
181
|
({
|
|
74
182
|
items,
|
|
75
183
|
divider,
|
|
@@ -77,62 +185,60 @@ export const Breadcrumb: React.FC<BreadcrumbProps> = memo(
|
|
|
77
185
|
'aria-label': ariaLabel = 'Breadcrumb',
|
|
78
186
|
LinkComponent,
|
|
79
187
|
style,
|
|
188
|
+
children,
|
|
80
189
|
}) => {
|
|
81
190
|
const breadcrumbClasses = [BREADCRUMB.CLASSES.BASE, className].filter(Boolean).join(' ');
|
|
82
191
|
|
|
192
|
+
let content: ReactNode;
|
|
193
|
+
|
|
194
|
+
if (items && items.length > 0) {
|
|
195
|
+
// Legacy rendering
|
|
196
|
+
content = items.map((item, index) => {
|
|
197
|
+
const isLast = index === items.length - 1;
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<BreadcrumbItem
|
|
201
|
+
key={index}
|
|
202
|
+
href={item.href}
|
|
203
|
+
active={item.active || isLast}
|
|
204
|
+
icon={item.icon}
|
|
205
|
+
onClick={item.onClick}
|
|
206
|
+
className={item.className}
|
|
207
|
+
style={item.style}
|
|
208
|
+
linkAs={LinkComponent}
|
|
209
|
+
>
|
|
210
|
+
{item.label}
|
|
211
|
+
</BreadcrumbItem>
|
|
212
|
+
);
|
|
213
|
+
});
|
|
214
|
+
} else {
|
|
215
|
+
// Compound rendering
|
|
216
|
+
const childrenCount = Children.count(children);
|
|
217
|
+
content = Children.map(children, (child, index) => {
|
|
218
|
+
if (isValidElement(child)) {
|
|
219
|
+
const isLast = index === childrenCount - 1;
|
|
220
|
+
const childProps = child.props as any;
|
|
221
|
+
|
|
222
|
+
return cloneElement(child, {
|
|
223
|
+
active: childProps.active ?? (isLast ? true : undefined),
|
|
224
|
+
linkAs: childProps.linkAs ?? LinkComponent,
|
|
225
|
+
} as any);
|
|
226
|
+
}
|
|
227
|
+
return child;
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
83
231
|
return (
|
|
84
232
|
<nav aria-label={ariaLabel} style={style}>
|
|
85
233
|
<ol className={breadcrumbClasses}>
|
|
86
|
-
{
|
|
87
|
-
const isLast = index === items.length - 1;
|
|
88
|
-
const itemClasses = [
|
|
89
|
-
BREADCRUMB.CLASSES.ITEM,
|
|
90
|
-
item.active || isLast ? BREADCRUMB.CLASSES.ACTIVE : '',
|
|
91
|
-
]
|
|
92
|
-
.filter(Boolean)
|
|
93
|
-
.join(' ');
|
|
94
|
-
|
|
95
|
-
const linkContent = (
|
|
96
|
-
<>
|
|
97
|
-
{item.icon && <span className="c-breadcrumb__icon">{item.icon}</span>}
|
|
98
|
-
{item.label}
|
|
99
|
-
</>
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
const linkProps = {
|
|
103
|
-
href: item.href,
|
|
104
|
-
className: BREADCRUMB.CLASSES.LINK,
|
|
105
|
-
onClick: item.onClick,
|
|
106
|
-
style: item.style,
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
return (
|
|
110
|
-
<li key={index} className={itemClasses} style={item.style}>
|
|
111
|
-
{item.href && !item.active ? (
|
|
112
|
-
LinkComponent ? (
|
|
113
|
-
(() => {
|
|
114
|
-
const Component = LinkComponent as React.ComponentType<any>;
|
|
115
|
-
return (
|
|
116
|
-
<Component {...(linkProps as React.ComponentProps<React.ElementType>)}>
|
|
117
|
-
{linkContent}
|
|
118
|
-
</Component>
|
|
119
|
-
);
|
|
120
|
-
})()
|
|
121
|
-
) : (
|
|
122
|
-
<a {...(linkProps as React.ComponentProps<'a'>)}>{linkContent}</a>
|
|
123
|
-
)
|
|
124
|
-
) : (
|
|
125
|
-
<span className={BREADCRUMB.CLASSES.LINK}>{linkContent}</span>
|
|
126
|
-
)}
|
|
127
|
-
</li>
|
|
128
|
-
);
|
|
129
|
-
})}
|
|
234
|
+
{content}
|
|
130
235
|
</ol>
|
|
131
236
|
</nav>
|
|
132
237
|
);
|
|
133
238
|
}
|
|
134
|
-
);
|
|
239
|
+
) as unknown as BreadcrumbComponent;
|
|
135
240
|
|
|
136
241
|
Breadcrumb.displayName = 'Breadcrumb';
|
|
242
|
+
Breadcrumb.Item = BreadcrumbItem;
|
|
137
243
|
|
|
138
244
|
export default Breadcrumb;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
2
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
3
|
+
import { Breadcrumb } from './Breadcrumb';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
|
|
6
|
+
describe('Breadcrumb Component', () => {
|
|
7
|
+
it('renders correctly with legacy items prop', () => {
|
|
8
|
+
const items = [
|
|
9
|
+
{ label: 'Home', href: '/' },
|
|
10
|
+
{ label: 'Products', href: '/products' },
|
|
11
|
+
{ label: 'Current', active: true },
|
|
12
|
+
];
|
|
13
|
+
render(<Breadcrumb items={items} />);
|
|
14
|
+
|
|
15
|
+
expect(screen.getByText('Home').closest('a')).toHaveAttribute('href', '/');
|
|
16
|
+
expect(screen.getByText('Products').closest('a')).toHaveAttribute('href', '/products');
|
|
17
|
+
expect(screen.getByText('Current').closest('span')).toBeInTheDocument();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('renders correctly with compound components', () => {
|
|
21
|
+
render(
|
|
22
|
+
<Breadcrumb>
|
|
23
|
+
<Breadcrumb.Item href="/">Home</Breadcrumb.Item>
|
|
24
|
+
<Breadcrumb.Item href="/products">Products</Breadcrumb.Item>
|
|
25
|
+
<Breadcrumb.Item active>Current</Breadcrumb.Item>
|
|
26
|
+
</Breadcrumb>
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
expect(screen.getByText('Home').closest('a')).toHaveAttribute('href', '/');
|
|
30
|
+
expect(screen.getByText('Products').closest('a')).toHaveAttribute('href', '/products');
|
|
31
|
+
expect(screen.getByText('Current').closest('span')).toBeInTheDocument();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('handles click events in compound components', () => {
|
|
35
|
+
const handleClick = vi.fn();
|
|
36
|
+
render(
|
|
37
|
+
<Breadcrumb>
|
|
38
|
+
<Breadcrumb.Item href="#" onClick={handleClick} active={false}>
|
|
39
|
+
Click Me
|
|
40
|
+
</Breadcrumb.Item>
|
|
41
|
+
<Breadcrumb.Item>Current</Breadcrumb.Item>
|
|
42
|
+
</Breadcrumb>
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
fireEvent.click(screen.getByText('Click Me'));
|
|
46
|
+
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('supports custom LinkComponent in compound components', () => {
|
|
50
|
+
const CustomLink = ({ href, children, ...props }: any) => (
|
|
51
|
+
<a href={href} data-testid="custom-link" {...props}>
|
|
52
|
+
{children} (Custom)
|
|
53
|
+
</a>
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
render(
|
|
57
|
+
<Breadcrumb LinkComponent={CustomLink}>
|
|
58
|
+
<Breadcrumb.Item href="/custom">Link</Breadcrumb.Item>
|
|
59
|
+
<Breadcrumb.Item>Current</Breadcrumb.Item>
|
|
60
|
+
</Breadcrumb>
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const link = screen.getByTestId('custom-link');
|
|
64
|
+
expect(link).toHaveAttribute('href', '/custom');
|
|
65
|
+
expect(link).toHaveTextContent('Link (Custom)');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('supports explicit linkAs prop on Item', () => {
|
|
69
|
+
const CustomLink = ({ href, children, ...props }: any) => (
|
|
70
|
+
<a href={href} data-testid="item-custom-link" {...props}>
|
|
71
|
+
{children}
|
|
72
|
+
</a>
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
render(
|
|
76
|
+
<Breadcrumb>
|
|
77
|
+
<Breadcrumb.Item href="/explicit" linkAs={CustomLink} active={false}>Explicit</Breadcrumb.Item>
|
|
78
|
+
<Breadcrumb.Item>Current</Breadcrumb.Item>
|
|
79
|
+
</Breadcrumb>
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
expect(screen.getByTestId('item-custom-link')).toHaveAttribute('href', '/explicit');
|
|
83
|
+
});
|
|
84
|
+
});
|