@marigold/components 0.0.2 → 0.3.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.
Files changed (224) hide show
  1. package/CHANGELOG.md +294 -0
  2. package/dist/ActionGroup/ActionGroup.d.ts +8 -0
  3. package/dist/ActionGroup/ActionGroup.stories.d.ts +5 -0
  4. package/dist/ActionGroup/index.d.ts +1 -0
  5. package/dist/Alert/Alert.d.ts +16 -3
  6. package/dist/Alert/Alert.stories.d.ts +5 -0
  7. package/dist/Badge/Badge.d.ts +11 -5
  8. package/dist/Badge/Badge.stories.d.ts +5 -0
  9. package/dist/Box.d.ts +2 -0
  10. package/dist/Button/Button.d.ts +9 -5
  11. package/dist/Button/Button.stories.d.ts +5 -0
  12. package/dist/Card/Card.d.ts +14 -0
  13. package/dist/Card/Card.stories.d.ts +5 -0
  14. package/dist/Card/index.d.ts +1 -0
  15. package/dist/Checkbox/Checkbox.d.ts +17 -5
  16. package/dist/Checkbox/Checkbox.stories.d.ts +5 -0
  17. package/dist/Checkbox/CheckboxIcons.d.ts +9 -0
  18. package/dist/Column/Column.d.ts +6 -4
  19. package/dist/Column/Column.stories.d.ts +5 -0
  20. package/dist/Columns/Columns.d.ts +10 -0
  21. package/dist/Columns/Columns.stories.d.ts +5 -0
  22. package/dist/Columns/index.d.ts +1 -0
  23. package/dist/Container/Container.d.ts +5 -4
  24. package/dist/Container/Container.stories.d.ts +5 -0
  25. package/dist/Dialog/Dialog.d.ts +17 -0
  26. package/dist/Dialog/Dialog.stories.d.ts +5 -0
  27. package/dist/Dialog/ModalDialog.d.ts +8 -0
  28. package/dist/Dialog/index.d.ts +1 -0
  29. package/dist/Divider/Divider.d.ts +10 -3
  30. package/dist/Divider/Divider.stories.d.ts +5 -0
  31. package/dist/Field/Field.d.ts +10 -5
  32. package/dist/Field/Field.stories.d.ts +5 -0
  33. package/dist/Image/Image.d.ts +11 -5
  34. package/dist/Image/Image.stories.d.ts +5 -0
  35. package/dist/Inline/Inline.d.ts +7 -0
  36. package/dist/Inline/Inline.stories.d.ts +5 -0
  37. package/dist/Inline/index.d.ts +1 -0
  38. package/dist/Input/Input.d.ts +10 -4
  39. package/dist/Input/Input.stories.d.ts +5 -0
  40. package/dist/Label/Label.d.ts +18 -5
  41. package/dist/Label/Label.stories.d.ts +5 -0
  42. package/dist/Link/Link.d.ts +10 -5
  43. package/dist/Link/Link.stories.d.ts +5 -0
  44. package/dist/Menu/Menu.d.ts +11 -4
  45. package/dist/Menu/Menu.stories.d.ts +5 -0
  46. package/dist/MenuItem/MenuItem.d.ts +11 -4
  47. package/dist/MenuItem/MenuItem.stories.d.ts +5 -0
  48. package/dist/Message/Message.d.ts +10 -4
  49. package/dist/Message/Message.stories.d.ts +5 -0
  50. package/dist/Provider/MarigoldProvider.d.ts +11 -0
  51. package/dist/Provider/index.d.ts +3 -0
  52. package/dist/Radio/Radio.d.ts +17 -4
  53. package/dist/Radio/RadioIcons.d.ts +10 -0
  54. package/dist/Select/ListBox.d.ts +9 -0
  55. package/dist/Select/ListBoxSection.d.ts +9 -0
  56. package/dist/Select/Option.d.ts +9 -0
  57. package/dist/Select/Popover.d.ts +9 -0
  58. package/dist/Select/Select.d.ts +27 -3
  59. package/dist/Select/Select.stories.d.ts +5 -0
  60. package/dist/Slider/Slider.d.ts +10 -4
  61. package/dist/Slider/Slider.stories.d.ts +5 -0
  62. package/dist/Stack/Stack.d.ts +7 -0
  63. package/dist/Stack/Stack.stories.d.ts +5 -0
  64. package/dist/Stack/index.d.ts +1 -0
  65. package/dist/Text/Text.d.ts +17 -7
  66. package/dist/Text/Text.stories.d.ts +5 -0
  67. package/dist/Textarea/Textarea.d.ts +15 -4
  68. package/dist/Textarea/Textarea.stories.d.ts +5 -0
  69. package/dist/ValidationMessage/ValidationMessage.d.ts +10 -4
  70. package/dist/ValidationMessage/ValidationMessage.stories.d.ts +5 -0
  71. package/dist/VisuallyHidden/VisuallyHidden.d.ts +1 -0
  72. package/dist/VisuallyHidden/VisuallyHidden.stories.d.ts +5 -0
  73. package/dist/VisuallyHidden/index.d.ts +1 -0
  74. package/dist/components.cjs.development.js +1276 -461
  75. package/dist/components.cjs.development.js.map +1 -1
  76. package/dist/components.cjs.production.min.js +1 -1
  77. package/dist/components.cjs.production.min.js.map +1 -1
  78. package/dist/components.esm.js +1229 -461
  79. package/dist/components.esm.js.map +1 -1
  80. package/dist/index.d.ts +10 -3
  81. package/dist/theme.d.ts +23 -28
  82. package/package.json +27 -4
  83. package/src/ActionGroup/ActionGroup.stories.tsx +47 -0
  84. package/src/ActionGroup/ActionGroup.test.tsx +83 -0
  85. package/src/ActionGroup/ActionGroup.tsx +32 -0
  86. package/src/ActionGroup/index.ts +1 -0
  87. package/src/Alert/Alert.stories.tsx +32 -0
  88. package/src/Alert/Alert.test.tsx +34 -23
  89. package/src/Alert/Alert.tsx +48 -24
  90. package/src/Badge/Badge.stories.tsx +38 -0
  91. package/src/Badge/Badge.test.tsx +14 -40
  92. package/src/Badge/Badge.tsx +31 -28
  93. package/src/Box.ts +2 -0
  94. package/src/Button/Button.stories.tsx +57 -0
  95. package/src/Button/Button.test.tsx +76 -13
  96. package/src/Button/Button.tsx +58 -23
  97. package/src/Card/Card.stories.tsx +41 -0
  98. package/src/Card/Card.test.tsx +71 -0
  99. package/src/Card/Card.tsx +48 -0
  100. package/src/Card/index.ts +1 -0
  101. package/src/Checkbox/Checkbox.stories.mdx +90 -112
  102. package/src/Checkbox/Checkbox.stories.tsx +78 -0
  103. package/src/Checkbox/Checkbox.test.tsx +139 -24
  104. package/src/Checkbox/Checkbox.tsx +95 -58
  105. package/src/Checkbox/CheckboxIcons.tsx +59 -0
  106. package/src/Column/Column.stories.tsx +33 -0
  107. package/src/Column/Column.test.tsx +15 -59
  108. package/src/Column/Column.tsx +21 -19
  109. package/src/Columns/Columns.stories.tsx +75 -0
  110. package/src/Columns/Columns.test.tsx +113 -0
  111. package/src/Columns/Columns.tsx +69 -0
  112. package/src/Columns/index.ts +1 -0
  113. package/src/Container/Container.stories.tsx +14 -0
  114. package/src/Container/Container.test.tsx +8 -49
  115. package/src/Container/Container.tsx +8 -19
  116. package/src/Dialog/Dialog.stories.tsx +88 -0
  117. package/src/Dialog/Dialog.test.tsx +158 -0
  118. package/src/Dialog/Dialog.tsx +130 -0
  119. package/src/Dialog/ModalDialog.tsx +76 -0
  120. package/src/Dialog/index.ts +1 -0
  121. package/src/Divider/Divider.stories.tsx +30 -0
  122. package/src/Divider/Divider.test.tsx +14 -6
  123. package/src/Divider/Divider.tsx +20 -13
  124. package/src/Field/Field.stories.tsx +110 -0
  125. package/src/Field/Field.test.tsx +75 -34
  126. package/src/Field/Field.tsx +50 -43
  127. package/src/Image/Image.stories.tsx +34 -0
  128. package/src/Image/Image.test.tsx +6 -3
  129. package/src/Image/Image.tsx +21 -15
  130. package/src/Inline/Inline.stories.tsx +39 -0
  131. package/src/Inline/Inline.test.tsx +99 -0
  132. package/src/Inline/Inline.tsx +38 -0
  133. package/src/Inline/index.ts +1 -0
  134. package/src/Input/Input.stories.tsx +54 -0
  135. package/src/Input/Input.test.tsx +9 -5
  136. package/src/Input/Input.tsx +21 -16
  137. package/src/Label/Label.stories.tsx +41 -0
  138. package/src/Label/Label.test.tsx +41 -6
  139. package/src/Label/Label.tsx +59 -18
  140. package/src/Link/Link.stories.tsx +35 -0
  141. package/src/Link/Link.test.tsx +52 -22
  142. package/src/Link/Link.tsx +40 -20
  143. package/src/Menu/Menu.stories.tsx +62 -0
  144. package/src/Menu/Menu.test.tsx +13 -7
  145. package/src/Menu/Menu.tsx +44 -38
  146. package/src/MenuItem/MenuItem.stories.tsx +30 -0
  147. package/src/MenuItem/MenuItem.test.tsx +23 -14
  148. package/src/MenuItem/MenuItem.tsx +29 -18
  149. package/src/Message/Message.stories.tsx +30 -0
  150. package/src/Message/Message.test.tsx +5 -2
  151. package/src/Message/Message.tsx +48 -40
  152. package/src/Provider/MarigoldProvider.test.tsx +136 -0
  153. package/src/Provider/MarigoldProvider.tsx +47 -0
  154. package/src/Provider/index.ts +4 -0
  155. package/src/Radio/Radio.stories.mdx +91 -94
  156. package/src/Radio/Radio.test.tsx +92 -16
  157. package/src/Radio/Radio.tsx +114 -50
  158. package/src/Radio/RadioIcons.tsx +39 -0
  159. package/src/Select/ListBox.tsx +40 -0
  160. package/src/Select/ListBoxSection.tsx +40 -0
  161. package/src/Select/Option.tsx +48 -0
  162. package/src/Select/Popover.tsx +50 -0
  163. package/src/Select/Select.stories.tsx +81 -0
  164. package/src/Select/Select.test.tsx +311 -43
  165. package/src/Select/Select.tsx +174 -28
  166. package/src/Slider/Slider.stories.tsx +24 -0
  167. package/src/Slider/Slider.test.tsx +11 -7
  168. package/src/Slider/Slider.tsx +30 -15
  169. package/src/Stack/Stack.stories.tsx +57 -0
  170. package/src/Stack/Stack.test.tsx +138 -0
  171. package/src/Stack/Stack.tsx +39 -0
  172. package/src/Stack/index.ts +1 -0
  173. package/src/Text/Text.stories.tsx +61 -0
  174. package/src/Text/Text.test.tsx +41 -36
  175. package/src/Text/Text.tsx +55 -29
  176. package/src/Textarea/Textarea.stories.tsx +64 -0
  177. package/src/Textarea/Textarea.test.tsx +41 -5
  178. package/src/Textarea/Textarea.tsx +57 -17
  179. package/src/ValidationMessage/ValidationMessage.stories.tsx +27 -0
  180. package/src/ValidationMessage/ValidationMessage.test.tsx +19 -14
  181. package/src/ValidationMessage/ValidationMessage.tsx +36 -21
  182. package/src/VisuallyHidden/VisuallyHidden.stories.tsx +19 -0
  183. package/src/VisuallyHidden/VisuallyHidden.test.tsx +10 -0
  184. package/src/VisuallyHidden/VisuallyHidden.tsx +1 -0
  185. package/src/VisuallyHidden/index.ts +1 -0
  186. package/src/index.ts +11 -3
  187. package/src/theme.ts +49 -28
  188. package/dist/Heading/Heading.d.ts +0 -5
  189. package/dist/Heading/index.d.ts +0 -1
  190. package/dist/Hidden/Hidden.d.ts +0 -6
  191. package/dist/Hidden/index.d.ts +0 -1
  192. package/dist/Svg/Svg.d.ts +0 -6
  193. package/dist/Svg/index.d.ts +0 -1
  194. package/src/Alert/Alert.stories.mdx +0 -49
  195. package/src/Badge/Badge.stories.mdx +0 -41
  196. package/src/Button/Button.stories.mdx +0 -155
  197. package/src/Column/Column.stories.mdx +0 -76
  198. package/src/Container/Container.stories.mdx +0 -42
  199. package/src/Divider/Divider.stories.mdx +0 -42
  200. package/src/Field/Field.stories.mdx +0 -57
  201. package/src/Heading/Heading.stories.mdx +0 -79
  202. package/src/Heading/Heading.test.tsx +0 -63
  203. package/src/Heading/Heading.tsx +0 -22
  204. package/src/Heading/index.ts +0 -1
  205. package/src/Hidden/Hidden.stories.mdx +0 -64
  206. package/src/Hidden/Hidden.test.tsx +0 -87
  207. package/src/Hidden/Hidden.tsx +0 -25
  208. package/src/Hidden/index.ts +0 -1
  209. package/src/Image/Image.stories.mdx +0 -40
  210. package/src/Input/Input.stories.mdx +0 -44
  211. package/src/Label/Label.stories.mdx +0 -34
  212. package/src/Link/Link.stories.mdx +0 -37
  213. package/src/Menu/Menu.stories.mdx +0 -47
  214. package/src/MenuItem/MenuItem.stories.mdx +0 -32
  215. package/src/Message/Message.stories.mdx +0 -43
  216. package/src/Select/Select.stories.mdx +0 -43
  217. package/src/Slider/Slider.stories.mdx +0 -57
  218. package/src/Svg/Svg.stories.mdx +0 -47
  219. package/src/Svg/Svg.test.tsx +0 -76
  220. package/src/Svg/Svg.tsx +0 -31
  221. package/src/Svg/index.ts +0 -1
  222. package/src/Text/Text.stories.mdx +0 -60
  223. package/src/Textarea/Textarea.stories.mdx +0 -34
  224. package/src/ValidationMessage/ValidationMessage.stories.mdx +0 -36
