@marigold/components 0.0.1 → 0.2.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 (194) hide show
  1. package/dist/ActionGroup/ActionGroup.d.ts +9 -0
  2. package/dist/ActionGroup/index.d.ts +1 -0
  3. package/dist/Alert/Alert.d.ts +20 -2
  4. package/dist/Badge/Badge.d.ts +8 -0
  5. package/dist/Badge/index.d.ts +1 -0
  6. package/dist/Box/Box.d.ts +47 -0
  7. package/dist/Box/index.d.ts +1 -0
  8. package/dist/Button/Button.d.ts +4 -3
  9. package/dist/Card/Card.d.ts +9 -0
  10. package/dist/Card/index.d.ts +1 -0
  11. package/dist/Checkbox/Checkbox.d.ts +14 -2
  12. package/dist/Checkbox/CheckboxIcons.d.ts +9 -0
  13. package/dist/Column/Column.d.ts +8 -0
  14. package/dist/Column/index.d.ts +1 -0
  15. package/dist/Columns/Columns.d.ts +10 -0
  16. package/dist/Columns/index.d.ts +1 -0
  17. package/dist/Container/Container.d.ts +6 -0
  18. package/dist/Container/index.d.ts +1 -0
  19. package/dist/Dialog/Dialog.d.ts +14 -0
  20. package/dist/Dialog/ModalDialog.d.ts +5 -0
  21. package/dist/Dialog/index.d.ts +1 -0
  22. package/dist/Divider/Divider.d.ts +7 -0
  23. package/dist/Divider/index.d.ts +1 -0
  24. package/dist/Field/Field.d.ts +11 -0
  25. package/dist/Field/index.d.ts +1 -0
  26. package/dist/Heading/Heading.d.ts +7 -5
  27. package/dist/Hidden/Hidden.d.ts +5 -0
  28. package/dist/Hidden/index.d.ts +1 -0
  29. package/dist/Image/Image.d.ts +7 -0
  30. package/dist/Image/index.d.ts +1 -0
  31. package/dist/Input/Input.d.ts +6 -0
  32. package/dist/Input/index.d.ts +1 -0
  33. package/dist/Label/Label.d.ts +8 -5
  34. package/dist/Link/Link.d.ts +7 -3
  35. package/dist/Menu/Menu.d.ts +12 -0
  36. package/dist/Menu/index.d.ts +1 -0
  37. package/dist/MenuItem/MenuItem.d.ts +7 -0
  38. package/dist/MenuItem/index.d.ts +1 -0
  39. package/dist/Message/Message.d.ts +7 -0
  40. package/dist/Message/index.d.ts +1 -0
  41. package/dist/Provider/MarigoldProvider.d.ts +3 -0
  42. package/dist/Provider/index.d.ts +3 -0
  43. package/dist/Radio/Radio.d.ts +14 -2
  44. package/dist/Radio/RadioIcons.d.ts +9 -0
  45. package/dist/Select/ListBox.d.ts +8 -0
  46. package/dist/Select/ListBoxSection.d.ts +8 -0
  47. package/dist/Select/Option.d.ts +8 -0
  48. package/dist/Select/Popover.d.ts +9 -0
  49. package/dist/Select/Select.d.ts +13 -3
  50. package/dist/Slider/Slider.d.ts +6 -3
  51. package/dist/Stack/Stack.d.ts +7 -0
  52. package/dist/Stack/index.d.ts +1 -0
  53. package/dist/Text/Text.d.ts +12 -3
  54. package/dist/Textarea/Textarea.d.ts +11 -3
  55. package/dist/ValidationMessage/ValidationMessage.d.ts +6 -0
  56. package/dist/ValidationMessage/index.d.ts +1 -0
  57. package/dist/components.cjs.development.js +1308 -195
  58. package/dist/components.cjs.development.js.map +1 -1
  59. package/dist/components.cjs.production.min.js +1 -1
  60. package/dist/components.cjs.production.min.js.map +1 -1
  61. package/dist/components.esm.js +1267 -185
  62. package/dist/components.esm.js.map +1 -1
  63. package/dist/index.d.ts +21 -4
  64. package/dist/theme.d.ts +24 -4
  65. package/package.json +24 -4
  66. package/src/ActionGroup/ActionGroup.stories.mdx +62 -0
  67. package/src/ActionGroup/ActionGroup.test.tsx +83 -0
  68. package/src/ActionGroup/ActionGroup.tsx +43 -0
  69. package/src/ActionGroup/index.ts +1 -0
  70. package/src/Alert/Alert.stories.mdx +30 -42
  71. package/src/Alert/Alert.test.tsx +37 -22
  72. package/src/Alert/Alert.tsx +31 -21
  73. package/src/Badge/Badge.stories.mdx +57 -0
  74. package/src/Badge/Badge.test.tsx +61 -0
  75. package/src/Badge/Badge.tsx +25 -0
  76. package/src/Badge/index.ts +1 -0
  77. package/src/Box/Box.stories.mdx +334 -0
  78. package/src/Box/Box.test.tsx +133 -0
  79. package/src/Box/Box.tsx +165 -0
  80. package/src/Box/index.ts +1 -0
  81. package/src/Button/Button.stories.mdx +58 -134
  82. package/src/Button/Button.test.tsx +65 -23
  83. package/src/Button/Button.tsx +48 -14
  84. package/src/Card/Card.stories.mdx +49 -0
  85. package/src/Card/Card.test.tsx +66 -0
  86. package/src/Card/Card.tsx +36 -0
  87. package/src/Card/index.ts +1 -0
  88. package/src/Checkbox/Checkbox.stories.mdx +79 -101
  89. package/src/Checkbox/Checkbox.test.tsx +73 -32
  90. package/src/Checkbox/Checkbox.tsx +114 -35
  91. package/src/Checkbox/CheckboxIcons.tsx +49 -0
  92. package/src/Column/Column.stories.mdx +49 -0
  93. package/src/Column/Column.test.tsx +32 -0
  94. package/src/Column/Column.tsx +27 -0
  95. package/src/Column/index.ts +1 -0
  96. package/src/Columns/Columns.stories.mdx +65 -0
  97. package/src/Columns/Columns.test.tsx +102 -0
  98. package/src/Columns/Columns.tsx +69 -0
  99. package/src/Columns/index.ts +1 -0
  100. package/src/Container/Container.stories.mdx +19 -0
  101. package/src/Container/Container.test.tsx +26 -0
  102. package/src/Container/Container.tsx +13 -0
  103. package/src/Container/index.ts +1 -0
  104. package/src/Dialog/Dialog.stories.mdx +73 -0
  105. package/src/Dialog/Dialog.test.tsx +87 -0
  106. package/src/Dialog/Dialog.tsx +84 -0
  107. package/src/Dialog/ModalDialog.tsx +47 -0
  108. package/src/Dialog/index.ts +1 -0
  109. package/src/Divider/Divider.stories.mdx +37 -0
  110. package/src/Divider/Divider.test.tsx +63 -0
  111. package/src/Divider/Divider.tsx +13 -0
  112. package/src/Divider/index.ts +1 -0
  113. package/src/Field/Field.stories.mdx +97 -0
  114. package/src/Field/Field.test.tsx +80 -0
  115. package/src/Field/Field.tsx +54 -0
  116. package/src/Field/index.ts +1 -0
  117. package/src/Heading/Heading.stories.mdx +36 -76
  118. package/src/Heading/Heading.test.tsx +31 -17
  119. package/src/Heading/Heading.tsx +15 -12
  120. package/src/Hidden/Hidden.stories.mdx +39 -0
  121. package/src/Hidden/Hidden.test.tsx +24 -0
  122. package/src/Hidden/Hidden.tsx +16 -0
  123. package/src/Hidden/index.ts +1 -0
  124. package/src/Image/Image.stories.mdx +36 -0
  125. package/src/Image/Image.test.tsx +70 -0
  126. package/src/Image/Image.tsx +13 -0
  127. package/src/Image/index.ts +1 -0
  128. package/src/Input/Input.stories.mdx +61 -0
  129. package/src/Input/Input.test.tsx +70 -0
  130. package/src/Input/Input.tsx +13 -0
  131. package/src/Input/index.ts +1 -0
  132. package/src/Label/Label.stories.mdx +50 -34
  133. package/src/Label/Label.test.tsx +45 -16
  134. package/src/Label/Label.tsx +26 -17
  135. package/src/Link/Link.stories.mdx +40 -31
  136. package/src/Link/Link.test.tsx +53 -28
  137. package/src/Link/Link.tsx +32 -14
  138. package/src/Menu/Menu.stories.mdx +81 -0
  139. package/src/Menu/Menu.test.tsx +79 -0
  140. package/src/Menu/Menu.tsx +41 -0
  141. package/src/Menu/index.ts +1 -0
  142. package/src/MenuItem/MenuItem.stories.mdx +37 -0
  143. package/src/MenuItem/MenuItem.test.tsx +63 -0
  144. package/src/MenuItem/MenuItem.tsx +23 -0
  145. package/src/MenuItem/index.ts +1 -0
  146. package/src/Message/Message.stories.mdx +44 -0
  147. package/src/Message/Message.test.tsx +87 -0
  148. package/src/Message/Message.tsx +43 -0
  149. package/src/Message/index.ts +1 -0
  150. package/src/Provider/MarigoldProvider.test.tsx +126 -0
  151. package/src/Provider/MarigoldProvider.tsx +29 -0
  152. package/src/Provider/index.ts +3 -0
  153. package/src/Radio/Radio.stories.mdx +80 -83
  154. package/src/Radio/Radio.test.tsx +63 -22
  155. package/src/Radio/Radio.tsx +110 -35
  156. package/src/Radio/RadioIcons.tsx +39 -0
  157. package/src/Select/ListBox.tsx +39 -0
  158. package/src/Select/ListBoxSection.tsx +40 -0
  159. package/src/Select/Option.tsx +48 -0
  160. package/src/Select/Popover.tsx +50 -0
  161. package/src/Select/Select.stories.mdx +72 -37
  162. package/src/Select/Select.test.tsx +271 -28
  163. package/src/Select/Select.tsx +158 -23
  164. package/src/Slider/Slider.stories.mdx +26 -54
  165. package/src/Slider/Slider.test.tsx +13 -13
  166. package/src/Slider/Slider.tsx +20 -18
  167. package/src/Stack/Stack.stories.mdx +51 -0
  168. package/src/Stack/Stack.test.tsx +129 -0
  169. package/src/Stack/Stack.tsx +39 -0
  170. package/src/Stack/index.ts +1 -0
  171. package/src/Text/Text.stories.mdx +53 -47
  172. package/src/Text/Text.test.tsx +55 -15
  173. package/src/Text/Text.tsx +44 -10
  174. package/src/Textarea/Textarea.stories.mdx +68 -21
  175. package/src/Textarea/Textarea.test.tsx +47 -16
  176. package/src/Textarea/Textarea.tsx +46 -14
  177. package/src/ValidationMessage/ValidationMessage.stories.mdx +36 -0
  178. package/src/ValidationMessage/ValidationMessage.test.tsx +63 -0
  179. package/src/ValidationMessage/ValidationMessage.tsx +28 -0
  180. package/src/ValidationMessage/index.ts +1 -0
  181. package/src/index.ts +22 -4
  182. package/src/theme.ts +24 -4
  183. package/dist/Svg/Svg.d.ts +0 -5
  184. package/dist/Svg/index.d.ts +0 -1
  185. package/dist/TextInput/TextInput.d.ts +0 -3
  186. package/dist/TextInput/index.d.ts +0 -1
  187. package/src/Svg/Svg.stories.mdx +0 -47
  188. package/src/Svg/Svg.test.tsx +0 -58
  189. package/src/Svg/Svg.tsx +0 -25
  190. package/src/Svg/index.ts +0 -1
  191. package/src/TextInput/TextInput.stories.mdx +0 -37
  192. package/src/TextInput/TextInput.test.tsx +0 -71
  193. package/src/TextInput/TextInput.tsx +0 -21
  194. package/src/TextInput/index.ts +0 -1
