@marigold/components 0.0.3 → 0.3.1

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 +174 -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 +15 -3
  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 +5 -0
  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 +10 -6
  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 +17 -10
  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 +1075 -562
  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 +1012 -542
  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 +21 -2
  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 +81 -52
  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 +13 -5
  115. package/src/Divider/Divider.tsx +12 -0
  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 +51 -21
  134. package/src/Link/Link.tsx +39 -13
  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 +27 -32
  164. package/src/Text/Text.stories.tsx +61 -0
  165. package/src/Text/Text.test.tsx +41 -36
  166. package/src/Text/Text.tsx +56 -31
  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 -45
  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 -152
  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 -119
  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,99 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import { ThemeProvider } from '@marigold/system';
4
+
5
+ import { Inline } from './Inline';
6
+ import { Text } from '../Text';
7
+
8
+ // Setup
9
+ // ---------------
10
+ const theme = {
11
+ space: {
12
+ none: 0,
13
+ small: 2,
14
+ medium: 4,
15
+ large: 8,
16
+ },
17
+ };
18
+
19
+ const getLeftPadding = (element: HTMLElement) =>
20
+ getComputedStyle(element).getPropertyValue('padding-left');
21
+
22
+ test('default space is "none"', () => {
23
+ render(
24
+ <ThemeProvider theme={theme}>
25
+ <Inline>
26
+ <Text>first</Text>
27
+ <Text>second</Text>
28
+ </Inline>
29
+ </ThemeProvider>
30
+ );
31
+ const first = screen.getByText(/first/).parentElement!;
32
+ const second = screen.getByText(/second/).parentElement!;
33
+
34
+ expect(getLeftPadding(first)).toEqual('');
35
+ expect(second).toHaveStyle(`padding-left: 0px`);
36
+ });
37
+
38
+ test('accepts and uses spacing from theme', () => {
39
+ render(
40
+ <ThemeProvider theme={theme}>
41
+ <Inline space="small">
42
+ <Text>first</Text>
43
+ <Text>second</Text>
44
+ </Inline>
45
+ </ThemeProvider>
46
+ );
47
+ const first = screen.getByText(/first/);
48
+ const second = screen.getByText(/second/);
49
+
50
+ expect(getLeftPadding(first)).toEqual('');
51
+ expect(second.parentElement).toHaveStyle(`padding-left: 2px`);
52
+ });
53
+
54
+ test('supports nesting', () => {
55
+ render(
56
+ <ThemeProvider theme={theme}>
57
+ <Inline space="large">
58
+ <Inline space="small" data-testid="leftInline">
59
+ <Text>first</Text>
60
+ <Text>second</Text>
61
+ </Inline>
62
+ <Inline space="small" data-testid="rightInline">
63
+ <Text>third</Text>
64
+ <Text>fourth</Text>
65
+ </Inline>
66
+ </Inline>
67
+ </ThemeProvider>
68
+ );
69
+ const first = screen.getByText(/first/);
70
+ const second = screen.getByText(/second/);
71
+ const leftInline = screen.getByTestId('leftInline');
72
+
73
+ const third = screen.getByText(/third/);
74
+ const fourth = screen.getByText(/fourth/);
75
+ const rightInline = screen.getByTestId('rightInline');
76
+
77
+ expect(getLeftPadding(leftInline.parentElement!)).toEqual('');
78
+ expect(rightInline.parentElement).toHaveStyle(`padding-left: 8px`);
79
+
80
+ expect(getLeftPadding(first.parentElement!)).toEqual('');
81
+ expect(second.parentElement).toHaveStyle(`padding-left: 2px`);
82
+
83
+ expect(getLeftPadding(third.parentElement!)).toEqual('');
84
+ expect(fourth.parentElement).toHaveStyle(`padding-left: 2px`);
85
+ });
86
+
87
+ test('renders div per default', () => {
88
+ render(
89
+ <ThemeProvider theme={theme}>
90
+ <Inline data-testid="inline">
91
+ <Text>first</Text>
92
+ <Text>second</Text>
93
+ </Inline>
94
+ </ThemeProvider>
95
+ );
96
+
97
+ const inline = screen.getByTestId('inline');
98
+ expect(inline instanceof HTMLDivElement).toBeTruthy();
99
+ });
@@ -0,0 +1,38 @@
1
+ import React, { Children } from 'react';
2
+ import flattenChildren from 'react-keyed-flatten-children';
3
+
4
+ import { ResponsiveStyleValue } from '@marigold/system';
5
+
6
+ import { Box } from '../Box';
7
+
8
+ export type InlineProps = {
9
+ space?: ResponsiveStyleValue<string>;
10
+ align?: 'top' | 'center' | 'bottom';
11
+ };
12
+
13
+ const ALIGNMENT = {
14
+ top: 'flex-start',
15
+ center: 'center',
16
+ bottom: 'flex-end',
17
+ };
18
+
19
+ export const Inline: React.FC<InlineProps> = ({
20
+ space = 'none',
21
+ align = 'center',
22
+ children,
23
+ ...props
24
+ }) => (
25
+ <Box
26
+ display="inline-flex"
27
+ css={{ '> * + *': { pl: space } }}
28
+ alignItems={ALIGNMENT[align]}
29
+ {...props}
30
+ >
31
+ {Children.map(
32
+ flattenChildren(children) as unknown as React.ReactElement,
33
+ (child: React.ReactElement) => (
34
+ <Box>{React.cloneElement(child, {}, child.props.children)}</Box>
35
+ )
36
+ )}
37
+ </Box>
38
+ );
@@ -0,0 +1 @@
1
+ export * from './Inline';
@@ -0,0 +1,54 @@
1
+ import React from 'react';
2
+ import type { Meta, ComponentStory } from '@storybook/react';
3
+ import { Input } from './Input';
4
+ import { Label } from '../Label';
5
+
6
+ export default {
7
+ title: 'Components/Input',
8
+ argTypes: {
9
+ variant: {
10
+ control: {
11
+ type: 'text',
12
+ },
13
+ description: '?',
14
+ table: {
15
+ defaultValue: {
16
+ summary: '__default',
17
+ },
18
+ },
19
+ },
20
+ type: {
21
+ control: {
22
+ type: 'select',
23
+ },
24
+ options: [
25
+ 'date',
26
+ 'datetime-local',
27
+ 'email',
28
+ 'month',
29
+ 'number',
30
+ 'password',
31
+ 'search',
32
+ 'tel',
33
+ 'text',
34
+ 'time',
35
+ 'time',
36
+ 'url',
37
+ 'week',
38
+ ],
39
+ defaultValue: 'text',
40
+ table: {
41
+ defaultValue: {
42
+ summary: 'text',
43
+ },
44
+ },
45
+ },
46
+ },
47
+ } as Meta;
48
+
49
+ export const Basic: ComponentStory<typeof Input> = args => (
50
+ <Label htmlFor="input">
51
+ Label
52
+ <Input id="input" placeholder="Placeholder..." {...args} />
53
+ </Label>
54
+ );
@@ -4,12 +4,16 @@ import { ThemeProvider } from '@marigold/system';
4
4
  import { Input } from './Input';
