Skip to content

Commit

Permalink
feat(main-nav): Main Nav refactoring, add the Home icon (#20176)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
simotae14 committed Apr 29, 2024
1 parent 4300e8f commit 53cf1f1
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 5 deletions.
14 changes: 13 additions & 1 deletion packages/core/admin/admin/src/components/LeftMenu.tsx
Expand Up @@ -15,7 +15,7 @@ import {
NavSections,
NavUser,
} 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 { NavLink as RouterNavLink, useLocation } from 'react-router-dom';
import styled from 'styled-components';
Expand All @@ -28,6 +28,7 @@ import { usePersistentState } from '../hooks/usePersistentState';
import { getDisplayName } from '../utils/users';

import { NavBrand as NewNavBrand } from './MainNav/NavBrand';
import { NavLink as NewNavLink } from './MainNav/NavLink';

const LinkUserWrapper = styled(Box)`
width: 15rem;
Expand Down Expand Up @@ -137,6 +138,17 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }: LeftMenuProps) =
<Divider />

<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
as={RouterNavLink}
// @ts-expect-error the props from the passed as prop are not inferred // joined together
Expand Down
8 changes: 4 additions & 4 deletions packages/core/admin/admin/src/components/MainNav/NavBrand.tsx
Expand Up @@ -9,9 +9,9 @@ const BrandIconWrapper = styled(Box)`
img {
border-radius: ${({ theme }) => theme.borderRadius};
object-fit: contain;
height: ${24 / 16}rem;
width: ${24 / 16}rem;
margin: ${3 / 16}rem;
height: 2.4rem;
width: 2.4rem;
margin: 0.4rem;
}
`;

Expand All @@ -22,7 +22,7 @@ export const NavBrand = () => {
} = useConfiguration('LeftMenu');
return (
<Box padding={3}>
<BrandIconWrapper>
<BrandIconWrapper width="3.2rem" height="3.2rem">
<img
src={menu.custom?.url || menu.default}
alt={formatMessage({
Expand Down
115 changes: 115 additions & 0 deletions packages/core/admin/admin/src/components/MainNav/NavLink.tsx
@@ -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();
});
});
1 change: 1 addition & 0 deletions packages/core/admin/admin/src/translations/en.json
Expand Up @@ -685,6 +685,7 @@
"global.change-password": "Change password",
"global.close": "Close",
"global.content-manager": "Content Manager",
"global.home": "Home",
"global.continue": "Continue",
"global.delete": "Delete",
"global.delete-target": "Delete {target}",
Expand Down

0 comments on commit 53cf1f1

Please sign in to comment.