@marigold/components 0.1.0 → 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 (112) 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 +16 -1
  4. package/dist/Box/Box.d.ts +2 -1
  5. package/dist/Button/Button.d.ts +4 -6
  6. package/dist/Card/Card.d.ts +9 -0
  7. package/dist/Card/index.d.ts +1 -0
  8. package/dist/Checkbox/Checkbox.d.ts +3 -0
  9. package/dist/Checkbox/CheckboxIcons.d.ts +9 -0
  10. package/dist/Column/Column.d.ts +3 -1
  11. package/dist/Dialog/Dialog.d.ts +8 -1
  12. package/dist/Dialog/ModalDialog.d.ts +5 -0
  13. package/dist/Field/Field.d.ts +4 -2
  14. package/dist/Label/Label.d.ts +2 -1
  15. package/dist/Provider/MarigoldProvider.d.ts +3 -0
  16. package/dist/Provider/index.d.ts +3 -0
  17. package/dist/Radio/Radio.d.ts +3 -0
  18. package/dist/Radio/RadioIcons.d.ts +9 -0
  19. package/dist/Select/ListBox.d.ts +8 -0
  20. package/dist/Select/ListBoxSection.d.ts +8 -0
  21. package/dist/Select/Option.d.ts +8 -0
  22. package/dist/Select/Popover.d.ts +9 -0
  23. package/dist/Select/Select.d.ts +11 -4
  24. package/dist/Stack/Stack.d.ts +1 -3
  25. package/dist/Textarea/Textarea.d.ts +2 -1
  26. package/dist/components.cjs.development.js +868 -357
  27. package/dist/components.cjs.development.js.map +1 -1
  28. package/dist/components.cjs.production.min.js +1 -1
  29. package/dist/components.cjs.production.min.js.map +1 -1
  30. package/dist/components.esm.js +837 -359
  31. package/dist/components.esm.js.map +1 -1
  32. package/dist/index.d.ts +4 -0
  33. package/package.json +18 -3
  34. package/src/ActionGroup/ActionGroup.stories.mdx +62 -0
  35. package/src/ActionGroup/ActionGroup.test.tsx +83 -0
  36. package/src/ActionGroup/ActionGroup.tsx +43 -0
  37. package/src/ActionGroup/index.ts +1 -0
  38. package/src/Alert/Alert.stories.mdx +28 -38
  39. package/src/Alert/Alert.test.tsx +1 -1
  40. package/src/Alert/Alert.tsx +11 -33
  41. package/src/Badge/Badge.stories.mdx +48 -34
  42. package/src/Badge/Badge.test.tsx +7 -15
  43. package/src/Badge/Badge.tsx +1 -2
  44. package/src/Box/Box.stories.mdx +322 -26
  45. package/src/Box/Box.tsx +58 -50
  46. package/src/Button/Button.stories.mdx +54 -166
  47. package/src/Button/Button.test.tsx +48 -8
  48. package/src/Button/Button.tsx +47 -18
  49. package/src/Card/Card.stories.mdx +49 -0
  50. package/src/Card/Card.test.tsx +66 -0
  51. package/src/Card/Card.tsx +36 -0
  52. package/src/Card/index.ts +1 -0
  53. package/src/Checkbox/Checkbox.stories.mdx +76 -71
  54. package/src/Checkbox/Checkbox.test.tsx +70 -24
  55. package/src/Checkbox/Checkbox.tsx +68 -49
  56. package/src/Checkbox/CheckboxIcons.tsx +49 -0
  57. package/src/Column/Column.stories.mdx +39 -64
  58. package/src/Column/Column.test.tsx +8 -0
  59. package/src/Column/Column.tsx +12 -2
  60. package/src/Columns/Columns.stories.mdx +58 -240
  61. package/src/Container/Container.stories.mdx +8 -25
  62. package/src/Dialog/Dialog.stories.mdx +65 -56
  63. package/src/Dialog/Dialog.test.tsx +64 -24
  64. package/src/Dialog/Dialog.tsx +64 -12
  65. package/src/Dialog/ModalDialog.tsx +47 -0
  66. package/src/Divider/Divider.stories.mdx +28 -34
  67. package/src/Field/Field.stories.mdx +88 -48
  68. package/src/Field/Field.test.tsx +31 -40
  69. package/src/Field/Field.tsx +26 -21
  70. package/src/Heading/Heading.stories.mdx +30 -82
  71. package/src/Hidden/Hidden.stories.mdx +29 -54
  72. package/src/Image/Image.stories.mdx +26 -30
  73. package/src/Input/Input.stories.mdx +52 -36
  74. package/src/Label/Label.stories.mdx +47 -29
  75. package/src/Label/Label.test.tsx +15 -1
  76. package/src/Label/Label.tsx +20 -7
  77. package/src/Link/Link.stories.mdx +36 -29
  78. package/src/Menu/Menu.stories.mdx +71 -39
  79. package/src/Menu/Menu.test.tsx +4 -4
  80. package/src/Menu/Menu.tsx +12 -14
  81. package/src/MenuItem/MenuItem.stories.mdx +32 -27
  82. package/src/MenuItem/MenuItem.test.tsx +15 -11
  83. package/src/MenuItem/MenuItem.tsx +7 -10
  84. package/src/Message/Message.stories.mdx +35 -35
  85. package/src/Message/Message.tsx +2 -10
  86. package/src/Provider/MarigoldProvider.test.tsx +126 -0
  87. package/src/Provider/MarigoldProvider.tsx +29 -0
  88. package/src/Provider/index.ts +3 -0
  89. package/src/Radio/Radio.stories.mdx +78 -92
  90. package/src/Radio/Radio.test.tsx +57 -15
  91. package/src/Radio/Radio.tsx +71 -51
  92. package/src/Radio/RadioIcons.tsx +39 -0
  93. package/src/Select/ListBox.tsx +39 -0
  94. package/src/Select/ListBoxSection.tsx +40 -0
  95. package/src/Select/Option.tsx +48 -0
  96. package/src/Select/Popover.tsx +50 -0
  97. package/src/Select/Select.stories.mdx +70 -36
  98. package/src/Select/Select.test.tsx +279 -35
  99. package/src/Select/Select.tsx +151 -18
  100. package/src/Slider/Slider.stories.mdx +22 -49
  101. package/src/Stack/Stack.stories.mdx +40 -94
  102. package/src/Stack/Stack.test.tsx +84 -65
  103. package/src/Stack/Stack.tsx +25 -41
  104. package/src/Text/Text.stories.mdx +52 -52
  105. package/src/Text/Text.tsx +13 -14
  106. package/src/Textarea/Textarea.stories.mdx +65 -56
  107. package/src/Textarea/Textarea.test.tsx +4 -5
  108. package/src/Textarea/Textarea.tsx +28 -37
  109. package/src/ValidationMessage/ValidationMessage.stories.mdx +26 -26
  110. package/src/ValidationMessage/ValidationMessage.test.tsx +4 -4
  111. package/src/ValidationMessage/ValidationMessage.tsx +11 -12
  112. package/src/index.ts +5 -0
