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

[Aksel.nav.no] Ny Meny for sidebar og TOC #2873

Merged
merged 12 commits into from
Apr 26, 2024
56 changes: 20 additions & 36 deletions aksel.nav.no/website/components/layout/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import Link from "next/link";
import { useRouter } from "next/router";
import { Box, Label, Show } from "@navikt/ds-react";
import { amplitudeLogNavigation } from "@/logging";
import { Box, Show } from "@navikt/ds-react";
import { SidebarT } from "@/types";
import { StatusTag } from "@/web/StatusTag";
import { Menu, MenuHeading, MenuLi, MenuLink, MenuUl } from "@/web/menu/Menu";

const Sidebar = ({
kategori,
Expand All @@ -21,11 +20,13 @@ const Sidebar = ({
as="nav"
className="w-sidebar shrink-0 self-start"
>
<ul className="space-y-6">
{links.map((section) => (
<Section key={section.value} section={section} />
))}
</ul>
<Menu loggingContext="meny" variant="action">
<ul className="space-y-6">
{links.map((section) => (
<Section key={section.value} section={section} />
))}
</ul>
</Menu>
</Box>
</Show>
);
Expand All @@ -39,39 +40,22 @@ function Section({ section }: { section: SidebarT[number] }) {
};
return (
<li>
<Label
as="div"
size="small"
textColor="subtle"
className="flex min-h-8 items-center px-4"
>
{section.title}
</Label>
<ul className="space-y-px pl-3">
<MenuHeading as="div">{section.title}</MenuHeading>
<MenuUl>
{section.pages.map((page) => (
<li
<MenuLi
aria-current={isActive(page.slug) ? "page" : undefined}
key={page.slug}
data-active={isActive(page.slug)}
className="relative mx-2 flex min-h-8 items-center rounded-medium transition-[background-color] ease-out hover:bg-surface-hover data-active:bg-surface-selected data-active:font-semibold data-active:text-deepblue-800 data-active:shadow-xsmall"
>
<Link
prefetch={false}
href={`/${page.slug}`}
className="flex min-h-8 w-full items-center justify-between gap-2 break-words rounded-medium px-2 text-medium focus:outline-none focus-visible:z-10 focus-visible:shadow-focus"
onClick={(e) =>
amplitudeLogNavigation(
"meny",
e.currentTarget.getAttribute("href"),
)
}
>
{page.heading}
<StatusTag size="xsmall" status={page.tag} />
</Link>
</li>
<MenuLink href={`/${page.slug}`} selected={isActive(page.slug)}>
<span className="flex items-center justify-between">
{page.heading}
<StatusTag size="xsmall" status={page.tag} />
</span>
</MenuLink>
</MenuLi>
))}
</ul>
</MenuUl>
</li>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.menuListItem {
padding-inline: var(--a-spacing-2);
}

.menuList .menuList .menuListItem {
padding-inline-start: var(--a-spacing-5);
}
114 changes: 114 additions & 0 deletions aksel.nav.no/website/components/website-modules/menu/Menu.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/* eslint-disable jsx-a11y/click-events-have-key-events */

/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
import { Meta, StoryObj } from "@storybook/react";
import { useState } from "react";
import { Menu, MenuHeading, MenuLi, MenuLink, MenuUl } from "./Menu";

const meta = {
title: "Website-modules/Menu",
component: Menu,
tags: ["autodocs"],
} satisfies Meta<typeof Menu>;

export default meta;

type Story = StoryObj<typeof meta>;

export const MenuDemo: Story = {
render: () => {
const [selected, setSelected] = useState(0);
return (
<Menu loggingContext="meny" variant="action">
<>
<MenuHeading as="div">Primitives</MenuHeading>
<ul onClick={() => setSelected((x) => (x === 0 ? 1 : 0))}>
<MenuLi>
<MenuLink href="#">Bleed</MenuLink>
</MenuLi>
<MenuLi>
<MenuLink href="#">Box</MenuLink>
</MenuLi>
<MenuLi>
<MenuLink href="#" selected={selected === 1}>
HGrid
</MenuLink>
</MenuLi>
<MenuLi>
<MenuLink href="#">Hide</MenuLink>
</MenuLi>
<MenuLi>
<MenuLink href="#" selected={selected === 0}>
HStack
</MenuLink>
</MenuLi>
<MenuLi>
<MenuLink href="#">Show</MenuLink>
</MenuLi>
<MenuLi>
<MenuLink href="#" selected>
VStack
</MenuLink>
</MenuLi>
</ul>
</>
</Menu>
);
},
args: {
children: null,
loggingContext: "meny",
variant: "action",
},
};

export const NestedMenuDemo: Story = {
render: () => {
return (
<Menu loggingContext="toc" variant="neutral">
<MenuHeading as="h2">Innhold på siden</MenuHeading>
<MenuUl>
<MenuLi>
<MenuLink href="#" selected>
Så, hvordan går vi frem for å få det til?
</MenuLink>
</MenuLi>
<MenuLi>
<MenuLink href="#">Bærekraftige team</MenuLink>
</MenuLi>

<MenuLi>
<MenuUl>
<MenuLi>
<MenuLink href="#">Teamstørrelse</MenuLink>
</MenuLi>
<MenuLi>
<MenuLink href="#">Stabilitet Vs. fleksibilitet</MenuLink>
</MenuLi>
<MenuLi>
<MenuLink href="#" selected>
Myndighet og hensikt
</MenuLink>
</MenuLi>
<MenuLi>
<MenuLink href="#">
Ansvar tilpasset kognitiv kapasitet
</MenuLink>
</MenuLi>
</MenuUl>
</MenuLi>
<MenuLi>
<MenuLink href="#" selected>
Teamtyper
</MenuLink>
</MenuLi>
</MenuUl>
</Menu>
);
},
args: {
children: null,
loggingContext: "toc",
variant: "neutral",
},
};
122 changes: 122 additions & 0 deletions aksel.nav.no/website/components/website-modules/menu/Menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import cl from "clsx";
import Link from "next/link";
import { createContext, useContext } from "react";
import { BodyShort, Label } from "@navikt/ds-react";
import { amplitudeLogNavigation } from "@/logging";
import styles from "./Menu.module.css";

type MenuProps = {
children: React.ReactNode;
loggingContext: "meny" | "toc";
variant: "action" | "neutral";
};

const MenuContext = createContext<Omit<MenuProps, "children"> | null>(null);

export function Menu({ children, ...rest }: MenuProps) {
return <MenuContext.Provider value={rest}>{children}</MenuContext.Provider>;
}

type MenuHeadingProps = {
children: React.ReactNode;
as: "div" | "h2";
};

export function MenuHeading({ children, as }: MenuHeadingProps) {
return (
<Label as={as} size="small" textColor="subtle" className="py-05">
{children}
</Label>
);
}

type MenuListProps = {
children: React.ReactNode;
id?: string;
className?: string;
};

export function MenuUl({ children, id, className }: MenuListProps) {
return (
<ul className={cl(styles.menuList, className)} id={id}>
{children}
</ul>
);
}

type MenuLiProps = {
children: React.ReactNode;
};

export function MenuLi({ children }: MenuLiProps) {
return <li className="group">{children}</li>;
}

type MenuLinkProps = {
children: React.ReactNode;
selected?: boolean;
href: string;
id?: string;
onClick?: () => void;
};

export function MenuLink({
children,
href,
selected,
id,
onClick,
}: MenuLinkProps) {
const ctx = useContext(MenuContext);

if (!ctx) {
throw new Error("MenuListItem must be used inside a Menu component");
}

return (
<div className="relative scroll-m-6 border-l border-border-subtle" id={id}>
<BodyShort
data-type={ctx.variant}
size="small"
as={Link}
prefetch={false}
href={href}
onClick={(e) => {
amplitudeLogNavigation(
ctx.loggingContext,
e.currentTarget.getAttribute("href"),
);
onClick?.();
}}
className={cl(
styles.menuListItem,
"flex py-05 focus:outline-none *:focus-visible:shadow-focus group-first:pt-0 group-last:last:pb-0",
"before:absolute before:-left-px before:top-05 before:h-[calc(100%-0.25rem)] before:rounded-r-sm before:transition-all group-first:before:top-0 group-first:before:h-[calc(100%-0.125rem)] group-last:before:h-[calc(100%-0.125rem)]",
{
"text-text-subtle before:w-0 before:bg-gray-400 before:duration-100 before:ease-linear hover:text-text-default hover:before:w-1":
!selected,
"before:w-1": selected,
"text-deepblue-700 before:bg-deepblue-700":
selected && ctx.variant === "action",
"text-text-default before:bg-gray-700":
selected && ctx.variant === "neutral",
},
)}
>
<span
className={cl(
"w-full rounded px-2 py-1 transition-colors duration-100 ease-out",
{
"bg-surface-selected": selected && ctx.variant === "action",
"bg-surface-neutral-subtle":
selected && ctx.variant === "neutral",
"bg-transparent": !selected,
},
)}
>
{children}
</span>
</BodyShort>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Label } from "@navikt/ds-react";
import { TableOfContentsT } from "@/types";
import { Menu, MenuHeading } from "@/web/menu/Menu";
import ScrollFade from "./parts/ScrollFade";
import UlList from "./parts/UlList";
import { useToc } from "./useToc";
Expand All @@ -16,6 +16,9 @@ function TableOfContents({ toc, variant }: TableOfContentsProps) {
return null;
}

/**
* We have to add this to account for different backgrounds for god-praksis and komponent-pages
*/
const style = {
"--shadow-color":
variant === "subtle"
Expand All @@ -28,13 +31,13 @@ function TableOfContents({ toc, variant }: TableOfContentsProps) {
className="sticky top-20 order-1 hidden min-w-60 self-start p-1 xl:block"
style={style}
>
<Label as="h2" className="px-2" textColor="subtle">
Innhold på siden
</Label>
<div className="relative">
<ScrollFade />
<UlList toc={toc} tocProps={tocCtx} />
</div>
<Menu loggingContext="toc" variant="neutral">
<MenuHeading as="h2">Innhold på siden</MenuHeading>
<div className="relative">
<ScrollFade />
<UlList toc={toc} tocProps={tocCtx} />
</div>
</Menu>
</aside>
);
}
Expand Down
Loading
Loading