@shohojdhara/atomix 0.3.15 → 0.4.0
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/build-tools/index.d.ts +31 -30
- package/build-tools/package.json +4 -21
- package/dist/atomix.css +20924 -2611
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +76 -2
- package/dist/atomix.min.css.map +1 -1
- package/dist/build-tools/index.d.ts +31 -30
- package/dist/build-tools/package.json +4 -21
- package/dist/charts.js.map +1 -1
- package/dist/core.js.map +1 -1
- package/dist/forms.js.map +1 -1
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +144 -18
- package/dist/index.esm.js +110 -55
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +110 -55
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/layout.js.map +1 -1
- package/dist/theme.d.ts +9 -9
- package/dist/theme.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Accordion/Accordion.stories.tsx +32 -23
- package/src/components/Accordion/Accordion.test.tsx +70 -50
- package/src/components/Accordion/Accordion.tsx +99 -94
- package/src/components/AtomixGlass/AtomixGlass.test.tsx +1 -1
- package/src/components/AtomixGlass/GlassFilter.tsx +9 -16
- package/src/components/AtomixGlass/glass-utils.ts +4 -3
- package/src/components/AtomixGlass/shader-utils.ts +128 -52
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +1 -1
- package/src/components/AtomixGlass/stories/Shaders.stories.tsx +1 -1
- package/src/components/Avatar/Avatar.stories.tsx +45 -62
- package/src/components/Avatar/Avatar.tsx +58 -56
- package/src/components/Badge/Badge.stories.tsx +20 -9
- package/src/components/Badge/Badge.test.tsx +41 -41
- package/src/components/Badge/Badge.tsx +64 -62
- package/src/components/Block/Block.stories.tsx +14 -4
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +9 -8
- package/src/components/Breadcrumb/Breadcrumb.tsx +62 -60
- package/src/components/Button/Button.stories.tsx +13 -22
- package/src/components/Button/Button.test.tsx +97 -81
- package/src/components/Button/Button.tsx +46 -14
- package/src/components/Button/ButtonGroup.stories.tsx +37 -32
- package/src/components/Button/ButtonGroup.tsx +4 -15
- package/src/components/Callout/Callout.stories.tsx +109 -16
- package/src/components/Card/Card.stories.tsx +67 -36
- package/src/components/Card/Card.tsx +30 -14
- package/src/components/Chart/AreaChart.tsx +1 -1
- package/src/components/Chart/CandlestickChart.tsx +23 -16
- package/src/components/Chart/Chart.stories.tsx +4 -9
- package/src/components/Chart/Chart.tsx +40 -44
- package/src/components/Chart/ChartRenderer.tsx +39 -12
- package/src/components/Chart/ChartToolbar.tsx +21 -5
- package/src/components/Chart/DonutChart.tsx +1 -1
- package/src/components/Chart/FunnelChart.tsx +4 -1
- package/src/components/Chart/GaugeChart.tsx +3 -1
- package/src/components/Chart/HeatmapChart.tsx +50 -37
- package/src/components/Chart/LineChart.tsx +3 -2
- package/src/components/Chart/MultiAxisChart.tsx +24 -16
- package/src/components/Chart/RadarChart.tsx +19 -17
- package/src/components/Chart/ScatterChart.tsx +29 -21
- package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +6 -2
- package/src/components/ColorModeToggle/ColorModeToggle.tsx +15 -3
- package/src/components/Countdown/Countdown.stories.tsx +7 -7
- package/src/components/DataTable/DataTable.stories.tsx +43 -38
- package/src/components/DataTable/DataTable.test.tsx +26 -148
- package/src/components/DataTable/DataTable.tsx +485 -456
- package/src/components/DatePicker/DatePicker.stories.tsx +32 -47
- package/src/components/DatePicker/DatePicker.tsx +31 -26
- package/src/components/Dropdown/Dropdown.stories.tsx +2 -5
- package/src/components/Dropdown/Dropdown.tsx +313 -299
- package/src/components/EdgePanel/EdgePanel.stories.tsx +6 -19
- package/src/components/EdgePanel/EdgePanel.tsx +1 -3
- package/src/components/Footer/Footer.stories.tsx +21 -16
- package/src/components/Footer/Footer.tsx +130 -128
- package/src/components/Footer/FooterLink.tsx +2 -2
- package/src/components/Form/Checkbox.test.tsx +49 -49
- package/src/components/Form/Checkbox.tsx +108 -100
- package/src/components/Form/Form.stories.tsx +2 -10
- package/src/components/Form/Input.stories.tsx +22 -39
- package/src/components/Form/Input.test.tsx +38 -44
- package/src/components/Form/Radio.stories.tsx +6 -12
- package/src/components/Form/Radio.tsx +68 -66
- package/src/components/Form/Select.tsx +184 -182
- package/src/components/Form/Textarea.test.tsx +27 -32
- package/src/components/Hero/Hero.stories.tsx +56 -23
- package/src/components/Hero/Hero.tsx +201 -55
- package/src/components/Icon/index.ts +7 -1
- package/src/components/List/List.tsx +19 -23
- package/src/components/Modal/Modal.stories.tsx +2 -1
- package/src/components/Modal/Modal.tsx +130 -127
- package/src/components/Navigation/Menu/MegaMenu.tsx +70 -70
- package/src/components/Navigation/Nav/NavDropdown.tsx +1 -5
- package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +128 -28
- package/src/components/Navigation/SideMenu/SideMenu.tsx +5 -7
- package/src/components/Navigation/SideMenu/SideMenuItem.tsx +4 -5
- package/src/components/Pagination/Pagination.stories.tsx +7 -4
- package/src/components/Pagination/Pagination.tsx +199 -202
- package/src/components/PhotoViewer/PhotoViewer.tsx +4 -1
- package/src/components/Popover/Popover.stories.tsx +99 -192
- package/src/components/Popover/Popover.tsx +41 -37
- package/src/components/Progress/Progress.stories.tsx +35 -44
- package/src/components/River/River.stories.tsx +2 -1
- package/src/components/SectionIntro/SectionIntro.stories.tsx +71 -71
- package/src/components/Slider/Slider.stories.tsx +12 -4
- package/src/components/Spinner/Spinner.stories.tsx +3 -1
- package/src/components/Spinner/Spinner.test.tsx +23 -23
- package/src/components/Spinner/Spinner.tsx +43 -46
- package/src/components/Steps/Steps.stories.tsx +8 -6
- package/src/components/Tabs/Tabs.stories.tsx +12 -9
- package/src/components/Tabs/Tabs.tsx +74 -72
- package/src/components/Toggle/Toggle.stories.tsx +27 -13
- package/src/components/Toggle/Toggle.test.tsx +65 -70
- package/src/components/Toggle/Toggle.tsx +4 -1
- package/src/components/Tooltip/Tooltip.stories.tsx +24 -20
- package/src/components/Tooltip/Tooltip.tsx +104 -106
- package/src/components/Upload/Upload.stories.tsx +129 -127
- package/src/components/Upload/Upload.tsx +287 -283
- package/src/components/VideoPlayer/VideoPlayer.tsx +6 -1
- package/src/components/index.ts +13 -2
- package/src/layouts/Grid/Grid.stories.tsx +9 -3
- package/src/layouts/MasonryGrid/MasonryGrid.tsx +5 -1
- package/src/lib/__tests__/theme-tools.test.ts +32 -6
- package/src/lib/composables/shared-mouse-tracker.ts +13 -14
- package/src/lib/composables/useAtomixGlass.ts +106 -49
- package/src/lib/composables/useChartExport.ts +1 -1
- package/src/lib/composables/useDataTable.ts +29 -17
- package/src/lib/composables/useHero.ts +58 -14
- package/src/lib/composables/useHeroBackgroundSlider.ts +2 -9
- package/src/lib/composables/useInput.ts +10 -8
- package/src/lib/composables/useSideMenu.ts +6 -5
- package/src/lib/composables/useTooltip.ts +1 -2
- package/src/lib/composables/useVideoPlayer.ts +44 -35
- package/src/lib/config/index.ts +154 -154
- package/src/lib/constants/cssVariables.ts +29 -29
- package/src/lib/hooks/__tests__/useComponentCustomization.test.ts +2 -6
- package/src/lib/hooks/index.ts +1 -1
- package/src/lib/hooks/useComponentCustomization.ts +11 -17
- package/src/lib/hooks/usePerformanceMonitor.ts +6 -7
- package/src/lib/patterns/__tests__/slots.test.ts +1 -1
- package/src/lib/patterns/index.ts +1 -1
- package/src/lib/patterns/slots.tsx +8 -13
- package/src/lib/storybook/InteractiveDemo.tsx +13 -18
- package/src/lib/storybook/PreviewContainer.tsx +1 -1
- package/src/lib/storybook/VariantsGrid.tsx +3 -7
- package/src/lib/storybook/index.ts +1 -1
- package/src/lib/theme/adapters/cssVariableMapper.ts +47 -74
- package/src/lib/theme/adapters/index.ts +3 -9
- package/src/lib/theme/adapters/themeAdapter.ts +41 -26
- package/src/lib/theme/config/index.ts +1 -1
- package/src/lib/theme/config/types.ts +2 -2
- package/src/lib/theme/config/validator.ts +10 -5
- package/src/lib/theme/constants/constants.ts +2 -2
- package/src/lib/theme/constants/index.ts +1 -2
- package/src/lib/theme/core/__tests__/createTheme.test.ts +20 -22
- package/src/lib/theme/core/composeTheme.ts +32 -26
- package/src/lib/theme/core/createTheme.ts +1 -1
- package/src/lib/theme/core/createThemeObject.ts +308 -301
- package/src/lib/theme/core/index.ts +3 -3
- package/src/lib/theme/devtools/CLI.ts +106 -104
- package/src/lib/theme/devtools/Comparator.tsx +50 -32
- package/src/lib/theme/devtools/DesignTokensCustomizer.stories.tsx +50 -48
- package/src/lib/theme/devtools/DesignTokensCustomizer.tsx +257 -63
- package/src/lib/theme/devtools/Inspector.tsx +75 -60
- package/src/lib/theme/devtools/LiveEditor.tsx +97 -76
- package/src/lib/theme/devtools/Preview.tsx +150 -106
- package/src/lib/theme/devtools/ThemeValidator.ts +29 -21
- package/src/lib/theme/devtools/index.ts +3 -9
- package/src/lib/theme/devtools/useHistory.ts +23 -21
- package/src/lib/theme/errors/errors.ts +12 -11
- package/src/lib/theme/errors/index.ts +2 -7
- package/src/lib/theme/generators/generateCSS.ts +9 -13
- package/src/lib/theme/generators/generateCSSNested.ts +1 -6
- package/src/lib/theme/generators/generateCSSVariables.ts +673 -630
- package/src/lib/theme/generators/index.ts +1 -4
- package/src/lib/theme/i18n/index.ts +1 -1
- package/src/lib/theme/i18n/rtl.ts +13 -13
- package/src/lib/theme/index.ts +7 -16
- package/src/lib/theme/runtime/ThemeApplicator.ts +4 -4
- package/src/lib/theme/runtime/ThemeContext.tsx +1 -1
- package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +19 -23
- package/src/lib/theme/runtime/ThemeProvider.tsx +230 -239
- package/src/lib/theme/runtime/__tests__/ThemeProvider.integration.test.tsx +1 -1
- package/src/lib/theme/runtime/__tests__/ThemeProvider.test.tsx +24 -29
- package/src/lib/theme/runtime/index.ts +2 -5
- package/src/lib/theme/runtime/useTheme.ts +18 -18
- package/src/lib/theme/runtime/useThemeTokens.ts +22 -22
- package/src/lib/theme/test/testTheme.ts +15 -16
- package/src/lib/theme/tokens/index.ts +2 -7
- package/src/lib/theme/tokens/tokens.ts +25 -24
- package/src/lib/theme/types.ts +428 -411
- package/src/lib/theme/utils/__tests__/themeValidation.test.ts +3 -3
- package/src/lib/theme/utils/componentTheming.ts +18 -18
- package/src/lib/theme/utils/domUtils.ts +277 -289
- package/src/lib/theme/utils/index.ts +1 -2
- package/src/lib/theme/utils/injectCSS.ts +10 -14
- package/src/lib/theme/utils/naming.ts +20 -16
- package/src/lib/theme/utils/themeHelpers.ts +10 -12
- package/src/lib/theme/utils/themeUtils.ts +85 -86
- package/src/lib/theme/utils/themeValidation.ts +82 -33
- package/src/lib/theme-tools.ts +8 -6
- package/src/lib/types/components.ts +172 -71
- package/src/lib/types/partProps.ts +1 -1
- package/src/lib/utils/__tests__/csv.test.ts +1 -1
- package/src/lib/utils/componentUtils.ts +8 -12
- package/src/lib/utils/csv.ts +3 -1
- package/src/lib/utils/dataTableExport.ts +1 -5
- package/src/lib/utils/fontPreloader.ts +10 -19
- package/src/lib/utils/icons.ts +4 -1
- package/src/lib/utils/index.ts +2 -6
- package/src/lib/utils/memoryMonitor.ts +10 -8
- package/src/lib/utils/themeNaming.ts +2 -2
- package/src/styles/01-settings/_index.scss +0 -1
- package/src/styles/01-settings/_settings.colors.scss +8 -8
- package/src/styles/01-settings/_settings.design-tokens.scss +61 -50
- package/src/styles/01-settings/_settings.navbar.scss +1 -1
- package/src/styles/01-settings/_settings.spacing.scss +3 -4
- package/src/styles/01-settings/_settings.tooltip.scss +1 -1
- package/src/styles/01-settings/_settings.typography.scss +1 -1
- package/src/styles/02-tools/_tools.button.scss +51 -21
- package/src/styles/02-tools/_tools.utility-api.scss +30 -18
- package/src/styles/03-generic/_generic.root.scss +4 -3
- package/src/styles/06-components/_components.atomix-glass.scss +13 -9
- package/src/styles/06-components/_components.button.scss +16 -4
- package/src/styles/06-components/_components.callout.scss +27 -21
- package/src/styles/06-components/_components.card.scss +5 -14
- package/src/styles/06-components/_components.chart.scss +22 -19
- package/src/styles/06-components/_components.checkbox.scss +3 -1
- package/src/styles/06-components/_components.color-mode-toggle.scss +3 -1
- package/src/styles/06-components/_components.edge-panel.scss +9 -2
- package/src/styles/06-components/_components.footer.scss +1 -1
- package/src/styles/06-components/_components.side-menu.scss +5 -5
- package/src/styles/06-components/_components.toggle.scss +18 -0
- package/src/styles/06-components/_index.scss +1 -1
- package/src/styles/06-components/old.chart.styles.scss +0 -2
- package/src/styles/99-utilities/_utilities.border.scss +69 -27
- package/src/styles/99-utilities/_utilities.display.scss +1 -1
- package/src/styles/99-utilities/_utilities.opacity.scss +10 -0
- package/src/styles/99-utilities/_utilities.position.scss +16 -9
- package/src/styles/99-utilities/_utilities.scss +1 -1
- package/src/styles/99-utilities/_utilities.sizes.scss +47 -18
- package/src/styles/99-utilities/_utilities.spacing.scss +118 -66
- package/src/styles/99-utilities/_utilities.text-gradient.scss +30 -30
- package/src/styles/99-utilities/_utilities.text.scss +67 -46
|
@@ -8,100 +8,116 @@ expect.extend(toHaveNoViolations);
|
|
|
8
8
|
|
|
9
9
|
// Mock AtomixGlass component
|
|
10
10
|
vi.mock('../AtomixGlass/AtomixGlass', () => ({
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
AtomixGlass: ({ children, ...props }: any) => (
|
|
12
|
+
<div data-testid="atomix-glass" data-glass-props={JSON.stringify(props)}>
|
|
13
|
+
{children}
|
|
14
|
+
</div>
|
|
15
|
+
),
|
|
16
16
|
}));
|
|
17
17
|
|
|
18
18
|
describe('Button Component', () => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
it('renders correctly with label', () => {
|
|
20
|
+
render(<Button label="Click Me" />);
|
|
21
|
+
expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument();
|
|
22
|
+
});
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
it('renders correctly with children', () => {
|
|
25
|
+
render(<Button>Click Me</Button>);
|
|
26
|
+
expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument();
|
|
27
|
+
});
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
it('handles onClick event', () => {
|
|
30
|
+
const handleClick = vi.fn();
|
|
31
|
+
render(<Button onClick={handleClick}>Click Me</Button>);
|
|
32
|
+
fireEvent.click(screen.getByRole('button'));
|
|
33
|
+
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
34
|
+
});
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
36
|
+
it('does not fire onClick when disabled', () => {
|
|
37
|
+
const handleClick = vi.fn();
|
|
38
|
+
render(
|
|
39
|
+
<Button disabled onClick={handleClick}>
|
|
40
|
+
Click Me
|
|
41
|
+
</Button>
|
|
42
|
+
);
|
|
43
|
+
const button = screen.getByRole('button');
|
|
44
|
+
expect(button).toBeDisabled();
|
|
45
|
+
fireEvent.click(button);
|
|
46
|
+
expect(handleClick).not.toHaveBeenCalled();
|
|
47
|
+
});
|
|
44
48
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
it('renders loading state correctly', () => {
|
|
50
|
+
render(<Button loading>Submit</Button>);
|
|
51
|
+
const button = screen.getByRole('button');
|
|
52
|
+
// It should be disabled or aria-disabled
|
|
53
|
+
expect(button).toHaveAttribute('aria-busy', 'true');
|
|
54
|
+
// Check for spinner (implementation detail: spinner usually has specific class or role)
|
|
55
|
+
// Based on Button.tsx: <span className="...spinner...">...</span>
|
|
56
|
+
// Let's look for the spinner component or class if we can't find by role
|
|
57
|
+
// Or check if text is still there.
|
|
58
|
+
expect(screen.getByText('Submit')).toBeInTheDocument();
|
|
59
|
+
});
|
|
56
60
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
it('replaces text with loadingText when loading', () => {
|
|
62
|
+
render(
|
|
63
|
+
<Button loading loadingText="Processing...">
|
|
64
|
+
Submit
|
|
65
|
+
</Button>
|
|
66
|
+
);
|
|
67
|
+
expect(screen.queryByText('Submit')).not.toBeInTheDocument();
|
|
68
|
+
expect(screen.getByText('Processing...')).toBeInTheDocument();
|
|
69
|
+
});
|
|
62
70
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
71
|
+
it('renders as a link when href is provided', () => {
|
|
72
|
+
render(<Button href="/home">Home</Button>);
|
|
73
|
+
const link = screen.getByRole('link', { name: /home/i });
|
|
74
|
+
expect(link).toHaveAttribute('href', '/home');
|
|
75
|
+
});
|
|
68
76
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
it('renders as disabled link when disabled and href provided', () => {
|
|
78
|
+
// Current implementation might be buggy here, let's see
|
|
79
|
+
render(
|
|
80
|
+
<Button href="/home" disabled>
|
|
81
|
+
Home
|
|
82
|
+
</Button>
|
|
83
|
+
);
|
|
84
|
+
const link = screen.queryByRole('link');
|
|
85
|
+
const button = screen.queryByRole('button');
|
|
74
86
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
// If it renders as button when disabled (which logic suggested), then:
|
|
88
|
+
if (button) {
|
|
89
|
+
expect(button).toBeDisabled();
|
|
90
|
+
} else if (link) {
|
|
91
|
+
expect(link).toHaveAttribute('aria-disabled', 'true');
|
|
92
|
+
// Should not navigate
|
|
93
|
+
}
|
|
94
|
+
});
|
|
83
95
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
96
|
+
it('forwards ref', () => {
|
|
97
|
+
const ref = React.createRef<HTMLButtonElement>();
|
|
98
|
+
render(<Button ref={ref}>Ref Button</Button>);
|
|
99
|
+
expect(ref.current).toBeInstanceOf(HTMLButtonElement);
|
|
100
|
+
});
|
|
89
101
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
102
|
+
it('handles iconOnly correctly with aria-label', () => {
|
|
103
|
+
render(<Button iconOnly icon={<span>Icon</span>} aria-label="Icon Button" />);
|
|
104
|
+
expect(screen.getByLabelText('Icon Button')).toBeInTheDocument();
|
|
105
|
+
});
|
|
94
106
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
107
|
+
it('does not render [object Object] as aria-label when children is an element', () => {
|
|
108
|
+
const { container } = render(
|
|
109
|
+
<Button iconOnly icon={<span>Icon</span>}>
|
|
110
|
+
<span>Text</span>
|
|
111
|
+
</Button>
|
|
112
|
+
);
|
|
113
|
+
const button = screen.getByRole('button');
|
|
114
|
+
// aria-label should probably be undefined or empty, or extracted text, but definitely not "[object Object]"
|
|
115
|
+
expect(button.getAttribute('aria-label')).not.toBe('[object Object]');
|
|
116
|
+
});
|
|
101
117
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
118
|
+
it('should have no accessibility violations', async () => {
|
|
119
|
+
const { container } = render(<Button>Accessible Button</Button>);
|
|
120
|
+
const results = await axe(container);
|
|
121
|
+
expect(results).toHaveNoViolations();
|
|
122
|
+
});
|
|
107
123
|
});
|
|
@@ -65,13 +65,19 @@ export const Button = React.memo(
|
|
|
65
65
|
const shouldRenderAsLink = Boolean(href && !isDisabled);
|
|
66
66
|
|
|
67
67
|
// Resolve icon element - support both icon (ReactNode) and iconName (string)
|
|
68
|
-
const iconElement = iconName ?
|
|
68
|
+
const iconElement = iconName ? (
|
|
69
|
+
<Icon name={iconName as PhosphorIconsType} size={iconSize} />
|
|
70
|
+
) : (
|
|
71
|
+
icon
|
|
72
|
+
);
|
|
69
73
|
|
|
70
74
|
const buttonClass = [
|
|
71
75
|
BUTTON.BASE_CLASS,
|
|
72
76
|
ThemeNaming.variantClass(THEME_NAMING.BUTTON_PREFIX, variant),
|
|
73
77
|
size !== 'md' ? ThemeNaming.sizeClass(THEME_NAMING.BUTTON_PREFIX, size) : '',
|
|
74
|
-
iconOnly
|
|
78
|
+
iconOnly
|
|
79
|
+
? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.ICON_ELEMENT)
|
|
80
|
+
: '',
|
|
75
81
|
rounded ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, 'rounded') : '',
|
|
76
82
|
isDisabled ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, 'disabled') : '',
|
|
77
83
|
glass ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, 'glass') : '',
|
|
@@ -134,30 +140,60 @@ export const Button = React.memo(
|
|
|
134
140
|
const spinnerSize = size === 'sm' ? 'sm' : size === 'lg' ? 'md' : 'sm';
|
|
135
141
|
|
|
136
142
|
// Safe Aria Label
|
|
137
|
-
const safeAriaLabel =
|
|
143
|
+
const safeAriaLabel =
|
|
144
|
+
ariaLabel ||
|
|
145
|
+
(iconOnly
|
|
146
|
+
? typeof label === 'string'
|
|
147
|
+
? label
|
|
148
|
+
: typeof children === 'string'
|
|
149
|
+
? children
|
|
150
|
+
: undefined
|
|
151
|
+
: undefined);
|
|
138
152
|
|
|
139
153
|
// Button content with icon positioning
|
|
140
154
|
const buttonContent = (
|
|
141
155
|
<>
|
|
142
156
|
{loading && (
|
|
143
|
-
<span
|
|
157
|
+
<span
|
|
158
|
+
className={ThemeNaming.bemClass(
|
|
159
|
+
THEME_NAMING.BUTTON_PREFIX,
|
|
160
|
+
THEME_NAMING.SPINNER_ELEMENT
|
|
161
|
+
)}
|
|
162
|
+
aria-hidden="true"
|
|
163
|
+
>
|
|
144
164
|
<Spinner
|
|
145
165
|
size={spinnerSize}
|
|
146
166
|
variant={
|
|
147
|
-
variant === 'link' ||
|
|
167
|
+
variant === 'link' ||
|
|
168
|
+
(typeof variant === 'string' && variant.startsWith('outline-'))
|
|
148
169
|
? 'primary'
|
|
149
|
-
:
|
|
170
|
+
: variant === 'danger'
|
|
171
|
+
? 'error'
|
|
172
|
+
: (variant as any)
|
|
150
173
|
}
|
|
151
174
|
/>
|
|
152
175
|
</span>
|
|
153
176
|
)}
|
|
154
177
|
{iconElement && !loading && (
|
|
155
|
-
<span
|
|
178
|
+
<span
|
|
179
|
+
className={ThemeNaming.bemClass(
|
|
180
|
+
THEME_NAMING.BUTTON_PREFIX,
|
|
181
|
+
THEME_NAMING.ICON_ELEMENT
|
|
182
|
+
)}
|
|
183
|
+
aria-hidden="true"
|
|
184
|
+
>
|
|
156
185
|
{iconElement}
|
|
157
186
|
</span>
|
|
158
187
|
)}
|
|
159
188
|
{!iconOnly && buttonText && (
|
|
160
|
-
<span
|
|
189
|
+
<span
|
|
190
|
+
className={ThemeNaming.bemClass(
|
|
191
|
+
THEME_NAMING.BUTTON_PREFIX,
|
|
192
|
+
THEME_NAMING.LABEL_ELEMENT
|
|
193
|
+
)}
|
|
194
|
+
>
|
|
195
|
+
{buttonText}
|
|
196
|
+
</span>
|
|
161
197
|
)}
|
|
162
198
|
</>
|
|
163
199
|
);
|
|
@@ -175,7 +211,7 @@ export const Button = React.memo(
|
|
|
175
211
|
'aria-describedby': ariaDescribedBy,
|
|
176
212
|
'aria-expanded': ariaExpanded,
|
|
177
213
|
'aria-controls': ariaControls,
|
|
178
|
-
tabIndex: tabIndex !== undefined ? tabIndex :
|
|
214
|
+
tabIndex: tabIndex !== undefined ? tabIndex : isDisabled ? -1 : 0,
|
|
179
215
|
style,
|
|
180
216
|
...props,
|
|
181
217
|
};
|
|
@@ -195,11 +231,7 @@ export const Button = React.memo(
|
|
|
195
231
|
rel: target === '_blank' ? 'noopener noreferrer' : undefined,
|
|
196
232
|
};
|
|
197
233
|
|
|
198
|
-
content =
|
|
199
|
-
<LinkComp {...linkProps}>
|
|
200
|
-
{buttonContent}
|
|
201
|
-
</LinkComp>
|
|
202
|
-
);
|
|
234
|
+
content = <LinkComp {...linkProps}>{buttonContent}</LinkComp>;
|
|
203
235
|
} else {
|
|
204
236
|
// Fallback to regular anchor tag
|
|
205
237
|
content = (
|
|
@@ -38,7 +38,6 @@ const sampleButtonTitles = {
|
|
|
38
38
|
filters: ['All', 'Active', 'Completed'],
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
-
|
|
42
41
|
// ============================================================================
|
|
43
42
|
// META CONFIGURATION
|
|
44
43
|
// ============================================================================
|
|
@@ -198,7 +197,7 @@ export const GroupWithIcons: Story = {
|
|
|
198
197
|
<path d="M12 8v4M12 16h.01" />
|
|
199
198
|
</svg>
|
|
200
199
|
);
|
|
201
|
-
|
|
200
|
+
|
|
202
201
|
return (
|
|
203
202
|
<ButtonGroup>
|
|
204
203
|
<Button label="First" icon={<Icon />} variant="primary" />
|
|
@@ -216,15 +215,15 @@ export const GroupWithIcons: Story = {
|
|
|
216
215
|
export const InFormIntegration: Story = {
|
|
217
216
|
render: () => (
|
|
218
217
|
<form className="u-flex u-flex-col u-gap-4">
|
|
219
|
-
<input
|
|
220
|
-
type="text"
|
|
221
|
-
placeholder="Username"
|
|
222
|
-
className="u-p-2 u-mb-2 u-border u-border-gray-300 u-rounded u-w-full"
|
|
218
|
+
<input
|
|
219
|
+
type="text"
|
|
220
|
+
placeholder="Username"
|
|
221
|
+
className="u-p-2 u-mb-2 u-border u-border-gray-300 u-rounded u-w-full"
|
|
223
222
|
/>
|
|
224
|
-
<input
|
|
225
|
-
type="password"
|
|
226
|
-
placeholder="Password"
|
|
227
|
-
className="u-p-2 u-mb-2 u-border u-border-gray-300 u-rounded u-w-full"
|
|
223
|
+
<input
|
|
224
|
+
type="password"
|
|
225
|
+
placeholder="Password"
|
|
226
|
+
className="u-p-2 u-mb-2 u-border u-border-gray-300 u-rounded u-w-full"
|
|
228
227
|
/>
|
|
229
228
|
<ButtonGroup className="u-justify-end">
|
|
230
229
|
<Button label="Cancel" variant="secondary" />
|
|
@@ -258,7 +257,7 @@ export const WithOtherComponents: Story = {
|
|
|
258
257
|
<Button label="Inactive" variant="outline-primary" />
|
|
259
258
|
</ButtonGroup>
|
|
260
259
|
</div>
|
|
261
|
-
|
|
260
|
+
|
|
262
261
|
<div className="u-flex u-justify-between u-items-center">
|
|
263
262
|
<div className="u-text-sm u-text-gray-600">Showing 1-10 of 42 results</div>
|
|
264
263
|
<ButtonGroup>
|
|
@@ -278,24 +277,24 @@ export const ResponsiveButtonGroup: Story = {
|
|
|
278
277
|
render: () => (
|
|
279
278
|
<div className="u-w-full">
|
|
280
279
|
<ButtonGroup className="u-flex-col md:u-flex-row u-gap-0">
|
|
281
|
-
<Button
|
|
282
|
-
label="Home"
|
|
283
|
-
variant="outline-primary"
|
|
280
|
+
<Button
|
|
281
|
+
label="Home"
|
|
282
|
+
variant="outline-primary"
|
|
284
283
|
className="u-w-full md:u-w-auto u-rounded-none md:u-rounded-l md:u-rounded-r-none"
|
|
285
284
|
/>
|
|
286
|
-
<Button
|
|
287
|
-
label="About"
|
|
288
|
-
variant="outline-primary"
|
|
285
|
+
<Button
|
|
286
|
+
label="About"
|
|
287
|
+
variant="outline-primary"
|
|
289
288
|
className="u-w-full md:u-w-auto u-rounded-none"
|
|
290
289
|
/>
|
|
291
|
-
<Button
|
|
292
|
-
label="Services"
|
|
293
|
-
variant="outline-primary"
|
|
290
|
+
<Button
|
|
291
|
+
label="Services"
|
|
292
|
+
variant="outline-primary"
|
|
294
293
|
className="u-w-full md:u-w-auto u-rounded-none"
|
|
295
294
|
/>
|
|
296
|
-
<Button
|
|
297
|
-
label="Contact"
|
|
298
|
-
variant="outline-primary"
|
|
295
|
+
<Button
|
|
296
|
+
label="Contact"
|
|
297
|
+
variant="outline-primary"
|
|
299
298
|
className="u-w-full md:u-w-auto u-rounded-none md:u-rounded-r md:u-rounded-l-none"
|
|
300
299
|
/>
|
|
301
300
|
</ButtonGroup>
|
|
@@ -410,17 +409,17 @@ export const VerticalLayout: Story = {
|
|
|
410
409
|
export const WithKeyboardNavigation: Story = {
|
|
411
410
|
render: () => {
|
|
412
411
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
413
|
-
|
|
412
|
+
|
|
414
413
|
const buttons = [
|
|
415
|
-
{ label:
|
|
416
|
-
{ label:
|
|
417
|
-
{ label:
|
|
418
|
-
{ label:
|
|
414
|
+
{ label: 'Button 1', variant: 'primary' },
|
|
415
|
+
{ label: 'Button 2', variant: 'secondary' },
|
|
416
|
+
{ label: 'Button 3', variant: 'outline-primary' },
|
|
417
|
+
{ label: 'Button 4', variant: 'success' },
|
|
419
418
|
];
|
|
420
419
|
|
|
421
420
|
const handleKeyDown = (index: number, e: React.KeyboardEvent) => {
|
|
422
421
|
let newIndex = index;
|
|
423
|
-
|
|
422
|
+
|
|
424
423
|
switch (e.key) {
|
|
425
424
|
case 'ArrowRight':
|
|
426
425
|
case 'ArrowDown':
|
|
@@ -439,7 +438,7 @@ export const WithKeyboardNavigation: Story = {
|
|
|
439
438
|
default:
|
|
440
439
|
return;
|
|
441
440
|
}
|
|
442
|
-
|
|
441
|
+
|
|
443
442
|
e.preventDefault();
|
|
444
443
|
setActiveIndex(newIndex);
|
|
445
444
|
};
|
|
@@ -454,7 +453,13 @@ export const WithKeyboardNavigation: Story = {
|
|
|
454
453
|
variant={btn.variant}
|
|
455
454
|
active={activeIndex === idx}
|
|
456
455
|
onKeyDown={(e: React.KeyboardEvent) => handleKeyDown(idx, e)}
|
|
457
|
-
className={
|
|
456
|
+
className={
|
|
457
|
+
idx === 0
|
|
458
|
+
? 'u-rounded-r-none'
|
|
459
|
+
: idx === buttons.length - 1
|
|
460
|
+
? 'u-rounded-l-none'
|
|
461
|
+
: 'u-rounded-none'
|
|
462
|
+
}
|
|
458
463
|
/>
|
|
459
464
|
))}
|
|
460
465
|
</div>
|
|
@@ -468,4 +473,4 @@ export const WithKeyboardNavigation: Story = {
|
|
|
468
473
|
},
|
|
469
474
|
},
|
|
470
475
|
},
|
|
471
|
-
};
|
|
476
|
+
};
|
|
@@ -5,10 +5,10 @@ import { Button } from './Button';
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* ButtonGroup - A component for grouping buttons together
|
|
8
|
-
*
|
|
8
|
+
*
|
|
9
9
|
* Groups buttons together with proper border radius handling and spacing.
|
|
10
10
|
* The buttons will be visually connected with shared borders.
|
|
11
|
-
*
|
|
11
|
+
*
|
|
12
12
|
* @example
|
|
13
13
|
* ```tsx
|
|
14
14
|
* <ButtonGroup>
|
|
@@ -26,12 +26,7 @@ export const ButtonGroup: React.FC<ButtonGroupProps> = ({
|
|
|
26
26
|
role = 'group',
|
|
27
27
|
}) => {
|
|
28
28
|
// Generate CSS classes
|
|
29
|
-
const buttonGroupClasses = [
|
|
30
|
-
BUTTON_GROUP.CLASSES.BASE,
|
|
31
|
-
className,
|
|
32
|
-
]
|
|
33
|
-
.filter(Boolean)
|
|
34
|
-
.join(' ');
|
|
29
|
+
const buttonGroupClasses = [BUTTON_GROUP.CLASSES.BASE, className].filter(Boolean).join(' ');
|
|
35
30
|
|
|
36
31
|
// Get valid Button children
|
|
37
32
|
const buttonChildren = Children.toArray(children).filter(
|
|
@@ -39,12 +34,7 @@ export const ButtonGroup: React.FC<ButtonGroupProps> = ({
|
|
|
39
34
|
);
|
|
40
35
|
|
|
41
36
|
return (
|
|
42
|
-
<div
|
|
43
|
-
className={buttonGroupClasses}
|
|
44
|
-
style={style}
|
|
45
|
-
role={role}
|
|
46
|
-
aria-label={ariaLabel}
|
|
47
|
-
>
|
|
37
|
+
<div className={buttonGroupClasses} style={style} role={role} aria-label={ariaLabel}>
|
|
48
38
|
{buttonChildren.map((child, index) => {
|
|
49
39
|
if (isValidElement<ButtonProps>(child)) {
|
|
50
40
|
// Clone the Button element to ensure proper styling
|
|
@@ -64,4 +54,3 @@ export type { ButtonGroupProps };
|
|
|
64
54
|
ButtonGroup.displayName = 'ButtonGroup';
|
|
65
55
|
|
|
66
56
|
export default ButtonGroup;
|
|
67
|
-
|