@automattic/vip-design-system 0.12.0 → 0.13.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 (35) hide show
  1. package/.storybook/decorators/withBoundingBox.jsx +11 -4
  2. package/.storybook/decorators/withThemeProvider.jsx +4 -6
  3. package/build/system/Form/Toggle.js +19 -11
  4. package/build/system/Form/Toggle.stories.js +46 -6
  5. package/build/system/NewDialog/DialogClose.js +75 -0
  6. package/build/system/NewDialog/DialogClose.test.js +67 -0
  7. package/build/system/NewDialog/DialogContent.js +26 -0
  8. package/build/system/NewDialog/DialogDescription.js +54 -0
  9. package/build/system/NewDialog/DialogDescription.test.js +95 -0
  10. package/build/system/NewDialog/DialogOverlay.js +48 -0
  11. package/build/system/NewDialog/DialogOverlay.test.js +63 -0
  12. package/build/system/NewDialog/DialogTitle.js +50 -0
  13. package/build/system/NewDialog/DialogTitle.test.js +95 -0
  14. package/build/system/NewDialog/NewDialog.js +117 -0
  15. package/build/system/NewDialog/NewDialog.stories.js +159 -0
  16. package/build/system/NewDialog/index.js +7 -0
  17. package/build/system/Notice/Notice.js +4 -4
  18. package/build/system/index.js +4 -0
  19. package/package.json +6 -4
  20. package/src/system/Form/Toggle.js +28 -10
  21. package/src/system/Form/Toggle.stories.jsx +43 -1
  22. package/src/system/NewDialog/DialogClose.js +45 -0
  23. package/src/system/NewDialog/DialogClose.test.js +32 -0
  24. package/src/system/NewDialog/DialogContent.js +17 -0
  25. package/src/system/NewDialog/DialogDescription.js +36 -0
  26. package/src/system/NewDialog/DialogDescription.test.js +47 -0
  27. package/src/system/NewDialog/DialogOverlay.js +31 -0
  28. package/src/system/NewDialog/DialogOverlay.test.js +27 -0
  29. package/src/system/NewDialog/DialogTitle.js +27 -0
  30. package/src/system/NewDialog/DialogTitle.test.js +47 -0
  31. package/src/system/NewDialog/NewDialog.js +87 -0
  32. package/src/system/NewDialog/NewDialog.stories.jsx +96 -0
  33. package/src/system/NewDialog/index.js +7 -0
  34. package/src/system/Notice/Notice.js +3 -3
  35. package/src/system/index.js +3 -0
