@automattic/vip-design-system 2.1.0 → 2.3.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/.eslintrc.js +3 -0
- package/build/system/Accordion/Accordion.test.d.ts +0 -1
- package/build/system/Accordion/Accordion.test.js +23 -12
- package/build/system/Avatar/Avatar.d.ts +0 -1
- package/build/system/Avatar/Avatar.js +9 -17
- package/build/system/Avatar/Avatar.stories.d.ts +2 -1
- package/build/system/Avatar/Avatar.stories.js +19 -2
- package/build/system/Button/Button.d.ts +2 -0
- package/build/system/Button/Button.js +6 -15
- package/build/system/Button/Button.stories.js +86 -67
- package/build/system/Card/Card.js +1 -2
- package/build/system/Drawer/Drawer.d.ts +12 -3
- package/build/system/Drawer/Drawer.js +8 -9
- package/build/system/Drawer/Drawer.stories.d.ts +0 -1
- package/build/system/Drawer/Drawer.stories.js +19 -225
- package/build/system/Drawer/styles.d.ts +2 -2
- package/build/system/Drawer/styles.js +13 -9
- package/build/system/Form/Checkbox/Checkbox.js +0 -3
- package/build/system/Form/Checkbox/Checkbox.stories.js +2 -2
- package/build/system/Form/Checkbox/styles.js +16 -11
- package/build/system/Form/Radio/Radio.stories.js +1 -1
- package/build/system/Form/Toggle.d.ts +14 -2
- package/build/system/Form/Toggle.js +68 -69
- package/build/system/Form/Toggle.stories.d.ts +29 -18
- package/build/system/Form/Toggle.stories.js +99 -0
- package/build/system/Form/Toggle.test.js +36 -19
- package/build/system/Link/Link.d.ts +1 -1
- package/build/system/Link/Link.js +5 -20
- package/build/system/Link/Link.stories.d.ts +3 -1
- package/build/system/Link/Link.stories.js +18 -1
- package/build/system/MobileMenu/MobileMenu.d.ts +16 -0
- package/build/system/MobileMenu/MobileMenu.js +112 -0
- package/build/system/MobileMenu/MobileMenu.stories.d.ts +19 -0
- package/build/system/MobileMenu/MobileMenu.stories.js +173 -0
- package/build/system/MobileMenu/MobileMenu.test.d.ts +1 -0
- package/build/system/MobileMenu/MobileMenu.test.js +81 -0
- package/build/system/Nav/NavItem.js +4 -5
- package/build/system/Nav/NavItemGroup.d.ts +3 -0
- package/build/system/Nav/NavItemGroup.js +18 -6
- package/build/system/Nav/styles/variants/menu.js +15 -9
- package/build/system/Nav/styles/variants/menugroup.js +9 -5
- package/build/system/Nav/styles/variants/toolbar.js +1 -2
- package/build/system/Nav/styles.js +1 -0
- package/build/system/NewDialog/DialogClose.d.ts +7 -2
- package/build/system/NewDialog/DialogClose.js +25 -15
- package/build/system/NewDialog/index.d.ts +2 -1
- package/build/system/NewDialog/index.js +2 -1
- package/build/system/NewForm/FormAutocomplete.css +1 -3
- package/build/system/Table/Table.js +1 -0
- package/build/system/Toolbar/Toolbar.js +1 -1
- package/build/system/Toolbar/Toolbar.stories.d.ts +3 -0
- package/build/system/Toolbar/Toolbar.stories.js +62 -6
- package/build/system/Toolbar/ToolbarUtilNav.d.ts +3 -0
- package/build/system/Toolbar/ToolbarUtilNav.js +21 -3
- package/build/system/Toolbar/index.d.ts +3 -0
- package/build/system/Toolbar/index.js +2 -1
- package/build/system/index.d.ts +4 -1
- package/build/system/index.js +4 -0
- package/build/system/theme/index.d.ts +70 -35
- package/build/system/theme/index.js +47 -18
- package/package.json +2 -1
- package/src/system/Accordion/Accordion.test.tsx +15 -8
- package/src/system/Avatar/Avatar.stories.tsx +15 -1
- package/src/system/Avatar/Avatar.tsx +8 -12
- package/src/system/Button/Button.stories.tsx +40 -31
- package/src/system/Button/Button.tsx +6 -15
- package/src/system/Card/Card.tsx +0 -1
- package/src/system/Drawer/Drawer.stories.tsx +28 -197
- package/src/system/Drawer/Drawer.tsx +31 -18
- package/src/system/Drawer/styles.ts +15 -5
- package/src/system/Form/Checkbox/Checkbox.stories.tsx +2 -2
- package/src/system/Form/Checkbox/Checkbox.tsx +0 -3
- package/src/system/Form/Checkbox/styles.ts +57 -46
- package/src/system/Form/Radio/Radio.stories.tsx +1 -1
- package/src/system/Form/Toggle.stories.tsx +122 -0
- package/src/system/Form/{Toggle.test.js → Toggle.test.tsx} +1 -1
- package/src/system/Form/Toggle.tsx +77 -0
- package/src/system/Link/Link.stories.tsx +21 -0
- package/src/system/Link/Link.tsx +10 -17
- package/src/system/MobileMenu/MobileMenu.stories.tsx +171 -0
- package/src/system/MobileMenu/MobileMenu.test.tsx +50 -0
- package/src/system/MobileMenu/MobileMenu.tsx +116 -0
- package/src/system/Nav/NavItem.tsx +3 -3
- package/src/system/Nav/NavItemGroup.tsx +18 -2
- package/src/system/Nav/styles/variants/menu.ts +15 -9
- package/src/system/Nav/styles/variants/menugroup.ts +7 -3
- package/src/system/Nav/styles/variants/toolbar.ts +1 -2
- package/src/system/Nav/styles.ts +1 -0
- package/src/system/NewDialog/DialogClose.tsx +36 -23
- package/src/system/NewDialog/index.ts +3 -2
- package/src/system/NewForm/FormAutocomplete.css +1 -3
- package/src/system/Table/Table.tsx +1 -1
- package/src/system/Toolbar/Toolbar.stories.tsx +50 -3
- package/src/system/Toolbar/Toolbar.tsx +1 -1
- package/src/system/Toolbar/ToolbarUtilNav.tsx +19 -2
- package/src/system/Toolbar/index.tsx +6 -1
- package/src/system/index.js +4 -0
- package/src/system/theme/index.js +47 -18
- package/build/system/Form/Toggle.stories.jsx +0 -96
- package/src/system/Form/Toggle.js +0 -74
- package/src/system/Form/Toggle.stories.jsx +0 -96
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
2
|
+
/** @jsxImportSource theme-ui */
|
|
3
|
+
|
|
4
|
+
import * as Switch from '@radix-ui/react-switch';
|
|
5
|
+
import classNames, { Argument } from 'classnames';
|
|
6
|
+
import { Theme } from 'theme-ui';
|
|
7
|
+
|
|
8
|
+
// Documentation for Radix Switch component
|
|
9
|
+
// https://www.radix-ui.com/docs/primitives/components/switch
|
|
10
|
+
|
|
11
|
+
interface ThemeProps extends Theme {
|
|
12
|
+
outline?: Record< string, string >;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type ToggleProps = Switch.SwitchProps & {
|
|
16
|
+
name?: string;
|
|
17
|
+
className?: Argument;
|
|
18
|
+
onChange?: () => void;
|
|
19
|
+
variant?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const Toggle = ( {
|
|
23
|
+
name = 'toggle',
|
|
24
|
+
onChange,
|
|
25
|
+
className,
|
|
26
|
+
variant = 'primary',
|
|
27
|
+
...rest
|
|
28
|
+
}: ToggleProps ) => (
|
|
29
|
+
<Switch.Root
|
|
30
|
+
className={ classNames( 'vip-toggle-component', className ) }
|
|
31
|
+
sx={ {
|
|
32
|
+
all: 'unset',
|
|
33
|
+
cursor: 'pointer',
|
|
34
|
+
position: 'relative',
|
|
35
|
+
width: 40,
|
|
36
|
+
height: 20,
|
|
37
|
+
borderRadius: 5,
|
|
38
|
+
backgroundColor: 'muted',
|
|
39
|
+
backgroundRepeat: 'no-repeat',
|
|
40
|
+
backgroundPosition: 'right 2px top 2px',
|
|
41
|
+
backgroundImage: `url(
|
|
42
|
+
'data:image/svg+xml;utf8,<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M4.53846 3L3 4.53846L6.46156 8.00001L3.00003 11.4615L4.53848 13L8.00001 9.53847L11.4615 13L13 11.4615L9.53847 8.00001L13 4.53849L11.4615 3.00003L8.00001 6.46156L4.53846 3Z" fill="white"/></svg>')`,
|
|
43
|
+
WebkitTapHighlightColor: 'rgba(0, 0, 0, 0)',
|
|
44
|
+
|
|
45
|
+
'&:focus-visible': ( theme: ThemeProps ) => theme.outline,
|
|
46
|
+
'&[disabled]': {
|
|
47
|
+
opacity: 0.7,
|
|
48
|
+
},
|
|
49
|
+
'&[data-state="checked"]': {
|
|
50
|
+
backgroundColor: variant,
|
|
51
|
+
backgroundPosition: 'left 2px top 2px',
|
|
52
|
+
backgroundImage: `url(
|
|
53
|
+
'data:image/svg+xml;utf8,<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M13.4999 4.9995L5.7254 12.4008L2.5 9.33023L3.83307 7.92994L5.7254 9.73144L12.1668 3.59921L13.4999 4.9995Z" fill="white"/></svg>')`,
|
|
54
|
+
},
|
|
55
|
+
} }
|
|
56
|
+
name={ name }
|
|
57
|
+
onCheckedChange={ onChange }
|
|
58
|
+
{ ...rest }
|
|
59
|
+
>
|
|
60
|
+
<Switch.Thumb
|
|
61
|
+
sx={ {
|
|
62
|
+
display: 'block',
|
|
63
|
+
width: 16,
|
|
64
|
+
height: 16,
|
|
65
|
+
backgroundColor: 'white',
|
|
66
|
+
borderRadius: '100%',
|
|
67
|
+
boxShadow: 'rgb(0 0 0 / 5%) 0px 1px 5px, rgb(0 0 0 / 15%) 0px 1px 1px',
|
|
68
|
+
transition: 'transform 100ms',
|
|
69
|
+
transform: 'translateX(2px)',
|
|
70
|
+
willChange: 'transform',
|
|
71
|
+
'&[data-state="checked"]': { transform: 'translateX(22px)' },
|
|
72
|
+
} }
|
|
73
|
+
/>
|
|
74
|
+
</Switch.Root>
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
Toggle.displayName = 'Toggle';
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
4
|
import { Link } from '..';
|
|
5
|
+
import { Flex } from '../Flex/Flex';
|
|
5
6
|
|
|
7
|
+
import type { LinkProps } from './Link';
|
|
6
8
|
import type { StoryObj } from '@storybook/react';
|
|
7
9
|
|
|
8
10
|
/**
|
|
@@ -22,3 +24,22 @@ export const Default: Story = {
|
|
|
22
24
|
href: '#!',
|
|
23
25
|
},
|
|
24
26
|
};
|
|
27
|
+
|
|
28
|
+
const buttonTypes: LinkProps[ 'variant' ][] = [
|
|
29
|
+
'button-primary',
|
|
30
|
+
'button-secondary',
|
|
31
|
+
'button-tertiary',
|
|
32
|
+
'button-danger',
|
|
33
|
+
'button-display',
|
|
34
|
+
'button-ghost',
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
export const ButtonVariants = () => (
|
|
38
|
+
<Flex sx={ { gap: 2 } }>
|
|
39
|
+
{ buttonTypes.map( ( variant, index ) => (
|
|
40
|
+
<Link key={ index } href="#!" variant={ variant }>
|
|
41
|
+
Hello
|
|
42
|
+
</Link>
|
|
43
|
+
) ) }
|
|
44
|
+
</Flex>
|
|
45
|
+
);
|
package/src/system/Link/Link.tsx
CHANGED
|
@@ -15,33 +15,26 @@ interface LinkTheme extends Theme {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export interface LinkProps extends ThemeLinkProps {
|
|
18
|
-
|
|
18
|
+
variant?:
|
|
19
|
+
| 'primary'
|
|
20
|
+
| 'button-primary'
|
|
21
|
+
| 'button-secondary'
|
|
22
|
+
| 'button-tertiary'
|
|
23
|
+
| 'button-ghost'
|
|
24
|
+
| 'button-display'
|
|
25
|
+
| 'button-danger';
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
export const defaultLinkComponentStyle: ThemeUIStyleObject = {
|
|
22
|
-
color: 'link',
|
|
23
|
-
textDecorationThickness: '0.1em',
|
|
24
|
-
textUnderlineOffset: '0.1em',
|
|
25
|
-
'&:visited': {
|
|
26
|
-
color: 'links.visited',
|
|
27
|
-
},
|
|
28
|
-
'&:active': {
|
|
29
|
-
color: 'links.active',
|
|
30
|
-
},
|
|
31
|
-
'&:hover, &:focus': {
|
|
32
|
-
color: 'links.hover',
|
|
33
|
-
textDecorationLine: 'underline',
|
|
34
|
-
textDecorationThickness: '2px',
|
|
35
|
-
},
|
|
36
29
|
'&:focus-visible': ( theme: LinkTheme ) => theme.outline,
|
|
37
30
|
};
|
|
38
31
|
|
|
39
32
|
export const Link = forwardRef< HTMLAnchorElement, LinkProps >(
|
|
40
|
-
( {
|
|
33
|
+
( { variant = 'primary', sx, ...props }: LinkProps, ref: Ref< HTMLAnchorElement > ) => (
|
|
41
34
|
<ThemeLink
|
|
35
|
+
variant={ variant }
|
|
42
36
|
sx={ {
|
|
43
37
|
...defaultLinkComponentStyle,
|
|
44
|
-
color: active ? 'links.active' : 'link',
|
|
45
38
|
...sx,
|
|
46
39
|
} }
|
|
47
40
|
ref={ ref }
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/** @jsxImportSource theme-ui */
|
|
2
|
+
|
|
3
|
+
import { AiOutlineLock } from 'react-icons/ai';
|
|
4
|
+
import {
|
|
5
|
+
BiBell,
|
|
6
|
+
BiBulb,
|
|
7
|
+
BiCodeAlt,
|
|
8
|
+
BiData,
|
|
9
|
+
BiGridAlt,
|
|
10
|
+
BiHistory,
|
|
11
|
+
BiTachometer,
|
|
12
|
+
BiWindows,
|
|
13
|
+
} from 'react-icons/bi';
|
|
14
|
+
import { MdOutlinePhotoLibrary } from 'react-icons/md';
|
|
15
|
+
|
|
16
|
+
import { MobileMenu, MobileMenuTrigger, MobileMenuWrapper } from './MobileMenu';
|
|
17
|
+
import { Nav, NavItem } from '..';
|
|
18
|
+
import { CustomLink } from '../utils/stories/CustomLink';
|
|
19
|
+
|
|
20
|
+
import type { StoryObj } from '@storybook/react';
|
|
21
|
+
|
|
22
|
+
export default {
|
|
23
|
+
title: 'Navigation/MobileMenu',
|
|
24
|
+
component: MobileMenu,
|
|
25
|
+
parameters: {
|
|
26
|
+
docs: {
|
|
27
|
+
description: {
|
|
28
|
+
component: `
|
|
29
|
+
The MobileMenu component is a navigation component that is hidden by default and can be toggled open and closed. It opens a panel that can contain any content. The base of this component is a Drawer.
|
|
30
|
+
|
|
31
|
+
## Guidance
|
|
32
|
+
|
|
33
|
+
### When to use the Drawer component
|
|
34
|
+
|
|
35
|
+
- Use the MobileMenu component to displaying a drawer with mobile content.
|
|
36
|
+
|
|
37
|
+
### When to consider something else
|
|
38
|
+
|
|
39
|
+
- If you need something that is not a menu on a mobile view, consider using another component.
|
|
40
|
+
|
|
41
|
+
## Accessibility Considerations guidance
|
|
42
|
+
|
|
43
|
+
- The MobileMenu is based on the Drawer component and inherits all of its accessibility considerations.
|
|
44
|
+
|
|
45
|
+
## Using the component
|
|
46
|
+
|
|
47
|
+
- Look at the examples below to see how the MobileMenu component is used.
|
|
48
|
+
- You can use the \`toolbarItems\` prop to pass the content that will be in the header
|
|
49
|
+
- You can pass \`display\` prop to the MobileMenuTrigger to control the display of the trigger. The array must match the ThemeUI breakpoints.
|
|
50
|
+
|
|
51
|
+
## Component Properties
|
|
52
|
+
`,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
type Story = StoryObj< typeof MobileMenu >;
|
|
59
|
+
|
|
60
|
+
export const MobileMenuExample = () => (
|
|
61
|
+
<MobileMenuWrapper>
|
|
62
|
+
<MobileMenuTrigger label="Menu" variant="primary" display={ [ 'flex', 'flex', 'flex' ] } />
|
|
63
|
+
<MobileMenu
|
|
64
|
+
toolbarItems={
|
|
65
|
+
<>
|
|
66
|
+
<NavItem.MenuInverse href="/apps" active as={ CustomLink }>
|
|
67
|
+
My Applications
|
|
68
|
+
</NavItem.MenuInverse>
|
|
69
|
+
|
|
70
|
+
<NavItem.MenuInverse href="/orgs" as={ CustomLink }>
|
|
71
|
+
My Organizations
|
|
72
|
+
</NavItem.MenuInverse>
|
|
73
|
+
</>
|
|
74
|
+
}
|
|
75
|
+
>
|
|
76
|
+
<Nav.Menu sx={ { mb: 4 } } label="Nav Menu">
|
|
77
|
+
<NavItem.Menu
|
|
78
|
+
href="https://wordpress.com"
|
|
79
|
+
renderIcon={ size => <BiGridAlt size={ size } /> }
|
|
80
|
+
as={ CustomLink }
|
|
81
|
+
>
|
|
82
|
+
Overview
|
|
83
|
+
</NavItem.Menu>
|
|
84
|
+
<NavItem.Menu
|
|
85
|
+
as={ CustomLink }
|
|
86
|
+
href="https://random-website.com/"
|
|
87
|
+
renderIcon={ size => <BiWindows size={ size } /> }
|
|
88
|
+
>
|
|
89
|
+
Network Sites
|
|
90
|
+
</NavItem.Menu>
|
|
91
|
+
<NavItem.Menu
|
|
92
|
+
as={ CustomLink }
|
|
93
|
+
href="https://random-website.com/"
|
|
94
|
+
renderIcon={ size => <AiOutlineLock size={ size } /> }
|
|
95
|
+
>
|
|
96
|
+
Domains & TLS
|
|
97
|
+
</NavItem.Menu>
|
|
98
|
+
|
|
99
|
+
<NavItem.MenuGroup active label="Logs" renderIcon={ size => <BiHistory size={ size } /> }>
|
|
100
|
+
<NavItem.Menu as={ CustomLink } href="https://google.com/">
|
|
101
|
+
Audit
|
|
102
|
+
</NavItem.Menu>
|
|
103
|
+
<NavItem.Menu active as={ CustomLink } href="https://wpvip.com/">
|
|
104
|
+
Runtime
|
|
105
|
+
</NavItem.Menu>
|
|
106
|
+
<NavItem.Menu as={ CustomLink } href="https://dashboard.wpvip.com/">
|
|
107
|
+
Slow Query
|
|
108
|
+
</NavItem.Menu>
|
|
109
|
+
</NavItem.MenuGroup>
|
|
110
|
+
|
|
111
|
+
<NavItem.MenuGroup
|
|
112
|
+
label="Performance"
|
|
113
|
+
renderIcon={ size => <BiTachometer size={ size } /> }
|
|
114
|
+
>
|
|
115
|
+
<NavItem.Menu as={ CustomLink } href="https://random-website.com/">
|
|
116
|
+
Metrics
|
|
117
|
+
</NavItem.Menu>
|
|
118
|
+
<NavItem.Menu as={ CustomLink } href="https://random-website.com/">
|
|
119
|
+
Monitor
|
|
120
|
+
</NavItem.Menu>
|
|
121
|
+
<NavItem.Menu as={ CustomLink } href="https://random-website.com/">
|
|
122
|
+
Cache
|
|
123
|
+
</NavItem.Menu>
|
|
124
|
+
</NavItem.MenuGroup>
|
|
125
|
+
<NavItem.Menu
|
|
126
|
+
as={ CustomLink }
|
|
127
|
+
href="https://random-website.com/"
|
|
128
|
+
renderIcon={ size => <BiCodeAlt size={ size } /> }
|
|
129
|
+
>
|
|
130
|
+
Code [v]
|
|
131
|
+
</NavItem.Menu>
|
|
132
|
+
<NavItem.Menu
|
|
133
|
+
as={ CustomLink }
|
|
134
|
+
href="https://random-website.com/"
|
|
135
|
+
renderIcon={ size => <BiData size={ size } /> }
|
|
136
|
+
>
|
|
137
|
+
Database [v]
|
|
138
|
+
</NavItem.Menu>
|
|
139
|
+
<NavItem.Menu
|
|
140
|
+
as={ CustomLink }
|
|
141
|
+
href="https://random-website.com/"
|
|
142
|
+
renderIcon={ size => <MdOutlinePhotoLibrary size={ size } /> }
|
|
143
|
+
>
|
|
144
|
+
Media [v]
|
|
145
|
+
</NavItem.Menu>
|
|
146
|
+
<NavItem.Menu
|
|
147
|
+
as={ CustomLink }
|
|
148
|
+
href="https://random-website.com/"
|
|
149
|
+
renderIcon={ size => <BiBell size={ size } /> }
|
|
150
|
+
>
|
|
151
|
+
Notifications
|
|
152
|
+
</NavItem.Menu>
|
|
153
|
+
<NavItem.Menu
|
|
154
|
+
as={ CustomLink }
|
|
155
|
+
href="https://random-website.com/"
|
|
156
|
+
renderIcon={ size => <BiBulb size={ size } /> }
|
|
157
|
+
>
|
|
158
|
+
Features
|
|
159
|
+
</NavItem.Menu>
|
|
160
|
+
</Nav.Menu>
|
|
161
|
+
</MobileMenu>
|
|
162
|
+
</MobileMenuWrapper>
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
export const Default: Story = {
|
|
166
|
+
render: () => (
|
|
167
|
+
<>
|
|
168
|
+
<MobileMenuExample />
|
|
169
|
+
</>
|
|
170
|
+
),
|
|
171
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
/** @jsxImportSource theme-ui */
|
|
4
|
+
import { render, screen } from '@testing-library/react';
|
|
5
|
+
import userEvent from '@testing-library/user-event';
|
|
6
|
+
import { axe } from 'jest-axe';
|
|
7
|
+
import { ThemeUIProvider } from 'theme-ui';
|
|
8
|
+
|
|
9
|
+
import { MobileMenuExample } from './MobileMenu.stories';
|
|
10
|
+
import { theme } from '../';
|
|
11
|
+
|
|
12
|
+
const renderWithTheme = children =>
|
|
13
|
+
render( <ThemeUIProvider theme={ theme }>{ children }</ThemeUIProvider> );
|
|
14
|
+
|
|
15
|
+
const renderComponent = () => renderWithTheme( <MobileMenuExample /> );
|
|
16
|
+
|
|
17
|
+
function getMenuTrigger() {
|
|
18
|
+
return screen.getByRole( 'button', { name: 'Menu' } );
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe( '<MobileMenu />', () => {
|
|
22
|
+
it( 'renders the MobileMenu trigger', async () => {
|
|
23
|
+
const { container } = renderComponent();
|
|
24
|
+
|
|
25
|
+
// Should find the trigger
|
|
26
|
+
expect( getMenuTrigger() ).toBeInTheDocument();
|
|
27
|
+
|
|
28
|
+
expect( getMenuTrigger() ).toHaveAttribute( 'data-state', 'closed' );
|
|
29
|
+
|
|
30
|
+
// Check for accessibility issues
|
|
31
|
+
expect( await axe( container ) ).toHaveNoViolations();
|
|
32
|
+
} );
|
|
33
|
+
|
|
34
|
+
it( 'opens MobileMenu and check for items', async () => {
|
|
35
|
+
const user = userEvent.setup();
|
|
36
|
+
|
|
37
|
+
const { container } = renderComponent();
|
|
38
|
+
|
|
39
|
+
await user.click( getMenuTrigger() );
|
|
40
|
+
|
|
41
|
+
// Should find the open content
|
|
42
|
+
expect( screen.getByText( 'My Applications' ) ).toBeVisible(); // First menu
|
|
43
|
+
expect( screen.getByText( 'My Organizations' ) ).toBeVisible();
|
|
44
|
+
expect( screen.getByText( 'Performance' ) ).toBeVisible();
|
|
45
|
+
expect( screen.getByText( 'Features' ) ).toBeVisible(); // Last Menu
|
|
46
|
+
|
|
47
|
+
// Check for accessibility issues
|
|
48
|
+
expect( await axe( container ) ).toHaveNoViolations();
|
|
49
|
+
} );
|
|
50
|
+
} );
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/** @jsxImportSource theme-ui */
|
|
2
|
+
|
|
3
|
+
import React, { forwardRef } from 'react';
|
|
4
|
+
import { BiMenu } from 'react-icons/bi';
|
|
5
|
+
|
|
6
|
+
import * as Drawer from '../Drawer/Drawer';
|
|
7
|
+
import { DialogCloseDefault } from '../NewDialog/DialogClose';
|
|
8
|
+
import { Logo } from '../Toolbar/Logo';
|
|
9
|
+
import { Button, Flex, Nav, Box } from '../index';
|
|
10
|
+
|
|
11
|
+
export interface MobileMenuProps {
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
toolbarItems?: React.ReactNode;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const MobileMenu = forwardRef< HTMLDivElement, MobileMenuProps >(
|
|
17
|
+
( { children, toolbarItems }, ref ) => (
|
|
18
|
+
<Drawer.Content
|
|
19
|
+
variant="left"
|
|
20
|
+
label="Main Navigation Items"
|
|
21
|
+
ref={ ref }
|
|
22
|
+
renderClose={ () => <DialogCloseDefault variant="inverse" /> }
|
|
23
|
+
>
|
|
24
|
+
<Box
|
|
25
|
+
sx={ {
|
|
26
|
+
backgroundColor: 'toolbar.background',
|
|
27
|
+
boxShadow: 'none',
|
|
28
|
+
gap: 4,
|
|
29
|
+
} }
|
|
30
|
+
>
|
|
31
|
+
<Flex
|
|
32
|
+
sx={ {
|
|
33
|
+
flexWrap: 'wrap',
|
|
34
|
+
height: 64,
|
|
35
|
+
width: '100%',
|
|
36
|
+
py: 0,
|
|
37
|
+
px: 5,
|
|
38
|
+
gap: 7,
|
|
39
|
+
alignItems: 'center',
|
|
40
|
+
} }
|
|
41
|
+
>
|
|
42
|
+
<Logo />
|
|
43
|
+
</Flex>
|
|
44
|
+
|
|
45
|
+
<div
|
|
46
|
+
sx={ {
|
|
47
|
+
overflowX: 'hidden',
|
|
48
|
+
overflowY: 'auto',
|
|
49
|
+
height: 'calc(100vh - 64px)',
|
|
50
|
+
display: 'flex',
|
|
51
|
+
flex: 1,
|
|
52
|
+
} }
|
|
53
|
+
>
|
|
54
|
+
<Flex sx={ { width: '100%', flexDirection: 'column' } }>
|
|
55
|
+
{ toolbarItems && (
|
|
56
|
+
<Nav.Primary label="Main Links" orientation="vertical">
|
|
57
|
+
{ toolbarItems }
|
|
58
|
+
</Nav.Primary>
|
|
59
|
+
) }
|
|
60
|
+
|
|
61
|
+
<Box
|
|
62
|
+
sx={ {
|
|
63
|
+
alignSelf: 'stretch',
|
|
64
|
+
backgroundColor: 'layer.1',
|
|
65
|
+
minHeight: `calc(100vh - 64px)`,
|
|
66
|
+
gap: 4,
|
|
67
|
+
width: '100%',
|
|
68
|
+
a: {
|
|
69
|
+
border: 'none',
|
|
70
|
+
},
|
|
71
|
+
} }
|
|
72
|
+
>
|
|
73
|
+
{ children }
|
|
74
|
+
</Box>
|
|
75
|
+
</Flex>
|
|
76
|
+
</div>
|
|
77
|
+
</Box>
|
|
78
|
+
</Drawer.Content>
|
|
79
|
+
)
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
export const MobileMenuWrapper = ( { children }: MobileMenuProps ) => (
|
|
83
|
+
<Drawer.Root>{ children }</Drawer.Root>
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
type MobileMenuTriggerDisplayProp = 'none' | 'flex';
|
|
87
|
+
type MobileMenuTriggerProps = {
|
|
88
|
+
label: string;
|
|
89
|
+
variant?: 'primary' | 'inverse';
|
|
90
|
+
display?: MobileMenuTriggerDisplayProp[];
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const MobileMenuTrigger = ( {
|
|
94
|
+
label = 'Menu',
|
|
95
|
+
variant = 'primary',
|
|
96
|
+
display = [ 'none', 'flex', 'flex', 'none' ],
|
|
97
|
+
}: MobileMenuTriggerProps ) => (
|
|
98
|
+
<Drawer.Trigger>
|
|
99
|
+
<Button
|
|
100
|
+
type="button"
|
|
101
|
+
variant="tertiary"
|
|
102
|
+
sx={ {
|
|
103
|
+
display,
|
|
104
|
+
alignItems: 'center',
|
|
105
|
+
color:
|
|
106
|
+
variant === 'inverse' ? 'button.primary.label.default' : 'button.tertiary.label.default',
|
|
107
|
+
width: 38,
|
|
108
|
+
height: 38,
|
|
109
|
+
p: 0,
|
|
110
|
+
} }
|
|
111
|
+
aria-label={ label }
|
|
112
|
+
>
|
|
113
|
+
<BiMenu size={ 16 } role="presentation" />
|
|
114
|
+
</Button>
|
|
115
|
+
</Drawer.Trigger>
|
|
116
|
+
);
|
|
@@ -6,7 +6,7 @@ import { Ref, forwardRef } from 'react';
|
|
|
6
6
|
import { Theme, ThemeUIStyleObject } from 'theme-ui';
|
|
7
7
|
|
|
8
8
|
import { NavItemRenderIconProp, NavProps, NavVariant, VIP_NAV } from './Nav';
|
|
9
|
-
import { ItemGroupMenu } from './NavItemGroup';
|
|
9
|
+
import { IconContainer, ItemGroupMenu } from './NavItemGroup';
|
|
10
10
|
import { navItemLinkStyles, navItemStyles } from './styles';
|
|
11
11
|
|
|
12
12
|
export const NAV_ITEM_ICON_SIZE = 20;
|
|
@@ -90,7 +90,7 @@ const NavLink = forwardRef< HTMLAnchorElement, NavItemProps >(
|
|
|
90
90
|
{
|
|
91
91
|
children,
|
|
92
92
|
as: LinkComponent = NavRawLink,
|
|
93
|
-
renderIcon
|
|
93
|
+
renderIcon,
|
|
94
94
|
href,
|
|
95
95
|
active,
|
|
96
96
|
disabled,
|
|
@@ -115,7 +115,7 @@ const NavLink = forwardRef< HTMLAnchorElement, NavItemProps >(
|
|
|
115
115
|
{ ...rest }
|
|
116
116
|
>
|
|
117
117
|
<LinkComponent>
|
|
118
|
-
{ renderIcon( NAV_ITEM_ICON_SIZE ) }
|
|
118
|
+
{ renderIcon ? <IconContainer>{ renderIcon( NAV_ITEM_ICON_SIZE ) }</IconContainer> : null }
|
|
119
119
|
{ children }
|
|
120
120
|
</LinkComponent>
|
|
121
121
|
</NavigationMenu.Link>
|
|
@@ -28,7 +28,7 @@ const NavItemGroupBase = forwardRef< HTMLLIElement, NavItemGroupProps >(
|
|
|
28
28
|
orientation,
|
|
29
29
|
className,
|
|
30
30
|
active,
|
|
31
|
-
renderIcon
|
|
31
|
+
renderIcon,
|
|
32
32
|
children,
|
|
33
33
|
sx,
|
|
34
34
|
}: NavItemGroupProps,
|
|
@@ -59,7 +59,9 @@ const NavItemGroupBase = forwardRef< HTMLLIElement, NavItemGroupProps >(
|
|
|
59
59
|
...navItemGroupTriggerStyles,
|
|
60
60
|
} }
|
|
61
61
|
>
|
|
62
|
-
{ renderIcon
|
|
62
|
+
{ renderIcon ? (
|
|
63
|
+
<IconContainer>{ renderIcon( NAV_ITEM_ICON_SIZE ) }</IconContainer>
|
|
64
|
+
) : null }
|
|
63
65
|
{ label }
|
|
64
66
|
|
|
65
67
|
<BiChevronDown
|
|
@@ -79,6 +81,20 @@ const NavItemGroupBase = forwardRef< HTMLLIElement, NavItemGroupProps >(
|
|
|
79
81
|
}
|
|
80
82
|
);
|
|
81
83
|
|
|
84
|
+
export const IconContainer = ( { children }: { children: React.ReactNode } ) => (
|
|
85
|
+
<div
|
|
86
|
+
sx={ {
|
|
87
|
+
width: 28,
|
|
88
|
+
height: 30,
|
|
89
|
+
alignItems: 'center',
|
|
90
|
+
justifyContent: 'center',
|
|
91
|
+
display: 'inline-flex',
|
|
92
|
+
} }
|
|
93
|
+
>
|
|
94
|
+
{ children }
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
|
|
82
98
|
export const ItemGroupMenu = forwardRef< HTMLLIElement, NavItemGroupProps >(
|
|
83
99
|
( props: NavItemGroupProps, ref: Ref< HTMLLIElement > ) => (
|
|
84
100
|
<NavItemGroupBase { ...props } orientation="vertical" variant="menu" ref={ ref } />
|
|
@@ -14,7 +14,7 @@ export const menuItemStyles = ( orientation: NavProps[ 'orientation' ] ): ThemeU
|
|
|
14
14
|
border: 'none',
|
|
15
15
|
height: 38,
|
|
16
16
|
width: '100%',
|
|
17
|
-
|
|
17
|
+
justifyContent: 'space-between',
|
|
18
18
|
} );
|
|
19
19
|
|
|
20
20
|
// Menu Inverse Item Style <li>
|
|
@@ -31,7 +31,7 @@ export const menuInverseItemStyles = (
|
|
|
31
31
|
// Menu Item Link <a>
|
|
32
32
|
|
|
33
33
|
const visitedLink = '&:visited';
|
|
34
|
-
const activeAfter = '&[data-active]::
|
|
34
|
+
const activeAfter = '&[data-active]::before';
|
|
35
35
|
const active = '&[data-active]';
|
|
36
36
|
const focusNotActiveHoverNotActive = '&:focus:not(&[data-active]), &:hover:not(&[data-active])';
|
|
37
37
|
const notHover = ':not(&:hover)';
|
|
@@ -45,15 +45,19 @@ export const menuItemLinkStyles: MixedStyleProp = {
|
|
|
45
45
|
borderRadius: 1,
|
|
46
46
|
color: 'text',
|
|
47
47
|
display: 'inline-flex',
|
|
48
|
-
fontSize: '
|
|
48
|
+
fontSize: '0.875rem',
|
|
49
49
|
fontWeight: 'body',
|
|
50
|
-
gap: 3,
|
|
51
50
|
height: 38,
|
|
52
51
|
mx: 0,
|
|
53
52
|
mb: 0,
|
|
53
|
+
pt: 3,
|
|
54
|
+
pr: 4,
|
|
55
|
+
pb: 3,
|
|
54
56
|
pl: 5,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
':has(div > svg)': {
|
|
58
|
+
pl: 4,
|
|
59
|
+
},
|
|
60
|
+
gap: 1,
|
|
57
61
|
textDecoration: 'none',
|
|
58
62
|
width: '100%',
|
|
59
63
|
[ visitedLink ]: {
|
|
@@ -68,11 +72,11 @@ export const menuItemLinkStyles: MixedStyleProp = {
|
|
|
68
72
|
borderRadius: '90px',
|
|
69
73
|
height: 26,
|
|
70
74
|
top: '6px',
|
|
71
|
-
left:
|
|
75
|
+
left: 2,
|
|
72
76
|
},
|
|
73
77
|
[ active ]: {
|
|
74
78
|
color: 'heading',
|
|
75
|
-
backgroundColor: '
|
|
79
|
+
backgroundColor: 'backgrounds.primary',
|
|
76
80
|
textDecoration: 'none',
|
|
77
81
|
cursor: 'default',
|
|
78
82
|
svg: {
|
|
@@ -93,6 +97,8 @@ export const menuItemLinkStyles: MixedStyleProp = {
|
|
|
93
97
|
[ svgIcon ]: {
|
|
94
98
|
color: 'icon.secondary',
|
|
95
99
|
fill: 'icon.secondary',
|
|
100
|
+
width: 20,
|
|
101
|
+
height: 20,
|
|
96
102
|
display: 'block',
|
|
97
103
|
},
|
|
98
104
|
};
|
|
@@ -114,7 +120,7 @@ export const menuInverseItemLinkStyles = {
|
|
|
114
120
|
? {
|
|
115
121
|
...menuItemLinkStyles[ active ],
|
|
116
122
|
color: 'toolbar.text.default',
|
|
117
|
-
backgroundColor: '
|
|
123
|
+
backgroundColor: 'toolbar.background',
|
|
118
124
|
}
|
|
119
125
|
: {},
|
|
120
126
|
[ focusNotActiveHoverNotActive ]:
|
|
@@ -8,8 +8,12 @@ export const navItemGroupStyles = (
|
|
|
8
8
|
variant?: NavVariant
|
|
9
9
|
): ThemeUIStyleObject => {
|
|
10
10
|
const defaultStyle = {
|
|
11
|
-
|
|
11
|
+
li: {
|
|
12
|
+
mb: 1,
|
|
13
|
+
},
|
|
14
|
+
'li:last-of-type': {
|
|
12
15
|
mr: orientation === 'horizontal' ? 0 : undefined,
|
|
16
|
+
mb: orientation === 'vertical' ? 0 : undefined,
|
|
13
17
|
},
|
|
14
18
|
};
|
|
15
19
|
|
|
@@ -29,7 +33,6 @@ export const navItemGroupStyles = (
|
|
|
29
33
|
};
|
|
30
34
|
|
|
31
35
|
export const navItemGroupTriggerStyles: ThemeUIStyleObject = {
|
|
32
|
-
mb: 1,
|
|
33
36
|
'svg[data-arrow-indicator]': {
|
|
34
37
|
position: 'absolute',
|
|
35
38
|
right: 3,
|
|
@@ -53,8 +56,9 @@ export const navItemGroupTriggerStyles: ThemeUIStyleObject = {
|
|
|
53
56
|
export const navItemGroupContentUlStyles: ThemeUIStyleObject = {
|
|
54
57
|
m: 0,
|
|
55
58
|
p: 0,
|
|
56
|
-
pl:
|
|
59
|
+
pl: 5,
|
|
57
60
|
listStyle: 'none',
|
|
61
|
+
pt: 1,
|
|
58
62
|
};
|
|
59
63
|
|
|
60
64
|
const slideDown = keyframes( {
|
|
@@ -8,6 +8,7 @@ export const toolbarRootStyles: ThemeUIStyleObject = {
|
|
|
8
8
|
display: [ 'none', 'none', 'flex' ],
|
|
9
9
|
height: '100%',
|
|
10
10
|
ml: 0,
|
|
11
|
+
gap: 6,
|
|
11
12
|
width: 'max-content',
|
|
12
13
|
};
|
|
13
14
|
|
|
@@ -28,8 +29,6 @@ export const defaultToolbarLinkStyle: ThemeUIStyleObject = {
|
|
|
28
29
|
export const toolbarItemLinkStyles: ThemeUIStyleObject = {
|
|
29
30
|
...defaultItemLinkStyles,
|
|
30
31
|
position: 'relative',
|
|
31
|
-
ml: 3,
|
|
32
|
-
mr: 3,
|
|
33
32
|
height: '100%',
|
|
34
33
|
...defaultToolbarLinkStyle,
|
|
35
34
|
|
package/src/system/Nav/styles.ts
CHANGED