uikit-react-public 0.30.1 → 0.32.4
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/components/Heading/Heading.stories.d.ts +5 -7
- package/dist/components/MenuNew/Menu.context.d.ts +9 -0
- package/dist/components/MenuNew/Menu.d.ts +2 -1
- package/dist/components/MenuNew/MenuHeading.d.ts +1 -1
- package/dist/components/MenuNew/MenuItemText.d.ts +2 -1
- package/dist/components/MenuNew/PrimaryMenuItem.d.ts +1 -1
- package/dist/components/Select/Select.types.d.ts +1 -1
- package/dist/components/Sidebar/Sidebar.d.ts +12 -0
- package/dist/components/Sidebar/Sidebar.stories.d.ts +38 -0
- package/dist/components/Sidebar/__tests__/Sidebar.test.d.ts +1 -0
- package/dist/components/Sidebar/index.d.ts +2 -0
- package/dist/components/Snackbar/Snackbar.d.ts +9 -5
- package/dist/components/Snackbar/Snackbar.stories.d.ts +3 -1
- package/dist/components/Snackbar/Snackbars.d.ts +19 -0
- package/dist/components/Snackbar/Snackbars.stories.d.ts +52 -0
- package/dist/components/Snackbar/__tests__/Snackbars.test.d.ts +1 -0
- package/dist/components/Snackbar/index.d.ts +2 -0
- package/dist/components/index.d.ts +4 -1
- package/dist/index.js +5979 -5448
- package/lib/components/Dropdown/Dropdown.stories.tsx +69 -74
- package/lib/components/Heading/Documentation.mdx +4 -14
- package/lib/components/Heading/Heading.stories.tsx +16 -30
- package/lib/components/MenuNew/Menu.context.tsx +18 -0
- package/lib/components/MenuNew/Menu.tsx +14 -9
- package/lib/components/MenuNew/MenuDivider.tsx +12 -1
- package/lib/components/MenuNew/MenuHeading.tsx +6 -0
- package/lib/components/MenuNew/MenuItemText.tsx +27 -3
- package/lib/components/MenuNew/MenuSection.tsx +11 -0
- package/lib/components/MenuNew/PrimaryMenuItem.tsx +71 -2
- package/lib/components/Select/Select.types.ts +1 -7
- package/lib/components/Select/__tests__/Select.test.tsx +112 -0
- package/lib/components/Select/__tests__/__snapshots__/Select.test.tsx.snap +5 -0
- package/lib/components/Select/subcomponents/CustomSelect.tsx +75 -3
- package/lib/components/Select/subcomponents/FilterInput.tsx +1 -1
- package/lib/components/Sidebar/Sidebar.stories.tsx +94 -0
- package/lib/components/Sidebar/Sidebar.tsx +208 -0
- package/lib/components/Sidebar/__tests__/Sidebar.test.tsx +96 -0
- package/lib/components/Sidebar/__tests__/__snapshots__/Sidebar.test.tsx.snap +272 -0
- package/lib/components/Sidebar/index.ts +2 -0
- package/lib/components/Snackbar/Snackbar.stories.tsx +22 -0
- package/lib/components/Snackbar/Snackbar.tsx +168 -119
- package/lib/components/Snackbar/Snackbars.stories.tsx +141 -0
- package/lib/components/Snackbar/Snackbars.tsx +377 -0
- package/lib/components/Snackbar/__tests__/Snackbar.test.tsx +222 -10
- package/lib/components/Snackbar/__tests__/Snackbars.test.tsx +377 -0
- package/lib/components/Snackbar/__tests__/__snapshots__/Snackbar.test.tsx.snap +46 -25
- package/lib/components/Snackbar/index.ts +6 -0
- package/lib/components/index.ts +10 -1
- package/package.json +1 -1
|
@@ -8,7 +8,6 @@ import IconButton from '../IconButton';
|
|
|
8
8
|
import LabelledCheckbox from '../Checkbox/LabelledCheckbox';
|
|
9
9
|
import useTheme from '../../theme/useTheme';
|
|
10
10
|
import useMediaQuery from '../../hooks/useMediaQuery';
|
|
11
|
-
import { ThemeContextProvider, lightTheme } from '../../theme';
|
|
12
11
|
|
|
13
12
|
const meta = {
|
|
14
13
|
title: 'Components/Dropdown',
|
|
@@ -74,55 +73,53 @@ export const HeaderResponsiveTrigger: Story = {
|
|
|
74
73
|
`;
|
|
75
74
|
|
|
76
75
|
return (
|
|
77
|
-
<
|
|
78
|
-
<
|
|
79
|
-
<HeaderNew
|
|
80
|
-
<
|
|
81
|
-
<Dropdown
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
aria-label='Menu'
|
|
89
|
-
>
|
|
90
|
-
Menu
|
|
91
|
-
</Button>
|
|
92
|
-
) : (
|
|
93
|
-
<IconButton
|
|
94
|
-
className={iconButtonStyle}
|
|
95
|
-
aria-label='Menu'
|
|
96
|
-
>
|
|
97
|
-
<Icon.Menu size={20} />
|
|
98
|
-
</IconButton>
|
|
99
|
-
)}
|
|
100
|
-
</Dropdown.Trigger>
|
|
101
|
-
<Dropdown.Content className={menuStyle}>
|
|
102
|
-
<button
|
|
103
|
-
type='button'
|
|
104
|
-
className={menuItemStyle}
|
|
76
|
+
<div className={containerStyle}>
|
|
77
|
+
<HeaderNew title='Name of the application'>
|
|
78
|
+
<HeaderNew.MenuContainer>
|
|
79
|
+
<Dropdown {...args}>
|
|
80
|
+
<Dropdown.Trigger>
|
|
81
|
+
{isDesktop ? (
|
|
82
|
+
<Button
|
|
83
|
+
variant='primary-subtle'
|
|
84
|
+
size='small'
|
|
85
|
+
icon={<Icon.Menu size={20} />}
|
|
86
|
+
aria-label='Menu'
|
|
105
87
|
>
|
|
106
|
-
|
|
107
|
-
</
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
className={
|
|
88
|
+
Menu
|
|
89
|
+
</Button>
|
|
90
|
+
) : (
|
|
91
|
+
<IconButton
|
|
92
|
+
className={iconButtonStyle}
|
|
93
|
+
aria-label='Menu'
|
|
111
94
|
>
|
|
112
|
-
|
|
113
|
-
</
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
95
|
+
<Icon.Menu size={20} />
|
|
96
|
+
</IconButton>
|
|
97
|
+
)}
|
|
98
|
+
</Dropdown.Trigger>
|
|
99
|
+
<Dropdown.Content className={menuStyle}>
|
|
100
|
+
<button
|
|
101
|
+
type='button'
|
|
102
|
+
className={menuItemStyle}
|
|
103
|
+
>
|
|
104
|
+
View profile
|
|
105
|
+
</button>
|
|
106
|
+
<button
|
|
107
|
+
type='button'
|
|
108
|
+
className={menuItemStyle}
|
|
109
|
+
>
|
|
110
|
+
Team settings
|
|
111
|
+
</button>
|
|
112
|
+
<button
|
|
113
|
+
type='button'
|
|
114
|
+
className={menuItemStyle}
|
|
115
|
+
>
|
|
116
|
+
Sign out
|
|
117
|
+
</button>
|
|
118
|
+
</Dropdown.Content>
|
|
119
|
+
</Dropdown>
|
|
120
|
+
</HeaderNew.MenuContainer>
|
|
121
|
+
</HeaderNew>
|
|
122
|
+
</div>
|
|
126
123
|
);
|
|
127
124
|
},
|
|
128
125
|
};
|
|
@@ -152,33 +149,31 @@ export const StandaloneInfoPanel: Story = {
|
|
|
152
149
|
`;
|
|
153
150
|
|
|
154
151
|
return (
|
|
155
|
-
<
|
|
156
|
-
<
|
|
157
|
-
<Dropdown
|
|
158
|
-
<
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
icon={<Icon.Info size={20} />}
|
|
163
|
-
>
|
|
164
|
-
Notification preferences
|
|
165
|
-
</Button>
|
|
166
|
-
</Dropdown.Trigger>
|
|
167
|
-
<Dropdown.Content
|
|
168
|
-
className={infoContentStyle}
|
|
169
|
-
align='left'
|
|
152
|
+
<div className={containerStyle}>
|
|
153
|
+
<Dropdown {...args}>
|
|
154
|
+
<Dropdown.Trigger>
|
|
155
|
+
<Button
|
|
156
|
+
variant='secondary'
|
|
157
|
+
size='small'
|
|
158
|
+
icon={<Icon.Info size={20} />}
|
|
170
159
|
>
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
</
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
160
|
+
Notification preferences
|
|
161
|
+
</Button>
|
|
162
|
+
</Dropdown.Trigger>
|
|
163
|
+
<Dropdown.Content
|
|
164
|
+
className={infoContentStyle}
|
|
165
|
+
align='left'
|
|
166
|
+
>
|
|
167
|
+
<p className={infoTitleStyle}>Weekly update</p>
|
|
168
|
+
<p className={infoBodyStyle}>
|
|
169
|
+
Get a summary of new activity in your workspace every Friday.
|
|
170
|
+
</p>
|
|
171
|
+
<LabelledCheckbox id='storybook-weekly-update-checkbox'>
|
|
172
|
+
Email me the weekly summary
|
|
173
|
+
</LabelledCheckbox>
|
|
174
|
+
</Dropdown.Content>
|
|
175
|
+
</Dropdown>
|
|
176
|
+
</div>
|
|
182
177
|
);
|
|
183
178
|
},
|
|
184
179
|
};
|
|
@@ -15,8 +15,7 @@ export const usage = {
|
|
|
15
15
|
level2: `<Heading level={2}>Level 2 Heading</Heading>`,
|
|
16
16
|
level3: `<Heading level={3}>Level 3 Heading</Heading>`,
|
|
17
17
|
level4: `<Heading level={4}>Level 4 Heading</Heading>`,
|
|
18
|
-
withMargins: `<Heading
|
|
19
|
-
noMargins: `<Heading margins={false} >Heading with no margins</Heading>`,
|
|
18
|
+
withMargins: `<Heading mb="24">Heading with bottom margin</Heading>`,
|
|
20
19
|
};
|
|
21
20
|
|
|
22
21
|
<Meta of={HeadingStories} />
|
|
@@ -52,7 +51,8 @@ All screens in UCL Experience apps should have a sensible semantic hierarchy. Th
|
|
|
52
51
|
### Props
|
|
53
52
|
|
|
54
53
|
- `level`: A **number** from 1 to 4, corresponding to the level of the HTML heading element to be rendered. Default is 1, if otherwise unspecified.
|
|
55
|
-
- `
|
|
54
|
+
- `m`, `mv`, `mh`, `mt`, `mb`, `ml`, `mr`: common margin props for applying theme spacing.
|
|
55
|
+
- `margins`: Deprecated. If `false`, the heading will have no margins.
|
|
56
56
|
- `testid`: A **string** value. If provided, the heading element will have a `data-testid` attribute with this value. The <a href="https://www.educative.io/answers/what-is-the-data-testid-attribute-in-testing" target="_blank">`data-testid` attribute</a> is used for identifying this element for testing. If unspecified, `ucl-uikit-heading` is used.
|
|
57
57
|
|
|
58
58
|
Pass the text you want in the heading as children: `<Heading>My heading text</Heading>`.
|
|
@@ -99,20 +99,10 @@ Custom CSS styling can be added as with all UIKit-React components by passing an
|
|
|
99
99
|
|
|
100
100
|
Red lines are added to the examples below to show the margins of the heading element.
|
|
101
101
|
|
|
102
|
-
Note that `margins={true}` in the below example merely specifies explicitly the default behaviour.
|
|
103
|
-
|
|
104
102
|
<Canvas
|
|
105
|
-
of={HeadingStories.
|
|
103
|
+
of={HeadingStories.withMargins}
|
|
106
104
|
sourceState='shown'
|
|
107
105
|
source={{
|
|
108
106
|
code: usage.withMargins,
|
|
109
107
|
}}
|
|
110
108
|
/>
|
|
111
|
-
|
|
112
|
-
<Canvas
|
|
113
|
-
of={HeadingStories.NoMargins}
|
|
114
|
-
sourceState='shown'
|
|
115
|
-
source={{
|
|
116
|
-
code: usage.noMargins,
|
|
117
|
-
}}
|
|
118
|
-
/>
|
|
@@ -21,45 +21,31 @@ export const Default: Story = {
|
|
|
21
21
|
},
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
export const
|
|
24
|
+
export const Level1: Story = {
|
|
25
25
|
args: {
|
|
26
|
-
level:
|
|
27
|
-
children: '
|
|
26
|
+
level: 1,
|
|
27
|
+
children: 'Level 1 Heading',
|
|
28
28
|
},
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
export const
|
|
31
|
+
export const Level2: Story = {
|
|
32
32
|
args: {
|
|
33
|
-
level:
|
|
34
|
-
children: '
|
|
33
|
+
level: 2,
|
|
34
|
+
children: 'Level 2 Heading',
|
|
35
35
|
},
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
-
export const
|
|
38
|
+
export const Level3: Story = {
|
|
39
39
|
args: {
|
|
40
|
-
level:
|
|
41
|
-
children: '
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export const LevelMd: Story = {
|
|
46
|
-
args: {
|
|
47
|
-
level: 'md',
|
|
48
|
-
children: 'Heading level MD',
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export const LevelSm: Story = {
|
|
53
|
-
args: {
|
|
54
|
-
level: 'sm',
|
|
55
|
-
children: 'Heading level SM',
|
|
40
|
+
level: 3,
|
|
41
|
+
children: 'Level 3 Heading',
|
|
56
42
|
},
|
|
57
43
|
};
|
|
58
44
|
|
|
59
|
-
export const
|
|
45
|
+
export const Level4: Story = {
|
|
60
46
|
args: {
|
|
61
|
-
level:
|
|
62
|
-
children: '
|
|
47
|
+
level: 4,
|
|
48
|
+
children: 'Level 4 Heading',
|
|
63
49
|
},
|
|
64
50
|
};
|
|
65
51
|
|
|
@@ -71,12 +57,12 @@ export const AsSpan: Story = {
|
|
|
71
57
|
},
|
|
72
58
|
};
|
|
73
59
|
|
|
74
|
-
export const
|
|
75
|
-
name: '
|
|
60
|
+
export const withMargins: Story = {
|
|
61
|
+
name: 'With margins',
|
|
76
62
|
args: {
|
|
77
|
-
|
|
63
|
+
mb: '24',
|
|
78
64
|
level: 'md',
|
|
79
|
-
children: 'Heading
|
|
65
|
+
children: 'Heading with bottom margin',
|
|
80
66
|
},
|
|
81
67
|
decorators: [
|
|
82
68
|
(Story) => (
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ReactNode, createContext, use } from 'react';
|
|
2
|
+
|
|
3
|
+
interface MenuContextValue {
|
|
4
|
+
collapsed: boolean;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const MenuContext = createContext<MenuContextValue>({
|
|
8
|
+
collapsed: false,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export const MenuProvider = ({
|
|
12
|
+
collapsed,
|
|
13
|
+
children,
|
|
14
|
+
}: MenuContextValue & { children: ReactNode }) => (
|
|
15
|
+
<MenuContext value={{ collapsed }}>{children}</MenuContext>
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
export const useMenu = () => use(MenuContext);
|
|
@@ -13,12 +13,14 @@ import ActionMenuButton, { ActionMenuButtonProps } from './ActionMenuButton';
|
|
|
13
13
|
import PrimaryMenuItem, { PrimaryMenuItemProps } from './PrimaryMenuItem';
|
|
14
14
|
import SecondaryMenuItem, { SecondaryMenuItemProps } from './SecondaryMenuItem';
|
|
15
15
|
import { useTheme } from '../../theme';
|
|
16
|
+
import { MenuProvider } from './Menu.context';
|
|
16
17
|
|
|
17
18
|
export const NAME = 'ucl-uikit-menu';
|
|
18
19
|
|
|
19
|
-
export interface MenuProps extends HTMLAttributes<
|
|
20
|
+
export interface MenuProps extends HTMLAttributes<HTMLElement> {
|
|
20
21
|
testId?: string;
|
|
21
22
|
border?: boolean;
|
|
23
|
+
collapsed?: boolean;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
export type {
|
|
@@ -39,6 +41,7 @@ const Menu = ({
|
|
|
39
41
|
className,
|
|
40
42
|
children,
|
|
41
43
|
border = false,
|
|
44
|
+
collapsed = false,
|
|
42
45
|
...props
|
|
43
46
|
}: MenuProps) => {
|
|
44
47
|
const [theme] = useTheme();
|
|
@@ -58,14 +61,16 @@ const Menu = ({
|
|
|
58
61
|
const style = cx(baseStyle, border && borderStyle, className);
|
|
59
62
|
|
|
60
63
|
return (
|
|
61
|
-
<
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
<MenuProvider collapsed={collapsed}>
|
|
65
|
+
<nav
|
|
66
|
+
className={style}
|
|
67
|
+
data-testid={testId}
|
|
68
|
+
role='menu'
|
|
69
|
+
{...props}
|
|
70
|
+
>
|
|
71
|
+
{children}
|
|
72
|
+
</nav>
|
|
73
|
+
</MenuProvider>
|
|
69
74
|
);
|
|
70
75
|
};
|
|
71
76
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { HTMLAttributes } from 'react';
|
|
2
2
|
import { css, cx } from '@emotion/css';
|
|
3
3
|
import { useTheme } from '../../theme';
|
|
4
|
+
import { useMenu } from './Menu.context';
|
|
4
5
|
|
|
5
6
|
export const NAME = 'ucl-uikit-menu__divider';
|
|
6
7
|
|
|
@@ -8,13 +9,23 @@ export interface MenuDividerProps extends HTMLAttributes<HTMLHRElement> {}
|
|
|
8
9
|
|
|
9
10
|
const MenuDivider = ({ className, ...props }: MenuDividerProps) => {
|
|
10
11
|
const [theme] = useTheme();
|
|
12
|
+
const { collapsed } = useMenu();
|
|
11
13
|
const baseStyle = css`
|
|
14
|
+
flex: 0 0 1px;
|
|
15
|
+
min-height: 1px;
|
|
16
|
+
box-sizing: border-box;
|
|
17
|
+
width: 100%;
|
|
18
|
+
align-self: stretch;
|
|
12
19
|
border: 0;
|
|
13
20
|
height: 1px;
|
|
14
21
|
background-color: ${theme.colour.border.subtle};
|
|
15
22
|
margin: ${theme.margin.m16} 0;
|
|
16
23
|
`;
|
|
17
|
-
const
|
|
24
|
+
const collapsedStyle = css`
|
|
25
|
+
width: 32px;
|
|
26
|
+
align-self: center;
|
|
27
|
+
`;
|
|
28
|
+
const style = cx(NAME, baseStyle, collapsed && collapsedStyle, className);
|
|
18
29
|
|
|
19
30
|
return (
|
|
20
31
|
<hr
|
|
@@ -2,6 +2,7 @@ import { HTMLAttributes } from 'react';
|
|
|
2
2
|
import { css, cx } from '@emotion/css';
|
|
3
3
|
import Heading from '../Heading';
|
|
4
4
|
import { useTheme } from '../../theme';
|
|
5
|
+
import { useMenu } from './Menu.context';
|
|
5
6
|
|
|
6
7
|
export const NAME = 'ucl-uikit-menu__heading';
|
|
7
8
|
|
|
@@ -16,6 +17,7 @@ const MenuHeading = ({
|
|
|
16
17
|
...props
|
|
17
18
|
}: MenuHeadingProps) => {
|
|
18
19
|
const [theme] = useTheme();
|
|
20
|
+
const { collapsed } = useMenu();
|
|
19
21
|
|
|
20
22
|
const baseStyle = css`
|
|
21
23
|
box-sizing: border-box;
|
|
@@ -30,6 +32,10 @@ const MenuHeading = ({
|
|
|
30
32
|
|
|
31
33
|
const style = cx(NAME, baseStyle, className);
|
|
32
34
|
|
|
35
|
+
if (collapsed) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
33
39
|
const headingStyle = css`
|
|
34
40
|
color: ${theme.colour.text.secondary};
|
|
35
41
|
text-transform: uppercase;
|
|
@@ -13,12 +13,14 @@ export const NAME = 'ucl-uikit-menu__item-text';
|
|
|
13
13
|
export interface MenuItemTextProps {
|
|
14
14
|
asChild?: boolean;
|
|
15
15
|
className?: string;
|
|
16
|
+
visuallyHidden?: boolean;
|
|
16
17
|
children?: ReactNode;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
const MenuItemText = ({
|
|
20
21
|
asChild = false,
|
|
21
22
|
className,
|
|
23
|
+
visuallyHidden = false,
|
|
22
24
|
children,
|
|
23
25
|
}: MenuItemTextProps) => {
|
|
24
26
|
const asChildResetStyle = css`
|
|
@@ -46,18 +48,40 @@ const MenuItemText = ({
|
|
|
46
48
|
box-shadow: none;
|
|
47
49
|
}
|
|
48
50
|
`;
|
|
49
|
-
const
|
|
51
|
+
const visuallyHiddenStyle = css`
|
|
52
|
+
position: absolute;
|
|
53
|
+
width: 1px;
|
|
54
|
+
height: 1px;
|
|
55
|
+
padding: 0;
|
|
56
|
+
margin: -1px;
|
|
57
|
+
overflow: hidden;
|
|
58
|
+
clip: rect(0 0 0 0);
|
|
59
|
+
white-space: nowrap;
|
|
60
|
+
border: 0;
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
const style = cx(NAME, visuallyHidden && visuallyHiddenStyle, className);
|
|
50
64
|
|
|
51
65
|
if (asChild) {
|
|
66
|
+
const visuallyHiddenAsChildStyle = css`
|
|
67
|
+
color: transparent;
|
|
68
|
+
font-size: 0;
|
|
69
|
+
line-height: 0;
|
|
70
|
+
`;
|
|
71
|
+
|
|
52
72
|
const child = Children.only(children) as ReactElement<{
|
|
53
73
|
className?: string;
|
|
54
74
|
}>;
|
|
55
75
|
|
|
56
76
|
if (isValidElement(child)) {
|
|
57
77
|
return (
|
|
58
|
-
<Text className={
|
|
78
|
+
<Text className={cx(NAME, className)}>
|
|
59
79
|
{cloneElement(child, {
|
|
60
|
-
className: cx(
|
|
80
|
+
className: cx(
|
|
81
|
+
asChildResetStyle,
|
|
82
|
+
visuallyHidden && visuallyHiddenAsChildStyle,
|
|
83
|
+
child.props.className
|
|
84
|
+
),
|
|
61
85
|
})}
|
|
62
86
|
</Text>
|
|
63
87
|
);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { HTMLAttributes } from 'react';
|
|
2
2
|
import { css, cx } from '@emotion/css';
|
|
3
|
+
import { useMenu } from './Menu.context';
|
|
3
4
|
|
|
4
5
|
export const NAME = 'ucl-uikit-menu__section';
|
|
5
6
|
|
|
@@ -13,12 +14,22 @@ const MenuSection = ({
|
|
|
13
14
|
children,
|
|
14
15
|
...props
|
|
15
16
|
}: MenuSectionProps) => {
|
|
17
|
+
const { collapsed } = useMenu();
|
|
18
|
+
|
|
16
19
|
const style = cx(
|
|
17
20
|
NAME,
|
|
18
21
|
css`
|
|
22
|
+
width: 100%;
|
|
23
|
+
box-sizing: border-box;
|
|
19
24
|
margin: 0;
|
|
20
25
|
padding: 0;
|
|
21
26
|
`,
|
|
27
|
+
collapsed &&
|
|
28
|
+
css`
|
|
29
|
+
display: flex;
|
|
30
|
+
flex-direction: column;
|
|
31
|
+
align-items: center;
|
|
32
|
+
`,
|
|
22
33
|
className
|
|
23
34
|
);
|
|
24
35
|
|
|
@@ -4,6 +4,7 @@ import { MenuBaseItemProps } from './MenuBaseItem';
|
|
|
4
4
|
import MenuBaseItem from './MenuBaseItem';
|
|
5
5
|
import MenuItemText from './MenuItemText';
|
|
6
6
|
import { useTheme } from '../../theme';
|
|
7
|
+
import { useMenu } from './Menu.context';
|
|
7
8
|
|
|
8
9
|
export const NAME = 'ucl-uikit-menu__primary-item';
|
|
9
10
|
|
|
@@ -20,20 +21,84 @@ const PrimaryMenuItem = ({
|
|
|
20
21
|
children,
|
|
21
22
|
asChild,
|
|
22
23
|
trailingContent,
|
|
24
|
+
icon: iconProp,
|
|
25
|
+
active = false,
|
|
23
26
|
...props
|
|
24
27
|
}: PrimaryMenuItemProps) => {
|
|
25
28
|
const [theme] = useTheme();
|
|
29
|
+
const { collapsed } = useMenu();
|
|
26
30
|
|
|
27
31
|
const baseStyle = css`
|
|
28
32
|
color: ${theme.colour.text.default};
|
|
29
33
|
padding: 0 ${theme.padding.p16};
|
|
34
|
+
white-space: nowrap;
|
|
35
|
+
border-radius: 4px;
|
|
36
|
+
|
|
37
|
+
> svg {
|
|
38
|
+
width: 20px;
|
|
39
|
+
height: 20px;
|
|
40
|
+
flex: 0 0 20px;
|
|
41
|
+
}
|
|
30
42
|
|
|
31
43
|
@media screen and (min-width: ${theme.breakpoints.tablet}px) {
|
|
32
44
|
padding: 0 ${theme.padding.p16};
|
|
33
45
|
}
|
|
46
|
+
|
|
47
|
+
&:focus-visible,
|
|
48
|
+
&:focus-within {
|
|
49
|
+
border-radius: 4px;
|
|
50
|
+
box-shadow: none !important;
|
|
51
|
+
z-index: 1;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
&:focus-visible::after,
|
|
55
|
+
&:focus-within::after {
|
|
56
|
+
content: '';
|
|
57
|
+
position: absolute;
|
|
58
|
+
inset: 0;
|
|
59
|
+
box-sizing: border-box;
|
|
60
|
+
border-radius: 4px;
|
|
61
|
+
border: 2px solid ${theme.colour.border.brand};
|
|
62
|
+
pointer-events: none;
|
|
63
|
+
}
|
|
34
64
|
`;
|
|
35
65
|
|
|
36
|
-
const
|
|
66
|
+
const collapsedStyle = css`
|
|
67
|
+
width: 32px;
|
|
68
|
+
min-height: 32px;
|
|
69
|
+
height: 32px;
|
|
70
|
+
padding: 0;
|
|
71
|
+
margin-bottom: ${theme.margin.m8};
|
|
72
|
+
justify-content: center;
|
|
73
|
+
gap: 0;
|
|
74
|
+
|
|
75
|
+
&:focus-visible,
|
|
76
|
+
&:focus-within {
|
|
77
|
+
box-shadow: ${theme.boxShadow.focus} !important;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
&:focus-visible::after,
|
|
81
|
+
&:focus-within::after {
|
|
82
|
+
content: none;
|
|
83
|
+
}
|
|
84
|
+
`;
|
|
85
|
+
|
|
86
|
+
const collapsedActiveStyle = css`
|
|
87
|
+
background-color: ${theme.colour.fill.brand};
|
|
88
|
+
color: ${theme.colour.fill.inverse};
|
|
89
|
+
|
|
90
|
+
&:hover {
|
|
91
|
+
background-color: ${theme.colour.fill.brandHover};
|
|
92
|
+
}
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
const style = cx(
|
|
96
|
+
NAME,
|
|
97
|
+
baseStyle,
|
|
98
|
+
collapsed && collapsedStyle,
|
|
99
|
+
collapsed && active && collapsedActiveStyle,
|
|
100
|
+
className
|
|
101
|
+
);
|
|
37
102
|
|
|
38
103
|
const textStyle = css``;
|
|
39
104
|
|
|
@@ -48,16 +113,20 @@ const PrimaryMenuItem = ({
|
|
|
48
113
|
className={style}
|
|
49
114
|
asChild={asChild}
|
|
50
115
|
iconPosition='left'
|
|
116
|
+
icon={iconProp}
|
|
117
|
+
active={!collapsed && active}
|
|
118
|
+
aria-current={active ? 'page' : props['aria-current']}
|
|
51
119
|
{...props}
|
|
52
120
|
>
|
|
53
121
|
<span className={contentStyle}>
|
|
54
122
|
<MenuItemText
|
|
55
123
|
asChild={asChild}
|
|
56
124
|
className={textStyle}
|
|
125
|
+
visuallyHidden={collapsed}
|
|
57
126
|
>
|
|
58
127
|
{children}
|
|
59
128
|
</MenuItemText>
|
|
60
|
-
{trailingContent}
|
|
129
|
+
{!collapsed && trailingContent}
|
|
61
130
|
</span>
|
|
62
131
|
</MenuBaseItem>
|
|
63
132
|
);
|
|
@@ -28,13 +28,7 @@ export type OptionData<T> = {
|
|
|
28
28
|
*/
|
|
29
29
|
export type FilterInputProps = Omit<
|
|
30
30
|
React.InputHTMLAttributes<HTMLInputElement>,
|
|
31
|
-
| '
|
|
32
|
-
| 'onChange'
|
|
33
|
-
| 'disabled'
|
|
34
|
-
| 'ref'
|
|
35
|
-
| 'role'
|
|
36
|
-
| 'aria-autocomplete'
|
|
37
|
-
| 'aria-label'
|
|
31
|
+
'value' | 'onChange' | 'disabled' | 'ref' | 'role' | 'aria-autocomplete'
|
|
38
32
|
>;
|
|
39
33
|
|
|
40
34
|
export type SelectDropdownWidth = 'content' | 'match-select';
|