Skip to content

Commit

Permalink
#4 - feat: add navbar component
Browse files Browse the repository at this point in the history
  • Loading branch information
svenvandescheur committed Jan 15, 2024
1 parent 5dc0775 commit 26c25da
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 7 deletions.
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from "./dropdown";
export * from "./icon";
export * from "./layout";
export * from "./logo";
export * from "./navbar";
export * from "./page";
export * from "./toolbar";
export * from "./typography";
1 change: 1 addition & 0 deletions src/components/navbar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./navbar";
4 changes: 4 additions & 0 deletions src/components/navbar/navbar.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.mykn-navbar {
float: right; // Fallback.
float: inline-end
}
74 changes: 74 additions & 0 deletions src/components/navbar/navbar.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import type { Meta, StoryObj } from "@storybook/react";
import { expect, userEvent, within } from "@storybook/test";
import React from "react";

import { Button, ButtonLink } from "../button";
import { Outline } from "../icon";
import { Navbar } from "./navbar";

const meta = {
title: "Controls/Navbar",
component: Navbar,
} satisfies Meta<typeof Navbar>;

export default meta;
type Story = StoryObj<typeof meta>;

export const NavbarComponent: Story = {
args: {
children: (
<>
<Button variant="transparent">
<Outline.PencilIcon />
Zaaktypen
</Button>

<Button variant="transparent">
<Outline.ClipboardDocumentIcon />
Documenttypen
</Button>

<ButtonLink
href="https://www.example.com"
target="_blank"
variant="transparent"
>
<Outline.UserIcon />
Admin
</ButtonLink>

<Button variant="primary">
<Outline.ArrowRightStartOnRectangleIcon />
Uitloggen
</Button>
</>
),
},
};

export const NavbarOnMobile: Story = {
...NavbarComponent,
parameters: {
viewport: { defaultViewport: "mobile1" },
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const button = canvas.getByRole("button");

// Click opens, escape closes.
await userEvent.click(button, { delay: 10 });
expect(await canvas.findByRole("dialog")).toBeVisible();
userEvent.keyboard("{Escape}");
expect(await canvas.findByRole("dialog")).not.toBeVisible();
await userEvent.click(button, { delay: 10 });
expect(await canvas.findByRole("dialog")).toBeVisible();

// Tab focuses items.
await userEvent.tab({ delay: 10 });
await userEvent.tab({ delay: 10 });
await userEvent.tab({ delay: 10 });

const buttons = await canvas.findAllByRole("button");
expect(buttons[buttons.length - 1]).toBe(document.activeElement);
},
};
60 changes: 60 additions & 0 deletions src/components/navbar/navbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React, { useEffect, useState } from "react";

import { Dropdown } from "../dropdown";
import { Outline } from "../icon";
import { Toolbar, ToolbarProps } from "../toolbar";
import "./navbar.scss";

export type NavbarProps = ToolbarProps;

/**
* Shows a toolbar or dropdown based on mobile/desktop.
* @param children
* @param props
* @constructor
*/
export const Navbar: React.FC<NavbarProps> = ({ children, ...props }) => {
const [isMobile, setIsMobile] = useState(
window?.matchMedia("(max-width: 767px)").matches,
);

/**
* Updates `isMobile` on resize.
*/
useEffect(() => {
const onResize = () =>
setIsMobile(window?.matchMedia("(max-width: 767px)").matches);

window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
});

/**
* Renders the toolbar.
*/
const renderToolbar = () => (
<Toolbar
align="end"
variant={isMobile ? "normal" : "transparent"}
{...props}
>
{children}
</Toolbar>
);

return (
<div className="mykn-navbar">
{isMobile ? (
<Dropdown
label={<Outline.Bars2Icon />}
variant="transparent"
aria-label="Menu openen/sluiten"
>
{renderToolbar()}
</Dropdown>
) : (
renderToolbar()
)}
</div>
);
};
8 changes: 7 additions & 1 deletion src/components/page/page.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
@use '../../settings/style';

.mykn-page {
background-color: var(--theme-color-primary-200);
container-name: page;
container-type: size;
box-sizing: border-box;
padding: var(--spacing-h-xl);
width: 100%;
height: 100%;
padding: var(--spacing-v-xl) var(--spacing-h-xl);

@media screen and (min-width: style.$breakpoint-desktop) {
padding: var(--spacing-v-xl) var(--spacing-h-xl);
}
}
17 changes: 11 additions & 6 deletions src/components/page/page.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Outline } from "../icon";
import { Container, Grid } from "../layout";
import { Column } from "../layout/column";
import { Logo } from "../logo";
import { Toolbar } from "../toolbar";
import { Navbar } from "../navbar";
import { Page } from "./page";

type PagePropsAndCustomArgs = React.ComponentProps<typeof Page> & {
Expand Down Expand Up @@ -38,16 +38,14 @@ export const SamplePage: Story = {
<Page>
<Container debug={debug}>
<Grid debug={debug}>
<Column debug={debug} span={1}>
<Column debug={debug} span={12}>
<Logo
href="/?path=/story/building-blocks-page--sample-page"
hrefLabel="Navigate to story page"
label="Maykin"
/>
</Column>

<Column debug={debug} span={11}>
<Toolbar align="end" variant="transparent">
<Navbar align="end">
<Button variant="transparent">
<Outline.PencilIcon />
Zaaktypen
Expand All @@ -71,10 +69,17 @@ export const SamplePage: Story = {
<Outline.ArrowRightStartOnRectangleIcon />
Uitloggen
</Button>
</Toolbar>
</Navbar>
</Column>
</Grid>
</Container>
</Page>
),
};

export const SamplePageOnMobile: Story = {
...SamplePage,
parameters: {
viewport: { defaultViewport: "mobile1" },
},
};

0 comments on commit 26c25da

Please sign in to comment.