@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
@@ -1,102 +1,113 @@
1
1
  import React from 'react';
2
2
  import { render, screen } from '@testing-library/react';
3
- import { Column, Columns } from '@marigold/components';
3
+ import { Column, Columns, MarigoldProvider } from '@marigold/components';
4
+
5
+ const theme = {
6
+ space: {
7
+ none: 0,
8
+ large: 24,
9
+ },
10
+ };
4
11
 
5
12
  test('supports default space prop', () => {
6
13
  render(
7
- <Columns title="column">
8
- <Column>column</Column>
9
- </Columns>
14
+ <MarigoldProvider theme={theme}>
15
+ <Columns data-testid="column">
16
+ <Column>column</Column>
17
+ </Columns>
18
+ </MarigoldProvider>
10
19
  );
11
- const column = screen.getByTitle(/column/);
20
+ const column = screen.getByTestId(/column/);
12
21
 
13
22
  expect(column).toHaveStyle(`margin: 0px`);
14
23
  });
15
24
 
16
25
  test('supports custom space prop', () => {
17
26
  render(
18
- <Columns space={24} title="column">
19
- <Column>column</Column>
20
- </Columns>
27
+ <MarigoldProvider theme={theme}>
28
+ <Columns space="large" data-testid="column">
29
+ <Column>column</Column>
30
+ </Columns>
31
+ </MarigoldProvider>
21
32
  );
22
- const column = screen.getByTitle(/column/);
33
+ const column = screen.getByTestId(/column/);
23
34
 
24
35
  expect(column).toHaveStyle(`margin: -12px`);
25
36
  });
26
37
 
27
38
  test('supports default horizontalAlign prop: left', () => {
28
39
  render(
29
- <Columns title="column">
40
+ <Columns data-testid="column">
30
41
  <Column>column</Column>
31
42
  </Columns>
32
43
  );
33
- const column = screen.getByTitle(/column/);
44
+ const column = screen.getByTestId(/column/);
34
45
 
35
46
  expect(column).toHaveStyle(`justify-content: flex-start`);
36
47
  });
37
48
 
38
49
  test('supports custom horizontalAlign prop: center', () => {
39
50
  render(
40
- <Columns horizontalAlign="center" title="column">
51
+ <Columns horizontalAlign="center" data-testid="column">
41
52
  <Column>column</Column>
42
53
  </Columns>
43
54
  );
44
- const column = screen.getByTitle(/column/);
55
+ const column = screen.getByTestId(/column/);
45
56
 
46
57
  expect(column).toHaveStyle(`justify-content: center`);
47
58
  });
48
59
 
49
60
  test('supports custom horizontalAlign prop: right', () => {
50
61
  render(
51
- <Columns horizontalAlign="right" title="column">
62
+ <Columns horizontalAlign="right" data-testid="column">
52
63
  <Column>column</Column>
53
64
  </Columns>
54
65
  );
55
- const column = screen.getByTitle(/column/);
66
+ const column = screen.getByTestId(/column/);
56
67
 
57
68
  expect(column).toHaveStyle(`justify-content: flex-end`);
58
69
  });
59
70
 
60
71
  test('supports default verticalAlign prop: top', () => {
61
72
  render(
62
- <Columns title="column">
73
+ <Columns data-testid="column">
63
74
  <Column>column</Column>
64
75
  </Columns>
65
76
  );
66
- const column = screen.getByTitle(/column/);
77
+ const column = screen.getByTestId(/column/);
67
78
 
68
79
  expect(column).toHaveStyle(`align-items: flex-start`);
69
80
  });
70
81
 
71
82
  test('supports custom verticalAlign prop: center', () => {
72
83
  render(
73
- <Columns verticalAlign="center" title="column">
84
+ <Columns verticalAlign="center" data-testid="column">
74
85
  <Column>column</Column>
75
86
  </Columns>
76
87
  );
77
- const column = screen.getByTitle(/column/);
88
+ const column = screen.getByTestId(/column/);
78
89
 
79
90
  expect(column).toHaveStyle(`align-items: center`);
80
91
  });
81
92
 
82
93
  test('supports custom verticalAlign prop: bottom', () => {
83
94
  render(
84
- <Columns verticalAlign="bottom" title="column">
95
+ <Columns verticalAlign="bottom" data-testid="column">
85
96
  <Column>column</Column>
86
97
  </Columns>
87
98
  );
88
- const column = screen.getByTitle(/column/);
99
+ const column = screen.getByTestId(/column/);
89
100
 
90
101
  expect(column).toHaveStyle(`align-items: flex-end`);
91
102
  });