@@ -1,100 +1,97 @@
1
- import { Meta, Story, Preview } from '@storybook/addon-docs/blocks';
2
- import { Radio } from '@marigold/components';
1
+ import { ArgsTable, Canvas, Meta, Story } from '@storybook/addon-docs';
2
+ import { Radio } from './Radio';
3
3
  import { useState } from 'react';
4
4
 
5
- <Meta title="Components/Form/Radio" />
5
+ <Meta
6
+ title="Components/Radio"
7
+ parameters={{
8
+ actions: {
9
+ handles: ['click'],
10
+ },
11
+ }}
12
+ argTypes={{
13
+ id: {
14
+ control: {
15
+ type: 'text',
16
+ },
17
+ type: { required: true },
18
+ description: 'Unique ID',
19
+ },
20
+ variant: {
21
+ control: {
22
+ type: 'text',
23
+ },
24
+ description: 'Radio variant',
25
+ table: {
26
+ defaultValue: {
27
+ summary: 'default',
28
+ },
29
+ },
30
+ },
31
+ labelVariant: {
32
+ control: {
33
+ type: 'text',
34
+ },
35
+ description: 'Radio label variant',
36
+ table: {
37
+ defaultValue: {
38
+ summary: 'inline',
39
+ },
40
+ },
41
+ },
42
+ label: {
43
+ control: {
44
+ type: 'text',
45
+ },
46
+ description: 'Label',
47
+ },
48
+ required: {
49
+ control: {
50
+ type: 'boolean',
51
+ },
52
+ description: 'Require',
53
+ table: {
54
+ defaultValue: {
55
+ summary: false,
56
+ },
57
+ },
58
+ },
59
+ error: {
60
+ control: {
61
+ type: 'boolean',
62
+ },
63
+ description: 'Error',
64
+ table: {
65
+ defaultValue: {
66
+ summary: false,
67
+ },
68
+ },
69
+ },
70
+ errorMessage: {
71
+ control: {
72
+ type: 'text',
73
+ },
74
+ description: 'Error Message',
75
+ },
76
+ }}
77
+ />
6
78
 
