@marigold/components 0.1.0 → 0.3.2

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 (220) hide show
  1. package/CHANGELOG.md +180 -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 +22 -1
  6. package/dist/Alert/Alert.stories.d.ts +5 -0
  7. package/dist/Badge/Badge.d.ts +5 -0
  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 -6
  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 +12 -4
  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 +3 -1
  19. package/dist/Column/Column.stories.d.ts +5 -0
  20. package/dist/Columns/Columns.d.ts +2 -2
  21. package/dist/Columns/Columns.stories.d.ts +5 -0
  22. package/dist/Container/Container.stories.d.ts +5 -0
  23. package/dist/Dialog/Dialog.d.ts +12 -2
  24. package/dist/Dialog/Dialog.stories.d.ts +5 -0
  25. package/dist/Dialog/ModalDialog.d.ts +8 -0
  26. package/dist/Divider/Divider.d.ts +7 -3
  27. package/dist/Divider/Divider.stories.d.ts +5 -0
  28. package/dist/Field/Field.d.ts +5 -1
  29. package/dist/Field/Field.stories.d.ts +5 -0
  30. package/dist/Image/Image.d.ts +5 -0
  31. package/dist/Image/Image.stories.d.ts +5 -0
  32. package/dist/Inline/Inline.d.ts +7 -0
  33. package/dist/Inline/Inline.stories.d.ts +5 -0
  34. package/dist/Inline/index.d.ts +1 -0
  35. package/dist/Input/Input.d.ts +5 -0
  36. package/dist/Input/Input.stories.d.ts +5 -0
  37. package/dist/Label/Label.d.ts +14 -2
  38. package/dist/Label/Label.stories.d.ts +5 -0
  39. package/dist/Link/Link.d.ts +3 -0
  40. package/dist/Link/Link.stories.d.ts +5 -0
  41. package/dist/Menu/Menu.d.ts +3 -0
  42. package/dist/Menu/Menu.stories.d.ts +5 -0
  43. package/dist/MenuItem/MenuItem.d.ts +5 -0
  44. package/dist/MenuItem/MenuItem.stories.d.ts +5 -0
  45. package/dist/Message/Message.d.ts +5 -0
  46. package/dist/Message/Message.stories.d.ts +5 -0
  47. package/dist/Provider/MarigoldProvider.d.ts +11 -0
  48. package/dist/Provider/index.d.ts +3 -0
  49. package/dist/Radio/Radio.d.ts +11 -4
  50. package/dist/Radio/Radio.stories.d.ts +5 -0
  51. package/dist/Radio/RadioIcon.d.ts +9 -0
  52. package/dist/Select/ListBox.d.ts +9 -0
  53. package/dist/Select/ListBoxSection.d.ts +9 -0
  54. package/dist/Select/Option.d.ts +9 -0
  55. package/dist/Select/Popover.d.ts +9 -0
  56. package/dist/Select/Select.d.ts +25 -4
  57. package/dist/Select/Select.stories.d.ts +5 -0
  58. package/dist/Slider/Slider.d.ts +5 -0
  59. package/dist/Slider/Slider.stories.d.ts +5 -0
  60. package/dist/Stack/Stack.d.ts +1 -3
  61. package/dist/Stack/Stack.stories.d.ts +5 -0
  62. package/dist/Text/Text.d.ts +5 -0
  63. package/dist/Text/Text.stories.d.ts +5 -0
  64. package/dist/Textarea/Textarea.d.ts +7 -1
  65. package/dist/Textarea/Textarea.stories.d.ts +5 -0
  66. package/dist/ValidationMessage/ValidationMessage.d.ts +5 -0
  67. package/dist/ValidationMessage/ValidationMessage.stories.d.ts +5 -0
  68. package/dist/VisuallyHidden/VisuallyHidden.d.ts +1 -0
  69. package/dist/VisuallyHidden/VisuallyHidden.stories.d.ts +5 -0
  70. package/dist/VisuallyHidden/index.d.ts +1 -0
  71. package/dist/components.cjs.development.js +1068 -601
  72. package/dist/components.cjs.development.js.map +1 -1
  73. package/dist/components.cjs.production.min.js +1 -1
  74. package/dist/components.cjs.production.min.js.map +1 -1
  75. package/dist/components.esm.js +1003 -579
  76. package/dist/components.esm.js.map +1 -1
  77. package/dist/index.d.ts +6 -2
  78. package/dist/theme.d.ts +23 -48
  79. package/package.json +22 -3
  80. package/src/ActionGroup/ActionGroup.stories.tsx +47 -0
  81. package/src/ActionGroup/ActionGroup.test.tsx +83 -0
  82. package/src/ActionGroup/ActionGroup.tsx +32 -0
  83. package/src/ActionGroup/index.ts +1 -0
  84. package/src/Alert/Alert.stories.tsx +32 -0
  85. package/src/Alert/Alert.test.tsx +5 -2
  86. package/src/Alert/Alert.tsx +27 -34
  87. package/src/Badge/Badge.stories.tsx +38 -0
  88. package/src/Badge/Badge.test.tsx +12 -16
  89. package/src/Badge/Badge.tsx +14 -3
  90. package/src/Box.ts +2 -0
  91. package/src/Button/Button.stories.tsx +57 -0
  92. package/src/Button/Button.test.tsx +76 -13
  93. package/src/Button/Button.tsx +61 -18
  94. package/src/Card/Card.stories.tsx +41 -0
  95. package/src/Card/Card.test.tsx +71 -0
  96. package/src/Card/Card.tsx +48 -0
  97. package/src/Card/index.ts +1 -0
  98. package/src/Checkbox/Checkbox.stories.tsx +78 -0
  99. package/src/Checkbox/Checkbox.test.tsx +138 -23
  100. package/src/Checkbox/Checkbox.tsx +67 -68
  101. package/src/Checkbox/CheckboxIcons.tsx +59 -0
  102. package/src/Column/Column.stories.tsx +33 -0
  103. package/src/Column/Column.test.tsx +8 -0
  104. package/src/Column/Column.tsx +12 -2
  105. package/src/Columns/Columns.stories.tsx +75 -0
  106. package/src/Columns/Columns.test.tsx +34 -23
  107. package/src/Columns/Columns.tsx +30 -30
  108. package/src/Container/Container.stories.tsx +14 -0
  109. package/src/Dialog/Dialog.stories.tsx +88 -0
  110. package/src/Dialog/Dialog.test.tsx +129 -18
  111. package/src/Dialog/Dialog.tsx +113 -15
  112. package/src/Dialog/ModalDialog.tsx +76 -0
  113. package/src/Divider/Divider.stories.tsx +30 -0
  114. package/src/Divider/Divider.test.tsx +32 -23
  115. package/src/Divider/Divider.tsx +27 -7
  116. package/src/Field/Field.stories.tsx +110 -0
  117. package/src/Field/Field.test.tsx +74 -33
  118. package/src/Field/Field.tsx +27 -20
  119. package/src/Image/Image.stories.tsx +34 -0
  120. package/src/Image/Image.test.tsx +4 -1
  121. package/src/Image/Image.tsx +13 -1
  122. package/src/Inline/Inline.stories.tsx +39 -0
  123. package/src/Inline/Inline.test.tsx +99 -0
  124. package/src/Inline/Inline.tsx +38 -0
  125. package/src/Inline/index.ts +1 -0
  126. package/src/Input/Input.stories.tsx +54 -0
  127. package/src/Input/Input.test.tsx +7 -3
  128. package/src/Input/Input.tsx +13 -1
  129. package/src/Label/Label.stories.tsx +41 -0
  130. package/src/Label/Label.test.tsx +40 -5
  131. package/src/Label/Label.tsx +54 -8
  132. package/src/Link/Link.stories.tsx +35 -0
  133. package/src/Link/Link.test.tsx +6 -2
  134. package/src/Link/Link.tsx +12 -1
  135. package/src/Menu/Menu.stories.tsx +62 -0
  136. package/src/Menu/Menu.test.tsx +11 -6
  137. package/src/Menu/Menu.tsx +22 -14
  138. package/src/MenuItem/MenuItem.stories.tsx +30 -0
  139. package/src/MenuItem/MenuItem.test.tsx +22 -13
  140. package/src/MenuItem/MenuItem.tsx +19 -10
  141. package/src/Message/Message.stories.tsx +30 -0
  142. package/src/Message/Message.test.tsx +4 -1
  143. package/src/Message/Message.tsx +18 -14
  144. package/src/Provider/MarigoldProvider.test.tsx +136 -0
  145. package/src/Provider/MarigoldProvider.tsx +47 -0
  146. package/src/Provider/index.ts +4 -0
  147. package/src/Radio/Radio.stories.tsx +78 -0
  148. package/src/Radio/Radio.test.tsx +129 -18
  149. package/src/Radio/Radio.tsx +62 -71
  150. package/src/Radio/RadioIcon.tsx +49 -0
  151. package/src/Select/ListBox.tsx +40 -0
  152. package/src/Select/ListBoxSection.tsx +40 -0
  153. package/src/Select/Option.tsx +48 -0
  154. package/src/Select/Popover.tsx +50 -0
  155. package/src/Select/Select.stories.tsx +81 -0
  156. package/src/Select/Select.test.tsx +317 -35
  157. package/src/Select/Select.tsx +162 -18
  158. package/src/Slider/Slider.stories.tsx +24 -0
  159. package/src/Slider/Slider.test.tsx +10 -6
  160. package/src/Slider/Slider.tsx +25 -13
  161. package/src/Stack/Stack.stories.tsx +57 -0
  162. package/src/Stack/Stack.test.tsx +93 -65
  163. package/src/Stack/Stack.tsx +25 -41
  164. package/src/Text/Text.stories.tsx +61 -0
  165. package/src/Text/Text.test.tsx +2 -2
  166. package/src/Text/Text.tsx +25 -14
  167. package/src/Textarea/Textarea.stories.tsx +64 -0
  168. package/src/Textarea/Textarea.test.tsx +11 -8
  169. package/src/Textarea/Textarea.tsx +41 -38
  170. package/src/ValidationMessage/ValidationMessage.stories.tsx +27 -0
  171. package/src/ValidationMessage/ValidationMessage.test.tsx +9 -4
  172. package/src/ValidationMessage/ValidationMessage.tsx +23 -12
  173. package/src/VisuallyHidden/VisuallyHidden.stories.tsx +19 -0
  174. package/src/VisuallyHidden/VisuallyHidden.test.tsx +10 -0
  175. package/src/VisuallyHidden/VisuallyHidden.tsx +1 -0
  176. package/src/VisuallyHidden/index.ts +1 -0
  177. package/src/index.ts +7 -2
  178. package/src/theme.ts +49 -48
  179. package/dist/Box/Box.d.ts +0 -46
  180. package/dist/Box/index.d.ts +0 -1
  181. package/dist/Heading/Heading.d.ts +0 -7
  182. package/dist/Heading/index.d.ts +0 -1
  183. package/dist/Hidden/Hidden.d.ts +0 -5
  184. package/dist/Hidden/index.d.ts +0 -1
  185. package/src/Alert/Alert.stories.mdx +0 -45
  186. package/src/Badge/Badge.stories.mdx +0 -43
  187. package/src/Box/Box.stories.mdx +0 -38
  188. package/src/Box/Box.test.tsx +0 -133
  189. package/src/Box/Box.tsx +0 -157
  190. package/src/Box/index.ts +0 -1
  191. package/src/Button/Button.stories.mdx +0 -176
  192. package/src/Checkbox/Checkbox.stories.mdx +0 -81
  193. package/src/Column/Column.stories.mdx +0 -74
  194. package/src/Columns/Columns.stories.mdx +0 -247
  195. package/src/Container/Container.stories.mdx +0 -36
  196. package/src/Dialog/Dialog.stories.mdx +0 -64
  197. package/src/Divider/Divider.stories.mdx +0 -43
  198. package/src/Field/Field.stories.mdx +0 -57
  199. package/src/Heading/Heading.stories.mdx +0 -91
  200. package/src/Heading/Heading.test.tsx +0 -77
  201. package/src/Heading/Heading.tsx +0 -19
  202. package/src/Heading/index.ts +0 -1
  203. package/src/Hidden/Hidden.stories.mdx +0 -64
  204. package/src/Hidden/Hidden.test.tsx +0 -24
  205. package/src/Hidden/Hidden.tsx +0 -16
  206. package/src/Hidden/index.ts +0 -1
  207. package/src/Image/Image.stories.mdx +0 -40
  208. package/src/Input/Input.stories.mdx +0 -45
  209. package/src/Label/Label.stories.mdx +0 -34
  210. package/src/Link/Link.stories.mdx +0 -38
  211. package/src/Menu/Menu.stories.mdx +0 -49
  212. package/src/MenuItem/MenuItem.stories.mdx +0 -32
  213. package/src/Message/Message.stories.mdx +0 -44
  214. package/src/Radio/Radio.stories.mdx +0 -100
  215. package/src/Select/Select.stories.mdx +0 -44
  216. package/src/Slider/Slider.stories.mdx +0 -58
  217. package/src/Stack/Stack.stories.mdx +0 -105
  218. package/src/Text/Text.stories.mdx +0 -60
  219. package/src/Textarea/Textarea.stories.mdx +0 -65
  220. package/src/ValidationMessage/ValidationMessage.stories.mdx +0 -36