@@ -1,68 +1,311 @@
1
1
  import React from 'react';
2
- import { render, screen } from '@testing-library/react';
3
- import { MarigoldProvider } from '@marigold/system';
4
- import { Select } from '@marigold/components';
2
+ import { fireEvent, render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+
5
+ import { Item, MarigoldProvider, Section } from '..';
6
+ import { Select } from './Select';
5
7
 
6
8
  const theme = {
7
- form: {
9
+ button: {
8
10
  select: {
9
11
  fontFamily: 'Inter',
10
- },
11
- other: {
12
- fontFamily: 'Oswald',
12
+ errorOpened: {
13
+ color: 'red',
14
+ },
13
15
  },
14
16
  },
15
17
  };
16
18
 
17
- test('supports default variant and themeSection', () => {
19
+ test('supports button select variant', () => {
18
20
  render(
19
21
  <MarigoldProvider theme={theme}>
20
- <Select title="select">
21
- <option>1</option>
22
+ <Select label="MyLabel" data-testid="selectId">
23
+ <Item>1</Item>
22
24
  </Select>
23
25
  </MarigoldProvider>
24
26
  );
25
- const select = screen.getByTitle(/select/);
26
-
27
+ const select = screen.getByTestId('selectId');
28
+ expect(select).toBeDefined();
27
29
  expect(select).toHaveStyle(`font-family: Inter`);
28
30
  });
29
31
 
30
- test('accepts other variant than default', () => {
32
+ test('accepts custom styles prop className', () => {
31
33
  render(
32
34
  <MarigoldProvider theme={theme}>
33
- <Select title="select" variant="other">
34
- <option>1</option>
35
+ <Select
36
+ label="MyLabel"
37
+ className="custom-class-name"
38
+ data-testid="selectId"
39
+ >
40
+ <Item>1</Item>
35
41
  </Select>
36
42
  </MarigoldProvider>
37
43
  );
38
- const select = screen.getByTitle(/select/);
44
+ const select = screen.getByTestId('selectId');
45
+ expect(select.className).toMatch('custom-class-name');
46
+ });
39
47
 
40
- expect(select).toHaveStyle(`font-family: Oswald`);
48
+ test('supports label with htmlFor prop', () => {
49
+ render(
50
+ <MarigoldProvider theme={theme}>
51
+ <Select label="MyLabel">
52
+ <Item>1</Item>
53
+ </Select>
54
+ </MarigoldProvider>
55
+ );
56
+ const selectLabel = screen.getAllByText(/MyLabel/);
57
+ expect(selectLabel[0]).toHaveAttribute('for');
41
58
  });
42
59
 
43
- test('renders correct HTML element', () => {
60
+ test('supports disabled prop', () => {
44
61
  render(
45
62
  <MarigoldProvider theme={theme}>
46
- <Select title="select">
47
- <option>1</option>
63
+ <Select label="MyLabel" data-testid="selectId" disabled>
64
+ <Item>1</Item>
48
65
  </Select>
49
66
  </MarigoldProvider>
50
67
  );
51
- const select = screen.getByTitle(/select/);
68
+ const select = screen.getByTestId('selectId');
69
+ expect(select).toHaveAttribute('disabled');
70
+ fireEvent.click(select);
71
+ expect(select).toHaveAttribute('aria-expanded', 'false');
72
+ });
52
73
 
53
- expect(select instanceof HTMLSelectElement).toBeTruthy();
74
+ test('supports placeholder prop', () => {
75
+ render(
76
+ <MarigoldProvider theme={theme}>
77
+ <Select label="MyLabel" placeholder="placeholder" data-testid="selectId">
78
+ <Item>1</Item>
79
+ </Select>
80
+ </MarigoldProvider>
81
+ );
82
+ const button = screen.getByTestId('selectId');
83
+ expect(button).toHaveTextContent(/placeholder/);
54
84
  });
55
85
 
56
- test('variant styles cannot be overridden with CSS prop', () => {
86
+ test('supports required prop', () => {
57
87
  render(
58
88
  <MarigoldProvider theme={theme}>
59
- <Select title="select" css={{ fontFamily: 'Oswald Regular' }}>
60
- <option>1</option>
89
+ <Select label="MyLabel" required data-testid="selectId">
90
+ <Item>1</Item>
61
91
  </Select>
62
92
  </MarigoldProvider>
63
93
  );
64
- const select = screen.getByTitle(/select/);
94
+ const selectLabel = screen.getAllByText(/MyLabel/);
95
+ expect(selectLabel[0]).toContainHTML('path d="M10.8 3.84003');
96
+ });
65
97
 
66
- expect(select).not.toHaveStyle(`font-family: Oswald Regular`);
67
- expect(select).toHaveStyle(`font-family: Inter`);
98
+ test('supports error and errorMessage prop', () => {
99
+ render(
100
+ <MarigoldProvider theme={theme}>
101
+ <Select label="MyLabel" error errorMessage="error" data-testid="selectId">
102
+ <Item>1</Item>
103
+ </Select>
104
+ </MarigoldProvider>
105
+ );
106
+ const validationMessage = screen.getAllByText(/error/);
107
+ expect(validationMessage).toBeDefined();
108
+ });
109
+
110
+ test('supports width prop', () => {
111
+ render(
112
+ <MarigoldProvider theme={theme}>
113
+ <Select label="MyLabel" width="120px" data-testid="selectId">
114
+ <Item>1</Item>
115
+ </Select>
116
+ </MarigoldProvider>
117
+ );
118
+ const select = screen.getByTestId('selectId');
119
+
120
+ expect(select.parentElement).toHaveStyle(`width: 120px`);
121
+ });
122
+
123
+ test('option list opens when element is clicked', () => {
124
+ render(
125
+ <MarigoldProvider theme={theme}>
126
+ <Select label="MyLabel" data-testid="selectId">
127
+ <Item>Red</Item>
128
+ </Select>
129
+ </MarigoldProvider>
130
+ );
131
+ const button = screen.getByTestId('selectId');
132
+ expect(button).toHaveAttribute('aria-expanded', 'false');
133
+
134
+ fireEvent.click(button);
135
+
136
+ // more than one item found because of the HiddenSelect component
137
+ const items = screen.getByRole('listbox');
138
+ expect(items).toBeVisible();
139
+ expect(button).toHaveAttribute('aria-expanded', 'true');
140
+ });
141
+
142
+ test('option list opens when element is clicked and theres an error', () => {
143
+ render(
144
+ <MarigoldProvider theme={theme}>
145
+ <Select label="MyLabel" error errorMessage="error" data-testid="selectId">
146
+ <Item>Red</Item>
147
+ </Select>
148
+ </MarigoldProvider>
149
+ );
150
+ const button = screen.getByTestId('selectId');
151
+ expect(button).toHaveAttribute('aria-expanded', 'false');
152
+
153
+ fireEvent.click(button);
154
+
155
+ const items = screen.getByRole('listbox');
156
+ expect(items).toBeVisible();
157
+ expect(button).toHaveAttribute('aria-expanded', 'true');
158
+ expect(button).toHaveStyle(`color: red`);
159
+ });
160
+
161
+ test('supports click and select an option', () => {
162
+ render(
163
+ <MarigoldProvider theme={theme}>
164
+ <Select label="MyLabel" data-testid="selectId">
165
+ <Item>Red</Item>
166
+ </Select>
167
+ </MarigoldProvider>
168
+ );
169
+ const button = screen.getByTestId('selectId');
170
+ expect(button).toHaveAttribute('aria-expanded', 'false');
171
+
172
+ fireEvent.click(button);
173
+ expect(button).toHaveAttribute('aria-expanded', 'true');
174
+ const items = screen.getAllByText(/Red/);
175
+ expect(items[1]).toBeVisible();
176
+ expect(items[1]).toHaveAttribute('aria-selected', 'false');
177
+
178
+ fireEvent.click(items[1]);
179
+ expect(button).toHaveTextContent('Red');
180
+
181
+ fireEvent.click(button);
182
+
183
+ // after selecting one item there are three elements with item text
184
+ const newItems = screen.getAllByText(/Red/);
185
+ expect(newItems[2]).toHaveAttribute('aria-selected', 'true');
186
+ });
187
+
188
+ test('popup closes after an option is selected', () => {
189
+ render(
190
+ <MarigoldProvider theme={theme}>
191
+ <Select label="MyLabel" data-testid="selectId">
192
+ <Item>Red</Item>
193
+ </Select>
194
+ </MarigoldProvider>
195
+ );
196
+ const button = screen.getByTestId('selectId');
197
+ expect(button).toHaveAttribute('aria-expanded', 'false');
198
+
199
+ fireEvent.click(button);
200
+ expect(button).toHaveAttribute('aria-expanded', 'true');
201
+ const items = screen.getAllByText(/Red/);
202
+ expect(items[1]).toBeVisible();
203
+
204
+ fireEvent.click(items[1]);
205
+ expect(button).toHaveTextContent('Red');
206
+ expect(items[1]).not.toBeVisible();
207
+ });
208
+
209
+ test('dismiss popup by clicking escape', () => {
210
+ render(
211
+ <MarigoldProvider theme={theme}>
212
+ <Select label="MyLabel" data-testid="selectId">
213
+ <Item>Red</Item>
214
+ </Select>
215
+ </MarigoldProvider>
216
+ );
217
+ const selectButton = screen.getByTestId('selectId');
218
+ fireEvent.click(selectButton);
219
+ expect(selectButton).toHaveAttribute('aria-expanded', 'true');
220
+ userEvent.type(selectButton, '{esc}');
221
+ expect(selectButton).toHaveAttribute('aria-expanded', 'false');
222
+ });
223
+
224
+ test('allow users to dismiss the popup with hidden dismiss button', () => {
225
+ render(
226
+ <MarigoldProvider theme={theme}>
227
+ <Select label="MyLabel" data-testid="selectId">
228
+ <Item>Red</Item>
229
+ </Select>
230
+ </MarigoldProvider>
231
+ );
232
+ const selectButton = screen.getByTestId('selectId');
233
+ fireEvent.click(selectButton);
234
+
235
+ const dismissButton = screen.getByLabelText(/Dismiss/);
236
+ expect(dismissButton).toBeDefined();
237
+
238
+ fireEvent.click(dismissButton);
239
+ expect(selectButton).toHaveAttribute('aria-expanded', 'false');
240
+ });
241
+
242
+ test('supports default selectedKey prop', () => {
243
+ render(
244
+ <MarigoldProvider theme={theme}>
245
+ <Select label="MyLabel" data-testid="selectId" defaultSelectedKey="Red">
246
+ <Item key="Red">Red</Item>
247
+ <Item key="Orange">Orange</Item>
248
+ </Select>
249
+ </MarigoldProvider>
250
+ );
251
+ const button = screen.getByTestId('selectId');
252
+ expect(button).toHaveTextContent('Red');
253
+ });
254
+
255
+ test('supports change default selectedKey', () => {
256
+ render(
257
+ <MarigoldProvider theme={theme}>
258
+ <Select label="MyLabel" data-testid="selectId" defaultSelectedKey="Red">
259
+ <Item key="Red">Red</Item>
260
+ <Item key="Orange">Orange</Item>
261
+ </Select>
262
+ </MarigoldProvider>
263
+ );
264
+ const button = screen.getByTestId('selectId');
265
+ expect(button).toHaveTextContent('Red');
266
+
267
+ fireEvent.click(button);
268
+ const items = screen.getAllByText(/Red/);
269
+ fireEvent.click(items[1]);
270
+
271
+ expect(button).toHaveTextContent('Red');
272
+ });
273
+
274
+ test('supports disabled item prop', () => {
275
+ render(
276
+ <MarigoldProvider theme={theme}>
277
+ <Select label="MyLabel" data-testid="selectId" disabledKeys={['Red']}>
278
+ <Item key="Red">Red</Item>
279
+ <Item key="Orange">Orange</Item>
280
+ </Select>
281
+ </MarigoldProvider>
282
+ );
283
+ const button = screen.getByTestId('selectId');
284
+ fireEvent.click(button);
285
+ const redItem = screen.getAllByText(/Red/);
286
+ fireEvent.click(redItem[1]);
287
+ expect(button).toHaveTextContent('Select an option');
288
+
289
+ const orangeItem = screen.getAllByText(/Orange/);
290
+ fireEvent.click(orangeItem[1]);
291
+ expect(button).toHaveTextContent('Orange');
292
+ });
293
+
294
+ test('supports section with items', () => {
295
+ render(
296
+ <MarigoldProvider theme={theme}>
297
+ <Select label="Section" data-testid="selectId">
298
+ <Section title="Color">
299
+ <Item>Red</Item>
300
+ </Section>
301
+ </Select>
302
+ </MarigoldProvider>
303
+ );
304
+ const button = screen.getByTestId('selectId');
305
+ fireEvent.click(button);
306
+
307
+ const items = screen.getAllByText(/Red/);
308
+ expect(items[1]).toBeVisible();
309
+ const sections = screen.getAllByText(/Color/);
310
+ expect(sections[0]).toBeVisible();
68
311
  });
@@ -1,28 +1,163 @@
1
- import React from 'react';
2
- import { Box, system } from '@marigold/system';
3
- import { ArrowDown } from '@marigold/icons';
1
+ import React, { Ref, RefObject, useRef } from 'react';
2
+ import { useSelectState } from '@react-stately/select';
3
+ import { useButton } from '@react-aria/button';
4
+ import { mergeProps } from '@react-aria/utils';
5
+ import { useFocusRing } from '@react-aria/focus';
6
+ import { HiddenSelect, useSelect } from '@react-aria/select';
7
+ import type { AriaSelectProps } from '@react-types/select';
8
+ import { useOverlayTriggerState } from '@react-stately/overlays';
9
+ import { useOverlayTrigger, useOverlayPosition } from '@react-aria/overlays';
10
+ import { SingleSelection } from '@react-types/shared';
4
11
 
5
- type SelectProps = {};
12
+ import { ComponentProps } from '@marigold/types';
13
+ import { ArrowDown, ArrowUp, Exclamation, Required } from '@marigold/icons';
14
+ import { ResponsiveStyleValue } from '@marigold/system';
6
15
 
7
- export const Select = system<SelectProps, 'select'>(
8
- ({ variant = 'select', ref, ...props }) => {
9
- return (
10
- <Box css={{ display: 'flex' }}>
16
+ import { Box } from '../Box';
17
+ import { Label } from '../Label';
18
+ import { ValidationMessage } from '../ValidationMessage';
19
+ import { ListBox } from './ListBox';
20
+ import { Popover } from './Popover';
21
+
22
+ export type SelectProps = {
23
+ placeholder?: string;
24
+ disabled?: boolean;
25
+ required?: boolean;
26
+ width?: ResponsiveStyleValue<number | string>;
27
+ error?: boolean;
28
+ errorMessage?: string;
29
+ } & ComponentProps<'select'> &
30
+ AriaSelectProps<object> &
31
+ SingleSelection;
32
+
33
+ export const Select = ({
34
+ placeholder = 'Select an option',
35
+ disabled,
36
+ required,
37
+ error,
38
+ errorMessage,
39
+ width,
40
+ className,
41
+ ...props
42
+ }: SelectProps) => {
43
+ const state = useSelectState(props);
44
+ const overlayTriggerState = useOverlayTriggerState({});
45
+ const triggerRef = useRef<HTMLElement>() as RefObject<HTMLElement>;
46
+ const overlayRef = useRef<HTMLDivElement>();
47
+
48
+ // Get props for the overlay
49
+ const { overlayProps } = useOverlayTrigger(
50
+ { type: 'listbox' },
51
+ overlayTriggerState,
52
+ triggerRef
53
+ );
54
+ // Get popover positioning props relative to the trigger
55
+ const { overlayProps: positionProps } = useOverlayPosition({
56
+ targetRef: triggerRef,
57
+ overlayRef: overlayRef as RefObject<HTMLElement>,
58
+ placement: 'bottom',
59
+ shouldFlip: false,
60
+ isOpen: state.isOpen,
61
+ onClose: state.close,
62
+ });
63
+ // Get props for child elements from useSelect
64
+ const { labelProps, triggerProps, valueProps, menuProps } = useSelect(
65
+ props,
66
+ state,
67
+ triggerRef
68
+ );
69
+ // Get props for the button based on the trigger props from useSelect
70
+ const { buttonProps } = useButton(triggerProps, triggerRef);
71
+
72
+ const { focusProps } = useFocusRing();
73
+
74
+ return (
75
+ <Box position="relative" display="inline-block" width={width && width}>
76
+ {props.label && (
77
+ <Box>
78
+ <Label
79
+ {...labelProps}
80
+ htmlFor={labelProps.id}
81
+ variant={disabled ? 'disabled' : 'above'}
82
+ >
83
+ {required ? (
84
+ <Box as="span" display="inline-flex" alignItems="center">
85
+ {props.label}
86
+ <Box as={Required} size={16} css={{ color: 'error' }} />
87
+ </Box>
88
+ ) : (
89
+ props.label
90
+ )}
91
+ </Label>
92
+ </Box>
93
+ )}
94
+ <HiddenSelect
95
+ state={state}
96
+ triggerRef={triggerRef}
97
+ label={props.label}
98
+ name={props.name}
99
+ isDisabled={disabled}
100
+ />
101
+ <Box
102
+ as="button"
103
+ {...mergeProps(buttonProps, focusProps)}
104
+ ref={triggerRef as RefObject<HTMLButtonElement>}
105
+ variant={
106
+ error && state.isOpen && !disabled
107
+ ? 'button.select.errorOpened'
108
+ : error
109
+ ? 'button.select.error'
110
+ : state.isOpen && !disabled
111
+ ? 'button.select.open'
112
+ : 'button.select'
113
+ }
114
+ disabled={disabled}
115
+ className={className}
116
+ >
117
+ <Box
118
+ as="span"
119
+ {...valueProps}
120
+ variant={disabled ? 'select.disabled' : 'select'}
121
+ >
122
+ {state.selectedItem ? state.selectedItem.rendered : placeholder}
123
+ </Box>
124
+ {state.isOpen && !disabled ? (
125
+ <Box
126
+ as={ArrowUp}
127
+ size={16}
128
+ css={{ fill: disabled ? 'disabled' : 'text' }}
129
+ />
130
+ ) : (
131
+ <Box
132
+ as={ArrowDown}
133
+ size={16}
134
+ css={{ fill: disabled ? 'disabled' : 'text' }}
135
+ />
136
+ )}
137
+ </Box>
138
+ {state.isOpen && !disabled && (
11
139
  <Box
12
- as="select"
13
- ref={ref}
14
- themeSection="form"
15
- variant={variant}
16
- {...props}
17
- />
18
- <ArrowDown
19
- ml={'-28px'}
140
+ as={Popover}
141
+ {...overlayProps}
142
+ {...positionProps}
20
143
  css={{
21
- alignSelf: 'center',
22
- pointerEvents: 'none',
144
+ width: width
145
+ ? width
146
+ : triggerRef.current && triggerRef.current.offsetWidth + 'px',
23
147
  }}
24
- />
25
- </Box>
26
- );
27
- }
28
- );
148
+ ref={overlayRef as Ref<HTMLDivElement>}
149
+ isOpen={state.isOpen}
150
+ onClose={state.close}
151
+ >
152
+ <ListBox error={error} {...menuProps} state={state} />
153
+ </Box>
154
+ )}
155
+ {error && errorMessage && (
156
+ <Box as="span" display="inline-flex" alignItems="center">
157
+ <Box as={Exclamation} size={16} css={{ color: 'error' }} />
158
+ <ValidationMessage>{errorMessage}</ValidationMessage>
159
+ </Box>
160
+ )}
161
+ </Box>
162
+ );
163
+ };
@@ -1,59 +1,31 @@
1
- import { Meta, Story, Preview } from '@storybook/addon-docs/blocks';
2
- import { Label, Slider } from '@marigold/components';
3
-
4
- <Meta title="Components/Form/Slider" />
1
+ import { ArgsTable, Canvas, Meta, Story } from '@storybook/addon-docs';
2
+ import { Slider } from './Slider';
3
+
4
+ <Meta
5
+ title="Components/Slider"
6
+ argTypes={{
7
+ variant: {
8
+ control: {
9
+ type: 'text',
10
+ },
11
+ description: 'Style',
12
+ table: {
13
+ defaultValue: {
14
+ summary: 'slider',
15
+ },
16
+ },
17
+ },
18
+ }}
19
+ />
5
20
 
6
21
  # Slider
7
22
 
8
- ## Description
9
-
10
- With the Slider component you can add a HTML `<input>` element with `type="range"` to your form.
11
- 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 `slider`.
12
- Specific css style prop can also be added.
13
-
14
- ## Properties
15
-
16
- | Property | Type | Default |
17
- | :-------- | :---------- | :--------- |
18
- | `variant` | `string` | `'slider'` |
19
- | `css` | `css props` | |
20
-
21
- ## Import
22
-
23
- ```tsx
24
- import { Slider } from '@marigold/components';
25
- ```
26
-
27
- ## Usage
28
-
29
- ### Slider with default range and fix value
30
-
31
- <Preview>
32
- <Story name="SliderOne">
33
- <div>
34
- <Label htmlFor="vol">
35
- Volume:
36
- <br />
37
- <Slider id="vol" name="vol" min="0" max="50" />
38
- <br />
39
- <Slider id="vol" name="vol" min="0" max="50" value="25" />
40
- </Label>
41
- </div>
42
- </Story>
43
- </Preview>
23
+ export const Template = args => (
24
+ <Slider name="vol" min="0" max="50" {...args} />
25
+ );
44
26
 
45
- ### Slider with steps
27
+ <Canvas>
28
+ <Story name="Default">{Template.bind({})}</Story>
29
+ </Canvas>
46
30
 
47
- <Preview>
48
- <Story name="SliderTwo">
49
- <div>
50
- <Label htmlFor="vol">
51
- Volume:
52
- <br />
53
- <Slider id="vol" name="vol" min="0" max="100" step="10"></Slider>
54
- <br />
55
- <Slider id="vol" name="vol" min="20" max="80" step="5"></Slider>
56
- </Label>
57
- </div>
58
- </Story>
59
- </Preview>
31
+ <ArgsTable story="Default" />
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { render, screen } from '@testing-library/react';
3
- import { MarigoldProvider } from '@marigold/system';
4
- import { Slider } from '@marigold/components';
3
+ import { ThemeProvider } from '@marigold/system';
4
+ import { Slider } from './Slider';
5
5
 
6
6
  const theme = {
7
7
  form: {
@@ -16,9 +16,9 @@ const theme = {
16
16
 
17
17
  test('supports default variant and themeSection', () => {
18
18
  render(
19
- <MarigoldProvider theme={theme}>
19
+ <ThemeProvider theme={theme}>
20
20
  <Slider title="slider" />
21
- </MarigoldProvider>
21
+ </ThemeProvider>
22
22
  );
23
23
  const slider = screen.getByTitle(/slider/);
24
24
 
@@ -27,9 +27,9 @@ test('supports default variant and themeSection', () => {
27
27
 
28
28
  test('accepts other variant than default', () => {
29
29
  render(
30
- <MarigoldProvider theme={theme}>
30
+ <ThemeProvider theme={theme}>
31
31
  <Slider variant="range" title="slider" />
32
- </MarigoldProvider>
32
+ </ThemeProvider>
33
33
  );
34
34
  const slider = screen.getByTitle(/slider/);
35
35
 
@@ -38,22 +38,22 @@ test('accepts other variant than default', () => {
38
38
 
39
39
  test('renders <input> element by default', () => {
40
40
  render(
41
- <MarigoldProvider theme={theme}>
41
+ <ThemeProvider theme={theme}>
42
42
  <Slider title="slider" />
43
- </MarigoldProvider>
43
+ </ThemeProvider>
44
44
  );
45
45
  const slider = screen.getByTitle(/slider/);
46
46
 
47
47
  expect(slider instanceof HTMLInputElement).toBeTruthy();
48
48
  });
49
49
 
50
- test('variant styles cannot be overridden with CSS prop', () => {
50
+ test('accepts custom styles prop className', () => {
51
51
  render(
52
- <MarigoldProvider theme={theme}>
53
- <Slider css={{ fontFamily: 'Arial' }} title="slider" />
54
- </MarigoldProvider>
52
+ <ThemeProvider theme={theme}>
53
+ <Slider className="custom-class-name" title="slider" />
54
+ </ThemeProvider>
55
55
  );
56
56
  const slider = screen.getByTitle(/slider/);
57
57
 
58
- expect(slider).not.toHaveStyle(`font-family: Arial`);
58
+ expect(slider.className).toMatch('custom-class-name');
59
59
  });