7
- # Radio Button
79
+ # Radio
8
80
 
9
- ## Description
81
+ export const Template = ({ onChange, checked, ...args }) => {
82
+ const [isChecked, setChecked] = useState(false);
83
+ return (
84
+ <Radio
85
+ onChange={() => setChecked(!isChecked)}
86
+ checked={isChecked}
87
+ label="Radio Label"
88
+ {...args}
89
+ />
90
+ );
91
+ };
10
92
 
11
- With the Radio Button component you can add a HTML `<input>` element with `type="radio"` to your form.
12
- The element uses always the themeSection with the name: `form` in your given theme. The variant in your section can be added with the variant prop. The default variant is `radio`.
93
+ <Canvas>
94
+ <Story name="Default">{Template.bind({})}</Story>
95
+ </Canvas>
13
96
 
14
- ## Properties
15
-
16
- | Property | Type | Default |
17
- | :-------------------- | :-------- | :------ |
18
- | `id` | `string` | |
19
- | `variant` (optional) | `string` | `radio` |
20
- | `label` (optional) | `string` | |
21
- | `required` (optional) | `boolean` | |
22
-
23
- ## Import
24
-
25
- ```tsx
26
- import { Radio } from '@marigold/components';
27
- ```
28
-
29
- ## Usage
30
-
31
- ### Radio standard labeled
32
-
33
- <Preview>
34
- <Story name="RadioOne">
35
- {() => {
36
- const [state, setState] = React.useState('Mastercard');
37
- const onChange = changeEvent => {
38
- setState(changeEvent.currentTarget.value);
39
- };
40
- return (
41
- <div>
42
- <Radio
43
- id="mc"
44
- name="payment"
45
- onChange={onChange}
46
- checked={state === 'Mastercard'}
47
- value="Mastercard"
48
- label="Mastercard"
49
- />
50
- <br />
51
- <Radio
52
- id="vi"
53
- name="payment"
54
- onChange={onChange}
55
- checked={state === 'Visa'}
56
- value="Visa"
57
- label="Visa"
58
- />
59
- <br />
60
- <Radio
61
- id="ae"
62
- name="payment"
63
- onChange={onChange}
64
- checked={state === 'AmericanExpress'}
65
- value="AmericanExpress"
66
- label="AmericanExpress"
67
- />
68
- </div>
69
- );
70
- }}
71
- </Story>
72
- </Preview>
73
-
74
- ### Radio checked and disabled
75
-
76
- <Preview>
77
- <Story name="RadioTwo">
78
- <div>
79
- <Radio id="radio" checked />
80
- <Radio id="radio" disabled />
81
- <Radio id="radio" checked disabled />
82
- </div>
83
- </Story>
84
- </Preview>
85
-
86
- ### Radio with required label
87
-
88
- <Preview>
89
- <Story name="RadioThree">
90
- <div>
91
- <Radio
92
- checked
93
- id="required"
94
- value="required"
95
- required
96
- label="This label is required"
97
- />
98
- </div>
99
- </Story>
100
- </Preview>
97
+ <ArgsTable story="Default" />
@@ -1,43 +1,119 @@
1
1
  import React from 'react';
