Skip to content

Commit

Permalink
[Aksel.nav.no] Ny Meny for sidebar og TOC (#2873)
Browse files Browse the repository at this point in the history
* 🎉 WIP for ny sidebar og TOC meny

* 💫 Animert farger

* 🎨 Støtte for nested menuitems

* 🎨 Støtter nå sidebar og toc-variant for farger

* ♻️ Migrert TOC til å bruke ny meny

* ♻️ Migrert Sidebar til å bruke ny meny

* 🐛 Fikset story

* ♿ Klikkhøyde nå minst 32px

* 💄 Justert bakgrunnsanimasjon fra 200ms -> 100ms

* Types: variant bruker semantiske navn

* bug: MenuLink ble brukt utenfor MenuLi
  • Loading branch information
KenAJoh committed Apr 26, 2024
1 parent 3b44a97 commit 710ed83
Show file tree
Hide file tree
Showing 7 changed files with 287 additions and 97 deletions.
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

0 comments on commit 710ed83

Please sign in to comment.