92
103
 
93
104
  test('renders correct HTML element', () => {
94
105
  render(
95
- <Columns title="column">
106
+ <Columns data-testid="column">
96
107
  <Column>column</Column>
97
108
  </Columns>
98
109
  );
99
- const column = screen.getByTitle(/column/);
110
+ const column = screen.getByTestId(/column/);
100
111
 
101
112
  expect(column instanceof HTMLDivElement).toBeTruthy();
102
113
  });
@@ -1,63 +1,63 @@
1
1
  import React, { Children } from 'react';
2
- import { useStyles } from '@marigold/system';
3
2
  import { Box } from '../Box';
4
3
  import flattenChildren from 'react-keyed-flatten-children';
4
+ import { ResponsiveStyleValue, useTheme } from '@marigold/system';
5
5
 
6
6
  type ColumnsProps = {
7
7
  className?: string;
8
- space?: number;
8
+ space?: ResponsiveStyleValue<string>;
9
9
  horizontalAlign?: 'left' | 'right' | 'center';
10
10
  verticalAlign?: 'top' | 'bottom' | 'center';
11
- title?: string; // Should only be used for testing.
11
+ };
12
+
13
+ const useAlignment = (direction: string) => {
14
+ switch (direction) {
15
+ case 'right':
16
+ return 'flex-end';
17
+ case 'bottom':
18
+ return 'flex-end';
19
+ case 'center':
20
+ return 'center';
21
+ }
22
+ return 'flex-start';
12
23
  };
13
24
 