2
2
  import { render, screen } from '@testing-library/react';
3
- import { Radio } from '@marigold/components';
3
+ import { Radio } from './Radio';
4
+ import { ThemeProvider } from '@marigold/system';
5
+
6
+ const theme = {
7
+ space: {
8
+ none: 0,
9
+ small: 2,
10
+ },
11
+ radio: {
12
+ __default: {
13
+ m: 'small',
14
+ },
15
+ },
16
+ label: {
17
+ above: {
18
+ fontSize: '8px',
19
+ },
20
+ inline: {
21
+ fontSize: '14px',
22
+ },
23
+ },
24
+ };
25
+
26
+ test('supports default labelVariant', () => {
27
+ render(
28
+ <ThemeProvider theme={theme}>
29
+ <Radio id="test" title="checkbox" label="label" />
30
+ </ThemeProvider>
31
+ );
32
+
33
+ const label = screen.getByText(/label/);
34
+ expect(label).toHaveStyle(`font-size: 14px`);
35
+ });
36
+
37
+ test('supports other labelVariant than default', () => {
38
+ render(
39
+ <ThemeProvider theme={theme}>
40
+ <Radio id="test" title="checkbox" label="label" labelVariant="above" />
41
+ </ThemeProvider>
42
+ );
43
+
44
+ const label = screen.getByText(/label/);
45
+ expect(label).toHaveStyle(`font-size: 8px`);
46
+ });
4
47
 
