@shohojdhara/atomix 0.3.14 → 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/CHANGELOG.md +20 -0
- package/build-tools/EXAMPLES.md +372 -0
- package/build-tools/README.md +242 -0
- package/build-tools/__tests__/error-handler.test.js +230 -0
- package/build-tools/__tests__/index.test.js +141 -0
- package/build-tools/__tests__/rollup-plugin.test.js +194 -0
- package/build-tools/__tests__/utils.test.js +161 -0
- package/build-tools/__tests__/vite-plugin.test.js +129 -0
- package/build-tools/__tests__/webpack-loader.test.js +190 -0
- package/build-tools/error-handler.js +308 -0
- package/build-tools/index.d.ts +44 -0
- package/build-tools/index.js +88 -0
- package/build-tools/package.json +50 -0
- package/build-tools/rollup-plugin.js +236 -0
- package/build-tools/types.d.ts +163 -0
- package/build-tools/utils.js +203 -0
- package/build-tools/vite-plugin.js +161 -0
- package/build-tools/webpack-loader.js +123 -0
- package/dist/atomix.css +21044 -2618
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +77 -3
- package/dist/atomix.min.css.map +1 -1
- package/dist/build-tools/EXAMPLES.md +372 -0
- package/dist/build-tools/README.md +242 -0
- package/dist/build-tools/__tests__/error-handler.test.js +230 -0
- package/dist/build-tools/__tests__/index.test.js +141 -0
- package/dist/build-tools/__tests__/rollup-plugin.test.js +194 -0
- package/dist/build-tools/__tests__/utils.test.js +161 -0
- package/dist/build-tools/__tests__/vite-plugin.test.js +129 -0
- package/dist/build-tools/__tests__/webpack-loader.test.js +190 -0
- package/dist/build-tools/error-handler.js +308 -0
- package/dist/build-tools/index.d.ts +44 -0
- package/dist/build-tools/index.js +88 -0
- package/dist/build-tools/package.json +50 -0
- package/dist/build-tools/rollup-plugin.js +236 -0
- package/dist/build-tools/types.d.ts +163 -0
- package/dist/build-tools/utils.js +203 -0
- package/dist/build-tools/vite-plugin.js +161 -0
- package/dist/build-tools/webpack-loader.js +123 -0
- package/dist/charts.d.ts +1 -1
- package/dist/charts.js +86 -57
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +136 -112
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +2 -5
- package/dist/forms.js +140 -128
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +1 -1
- package/dist/heavy.js +136 -112
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +152 -78
- package/dist/index.esm.js +346 -340
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +359 -353
- 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 +23 -8
- package/scripts/atomix-cli.js +170 -73
- package/scripts/cli/__tests__/README.md +81 -0
- package/scripts/cli/__tests__/basic.test.js +115 -0
- package/scripts/cli/__tests__/component-generator.test.js +332 -0
- package/scripts/cli/__tests__/integration.test.js +327 -0
- package/scripts/cli/__tests__/test-setup.js +133 -0
- package/scripts/cli/__tests__/token-manager.test.js +251 -0
- package/scripts/cli/__tests__/utils.test.js +161 -0
- package/scripts/cli/component-generator.js +253 -299
- package/scripts/cli/dependency-checker.js +355 -0
- package/scripts/cli/interactive-init.js +46 -5
- package/scripts/cli/template-manager.js +0 -2
- package/scripts/cli/templates/common-templates.js +636 -0
- package/scripts/cli/templates/composable-templates.js +148 -126
- package/scripts/cli/templates/index.js +23 -16
- package/scripts/cli/templates/project-templates.js +151 -23
- package/scripts/cli/templates/react-templates.js +280 -210
- package/scripts/cli/templates/scss-templates.js +90 -91
- package/scripts/cli/templates/testing-templates.js +206 -27
- package/scripts/cli/templates/testing-utils.js +278 -0
- package/scripts/cli/templates/types-templates.js +70 -56
- package/scripts/cli/theme-bridge.js +8 -2
- package/scripts/cli/token-manager.js +318 -206
- package/scripts/cli/utils.js +0 -1
- package/src/components/Accordion/Accordion.stories.tsx +358 -850
- 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/AtomixGlass.tsx +80 -39
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +103 -81
- package/src/components/AtomixGlass/GlassFilter.tsx +9 -16
- package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +8 -7
- package/src/components/AtomixGlass/glass-utils.ts +6 -5
- package/src/components/AtomixGlass/shader-utils.ts +133 -52
- package/src/components/AtomixGlass/stories/Customization.stories.tsx +131 -0
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +2957 -2853
- package/src/components/AtomixGlass/stories/Modes.stories.tsx +1 -1
- package/src/components/AtomixGlass/stories/Overview.stories.tsx +348 -0
- package/src/components/AtomixGlass/stories/Performance.stories.tsx +103 -0
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +51 -36
- package/src/components/AtomixGlass/stories/{ShaderVariants.stories.tsx → Shaders.stories.tsx} +2 -2
- package/src/components/AtomixGlass/stories/shared-components.tsx +90 -190
- package/src/components/Avatar/Avatar.stories.tsx +195 -0
- package/src/components/Avatar/Avatar.tsx +58 -56
- package/src/components/Badge/Badge.stories.tsx +122 -352
- package/src/components/Badge/Badge.test.tsx +41 -41
- package/src/components/Badge/Badge.tsx +64 -62
- package/src/components/Block/Block.stories.tsx +30 -11
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +142 -23
- package/src/components/Breadcrumb/Breadcrumb.tsx +62 -60
- package/src/components/Button/Button.stories.tsx +454 -1126
- package/src/components/Button/Button.test.tsx +123 -0
- package/src/components/Button/Button.tsx +88 -60
- package/src/components/Button/ButtonGroup.stories.tsx +376 -215
- package/src/components/Button/ButtonGroup.tsx +4 -15
- package/src/components/Callout/Callout.stories.tsx +316 -568
- package/src/components/Card/Card.stories.tsx +292 -81
- 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 +153 -16
- 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 +152 -66
- package/src/components/ColorModeToggle/ColorModeToggle.tsx +15 -3
- package/src/components/Countdown/Countdown.stories.tsx +114 -7
- package/src/components/DataTable/DataTable.stories.tsx +349 -144
- package/src/components/DataTable/DataTable.test.tsx +26 -148
- package/src/components/DataTable/DataTable.tsx +485 -456
- package/src/components/DatePicker/DatePicker.stories.tsx +310 -1066
- package/src/components/DatePicker/DatePicker.tsx +31 -26
- package/src/components/Dropdown/Dropdown.stories.tsx +153 -36
- package/src/components/Dropdown/Dropdown.tsx +313 -299
- package/src/components/EdgePanel/EdgePanel.stories.tsx +221 -25
- package/src/components/EdgePanel/EdgePanel.tsx +1 -3
- package/src/components/Footer/Footer.stories.tsx +396 -327
- package/src/components/Footer/Footer.tsx +130 -128
- package/src/components/Footer/FooterLink.tsx +2 -2
- package/src/components/Form/Checkbox.stories.tsx +140 -6
- package/src/components/Form/Checkbox.test.tsx +63 -0
- package/src/components/Form/Checkbox.tsx +122 -78
- package/src/components/Form/Form.stories.tsx +110 -19
- package/src/components/Form/FormGroup.stories.tsx +127 -4
- 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 +146 -17
- package/src/components/Form/Radio.tsx +68 -66
- package/src/components/Form/Select.stories.tsx +140 -8
- package/src/components/Form/Select.tsx +184 -182
- package/src/components/Form/Textarea.stories.tsx +149 -6
- package/src/components/Form/Textarea.test.tsx +27 -32
- package/src/components/Hero/Hero.stories.tsx +372 -38
- package/src/components/Hero/Hero.tsx +201 -55
- package/src/components/Icon/index.ts +7 -1
- package/src/components/List/List.stories.tsx +141 -3
- package/src/components/List/List.tsx +19 -23
- package/src/components/Modal/Modal.stories.tsx +183 -43
- 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 +354 -97
- package/src/components/Popover/Popover.tsx +41 -37
- package/src/components/Progress/Progress.stories.tsx +160 -7
- package/src/components/River/River.stories.tsx +3 -2
- package/src/components/SectionIntro/SectionIntro.stories.tsx +239 -47
- package/src/components/Slider/Slider.stories.tsx +12 -4
- package/src/components/Spinner/Spinner.stories.tsx +104 -8
- package/src/components/Spinner/Spinner.test.tsx +23 -23
- package/src/components/Spinner/Spinner.tsx +43 -46
- package/src/components/Steps/Steps.stories.tsx +173 -42
- package/src/components/Tabs/Tabs.stories.tsx +141 -12
- package/src/components/Tabs/Tabs.tsx +74 -72
- package/src/components/Testimonial/Testimonial.stories.tsx +120 -3
- package/src/components/Todo/Todo.stories.tsx +198 -9
- package/src/components/Toggle/Toggle.stories.tsx +137 -36
- package/src/components/Toggle/Toggle.test.tsx +65 -70
- package/src/components/Toggle/Toggle.tsx +4 -1
- package/src/components/Tooltip/Tooltip.stories.tsx +194 -100
- package/src/components/Tooltip/Tooltip.tsx +104 -106
- package/src/components/Upload/Upload.stories.tsx +241 -150
- 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/README.md +2 -2
- package/src/lib/__tests__/theme-tools.test.ts +219 -0
- package/src/lib/composables/index.ts +2 -2
- package/src/lib/composables/shared-mouse-tracker.ts +13 -14
- package/src/lib/composables/useAtomixGlass.ts +126 -97
- package/src/lib/composables/useChartExport.ts +3 -8
- package/src/lib/composables/useDataTable.ts +72 -43
- 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/components.ts +9 -32
- 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 +107 -105
- 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 +45 -0
- package/src/lib/utils/componentUtils.ts +8 -12
- package/src/lib/utils/csv.ts +19 -0
- package/src/lib/utils/dataTableExport.ts +2 -15
- 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 +1 -1
- package/src/styles/01-settings/_settings.accordion.scss +28 -7
- package/src/styles/01-settings/_settings.colors.scss +11 -11
- 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 +4 -4
- package/src/styles/02-tools/_tools.button.scss +51 -21
- package/src/styles/02-tools/_tools.utility-api.scss +38 -12
- package/src/styles/03-generic/_generic.root.scss +4 -3
- package/src/styles/06-components/_components.accordion.scss +56 -14
- 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 +25 -17
- 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/_index.scss +2 -0
- 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 +2 -0
- 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 +45 -0
- package/src/styles/99-utilities/_utilities.text.scss +67 -46
- package/themes/dark-complementary/README.md +98 -0
- package/themes/dark-complementary/index.scss +158 -0
- package/themes/default-light/README.md +81 -0
- package/themes/default-light/index.scss +154 -0
- package/themes/high-contrast/README.md +105 -0
- package/themes/high-contrast/index.scss +172 -0
- package/themes/test-theme/README.md +38 -0
- package/themes/test-theme/index.scss +47 -0
- package/scripts/cli/templates-original-backup.js +0 -1655
- package/scripts/cli/templates_backup.js +0 -684
- package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +0 -1438
- package/src/lib/composables/useButton.ts +0 -93
- package/src/lib/composables/useCheckbox.ts +0 -70
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
2
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
3
|
+
import { axe, toHaveNoViolations } from 'jest-axe';
|
|
4
|
+
import { Button } from './Button';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
expect.extend(toHaveNoViolations);
|
|
8
|
+
|
|
9
|
+
// Mock AtomixGlass component
|
|
10
|
+
vi.mock('../AtomixGlass/AtomixGlass', () => ({
|
|
11
|
+
AtomixGlass: ({ children, ...props }: any) => (
|
|
12
|
+
<div data-testid="atomix-glass" data-glass-props={JSON.stringify(props)}>
|
|
13
|
+
{children}
|
|
14
|
+
</div>
|
|
15
|
+
),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
describe('Button Component', () => {
|
|
19
|
+
it('renders correctly with label', () => {
|
|
20
|
+
render(<Button label="Click Me" />);
|
|
21
|
+
expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('renders correctly with children', () => {
|
|
25
|
+
render(<Button>Click Me</Button>);
|
|
26
|
+
expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument();
|
|
27
|
+
});
|
|
28
|
+
|
|
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
|
+
|
|
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
|
+
});
|
|
48
|
+
|
|
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
|
+
});
|
|
60
|
+
|
|
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
|
+
});
|
|
70
|
+
|
|
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
|
+
});
|
|
76
|
+
|
|
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');
|
|
86
|
+
|
|
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
|
+
});
|
|
95
|
+
|
|
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
|
+
});
|
|
101
|
+
|
|
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
|
+
});
|
|
106
|
+
|
|
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
|
+
});
|
|
117
|
+
|
|
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
|
+
});
|
|
123
|
+
});
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React, { ElementType, forwardRef, useCallback } from 'react';
|
|
2
|
-
import { useButton } from '../../lib/composables/useButton';
|
|
3
2
|
import { ButtonProps } from '../../lib/types/components';
|
|
4
3
|
import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
|
|
5
4
|
import { Spinner } from '../Spinner/Spinner';
|
|
@@ -60,29 +59,25 @@ export const Button = React.memo(
|
|
|
60
59
|
const isDisabled = disabled || loading;
|
|
61
60
|
|
|
62
61
|
// Determine if we should render as a link
|
|
62
|
+
// If disabled, we still check href, but we might want to render as button or anchor with aria-disabled
|
|
63
|
+
// The previous logic was Boolean(href && !isDisabled). This meant if disabled, it renders as <button>.
|
|
64
|
+
// This is a safe fallback for disabled links.
|
|
63
65
|
const shouldRenderAsLink = Boolean(href && !isDisabled);
|
|
64
66
|
|
|
65
67
|
// Resolve icon element - support both icon (ReactNode) and iconName (string)
|
|
66
|
-
const iconElement = iconName ?
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
disabled: isDisabled,
|
|
72
|
-
rounded,
|
|
73
|
-
glass,
|
|
74
|
-
loading,
|
|
75
|
-
fullWidth,
|
|
76
|
-
block,
|
|
77
|
-
active,
|
|
78
|
-
selected,
|
|
79
|
-
});
|
|
68
|
+
const iconElement = iconName ? (
|
|
69
|
+
<Icon name={iconName as PhosphorIconsType} size={iconSize} />
|
|
70
|
+
) : (
|
|
71
|
+
icon
|
|
72
|
+
);
|
|
80
73
|
|
|
81
74
|
const buttonClass = [
|
|
82
75
|
BUTTON.BASE_CLASS,
|
|
83
76
|
ThemeNaming.variantClass(THEME_NAMING.BUTTON_PREFIX, variant),
|
|
84
77
|
size !== 'md' ? ThemeNaming.sizeClass(THEME_NAMING.BUTTON_PREFIX, size) : '',
|
|
85
|
-
iconOnly
|
|
78
|
+
iconOnly
|
|
79
|
+
? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, THEME_NAMING.ICON_ELEMENT)
|
|
80
|
+
: '',
|
|
86
81
|
rounded ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, 'rounded') : '',
|
|
87
82
|
isDisabled ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, 'disabled') : '',
|
|
88
83
|
glass ? ThemeNaming.stateClass(THEME_NAMING.BUTTON_PREFIX, 'glass') : '',
|
|
@@ -144,107 +139,140 @@ export const Button = React.memo(
|
|
|
144
139
|
// Determine spinner size based on button size
|
|
145
140
|
const spinnerSize = size === 'sm' ? 'sm' : size === 'lg' ? 'md' : 'sm';
|
|
146
141
|
|
|
142
|
+
// Safe Aria Label
|
|
143
|
+
const safeAriaLabel =
|
|
144
|
+
ariaLabel ||
|
|
145
|
+
(iconOnly
|
|
146
|
+
? typeof label === 'string'
|
|
147
|
+
? label
|
|
148
|
+
: typeof children === 'string'
|
|
149
|
+
? children
|
|
150
|
+
: undefined
|
|
151
|
+
: undefined);
|
|
152
|
+
|
|
147
153
|
// Button content with icon positioning
|
|
148
154
|
const buttonContent = (
|
|
149
155
|
<>
|
|
150
156
|
{loading && (
|
|
151
|
-
<span
|
|
157
|
+
<span
|
|
158
|
+
className={ThemeNaming.bemClass(
|
|
159
|
+
THEME_NAMING.BUTTON_PREFIX,
|
|
160
|
+
THEME_NAMING.SPINNER_ELEMENT
|
|
161
|
+
)}
|
|
162
|
+
aria-hidden="true"
|
|
163
|
+
>
|
|
152
164
|
<Spinner
|
|
153
165
|
size={spinnerSize}
|
|
154
166
|
variant={
|
|
155
|
-
variant === 'link' ||
|
|
167
|
+
variant === 'link' ||
|
|
168
|
+
(typeof variant === 'string' && variant.startsWith('outline-'))
|
|
156
169
|
? 'primary'
|
|
157
|
-
:
|
|
170
|
+
: variant === 'danger'
|
|
171
|
+
? 'error'
|
|
172
|
+
: (variant as any)
|
|
158
173
|
}
|
|
159
174
|
/>
|
|
160
175
|
</span>
|
|
161
176
|
)}
|
|
162
177
|
{iconElement && !loading && (
|
|
163
|
-
<span
|
|
178
|
+
<span
|
|
179
|
+
className={ThemeNaming.bemClass(
|
|
180
|
+
THEME_NAMING.BUTTON_PREFIX,
|
|
181
|
+
THEME_NAMING.ICON_ELEMENT
|
|
182
|
+
)}
|
|
183
|
+
aria-hidden="true"
|
|
184
|
+
>
|
|
164
185
|
{iconElement}
|
|
165
186
|
</span>
|
|
166
187
|
)}
|
|
167
188
|
{!iconOnly && buttonText && (
|
|
168
|
-
<span
|
|
189
|
+
<span
|
|
190
|
+
className={ThemeNaming.bemClass(
|
|
191
|
+
THEME_NAMING.BUTTON_PREFIX,
|
|
192
|
+
THEME_NAMING.LABEL_ELEMENT
|
|
193
|
+
)}
|
|
194
|
+
>
|
|
195
|
+
{buttonText}
|
|
196
|
+
</span>
|
|
169
197
|
)}
|
|
170
198
|
</>
|
|
171
199
|
);
|
|
172
200
|
|
|
173
201
|
// Button props
|
|
174
202
|
const buttonProps = {
|
|
175
|
-
ref,
|
|
176
203
|
className: buttonClass,
|
|
177
|
-
type: Component === 'button' && !shouldRenderAsLink ? type : undefined,
|
|
178
204
|
onClick: handleClickEvent,
|
|
179
205
|
onMouseEnter: onHover ? handleMouseEnter : undefined,
|
|
180
206
|
onFocus: onFocus ? handleFocusEvent : undefined,
|
|
181
207
|
onBlur: onBlur ? handleBlurEvent : undefined,
|
|
182
|
-
disabled: isDisabled && Component === 'button' && !shouldRenderAsLink,
|
|
183
208
|
'aria-disabled': isDisabled,
|
|
184
209
|
'aria-busy': loading,
|
|
185
|
-
'aria-label':
|
|
210
|
+
'aria-label': safeAriaLabel,
|
|
186
211
|
'aria-describedby': ariaDescribedBy,
|
|
187
212
|
'aria-expanded': ariaExpanded,
|
|
188
213
|
'aria-controls': ariaControls,
|
|
189
|
-
tabIndex: tabIndex !== undefined ? tabIndex :
|
|
214
|
+
tabIndex: tabIndex !== undefined ? tabIndex : isDisabled ? -1 : 0,
|
|
190
215
|
style,
|
|
191
216
|
...props,
|
|
192
217
|
};
|
|
193
218
|
|
|
194
|
-
|
|
195
|
-
const defaultGlassProps = {
|
|
196
|
-
displacementScale: 20,
|
|
197
|
-
blurAmount: 0,
|
|
198
|
-
saturation: 200,
|
|
199
|
-
elasticity: 0,
|
|
200
|
-
};
|
|
201
|
-
const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
|
|
219
|
+
let content: React.ReactElement;
|
|
202
220
|
|
|
203
221
|
// Render as anchor if href is provided
|
|
204
222
|
if (shouldRenderAsLink) {
|
|
205
|
-
const { ref: _, ...buttonPropsWithoutRef } = buttonProps;
|
|
206
|
-
const anchorButtonProps = {
|
|
207
|
-
...buttonPropsWithoutRef,
|
|
208
|
-
type: undefined,
|
|
209
|
-
disabled: undefined,
|
|
210
|
-
};
|
|
211
|
-
|
|
212
223
|
// Use custom LinkComponent if provided (e.g., Next.js Link)
|
|
213
224
|
if (LinkComponent) {
|
|
214
225
|
const LinkComp = LinkComponent as React.ComponentType<any>;
|
|
215
226
|
const linkProps = {
|
|
216
|
-
...
|
|
217
|
-
ref: ref as
|
|
227
|
+
...buttonProps,
|
|
228
|
+
ref: ref as any, // LinkComponent usually forwards ref to anchor
|
|
218
229
|
href,
|
|
219
230
|
target,
|
|
220
231
|
rel: target === '_blank' ? 'noopener noreferrer' : undefined,
|
|
221
232
|
};
|
|
222
233
|
|
|
223
|
-
|
|
224
|
-
|
|
234
|
+
content = <LinkComp {...linkProps}>{buttonContent}</LinkComp>;
|
|
235
|
+
} else {
|
|
236
|
+
// Fallback to regular anchor tag
|
|
237
|
+
content = (
|
|
238
|
+
<a
|
|
239
|
+
{...buttonProps}
|
|
240
|
+
ref={ref as React.Ref<HTMLAnchorElement>}
|
|
241
|
+
href={href}
|
|
242
|
+
target={target}
|
|
243
|
+
rel={target === '_blank' ? 'noopener noreferrer' : undefined}
|
|
244
|
+
>
|
|
225
245
|
{buttonContent}
|
|
226
|
-
</
|
|
246
|
+
</a>
|
|
227
247
|
);
|
|
228
|
-
|
|
229
|
-
return glass ? <AtomixGlass {...glassProps}>{linkElement}</AtomixGlass> : linkElement;
|
|
230
248
|
}
|
|
231
|
-
|
|
232
|
-
//
|
|
233
|
-
|
|
234
|
-
<
|
|
249
|
+
} else {
|
|
250
|
+
// Default button rendering
|
|
251
|
+
content = (
|
|
252
|
+
<Component
|
|
253
|
+
{...buttonProps}
|
|
254
|
+
ref={ref}
|
|
255
|
+
type={Component === 'button' ? type : undefined}
|
|
256
|
+
disabled={isDisabled}
|
|
257
|
+
>
|
|
235
258
|
{buttonContent}
|
|
236
|
-
</
|
|
259
|
+
</Component>
|
|
237
260
|
);
|
|
238
|
-
|
|
239
|
-
return glass ? <AtomixGlass {...glassProps}>{anchorElement}</AtomixGlass> : anchorElement;
|
|
240
261
|
}
|
|
241
262
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
263
|
+
if (glass) {
|
|
264
|
+
// Default glass props
|
|
265
|
+
const defaultGlassProps = {
|
|
266
|
+
displacementScale: 20,
|
|
267
|
+
blurAmount: 0,
|
|
268
|
+
saturation: 200,
|
|
269
|
+
elasticity: 0,
|
|
270
|
+
};
|
|
271
|
+
const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
|
|
272
|
+
return <AtomixGlass {...glassProps}>{content}</AtomixGlass>;
|
|
273
|
+
}
|
|
246
274
|
|
|
247
|
-
return
|
|
275
|
+
return content;
|
|
248
276
|
}
|
|
249
277
|
)
|
|
250
278
|
);
|