Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions dashboard/src/components/SideMenu/MobileSideMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type { JSX } from 'react';

import useIntl from 'react-intl/src/components/useIntl';

import {
Drawer,
DrawerContent,
DrawerDescription,
DrawerHeader,
DrawerTitle,
} from '@/components/ui/drawer';

import SideMenuContent from './SideMenuContent';

type MobileSideMenuProps = {
isOpen: boolean;
onClose: () => void;
};

const MobileSideMenu = ({
isOpen,
onClose,
}: MobileSideMenuProps): JSX.Element => {
const { formatMessage } = useIntl();

return (
<Drawer
direction="left"
open={isOpen}
onOpenChange={open => !open && onClose()}
>
{/* Hides the first drawer child which is the drawer handle */}
<DrawerContent className="bg-bg-secondary fixed inset-y-0 right-auto left-0 mt-0 h-full w-auto overflow-y-auto border-none pb-12 [&>div:first-child]:hidden">
<DrawerHeader className="sr-only">
<DrawerTitle>{formatMessage({ id: 'sidemenu.title' })}</DrawerTitle>
<DrawerDescription>
{formatMessage({ id: 'sidemenu.description' })}
</DrawerDescription>
</DrawerHeader>
<SideMenuContent onLinkClick={onClose} />
</DrawerContent>
Comment on lines 33 to 41
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should add a description here or the application will be warning about it missing:

Warning: Missing Description or aria-describedby={undefined} for {DialogContent}.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Thanks for noticing that

</Drawer>
);
};

export default MobileSideMenu;
3 changes: 3 additions & 0 deletions dashboard/src/components/SideMenu/NavLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type INavLinkBase = LinkProps & {
asTag?: string;
selected?: boolean;
linkClassName?: string;
onClickElement?: () => void;
};

type INavLinkWithIntl = INavLinkBase & {
Expand All @@ -38,6 +39,7 @@ const NavLink = ({
label,
asTag,
linkClassName,
onClickElement,
...props
}: INavLink): JSX.Element => {
const LinkElement = asTag ?? Link;
Expand All @@ -58,6 +60,7 @@ const NavLink = ({
selected ? selectedItemClassName : notSelectedItemClassName,
linkClassName,
)}
onClick={onClickElement}
{...props}
>
{icon && <span className="mr-3">{icon}</span>}
Expand Down
194 changes: 5 additions & 189 deletions dashboard/src/components/SideMenu/SideMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,196 +1,12 @@
import { useMemo, type JSX } from 'react';
import type { JSX } from 'react';

import { MdOutlineMonitorHeart } from 'react-icons/md';

import { RxRadiobutton } from 'react-icons/rx';

import { ImTree } from 'react-icons/im';
import { HiOutlineDocumentSearch } from 'react-icons/hi';

import { useLocation } from '@tanstack/react-router';

import { FormattedMessage } from 'react-intl';

import type { MessagesKey } from '@/locales/messages';

import { DOCUMENTATION_URL } from '@/utils/constants/general';

import {
NavigationMenu,
NavigationMenuItem,
NavigationMenuList,
} from '@/components/ui/navigation-menu';

import { Separator } from '@/components/ui/separator';

import type { PossibleMonitorPath } from '@/types/general';

import { ExternalLinkIcon } from '@/components/Icons/ExternalLink';

import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/Tooltip';

import SendFeedback from './SendFeedback';

import NavLink from './NavLink';

type RouteMenuItems = {
navigateTo: PossibleMonitorPath;
idIntl: MessagesKey;
icon: JSX.Element;
selected: boolean;
};

type LinkMenuItems = {
url: string;
idIntl: MessagesKey;
icon: JSX.Element;
};

type LinkStringItems = {
url: string;
label: string;
};

const linkItems: LinkMenuItems[] = [
{
url: DOCUMENTATION_URL,
idIntl: 'global.documentation',
icon: <HiOutlineDocumentSearch />,
},
];

const dashboardItems: LinkStringItems[] = [
{
url: 'https://kdevops.org/',
label: 'kdevops',
},
{
url: 'https://netdev.bots.linux.dev/contest.html',
label: 'netdev-CI',
},
];

type SideMenuItemProps = {
item: RouteMenuItems;
};

const SideMenuItem = ({ item }: SideMenuItemProps): JSX.Element => {
const { pathname } = useLocation();

const isCurrentPath =
pathname.startsWith(item.navigateTo) &&
(pathname.length === item.navigateTo.length ||
pathname[item.navigateTo.length] === '/');

return (
<NavigationMenuItem key={item.idIntl} className="w-full">
<NavLink
selected={isCurrentPath}
to={item.navigateTo}
search={prevSearch => ({
origin: prevSearch.origin,
})}
icon={item.icon}
idIntl={item.idIntl}
/>
</NavigationMenuItem>
);
};
import SideMenuContent from './SideMenuContent';

const SideMenu = (): JSX.Element => {
const routeItems: RouteMenuItems[] = [
{
navigateTo: '/tree',
idIntl: 'routes.treeMonitor',
icon: <ImTree className="size-5" />,
selected: true,
},
{
navigateTo: '/hardware',
idIntl: 'routes.hardwareMonitor',
icon: <MdOutlineMonitorHeart className="size-5" />,
selected: false,
},
{
navigateTo: '/hardware-new',
idIntl: 'routes.hardwareNewMonitor',
icon: <MdOutlineMonitorHeart className="size-5" />,
selected: false,
},
{
navigateTo: '/issues',
idIntl: 'routes.issueMonitor',
icon: <RxRadiobutton className="size-5" />,
selected: false,
},
];

const linksItemElements = useMemo(
() =>
linkItems.map(item => (
<NavigationMenuItem key={item.idIntl} className="w-full">
<NavLink
asTag="a"
icon={item.icon}
idIntl={item.idIntl}
href={item.url}
target="_blank"
/>
</NavigationMenuItem>
)),
[],
);

const dashboardElements = useMemo(
() =>
dashboardItems.map(item => (
<NavigationMenuItem key={item.label} className="w-full">
<NavLink
asTag="a"
icon={<ExternalLinkIcon />}
label={item.label}
href={item.url}
target="_blank"
/>
</NavigationMenuItem>
)),
[],
);

return (
<NavigationMenu
className="bg-bg-secondary min-h-screen flex-col justify-start pt-6"
orientation="vertical"
>
<div className="w-full px-4">
<img src="/kernelci-logo-white.svg" className="max-w-[125px]" />
</div>

<Separator className="bg-on-secondary-10 my-4" />

<NavigationMenuList className="w-56 flex-col space-y-4 space-x-0">
{routeItems.map(item => (
<SideMenuItem item={item} key={item.idIntl} />
))}
<Separator className="bg-on-secondary-10 my-4" />
{linksItemElements}
<SendFeedback className="w-full" />
<Separator className="bg-on-secondary-10 my-4" />
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center gap-2 text-white">
<FormattedMessage id={'sidemenu.communityDashboards'} />
</div>
</TooltipTrigger>
<TooltipContent className="max-w-[450px]">
<FormattedMessage id={'sidemenu.communityDashboardsMsg'} />
</TooltipContent>
</Tooltip>
<div className="flex w-full flex-col space-y-0">
{dashboardElements}
</div>
</NavigationMenuList>
</NavigationMenu>
<div className="bg-bg-secondary hidden md:block">
<SideMenuContent className="min-h-screen" />
</div>
);
};

Expand Down
Loading