@marigold/components 0.0.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ActionGroup/ActionGroup.d.ts +9 -0
- package/dist/ActionGroup/index.d.ts +1 -0
- package/dist/Alert/Alert.d.ts +20 -2
- package/dist/Badge/Badge.d.ts +8 -0
- package/dist/Badge/index.d.ts +1 -0
- package/dist/Box/Box.d.ts +47 -0
- package/dist/Box/index.d.ts +1 -0
- package/dist/Button/Button.d.ts +4 -3
- package/dist/Card/Card.d.ts +9 -0
- package/dist/Card/index.d.ts +1 -0
- package/dist/Checkbox/Checkbox.d.ts +14 -2
- package/dist/Checkbox/CheckboxIcons.d.ts +9 -0
- package/dist/Column/Column.d.ts +8 -0
- package/dist/Column/index.d.ts +1 -0
- package/dist/Columns/Columns.d.ts +10 -0
- package/dist/Columns/index.d.ts +1 -0
- package/dist/Container/Container.d.ts +6 -0
- package/dist/Container/index.d.ts +1 -0
- package/dist/Dialog/Dialog.d.ts +14 -0
- package/dist/Dialog/ModalDialog.d.ts +5 -0
- package/dist/Dialog/index.d.ts +1 -0
- package/dist/Divider/Divider.d.ts +7 -0
- package/dist/Divider/index.d.ts +1 -0
- package/dist/Field/Field.d.ts +11 -0
- package/dist/Field/index.d.ts +1 -0
- package/dist/Heading/Heading.d.ts +7 -5
- package/dist/Hidden/Hidden.d.ts +5 -0
- package/dist/Hidden/index.d.ts +1 -0
- package/dist/Image/Image.d.ts +7 -0
- package/dist/Image/index.d.ts +1 -0
- package/dist/Input/Input.d.ts +6 -0
- package/dist/Input/index.d.ts +1 -0
- package/dist/Label/Label.d.ts +8 -5
- package/dist/Link/Link.d.ts +7 -3
- package/dist/Menu/Menu.d.ts +12 -0
- package/dist/Menu/index.d.ts +1 -0
- package/dist/MenuItem/MenuItem.d.ts +7 -0
- package/dist/MenuItem/index.d.ts +1 -0
- package/dist/Message/Message.d.ts +7 -0
- package/dist/Message/index.d.ts +1 -0
- package/dist/Provider/MarigoldProvider.d.ts +3 -0
- package/dist/Provider/index.d.ts +3 -0
- package/dist/Radio/Radio.d.ts +14 -2
- package/dist/Radio/RadioIcons.d.ts +9 -0
- package/dist/Select/ListBox.d.ts +8 -0
- package/dist/Select/ListBoxSection.d.ts +8 -0
- package/dist/Select/Option.d.ts +8 -0
- package/dist/Select/Popover.d.ts +9 -0
- package/dist/Select/Select.d.ts +13 -3
- package/dist/Slider/Slider.d.ts +6 -3
- package/dist/Stack/Stack.d.ts +7 -0
- package/dist/Stack/index.d.ts +1 -0
- package/dist/Text/Text.d.ts +12 -3
- package/dist/Textarea/Textarea.d.ts +11 -3
- package/dist/ValidationMessage/ValidationMessage.d.ts +6 -0
- package/dist/ValidationMessage/index.d.ts +1 -0
- package/dist/components.cjs.development.js +1308 -195
- package/dist/components.cjs.development.js.map +1 -1
- package/dist/components.cjs.production.min.js +1 -1
- package/dist/components.cjs.production.min.js.map +1 -1
- package/dist/components.esm.js +1267 -185
- package/dist/components.esm.js.map +1 -1
- package/dist/index.d.ts +21 -4
- package/dist/theme.d.ts +24 -4
- package/package.json +24 -4
- package/src/ActionGroup/ActionGroup.stories.mdx +62 -0
- package/src/ActionGroup/ActionGroup.test.tsx +83 -0
- package/src/ActionGroup/ActionGroup.tsx +43 -0
- package/src/ActionGroup/index.ts +1 -0
- package/src/Alert/Alert.stories.mdx +30 -42
- package/src/Alert/Alert.test.tsx +37 -22
- package/src/Alert/Alert.tsx +31 -21
- package/src/Badge/Badge.stories.mdx +57 -0
- package/src/Badge/Badge.test.tsx +61 -0
- package/src/Badge/Badge.tsx +25 -0
- package/src/Badge/index.ts +1 -0
- package/src/Box/Box.stories.mdx +334 -0
- package/src/Box/Box.test.tsx +133 -0
- package/src/Box/Box.tsx +165 -0
- package/src/Box/index.ts +1 -0
- package/src/Button/Button.stories.mdx +58 -134
- package/src/Button/Button.test.tsx +65 -23
- package/src/Button/Button.tsx +48 -14
- package/src/Card/Card.stories.mdx +49 -0
- package/src/Card/Card.test.tsx +66 -0
- package/src/Card/Card.tsx +36 -0
- package/src/Card/index.ts +1 -0
- package/src/Checkbox/Checkbox.stories.mdx +79 -101
- package/src/Checkbox/Checkbox.test.tsx +73 -32
- package/src/Checkbox/Checkbox.tsx +114 -35
- package/src/Checkbox/CheckboxIcons.tsx +49 -0
- package/src/Column/Column.stories.mdx +49 -0
- package/src/Column/Column.test.tsx +32 -0
- package/src/Column/Column.tsx +27 -0
- package/src/Column/index.ts +1 -0
- package/src/Columns/Columns.stories.mdx +65 -0
- package/src/Columns/Columns.test.tsx +102 -0
- package/src/Columns/Columns.tsx +69 -0
- package/src/Columns/index.ts +1 -0
- package/src/Container/Container.stories.mdx +19 -0
- package/src/Container/Container.test.tsx +26 -0
- package/src/Container/Container.tsx +13 -0
- package/src/Container/index.ts +1 -0
- package/src/Dialog/Dialog.stories.mdx +73 -0
- package/src/Dialog/Dialog.test.tsx +87 -0
- package/src/Dialog/Dialog.tsx +84 -0
- package/src/Dialog/ModalDialog.tsx +47 -0
- package/src/Dialog/index.ts +1 -0
- package/src/Divider/Divider.stories.mdx +37 -0
- package/src/Divider/Divider.test.tsx +63 -0
- package/src/Divider/Divider.tsx +13 -0
- package/src/Divider/index.ts +1 -0
- package/src/Field/Field.stories.mdx +97 -0
- package/src/Field/Field.test.tsx +80 -0
- package/src/Field/Field.tsx +54 -0
- package/src/Field/index.ts +1 -0
- package/src/Heading/Heading.stories.mdx +36 -76
- package/src/Heading/Heading.test.tsx +31 -17
- package/src/Heading/Heading.tsx +15 -12
- package/src/Hidden/Hidden.stories.mdx +39 -0
- package/src/Hidden/Hidden.test.tsx +24 -0
- package/src/Hidden/Hidden.tsx +16 -0
- package/src/Hidden/index.ts +1 -0
- package/src/Image/Image.stories.mdx +36 -0
- package/src/Image/Image.test.tsx +70 -0
- package/src/Image/Image.tsx +13 -0
- package/src/Image/index.ts +1 -0
- package/src/Input/Input.stories.mdx +61 -0
- package/src/Input/Input.test.tsx +70 -0
- package/src/Input/Input.tsx +13 -0
- package/src/Input/index.ts +1 -0
- package/src/Label/Label.stories.mdx +50 -34
- package/src/Label/Label.test.tsx +45 -16
- package/src/Label/Label.tsx +26 -17
- package/src/Link/Link.stories.mdx +40 -31
- package/src/Link/Link.test.tsx +53 -28
- package/src/Link/Link.tsx +32 -14
- package/src/Menu/Menu.stories.mdx +81 -0
- package/src/Menu/Menu.test.tsx +79 -0
- package/src/Menu/Menu.tsx +41 -0
- package/src/Menu/index.ts +1 -0
- package/src/MenuItem/MenuItem.stories.mdx +37 -0
- package/src/MenuItem/MenuItem.test.tsx +63 -0
- package/src/MenuItem/MenuItem.tsx +23 -0
- package/src/MenuItem/index.ts +1 -0
- package/src/Message/Message.stories.mdx +44 -0
- package/src/Message/Message.test.tsx +87 -0
- package/src/Message/Message.tsx +43 -0
- package/src/Message/index.ts +1 -0
- package/src/Provider/MarigoldProvider.test.tsx +126 -0
- package/src/Provider/MarigoldProvider.tsx +29 -0
- package/src/Provider/index.ts +3 -0
- package/src/Radio/Radio.stories.mdx +80 -83
- package/src/Radio/Radio.test.tsx +63 -22
- package/src/Radio/Radio.tsx +110 -35
- package/src/Radio/RadioIcons.tsx +39 -0
- package/src/Select/ListBox.tsx +39 -0
- package/src/Select/ListBoxSection.tsx +40 -0
- package/src/Select/Option.tsx +48 -0
- package/src/Select/Popover.tsx +50 -0
- package/src/Select/Select.stories.mdx +72 -37
- package/src/Select/Select.test.tsx +271 -28
- package/src/Select/Select.tsx +158 -23
- package/src/Slider/Slider.stories.mdx +26 -54
- package/src/Slider/Slider.test.tsx +13 -13
- package/src/Slider/Slider.tsx +20 -18
- package/src/Stack/Stack.stories.mdx +51 -0
- package/src/Stack/Stack.test.tsx +129 -0
- package/src/Stack/Stack.tsx +39 -0
- package/src/Stack/index.ts +1 -0
- package/src/Text/Text.stories.mdx +53 -47
- package/src/Text/Text.test.tsx +55 -15
- package/src/Text/Text.tsx +44 -10
- package/src/Textarea/Textarea.stories.mdx +68 -21
- package/src/Textarea/Textarea.test.tsx +47 -16
- package/src/Textarea/Textarea.tsx +46 -14
- package/src/ValidationMessage/ValidationMessage.stories.mdx +36 -0
- package/src/ValidationMessage/ValidationMessage.test.tsx +63 -0
- package/src/ValidationMessage/ValidationMessage.tsx +28 -0
- package/src/ValidationMessage/index.ts +1 -0
- package/src/index.ts +22 -4
- package/src/theme.ts +24 -4
- package/dist/Svg/Svg.d.ts +0 -5
- package/dist/Svg/index.d.ts +0 -1
- package/dist/TextInput/TextInput.d.ts +0 -3
- package/dist/TextInput/index.d.ts +0 -1
- package/src/Svg/Svg.stories.mdx +0 -47
- package/src/Svg/Svg.test.tsx +0 -58
- package/src/Svg/Svg.tsx +0 -25
- package/src/Svg/index.ts +0 -1
- package/src/TextInput/TextInput.stories.mdx +0 -37
- package/src/TextInput/TextInput.test.tsx +0 -71
- package/src/TextInput/TextInput.tsx +0 -21
- package/src/TextInput/index.ts +0 -1
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React, { RefObject } from 'react';
|
|
2
|
+
import { ComponentProps } from '@marigold/types';
|
|
3
|
+
import { useOverlayTriggerState } from '@react-stately/overlays';
|
|
4
|
+
import { OverlayContainer } from '@react-aria/overlays';
|
|
5
|
+
import { useButton } from '@react-aria/button';
|
|
6
|
+
import { Close } from '@marigold/icons';
|
|
7
|
+
|
|
8
|
+
import { Box } from '../Box';
|
|
9
|
+
import { Button } from '../Button';
|
|
10
|
+
import { Heading } from '../Heading';
|
|
11
|
+
|
|
12
|
+
import { ModalDialog } from './ModalDialog';
|
|
13
|
+
|
|
14
|
+
export type DialogProps = {
|
|
15
|
+
isOpen: boolean;
|
|
16
|
+
close: ComponentProps<typeof Button>['onClick'];
|
|
17
|
+
title?: string;
|
|
18
|
+
} & ComponentProps<'div'>;
|
|
19
|
+
|
|
20
|
+
export const Dialog: React.FC<DialogProps> = ({
|
|
21
|
+
children,
|
|
22
|
+
title,
|
|
23
|
+
className,
|
|
24
|
+
isOpen,
|
|
25
|
+
close,
|
|
26
|
+
...props
|
|
27
|
+
}) => {
|
|
28
|
+
const closeButtonRef = React.useRef<HTMLElement>() as RefObject<HTMLElement>;
|
|
29
|
+
|
|
30
|
+
// useButton ensures that focus management is handled correctly,
|
|
31
|
+
// across all browsers. Focus is restored to the button once the
|
|
32
|
+
// dialog closes.
|
|
33
|
+
const { buttonProps: closeButtonProps } = useButton(
|
|
34
|
+
{
|
|
35
|
+
onPress: () => close(),
|
|
36
|
+
},
|
|
37
|
+
closeButtonRef
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<OverlayContainer>
|
|
42
|
+
<ModalDialog isOpen={isOpen} onClose={close} isDismissable>
|
|
43
|
+
<Box variant="dialog.wrapper" className={className} {...props}>
|
|
44
|
+
<Box variant="dialog.body">
|
|
45
|
+
{title && (
|
|
46
|
+
<Heading as="h4" variant="h4">
|
|
47
|
+
{title}
|
|
48
|
+
</Heading>
|
|
49
|
+
)}
|
|
50
|
+
{children}
|
|
51
|
+
</Box>
|
|
52
|
+
<Box variant="dialog.onClose">
|
|
53
|
+
<Button
|
|
54
|
+
variant="close"
|
|
55
|
+
size="xsmall"
|
|
56
|
+
{...closeButtonProps}
|
|
57
|
+
ref={closeButtonRef}
|
|
58
|
+
>
|
|
59
|
+
<Close size={16} />
|
|
60
|
+
</Button>
|
|
61
|
+
</Box>
|
|
62
|
+
</Box>
|
|
63
|
+
</ModalDialog>
|
|
64
|
+
</OverlayContainer>
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// use this hook to get the overlayTriggerState and openButton props for using the dialog component
|
|
69
|
+
export const useDialogButtonProps = () => {
|
|
70
|
+
const state = useOverlayTriggerState({});
|
|
71
|
+
const openButtonRef = React.useRef<HTMLElement>() as RefObject<HTMLElement>;
|
|
72
|
+
const { buttonProps: openButtonProps } = useButton(
|
|
73
|
+
{
|
|
74
|
+
onPress: () => state.open(),
|
|
75
|
+
},
|
|
76
|
+
openButtonRef
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
state,
|
|
81
|
+
openButtonProps,
|
|
82
|
+
openButtonRef,
|
|
83
|
+
};
|
|
84
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
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
|
+
export type ModalDialogProps = OverlayProps & AriaDialogProps;
|
|
15
|
+
|
|
16
|
+
export const ModalDialog: React.FC<ModalDialogProps> = ({
|
|
17
|
+
children,
|
|
18
|
+
...props
|
|
19
|
+
}) => {
|
|
20
|
+
// Handle interacting outside the dialog and pressing
|
|
21
|
+
// the Escape key to close the modal.
|
|
22
|
+
const ref = React.useRef<HTMLElement>() as RefObject<HTMLElement>;
|
|
23
|
+
const { overlayProps, underlayProps } = useOverlay(props, ref);
|
|
24
|
+
|
|
25
|
+
// Prevent scrolling while the modal is open, and hide content
|
|
26
|
+
// outside the modal from screen readers.
|
|
27
|
+
usePreventScroll();
|
|
28
|
+
|
|
29
|
+
const { modalProps } = useModal();
|
|
30
|
+
const { dialogProps } = useDialog(props, ref);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Box variant="dialog.modalWrapper" {...underlayProps}>
|
|
34
|
+
<FocusScope contain restoreFocus autoFocus>
|
|
35
|
+
<Box
|
|
36
|
+
{...overlayProps}
|
|
37
|
+
{...dialogProps}
|
|
38
|
+
{...modalProps}
|
|
39
|
+
ref={ref}
|
|
40
|
+
variant="dialog.modalBody"
|
|
41
|
+
>
|
|
42
|
+
{children}
|
|
43
|
+
</Box>
|
|
44
|
+
</FocusScope>
|
|
45
|
+
</Box>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Dialog';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ArgsTable, Canvas, Meta, Story } from '@storybook/addon-docs';
|
|
2
|
+
import { Divider } from './Divider';
|
|
3
|
+
import { Text } from '../Text';
|
|
4
|
+
|
|
5
|
+
<Meta
|
|
6
|
+
title="Components/Divider"
|
|
7
|
+
argTypes={{
|
|
8
|
+
variant: {
|
|
9
|
+
control: {
|
|
10
|
+
type: 'select',
|
|
11
|
+
},
|
|
12
|
+
options: ['regular', 'bold'],
|
|
13
|
+
description: 'Thick or thin line',
|
|
14
|
+
table: {
|
|
15
|
+
defaultValue: {
|
|
16
|
+
summary: 'regular',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
}}
|
|
21
|
+
/>
|
|
22
|
+
|
|
23
|
+
# Divider
|
|
24
|
+
|
|
25
|
+
export const Template = args => (
|
|
26
|
+
<div>
|
|
27
|
+
<Text>Above</Text>
|
|
28
|
+
<Divider {...args} />
|
|
29
|
+
<Text>Below</Text>
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
<Canvas>
|
|
34
|
+
<Story name="Default">{Template.bind({})}</Story>
|
|
35
|
+
</Canvas>
|
|
36
|
+
|
|
37
|
+
<ArgsTable story="Default" />
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import { ThemeProvider } from '@marigold/system';
|
|
4
|
+
import { Divider } from './Divider';
|
|
5
|
+
|
|
6
|
+
const theme = {
|
|
7
|
+
divider: {
|
|
8
|
+
regular: {
|
|
9
|
+
border: 0,
|
|
10
|
+
borderBottom: '1px solid',
|
|
11
|
+
margin: '2px',
|
|
12
|
+
},
|
|
13
|
+
bold: {
|
|
14
|
+
border: 0,
|
|
15
|
+
borderBottom: '2px solid',
|
|
16
|
+
margin: '2px',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
test('supports default variant and themeSection', () => {
|
|
22
|
+
render(
|
|
23
|
+
<ThemeProvider theme={theme}>
|
|
24
|
+
<Divider title="divider" />
|
|
25
|
+
</ThemeProvider>
|
|
26
|
+
);
|
|
27
|
+
const divider = screen.getByTitle(/divider/);
|
|
28
|
+
|
|
29
|
+
expect(divider).toHaveStyle(`borderBottom: 1px solid`);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('accepts other variant than default', () => {
|
|
33
|
+
render(
|
|
34
|
+
<ThemeProvider theme={theme}>
|
|
35
|
+
<Divider variant="bold" title="divider" />
|
|
36
|
+
</ThemeProvider>
|
|
37
|
+
);
|
|
38
|
+
const divider = screen.getByTitle(/divider/);
|
|
39
|
+
|
|
40
|
+
expect(divider).toHaveStyle(`borderBottom: 2px solid`);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('renders correct HTML element', () => {
|
|
44
|
+
render(
|
|
45
|
+
<ThemeProvider theme={theme}>
|
|
46
|
+
<Divider title="divider" />
|
|
47
|
+
</ThemeProvider>
|
|
48
|
+
);
|
|
49
|
+
const divider = screen.getByTitle(/divider/);
|
|
50
|
+
|
|
51
|
+
expect(divider instanceof HTMLHRElement).toBeTruthy();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('accepts custom styles prop className', () => {
|
|
55
|
+
render(
|
|
56
|
+
<ThemeProvider theme={theme}>
|
|
57
|
+
<Divider className="custom-class-name" title="divider" />
|
|
58
|
+
</ThemeProvider>
|
|
59
|
+
);
|
|
60
|
+
const divider = screen.getByTitle(/divider/);
|
|
61
|
+
|
|
62
|
+
expect(divider.className).toMatch('custom-class-name');
|
|
63
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box } from '../Box';
|
|
3
|
+
|
|
4
|
+
export type DividerProps = {
|
|
5
|
+
className?: string;
|
|
6
|
+
variant?: string;
|
|
7
|
+
title?: string; // Should only be used for testing.
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const Divider: React.FC<DividerProps> = ({
|
|
11
|
+
variant = 'regular',
|
|
12
|
+
...props
|
|
13
|
+
}) => <Box {...props} as="hr" variant={`divider.${variant}`} />;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Divider';
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { ArgsTable, Canvas, Meta, Story } from '@storybook/addon-docs';
|
|
2
|
+
import { Field } from './Field';
|
|
3
|
+
|
|
4
|
+
<Meta
|
|
5
|
+
title="Components/Field"
|
|
6
|
+
argTypes={{
|
|
7
|
+
htmlFor: {
|
|
8
|
+
control: {
|
|
9
|
+
type: 'text',
|
|
10
|
+
},
|
|
11
|
+
type: { required: true },
|
|
12
|
+
},
|
|
13
|
+
label: {
|
|
14
|
+
control: {
|
|
15
|
+
type: 'text',
|
|
16
|
+
},
|
|
17
|
+
type: { required: true },
|
|
18
|
+
},
|
|
19
|
+
error: {
|
|
20
|
+
control: {
|
|
21
|
+
type: 'boolean',
|
|
22
|
+
},
|
|
23
|
+
description: 'Error',
|
|
24
|
+
table: {
|
|
25
|
+
defaultValue: {
|
|
26
|
+
summary: false,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
errorMessage: {
|
|
31
|
+
control: {
|
|
32
|
+
type: 'text',
|
|
33
|
+
},
|
|
34
|
+
description: 'Error Message',
|
|
35
|
+
},
|
|
36
|
+
required: {
|
|
37
|
+
control: {
|
|
38
|
+
type: 'boolean',
|
|
39
|
+
},
|
|
40
|
+
options: [true, false],
|
|
41
|
+
table: {
|
|
42
|
+
defaultValue: {
|
|
43
|
+
summary: false,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
disabled: {
|
|
48
|
+
control: {
|
|
49
|
+
type: 'boolean',
|
|
50
|
+
},
|
|
51
|
+
options: [true, false],
|
|
52
|
+
table: {
|
|
53
|
+
defaultValue: {
|
|
54
|
+
summary: false,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
type: {
|
|
59
|
+
control: {
|
|
60
|
+
type: 'select',
|
|
61
|
+
},
|
|
62
|
+
options: [
|
|
63
|
+
'date',
|
|
64
|
+
'datetime-local',
|
|
65
|
+
'email',
|
|
66
|
+
'month',
|
|
67
|
+
'number',
|
|
68
|
+
'password',
|
|
69
|
+
'search',
|
|
70
|
+
'tel',
|
|
71
|
+
'text',
|
|
72
|
+
'time',
|
|
73
|
+
'time',
|
|
74
|
+
'url',
|
|
75
|
+
'week',
|
|
76
|
+
],
|
|
77
|
+
type: { required: true },
|
|
78
|
+
table: {
|
|
79
|
+
defaultValue: {
|
|
80
|
+
summary: 'text',
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
}}
|
|
85
|
+
/>
|
|
86
|
+
|
|
87
|
+
# Field
|
|
88
|
+
|
|
89
|
+
export const Template = args => (
|
|
90
|
+
<Field htmlFor="id" label="A label" type="text" {...args} />
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
<Canvas>
|
|
94
|
+
<Story name="Default">{Template.bind({})}</Story>
|
|
95
|
+
</Canvas>
|
|
96
|
+
|
|
97
|
+
<ArgsTable story="Default" />
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import { ThemeProvider } from '@marigold/system';
|
|
4
|
+
import { Field } from './Field';
|
|
5
|
+
|
|
6
|
+
const theme = {
|
|
7
|
+
field: {
|
|
8
|
+
default: {
|
|
9
|
+
padding: '4px',
|
|
10
|
+
},
|
|
11
|
+
inputField: {
|
|
12
|
+
padding: '8px',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
test('renders correct HTML element', () => {
|
|
18
|
+
render(
|
|
19
|
+
<ThemeProvider theme={theme}>
|
|
20
|
+
<Field htmlFor="myId" label="label" />
|
|
21
|
+
</ThemeProvider>
|
|
22
|
+
);
|
|
23
|
+
const field = screen.getByText(/label/);
|
|
24
|
+
|
|
25
|
+
expect(field instanceof HTMLLabelElement).toBeTruthy();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('supports label prop', () => {
|
|
29
|
+
render(<Field htmlFor="myId" label="Name" />);
|
|
30
|
+
const field = screen.getByText(/Name/);
|
|
31
|
+
|
|
32
|
+
expect(field).toBeDefined();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('supports htmlFor prop', () => {
|
|
36
|
+
render(<Field htmlFor="myId" label="Name" />);
|
|
37
|
+
const field = screen.getByText(/Name/);
|
|
38
|
+
|
|
39
|
+
expect(field).toHaveAttribute('for');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('supports required prop', () => {
|
|
43
|
+
render(<Field htmlFor="myId" label="label" required />);
|
|
44
|
+
const fieldLabel = screen.getByText(/label/);
|
|
45
|
+
|
|
46
|
+
expect(fieldLabel.nextSibling).toBeDefined();
|
|
47
|
+
expect(fieldLabel.nextSibling instanceof SVGElement).toBeTruthy();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('supports error and errorMessage prop', () => {
|
|
51
|
+
render(
|
|
52
|
+
<Field htmlFor="myId" label="label" error errorMessage="Validation error" />
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const errorMessage = screen.getByText(/Validation/);
|
|
56
|
+
expect(errorMessage).toBeDefined();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('supports disabled prop', () => {
|
|
60
|
+
render(<Field htmlFor="myId" label="label" disabled />);
|
|
61
|
+
const fieldLabel = screen.getByText(/label/);
|
|
62
|
+
expect(fieldLabel.nextSibling).toHaveAttribute('disabled');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('accepts custom styles prop className', () => {
|
|
66
|
+
render(
|
|
67
|
+
<ThemeProvider theme={theme}>
|
|
68
|
+
<Field
|
|
69
|
+
htmlFor="myId"
|
|
70
|
+
label="label"
|
|
71
|
+
className="custom-class-name"
|
|
72
|
+
title="field"
|
|
73
|
+
data-testid="field"
|
|
74
|
+
/>
|
|
75
|
+
</ThemeProvider>
|
|
76
|
+
);
|
|
77
|
+
const field = screen.getByTestId(/field/);
|
|
78
|
+
|
|
79
|
+
expect(field.className).toMatch('custom-class-name');
|
|
80
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Exclamation } from '@marigold/icons';
|
|
3
|
+
import { ComponentProps } from '@marigold/types';
|
|
4
|
+
|
|
5
|
+
import { Input } from '../Input';
|
|
6
|
+
import { Label } from '../Label';
|
|
7
|
+
import { ValidationMessage } from '../ValidationMessage';
|
|
8
|
+
|
|
9
|
+
export type FieldProps = {
|
|
10
|
+
htmlFor: string;
|
|
11
|
+
label: string;
|
|
12
|
+
required?: boolean;
|
|
13
|
+
error?: boolean;
|
|
14
|
+
errorMessage?: string;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
} & ComponentProps<'input'>;
|
|
17
|
+
|
|
18
|
+
export const Field: React.FC<FieldProps> = ({
|
|
19
|
+
type = 'text',
|
|
20
|
+
className,
|
|
21
|
+
htmlFor,
|
|
22
|
+
label,
|
|
23
|
+
required,
|
|
24
|
+
error,
|
|
25
|
+
errorMessage,
|
|
26
|
+
disabled,
|
|
27
|
+
...props
|
|
28
|
+
}) => {
|
|
29
|
+
return (
|
|
30
|
+
<>
|
|
31
|
+
<Label
|
|
32
|
+
variant={disabled ? 'disabled' : 'above'}
|
|
33
|
+
htmlFor={htmlFor}
|
|
34
|
+
required={required}
|
|
35
|
+
>
|
|
36
|
+
{label}
|
|
37
|
+
</Label>
|
|
38
|
+
<Input
|
|
39
|
+
{...props}
|
|
40
|
+
type={type}
|
|
41
|
+
id={htmlFor}
|
|
42
|
+
disabled={disabled}
|
|
43
|
+
variant={error ? 'error' : 'default'}
|
|
44
|
+
className={className}
|
|
45
|
+
/>
|
|
46
|
+
{error && errorMessage && (
|
|
47
|
+
<ValidationMessage>
|
|
48
|
+
<Exclamation size={16} />
|
|
49
|
+
{errorMessage}
|
|
50
|
+
</ValidationMessage>
|
|
51
|
+
)}
|
|
52
|
+
</>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Field';
|
|
@@ -1,79 +1,39 @@
|
|
|
1
|
-
import { Meta, Story
|
|
2
|
-
import { Heading
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { ArgsTable, Canvas, Meta, Story } from '@storybook/addon-docs';
|
|
2
|
+
import { Heading } from './Heading';
|
|
3
|
+
import { Text } from '../Text';
|
|
4
|
+
|
|
5
|
+
<Meta
|
|
6
|
+
title="Components/Heading"
|
|
7
|
+
argTypes={{
|
|
8
|
+
variant: {
|
|
9
|
+
control: {
|
|
10
|
+
type: 'select',
|
|
11
|
+
},
|
|
12
|
+
options: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
|
|
13
|
+
description: 'HTML element style',
|
|
14
|
+
table: {
|
|
15
|
+
defaultValue: {
|
|
16
|
+
summary: 'h2',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
}}
|
|
21
|
+
/>
|
|
5
22
|
|
|
6
23
|
# Heading
|
|
7
24
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
## Usage
|
|
25
|
-
|
|
26
|
-
<Preview>
|
|
27
|
-
<Story name="Headings">
|
|
28
|
-
<div>
|
|
29
|
-
<Heading headingStyle="h1">H1 amazing heading</Heading>
|
|
30
|
-
<Text as="p" variant="heading">
|
|
31
|
-
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
|
|
32
|
-
eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
|
|
33
|
-
voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
|
|
34
|
-
clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
|
|
35
|
-
amet.
|
|
36
|
-
</Text>
|
|
37
|
-
<Heading>H2 amazing heading</Heading>
|
|
38
|
-
<Text as="p" variant="heading">
|
|
39
|
-
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
|
|
40
|
-
eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
|
|
41
|
-
voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
|
|
42
|
-
clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
|
|
43
|
-
amet.
|
|
44
|
-
</Text>
|
|
45
|
-
<Heading headingStyle="h3">H3 amazing heading</Heading>
|
|
46
|
-
<Text as="p" variant="heading">
|
|
47
|
-
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
|
|
48
|
-
eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
|
|
49
|
-
voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
|
|
50
|
-
clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
|
|
51
|
-
amet.
|
|
52
|
-
</Text>
|
|
53
|
-
<Heading headingStyle="h4">H4 amazing heading</Heading>
|
|
54
|
-
<Text as="p" variant="heading">
|
|
55
|
-
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
|
|
56
|
-
eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
|
|
57
|
-
voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
|
|
58
|
-
clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
|
|
59
|
-
amet.
|
|
60
|
-
</Text>
|
|
61
|
-
<Heading headingStyle="h5">H5 amazing heading</Heading>
|
|
62
|
-
<Text as="p" variant="heading">
|
|
63
|
-
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
|
|
64
|
-
eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
|
|
65
|
-
voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
|
|
66
|
-
clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
|
|
67
|
-
amet.
|
|
68
|
-
</Text>
|
|
69
|
-
<Heading headingStyle="h6">H6 amazing heading</Heading>
|
|
70
|
-
<Text as="p" variant="heading">
|
|
71
|
-
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy
|
|
72
|
-
eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
|
|
73
|
-
voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
|
|
74
|
-
clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit
|
|
75
|
-
amet.
|
|
76
|
-
</Text>
|
|
77
|
-
</div>
|
|
78
|
-
</Story>
|
|
79
|
-
</Preview>
|
|
25
|
+
export const Template = args => (
|
|
26
|
+
<div>
|
|
27
|
+
<Heading {...args}>Heading</Heading>
|
|
28
|
+
<Text>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse
|
|
29
|
+
dignissim dapibus elit, vel egestas felis pharetra non. Cras
|
|
30
|
+
malesuada, massa nec ultricies efficitur, lectus ante consequat magna,
|
|
31
|
+
a porttitor massa ex ut quam.</Text>
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
<Canvas>
|
|
36
|
+
<Story name="Default">{Template.bind({})}</Story>
|
|
37
|
+
</Canvas>
|
|
38
|
+
|
|
39
|
+
<ArgsTable story="Default" />
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { render, screen } from '@testing-library/react';
|
|
3
|
-
import {
|
|
4
|
-
import { Heading } from '
|
|
3
|
+
import { ThemeProvider } from '@marigold/system';
|
|
4
|
+
import { Heading } from './Heading';
|
|
5
5
|
|
|
6
6
|
const theme = {
|
|
7
7
|
text: {
|
|
@@ -16,9 +16,9 @@ const theme = {
|
|
|
16
16
|
|
|
17
17
|
test('supports default variant and themeSection', () => {
|
|
18
18
|
render(
|
|
19
|
-
<
|
|
19
|
+
<ThemeProvider theme={theme}>
|
|
20
20
|
<Heading title="default">Default</Heading>
|
|
21
|
-
</
|
|
21
|
+
</ThemeProvider>
|
|
22
22
|
);
|
|
23
23
|
const heading = screen.getByTitle(/default/);
|
|
24
24
|
|
|
@@ -27,37 +27,51 @@ test('supports default variant and themeSection', () => {
|
|
|
27
27
|
|
|
28
28
|
test('accepts other variant than default', () => {
|
|
29
29
|
render(
|
|
30
|
-
<
|
|
31
|
-
<Heading title="default"
|
|
30
|
+
<ThemeProvider theme={theme}>
|
|
31
|
+
<Heading title="default" variant="h3">
|
|
32
32
|
Default
|
|
33
33
|
</Heading>
|
|
34
|
-
</
|
|
34
|
+
</ThemeProvider>
|
|
35
35
|
);
|
|
36
36
|
const heading = screen.getByTitle(/default/);
|
|
37
37
|
|
|
38
38
|
expect(heading).toHaveStyle(`font-family: Roboto`);
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
test('
|
|
41
|
+
test('supports default as prop', () => {
|
|
42
|
+
render(<Heading title="default">Default</Heading>);
|
|
43
|
+
const heading = screen.getByTitle(/default/);
|
|
44
|
+
|
|
45
|
+
expect(heading.tagName).toEqual('H2');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('accepts other as prop than default', () => {
|
|
42
49
|
render(
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
</
|
|
50
|
+
<Heading as="h3" title="default" variant="h3">
|
|
51
|
+
Default
|
|
52
|
+
</Heading>
|
|
46
53
|
);
|
|
47
54
|
const heading = screen.getByTitle(/default/);
|
|
48
55
|
|
|
56
|
+
expect(heading.tagName).toEqual('H3');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('renders correct HTML element', () => {
|
|
60
|
+
render(<Heading title="default">Default</Heading>);
|
|
61
|
+
const heading = screen.getByTitle(/default/);
|
|
62
|
+
|
|
49
63
|
expect(heading instanceof HTMLHeadingElement).toBeTruthy();
|
|
50
64
|
});
|
|
51
65
|
|
|
52
|
-
test('
|
|
66
|
+
test('accepts custom styles prop className', () => {
|
|
53
67
|
render(
|
|
54
|
-
<
|
|
55
|
-
<Heading
|
|
68
|
+
<ThemeProvider theme={theme}>
|
|
69
|
+
<Heading className="custom-class-name" title="heading">
|
|
56
70
|
Default
|
|
57
71
|
</Heading>
|
|
58
|
-
</
|
|
72
|
+
</ThemeProvider>
|
|
59
73
|
);
|
|
60
|
-
const heading = screen.getByTitle(/
|
|
74
|
+
const heading = screen.getByTitle(/heading/);
|
|
61
75
|
|
|
62
|
-
expect(heading).
|
|
76
|
+
expect(heading.className).toMatch('custom-class-name');
|
|
63
77
|
});
|