@shohojdhara/atomix 0.3.14 → 0.3.15

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 (173) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/build-tools/EXAMPLES.md +372 -0
  3. package/build-tools/README.md +242 -0
  4. package/build-tools/__tests__/error-handler.test.js +230 -0
  5. package/build-tools/__tests__/index.test.js +141 -0
  6. package/build-tools/__tests__/rollup-plugin.test.js +194 -0
  7. package/build-tools/__tests__/utils.test.js +161 -0
  8. package/build-tools/__tests__/vite-plugin.test.js +129 -0
  9. package/build-tools/__tests__/webpack-loader.test.js +190 -0
  10. package/build-tools/error-handler.js +308 -0
  11. package/build-tools/index.d.ts +43 -0
  12. package/build-tools/index.js +88 -0
  13. package/build-tools/package.json +67 -0
  14. package/build-tools/rollup-plugin.js +236 -0
  15. package/build-tools/types.d.ts +163 -0
  16. package/build-tools/utils.js +203 -0
  17. package/build-tools/vite-plugin.js +161 -0
  18. package/build-tools/webpack-loader.js +123 -0
  19. package/dist/atomix.css +203 -90
  20. package/dist/atomix.css.map +1 -1
  21. package/dist/atomix.min.css +3 -3
  22. package/dist/atomix.min.css.map +1 -1
  23. package/dist/build-tools/EXAMPLES.md +372 -0
  24. package/dist/build-tools/README.md +242 -0
  25. package/dist/build-tools/__tests__/error-handler.test.js +230 -0
  26. package/dist/build-tools/__tests__/index.test.js +141 -0
  27. package/dist/build-tools/__tests__/rollup-plugin.test.js +194 -0
  28. package/dist/build-tools/__tests__/utils.test.js +161 -0
  29. package/dist/build-tools/__tests__/vite-plugin.test.js +129 -0
  30. package/dist/build-tools/__tests__/webpack-loader.test.js +190 -0
  31. package/dist/build-tools/error-handler.js +308 -0
  32. package/dist/build-tools/index.d.ts +43 -0
  33. package/dist/build-tools/index.js +88 -0
  34. package/dist/build-tools/package.json +67 -0
  35. package/dist/build-tools/rollup-plugin.js +236 -0
  36. package/dist/build-tools/types.d.ts +163 -0
  37. package/dist/build-tools/utils.js +203 -0
  38. package/dist/build-tools/vite-plugin.js +161 -0
  39. package/dist/build-tools/webpack-loader.js +123 -0
  40. package/dist/charts.d.ts +1 -1
  41. package/dist/charts.js +86 -57
  42. package/dist/charts.js.map +1 -1
  43. package/dist/core.d.ts +1 -1
  44. package/dist/core.js +136 -112
  45. package/dist/core.js.map +1 -1
  46. package/dist/forms.d.ts +2 -5
  47. package/dist/forms.js +140 -128
  48. package/dist/forms.js.map +1 -1
  49. package/dist/heavy.d.ts +1 -1
  50. package/dist/heavy.js +136 -112
  51. package/dist/heavy.js.map +1 -1
  52. package/dist/index.d.ts +9 -61
  53. package/dist/index.esm.js +237 -286
  54. package/dist/index.esm.js.map +1 -1
  55. package/dist/index.js +250 -299
  56. package/dist/index.js.map +1 -1
  57. package/dist/index.min.js +1 -1
  58. package/dist/index.min.js.map +1 -1
  59. package/package.json +23 -8
  60. package/scripts/atomix-cli.js +170 -73
  61. package/scripts/cli/__tests__/README.md +81 -0
  62. package/scripts/cli/__tests__/basic.test.js +115 -0
  63. package/scripts/cli/__tests__/component-generator.test.js +332 -0
  64. package/scripts/cli/__tests__/integration.test.js +327 -0
  65. package/scripts/cli/__tests__/test-setup.js +133 -0
  66. package/scripts/cli/__tests__/token-manager.test.js +251 -0
  67. package/scripts/cli/__tests__/utils.test.js +161 -0
  68. package/scripts/cli/component-generator.js +253 -299
  69. package/scripts/cli/dependency-checker.js +355 -0
  70. package/scripts/cli/interactive-init.js +46 -5
  71. package/scripts/cli/template-manager.js +0 -2
  72. package/scripts/cli/templates/common-templates.js +636 -0
  73. package/scripts/cli/templates/composable-templates.js +148 -126
  74. package/scripts/cli/templates/index.js +23 -16
  75. package/scripts/cli/templates/project-templates.js +151 -23
  76. package/scripts/cli/templates/react-templates.js +280 -210
  77. package/scripts/cli/templates/scss-templates.js +90 -91
  78. package/scripts/cli/templates/testing-templates.js +206 -27
  79. package/scripts/cli/templates/testing-utils.js +278 -0
  80. package/scripts/cli/templates/types-templates.js +70 -56
  81. package/scripts/cli/theme-bridge.js +8 -2
  82. package/scripts/cli/token-manager.js +318 -206
  83. package/scripts/cli/utils.js +0 -1
  84. package/src/components/Accordion/Accordion.stories.tsx +369 -870
  85. package/src/components/AtomixGlass/AtomixGlass.tsx +80 -39
  86. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +103 -81
  87. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +8 -7
  88. package/src/components/AtomixGlass/glass-utils.ts +2 -2
  89. package/src/components/AtomixGlass/shader-utils.ts +5 -0
  90. package/src/components/AtomixGlass/stories/Customization.stories.tsx +131 -0
  91. package/src/components/AtomixGlass/stories/Examples.stories.tsx +2957 -2853
  92. package/src/components/AtomixGlass/stories/Modes.stories.tsx +1 -1
  93. package/src/components/AtomixGlass/stories/Overview.stories.tsx +348 -0
  94. package/src/components/AtomixGlass/stories/Performance.stories.tsx +103 -0
  95. package/src/components/AtomixGlass/stories/Playground.stories.tsx +50 -35
  96. package/src/components/AtomixGlass/stories/{ShaderVariants.stories.tsx → Shaders.stories.tsx} +1 -1
  97. package/src/components/AtomixGlass/stories/shared-components.tsx +90 -190
  98. package/src/components/Avatar/Avatar.stories.tsx +213 -1
  99. package/src/components/Badge/Badge.stories.tsx +121 -362
  100. package/src/components/Block/Block.stories.tsx +21 -12
  101. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +141 -23
  102. package/src/components/Button/Button.stories.tsx +463 -1126
  103. package/src/components/Button/Button.test.tsx +107 -0
  104. package/src/components/Button/Button.tsx +46 -50
  105. package/src/components/Button/ButtonGroup.stories.tsx +373 -217
  106. package/src/components/Callout/Callout.stories.tsx +289 -634
  107. package/src/components/Card/Card.stories.tsx +248 -68
  108. package/src/components/Chart/Chart.stories.tsx +150 -8
  109. package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +151 -69
  110. package/src/components/Countdown/Countdown.stories.tsx +115 -8
  111. package/src/components/DataTable/DataTable.stories.tsx +346 -146
  112. package/src/components/DatePicker/DatePicker.stories.tsx +325 -1066
  113. package/src/components/Dropdown/Dropdown.stories.tsx +153 -33
  114. package/src/components/EdgePanel/EdgePanel.stories.tsx +230 -21
  115. package/src/components/Footer/Footer.stories.tsx +392 -328
  116. package/src/components/Form/Checkbox.stories.tsx +140 -6
  117. package/src/components/Form/Checkbox.test.tsx +63 -0
  118. package/src/components/Form/Checkbox.tsx +87 -51
  119. package/src/components/Form/Form.stories.tsx +119 -20
  120. package/src/components/Form/FormGroup.stories.tsx +127 -4
  121. package/src/components/Form/Radio.stories.tsx +140 -5
  122. package/src/components/Form/Select.stories.tsx +140 -8
  123. package/src/components/Form/Textarea.stories.tsx +149 -6
  124. package/src/components/Hero/Hero.stories.tsx +333 -32
  125. package/src/components/List/List.stories.tsx +141 -3
  126. package/src/components/Modal/Modal.stories.tsx +181 -42
  127. package/src/components/Popover/Popover.stories.tsx +448 -98
  128. package/src/components/Progress/Progress.stories.tsx +167 -5
  129. package/src/components/River/River.stories.tsx +1 -1
  130. package/src/components/SectionIntro/SectionIntro.stories.tsx +240 -48
  131. package/src/components/Spinner/Spinner.stories.tsx +102 -8
  132. package/src/components/Steps/Steps.stories.tsx +172 -43
  133. package/src/components/Tabs/Tabs.stories.tsx +136 -10
  134. package/src/components/Testimonial/Testimonial.stories.tsx +120 -3
  135. package/src/components/Todo/Todo.stories.tsx +198 -9
  136. package/src/components/Toggle/Toggle.stories.tsx +126 -39
  137. package/src/components/Tooltip/Tooltip.stories.tsx +194 -104
  138. package/src/components/Upload/Upload.stories.tsx +113 -24
  139. package/src/lib/README.md +2 -2
  140. package/src/lib/__tests__/theme-tools.test.ts +193 -0
  141. package/src/lib/composables/index.ts +2 -2
  142. package/src/lib/composables/useAtomixGlass.ts +28 -56
  143. package/src/lib/composables/useChartExport.ts +2 -7
  144. package/src/lib/composables/useDataTable.ts +46 -29
  145. package/src/lib/constants/components.ts +9 -32
  146. package/src/lib/theme/devtools/CLI.ts +1 -1
  147. package/src/lib/types/components.ts +1 -1
  148. package/src/lib/utils/__tests__/csv.test.ts +45 -0
  149. package/src/lib/utils/csv.ts +17 -0
  150. package/src/lib/utils/dataTableExport.ts +1 -10
  151. package/src/styles/01-settings/_index.scss +2 -1
  152. package/src/styles/01-settings/_settings.accordion.scss +28 -7
  153. package/src/styles/01-settings/_settings.colors.scss +11 -11
  154. package/src/styles/01-settings/_settings.typography.scss +5 -5
  155. package/src/styles/02-tools/_tools.utility-api.scss +14 -0
  156. package/src/styles/06-components/_components.accordion.scss +56 -14
  157. package/src/styles/06-components/_components.checkbox.scss +23 -17
  158. package/src/styles/99-utilities/_index.scss +2 -0
  159. package/src/styles/99-utilities/_utilities.scss +3 -1
  160. package/src/styles/99-utilities/_utilities.text-gradient.scss +45 -0
  161. package/themes/dark-complementary/README.md +98 -0
  162. package/themes/dark-complementary/index.scss +158 -0
  163. package/themes/default-light/README.md +81 -0
  164. package/themes/default-light/index.scss +154 -0
  165. package/themes/high-contrast/README.md +105 -0
  166. package/themes/high-contrast/index.scss +172 -0
  167. package/themes/test-theme/README.md +38 -0
  168. package/themes/test-theme/index.scss +47 -0
  169. package/scripts/cli/templates-original-backup.js +0 -1655
  170. package/scripts/cli/templates_backup.js +0 -684
  171. package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +0 -1438
  172. package/src/lib/composables/useButton.ts +0 -93
  173. package/src/lib/composables/useCheckbox.ts +0 -70