@@ -1,78 +1,79 @@
1
1
  import React from 'react';
2
- import { CircleUnchecked, CircleChecked, Required } from '@marigold/icons';
3
- import { useStyles } from '@marigold/system';
4
2
  import { ComponentProps } from '@marigold/types';
3
+ import { Exclamation } from '@marigold/icons';
4
+
5
5
  import { Box } from '../Box';
6
6
  import { Label } from '../Label';
7
+ import { ValidationMessage } from '../ValidationMessage';
8
+
9
+ import { RadioChecked, RadioUnchecked } from './RadioIcons';
7
10
 
8
11
  // Radio Icon
9
12
  // ---------------
10
13
  type RadioIconProps = {
11
- className?: string;
12
14
  variant?: string;
13
15
  checked?: boolean;
16
+ disabled?: boolean;
17
+ error?: boolean;
14
18
  children?: never;
15
19
  };
16
20
 
17
21
  const RadioIcon: React.FC<RadioIconProps> = ({
18
- className,
19
22
  variant,
20
23
  checked,
24
+ disabled,
25
+ error,
21
26
  }) => {
22
- const radioIconStyle = useStyles({
23
- variant: `form.${variant}`,
24
- css: {
25
- ariaHidden: 'true',
26
- mr: 2,
27
- verticalAlign: 'middle',
28
- ':hover': { cursor: 'pointer' },
29
- 'input:disabled ~ &': {
30
- color: 'muted',
31
- cursor: 'not-allowed',
32
- },
33
- },
34
- className,
35
- });
36
-
37
27
  if (checked) {
38
- return <CircleChecked className={radioIconStyle} />;
28
+ return (
29
+ <Box as={RadioChecked} variant={`radio.${variant}`} disabled={disabled} />
30
+ );
39
31
  }
40
- return <CircleUnchecked className={radioIconStyle} />;
32
+ return (
33
+ <Box
34
+ as={RadioUnchecked}
35
+ variant={`radio.${variant}`}
36
+ disabled={disabled}
37
+ error={error}
38
+ />
39
+ );
41
40
  };