5
48
  test('supports label prop', () => {
6
49
  render(<Radio label="Test" id="test" title="radio" />);
7
- const radio = screen.getByText(/Test/);
8
50
 
9
- expect(radio).toBeDefined();
51
+ const radioLabel = screen.getByText(/Test/);
52
+ expect(radioLabel).toBeDefined();
10
53
  });
11
54
 
12
- test('supports required prop an renders required icon', () => {
55
+ test('supports required prop and renders required icon', () => {
13
56
  render(<Radio label="Test" id="test" required title="radio" />);
14
- const radio = screen.getByText(/Test/);
15
57
 
16
- expect(radio).toContainHTML('path d="M10.8');
58
+ const label = screen.getByText(/Test/);
59
+ expect(label.nextSibling).toContainHTML('path d="M10.8');
17
60
  });
18
61
 
19
62
  test('supports default type', () => {
20
63
  render(<Radio id="radio" title="radio" />);
21
- const radio = screen.getByTitle(/radio/);
22
64
 
65
+ const radio = screen.getByTitle(/radio/);
23
66
  expect(radio.getAttribute('type')).toEqual('radio');
24
67
  });
25
68
 
26
69
  test('renders <input> element', () => {
27
70
  render(<Radio id="radio" title="radio" />);
28
- const radio = screen.getByTitle(/radio/);
29
71
 
72
+ const radio = screen.getByTitle(/radio/);
30
73
  expect(radio instanceof HTMLInputElement).toBeTruthy();
31
74
  });
32
75
 
33
- test('renders <SVG> CircleUnchecked element', () => {
34
- render(<Radio id="radio" label="Test" />);
35
- const radio = screen.getByText(/Test/);
36
- expect(radio).toContainHTML('path d="M5.62507');
76
+ test('supports disabled prop', () => {
77
+ render(
78
+ <ThemeProvider theme={theme}>
79
+ <Radio id="test" title="radio" label="label" disabled />
80
+ </ThemeProvider>
81
+ );
82
+
83
+ const radio = screen.getByTitle(/radio/);
84
+ expect(radio).toHaveAttribute('disabled');
85
+ });
86
+
87
+ test('supports error and errorMessage prop', () => {
88
+ render(
89
+ <ThemeProvider theme={theme}>
90
+ <Radio id="test" title="radio" label="test" error errorMessage="error" />
91
+ </ThemeProvider>
92
+ );
93
+
94
+ const errorMessage = screen.getByText(/error/);
95
+ expect(errorMessage).toBeDefined();
96
+ });
97
+
98
+ test('supports checked radio', () => {
99
+ render(
100
+ <ThemeProvider theme={theme}>
101
+ <Radio id="test" title="radio" onChange={() => {}} checked />
102
+ </ThemeProvider>
103
+ );
104
+
105
+ const radio = screen.getByTitle(/radio/);
106
+ expect(radio).toBeDefined();
37
107
  });
38
108
 
39
- test('renders <SVG> CircleChecked element', () => {
40
- render(<Radio id="radio" checked onChange={() => {}} label="Test" />);
41
- const radio = screen.getByText(/Test/);
42
- expect(radio).toContainHTML('path d="M12');
109
+ test('supports checked and disabled radio', () => {
110
+ render(
111
+ <ThemeProvider theme={theme}>
112
+ <Radio id="test" title="radio" onChange={() => {}} checked disabled />
113
+ </ThemeProvider>
114
+ );
115
+
116
+ const radio = screen.getByTitle(/radio/);
117
+ expect(radio).toBeDefined();
118
+ expect(radio).toHaveAttribute('disabled');
43
119
  });
@@ -1,64 +1,128 @@
1
1
  import React from 'react';
2
- import { useStyles, system } from '@marigold/system';
3
- import { CircleUnchecked, CircleChecked, Required } from '@marigold/icons';
4
- import { Label } from '@marigold/components';
2
+ import { ComponentProps } from '@marigold/types';
3
+ import { Exclamation } from '@marigold/icons';
5
4
 
6
- type RadioProps = {
7
- id: string;
5
+ import { Box } from '../Box';
6
+ import { Label } from '../Label';
7
+ import { ValidationMessage } from '../ValidationMessage';
8
+
9
+ import { RadioChecked, RadioUnchecked } from './RadioIcons';
10
+
11
+ // Theme Extension
12
+ // ---------------
13
+ export interface RadioThemeExtension<Value> {
14
+ radio?: {
15
+ [key: string]: Value;
16
+ };
17
+ }
18
+
19
+ // Radio Icon
20
+ // ---------------
21
+ type RadioIconProps = {
8
22
  variant?: string;
9
- label?: string;
10
- required?: boolean;
23
+ checked?: boolean;
24
+ disabled?: boolean;
25
+ error?: boolean;
26
+ children?: never;
11
27
  };
12
28
 
13
- export const Radio = system<RadioProps, 'input'>(
14
- ({ id, variant = 'radio', label, required, className, ...props }) => {
15
- const radioStyle = useStyles({
16
- position: 'absolute',
17
- opacity: 0,
18
- zIndex: -1,
19
- width: 1,
20
- height: 1,
21
- overflow: 'hidden',
22
- });
23
-
24
- const radioIconStyle = useStyles(
25
- {
26
- variant: `form.${variant}`,
27
- ariaHidden: 'true',
28
- mr: 2,
29
- verticalAlign: 'middle',
30
- ':hover': { cursor: 'pointer' },
31
- 'input:disabled ~ &': {
32
- color: 'muted',
33
- cursor: 'not-allowed',
34
- },
35
- },
36
- className
29
+ const RadioIcon: React.FC<RadioIconProps> = ({
30
+ variant,
31
+ checked,
32
+ disabled,
33
+ error,
34
+ }) => {
35
+ if (checked) {
36
+ return (
37
+ <Box as={RadioChecked} variant={`radio.${variant}`} disabled={disabled} />
37
38
  );
39
+ }
40
+ return (
41
+ <Box
42
+ as={RadioUnchecked}
43
+ variant={`radio.${variant}`}
44
+ disabled={disabled}
45
+ error={error}
46
+ />
47
+ );
48
+ };
38
49
 
39
- const radio = (
40
- <div className={useStyles({ display: 'inline-block' })}>
41
- <input type="radio" id={id} className={radioStyle} {...props} />
42
- {props.checked ? (
43
- <CircleChecked className={radioIconStyle} />
44
- ) : (
45
- <CircleUnchecked className={radioIconStyle} />
46
- )}
47
- </div>
48
- );
50
+ // Radio Input
51
+ // ---------------
52
+ type RadioInputProps = {
53
+ variant?: string;
54
+ error?: boolean;
55
+ } & ComponentProps<'input'>;
56
+
57
+ const RadioInput: React.FC<RadioInputProps> = ({
58
+ className,
59
+ variant = '',
60
+ error,
61
+ ...props
62
+ }) => (
63
+ <Box display="inline-block" className={className}>
64
+ <Box
65
+ as="input"
66
+ type="radio"
67
+ css={{
68
+ position: 'absolute',
69
+ opacity: 0,
70
+ zIndex: -1,
71
+ width: 1,
72
+ height: 1,
73
+ overflow: 'hidden',
74
+ }}
75
+ {...props}
76
+ />
77
+ <RadioIcon
78
+ checked={props.checked}
79
+ variant={variant}
80
+ disabled={props.disabled}
81
+ error={error}
82
+ />
83
+ </Box>
84
+ );
49
85
 
86
+ // Radio
87
+ // ---------------
88
+ export type RadioProps = {
89
+ id: string;
90
+ label?: string;
91
+ required?: boolean;
92
+ labelVariant?: string;
93
+ error?: boolean;
94
+ errorMessage?: string;
95
+ } & RadioInputProps;
96
+
97
+ export const Radio: React.FC<RadioProps> = ({
98
+ label,
99
+ required,
100
+ labelVariant = 'inline',
101
+ error,
102
+ errorMessage,
103
+ ...props
104
+ }) => {
105
+ if (label) {
50
106
  return (
51
107
  <>
52
- {label ? (
53
- <Label htmlFor={id}>
54
- {radio}
55
- {label}
56
- {required ? <Required size={16} /> : ''}
57
- </Label>
58
- ) : (
59
- <>{radio}</>
108
+ <Label
109
+ htmlFor={props.id}
110
+ required={required}
111
+ variant={labelVariant}
112
+ color={props.disabled ? 'disabled' : 'text'}
113
+ >
114
+ <Box as={RadioInput} pr="8px" error={error} {...props} />
115
+ {label}
116
+ </Label>
117
+ {error && errorMessage && (
118
+ <ValidationMessage>
119
+ <Exclamation size={16} />
120
+ {errorMessage}
121
+ </ValidationMessage>
60
122
  )}
61
123
  </>
62
124
  );
63
125
  }
64
- );
126
+
127
+ return <RadioInput {...props} />;
128
+ };
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ import { SVG } from '@marigold/system';
3
+
4
+ import { Box } from '../Box';
5
+
6
+ export const RadioChecked = ({ disabled = false, ...props }) => (
7
+ <SVG width="16" height="32" viewBox="0 0 16 32" fill="none" {...props}>
8
+ <Box
9
+ as="circle"
10
+ cx="8"
11
+ cy="16"
12
+ r="7.5"
13
+ variant={disabled ? 'radio.checked.disabled' : 'radio.checked'}
14
+ />
15
+ <Box as="circle" cx="8" cy="16" r="3" variant="radio.checked.circle" />
16
+ </SVG>
17
+ );
18
+
19
+ export const RadioUnchecked = ({
20
+ disabled = false,
21
+ error = false,
22
+ ...props
23
+ }) => (
24
+ <SVG width="16" height="32" viewBox="0 0 16 32" fill="none" {...props}>
25
+ <Box
26
+ as="circle"
27
+ cx="8"
28
+ cy="16"
29
+ r="7.5"
30
+ variant={
31
+ disabled
32
+ ? 'radio.unchecked.disabled'
33
+ : error
34
+ ? 'radio.unchecked.error'
35
+ : 'radio.unchecked'
36
+ }
37
+ />
38
+ </SVG>
39
+ );
@@ -0,0 +1,40 @@
1
+ import React, { useRef } from 'react';
2
+ import { useListBox } from '@react-aria/listbox';
3
+ import type { AriaListBoxOptions } from '@react-aria/listbox';
4
+ import type { ListState } from '@react-stately/list';
5
+
6
+ import { Box } from '../Box';
7
+ import { Option } from './Option';
8
+ import { ListBoxSection } from './ListBoxSection';
9
+
10
+ interface ListBoxProps extends AriaListBoxOptions<unknown> {
11
+ state: ListState<unknown>;
12
+ error?: boolean;
13
+ }
14
+
15
+ export const ListBox = (props: ListBoxProps) => {
16
+ const ref = useRef<HTMLUListElement>(null);
17
+ const { state, error } = props;
18
+ const { listBoxProps } = useListBox(props, state, ref);
19
+
20
+ return (
21
+ <Box
22
+ as="ul"
23
+ p="none"
24
+ css={{
25
+ listStyle: 'none',
26
+ }}
27
+ {...listBoxProps}
28
+ variant={error ? 'select.listbox.error' : 'select.listbox'}
29
+ ref={ref}
30
+ >
31
+ {[...state.collection].map(item =>
32
+ item.type === 'section' ? (
33
+ <ListBoxSection key={item.key} section={item} state={state} />
34
+ ) : (
35
+ <Option key={item.key} item={item} state={state} />
36
+ )
37
+ )}
38
+ </Box>
39
+ );
40
+ };
@@ -0,0 +1,40 @@
1
+ import React from 'react';
2
+ import { useListBoxSection } from '@react-aria/listbox';
3
+ import type { ListState } from '@react-stately/list';
4
+ import type { Node } from '@react-types/shared';
5
+
6
+ import { Box } from '../Box';
7
+ import { Option } from './Option';
8
+
9
+ interface SectionProps {
10
+ section: Node<unknown>;
11
+ state: ListState<unknown>;
12
+ }
13
+
14
+ export const ListBoxSection = ({ section, state }: SectionProps) => {
15
+ const { itemProps, headingProps, groupProps } = useListBoxSection({
16
+ heading: section.rendered,
17
+ 'aria-label': section['aria-label'],
18
+ });
19
+
20
+ return (
21
+ <Box
22
+ as="li"
23
+ {...itemProps}
24
+ css={{
25
+ cursor: 'not-allowed',
26
+ }}
27
+ >
28
+ {section.rendered && (
29
+ <Box as="span" {...headingProps} variant={'select.section'}>
30
+ {section.rendered}
31
+ </Box>
32
+ )}
33
+ <Box as="ul" {...groupProps}>
34
+ {[...section.childNodes].map(node => (
35
+ <Option key={node.key} item={node} state={state} />
36
+ ))}
37
+ </Box>
38
+ </Box>
39
+ );
40
+ };
@@ -0,0 +1,48 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import type { ListState } from '@react-stately/list';
3
+ import type { Node } from '@react-types/shared';
4
+ import { useOption } from '@react-aria/listbox';
5
+
6
+ import { Box } from '../Box';
7
+
8
+ interface OptionProps {
9
+ item: Node<unknown>;
10
+ state: ListState<unknown>;
11
+ }
12
+
13
+ export const Option = ({ item, state }: OptionProps) => {
14
+ const ref = useRef<HTMLLIElement>(null);
15
+ const [disabled, setDisabled] = useState(false);
16
+ const { optionProps, isSelected } = useOption(
17
+ {
18
+ key: item.key,
19
+ },
20
+ state,
21
+ ref
22
+ );
23
+
24
+ useEffect(() => {
25
+ for (const key of state.disabledKeys.values()) {
26
+ if (key === item.key) {
27
+ setDisabled(true);
28
+ }
29
+ }
30
+ }, [state.disabledKeys, item.key]);
31
+
32
+ return (
33
+ <Box
34
+ as="li"
35
+ {...optionProps}
36
+ ref={ref}
37
+ variant={
38
+ isSelected
39
+ ? 'select.option.selected'
40
+ : disabled
41
+ ? 'select.option.disabled'
42
+ : 'select.option'
43
+ }
44
+ >
45
+ {item.rendered}
46
+ </Box>
47
+ );
48
+ };