@@ -0,0 +1,32 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { Dialog } from '@radix-ui/react-dialog';
5
+ import { render, screen } from '@testing-library/react';
6
+ import { axe } from 'jest-axe';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import { DialogClose } from './DialogClose';
12
+
13
+ // If you render any Dialog child without the `<Dialog />` parent, it will throw an error.
14
+ const Wrapper = props => <Dialog open={ true } { ...props } />;
15
+ const defaultProps = {
16
+ title: 'This is a DialogClose',
17
+ };
18
+
19
+ describe( '<DialogClose />', () => {
20
+ it( 'renders the DialogClose component', async () => {
21
+ const { container } = render(
22
+ <Wrapper>
23
+ <DialogClose { ...defaultProps } />
24
+ </Wrapper>
25
+ );
26
+
27
+ expect( screen.getByLabelText( 'Close' ) ).toBeInTheDocument();
28
+
29
+ // Check for accessibility issues
30
+ await expect( await axe( container ) ).toHaveNoViolations();
31
+ } );
32
+ } );
@@ -0,0 +1,17 @@
1
+ /** @jsxImportSource theme-ui */
2
+
3
+ export const contentStyles = {
4
+ background: 'white',
5
+ borderRadius: 10,
6
+ boxShadow: 'hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px',
7
+ position: 'fixed',
8
+ top: '50%',
9
+ left: '50%',
10
+ transform: 'translate(-50%, -50%)',
11
+ width: '90vw',
12
+ maxWidth: '640px',
13
+ maxHeight: '85vh',
14
+ padding: 25,
15
+ '&:focus': { outline: 'none' },
16
+ '> h1, > h2': { marginTop: '0 !important' },
17
+ };
@@ -0,0 +1,36 @@
1
+ /** @jsxImportSource theme-ui */
2
+
3
+ /**
4
+ * External dependencies
5
+ */
6
+ import React from 'react';
7
+ import PropTypes from 'prop-types';
8
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
9
+ import ScreenReaderText from '../ScreenReaderText/ScreenReaderText';
10
+
11
+ /**
12
+ * Internal dependencies
13
+ */
14
+
15
+ export const DialogDescription = React.forwardRef(
16
+ ( { description, hidden, ...rest }, forwardedRef ) => {
17
+ let text = description;
18
+
19
+ if ( hidden ) {
20
+ text = <ScreenReaderText>{ text }</ScreenReaderText>;
21
+ }
22
+
23
+ return (
24
+ <DialogPrimitive.Description { ...rest } ref={ forwardedRef }>
25
+ { text }
26
+ </DialogPrimitive.Description>
27
+ );
28
+ }
29
+ );
30
+
31
+ DialogDescription.displayName = 'DialogDescription';
32
+
33
+ DialogDescription.propTypes = {
34
+ description: PropTypes.string,
35
+ hidden: PropTypes.bool,
36
+ };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { Dialog } from '@radix-ui/react-dialog';
5
+ import { render, screen } from '@testing-library/react';
6
+ import { axe } from 'jest-axe';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import { DialogDescription } from './DialogDescription';
12
+
13
+ // If you render any Dialog child without the `<Dialog />` parent, it will throw an error.
14
+ const Wrapper = props => <Dialog open={ true } { ...props } />;
15
+ const defaultProps = {
16
+ description: 'My Custom Text',
17
+ };
18
+
19
+ const getParagraph = () => screen.getByText( defaultProps.description );
20
+
21
+ describe( '<DialogDescription />', () => {
22
+ it( 'renders the DialogDescription component', async () => {
23
+ const { container } = render(
24
+ <Wrapper>
25
+ <DialogDescription { ...defaultProps } />
26
+ </Wrapper>
27
+ );
28
+
29
+ expect( getParagraph() ).toHaveTextContent( defaultProps.description );
30
+
31
+ // Check for accessibility issues
32
+ await expect( await axe( container ) ).toHaveNoViolations();
33
+ } );
34
+
35
+ it( 'renders text visually hidden for a11y purposes', async () => {
36
+ const { container } = render(
37
+ <Wrapper>
38
+ <DialogDescription { ...defaultProps } hidden={ true } />
39
+ </Wrapper>
40
+ );
41
+
42
+ // Small check to make sure we are hiding with the css class
43
+ expect( container.innerHTML ).toContain( 'screen-reader-text' );
44
+
45
+ expect( getParagraph() ).toHaveTextContent( defaultProps.description );
46
+ } );
47
+ } );
@@ -0,0 +1,31 @@
1
+ /** @jsxImportSource theme-ui */
2
+ /**
3
+ * External dependencies
4
+ */
5
+ import React from 'react';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import * as Dialog from '@radix-ui/react-dialog';
11
+
12
+ export const DialogOverlay = React.forwardRef( ( props, forwardedRef ) => (
13
+ <Dialog.Overlay
14
+ sx={ {
15
+ position: 'fixed',
16
+ top: 0,
17
+ left: 0,
18
+ right: 0,
19
+ bottom: 0,
20
+ inset: 0,
21
+ opacity: 0.7,
22
+ background:
23
+ // eslint-disable-next-line max-len
24
+ 'linear-gradient(198.09deg,#E5F0F6 2.01%, rgba(235, 238, 242, 0) 43.18%,rgba(249, 234, 232, 0) 47.86%, #FFE9D1 94.31%), linear-gradient(98.65deg, #FFE8E6 0.58%, rgba(255, 233, 214, 0) 52.45%, rgba(255, 233, 219, 0) 53.76%,#FFE9D1 105.86%), #F5F2F1',
25
+ } }
26
+ { ...props }
27
+ ref={ forwardedRef }
28
+ />
29
+ ) );
30
+
31
+ DialogOverlay.displayName = 'DialogOverlay';
@@ -0,0 +1,27 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { Dialog } from '@radix-ui/react-dialog';
5
+ import { render } from '@testing-library/react';
6
+ import { axe } from 'jest-axe';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import { DialogOverlay } from './DialogOverlay';
12
+
13
+ // If you render any Dialog child without the `<Dialog />` parent, it will throw an error.
14
+ const Wrapper = props => <Dialog open={ true } { ...props } />;
15
+
16
+ describe( '<DialogOverlay />', () => {
17
+ it( 'renders the DialogOverlay component', async () => {
18
+ const { container } = render(
19
+ <Wrapper>
20
+ <DialogOverlay />
21
+ </Wrapper>
22
+ );
23
+
24
+ // Check for accessibility issues
25
+ await expect( await axe( container ) ).toHaveNoViolations();
26
+ } );
27
+ } );
@@ -0,0 +1,27 @@
1
+ /** @jsxImportSource theme-ui */
2
+
3
+ /**
4
+ * External dependencies
5
+ */
6
+ import PropTypes from 'prop-types';
7
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
8
+ import ScreenReaderText from '../ScreenReaderText/ScreenReaderText';
9
+
10
+ /**
11
+ * Internal dependencies
12
+ */
13
+
14
+ export const DialogTitle = ( { title, hidden = false } ) => {
15
+ let titleNode = title;
16
+
17
+ if ( hidden ) {
18
+ titleNode = <ScreenReaderText>{ titleNode }</ScreenReaderText>;
19
+ }
20
+
21
+ return <DialogPrimitive.Title>{ titleNode }</DialogPrimitive.Title>;
22
+ };
23
+
24
+ DialogTitle.propTypes = {
25
+ title: PropTypes.string,
26
+ hidden: PropTypes.bool,
27
+ };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { Dialog } from '@radix-ui/react-dialog';
5
+ import { render, screen } from '@testing-library/react';
6
+ import { axe } from 'jest-axe';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import { DialogTitle } from './DialogTitle';
12
+
13
+ // If you render any Dialog child without the `<Dialog />` parent, it will throw an error.
14
+ const Wrapper = props => <Dialog open={ true } { ...props } />;
15
+ const defaultProps = {
16
+ title: 'This is a DialogTitle',
17
+ };
18
+
19
+ const getTitle = () => screen.getByRole( 'heading' );
20
+
21
+ describe( '<DialogTitle />', () => {
22
+ it( 'renders the DialogTitle component', async () => {
23
+ const { container } = render(
24
+ <Wrapper>
25
+ <DialogTitle { ...defaultProps } />
26
+ </Wrapper>
27
+ );
28
+
29
+ expect( getTitle() ).toHaveTextContent( defaultProps.title );
30
+
31
+ // Check for accessibility issues
32
+ await expect( await axe( container ) ).toHaveNoViolations();
33
+ } );
34
+
35
+ it( 'renders text visually hidden for a11y purposes', async () => {
36
+ const { container } = render(
37
+ <Wrapper>
38
+ <DialogTitle { ...defaultProps } hidden={ true } />
39
+ </Wrapper>
40
+ );
41
+
42
+ // Small check to make sure we are hiding with the css class
43
+ expect( container.innerHTML ).toContain( 'screen-reader-text' );
44
+
45
+ expect( getTitle() ).toHaveTextContent( defaultProps.title );
46
+ } );
47
+ } );
@@ -0,0 +1,87 @@
1
+ /** @jsxImportSource theme-ui */
2
+
3
+ /**
4
+ * External dependencies
5
+ */
6
+ import PropTypes from 'prop-types';
7
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import { DialogOverlay } from './DialogOverlay';
12
+ import { DialogClose } from './DialogClose';
13
+ import { DialogTitle } from './DialogTitle';
14
+ import { DialogDescription } from './DialogDescription';
15
+ import { contentStyles } from './DialogContent';
16
+
17
+ export const NewDialog = ( {
18
+ trigger = null,
19
+ description,
20
+ title,
21
+ content = null,
22
+ showHeading = true,
23
+ disabled = false,
24
+ style: extraStyles,
25
+
26
+ // Radix Specific Properties
27
+ defaultOpen = false,
28
+ allowPinchZoom = false,
29
+ onOpenAutoFocus = null,
30
+ onCloseAutoFocus = null,
31
+ onEscapeKeyDown = null,
32
+ onPointerDownOutside = null,
33
+ onInteractOutside = null,
34
+ } ) => {
35
+ if ( disabled ) {
36
+ return;
37
+ }
38
+
39
+ return (
40
+ <DialogPrimitive.Root defaultOpen={ defaultOpen } allowPinchZoom={ allowPinchZoom }>
41
+ { trigger && <DialogPrimitive.Trigger asChild>{ trigger }</DialogPrimitive.Trigger> }
42
+
43
+ <DialogPrimitive.Portal>
44
+ <DialogOverlay />
45
+
46
+ <DialogPrimitive.Content
47
+ className="vip-dialog-component"
48
+ sx={ { ...contentStyles, ...extraStyles } }
49
+ onOpenAutoFocus={ onOpenAutoFocus }
50
+ onCloseAutoFocus={ onCloseAutoFocus }
51
+ onEscapeKeyDown={ onEscapeKeyDown }
52
+ onPointerDownOutside={ onPointerDownOutside }
53
+ onInteractOutside={ onInteractOutside }
54
+ >
55
+ <DialogClose />
56
+ <DialogTitle title={ title } hidden={ ! showHeading } />
57
+ <DialogDescription description={ description } hidden={ ! showHeading } />
58
+
59
+ { content }
60
+ </DialogPrimitive.Content>
61
+ </DialogPrimitive.Portal>
62
+ </DialogPrimitive.Root>
63
+ );
64
+ };
65
+
66
+ NewDialog.propTypes = {
67
+ trigger: PropTypes.node.isRequired,
68
+ title: PropTypes.string.isRequired,
69
+ description: PropTypes.string.isRequired,
70
+ content: PropTypes.node,
71
+ showHeading: PropTypes.bool,
72
+ disabled: PropTypes.bool,
73
+ style: PropTypes.oneOfType( [ PropTypes.object, PropTypes.func ] ),
74
+
75
+ // Radix DialogPrimitive.Root properties
76
+ // https://www.radix-ui.com/docs/primitives/components/dialog#root
77
+ defaultOpen: PropTypes.bool,
78
+ allowPinchZoom: PropTypes.bool,
79
+
80
+ // Radix DialogPrimitive.Content properties
81
+ // https://www.radix-ui.com/docs/primitives/components/dialog#content
82
+ onOpenAutoFocus: PropTypes.func,
83
+ onCloseAutoFocus: PropTypes.func,
84
+ onEscapeKeyDown: PropTypes.func,
85
+ onPointerDownOutside: PropTypes.func,
86
+ onInteractOutside: PropTypes.func,
87
+ };
@@ -0,0 +1,96 @@
1
+ /** @jsxImportSource theme-ui */
2
+
3
+ /**
4
+ /**
5
+ * Internal dependencies
6
+ */
7
+ import { Button, Text, Input, Label } from '../../system';
8
+ import ScreenReaderText from '../ScreenReaderText/ScreenReaderText';
9
+ import { NewDialog } from './NewDialog';
10
+
11
+ export default {
12
+ title: 'NewDialog',
13
+ component: NewDialog,
14
+ };
15
+
16
+ const defaultProps = {
17
+ title: 'User settings',
18
+ description: 'Use this form to manage your settings',
19
+ };
20
+
21
+ export const Default = () => (
22
+ <>
23
+ <Text sx={ { fontSize: 3, mb: 3 } }>
24
+ Regular Dialog where the title and description are built-in and the content is provided by the
25
+ user.
26
+ </Text>
27
+ <NewDialog { ...defaultProps } trigger={ <Button>Trigger Dialog</Button> } />
28
+ </>
29
+ );
30
+
31
+ export const AutoOpen = () => (
32
+ <>
33
+ <Text sx={ { fontSize: 3, mb: 3 } }>Auto Opens when rendered. Press escape to close it.</Text>
34
+ <NewDialog
35
+ { ...defaultProps }
36
+ defaultOpen={ true }
37
+ trigger={ <ScreenReaderText>hey</ScreenReaderText> }
38
+ />
39
+ </>
40
+ );
41
+
42
+ export const HiddenHeadings = () => (
43
+ <>
44
+ <Text sx={ { fontSize: 3, mb: 3 } }>
45
+ Title and description are hidden, but still announced using a screen reader. Activate
46
+ VoiceOver or any similar screen reader to listen to: Custom dialog title, Description of the
47
+ dialog content.
48
+ </Text>
49
+
50
+ <NewDialog
51
+ { ...defaultProps }
52
+ trigger={ <Button>Trigger Dialog</Button> }
53
+ title="Custom dialog title"
54
+ showHeading={ false }
55
+ content={
56
+ <div>
57
+ <h3>My Custom Content</h3>
58
+
59
+ <form>
60
+ <Label htmlFor="username">User name</Label>
61
+ <Input type="text" name="username" id="username" />
62
+ <Button type="submit">Submit</Button>
63
+ </form>
64
+ </div>
65
+ }
66
+ />
67
+ </>
68
+ );
69
+ export const CustomStyling = () => (
70
+ <>
71
+ <Text sx={ { fontSize: 3, mb: 3 } }>Custom Styling on Dialog Content</Text>
72
+
73
+ <NewDialog
74
+ { ...defaultProps }
75
+ defaultOpen
76
+ trigger={ <Button>Trigger Dialog</Button> }
77
+ title="Custom dialog title"
78
+ style={ {
79
+ background: theme => `${ theme.colors.primary }`,
80
+ padding: 5,
81
+ borderRadius: 20,
82
+ h2: { fontSize: 4, color: theme => `${ theme.colors.text }` },
83
+ h3: { fontSize: 3, color: theme => `${ theme.colors.heading }` },
84
+ p: { color: 'white' },
85
+ 'button[type="button"]:focus': { outlineColor: 'white', color: 'white' },
86
+ } }
87
+ content={
88
+ <div>
89
+ <h3>This is Read because it is Custom</h3>
90
+
91
+ <p>This Dialog is styled using the `sx` property.</p>
92
+ </div>
93
+ }
94
+ />
95
+ </>
96
+ );
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+
5
+ import { NewDialog } from './NewDialog';
6
+
7
+ export { NewDialog };
@@ -27,7 +27,7 @@ const NoticeIcon = ( { color, variant } ) => {
27
27
  break;
28
28
  }