42
41
 
43
42
  // Radio Input
44
43
  // ---------------
45
44
  type RadioInputProps = {
46
45
  variant?: string;
46
+ error?: boolean;
47
47
  } & ComponentProps<'input'>;
48
48
 
49
49
  const RadioInput: React.FC<RadioInputProps> = ({
50
50
  className,
51
- variant = 'radio',
51
+ variant = 'default',
52
+ error,
52
53
  ...props
53
- }) => {
54
- const radioStyle = useStyles({
55
- css: {
56
- position: 'absolute',
57
- opacity: 0,
58
- zIndex: -1,
59
- width: 1,
60
- height: 1,
61
- overflow: 'hidden',
62
- },
63
- });
64
-
65
- return (
66
- <Box display="inline-block">
67
- <input type="radio" className={radioStyle} {...props} />
68
- <RadioIcon
69
- checked={props.checked}
70
- className={className}
71
- variant={variant}
72
- />
73
- </Box>
74
- );
75
- };
54
+ }) => (
55
+ <Box display="inline-block" className={className}>
56
+ <Box
57
+ as="input"
58
+ type="radio"
59
+ css={{
60
+ position: 'absolute',
61
+ opacity: 0,
62
+ zIndex: -1,
63
+ width: 1,
64
+ height: 1,
65
+ overflow: 'hidden',
66
+ }}
67
+ {...props}
68
+ />
69
+ <RadioIcon
70
+ checked={props.checked}
71
+ variant={variant}
72
+ disabled={props.disabled}
73
+ error={error}
74
+ />
75
+ </Box>
76
+ );
76
77
 
77
78
  // Radio
78
79
  // ---------------
@@ -80,16 +81,35 @@ export type RadioProps = {
80
81
  id: string;
81
82
  label?: string;
82
83
  required?: boolean;
84
+ error?: boolean;
85
+ errorMessage?: string;
83
86
  } & RadioInputProps;
84
87
 
