mirror of
https://github.com/strapi/strapi.git
synced 2025-11-02 10:55:37 +00:00
feat(main-nav): Main Nav refactoring, add the Home icon (#20176)
* feat(main-nav): replace DS NavLink with admin NavLink * feat(main-nav): change icon type * feat(main-nav): fix prettier errors * feat(main-nav): refactor navlink code and add more test cases * feat(main-nav): minor fixes * feat(main-nav): fix ui errors * feat(main-nav): fix merge issues * feat(main-nav): fix unit test and types * feat(main-nav): change offset values
This commit is contained in:
parent
4300e8f16c
commit
53cf1f19cc
@ -15,7 +15,7 @@ import {
|
|||||||
NavSections,
|
NavSections,
|
||||||
NavUser,
|
NavUser,
|
||||||
} from '@strapi/design-system';
|
} from '@strapi/design-system';
|
||||||
import { SignOut, Feather, Lock } from '@strapi/icons';
|
import { SignOut, Feather, Lock, House } from '@strapi/icons';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { NavLink as RouterNavLink, useLocation } from 'react-router-dom';
|
import { NavLink as RouterNavLink, useLocation } from 'react-router-dom';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
@ -28,6 +28,7 @@ import { usePersistentState } from '../hooks/usePersistentState';
|
|||||||
import { getDisplayName } from '../utils/users';
|
import { getDisplayName } from '../utils/users';
|
||||||
|
|
||||||
import { NavBrand as NewNavBrand } from './MainNav/NavBrand';
|
import { NavBrand as NewNavBrand } from './MainNav/NavBrand';
|
||||||
|
import { NavLink as NewNavLink } from './MainNav/NavLink';
|
||||||
|
|
||||||
const LinkUserWrapper = styled(Box)`
|
const LinkUserWrapper = styled(Box)`
|
||||||
width: 15rem;
|
width: 15rem;
|
||||||
@ -137,6 +138,17 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }: LeftMenuProps) =
|
|||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<NavSections>
|
<NavSections>
|
||||||
|
{condensed && (
|
||||||
|
<NewNavLink.Link to="/" onClick={() => handleClickOnLink('/')}>
|
||||||
|
<NewNavLink.Tooltip
|
||||||
|
label={formatMessage({ id: 'global.home', defaultMessage: 'Home' })}
|
||||||
|
>
|
||||||
|
<NewNavLink.Icon>
|
||||||
|
<House fill="neutral500" />
|
||||||
|
</NewNavLink.Icon>
|
||||||
|
</NewNavLink.Tooltip>
|
||||||
|
</NewNavLink.Link>
|
||||||
|
)}
|
||||||
<NavLink
|
<NavLink
|
||||||
as={RouterNavLink}
|
as={RouterNavLink}
|
||||||
// @ts-expect-error the props from the passed as prop are not inferred // joined together
|
// @ts-expect-error the props from the passed as prop are not inferred // joined together
|
||||||
|
|||||||
@ -9,9 +9,9 @@ const BrandIconWrapper = styled(Box)`
|
|||||||
img {
|
img {
|
||||||
border-radius: ${({ theme }) => theme.borderRadius};
|
border-radius: ${({ theme }) => theme.borderRadius};
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
height: ${24 / 16}rem;
|
height: 2.4rem;
|
||||||
width: ${24 / 16}rem;
|
width: 2.4rem;
|
||||||
margin: ${3 / 16}rem;
|
margin: 0.4rem;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ export const NavBrand = () => {
|
|||||||
} = useConfiguration('LeftMenu');
|
} = useConfiguration('LeftMenu');
|
||||||
return (
|
return (
|
||||||
<Box padding={3}>
|
<Box padding={3}>
|
||||||
<BrandIconWrapper>
|
<BrandIconWrapper width="3.2rem" height="3.2rem">
|
||||||
<img
|
<img
|
||||||
src={menu.custom?.url || menu.default}
|
src={menu.custom?.url || menu.default}
|
||||||
alt={formatMessage({
|
alt={formatMessage({
|
||||||
|
|||||||
115
packages/core/admin/admin/src/components/MainNav/NavLink.tsx
Normal file
115
packages/core/admin/admin/src/components/MainNav/NavLink.tsx
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { Tooltip, Flex, Badge } from '@strapi/design-system';
|
||||||
|
import { NavLink as RouterLink, LinkProps } from 'react-router-dom';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------------------------------
|
||||||
|
* Link
|
||||||
|
* -----------------------------------------------------------------------------------------------*/
|
||||||
|
const MainNavLinkWrapper = styled(RouterLink)`
|
||||||
|
text-decoration: none;
|
||||||
|
display: block;
|
||||||
|
border-radius: ${({ theme }) => theme.borderRadius};
|
||||||
|
background: ${({ theme }) => theme.colors.neutral0};
|
||||||
|
color: ${({ theme }) => theme.colors.neutral600};
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&.active {
|
||||||
|
background: ${({ theme }) => theme.colors.neutral100};
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
svg path {
|
||||||
|
fill: ${({ theme }) => theme.colors.neutral600};
|
||||||
|
}
|
||||||
|
color: ${({ theme }) => theme.colors.neutral700};
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
svg path {
|
||||||
|
fill: ${({ theme }) => theme.colors.primary600};
|
||||||
|
}
|
||||||
|
|
||||||
|
color: ${({ theme }) => theme.colors.primary600};
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const LinkImpl = ({ children, ...props }: LinkProps) => {
|
||||||
|
return <MainNavLinkWrapper {...props}>{children}</MainNavLinkWrapper>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------------------------------
|
||||||
|
* Tooltip
|
||||||
|
* -----------------------------------------------------------------------------------------------*/
|
||||||
|
const TooltipImpl = ({ children, label, position = 'right' }: NavLink.TooltipProps) => {
|
||||||
|
return (
|
||||||
|
<Tooltip position={position} label={label}>
|
||||||
|
<Flex justifyContent="center" width={7} height={7}>
|
||||||
|
{children}
|
||||||
|
</Flex>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------------------------------
|
||||||
|
* Icon
|
||||||
|
* -----------------------------------------------------------------------------------------------*/
|
||||||
|
const IconImpl = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
if (!children) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Flex justifyContent="center" aria-hidden as="span" width={5} height={5}>
|
||||||
|
{children}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------------------------------
|
||||||
|
* Badge
|
||||||
|
* -----------------------------------------------------------------------------------------------*/
|
||||||
|
const CustomBadge = styled(Badge)`
|
||||||
|
/* override default badge styles to change the border radius of the Base element in the Design System */
|
||||||
|
border-radius: ${({ theme }) => theme.spaces[10]};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const BadgeImpl = ({ children, label, ...props }: NavLink.BadgeProps) => {
|
||||||
|
if (!children) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<CustomBadge position="absolute" top="-1.2rem" right="-0.4rem" aria-label={label} {...props}>
|
||||||
|
{children}
|
||||||
|
</CustomBadge>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------------------------------
|
||||||
|
* EXPORTS
|
||||||
|
* -----------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
const NavLink = {
|
||||||
|
Link: LinkImpl,
|
||||||
|
Tooltip: TooltipImpl,
|
||||||
|
Icon: IconImpl,
|
||||||
|
Badge: BadgeImpl,
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
namespace NavLink {
|
||||||
|
export interface BadgeProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TooltipProps {
|
||||||
|
position?: 'top' | 'bottom' | 'left' | 'right';
|
||||||
|
label?: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { NavLink };
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
import { House, Lock } from '@strapi/icons';
|
||||||
|
import { screen, render as renderRTL } from '@tests/utils';
|
||||||
|
|
||||||
|
import { NavLink } from '../NavLink';
|
||||||
|
|
||||||
|
describe('NavLink', () => {
|
||||||
|
const Component = () => (
|
||||||
|
<NavLink.Link to="/test-link">
|
||||||
|
<NavLink.Tooltip label="test-tooltip">
|
||||||
|
<>
|
||||||
|
<NavLink.Icon>
|
||||||
|
<House data-testid="nav-link-icon" />
|
||||||
|
</NavLink.Icon>
|
||||||
|
<NavLink.Badge label="badge label">
|
||||||
|
<Lock data-testid="nav-link-badge" />
|
||||||
|
</NavLink.Badge>
|
||||||
|
</>
|
||||||
|
</NavLink.Tooltip>
|
||||||
|
</NavLink.Link>
|
||||||
|
);
|
||||||
|
|
||||||
|
const render = () => renderRTL(<Component />);
|
||||||
|
|
||||||
|
it('shows the NavLink with link to destination', async () => {
|
||||||
|
render();
|
||||||
|
const link = screen.getByRole('link');
|
||||||
|
expect(link).toBeInTheDocument();
|
||||||
|
expect(link).toHaveAttribute('href', '/test-link');
|
||||||
|
});
|
||||||
|
it('shows the home icon in the link', async () => {
|
||||||
|
render();
|
||||||
|
expect(screen.getByTestId('nav-link-icon')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
it('shows the badge next to the link', async () => {
|
||||||
|
render();
|
||||||
|
expect(screen.getByTestId('nav-link-badge')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -685,6 +685,7 @@
|
|||||||
"global.change-password": "Change password",
|
"global.change-password": "Change password",
|
||||||
"global.close": "Close",
|
"global.close": "Close",
|
||||||
"global.content-manager": "Content Manager",
|
"global.content-manager": "Content Manager",
|
||||||
|
"global.home": "Home",
|
||||||
"global.continue": "Continue",
|
"global.continue": "Continue",
|
||||||
"global.delete": "Delete",
|
"global.delete": "Delete",
|
||||||
"global.delete-target": "Delete {target}",
|
"global.delete-target": "Delete {target}",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user