Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(main-nav): Main Nav refactoring, add the Home icon #20176

Merged
merged 13 commits into from Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
simotae14 marked this conversation as resolved.
Show resolved Hide resolved
@@ -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 (
simotae14 marked this conversation as resolved.
Show resolved Hide resolved
<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', () => {
simotae14 marked this conversation as resolved.
Show resolved Hide resolved
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