85
- export const Radio: React.FC<RadioProps> = ({ label, required, ...props }) => {
88
+ export const Radio: React.FC<RadioProps> = ({
89
+ label,
90
+ required,
91
+ error,
92
+ errorMessage,
93
+ ...props
94
+ }) => {
86
95
  if (label) {
87
96
  return (
88
- <Label htmlFor={props.id}>
89
- <RadioInput {...props} />
90
- {label}
91
- {required && <Required size={16} />}
92
- </Label>
97
+ <>
98
+ <Label
99
+ htmlFor={props.id}
100
+ required={required}
101
+ variant={props.disabled ? 'disabled' : 'inline'}
102
+ >
103
+ <Box as={RadioInput} pr="8px" error={error} {...props} />
104
+ {label}
105
+ </Label>
106
+ {error && errorMessage && (
107
+ <ValidationMessage>
108
+ <Exclamation size={16} />
109
+ {errorMessage}
110
+ </ValidationMessage>
111
+ )}
112
+ </>
93
113
  );
94
114
  }
95
115
 
@@ -0,0 +1,39 @@
1
+ import React from 'react';
2
+ import { SVG } from '@marigold/icons';
3
+
4
+ import { Box } from '../Box';
5
+
6
+ export const RadioChecked = ({ disabled = false, ...props }) => (
7
+ <SVG width="16" height="32" viewBox="0 0 16 32" fill="none" {...props}>
8
+ <Box
9
+ as="circle"
10
+ cx="8"
11
+ cy="16"
12
+ r="7.5"
13
+ variant={disabled ? 'radio.checked.disabled' : 'radio.checked'}
14
+ />
15
+ <Box as="circle" cx="8" cy="16" r="3" variant="radio.checked.circle" />
16
+ </SVG>
17
+ );
18
+
19
+ export const RadioUnchecked = ({
20
+ disabled = false,
21
+ error = false,
22
+ ...props
23
+ }) => (
24
+ <SVG width="16" height="32" viewBox="0 0 16 32" fill="none" {...props}>
25
+ <Box
26
+ as="circle"
27
+ cx="8"
28
+ cy="16"
29
+ r="7.5"
30
+ variant={
31
+ disabled
32
+ ? 'radio.unchecked.disabled'
33
+ : error
34
+ ? 'radio.unchecked.error'
35
+ : 'radio.unchecked'
36
+ }
37
+ />
38
+ </SVG>
39
+ );
@@ -0,0 +1,39 @@
1
+ import React, { useRef } from 'react';
2
+ import { useListBox } from '@react-aria/listbox';
3
+ import type { AriaListBoxOptions } from '@react-aria/listbox';
4
+ import type { ListState } from '@react-stately/list';
5
+
6
+ import { Box } from '../Box';
7
+ import { Option } from './Option';
8
+ import { ListBoxSection } from './ListBoxSection';
9
+
10
+ interface ListBoxProps extends AriaListBoxOptions<unknown> {
11
+ state: ListState<unknown>;
12
+ error?: boolean;
13
+ }
14
+
15
+ export const ListBox = (props: ListBoxProps) => {
16
+ const ref = useRef<HTMLUListElement>(null);
17
+ const { state, error } = props;
18
+ const { listBoxProps } = useListBox(props, state, ref);
19
+
20
+ return (
21
+ <Box
22
+ as="ul"
23
+ css={{
24
+ listStyle: 'none',
25
+ }}
26
+ {...listBoxProps}
27
+ variant={error ? 'select.listbox.error' : 'select.listbox'}
28
+ ref={ref}
29
+ >
30
+ {[...state.collection].map(item =>
31
+ item.type === 'section' ? (
32
+ <ListBoxSection key={item.key} section={item} state={state} />
33
+ ) : (
34
+ <Option key={item.key} item={item} state={state} />
35
+ )
36
+ )}
37
+ </Box>
38
+ );
39
+ };
@@ -0,0 +1,40 @@
1
+ import React from 'react';
2
+ import { useListBoxSection } from '@react-aria/listbox';
3
+ import type { ListState } from '@react-stately/list';
4
+ import type { Node } from '@react-types/shared';
5
+
6
+ import { Box } from '../Box';
7
+ import { Option } from './Option';
8
+
9
+ interface SectionProps {
10
+ section: Node<unknown>;
11
+ state: ListState<unknown>;
12
+ }
13
+
14
+ export const ListBoxSection = ({ section, state }: SectionProps) => {
15
+ const { itemProps, headingProps, groupProps } = useListBoxSection({
16
+ heading: section.rendered,
17
+ 'aria-label': section['aria-label'],
18
+ });
19
+
20
+ return (
21
+ <Box
22
+ as="li"
23
+ {...itemProps}
24
+ css={{
25
+ cursor: 'not-allowed',
26
+ }}
27
+ >
28
+ {section.rendered && (
29
+ <Box as="span" {...headingProps} variant={'select.section'}>
30
+ {section.rendered}
31
+ </Box>
32
+ )}
33
+ <Box as="ul" {...groupProps}>
34
+ {[...section.childNodes].map(node => (
35
+ <Option key={node.key} item={node} state={state} />
36
+ ))}
37
+ </Box>
38
+ </Box>
39
+ );
40
+ };
@@ -0,0 +1,48 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import type { ListState } from '@react-stately/list';
3
+ import type { Node } from '@react-types/shared';
4
+ import { useOption } from '@react-aria/listbox';
5
+
6
+ import { Box } from '../Box';
7
+
8
+ interface OptionProps {
9
+ item: Node<unknown>;
10
+ state: ListState<unknown>;
11
+ }
12
+
13
+ export const Option = ({ item, state }: OptionProps) => {
14
+ const ref = useRef<HTMLLIElement>(null);
15
+ const [disabled, setDisabled] = useState(false);
16
+ const { optionProps, isSelected } = useOption(
17
+ {
18
+ key: item.key,
19
+ },
20
+ state,
21
+ ref
22
+ );
23
+
24
+ useEffect(() => {
25
+ for (const key of state.disabledKeys.values()) {
26
+ if (key === item.key) {
27
+ setDisabled(true);
28
+ }
29
+ }
30
+ }, [state.disabledKeys, item.key]);
31
+
32
+ return (
33
+ <Box
34
+ as="li"
35
+ {...optionProps}
36
+ ref={ref}
37
+ variant={
38
+ isSelected
39
+ ? 'select.option.selected'
40
+ : disabled
41
+ ? 'select.option.disabled'
42
+ : 'select.option'
43
+ }
44
+ >
45
+ {item.rendered}
46
+ </Box>
47
+ );
48
+ };
@@ -0,0 +1,50 @@
1
+ import React, { forwardRef, RefObject } from 'react';
2
+ import { FocusScope } from '@react-aria/focus';
3
+ import {
4
+ DismissButton,
5
+ OverlayContainer,
6
+ useModal,
7
+ useOverlay,
8
+ } from '@react-aria/overlays';
9
+ import { mergeProps } from '@react-aria/utils';
10
+
11
+ import { Box } from '../Box';
12
+
13
+ interface PopoverProps {
14
+ isOpen?: boolean;
15
+ onClose?: () => void;
16
+ ref?: React.Ref<HTMLDivElement>;
17
+ className?: string;
18
+ }
19
+
20
+ export const Popover: React.FC<PopoverProps> = forwardRef(
21
+ ({ children, className, isOpen, onClose, ...otherProps }, ref) => {
22
+ // Handle events that should cause the popup to close,
23
+ const { overlayProps } = useOverlay(
24
+ {
25
+ isOpen,
26
+ onClose,
27
+ shouldCloseOnBlur: true,
28
+ isDismissable: true,
29
+ },
30
+ ref as RefObject<HTMLElement>
31
+ );
32
+ // Hide content outside the modal from screen readers.
33
+ const { modalProps } = useModal();
34
+
35
+ return (
36
+ <OverlayContainer>
37
+ <FocusScope restoreFocus>
38
+ <Box
39
+ {...mergeProps(overlayProps, otherProps, modalProps)}
40
+ className={className}
41
+ ref={ref}
42
+ >
43
+ {children}
44
+ <DismissButton onDismiss={onClose} />
45
+ </Box>
46
+ </FocusScope>
47
+ </OverlayContainer>
48
+ );
49
+ }
50
+ );
@@ -1,44 +1,78 @@
1
- import { Meta, Story, Canvas } from '@storybook/addon-docs/blocks';
2
- import { Label } from '../Label';
1
+ import { ArgsTable, Canvas, Meta, Story } from '@storybook/addon-docs';
3
2
  import { Select } from './Select';
3
+ import { Item } from '@marigold/components';
4
4
 
5
- <Meta title="Components/Select" />
5
+ <Meta
6
+ title="Components/Select"
7
+ argTypes={{
8
+ placeholder: {
9
+ control: {
10
+ type: 'text',
11
+ },
12
+ table: {
13
+ defaultValue: {
14
+ summary: 'Select an option',
15
+ },
16
+ },
17
+ },
18
+ disabled: {
19
+ control: {
20
+ type: 'boolean',
21
+ },
22
+ options: [true, false],
23
+ table: {
24
+ defaultValue: {
25
+ summary: false,
26
+ },
27
+ },
28
+ },
29
+ required: {
30
+ control: {
31
+ type: 'boolean',
32
+ },
33
+ options: [true, false],
34
+ table: {
35
+ defaultValue: {
36
+ summary: false,
37
+ },
38
+ },
39
+ },
40
+ error: {
41
+ control: {
42
+ type: 'boolean',
43
+ },
44
+ description: 'Error',
45
+ table: {
46
+ defaultValue: {
47
+ summary: false,
48
+ },
49
+ },
50
+ },
51
+ errorMessage: {
52
+ control: {
53
+ type: 'text',
54
+ },
55
+ description: 'Error Message',
56
+ },
57
+ width: {
58
+ control: 'number',
59
+ },
60
+ }}
61
+ />
6
62
 
7
63
  # Select
8
64
 
9
- ## Description
10
-
11
- Dropdown for selecting a value among different options.
12
-
13
- ## Import
14
-
15
- ```tsx
16
- import { Select } from '@marigold/components';
17
- ```
18
-
19
- ## Usage
65
+ export const Template = args => (
66
+ <Select label="Favorite Color" htmlFor="id" {...args}>
67
+ {/* Storybook crashes with imported <Item> component
68
+ <Item>Red</Item>
69
+ <Item>Orange</Item>
70
+ <Item>Yellow</Item> */}
71
+ </Select>
72
+ );
20
73
 
21
74
  <Canvas>
22
- <Story name="Select">
23
- <Label htmlFor="select">
24
- Dropdown
25
- <Select id="select">
26
- <option selected>Choose here</option>
27
- <option>Hi</option>
28
- <option>Hey</option>
29
- <option>Blubb</option>
30
- </Select>
31
- </Label>
32
- </Story>
33
- <Story name="Select disabled">
34
- <Label htmlFor="select2">
35
- Dropdown disabled
36
- <Select disabled>
37
- <option selected>Can't choose here</option>
38
- <option>Hi</option>
39
- <option>Hey</option>
40
- <option>Blubb</option>
41
- </Select>
42
- </Label>
43
- </Story>
75
+ <Story name="Default">{Template.bind({})}</Story>
44
76
  </Canvas>
77
+
78
+ <ArgsTable story="Default" />