uikit-react-public 0.24.4 → 0.25.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/components/Badge/Badge.d.ts +34 -2
- package/dist/components/Badge/Badge.stories.d.ts +46 -1
- package/dist/components/MenuNew/ActionMenuItem.d.ts +7 -0
- package/dist/components/MenuNew/Menu.d.ts +15 -15
- package/dist/components/MenuNew/MenuBaseItem.d.ts +7 -0
- package/dist/components/MenuNew/MenuDivider.d.ts +6 -0
- package/dist/components/MenuNew/MenuItemText.d.ts +9 -0
- package/dist/components/MenuNew/PrimaryMenuItem.d.ts +9 -0
- package/dist/components/MenuNew/SecondaryMenuItem.d.ts +8 -0
- package/dist/components/MenuNew/__tests__/Menu.test.d.ts +1 -0
- package/dist/components/MenuNew/index.d.ts +1 -5
- package/dist/components/Text/Text.d.ts +10 -0
- package/dist/components/Text/index.d.ts +2 -0
- package/dist/components/index.d.ts +2 -4
- package/dist/index.js +5980 -5919
- package/lib/components/Badge/Badge.stories.tsx +113 -0
- package/lib/components/Badge/Badge.tsx +171 -12
- package/lib/components/Badge/__tests__/Badge.test.tsx +122 -0
- package/lib/components/Badge/__tests__/__snapshots__/Badge.test.tsx.snap +133 -0
- package/lib/components/MenuNew/ActionMenuItem.tsx +31 -0
- package/lib/components/MenuNew/Menu.tsx +37 -49
- package/lib/components/MenuNew/MenuBaseItem.tsx +45 -0
- package/lib/components/MenuNew/MenuDivider.tsx +25 -0
- package/lib/components/MenuNew/MenuItemText.tsx +58 -0
- package/lib/components/MenuNew/PrimaryMenuItem.tsx +60 -0
- package/lib/components/MenuNew/SecondaryMenuItem.tsx +61 -0
- package/lib/components/MenuNew/__tests__/Menu.test.tsx +87 -0
- package/lib/components/MenuNew/index.ts +8 -7
- package/lib/components/Text/Text.tsx +171 -0
- package/lib/components/Text/index.ts +2 -0
- package/lib/components/index.ts +2 -7
- package/package.json +1 -1
- package/dist/components/MenuNew/Menu.context.d.ts +0 -14
- package/dist/components/MenuNew/MenuContent.d.ts +0 -9
- package/dist/components/MenuNew/MenuItem.d.ts +0 -10
- package/dist/components/MenuNew/MenuSection.d.ts +0 -7
- package/dist/components/MenuNew/trigger/ButtonMenuTrigger.d.ts +0 -8
- package/dist/components/MenuNew/trigger/IconMenuTrigger.d.ts +0 -8
- package/dist/components/SimpleMenu/SimpleMenu.d.ts +0 -7
- package/dist/components/SimpleMenu/index.d.ts +0 -2
- package/lib/components/MenuNew/Menu.context.tsx +0 -149
- package/lib/components/MenuNew/MenuContent.tsx +0 -140
- package/lib/components/MenuNew/MenuItem.tsx +0 -101
- package/lib/components/MenuNew/MenuSection.tsx +0 -47
- package/lib/components/MenuNew/trigger/ButtonMenuTrigger.tsx +0 -42
- package/lib/components/MenuNew/trigger/IconMenuTrigger.tsx +0 -40
- package/lib/components/SimpleMenu/SimpleMenu.tsx +0 -40
- package/lib/components/SimpleMenu/__tests__/SimpleMenu.test.tsx +0 -38
- package/lib/components/SimpleMenu/index.ts +0 -2
- /package/dist/components/{SimpleMenu/__tests__/SimpleMenu.test.d.ts → Badge/__tests__/Badge.test.d.ts} +0 -0
|
@@ -1,44 +1,37 @@
|
|
|
1
|
-
import { HTMLAttributes, memo } from 'react';
|
|
1
|
+
import { HTMLAttributes, NamedExoticComponent, memo } from 'react';
|
|
2
2
|
import { css, cx } from '@emotion/css';
|
|
3
|
+
import MenuBaseItem, { MenuBaseItemProps } from './MenuBaseItem';
|
|
4
|
+
import MenuDivider, { MenuDividerProps } from './MenuDivider';
|
|
5
|
+
import ActionMenuItem, { ActionMenuItemProps } from './ActionMenuItem';
|
|
6
|
+
import PrimaryMenuItem, { PrimaryMenuItemProps } from './PrimaryMenuItem';
|
|
7
|
+
import SecondaryMenuItem, { SecondaryMenuItemProps } from './SecondaryMenuItem';
|
|
3
8
|
import { useTheme } from '../../theme';
|
|
4
|
-
import MenuProvider from './Menu.context';
|
|
5
|
-
import MenuContent from './MenuContent';
|
|
6
|
-
import MenuItem from './MenuItem';
|
|
7
|
-
import MenuSection from './MenuSection';
|
|
8
|
-
import ButtonMenuTrigger, { ButtonMenuTriggerProps } from './trigger/ButtonMenuTrigger';
|
|
9
9
|
|
|
10
10
|
export const NAME = 'ucl-uikit-menu';
|
|
11
11
|
|
|
12
12
|
export interface MenuProps extends HTMLAttributes<HTMLDivElement> {
|
|
13
|
-
defaultOpen?: boolean;
|
|
14
|
-
title?: string;
|
|
15
|
-
menuId?: string;
|
|
16
|
-
position?: 'left' | 'right';
|
|
17
|
-
trigger?: React.ComponentType<ButtonMenuTriggerProps>;
|
|
18
13
|
testId?: string;
|
|
19
14
|
}
|
|
20
15
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
children,
|
|
29
|
-
className,
|
|
30
|
-
...props
|
|
31
|
-
}: MenuProps) => {
|
|
32
|
-
const [theme] = useTheme();
|
|
16
|
+
export type {
|
|
17
|
+
MenuDividerProps,
|
|
18
|
+
MenuBaseItemProps,
|
|
19
|
+
PrimaryMenuItemProps,
|
|
20
|
+
SecondaryMenuItemProps,
|
|
21
|
+
ActionMenuItemProps,
|
|
22
|
+
};
|
|
33
23
|
|
|
24
|
+
const Menu = ({ testId = NAME, className, children, ...props }: MenuProps) => {
|
|
25
|
+
const [theme] = useTheme();
|
|
34
26
|
const baseStyle = css`
|
|
35
|
-
|
|
36
|
-
font-family: ${theme.font.family.primary};
|
|
37
|
-
`;
|
|
27
|
+
padding: ${theme.padding.p16} ${theme.padding.p24};
|
|
38
28
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
29
|
+
@media screen and (min-width: ${theme.breakpoints.tablet}px) {
|
|
30
|
+
padding: ${theme.padding.p48} ${theme.padding.p64};
|
|
31
|
+
border: 1px solid ${theme.colour.border.subtle};
|
|
32
|
+
}
|
|
33
|
+
`;
|
|
34
|
+
const style = cx(baseStyle, className);
|
|
42
35
|
|
|
43
36
|
return (
|
|
44
37
|
<nav
|
|
@@ -46,30 +39,25 @@ const Menu = ({
|
|
|
46
39
|
data-testid={testId}
|
|
47
40
|
{...props}
|
|
48
41
|
>
|
|
49
|
-
|
|
50
|
-
<TriggerComp aria-controls={menuId} />
|
|
51
|
-
<MenuContent
|
|
52
|
-
id={menuId}
|
|
53
|
-
title={title}
|
|
54
|
-
position={position}
|
|
55
|
-
>
|
|
56
|
-
{children}
|
|
57
|
-
</MenuContent>
|
|
58
|
-
</MenuProvider>
|
|
42
|
+
{children}
|
|
59
43
|
</nav>
|
|
60
44
|
);
|
|
61
45
|
};
|
|
62
46
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
47
|
+
interface MenuComponent extends NamedExoticComponent<MenuProps> {
|
|
48
|
+
Divider: typeof MenuDivider;
|
|
49
|
+
BaseItem: typeof MenuBaseItem;
|
|
50
|
+
PrimaryItem: typeof PrimaryMenuItem;
|
|
51
|
+
SecondaryItem: typeof SecondaryMenuItem;
|
|
52
|
+
ActionItem: typeof ActionMenuItem;
|
|
66
53
|
}
|
|
67
54
|
|
|
68
|
-
const MemoMenu = memo(Menu)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
55
|
+
const MemoMenu: MenuComponent = Object.assign(memo(Menu), {
|
|
56
|
+
Divider: MenuDivider,
|
|
57
|
+
BaseItem: MenuBaseItem,
|
|
58
|
+
PrimaryItem: PrimaryMenuItem,
|
|
59
|
+
SecondaryItem: SecondaryMenuItem,
|
|
60
|
+
ActionItem: ActionMenuItem,
|
|
61
|
+
});
|
|
74
62
|
|
|
75
|
-
export default
|
|
63
|
+
export default MemoMenu;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import { css, cx } from '@emotion/css';
|
|
3
|
+
import { useTheme } from '../../theme';
|
|
4
|
+
|
|
5
|
+
export interface MenuBaseItemProps extends HTMLAttributes<HTMLDivElement> {
|
|
6
|
+
icon?: ReactNode;
|
|
7
|
+
iconPosition?: 'left' | 'right';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const MenuBaseItem = ({
|
|
11
|
+
icon,
|
|
12
|
+
iconPosition = 'left',
|
|
13
|
+
className,
|
|
14
|
+
children,
|
|
15
|
+
...props
|
|
16
|
+
}: MenuBaseItemProps) => {
|
|
17
|
+
const [theme] = useTheme();
|
|
18
|
+
const baseStyle = css`
|
|
19
|
+
width: 360px;
|
|
20
|
+
box-sizing: border-box;
|
|
21
|
+
min-height: 48px;
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
gap: ${theme.margin.m32};
|
|
25
|
+
cursor: pointer;
|
|
26
|
+
|
|
27
|
+
&:hover .ucl-uikit-menu__item-text {
|
|
28
|
+
text-decoration: underline;
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
31
|
+
const style = cx(baseStyle, className);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div
|
|
35
|
+
className={style}
|
|
36
|
+
{...props}
|
|
37
|
+
>
|
|
38
|
+
{iconPosition === 'left' && icon}
|
|
39
|
+
{children}
|
|
40
|
+
{iconPosition === 'right' && icon}
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export default MenuBaseItem;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { HTMLAttributes } from 'react';
|
|
2
|
+
import { css, cx } from '@emotion/css';
|
|
3
|
+
import { useTheme } from '../../theme';
|
|
4
|
+
|
|
5
|
+
export const NAME = 'ucl-uikit-menu__divider';
|
|
6
|
+
|
|
7
|
+
export interface MenuDividerProps extends HTMLAttributes<HTMLHRElement> {}
|
|
8
|
+
|
|
9
|
+
const MenuDivider = ({ className, ...props }: MenuDividerProps) => {
|
|
10
|
+
const [theme] = useTheme();
|
|
11
|
+
const baseStyle = css`
|
|
12
|
+
border: 0;
|
|
13
|
+
border-top: 1px solid ${theme.colour.border.subtle};
|
|
14
|
+
`;
|
|
15
|
+
const style = cx(NAME, baseStyle, className);
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<hr
|
|
19
|
+
className={style}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default MenuDivider;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { css, cx } from '@emotion/css';
|
|
2
|
+
import {
|
|
3
|
+
Children,
|
|
4
|
+
ReactElement,
|
|
5
|
+
ReactNode,
|
|
6
|
+
cloneElement,
|
|
7
|
+
isValidElement,
|
|
8
|
+
} from 'react';
|
|
9
|
+
import Text from '../Text';
|
|
10
|
+
|
|
11
|
+
export const NAME = 'ucl-uikit-menu__item-text';
|
|
12
|
+
|
|
13
|
+
export interface MenuItemTextProps {
|
|
14
|
+
asChild?: boolean;
|
|
15
|
+
className?: string;
|
|
16
|
+
children?: ReactNode;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const MenuItemText = ({
|
|
20
|
+
asChild = false,
|
|
21
|
+
className,
|
|
22
|
+
children,
|
|
23
|
+
}: MenuItemTextProps) => {
|
|
24
|
+
const asChildResetStyle = css`
|
|
25
|
+
font: inherit;
|
|
26
|
+
color: inherit;
|
|
27
|
+
text-decoration: none;
|
|
28
|
+
|
|
29
|
+
&:hover,
|
|
30
|
+
&:visited,
|
|
31
|
+
&:active,
|
|
32
|
+
&:focus {
|
|
33
|
+
color: inherit;
|
|
34
|
+
text-decoration: inherit;
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
37
|
+
const style = cx(NAME, className);
|
|
38
|
+
|
|
39
|
+
if (asChild) {
|
|
40
|
+
const child = Children.only(children) as ReactElement<{
|
|
41
|
+
className?: string;
|
|
42
|
+
}>;
|
|
43
|
+
|
|
44
|
+
if (isValidElement(child)) {
|
|
45
|
+
return (
|
|
46
|
+
<Text className={style}>
|
|
47
|
+
{cloneElement(child, {
|
|
48
|
+
className: cx(asChildResetStyle, child.props.className),
|
|
49
|
+
})}
|
|
50
|
+
</Text>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return <Text className={style}>{children}</Text>;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default MenuItemText;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { css, cx } from '@emotion/css';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { MenuBaseItemProps } from './MenuBaseItem';
|
|
4
|
+
import MenuBaseItem from './MenuBaseItem';
|
|
5
|
+
import MenuItemText from './MenuItemText';
|
|
6
|
+
import { useTheme } from '../../theme';
|
|
7
|
+
|
|
8
|
+
export const NAME = 'ucl-uikit-menu__primary-item';
|
|
9
|
+
|
|
10
|
+
export interface PrimaryMenuItemProps extends Omit<
|
|
11
|
+
MenuBaseItemProps,
|
|
12
|
+
'iconPosition'
|
|
13
|
+
> {
|
|
14
|
+
asChild?: boolean;
|
|
15
|
+
badge?: ReactNode;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const PrimaryMenuItem = ({
|
|
19
|
+
className,
|
|
20
|
+
children,
|
|
21
|
+
asChild,
|
|
22
|
+
badge,
|
|
23
|
+
...props
|
|
24
|
+
}: PrimaryMenuItemProps) => {
|
|
25
|
+
const [theme] = useTheme();
|
|
26
|
+
|
|
27
|
+
const baseStyle = css`
|
|
28
|
+
color: ${theme.colour.text.default};
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
const style = cx(NAME, baseStyle, className);
|
|
32
|
+
|
|
33
|
+
const textStyle = css``;
|
|
34
|
+
|
|
35
|
+
const contentStyle = css`
|
|
36
|
+
display: inline-flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
gap: ${theme.margin.m16};
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<MenuBaseItem
|
|
43
|
+
className={style}
|
|
44
|
+
{...props}
|
|
45
|
+
iconPosition='left'
|
|
46
|
+
>
|
|
47
|
+
<span className={contentStyle}>
|
|
48
|
+
<MenuItemText
|
|
49
|
+
asChild={asChild}
|
|
50
|
+
className={textStyle}
|
|
51
|
+
>
|
|
52
|
+
{children}
|
|
53
|
+
</MenuItemText>
|
|
54
|
+
{badge}
|
|
55
|
+
</span>
|
|
56
|
+
</MenuBaseItem>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export default PrimaryMenuItem;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { css, cx } from '@emotion/css';
|
|
2
|
+
import { MenuBaseItemProps } from './MenuBaseItem';
|
|
3
|
+
import MenuBaseItem from './MenuBaseItem';
|
|
4
|
+
import MenuItemText from './MenuItemText';
|
|
5
|
+
import { useTheme } from '../../theme';
|
|
6
|
+
import Icon from '../Icon';
|
|
7
|
+
|
|
8
|
+
export const NAME = 'ucl-uikit-menu__secondary-item';
|
|
9
|
+
|
|
10
|
+
export interface SecondaryMenuItemProps extends Omit<
|
|
11
|
+
MenuBaseItemProps,
|
|
12
|
+
'iconPosition'
|
|
13
|
+
> {
|
|
14
|
+
asChild?: boolean;
|
|
15
|
+
externalLink?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const SecondaryMenuItem = ({
|
|
19
|
+
className,
|
|
20
|
+
children,
|
|
21
|
+
asChild,
|
|
22
|
+
externalLink,
|
|
23
|
+
...props
|
|
24
|
+
}: SecondaryMenuItemProps) => {
|
|
25
|
+
const [theme] = useTheme();
|
|
26
|
+
|
|
27
|
+
const baseStyle = css`
|
|
28
|
+
color: ${theme.colour.text.secondary};
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
const style = cx(NAME, baseStyle, className);
|
|
32
|
+
|
|
33
|
+
const textStyle = css`
|
|
34
|
+
color: ${theme.colour.text.secondary};
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
const contentStyle = css`
|
|
38
|
+
display: inline-flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
gap: ${theme.margin.m8};
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<MenuBaseItem
|
|
45
|
+
className={style}
|
|
46
|
+
{...props}
|
|
47
|
+
>
|
|
48
|
+
<span className={contentStyle}>
|
|
49
|
+
<MenuItemText
|
|
50
|
+
asChild={asChild}
|
|
51
|
+
className={textStyle}
|
|
52
|
+
>
|
|
53
|
+
{children}
|
|
54
|
+
</MenuItemText>
|
|
55
|
+
{externalLink && <Icon.ExternalLink size={16} />}
|
|
56
|
+
</span>
|
|
57
|
+
</MenuBaseItem>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export default SecondaryMenuItem;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { describe, expect, test, vi } from 'vitest';
|
|
2
|
+
import { fireEvent, render, screen } from '@testing-library/react';
|
|
3
|
+
import Menu from '../Menu';
|
|
4
|
+
import { ThemeContextProvider } from '../../../theme/useTheme';
|
|
5
|
+
|
|
6
|
+
const wrap = (ui: React.ReactElement) =>
|
|
7
|
+
render(<ThemeContextProvider>{ui}</ThemeContextProvider>);
|
|
8
|
+
|
|
9
|
+
describe('Menu', () => {
|
|
10
|
+
test('renders menu root with default test id', () => {
|
|
11
|
+
wrap(<Menu>Content</Menu>);
|
|
12
|
+
|
|
13
|
+
expect(screen.getByTestId('ucl-uikit-menu')).toBeInTheDocument();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('supports click-handler items without asChild link component', () => {
|
|
17
|
+
const onClick = vi.fn();
|
|
18
|
+
|
|
19
|
+
wrap(
|
|
20
|
+
<Menu>
|
|
21
|
+
<Menu.PrimaryItem onClick={onClick}>Home</Menu.PrimaryItem>
|
|
22
|
+
</Menu>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
fireEvent.click(screen.getByText('Home'));
|
|
26
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('renders asChild link content for primary item', () => {
|
|
30
|
+
wrap(
|
|
31
|
+
<Menu>
|
|
32
|
+
<Menu.PrimaryItem asChild>
|
|
33
|
+
<a href='/home'>Home</a>
|
|
34
|
+
</Menu.PrimaryItem>
|
|
35
|
+
</Menu>
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const link = screen.getByRole('link', { name: 'Home' });
|
|
39
|
+
expect(link).toBeInTheDocument();
|
|
40
|
+
expect(link.closest('.ucl-uikit-menu__item-text')).toBeInTheDocument();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('primary item enforces icon on the left', () => {
|
|
44
|
+
const { getByText } = wrap(
|
|
45
|
+
<Menu>
|
|
46
|
+
<Menu.PrimaryItem icon={<span data-testid='left-icon'>I</span>}>
|
|
47
|
+
Home
|
|
48
|
+
</Menu.PrimaryItem>
|
|
49
|
+
</Menu>
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const item = getByText('Home').closest('.ucl-uikit-menu__primary-item');
|
|
53
|
+
expect(item?.firstChild).toHaveAttribute('data-testid', 'left-icon');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('action item enforces icon on the right', () => {
|
|
57
|
+
const { getByText } = wrap(
|
|
58
|
+
<Menu>
|
|
59
|
+
<Menu.ActionItem icon={<span data-testid='right-icon'>I</span>}>
|
|
60
|
+
Sign out
|
|
61
|
+
</Menu.ActionItem>
|
|
62
|
+
</Menu>
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const item = getByText('Sign out').closest('.ucl-uikit-menu__action-item');
|
|
66
|
+
expect(item?.lastChild).toHaveAttribute('data-testid', 'right-icon');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('secondary externalLink item renders external icon', () => {
|
|
70
|
+
const { container } = wrap(
|
|
71
|
+
<Menu>
|
|
72
|
+
<Menu.SecondaryItem externalLink>
|
|
73
|
+
Accessibility statement
|
|
74
|
+
</Menu.SecondaryItem>
|
|
75
|
+
</Menu>
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const externalItem = screen
|
|
79
|
+
.getByText('Accessibility statement')
|
|
80
|
+
.closest('.ucl-uikit-menu__secondary-item');
|
|
81
|
+
|
|
82
|
+
expect(externalItem).toBeInTheDocument();
|
|
83
|
+
expect(
|
|
84
|
+
container.querySelector('.ucl-uikit-menu__secondary-item svg')
|
|
85
|
+
).toBeTruthy();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export { default } from './Menu';
|
|
2
|
-
export type {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
export type {
|
|
3
|
+
MenuProps,
|
|
4
|
+
MenuDividerProps,
|
|
5
|
+
MenuBaseItemProps,
|
|
6
|
+
PrimaryMenuItemProps,
|
|
7
|
+
SecondaryMenuItemProps,
|
|
8
|
+
ActionMenuItemProps,
|
|
9
|
+
} from './Menu';
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { HTMLAttributes, Ref, memo } from 'react';
|
|
2
|
+
import { css, cx } from '@emotion/css';
|
|
3
|
+
import useTheme from '../../theme/useTheme';
|
|
4
|
+
|
|
5
|
+
export const NAME = 'ucl-uikit-text';
|
|
6
|
+
|
|
7
|
+
export type TextLevel =
|
|
8
|
+
| 'lg'
|
|
9
|
+
| 'lg-medium'
|
|
10
|
+
| 'lg-semibold'
|
|
11
|
+
| 'lg-bold'
|
|
12
|
+
| 'md'
|
|
13
|
+
| 'md-medium'
|
|
14
|
+
| 'md-semibold'
|
|
15
|
+
| 'md-medium-numeric'
|
|
16
|
+
| 'sm'
|
|
17
|
+
| 'sm-medium'
|
|
18
|
+
| 'sm-semibold'
|
|
19
|
+
| 'xs'
|
|
20
|
+
| 'xs-medium';
|
|
21
|
+
|
|
22
|
+
export interface TextProps extends HTMLAttributes<HTMLSpanElement> {
|
|
23
|
+
level?: TextLevel;
|
|
24
|
+
testId?: string;
|
|
25
|
+
ref?: Ref<HTMLSpanElement>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const Text = ({
|
|
29
|
+
level = 'md',
|
|
30
|
+
testId = NAME,
|
|
31
|
+
className,
|
|
32
|
+
ref,
|
|
33
|
+
...props
|
|
34
|
+
}: TextProps) => {
|
|
35
|
+
const [theme] = useTheme();
|
|
36
|
+
|
|
37
|
+
const {
|
|
38
|
+
typography: {
|
|
39
|
+
body: {
|
|
40
|
+
lg,
|
|
41
|
+
lgMedium,
|
|
42
|
+
lgSemibold,
|
|
43
|
+
lgBold,
|
|
44
|
+
md,
|
|
45
|
+
mdMedium,
|
|
46
|
+
mdSemibold,
|
|
47
|
+
mdMediumNumeric,
|
|
48
|
+
sm,
|
|
49
|
+
smMedium,
|
|
50
|
+
smSemibold,
|
|
51
|
+
xs,
|
|
52
|
+
xsMedium,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
} = theme;
|
|
56
|
+
|
|
57
|
+
const baseStyle = css`
|
|
58
|
+
font-family: ${md.fontFamily};
|
|
59
|
+
font-feature-settings: ${md.fontSettings};
|
|
60
|
+
color: ${theme.colour.text.default};
|
|
61
|
+
margin: 0;
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
const lgStyle = css`
|
|
65
|
+
font-size: ${lg.fontSize}px;
|
|
66
|
+
font-weight: ${lg.fontWeight};
|
|
67
|
+
line-height: ${lg.lineHeight}%;
|
|
68
|
+
`;
|
|
69
|
+
|
|
70
|
+
const lgMediumStyle = css`
|
|
71
|
+
font-size: ${lgMedium.fontSize}px;
|
|
72
|
+
font-weight: ${lgMedium.fontWeight};
|
|
73
|
+
line-height: ${lgMedium.lineHeight}%;
|
|
74
|
+
`;
|
|
75
|
+
|
|
76
|
+
const lgSemiboldStyle = css`
|
|
77
|
+
font-size: ${lgSemibold.fontSize}px;
|
|
78
|
+
font-weight: ${lgSemibold.fontWeight};
|
|
79
|
+
line-height: ${lgSemibold.lineHeight}%;
|
|
80
|
+
`;
|
|
81
|
+
|
|
82
|
+
const lgBoldStyle = css`
|
|
83
|
+
font-size: ${lgBold.fontSize}px;
|
|
84
|
+
font-weight: ${lgBold.fontWeight};
|
|
85
|
+
line-height: ${lgBold.lineHeight}%;
|
|
86
|
+
`;
|
|
87
|
+
|
|
88
|
+
const mdStyle = css`
|
|
89
|
+
font-size: ${md.fontSize}px;
|
|
90
|
+
font-weight: ${md.fontWeight};
|
|
91
|
+
line-height: ${md.lineHeight}%;
|
|
92
|
+
`;
|
|
93
|
+
|
|
94
|
+
const mdMediumStyle = css`
|
|
95
|
+
font-size: ${mdMedium.fontSize}px;
|
|
96
|
+
font-weight: ${mdMedium.fontWeight};
|
|
97
|
+
line-height: ${mdMedium.lineHeight}%;
|
|
98
|
+
`;
|
|
99
|
+
|
|
100
|
+
const mdSemiboldStyle = css`
|
|
101
|
+
font-size: ${mdSemibold.fontSize}px;
|
|
102
|
+
font-weight: ${mdSemibold.fontWeight};
|
|
103
|
+
line-height: ${mdSemibold.lineHeight}%;
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
const mdMediumNumericStyle = css`
|
|
107
|
+
font-size: ${mdMediumNumeric.fontSize}px;
|
|
108
|
+
font-weight: ${mdMediumNumeric.fontWeight};
|
|
109
|
+
line-height: ${mdMediumNumeric.lineHeight}%;
|
|
110
|
+
`;
|
|
111
|
+
|
|
112
|
+
const smStyle = css`
|
|
113
|
+
font-size: ${sm.fontSize}px;
|
|
114
|
+
font-weight: ${sm.fontWeight};
|
|
115
|
+
line-height: ${sm.lineHeight}%;
|
|
116
|
+
`;
|
|
117
|
+
|
|
118
|
+
const smMediumStyle = css`
|
|
119
|
+
font-size: ${smMedium.fontSize}px;
|
|
120
|
+
font-weight: ${smMedium.fontWeight};
|
|
121
|
+
line-height: ${smMedium.lineHeight}%;
|
|
122
|
+
`;
|
|
123
|
+
|
|
124
|
+
const smSemiboldStyle = css`
|
|
125
|
+
font-size: ${smSemibold.fontSize}px;
|
|
126
|
+
font-weight: ${smSemibold.fontWeight};
|
|
127
|
+
line-height: ${smSemibold.lineHeight}%;
|
|
128
|
+
`;
|
|
129
|
+
|
|
130
|
+
const xsStyle = css`
|
|
131
|
+
font-size: ${xs.fontSize}px;
|
|
132
|
+
font-weight: ${xs.fontWeight};
|
|
133
|
+
line-height: ${xs.lineHeight}%;
|
|
134
|
+
`;
|
|
135
|
+
|
|
136
|
+
const xsMediumStyle = css`
|
|
137
|
+
font-size: ${xsMedium.fontSize}px;
|
|
138
|
+
font-weight: ${xsMedium.fontWeight};
|
|
139
|
+
line-height: ${xsMedium.lineHeight}%;
|
|
140
|
+
`;
|
|
141
|
+
|
|
142
|
+
const style = cx(
|
|
143
|
+
NAME,
|
|
144
|
+
baseStyle,
|
|
145
|
+
level === 'lg' && lgStyle,
|
|
146
|
+
level === 'lg-medium' && lgMediumStyle,
|
|
147
|
+
level === 'lg-semibold' && lgSemiboldStyle,
|
|
148
|
+
level === 'lg-bold' && lgBoldStyle,
|
|
149
|
+
level === 'md' && mdStyle,
|
|
150
|
+
level === 'md-medium' && mdMediumStyle,
|
|
151
|
+
level === 'md-semibold' && mdSemiboldStyle,
|
|
152
|
+
level === 'md-medium-numeric' && mdMediumNumericStyle,
|
|
153
|
+
level === 'sm' && smStyle,
|
|
154
|
+
level === 'sm-medium' && smMediumStyle,
|
|
155
|
+
level === 'sm-semibold' && smSemiboldStyle,
|
|
156
|
+
level === 'xs' && xsStyle,
|
|
157
|
+
level === 'xs-medium' && xsMediumStyle,
|
|
158
|
+
className
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<span
|
|
163
|
+
ref={ref}
|
|
164
|
+
className={style}
|
|
165
|
+
data-testid={testId}
|
|
166
|
+
{...props}
|
|
167
|
+
/>
|
|
168
|
+
);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
export default memo(Text);
|
package/lib/components/index.ts
CHANGED
|
@@ -52,10 +52,8 @@ export type { ParagraphProps } from './Paragraph';
|
|
|
52
52
|
export { default as ParagraphNew } from './Paragraph';
|
|
53
53
|
export type { ParagraphProps as ParagraphNewProps } from './Paragraph';
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
export { default as Text } from './Paragraph';
|
|
58
|
-
export type { TextProps } from './Paragraph';
|
|
55
|
+
export { default as Text } from './Text';
|
|
56
|
+
export type { TextProps, TextLevel } from './Text';
|
|
59
57
|
|
|
60
58
|
export { default as Modal } from './Modal';
|
|
61
59
|
export type { ModalProps } from './Modal';
|
|
@@ -93,9 +91,6 @@ export type { MenuProps } from './Menu';
|
|
|
93
91
|
export { default as MenuNew } from './MenuNew';
|
|
94
92
|
export type { MenuProps as MenuNewProps } from './MenuNew';
|
|
95
93
|
|
|
96
|
-
export { default as SimpleMenu } from './SimpleMenu';
|
|
97
|
-
export type { SimpleMenuProps } from './SimpleMenu';
|
|
98
|
-
|
|
99
94
|
export { default as Select } from './Select';
|
|
100
95
|
export type { SelectProps } from './Select';
|
|
101
96
|
|
package/package.json
CHANGED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { default as React } from 'react';
|
|
2
|
-
interface IContext {
|
|
3
|
-
isOpen: boolean;
|
|
4
|
-
toggleMenu: () => void;
|
|
5
|
-
triggerRef: React.RefObject<HTMLButtonElement | null>;
|
|
6
|
-
contentRef: React.RefObject<HTMLDivElement | null>;
|
|
7
|
-
}
|
|
8
|
-
export declare const AppMenuContext: React.Context<IContext>;
|
|
9
|
-
export declare const useAppMenu: () => IContext;
|
|
10
|
-
declare const MenuProvider: ({ defaultOpen, children, }: {
|
|
11
|
-
defaultOpen?: boolean;
|
|
12
|
-
children: React.ReactNode;
|
|
13
|
-
}) => import("react/jsx-runtime").JSX.Element;
|
|
14
|
-
export default MenuProvider;
|