14
25
  export const Columns: React.FC<ColumnsProps> = ({
15
- space = 0,
26
+ space = 'none',
16
27
  horizontalAlign = 'left',
17
28
  verticalAlign = 'top',
18
29
  className,
19
30
  children,
20
31
  ...props
21
32
  }) => {
22
- let columnItems = flattenChildren(children);
23
- let childClassNames = useStyles({ css: { p: `${space / 2}px` } });
33
+ const justifyContent = useAlignment(horizontalAlign);
34
+ const alignItems = useAlignment(verticalAlign);
24
35
 
25
- // horizontal Alignment
26
- let justify = 'flex-start';
27
- if (horizontalAlign === 'right') {
28
- justify = 'flex-end';
29
- } else if (horizontalAlign === 'center') {
30
- justify = 'center';
31
- }
32
-
33
- // vertical Alignment
34
- let alignItems = 'flex-start';
35
- if (verticalAlign === 'bottom') {
36
- alignItems = 'flex-end';
37
- } else if (verticalAlign === 'center') {
38
- alignItems = 'center';
39
- }
36
+ /**
37
+ * transform space string to space value from theme
38
+ */
39
+ const { css } = useTheme();
40
+ const spaceObject = css({ space }) as Object;
41
+ const spaceValue = Object.values(spaceObject)[0];
40
42
 
41
43
  return (
42
- <Box p={`${space}px`} display="flex" className={className}>
44
+ <Box p={space} display="flex" className={className}>
43
45
  <Box
44
- width={`calc(100% + ${space}px)`}
45
- m={`${-space / 2}px`}
46
+ width={`calc(100% + ${spaceValue}px)`}
47
+ m={`${-spaceValue / 2}px`}
46
48
  display="flex"
47
49
  flexWrap="wrap"
48
50
  alignItems={alignItems}
49
- justifyContent={justify}
51
+ justifyContent={justifyContent}
50
52
  {...props}
51
53
  >
52
54
  {Children.map(
53
- (columnItems as unknown) as React.ReactElement,
55
+ flattenChildren(children) as unknown as React.ReactElement,
54
56
  (child: React.ReactElement) => {
55
57
  return React.cloneElement(
56
58
  child,
57
- {
58
- className: childClassNames,
59
- },
60
- <Box className={child && child.props.className}>
59
+ {},
60
+ <Box css={{ p: `${spaceValue / 2}px` }}>
61
61
  {child.props.children}
62
62
  </Box>
63
63
  );
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import type { Meta, ComponentStory } from '@storybook/react';
3
+ import { Container } from './Container';
4
+ import { Text } from '../Text';
5
+
6
+ export default {
7
+ title: 'Components/Container',
8
+ } as Meta;
9
+
10
+ export const Basic: ComponentStory<typeof Container> = args => (
11
+ <Container {...args}>
12
+ <Text>Container with width=100%</Text>
13
+ </Container>
14
+ );
@@ -0,0 +1,88 @@
1
+ import React from 'react';
2
+ import type { Meta, ComponentStory } from '@storybook/react';
3
+ import { Dialog, useDialogButtonProps } from './Dialog';
4
+ import { Button } from '../Button';
5
+ import { Text } from '../Text';
6
+
7
+ export default {
8
+ title: 'Components/Dialog',
9
+ parameters: {
10
+ actions: {
11
+ handles: ['click'],
12
+ },
13
+ },
14
+ argTypes: {
15
+ isOpen: {
16
+ control: {
17
+ type: 'boolean',
18
+ },
19
+ description: 'handled by state from useDialogButtonProps',
20
+ options: [true, false],
21
+ table: {
22
+ defaultValue: {
23
+ summary: false,
24
+ },
25
+ },
26
+ },
27
+ title: {
28
+ control: {
29
+ type: 'text',
30
+ },
31
+ description: 'set dialog title',
32
+ },
33
+ close: {
34
+ control: {
35
+ type: 'text',
36
+ },
37
+ description: 'handled by state from useDialogButtonProps',
38
+ },
39
+ variant: {
40
+ control: {
41
+ type: 'text',
42
+ },
43
+ description: 'Dialog variant',
44
+ table: {
45
+ defaultValue: {
46
+ summary: '__default',
47
+ },
48
+ },
49
+ },
50
+ backdropVariant: {
51
+ control: {
52
+ type: 'text',
53
+ },
54
+ description: 'Dialog backdrop variant',
55
+ table: {
56
+ defaultValue: {
57
+ summary: 'backdrop',
58
+ },
59
+ },
60
+ },
61
+ },
62
+ } as Meta;
63
+
64
+ export const Basic: ComponentStory<typeof Dialog> = args => {
65
+ const { state, openButtonProps, openButtonRef } = useDialogButtonProps();
66
+ return (
67
+ <>
68
+ <Button
69
+ variant="secondary"
70
+ size="small"
71
+ {...openButtonProps}
72
+ ref={openButtonRef}
73
+ >
74
+ Open Dialog
75
+ </Button>
76
+ {state.isOpen && (
77
+ <Dialog
78
+ title="Dialog Title"
79
+ {...args}
80
+ isOpen={state.isOpen}
81
+ close={state.close}
82
+ >
83
+ <Text>Dialog content</Text>
84
+ </Dialog>
85
+ )}
86
+ </>
87
+ );
88
+ };
@@ -1,47 +1,158 @@
1
1
  import React from 'react';
2
- import { render, screen } from '@testing-library/react';
2
+ import { fireEvent, render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+
3
5
  import { ThemeProvider } from '@marigold/system';
4
- import { Dialog } from './Dialog';
6
+
7
+ import { Dialog, useDialogButtonProps } from './Dialog';
8
+ import { Button } from '../Button';
5
9
 
6
10
  const theme = {
11
+ space: {
12
+ none: 0,
13
+ xxsmall: 1,
14
+ xsmall: 2,
15
+ small: 4,
16
+ medium: 8,
17
+ large: 16,
18
+ },
7
19
  dialog: {
8
- wrapper: {
9
- alignItems: 'center',
20
+ __default: {
21
+ p: 'small',
22
+ },
23
+ default: {
24
+ p: 'medium',
25
+ },
26
+ backdrop: {
27
+ p: 'none',
10
28
  },
11
29
  },
12
30
  };
13
31
 
14
- test('supports default variant', () => {
32
+ type DialogComponentProps = {
33
+ title?: string;
34
+ variant?: string;
35
+ backdropVariant?: string;
36
+ };
37
+
38
+ const DialogComponent: React.FC<DialogComponentProps> = ({
39
+ variant,
40
+ backdropVariant,
41
+ }) => {
42
+ const { state, openButtonProps, openButtonRef } = useDialogButtonProps();
43
+ return (
44
+ <>
45
+ <Button {...openButtonProps} ref={openButtonRef}>
46
+ Open
47
+ </Button>
48
+ {state.isOpen && (
49
+ <Dialog
50
+ variant={variant}
51
+ backdropVariant={backdropVariant}
52
+ title="Title"
53
+ isOpen={state.isOpen}
54
+ close={state.close}
55
+ >
56
+ Content
57
+ </Dialog>
58
+ )}
59
+ </>
60
+ );
61
+ };
62
+
63
+ test('dialog can be opened by button', () => {
64
+ render(<DialogComponent />);
65
+ const button = screen.getByText(/Open/);
66
+ fireEvent.click(button);
67
+ const dialog = screen.getByText(/Content/);
68
+ expect(dialog).toBeDefined();
69
+ });
70
+
71
+ test('supports default variants', () => {
15
72
  render(
16
73
  <ThemeProvider theme={theme}>
17
- <Dialog title="default">Default</Dialog>
74
+ <DialogComponent />
18
75
  </ThemeProvider>
19
76
  );
20
- const dialog = screen.getByTitle(/default/);
77
+ const button = screen.getByText(/Open/);
78
+ fireEvent.click(button);
21
79
 
22
- expect(dialog).toHaveStyle(`align-items: center`);
80
+ const dialog = screen.getByRole(/dialog/);
81
+ expect(dialog).toHaveStyle(`padding: 4px`);
82
+ expect(dialog.parentElement).toHaveStyle(`padding: 0px`);
23
83
  });
24
84
 
25
- test('renders correct HTML element', () => {
85
+ test('supports other variants than default', () => {
26
86
  render(
27
87
  <ThemeProvider theme={theme}>
28
- <Dialog title="default">Default</Dialog>
88
+ <DialogComponent variant="default" backdropVariant="default" />
29
89
  </ThemeProvider>
30
90
  );
31
- const dialog = screen.getByTitle(/default/);
91
+ const button = screen.getByText(/Open/);
92
+ fireEvent.click(button);
32
93
 
33
- expect(dialog instanceof HTMLDivElement).toBeTruthy();
94
+ const dialog = screen.getByRole(/dialog/);
95
+ expect(dialog).toHaveStyle(`padding: 8px`);
96
+ expect(dialog.parentElement).toHaveStyle(`padding: 8px`);
34
97
  });
35
98
 
36
- test('accepts custom styles prop className', () => {
99
+ test('dialog has correct baseCSS styles', async () => {
37
100
  render(
38
101
  <ThemeProvider theme={theme}>
39
- <Dialog className="custom-class-name" title="dialog">
40
- Dialog
41
- </Dialog>
102
+ <DialogComponent variant="default" backdropVariant="default" />
42
103
  </ThemeProvider>
43
104
  );
44
- const dialog = screen.getByTitle(/dialog/);
105
+ const button = screen.getByText(/Open/);
106
+ fireEvent.click(button);
107
+
108
+ const dialog = screen.getByRole(/dialog/);
109
+ expect(dialog.firstChild).toHaveStyle(`display: flex`);
110
+ expect(dialog.firstChild?.lastChild).toHaveStyle(`alignItems: start`);
111
+
112
+ // ModalDialog baseCSS
113
+ expect(dialog.parentElement).toHaveStyle(`display: grid`);
114
+ });
115
+
116
+ test('dialog has correct baseCSS styles with theme index', async () => {
117
+ render(
118
+ <ThemeProvider theme={theme}>
119
+ <DialogComponent variant="default" backdropVariant="default" />
120
+ </ThemeProvider>
121
+ );
122
+ const button = screen.getByText(/Open/);
123
+ fireEvent.click(button);
124
+
125
+ const dialog = screen.getByRole(/dialog/);
126
+ expect(dialog.firstChild).toHaveStyle(`paddingLeft: 16`);
127
+ expect(dialog.firstChild?.lastChild).toHaveStyle(`paddingTop: 2`);
128
+
129
+ // find all buttons to get the close and not the open button
130
+ const onCloseButton = await screen.findAllByRole('button');
131
+ expect(onCloseButton[1]).toHaveStyle(`paddingLeft: 1`);
132
+ });
133
+
134
+ test('close Dialog by escape key', () => {
135
+ render(<DialogComponent />);
136
+ const button = screen.getByText(/Open/);
137
+ fireEvent.click(button);
138
+
139
+ const dialog = screen.getByText(/Content/);
140
+ userEvent.type(dialog, '{esc}');
141
+ expect(dialog).not.toBeVisible();
142
+ });
143
+
144
+ test('close Dialog by close button', async () => {
145
+ render(<DialogComponent />);
146
+ const button = screen.getByText(/Open/);
147
+ fireEvent.click(button);
148
+
149
+ const dialog = screen.getByRole(/dialog/);
150
+ expect(dialog).toBeVisible();
151
+
152
+ // find all buttons to get the close and not the open button
153
+ const onCloseButton = await screen.findAllByRole('button');
154
+ expect(onCloseButton[1]).toBeVisible();
155
+ fireEvent.click(onCloseButton[1]);
45
156
 
46
- expect(dialog.className).toMatch('custom-class-name');
157
+ expect(dialog).not.toBeVisible();
47
158
  });
@@ -1,32 +1,130 @@
1
- import React from 'react';
1
+ import React, { RefObject } from 'react';
2
2
  import { ComponentProps } from '@marigold/types';
3
-
3
+ import { useOverlayTriggerState } from '@react-stately/overlays';
4
+ import { OverlayContainer } from '@react-aria/overlays';
5
+ import { useButton } from '@react-aria/button';
4
6
  import { Close } from '@marigold/icons';
5
- import { Button } from '../Button';
7
+
6
8
  import { Box } from '../Box';
9
+ import { Button } from '../Button';
10
+ import { Text } from '../Text';
7
11
 
12
+ import { ModalDialog, ModalDialogProps } from './ModalDialog';
13
+
14
+ // Props
15
+ // ---------------
8
16
  export type DialogProps = {
9
- onClose?: ComponentProps<typeof Button>['onClick'];
10
- } & ComponentProps<'div'>;
17
+ backdropVariant?: string;
18
+ close: ComponentProps<typeof Button>['onClick'];
19
+ isOpen: boolean;
20
+ title?: string;
21
+ variant?: string;
22
+ } & ModalDialogProps &
23
+ ComponentProps<'div'>;
11
24
 
25
+ // Component
26
+ // ---------------
12
27
  export const Dialog: React.FC<DialogProps> = ({
13
- onClose,
28
+ backdropVariant,
14
29
  children,
15
30
  className,
31
+ close,
32
+ isOpen,
33
+ title,
34
+ variant,
16
35
  ...props
17
36
  }) => {
37
+ const closeButtonRef = React.useRef<HTMLElement>() as RefObject<HTMLElement>;
38
+
39
+ // useButton ensures that focus management is handled correctly,
40
+ // across all browsers. Focus is restored to the button once the
41
+ // dialog closes.
42
+ const { buttonProps: closeButtonProps } = useButton(
43
+ {
44
+ onPress: () => close(),
45
+ },
46
+ closeButtonRef
47
+ );
48
+
18
49
  return (
19
- <Box display="flex" width="100%">
20
- <Box {...props} variant="dialog.wrapper" className={className}>
21
- <Box display="flex">
22
- <Box variant="dialog.body">{children}</Box>
23
- <Box variant="dialog.onClose">
24
- <Button variant="text.root" onClick={onClose}>
50
+ <OverlayContainer>
51
+ <ModalDialog
52
+ variant={variant}
53
+ backdropVariant={backdropVariant}
54
+ isOpen={isOpen}
55
+ onClose={close}
56
+ isDismissable
57
+ {...props}
58
+ >
59
+ <Box
60
+ __baseCSS={{
61
+ display: 'flex',
62
+ justifyContent: 'space-between',
63
+ borderRadius: 'small',
64
+ pl: 'large',
65
+ pb: 'large',
66
+ }}
67
+ className={className}
68
+ >
69
+ <Box pt="medium">
70
+ {title && (
71
+ <Text as="h4" variant="headline4">
72
+ {title}
73
+ </Text>
74
+ )}
75
+ {children}
76
+ </Box>
77
+ <Box
78
+ __baseCSS={{
79
+ display: 'flex',
80
+ justifyContent: 'flex-end',
81
+ alignItems: 'start',
82
+ paddingTop: 'xsmall',
83
+ paddingX: 'xsmall',
84
+ }}
85
+ >
86
+ <Box
87
+ as={Button}
88
+ __baseCSS={{
89
+ color: 'text',
90
+ bg: 'transparent',
91
+ lineHeight: 'xsmall',
92
+ px: 'xxsmall',
93
+ ':hover': {
94
+ color: 'text',
95
+ bg: 'transparent',
96
+ cursor: 'pointer',
97
+ },
98
+ ':focus': {
99
+ outline: 0,
100
+ },
101
+ }}
102
+ {...closeButtonProps}
103
+ ref={closeButtonRef}
104
+ >
25
105
  <Close size={16} />
26
- </Button>
106
+ </Box>
27
107
  </Box>
28
108
  </Box>
29
- </Box>
30
- </Box>
109
+ </ModalDialog>
110
+ </OverlayContainer>
111
+ );
112
+ };
113
+
114
+ // get the overlayTriggerState and openButton props for using the dialog component
115
+ export const useDialogButtonProps = () => {
116
+ const state = useOverlayTriggerState({});
117
+ const openButtonRef = React.useRef<HTMLElement>() as RefObject<HTMLElement>;
118
+ const { buttonProps: openButtonProps } = useButton(
119
+ {
120
+ onPress: () => state.open(),
121
+ },
122
+ openButtonRef
31
123
  );
124
+
125
+ return {
126
+ state,
127
+ openButtonProps,
128
+ openButtonRef,
129
+ };
32
130
  };