@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.
Files changed (66) hide show
  1. package/dist/atomix.css +9231 -9337
  2. package/dist/atomix.css.map +1 -1
  3. package/dist/atomix.min.css +2 -2
  4. package/dist/atomix.min.css.map +1 -1
  5. package/dist/charts.js +4 -5
  6. package/dist/charts.js.map +1 -1
  7. package/dist/core.d.ts +87 -10
  8. package/dist/core.js +673 -480
  9. package/dist/core.js.map +1 -1
  10. package/dist/forms.d.ts +15 -3
  11. package/dist/forms.js +530 -97
  12. package/dist/forms.js.map +1 -1
  13. package/dist/heavy.js +5 -6
  14. package/dist/heavy.js.map +1 -1
  15. package/dist/index.d.ts +495 -254
  16. package/dist/index.esm.js +1269 -723
  17. package/dist/index.esm.js.map +1 -1
  18. package/dist/index.js +1273 -723
  19. package/dist/index.js.map +1 -1
  20. package/dist/index.min.js +1 -1
  21. package/dist/index.min.js.map +1 -1
  22. package/package.json +2 -2
  23. package/scripts/atomix-cli.js +10 -1
  24. package/scripts/cli/__tests__/utils.test.js +6 -2
  25. package/scripts/cli/migration-tools.js +2 -2
  26. package/scripts/cli/theme-bridge.js +7 -9
  27. package/scripts/cli/utils.js +2 -1
  28. package/src/components/Accordion/Accordion.stories.tsx +40 -0
  29. package/src/components/Accordion/Accordion.tsx +174 -56
  30. package/src/components/Accordion/AccordionCompound.test.tsx +70 -0
  31. package/src/components/Breadcrumb/Breadcrumb.tsx +156 -50
  32. package/src/components/Breadcrumb/BreadcrumbCompound.test.tsx +84 -0
  33. package/src/components/Callout/Callout.stories.tsx +166 -1011
  34. package/src/components/Callout/Callout.tsx +196 -84
  35. package/src/components/Callout/CalloutCompound.test.tsx +72 -0
  36. package/src/components/Dropdown/Dropdown.tsx +133 -20
  37. package/src/components/Dropdown/DropdownCompound.test.tsx +64 -0
  38. package/src/components/EdgePanel/EdgePanel.tsx +164 -112
  39. package/src/components/EdgePanel/EdgePanelCompound.test.tsx +53 -0
  40. package/src/components/Form/Select.stories.tsx +23 -0
  41. package/src/components/Form/Select.test.tsx +99 -0
  42. package/src/components/Form/Select.tsx +144 -93
  43. package/src/components/Form/SelectOption.tsx +88 -0
  44. package/src/components/Hero/Hero.stories.tsx +37 -0
  45. package/src/components/Hero/Hero.test.tsx +142 -0
  46. package/src/components/Hero/Hero.tsx +142 -3
  47. package/src/components/List/List.test.tsx +62 -0
  48. package/src/components/List/List.tsx +16 -5
  49. package/src/components/List/ListItem.tsx +20 -0
  50. package/src/components/Modal/Modal.stories.tsx +65 -1
  51. package/src/components/Modal/Modal.tsx +115 -35
  52. package/src/components/Modal/ModalCompound.test.tsx +94 -0
  53. package/src/components/Steps/Steps.tsx +124 -21
  54. package/src/components/Steps/StepsCompound.test.tsx +81 -0
  55. package/src/components/Tabs/Tabs.tsx +197 -44
  56. package/src/components/Tabs/TabsCompound.test.tsx +64 -0
  57. package/src/lib/composables/index.ts +0 -4
  58. package/src/lib/composables/useAtomixGlass.ts +0 -15
  59. package/src/lib/theme/devtools/CLI.ts +2 -10
  60. package/src/lib/types/components.ts +8 -2
  61. package/src/lib/utils/__tests__/componentUtils.test.ts +57 -2
  62. package/src/lib/utils/__tests__/themeNaming.test.ts +117 -0
  63. package/src/lib/utils/themeNaming.ts +1 -1
  64. package/src/styles/02-tools/_tools.breakpoints.scss +1 -1
  65. package/src/styles/02-tools/_tools.utility-api.scss +6 -6
  66. 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
- export interface BreadcrumbItem {
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: BreadcrumbItem[];
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
- export const Breadcrumb: React.FC<BreadcrumbProps> = memo(
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
- {items.map((item, index) => {
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
+ });