@@ -0,0 +1,76 @@
1
+ import React, { RefObject } from 'react';
2
+ import {
3
+ useOverlay,
4
+ usePreventScroll,
5
+ useModal,
6
+ OverlayProps,
7
+ } from '@react-aria/overlays';
8
+ import { useDialog } from '@react-aria/dialog';
9
+ import { FocusScope } from '@react-aria/focus';
10
+ import { AriaDialogProps } from '@react-types/dialog';
11
+
12
+ import { Box } from '../Box';
13
+
14
+ // Props
15
+ // ---------------
16
+ export type ModalDialogProps = {
17
+ variant?: string;
18
+ backdropVariant?: string;
19
+ } & OverlayProps &
20
+ AriaDialogProps;
21
+
22
+ // Component
23
+ // ---------------
24
+ export const ModalDialog: React.FC<ModalDialogProps> = ({
25
+ variant,
26
+ backdropVariant = 'backdrop',
27
+ children,
28
+ ...props
29
+ }) => {
30
+ const { isDismissable, isOpen, onClose, ...restProps } = props;
31
+
32
+ // Handle interacting outside the dialog and pressing
33
+ // the Escape key to close the modal.
34
+ const ref = React.useRef<HTMLElement>() as RefObject<HTMLElement>;
35
+ const { overlayProps, underlayProps } = useOverlay(
36
+ { isDismissable, isOpen, onClose },
37
+ ref
38
+ );
39
+
40
+ // Prevent scrolling while the modal is open, and hide content
41
+ // outside the modal from screen readers.
42
+ usePreventScroll();
43
+
44
+ const { modalProps } = useModal();
45
+ const { dialogProps } = useDialog(props, ref);
46
+
47
+ return (
48
+ <Box
49
+ __baseCSS={{
50
+ display: 'grid',
51
+ placeItems: 'center',
52
+ position: 'fixed',
53
+ zIndex: 100,
54
+ top: 0,
55
+ left: 0,
56
+ bottom: 0,
57
+ right: 0,
58
+ }}
59
+ variant={`dialog.${backdropVariant}`}
60
+ {...underlayProps}
61
+ >
62
+ <FocusScope contain restoreFocus autoFocus>
63
+ <Box
64
+ {...overlayProps}
65
+ {...dialogProps}
66
+ {...modalProps}
67
+ ref={ref}
68
+ variant={variant ? `dialog.${variant}` : `dialog`}
69
+ {...restProps}
70
+ >
71
+ {children}
72
+ </Box>
73
+ </FocusScope>
74
+ </Box>
75
+ );
76
+ };
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import type { Meta, ComponentStory } from '@storybook/react';
3
+ import { Divider } from './Divider';
4
+ import { Text } from '../Text';
5
+
6
+ export default {
7
+ title: 'Components/Divider',
8
+ argTypes: {
9
+ variant: {
10
+ control: {
11
+ type: 'select',
12
+ },
13
+ options: ['__default', 'bold'],
14
+ description: 'Thick or thin line',
15
+ table: {
16
+ defaultValue: {
17
+ summary: '__default',
18
+ },
19
+ },
20
+ },
21
+ },
22
+ } as Meta;
23
+
24
+ export const Basic: ComponentStory<typeof Divider> = args => (
25
+ <>
26
+ <Text>Above</Text>
27
+ <Divider {...args} />
28
+ <Text>Below</Text>
29
+ </>
30
+ );
@@ -4,60 +4,69 @@ import { ThemeProvider } from '@marigold/system';
4
4
  import { Divider } from './Divider';
