From c734c14d5f69a57f54c3edc10aaecf5a702940af Mon Sep 17 00:00:00 2001 From: Simone Date: Mon, 13 May 2024 08:58:09 +0200 Subject: [PATCH] feat(main-nav): Main nav refactoring, change links order (#20275) * feat(main-nav): change links order in main nav * feat(main-nav): add the position property to order links in the main nav * feat(main-nav): refactor the sorting of the nav links * feat(main-nav): add useCollator and format message to sort by name the links --- packages/core/admin/admin/src/StrapiApp.tsx | 1 + .../admin/admin/src/components/LeftMenu.tsx | 145 +++++++----------- .../admin/src/components/MainNav/NavLink.tsx | 1 - .../core/admin/admin/src/hooks/useMenu.ts | 17 +- .../core/content-manager/admin/src/index.ts | 1 + .../core/content-releases/admin/src/index.ts | 2 + .../content-type-builder/admin/src/index.ts | 1 + packages/core/upload/admin/src/index.js | 1 + .../plugins/documentation/admin/src/index.ts | 1 + 9 files changed, 77 insertions(+), 93 deletions(-) diff --git a/packages/core/admin/admin/src/StrapiApp.tsx b/packages/core/admin/admin/src/StrapiApp.tsx index b98dfd1a324..dbe59409164 100644 --- a/packages/core/admin/admin/src/StrapiApp.tsx +++ b/packages/core/admin/admin/src/StrapiApp.tsx @@ -64,6 +64,7 @@ interface MenuItem { Component: React.LazyExoticComponent; exact?: boolean; lockIcon?: boolean; + position?: number; } interface StrapiAppSettingLink extends Omit { diff --git a/packages/core/admin/admin/src/components/LeftMenu.tsx b/packages/core/admin/admin/src/components/LeftMenu.tsx index 50f4f365fbc..afcf19a96df 100644 --- a/packages/core/admin/admin/src/components/LeftMenu.tsx +++ b/packages/core/admin/admin/src/components/LeftMenu.tsx @@ -1,14 +1,14 @@ import * as React from 'react'; -import { Divider, Flex, FlexComponent } from '@strapi/design-system'; -import { Feather, Lock, House } from '@strapi/icons'; +import { Divider, Flex, FlexComponent, useCollator } from '@strapi/design-system'; +import { Lock } from '@strapi/icons'; import { useIntl } from 'react-intl'; import { useLocation } from 'react-router-dom'; import { styled } from 'styled-components'; import { useAuth } from '../features/Auth'; import { useTracking } from '../features/Tracking'; -import { Menu } from '../hooks/useMenu'; +import { Menu, MenuItem } from '../hooks/useMenu'; import { getDisplayName } from '../utils/users'; import { MainNav } from './MainNav/MainNav'; @@ -16,12 +16,30 @@ import { NavBrand } from './MainNav/NavBrand'; import { NavLink } from './MainNav/NavLink'; import { NavUser } from './MainNav/NavUser'; -const NewNavLinkBadge = styled(NavLink.Badge)` +const sortLinks = (links: MenuItem[]) => { + return links.sort((a, b) => { + // if no position is defined, we put the link in the position of the external plugins, before the plugins list + const positionA = a.position ?? 6; + const positionB = b.position ?? 6; + + if (positionA < positionB) { + return -1; + } else { + return 1; + } + }); +}; + +const NavLinkBadgeCounter = styled(NavLink.Badge)` span { color: ${({ theme }) => theme.colors.neutral0}; } `; +const NavLinkBadgeLock = styled(NavLink.Badge)` + background-color: transparent; +`; + const NavListWrapper = styled>(Flex)` overflow-y: auto; `; @@ -30,10 +48,13 @@ interface LeftMenuProps extends Pick { const user = useAuth('AuthenticatedApp', (state) => state.user); - const { formatMessage } = useIntl(); const { trackUsage } = useTracking(); const { pathname } = useLocation(); const userDisplayName = getDisplayName(user); + const { formatMessage, locale } = useIntl(); + const formatter = useCollator(locale, { + sensitivity: 'base', + }); const initials = userDisplayName .split(' ') @@ -45,6 +66,11 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }: LeftMenuProps) = trackUsage('willNavigate', { from: pathname, to: destination }); }; + const listLinksAlphabeticallySorted = [...pluginsSectionLinks, ...generalSectionLinks].sort( + (a, b) => formatter.compare(formatMessage(a.intlLabel), formatMessage(b.intlLabel)) + ); + const listLinks = sortLinks(listLinksAlphabeticallySorted); + return ( @@ -52,108 +78,47 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }: LeftMenuProps) = - - - handleClickOnLink('/')} - aria-label={formatMessage({ id: 'global.home', defaultMessage: 'Home' })} - > - - - - - - - - - handleClickOnLink('/content-manager')} - aria-label={formatMessage({ - id: 'global.content-manager', - defaultMessage: 'Content Manager', - })} - > - - - - - - - {pluginsSectionLinks.length > 0 - ? pluginsSectionLinks.map((link) => { - if (link.to === 'content-manager') { - return null; - } - + {listLinks.length > 0 + ? listLinks.map((link) => { const LinkIcon = link.icon; - const badgeContent = link?.lockIcon ? : undefined; + const badgeContentLock = link?.lockIcon ? : undefined; - const labelValue = formatMessage(link.intlLabel); - return ( - - - handleClickOnLink(link.to)} - aria-label={labelValue} - > - - - - {badgeContent && ( - - {badgeContent} - - )} - - - - ); - }) - : null} - {generalSectionLinks.length > 0 - ? generalSectionLinks.map((link) => { - const LinkIcon = link.icon; - - const badgeContent = + const badgeContentNumeric = link.notificationsCount && link.notificationsCount > 0 ? link.notificationsCount.toString() : undefined; const labelValue = formatMessage(link.intlLabel); - return ( handleClickOnLink(link.to)} + aria-label={labelValue} > - {badgeContent && ( - - {badgeContent} - - )} + {badgeContentLock ? ( + + {badgeContentLock} + + ) : badgeContentNumeric ? ( + + {badgeContentNumeric} + + ) : null} diff --git a/packages/core/admin/admin/src/components/MainNav/NavLink.tsx b/packages/core/admin/admin/src/components/MainNav/NavLink.tsx index 0f7bcdd7266..52d68674a4d 100644 --- a/packages/core/admin/admin/src/components/MainNav/NavLink.tsx +++ b/packages/core/admin/admin/src/components/MainNav/NavLink.tsx @@ -81,7 +81,6 @@ const BadgeImpl = ({ children, label, ...props }: NavLink.NavBadgeProps) => { return ( ; +export type MenuItem = Omit; export interface Menu { generalSectionLinks: MenuItem[]; @@ -26,6 +26,16 @@ const useMenu = (shouldUpdateStrapi: boolean) => { const permissions = useSelector(selectAdminPermissions); const [menuWithUserPermissions, setMenuWithUserPermissions] = React.useState({ generalSectionLinks: [ + { + icon: House, + intlLabel: { + id: 'global.home', + defaultMessage: 'Home', + }, + to: '/', + permissions: [], + position: 0, + }, { icon: PuzzlePiece, intlLabel: { @@ -34,6 +44,7 @@ const useMenu = (shouldUpdateStrapi: boolean) => { }, to: '/list-plugins', permissions: permissions.marketplace?.main ?? [], + position: 7, }, { icon: ShoppingCart, @@ -43,6 +54,7 @@ const useMenu = (shouldUpdateStrapi: boolean) => { }, to: '/marketplace', permissions: permissions.marketplace?.main ?? [], + position: 8, }, { icon: Cog, @@ -55,6 +67,7 @@ const useMenu = (shouldUpdateStrapi: boolean) => { // using the settings menu permissions: [], notificationsCount: 0, + position: 10, }, ], pluginsSectionLinks: [], diff --git a/packages/core/content-manager/admin/src/index.ts b/packages/core/content-manager/admin/src/index.ts index 7c768b2735b..766150b1f41 100644 --- a/packages/core/content-manager/admin/src/index.ts +++ b/packages/core/content-manager/admin/src/index.ts @@ -27,6 +27,7 @@ export default { }, permissions: [], Component: () => import('./layout').then((mod) => ({ default: mod.Layout })), + position: 1, }); app.registerPlugin(cm.config); diff --git a/packages/core/content-releases/admin/src/index.ts b/packages/core/content-releases/admin/src/index.ts index 89d71e11062..a7c9b0edefb 100644 --- a/packages/core/content-releases/admin/src/index.ts +++ b/packages/core/content-releases/admin/src/index.ts @@ -32,6 +32,7 @@ const admin: Plugin.Config.AdminInput = { }, Component: () => import('./pages/App').then((mod) => ({ default: mod.App })), permissions: PERMISSIONS.main, + position: 2, }); /** @@ -77,6 +78,7 @@ const admin: Plugin.Config.AdminInput = { return { default: PurchaseContentReleases }; }, lockIcon: true, + position: 2, }); } }, diff --git a/packages/core/content-type-builder/admin/src/index.ts b/packages/core/content-type-builder/admin/src/index.ts index ced788bb396..386f470cd75 100644 --- a/packages/core/content-type-builder/admin/src/index.ts +++ b/packages/core/content-type-builder/admin/src/index.ts @@ -23,6 +23,7 @@ export default { }, permissions: PERMISSIONS.main, Component: () => import('./pages/App'), + position: 5, }); app.registerPlugin({ diff --git a/packages/core/upload/admin/src/index.js b/packages/core/upload/admin/src/index.js index 76853bac5f3..398fe5678c3 100644 --- a/packages/core/upload/admin/src/index.js +++ b/packages/core/upload/admin/src/index.js @@ -22,6 +22,7 @@ export default { }, permissions: PERMISSIONS.main, Component: () => import('./pages/App'), + position: 4, }); app.addSettingsLink('global', { diff --git a/packages/plugins/documentation/admin/src/index.ts b/packages/plugins/documentation/admin/src/index.ts index 7a5c9891413..2f90457dc78 100644 --- a/packages/plugins/documentation/admin/src/index.ts +++ b/packages/plugins/documentation/admin/src/index.ts @@ -20,6 +20,7 @@ export default { const { App } = await import('./pages/App'); return App; }, + position: 9, }); app.addMiddlewares([() => api.middleware]);