5
5
 
6
6
  const theme = {
7
+ fonts: {
8
+ body: 'Inter',
9
+ forms: 'Roboto',
10
+ },
7
11
  input: {
8
- default: {
9
- fontFamily: 'Inter',
12
+ __default: {
13
+ fontFamily: 'body',
10
14
  },
11
15
  input2: {
12
- fontFamily: 'Roboto',
16
+ fontFamily: 'forms',
13
17
  },
14
18
  },
15
19
  };
@@ -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 InputThemeExtension<Value> {
8
+ input?: {
9
+ [key: string]: Value;
10
+ };
11
+ }
12
+
13
+ // Props
14
+ // ---------------
5
15
  export type InputProps = {
6
16
  variant?: string;
7
17
  } & ComponentProps<'input'>;
8
18
 
19
+ // Component
20
+ // ---------------
9
21
  export const Input: React.FC<InputProps> = ({
10
- variant = 'default',
22
+ variant = '',
11
23
  type = 'text',
12
24
  ...props
13
25
  }) => <Box {...props} as="input" type={type} variant={`input.${variant}`} />;
@@ -0,0 +1,41 @@
1
+ import React from 'react';
2
+ import type { Meta, ComponentStory } from '@storybook/react';
3
+ import { Label } from './Label';
4
+
5
+ export default {
6
+ title: 'Components/Label',
7
+ argTypes: {
8
+ variant: {
9
+ control: {
10
+ type: 'text',
11
+ },
12
+ description: '?',
13
+ table: {
14
+ defaultValue: {
15
+ summary: 'above',
16
+ },
17
+ },
18
+ },
19
+ htmlFor: {
20
+ control: {
21
+ type: 'text',
22
+ },
23
+ defaultValue: 'input',
24
+ },
25
+ required: {
26
+ control: {
27
+ type: 'boolean',
28
+ },
29
+ description: 'Adds required Icon to label',
30
+ table: {
31
+ defaultValue: {
32
+ summary: false,
33
+ },
34
+ },
35
+ },
36
+ },
37
+ } as Meta;
38
+
39
+ export const Basic: ComponentStory<typeof Label> = args => (
40
+ <Label {...args}>Label</Label>
41
+ );
@@ -4,17 +4,25 @@ import { ThemeProvider } from '@marigold/system';
4
4
  import { Label } from './Label';
5
5
 
6
6
  const theme = {
7
+ fonts: {
8
+ body: 'Inter Regular',
9
+ label: 'Oswald',
10
+ },
7
11
  label: {
8
- default: {
9
- fontFamily: 'Inter Regular',
12
+ above: {
13
+ fontFamily: 'body',
10
14
  },
11
15
  myLabel: {
12
- fontFamily: 'Oswald Regular',
16
+ fontFamily: 'label',
13
17
  },
14
18
  },
19
+ colors: {
20
+ text: 'black',
21
+ disabled: 'gray',
22
+ },
15
23
  };
16
24
 
17
- test('supports default variant and themeSection', () => {
25
+ test('supports default variant and styles', () => {
18
26
  render(
19
27
  <ThemeProvider theme={theme}>
20
28
  <Label htmlFor="labelId">label</Label>
@@ -23,6 +31,7 @@ test('supports default variant and themeSection', () => {
23
31
  const label = screen.getByText(/label/);
24
32
 
25
33
  expect(label).toHaveStyle(`font-family: Inter Regular`);
34
+ expect(label).toHaveStyle(`color: black`);
26
35
  });
27
36
 
28
37
  test('supports other variant than default', () => {
@@ -35,7 +44,7 @@ test('supports other variant than default', () => {
35
44
  );
36
45
  const label = screen.getByText(/label/);
37
46
 
38
- expect(label).toHaveStyle(`font-family: Oswald Regular`);
47
+ expect(label).toHaveStyle(`font-family: Oswald`);
39
48
  });
40
49
 
41
50
  test('supports htmlFor prop', () => {
@@ -49,6 +58,32 @@ test('supports htmlFor prop', () => {
49
58
  expect(label).toHaveAttribute('for');
50
59
  });
51
60
 
61
+ test('supports required prop', () => {
62
+ render(
63
+ <ThemeProvider theme={theme}>
64
+ <Label htmlFor="labelId" required>
65
+ label
66
+ </Label>
67
+ </ThemeProvider>
68
+ );
69
+ const label = screen.getByText(/label/);
70
+ const parent = label.parentElement;
71
+
72
+ expect(parent instanceof HTMLSpanElement).toBeTruthy();
73
+ });
74
+
75
+ test('supports color prop', () => {
76
+ render(
77
+ <ThemeProvider theme={theme}>
78
+ <Label htmlFor="labelId" color="disabled">
79
+ label
80
+ </Label>
81
+ </ThemeProvider>
82
+ );
83
+ const label = screen.getByText(/label/);
84
+ expect(label).toHaveStyle(`color: gray`);
85
+ });
86
+
52
87
  test('renders <label> element', () => {
53
88
  render(
54
89
  <ThemeProvider theme={theme}>
@@ -1,18 +1,64 @@
1
1
  import React from 'react';
2
+
2
3
  import { ComponentProps } from '@marigold/types';
4
+ import { Required } from '@marigold/icons';
5
+ import { ResponsiveStyleValue } from '@marigold/system';
6
+
3
7
  import { Box } from '../Box';
4
8
 
5
- export type LabelProps = {
6
- htmlFor: string;
9
+ // Theme Extension
10
+ // ---------------
11
+ export interface LabelThemeExtension<Value> {
12
+ label?: {
13
+ [key: string]: Value;
14
+ };
15
+ }
16
+
17
+ // LabelBase
18
+ // ---------------
19
+ export type LabelBaseProps = {
20
+ htmlFor?: string;
7
21
  variant?: string;
22
+ required?: boolean;
23
+ color?: ResponsiveStyleValue<string>;
8
24
  } & ComponentProps<'label'>;
9
25
 
26
+ export const LabelBase: React.FC<LabelProps> = ({
27
+ variant = 'above',
28
+ required,
29
+ color = 'text',
30
+ children,
31
+ ...props
32
+ }) => {
33
+ return (
34
+ <Box
35
+ {...props}
36
+ as="label"
37
+ __baseCSS={{ color: color }}
38
+ variant={`label.${variant}`}
39
+ >
40
+ {children}
41
+ </Box>
42
+ );
43
+ };
44
+
45
+ // Label
46
+ // ---------------
47
+ export type LabelProps = {
48
+ required?: boolean;
49
+ } & LabelBaseProps;
50
+
10
51
  export const Label: React.FC<LabelProps> = ({
11
- variant = 'default',
52
+ required,
12
53
  children,
13
54
  ...props
14
- }) => (
15
- <Box {...props} as="label" variant={`label.${variant}`}>
16
- {children}
17
- </Box>
18
- );
55
+ }) => {
56
+ return required ? (
57
+ <Box as="span" display="inline-flex" alignItems="center">
58
+ <LabelBase {...props}>{children}</LabelBase>
59
+ {required && <Box as={Required} size={16} css={{ color: 'error' }} />}
60
+ </Box>
61
+ ) : (
62
+ <LabelBase {...props}>{children}</LabelBase>
63
+ );
64
+ };
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+ import type { Meta, ComponentStory } from '@storybook/react';
3
+ import { Link } from './Link';
4
+ import { Text } from '../Text';
5
+
6
+ export default {
7
+ title: 'Components/Link',
8
+ argTypes: {
9
+ variant: {
10
+ control: {
11
+ type: 'text',
12
+ },
13
+ description: '?',
14
+ table: {
15
+ defaultValue: {
16
+ summary: 'link',
17
+ },
18
+ },
19
+ },
20
+ href: {
21
+ control: {
22
+ type: 'text',
23
+ },
24
+ description: 'The URL to direct to',
25
+ },
26
+ },
27
+ } as Meta;
28
+
29
+ export const Basic: ComponentStory<typeof Link> = args => (
30
+ <Text>
31
+ <Link href="https://marigold-ui.io" target="_blank" {...args}>
32
+ Marigold Docs
33
+ </Link>
34
+ </Text>
35
+ );
@@ -4,64 +4,94 @@ import { ThemeProvider } from '@marigold/system';
4
4
  import { Link } from './Link';
5
5
 
6
6
  const theme = {
7
- link: {
8
- normal: {
9
- fontFamily: 'Inter',
7
+ fonts: {
8
+ link: 'Inter',
9
+ body: 'Oswald',
10
+ },
11
+ text: {
12
+ link: {
13
+ fontFamily: 'link',
10
14
  },
11
15
  second: {
12
- fontFamily: 'Oswald',
16
+ fontFamily: 'body',
13
17
  },
14
18
  },
15
19
  };
16
20
 
17
- test('supports default variant and themeSection', () => {
21
+ test('uses `text.link` as default variant', () => {
18
22
  render(
19
23
  <ThemeProvider theme={theme}>
20
- <Link href="#!" title="link">
21
- Link
22
- </Link>
24
+ <Link href="#!">Link</Link>
23
25
  </ThemeProvider>
24
26
  );
25
- const link = screen.getByTitle(/link/);
27
+ const link = screen.getByText(/Link/);
26
28
 
27
29
  expect(link).toHaveStyle(`font-family: Inter`);
28
30
  });
29
31
 
30
- test('accepts other variant than default', () => {
32
+ test('allows to change variants via `variant` prop (with "text" prefix)', () => {
31
33
  render(
32
34
  <ThemeProvider theme={theme}>
33
- <Link href="#!" title="link" variant="second">
35
+ <Link href="#!" variant="second">
34
36
  Link
35
37
  </Link>
36
38
  </ThemeProvider>
37
39
  );
38
- const link = screen.getByTitle(/link/);
40
+ const link = screen.getByText(/Link/);
39
41
 
40
42
  expect(link).toHaveStyle(`font-family: Oswald`);
41
43
  });
42
44
 
43
- test('renders correct HTML element', () => {
45
+ test('renders a <a> element by default', () => {
44
46
  render(
45
47
  <ThemeProvider theme={theme}>
46
- <Link href="#!" title="link">
47
- Link
48
- </Link>
48
+ <Link href="#!">Link</Link>
49
49
  </ThemeProvider>
50
50
  );
51
- const link = screen.getByTitle(/link/);
51
+ const link = screen.getByText(/Link/);
52
52
 
53
53
  expect(link instanceof HTMLAnchorElement).toBeTruthy();
54
54
  });
55
55
 
56
- test('accepts custom styles prop className', () => {
56
+ test('accepts custom className', () => {
57
57
  render(
58
58
  <ThemeProvider theme={theme}>
59
- <Link href="#!" className="custom-class-name" title="link">
60
- link
59
+ <Link href="#!" className="custom-class-name">
60
+ Link
61
61
  </Link>
62
62
  </ThemeProvider>
63
63
  );
64
- const link = screen.getByTitle(/link/);
64
+ const link = screen.getByText(/Link/);
65
65
 
66
66
  expect(link.className).toMatch('custom-class-name');
67
67
  });
68
+
69
+ test('accepts other routing components', () => {
70
+ const RouterLink = React.forwardRef<
71
+ HTMLSpanElement,
72
+ { to: string; children?: React.ReactNode }
73
+ >(() => <span>I am a Router Link!</span>);
74
+
75
+ render(
76
+ <ThemeProvider theme={theme}>
77
+ <Link as={RouterLink} to="/Home">
78
+ Link
79
+ </Link>
80
+ </ThemeProvider>
81
+ );
82
+
83
+ const link = screen.getByText('I am a Router Link!');
84
+ expect(link).toBeTruthy();
85
+ });
86
+
87
+ test('a link can be disabled via aria attributes', () => {
88
+ render(
89
+ <ThemeProvider theme={theme}>
90
+ <Link href="#!" disabled={true}>
91
+ Link
92
+ </Link>
93
+ </ThemeProvider>
94
+ );
95
+ const link = screen.getByText(/Link/);
96
+ expect(link.getAttribute('aria-disabled')).toEqual('true');
97
+ });
package/src/Link/Link.tsx CHANGED
@@ -1,17 +1,43 @@
1
- import React from 'react';
2
- import { ComponentProps } from '@marigold/types';
3
- import { Box } from '../Box';
1
+ import React, { useRef } from 'react';
2
+ import { useLink } from '@react-aria/link';
3
+ import { PolymorphicComponent, PolymorphicProps } from '@marigold/types';
4
4
 
5
- export type LinkProps = {
6
- variant?: string;
7
- } & ComponentProps<'a'>;
5
+ import { Text, TextOwnProps } from '../Text';
8
6
 
9
- export const Link: React.FC<LinkProps> = ({
10
- variant = 'normal',
7
+ // Theme Extension
8
+ // ---------------
9
+ export interface LinkThemeExtension<Value> {
10
+ link?: Value;
11
+ }
12
+
13
+ // Props
14
+ // ---------------
15
+ export type LinkOwnProps = { disabled?: boolean } & TextOwnProps;
16
+ export type LinkProps = PolymorphicProps<LinkOwnProps, 'a'>;
17
+
18
+ // Component
19
+ // ---------------
20
+ export const Link = (({
21
+ as = 'a',
22
+ variant = 'link',
11
23
  children,
24
+ disabled,
12
25
  ...props
13
- }) => (
14
- <Box {...props} as="a" variant={`link.${variant}`}>
15
- {children}
16
- </Box>
17
- );
26
+ }: LinkProps) => {
27
+ const ref = useRef<any>();
28
+ const { linkProps } = useLink(
29
+ {
30
+ // We typecast here because the element could very well be a `span`
31
+ ...(props as PolymorphicProps<LinkOwnProps, any>),
32
+ elementType: typeof as === 'string' ? as : 'span',
33
+ isDisabled: disabled,
34
+ },
35
+ ref
36
+ );
37
+
38
+ return (
39
+ <Text {...props} {...linkProps} as={as} variant={variant} ref={ref}>
40
+ {children}
41
+ </Text>
42
+ );
43
+ }) as PolymorphicComponent<LinkOwnProps, 'a'>;