5
5
 
6
6
  const theme = {
7
+ space: {
8
+ none: 0,
9
+ small: 2,
10
+ medium: 4,
11
+ },
12
+ colors: {
13
+ text: 'hotpink',
14
+ },
7
15
  divider: {
8
- regular: {
9
- border: 0,
10
- borderBottom: '1px solid',
11
- margin: '2px',
16
+ __default: {
17
+ margin: 'small',
12
18
  },
13
19
  bold: {
14
- border: 0,
15
- borderBottom: '2px solid',
16
- margin: '2px',
20
+ margin: 'medium',
17
21
  },
18
22
  },
19
23
  };
20
24
 
21
- test('supports default variant and themeSection', () => {
25
+ test('has base styles', () => {
22
26
  render(
23
27
  <ThemeProvider theme={theme}>
24
- <Divider title="divider" />
28
+ <Divider data-testid="divider" />
25
29
  </ThemeProvider>
26
30
  );
27
- const divider = screen.getByTitle(/divider/);
31
+ const divider = screen.getByTestId(/divider/);
28
32
 
29
- expect(divider).toHaveStyle(`borderBottom: 1px solid`);
33
+ // __baseCSS
34
+ expect(divider).toHaveStyle(`background: hotpink`);
35
+ expect(divider).toHaveStyle(`width: 100%`);
36
+ expect(divider).toHaveStyle(`height: 1px`);
37
+ // margin from default variant
38
+ expect(divider).toHaveStyle(`margin: 2px`);
30
39
  });
31
40
 
32
- test('accepts other variant than default', () => {
41
+ test('supports default variant', () => {
33
42
  render(
34
43
  <ThemeProvider theme={theme}>
35
- <Divider variant="bold" title="divider" />
44
+ <Divider data-testid="divider" />
36
45
  </ThemeProvider>
37
46
  );
38
- const divider = screen.getByTitle(/divider/);
47
+ const divider = screen.getByTestId(/divider/);
39
48
 
40
- expect(divider).toHaveStyle(`borderBottom: 2px solid`);
49
+ expect(divider).toHaveStyle(`margin: 2px`);
41
50
  });
42
51
 
43
- test('renders correct HTML element', () => {
52
+ test('accepts other variant than default', () => {
44
53
  render(
45
54
  <ThemeProvider theme={theme}>
46
- <Divider title="divider" />
55
+ <Divider variant="bold" data-testid="divider" />
47
56
  </ThemeProvider>
48
57
  );
49
- const divider = screen.getByTitle(/divider/);
58
+ const divider = screen.getByTestId(/divider/);
50
59
 
51
- expect(divider instanceof HTMLHRElement).toBeTruthy();
60
+ expect(divider).toHaveStyle(`margin: 4px`);
52
61
  });
53
62
 
54
- test('accepts custom styles prop className', () => {
63
+ test('renders correct HTML element', () => {
55
64
  render(
56
65
  <ThemeProvider theme={theme}>
57
- <Divider className="custom-class-name" title="divider" />
66
+ <Divider data-testid="divider" />
58
67
  </ThemeProvider>
59
68
  );
60
- const divider = screen.getByTitle(/divider/);
69
+ const divider = screen.getByTestId(/divider/);
61
70
 
62
- expect(divider.className).toMatch('custom-class-name');
71
+ expect(divider instanceof HTMLDivElement).toBeTruthy();
63
72
  });
@@ -1,13 +1,33 @@
1
1
  import React from 'react';
2
+ import { SeparatorProps, useSeparator } from '@react-aria/separator';
3
+
2
4
  import { Box } from '../Box';
3
5
 
6
+ // Theme Extension
7
+ // ---------------
8
+ export interface DividerThemeExtension<Value> {
9
+ divider?: {
10
+ [key: string]: Value;
11
+ };
12
+ }
13
+
14
+ // Props
15
+ // ---------------
4
16
  export type DividerProps = {
5
- className?: string;
6
17
  variant?: string;
7
- title?: string; // Should only be used for testing.
8
- };
18
+ } & SeparatorProps;
9
19
 
10
- export const Divider: React.FC<DividerProps> = ({
11
- variant = 'regular',
12
- ...props
13
- }) => <Box {...props} as="hr" variant={`divider.${variant}`} />;
20
+ // Component
21
+ // ---------------
22
+ export const Divider: React.FC<DividerProps> = ({ variant = '', ...props }) => {
23
+ const { separatorProps } = useSeparator(props);
24
+
25
+ return (
26
+ <Box
27
+ __baseCSS={{ width: '100%', height: '1px', m: 'none', bg: 'text' }}
28
+ variant={`divider.${variant}`}
29
+ {...props}
30
+ {...separatorProps}
31
+ />
32
+ );
33
+ };
@@ -0,0 +1,110 @@
1
+ import React from 'react';
2
+ import type { Meta, ComponentStory } from '@storybook/react';
3
+ import { Field } from './Field';
4
+
5
+ export default {
6
+ title: 'Components/Field',
7
+ argTypes: {
8
+ variant: {
9
+ control: {
10
+ type: 'text',
11
+ },
12
+ description: 'Field variant',
13
+ table: {
14
+ defaultValue: {
15
+ summary: '__default',
16
+ },
17
+ },
18
+ },
19
+ labelVariant: {
20
+ control: {
21
+ type: 'text',
22
+ },
23
+ description: 'Field label variant',
24
+ table: {
25
+ defaultValue: {
26
+ summary: 'above',
27
+ },
28
+ },
29
+ },
30
+ htmlFor: {
31
+ control: {
32
+ type: 'text',
33
+ },
34
+ defaultValue: 'id',
35
+ },
36
+ label: {
37
+ control: {
38
+ type: 'text',
39
+ },
40
+ defaultValue: 'Label',
41
+ },
42
+ error: {
43
+ control: {
44
+ type: 'boolean',
45
+ },
46
+ description: 'Error',
47
+ table: {
48
+ defaultValue: {
49
+ summary: false,
50
+ },
51
+ },
52
+ },
53
+ errorMessage: {
54
+ control: {
55
+ type: 'text',
56
+ },
57
+ description: 'Error Message',
58
+ },
59
+ required: {
60
+ control: {
61
+ type: 'boolean',
62
+ },
63
+ options: [true, false],
64
+ table: {
65
+ defaultValue: {
66
+ summary: false,
67
+ },
68
+ },
69
+ },
70
+ disabled: {
71
+ control: {
72
+ type: 'boolean',
73
+ },
74
+ options: [true, false],
75
+ table: {
76
+ defaultValue: {
77
+ summary: false,
78
+ },
79
+ },
80
+ },
81
+ type: {
82
+ control: {
83
+ type: 'select',
84
+ },
85
+ options: [
86
+ 'date',
87
+ 'datetime-local',
88
+ 'email',
89
+ 'month',
90
+ 'number',
91
+ 'password',
92
+ 'search',
93
+ 'tel',
94
+ 'text',
95
+ 'time',
96
+ 'time',
97
+ 'url',
98
+ 'week',
99
+ ],
100
+ defaultValue: 'text',
101
+ table: {
102
+ defaultValue: {
103
+ summary: 'text',
104
+ },
105
+ },
106
+ },
107
+ },
108
+ } as Meta;
109
+
110
+ export const Basic: ComponentStory<typeof Field> = args => <Field {...args} />;
@@ -2,20 +2,29 @@ import React from 'react';
2
2
  import { render, screen } from '@testing-library/react';
3
3
  import { ThemeProvider } from '@marigold/system';
4
4
  import { Field } from './Field';
5
- import { useStyles } from '@marigold/system';
6
5
 
7
6
  const theme = {
8
- field: {
9
- default: {
10
- padding: '4px',
7
+ space: {
8
+ none: 0,
9
+ small: 4,
10
+ medium: 8,
11
+ },
12
+ label: {
13
+ above: {
14
+ fontSize: '14px',
15
+ },
16
+ },
17
+ input: {
18
+ __default: {
19
+ padding: 'small',
11
20
  },
12
- inputField: {
13
- padding: '8px',
21
+ error: {
22
+ padding: 'medium',
14
23
  },
15
24
  },
16
25
  };
17
26
 
18
- test('supports default variant and themeSection', () => {
27
+ test('renders correct HTML element', () => {
19
28
  render(
20
29
  <ThemeProvider theme={theme}>
21
30
  <Field htmlFor="myId" label="label" />
@@ -23,29 +32,47 @@ test('supports default variant and themeSection', () => {
23
32
  );
24
33
  const field = screen.getByText(/label/);
25
34
 
26
- expect(field).toHaveStyle(`padding: 4px`);
35
+ expect(field instanceof HTMLLabelElement).toBeTruthy();
27
36
  });
28
37
 
29
- test('accepts other variant than default', () => {
38
+ test('supports default variant', () => {
30
39
  render(
31
40
  <ThemeProvider theme={theme}>
32
- <Field htmlFor="myId" label="inputField" variant="inputField" />
41
+ <Field htmlFor="myId" label="Name" data-testid="field" />
33
42
  </ThemeProvider>
34
43
  );
35
- const inputField = screen.getByText(/inputField/);
44
+ const field = screen.getByTestId(/field/);
45
+ expect(field).toHaveStyle(`padding: 4px`);
46
+ });
36
47
 
37
- expect(inputField).toHaveStyle(`padding: 8px`);
48
+ test('supports other variant than default', () => {
49
+ render(
50
+ <ThemeProvider theme={theme}>
51
+ <Field htmlFor="myId" label="Name" variant="error" data-testid="field" />
52
+ </ThemeProvider>
53
+ );
54
+ const field = screen.getByTestId(/field/);
55
+ expect(field).toHaveStyle(`padding: 8px`);
38
56
  });
39
57
 
40
- test('renders correct HTML element', () => {
58
+ test('supports default variantLabel', () => {
41
59
  render(
42
60
  <ThemeProvider theme={theme}>
43
- <Field htmlFor="myId" label="label" />
61
+ <Field htmlFor="myId" label="Name" />
44
62
  </ThemeProvider>
45
63
  );
46
- const field = screen.getByText(/label/);
64
+ const label = screen.getByText(/Name/);
65
+ expect(label).toHaveStyle(`font-size: 14px`);
66
+ });
47
67
 
48
- expect(field instanceof HTMLLabelElement).toBeTruthy();
68
+ test('supports other variantLabel than default', () => {
69
+ render(
70
+ <ThemeProvider theme={theme}>
71
+ <Field htmlFor="myId" label="Name" />
72
+ </ThemeProvider>
73
+ );
74
+ const label = screen.getByText(/Name/);
75
+ expect(label).toHaveStyle(`font-size: 14px`);
49
76
  });
50
77
 
51
78
  test('supports label prop', () => {
@@ -56,34 +83,48 @@ test('supports label prop', () => {
56
83
  });
57
84
 
58
85
  test('supports htmlFor prop', () => {
59
- render(<Field htmlFor="myId" label="Name" error="Validation error" />);
86
+ render(<Field htmlFor="myId" label="Name" />);
60
87
  const field = screen.getByText(/Name/);
61
88
 
62
89
  expect(field).toHaveAttribute('for');
63
90
  });
64
91
 
65
- test('supports error prop', () => {
66
- render(<Field htmlFor="myId" label="label" error="Validation error" />);
67
- const field = screen.getByText(/Validation/);
92
+ test('supports required prop', () => {
93
+ render(<Field htmlFor="myId" label="label" required />);
94
+ const fieldLabel = screen.getByText(/label/);
68
95
 
69
- expect(field).toBeDefined();
96
+ expect(fieldLabel.nextSibling).toBeDefined();
97
+ expect(fieldLabel.nextSibling instanceof SVGElement).toBeTruthy();
98
+ });
99
+
100
+ test('supports error and errorMessage prop', () => {
101
+ render(
102
+ <Field htmlFor="myId" label="label" error errorMessage="Validation error" />
103
+ );
104
+
105
+ const errorMessage = screen.getByText(/Validation/);
106
+ expect(errorMessage).toBeDefined();
107
+ });
108
+
109
+ test('supports disabled prop', () => {
110
+ render(<Field htmlFor="myId" label="label" disabled />);
111
+ const fieldLabel = screen.getByText(/label/);
112
+ expect(fieldLabel.nextSibling).toHaveAttribute('disabled');
70
113
  });
71
114
 
72
115
  test('accepts custom styles prop className', () => {
73
- const TestComponent: React.FC = ({ children, ...props }) => {
74
- const classNames = useStyles({ css: { fontSize: '8px' } });
75
- return (
76
- <Field htmlFor="myId" label="label" className={classNames} {...props} />
77
- );
78
- };
79
-
80
- const { getByText } = render(
116
+ render(
81
117
  <ThemeProvider theme={theme}>
82
- <TestComponent />
118
+ <Field
119
+ htmlFor="myId"
120
+ label="label"
121
+ className="custom-class-name"
122
+ title="field"
123
+ data-testid="field"
124
+ />
83
125
  </ThemeProvider>
84
126
  );
85
- const testelem = getByText('label');
86
- const field = getComputedStyle(testelem);
127
+ const field = screen.getByTestId(/field/);
87
128
 
88
- expect(field.fontSize).toEqual('8px');
129
+ expect(field.className).toMatch('custom-class-name');
89
130
  });
@@ -1,49 +1,56 @@
1
1
  import React from 'react';
2
- import { useStyles } from '@marigold/system';
3
- import { Exclamation, Required } from '@marigold/icons';
2
+ import { Exclamation } from '@marigold/icons';
4
3
  import { ComponentProps } from '@marigold/types';
5
4
 
6
5
  import { Input } from '../Input';
7
6
  import { Label } from '../Label';
8
7
  import { ValidationMessage } from '../ValidationMessage';
9
8
 
9
+ // Props
10
+ // ---------------
10
11
  export type FieldProps = {
11
12
  variant?: string;
13
+ labelVariant?: string;
12
14
  htmlFor: string;
13
15
  label: string;
14
- error?: string;
16
+ required?: boolean;
17
+ error?: boolean;
18
+ errorMessage?: string;
19
+ disabled?: boolean;
15
20
  } & ComponentProps<'input'>;
16
21
 
22
+ // Component
23
+ // ---------------
17
24
  export const Field: React.FC<FieldProps> = ({
18
- variant = 'default',
19
25
  type = 'text',
20
- className = '',
26
+ variant = '',
27
+ labelVariant = 'above',
21
28
  htmlFor,
22
29
  label,
30
+ required,
23
31
  error,
32
+ errorMessage,
33
+ disabled,
24
34
  ...props
25
35
  }) => {
26
- const labelClassName = useStyles({
27
- variant: `field.${variant}`,
28
- className,
29
- });
30
- const errorClassName = useStyles({ css: { color: 'red60' } });
31
-
32
36
  return (
33
- <div>
34
- <Label className={labelClassName} htmlFor={htmlFor}>
37
+ <>
38
+ <Label variant={labelVariant} htmlFor={htmlFor} required={required}>
35
39
  {label}
36
- {error ? <Required size={16} className={errorClassName} /> : ''}
37
40
  </Label>
38
- <Input {...props} type={type} id={htmlFor} />
39
- {error ? (
41
+ <Input
42
+ {...props}
43
+ type={type}
44
+ id={htmlFor}
45
+ disabled={disabled}
46
+ variant={variant}
47
+ />
48
+ {error && errorMessage && (
40
49
  <ValidationMessage>
41
50
  <Exclamation size={16} />
42
- {error}
51
+ {errorMessage}
43
52
  </ValidationMessage>
44
- ) : (
45
- ''
46
53
  )}
47
- </div>
54
+ </>
48
55
  );
49
56
  };
@@ -0,0 +1,34 @@
1
+ import React from 'react';
2
+ import type { Meta, ComponentStory } from '@storybook/react';
3
+ import { Image } from './Image';
4
+
5
+ export default {
6
+ title: 'Components/Image',
7
+ argTypes: {
8
+ variant: {
9
+ control: {
10
+ type: 'text',
11
+ },
12
+ description: 'there is only one variant',
13
+ table: {
14
+ defaultValue: {
15
+ summary: 'fullWidth',
16
+ },
17
+ },
18
+ },
19
+ alt: {
20
+ control: {
21
+ type: 'text',
22
+ },
23
+ description: 'Description text for screenreaders',
24
+ },
25
+ },
26
+ } as Meta;
27
+
28
+ export const Basic: ComponentStory<typeof Image> = args => (
29
+ <Image
30
+ src="https://www.reservix.net/_Resources/Persistent/0e8f5885125940fdb2bc2d54840f497782f56584/Reservix_Logo_dtp_web_rgb_font_black_180704.png"
31
+ alt="marigold_logo"
32
+ {...args}
33
+ />
34
+ );
@@ -4,7 +4,10 @@ import { ThemeProvider } from '@marigold/system';
4
4
  import { Image } from './Image';
5
5
 
6
6
  const theme = {
7
- images: {
7
+ colors: {
8
+ primary: 'hotpink',
9
+ },
10
+ image: {
8
11
  fullWidth: {
9
12
  alignItems: 'center',
10
13
  },
@@ -2,12 +2,24 @@ import React from 'react';
2
2
  import { ComponentProps } from '@marigold/types';
3
3
  import { Box } from '../Box';
4
4
 
5
+ // Theme Extension
6
+ // ---------------
7
+ export interface ImageThemeExtension<Value> {
8
+ image?: {
9
+ [key: string]: Value;
10
+ };
11
+ }
12
+
13
+ // Props
14
+ // ---------------
5
15
  export type ImageProps = {
6
16
  variant?: string;
7
17
  children?: never;
8
18
  } & ComponentProps<'img'>;
9
19
 
20
+ // Component
21
+ // ---------------
10
22
  export const Image: React.FC<ImageProps> = ({
11
23
  variant = 'fullWidth',
12
24
  ...props
13
- }) => <Box {...props} as="img" variant={`images.${variant}`} />;
25
+ }) => <Box {...props} as="img" variant={`image.${variant}`} />;