@@ -1,6 +1,11 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
+ import { fn } from '@storybook/test';
3
+ import { useState } from 'react';
2
4
  import { Checkbox } from './Checkbox';
3
5
 
6
+ // Mock event handlers
7
+ const mockOnChange = fn();
8
+
4
9
  const meta = {
5
10
  title: 'Components/Form/Checkbox',
6
11
  component: Checkbox,
@@ -8,8 +13,66 @@ const meta = {
8
13
  layout: 'centered',
9
14
  docs: {
10
15
  description: {
11
- component:
12
- 'The Checkbox component allows users to select one or more options from a set. It supports checked, unchecked, and indeterminate states, and can be used in forms or as standalone controls. Checkboxes provide clear visual feedback and support keyboard navigation.',
16
+ component: `
17
+ # Checkbox
18
+
19
+ ## Overview
20
+
21
+ Checkbox component allows users to select one or more options from a set. It supports checked, unchecked, and indeterminate states, and can be used in forms or as standalone controls. Checkboxes provide clear visual feedback and support keyboard navigation.
22
+
23
+ ## Features
24
+
25
+ - Checked, unchecked, and indeterminate states
26
+ - Label support
27
+ - Disabled state
28
+ - Validation states (valid/invalid)
29
+ - Glass morphism effect
30
+ - Accessible design
31
+ - Responsive behavior
32
+
33
+ ## Accessibility
34
+
35
+ - Keyboard support: Navigate and toggle with keyboard
36
+ - Screen reader: State and label announced properly
37
+ - ARIA support: Proper roles and properties for checkbox components
38
+ - Focus management: Visible focus indicators maintained
39
+
40
+ ## Usage Examples
41
+
42
+ ### Basic Usage
43
+
44
+ \`\`\`tsx
45
+ <Checkbox
46
+ label="Option label"
47
+ checked={isChecked}
48
+ onChange={setChecked}
49
+ />
50
+ \`\`\`
51
+
52
+ ### Indeterminate State
53
+
54
+ \`\`\`tsx
55
+ <Checkbox
56
+ label="Option label"
57
+ indeterminate={true}
58
+ />
59
+ \`\`\`
60
+
61
+ ## API Reference
62
+
63
+ ### Props
64
+
65
+ | Prop | Type | Default | Description |
66
+ | ---- | ---- | ------- | ----------- |
67
+ | label | ReactNode | - | Checkbox label text or element |
68
+ | checked | boolean | false | Whether the checkbox is checked |
69
+ | disabled | boolean | false | Whether the checkbox is disabled |
70
+ | invalid | boolean | false | Whether the checkbox is invalid |
71
+ | valid | boolean | false | Whether the checkbox is valid |
72
+ | indeterminate | boolean | false | Whether the checkbox is in indeterminate state |
73
+ | glass | boolean \| AtomixGlassProps | false | Enable glass morphism effect |
74
+ | onChange | (event: ChangeEvent<HTMLInputElement>) => void | - | Callback when checkbox state changes |
75
+ `,
13
76
  },
14
77
  },
15
78
  },
@@ -17,31 +80,63 @@ const meta = {
17
80
  argTypes: {
18
81
  label: {
19
82
  control: 'text',
20
- description: 'Checkbox label text',
83
+ description: 'Checkbox label text or element',
84
+ table: {
85
+ type: { summary: 'ReactNode' },
86
+ defaultValue: { summary: '-' },
87
+ },
21
88
  },
22
89
  checked: {
23
90
  control: 'boolean',
24
91
  description: 'Whether the checkbox is checked',
92
+ table: {
93
+ type: { summary: 'boolean' },
94
+ defaultValue: { summary: 'false' },
95
+ },
25
96
  },
26
97
  disabled: {
27
98
  control: 'boolean',
28
99
  description: 'Whether the checkbox is disabled',
100
+ table: {
101
+ type: { summary: 'boolean' },
102
+ defaultValue: { summary: 'false' },
103
+ },
29
104
  },
30
105
  invalid: {
31
106
  control: 'boolean',
32
107
  description: 'Whether the checkbox is invalid',
108
+ table: {
109
+ type: { summary: 'boolean' },
110
+ defaultValue: { summary: 'false' },
111
+ },
33
112
  },
34
113
  valid: {
35
114
  control: 'boolean',
36
115
  description: 'Whether the checkbox is valid',
116
+ table: {
117
+ type: { summary: 'boolean' },
118
+ defaultValue: { summary: 'false' },
119
+ },
37
120
  },
38
121
  indeterminate: {
39
122
  control: 'boolean',
40
123
  description: 'Whether the checkbox is in indeterminate state',
124
+ table: {
125
+ type: { summary: 'boolean' },
126
+ defaultValue: { summary: 'false' },
127
+ },
41
128
  },
42
129
  glass: {
43
- control: 'boolean',
130
+ control: { type: 'boolean' },
44
131
  description: 'Enable glass morphism effect',
132
+ table: {
133
+ type: { summary: 'boolean | AtomixGlassProps' },
134
+ defaultValue: { summary: 'false' },
135
+ },
136
+ },
137
+ onChange: {
138
+ action: 'changed',
139
+ description: 'Callback when checkbox state changes',
45
140
  },
46
141
  },
47
142
  } satisfies Meta<typeof Checkbox>;
@@ -50,9 +145,17 @@ export default meta;
50
145
  type Story = StoryObj<typeof meta>;
51
146
 
52
147
  // Basic checkbox
53
- export const Basic: Story = {
148
+ export const BasicUsage: Story = {
54
149
  args: {
55
150
  label: 'Accept terms and conditions',
151
+ onChange: mockOnChange,
152
+ },
153
+ parameters: {
154
+ docs: {
155
+ description: {
156
+ story: 'Basic checkbox with label.',
157
+ },
158
+ },
56
159
  },
57
160
  };
58
161
 
@@ -61,11 +164,19 @@ export const Checked: Story = {
61
164
  args: {
62
165
  label: 'Accept terms and conditions',
63
166
  checked: true,
167
+ onChange: mockOnChange,
168
+ },
169
+ parameters: {
170
+ docs: {
171
+ description: {
172
+ story: 'Checked checkbox state.',
173
+ },
174
+ },
64
175
  },
65
176
  };
66
177
 
67
178
  // Checkbox states
68
- export const States: Story = {
179
+ export const AllStates: Story = {
69
180
  render: (args: any) => (
70
181
  <div className="u-flex u-flex-column u-gap-3">
71
182
  <Checkbox label="Default checkbox" />
@@ -77,12 +188,27 @@ export const States: Story = {
77
188
  <Checkbox label="Indeterminate checkbox" indeterminate />
78
189
  </div>
79
190
  ),
191
+ parameters: {
192
+ docs: {
193
+ description: {
194
+ story: 'Checkbox in all available states.',
195
+ },
196
+ },
197
+ },
80
198
  };
81
199
 
82
200
  // Without label
83
201
  export const WithoutLabel: Story = {
84
202
  args: {
85
203
  'aria-label': 'Checkbox without visible label',
204
+ onChange: mockOnChange,
205
+ },
206
+ parameters: {
207
+ docs: {
208
+ description: {
209
+ story: 'Checkbox without visible label, using aria-label.',
210
+ },
211
+ },
86
212
  },
87
213
  };
88
214
 
@@ -91,6 +217,7 @@ export const Glass: Story = {
91
217
  args: {
92
218
  label: 'Glass Checkbox',
93
219
  glass: true,
220
+ onChange: mockOnChange,
94
221
  },
95
222
  render: (args: any) => (
96
223
  <div
@@ -107,6 +234,13 @@ export const Glass: Story = {
107
234
  <Checkbox {...args} />
108
235
  </div>
109
236
  ),
237
+ parameters: {
238
+ docs: {
239
+ description: {
240
+ story: 'Checkbox with glass morphism effect.',
241
+ },
242
+ },
243
+ },
110
244
  };
111
245
 
112
246
  // Glass with custom settings
@@ -0,0 +1,63 @@
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 { Checkbox } from './Checkbox';
5
+ import React from 'react';
6
+
7
+ expect.extend(toHaveNoViolations);
8
+
9
+ // Mock AtomixGlass
10
+ vi.mock('../AtomixGlass/AtomixGlass', () => ({
11
+ AtomixGlass: ({ children }: any) => <div>{children}</div>,
12
+ }));
13
+
14
+ describe('Checkbox Component', () => {
15
+ it('renders correctly with label', () => {
16
+ render(<Checkbox label="Accept Terms" />);
17
+ // In current implementation, if no ID is provided, htmlFor is undefined, so label is not associated.
18
+ // screen.getByLabelText might fail or might not find the input.
19
+ // Let's see.
20
+ expect(screen.getByText('Accept Terms')).toBeInTheDocument();
21
+ });
22
+
23
+ it('associates label with input when ID is provided', () => {
24
+ render(<Checkbox label="Subscribe" id="subscribe-check" />);
25
+ expect(screen.getByLabelText('Subscribe')).toBeInTheDocument();
26
+ });
27
+
28
+ it('associates label with input WITHOUT ID', () => {
29
+ // This tests my proposed improvement: wrapping input in label or auto-ID
30
+ render(<Checkbox label="No ID Checkbox" />);
31
+ // If not associated, this throws
32
+ expect(screen.getByLabelText('No ID Checkbox')).toBeInTheDocument();
33
+ });
34
+
35
+ it('handles checked state', () => {
36
+ const handleChange = vi.fn();
37
+ render(<Checkbox checked onChange={handleChange} label="Checked" id="checked-id" />);
38
+ const input = screen.getByLabelText('Checked');
39
+ expect(input).toBeChecked();
40
+
41
+ fireEvent.click(input);
42
+ expect(handleChange).toHaveBeenCalledTimes(1);
43
+ });
44
+
45
+ it('forwards ref', () => {
46
+ const ref = React.createRef<HTMLInputElement>();
47
+ render(<Checkbox ref={ref} label="Ref Checkbox" />);
48
+ expect(ref.current).toBeInstanceOf(HTMLInputElement);
49
+ });
50
+
51
+ it('handles indeterminate state', () => {
52
+ // This might need manual DOM check as indeterminate is a property, not attribute
53
+ const { getByRole } = render(<Checkbox indeterminate label="Indeterminate" id="indet" />);
54
+ const input = getByRole('checkbox') as HTMLInputElement;
55
+ expect(input.indeterminate).toBe(true);
56
+ });
57
+
58
+ it('should have no accessibility violations', async () => {
59
+ const { container } = render(<Checkbox label="Accessible Checkbox" id="a11y-check" />);
60
+ const results = await axe(container);
61
+ expect(results).toHaveNoViolations();
62
+ });
63
+ });
@@ -1,14 +1,18 @@
1
- import React, { memo } from 'react';
1
+ import React, { memo, forwardRef, useRef, useEffect, useImperativeHandle } from 'react';
2
2
  import { CheckboxProps } from '../../lib/types/components';
3
- import { useCheckbox } from '../../lib/composables/useCheckbox';
4
3
  import { AtomixGlass } from '../AtomixGlass/AtomixGlass';
5
4
 
6
- /**
7
- * Checkbox - A component for checkbox inputs
8
- */
9
- export const Checkbox: React.FC<CheckboxProps> = memo(({
5
+ const CHECKBOX_CLASSES = {
6
+ BASE: 'c-checkbox',
7
+ INVALID: 'is-error',
8
+ VALID: 'is-valid',
9
+ DISABLED: 'is-disabled',
10
+ MIXED: 'c-checkbox--mixed',
11
+ };
12
+
13
+ export const Checkbox = React.memo(forwardRef<HTMLInputElement, CheckboxProps>(({
10
14
  label,
11
- checked = false,
15
+ checked,
12
16
  onChange,
13
17
  className = '',
14
18
  style,
@@ -24,50 +28,84 @@ export const Checkbox: React.FC<CheckboxProps> = memo(({
24
28
  'aria-describedby': ariaDescribedBy,
25
29
  onClick,
26
30
  glass,
27
- }) => {
28
- const { generateCheckboxClass, checkboxRef } = useCheckbox({
29
- indeterminate,
30
- disabled,
31
- invalid,
32
- valid,
33
- });
31
+ ...props
32
+ }, ref) => {
33
+ // Local ref to handle indeterminate state
34
+ const localRef = useRef<HTMLInputElement>(null);
35
+
36
+ // Merge refs
37
+ useImperativeHandle(ref, () => localRef.current as HTMLInputElement);
38
+
39
+ // Handle indeterminate
40
+ useEffect(() => {
41
+ if (localRef.current) {
42
+ localRef.current.indeterminate = Boolean(indeterminate);
43
+ }
44
+ }, [indeterminate]);
45
+
46
+ // Generate classes
47
+ let validationClass = '';
48
+ if (invalid) {
49
+ validationClass = CHECKBOX_CLASSES.INVALID;
50
+ } else if (valid) {
51
+ validationClass = CHECKBOX_CLASSES.VALID;
52
+ }
53
+
54
+ const disabledClass = disabled ? CHECKBOX_CLASSES.DISABLED : '';
55
+ const indeterminateClass = indeterminate ? CHECKBOX_CLASSES.MIXED : '';
56
+ const glassClass = glass ? 'c-checkbox--glass' : '';
57
+
58
+ const checkboxClass = `${CHECKBOX_CLASSES.BASE} ${validationClass} ${disabledClass} ${indeterminateClass} ${glassClass} ${className}`.trim();
59
+
60
+ const inputElement = (
61
+ <input
62
+ ref={localRef}
63
+ type="checkbox"
64
+ className="c-checkbox__input"
65
+ checked={checked}
66
+ onChange={onChange}
67
+ onClick={onClick}
68
+ disabled={disabled}
69
+ required={required}
70
+ id={id}
71
+ name={name}
72
+ value={value}
73
+ aria-label={!label ? ariaLabel : undefined}
74
+ aria-describedby={ariaDescribedBy}
75
+ aria-invalid={invalid}
76
+ {...props}
77
+ />
78
+ );
34
79
 
35
- const checkboxClass = generateCheckboxClass({
36
- className: `${className} ${glass ? 'c-checkbox--glass' : ''}`.trim(),
37
- disabled,
38
- invalid,
39
- valid,
40
- indeterminate,
41
- });
80
+ let content: React.ReactNode;
42
81
 
43
- const checkboxContent = (
44
- <div className={checkboxClass} style={style}>
45
- <input
46
- ref={checkboxRef}
47
- type="checkbox"
48
- className="c-checkbox__input"
49
- checked={checked}
50
- onChange={onChange}
51
- onClick={onClick}
52
- disabled={disabled}
53
- required={required}
54
- id={id}
55
- name={name}
56
- value={value}
57
- aria-label={!label ? ariaLabel : undefined}
58
- aria-describedby={ariaDescribedBy}
59
- aria-invalid={invalid}
60
- />
61
- {label && (
82
+ if (id && label) {
83
+ content = (
84
+ <div className={checkboxClass} style={style}>
85
+ {inputElement}
62
86
  <label className="c-checkbox__label" htmlFor={id}>
63
87
  {label}
64
88
  </label>
65
- )}
66
- </div>
67
- );
89
+ </div>
90
+ );
91
+ } else if (label) {
92
+ // Wrap input in label for accessibility when no ID is provided
93
+ content = (
94
+ <label className={checkboxClass} style={style}>
95
+ {inputElement}
96
+ <span className="c-checkbox__label">{label}</span>
97
+ </label>
98
+ );
99
+ } else {
100
+ // No label
101
+ content = (
102
+ <div className={checkboxClass} style={style}>
103
+ {inputElement}
104
+ </div>
105
+ );
106
+ }
68
107
 
69
108
  if (glass) {
70
- // Default glass settings for checkboxes
71
109
  const defaultGlassProps = {
72
110
  displacementScale: 40,
73
111
  blurAmount: 1,
@@ -76,17 +114,15 @@ export const Checkbox: React.FC<CheckboxProps> = memo(({
76
114
  cornerRadius: 6,
77
115
  mode: 'shader' as const,
78
116
  };
79
-
80
117
  const glassProps = glass === true ? defaultGlassProps : { ...defaultGlassProps, ...glass };
81
-
82
- return <AtomixGlass {...glassProps}>{checkboxContent}</AtomixGlass>;
118
+ return <AtomixGlass {...glassProps}>{content}</AtomixGlass>;
83
119
  }
84
120
 
85
- return checkboxContent;
86
- });
87
-
88
- export type { CheckboxProps };
121
+ return content;
122
+ }));
89
123
 
90
124
  Checkbox.displayName = 'Checkbox';
91
125
 
126
+ export type { CheckboxProps };
127
+
92
128
  export default Checkbox;
@@ -1,4 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
+ import { fn } from '@storybook/test';
2
3
  import { useState } from 'react';
3
4
  import { Checkbox } from './Checkbox';
4
5
  import { Form } from './Form';
@@ -20,8 +21,69 @@ const meta = {
20
21
  layout: 'centered',
21
22
  docs: {
22
23
  description: {
23
- component:
24
- 'The Form component provides a semantic HTML form wrapper with enhanced functionality. It supports form validation, submission handling, and can be disabled as a whole. Forms work seamlessly with FormGroup and all form input components to create complete, accessible form experiences.',
24
+ component: `
25
+ # Form
26
+
27
+ ## Overview
28
+
29
+ Form component provides a semantic HTML form wrapper with enhanced functionality. It supports form validation, submission handling, and can be disabled as a whole. Forms work seamlessly with FormGroup and all form input components to create complete, accessible form experiences.
30
+
31
+ ## Features
32
+
33
+ - Semantic HTML form wrapper
34
+ - Form validation support
35
+ - Submission handling
36
+ - Disabled state
37
+ - Auto-complete control
38
+ - Method selection (GET/POST)
39
+ - Accessible design
40
+ - Responsive behavior
41
+
42
+ ## Accessibility
43
+
44
+ - Screen reader: Form structure and labels announced properly
45
+ - ARIA support: Proper roles and properties for form components
46
+ - Keyboard support: Navigate and submit forms with keyboard
47
+ - Focus management: Maintains focus on interactive elements
48
+
49
+ ## Usage Examples
50
+
51
+ ### Basic Usage
52
+
53
+ \`\`\`tsx
54
+ <Form onSubmit={handleSubmit}>
55
+ <FormGroup label="Name" htmlFor="name">
56
+ <Input id="name" placeholder="Enter your name" />
57
+ </FormGroup>
58
+ <button type="submit">Submit</button>
59
+ </Form>
60
+ \`\`\`
61
+
62
+ ### With Validation
63
+
64
+ \`\`\`tsx
65
+ <Form
66
+ onSubmit={handleSubmit}
67
+ noValidate={false}
68
+ autoComplete="on"
69
+ >
70
+ {/* Form fields */}
71
+ </Form>
72
+ \`\`\`
73
+
74
+ ## API Reference
75
+
76
+ ### Props
77
+
78
+ | Prop | Type | Default | Description |
79
+ | ---- | ---- | ------- | ----------- |
80
+ | disabled | boolean | false | Whether the form is disabled |
81
+ | method | 'get' \\| 'post' | 'get' | Form submission method |
82
+ | noValidate | boolean | false | Whether to disable browser validation |
83
+ | autoComplete | string | 'on' | Form autocomplete setting |
84
+ | className | string | - | Additional CSS class names |
85
+ | onSubmit | (event: FormEvent) => void | - | Callback when form is submitted |
86
+ `,
25
87
  },
26
88
  },
27
89
  },
@@ -30,23 +92,47 @@ const meta = {
30
92
  disabled: {
31
93
  control: 'boolean',
32
94
  description: 'Whether the form is disabled',
95
+ table: {
96
+ type: { summary: 'boolean' },
97
+ defaultValue: { summary: false },
98
+ },
33
99
  },
34
100
  className: {
35
101
  control: 'text',
36
102
  description: 'Additional CSS class names',
103
+ table: {
104
+ type: { summary: 'string' },
105
+ defaultValue: { summary: '-' },
106
+ },
37
107
  },
38
108
  method: {
39
109
  control: { type: 'select' },
40
110
  options: ['get', 'post'],
41
111
  description: 'Form submission method',
112
+ table: {
113
+ type: { summary: '"get" | "post"' },
114
+ defaultValue: { summary: 'get' },
115
+ },
42
116
  },
43
117
  noValidate: {
44
118
  control: 'boolean',
45
119
  description: 'Whether to disable browser validation',
120
+ table: {
121
+ type: { summary: 'boolean' },
122
+ defaultValue: { summary: false },
123
+ },
46
124
  },
47
125
  autoComplete: {
48
126
  control: 'text',
49
127
  description: 'Form autocomplete setting',
128
+ table: {
129
+ type: { summary: 'string' },
130
+ defaultValue: { summary: 'on' },
131
+ },
132
+ },
133
+ onSubmit: {
134
+ action: 'submitted',
135
+ description: 'Callback when form is submitted',
50
136
  },
51
137
  },
52
138
  } satisfies Meta<FormWithOptionalChildren>;
@@ -55,10 +141,10 @@ export default meta;
55
141
  type Story = StoryObj<typeof meta>;
56
142
 
57
143
  // Basic form
58
- export const Basic: Story = {
144
+ export const BasicUsage: Story = {
59
145
  args: { children: undefined },
60
146
  render: args => (
61
- <Form {...args}>
147
+ <Form {...args} onSubmit={fn()}>
62
148
  <FormGroup label="Name" htmlFor="name">
63
149
  <Input id="name" placeholder="Enter your name" />
64
150
  </FormGroup>
@@ -70,6 +156,13 @@ export const Basic: Story = {
70
156
  </button>
71
157
  </Form>
72
158
  ),
159
+ parameters: {
160
+ docs: {
161
+ description: {
162
+ story: 'Basic form with name and email fields.',
163
+ },
164
+ },
165
+ },
73
166
  };
74
167
 
75
168
  // Complete form with all input types
@@ -77,7 +170,7 @@ export const CompleteForm: Story = {
77
170
  args: { children: undefined },
78
171
  render: () => (
79
172
  <div style={{ width: '500px' }}>
80
- <Form>
173
+ <Form onSubmit={fn()}>
81
174
  <h2 className="u-mb-4">Registration Form</h2>
82
175
 
83
176
  <FormGroup label="Full Name" htmlFor="fullName" required>
@@ -109,42 +202,48 @@ export const CompleteForm: Story = {
109
202
  id="country"
110
203
  name="country"
111
204
  options={[
205
+ { value: '', label: 'Select a country' },
112
206
  { value: 'us', label: 'United States' },
113
207
  { value: 'ca', label: 'Canada' },
114
- { value: 'mx', label: 'Mexico' },
115
208
  { value: 'uk', label: 'United Kingdom' },
116
209
  ]}
117
- placeholder="Select your country"
118
210
  />
119
211
  </FormGroup>
120
212
 
121
- <FormGroup label="About yourself" htmlFor="bio">
122
- <Textarea id="bio" name="bio" placeholder="Tell us about yourself" rows={4} />
123
- </FormGroup>
124
-
125
- <FormGroup>
126
- <Checkbox id="terms" name="terms" label="I agree to the Terms and Conditions" required />
213
+ <FormGroup label="Bio" htmlFor="bio">
214
+ <Textarea
215
+ id="bio"
216
+ name="bio"
217
+ placeholder="Tell us about yourself"
218
+ rows={4}
219
+ />
127
220
  </FormGroup>
128
221
 
129
- <FormGroup label="Preferred contact method">
130
- <div className="u-flex u-flex-column u-gap-2">
131
- <Radio id="contact-email" name="contactMethod" value="email" label="Email" checked />
132
- <Radio id="contact-phone" name="contactMethod" value="phone" label="Phone" />
133
- <Radio id="contact-mail" name="contactMethod" value="mail" label="Mail" />
134
- </div>
222
+ <FormGroup label="Subscribe to newsletter">
223
+ <Checkbox
224
+ name="newsletter"
225
+ label="Yes, I would like to receive updates"
226
+ />
135
227
  </FormGroup>
136
228
 
137
229
  <div className="u-flex u-gap-3 u-mt-4">
138
230
  <button type="submit" className="c-btn c-btn--primary">
139
231
  Register
140
232
  </button>
141
- <button type="reset" className="c-btn c-btn--outline-secondary">
233
+ <button type="reset" className="c-btn c-btn--secondary">
142
234
  Reset
143
235
  </button>
144
236
  </div>
145
237
  </Form>
146
238
  </div>
147
239
  ),
240
+ parameters: {
241
+ docs: {
242
+ description: {
243
+ story: 'Complete registration form with various input types.',
244
+ },
245
+ },
246
+ },
148
247
  };
149
248
 
150
249
  // Interactive form