29
29
 
30
- return <Icon sx={ { marginRight: 2, color, flex: '0 0 auto' } } />;
30
+ return <Icon sx={ { color, flex: '0 0 auto' } } size={ 21 } />;
31
31
  };
32
32
 
33
33
  NoticeIcon.propTypes = {
@@ -86,10 +86,10 @@ const Notice = ( {
86
86
  alignItems: 'center',
87
87
  } }
88
88
  >
89
- <NoticeIcon color={ `${ color }.100` } variant={ variant } />
89
+ <NoticeIcon color={ `${ color }.200` } variant={ variant } />
90
90
  </Flex>
91
91
 
92
- <Box sx={ { ml: 23 } }>
92
+ <Box sx={ { ml: 3 } }>
93
93
  { title && (
94
94
  <Heading variant="h4" as="p" sx={ { color: `${ color }.100`, mb: 0 } }>
95
95
  { title }
@@ -17,6 +17,8 @@ import {
17
17
  DialogTrigger,
18
18
  DialogContent,
19
19
  } from './Dialog';
20
+
21
+ import { NewDialog } from './NewDialog';
20
22
  import { ConfirmationDialog } from './ConfirmationDialog';
21
23
  import { Flex } from './Flex';
22
24
  import {
@@ -60,6 +62,7 @@ export {
60
62
  Checkbox,
61
63
  Code,
62
64
  Dialog,
65
+ NewDialog,
63
66
  DialogButton,
64
67
  DialogMenu,
65
68
  DialogMenuItem,