Skip to content

Commit

Permalink
feat(OnyxNavBar): implement basic mobile nav bar (#1252)
Browse files Browse the repository at this point in the history
Relates to #1073

- implement basic mobile nav bar
- fix selected styles for nested OnyxNavItem options
  • Loading branch information
larsrickert authored Jun 14, 2024
1 parent c58780d commit 0511127
Show file tree
Hide file tree
Showing 33 changed files with 443 additions and 59 deletions.
5 changes: 5 additions & 0 deletions .changeset/moody-taxis-chew.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"sit-onyx": patch
---

fix(OnyxNavItem): fix styles for selected child items
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { test } from "../../playwright/a11y";
import { executeMatrixScreenshotTest, mockPlaywrightIcon } from "../../playwright/screenshots";
import OnyxMobileNavButton from "./OnyxMobileNavButton.vue";

test.describe("Screenshot tests", () => {
executeMatrixScreenshotTest({
name: "Mobile nav button",
columns: ["default", "open"],
rows: ["default", "hover", "active", "focus-visible"],
component: (column) => (
<OnyxMobileNavButton label="Label" icon={mockPlaywrightIcon} open={column === "open"} />
),
beforeScreenshot: async (component, page, column, row) => {
if (row === "hover") await component.hover();
if (row === "focus-visible") await page.keyboard.press("Tab");
if (row === "active") {
const box = (await component.boundingBox())!;
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
await page.mouse.down();
}
},
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import menu from "@sit-onyx/icons/menu.svg?raw";
import { defineStorybookActionsAndVModels } from "@sit-onyx/storybook-utils";
import type { Meta, StoryObj } from "@storybook/vue3";
import { defineIconSelectArgType } from "../../utils/storybook";
import OnyxMobileNavButton from "./OnyxMobileNavButton.vue";

/**
* Nav button that is mainly used inside the nav bar on mobile, e.g. for the burger and context menu buttons.
*/
const meta: Meta<typeof OnyxMobileNavButton> = {
title: "support/MobileNavButton",
...defineStorybookActionsAndVModels({
component: OnyxMobileNavButton,
events: ["update:open"],
argTypes: {
icon: defineIconSelectArgType(),
},
}),
};

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

export const Default = {
args: {
label: "Open burger menu",
icon: menu,
},
} satisfies Story;

export const Open = {
args: {
label: "Close burger menu",
icon: menu,
open: true,
},
} satisfies Story;
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<script lang="ts" setup>
import x from "@sit-onyx/icons/x.svg?raw";
import OnyxIcon from "../OnyxIcon/OnyxIcon.vue";
import type { OnyxMobileNavButtonProps } from "./types";
const props = withDefaults(defineProps<OnyxMobileNavButtonProps>(), {
open: false,
});
const emit = defineEmits<{
/**
* Emitted when the open state changes on click.
*/
"update:open": [isOpen: boolean];
}>();
</script>

<template>
<button
class="onyx-mobile-nav-button"
:class="{ 'onyx-mobile-nav-button--active': props.open }"
:aria-label="props.label"
@click="emit('update:open', !props.open)"
>
<OnyxIcon :icon="props.open ? x : props.icon" />
</button>
</template>

<style lang="scss">
@use "../../styles/mixins/layers.scss";
.onyx-mobile-nav-button {
@include layers.component() {
display: flex;
background-color: var(--onyx-color-base-background-blank);
color: var(--onyx-color-text-icons-neutral-medium);
padding: var(--onyx-spacing-md);
cursor: pointer;
:where(&) {
border: none;
}
&:hover:not(&--active) {
background-color: var(--onyx-color-base-background-tinted);
}
&:focus-visible {
background-color: var(--onyx-color-base-secondary-100);
outline: none;
}
&:active,
&--active {
background-color: var(--onyx-color-base-secondary-100);
color: var(--onyx-color-text-icons-secondary-intense);
}
}
}
</style>
15 changes: 15 additions & 0 deletions packages/sit-onyx/src/components/OnyxMobileNavButton/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export type OnyxMobileNavButtonProps = {
/**
* Aria label that describes the action when clicking the button.
*/
label: string;
/**
* Icon to show when closed.
*/
icon: string;
/**
* Whether the button is considered open / is connected to an open menu/flyout.
* If `true`, an "x" icon will be displayed.
*/
open?: boolean;
};
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ const buttonLabel = computed(() => props.label ?? t.value("navigation.goToHome")
align-items: center;
gap: var(--onyx-spacing-md);
padding: var(--onyx-spacing-md);
border-right: var(--onyx-1px-in-rem) solid var(--onyx-color-base-neutral-300);
font-weight: 600;
white-space: pre-line;
max-height: 100%;
Expand Down
4 changes: 4 additions & 0 deletions packages/sit-onyx/src/components/OnyxNavBar/OnyxNavBar.ct.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ test.describe("Screenshot tests", () => {
<OnyxNavItem label="Item" active />
<OnyxNavItem label="Item" />

<template v-slot:mobileActivePage>Item</template>

{row.includes("context") && (
<template v-slot:contextArea>
<OnyxUserMenu username="John Doe" options={[]} />
Expand Down Expand Up @@ -92,6 +94,8 @@ test("should be aligned with the grid in a full app layout", async ({ page, moun
<OnyxNavItem label="Item" active />
<OnyxNavItem label="Item" />

<template v-slot:mobileActivePage>Item</template>

<template v-slot:contextArea>
<OnyxUserMenu username="John Doe" options={[]} />
</template>
Expand Down
23 changes: 20 additions & 3 deletions packages/sit-onyx/src/components/OnyxNavBar/OnyxNavBar.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import placeholder from "@sit-onyx/icons/placeholder.svg?raw";
import { defineStorybookActionsAndVModels } from "@sit-onyx/storybook-utils";
import type { Decorator, Meta, StoryObj } from "@storybook/vue3";
import { h } from "vue";
import { ONYX_BREAKPOINTS } from "../../types";
import OnyxBadge from "../OnyxBadge/OnyxBadge.vue";
import OnyxIcon from "../OnyxIcon/OnyxIcon.vue";
import OnyxNavItem from "../OnyxNavItem/OnyxNavItem.vue";
Expand All @@ -22,6 +23,19 @@ const meta: Meta<typeof OnyxNavBar> = {
default: { control: { disable: true } },
contextArea: { control: { disable: true } },
appArea: { control: { type: "text" } },
mobileActivePage: { control: { type: "text" } },
mobileBreakpoint: {
options: Object.keys(ONYX_BREAKPOINTS),
control: {
labels: Object.entries(ONYX_BREAKPOINTS).reduce<Record<string, string>>(
(labels, [name, width]) => {
labels[name] = `${name} (${width}px)`;
return labels;
},
{},
),
},
},
},
decorators: [
(story) => ({
Expand All @@ -40,22 +54,23 @@ export const Default = {
logoUrl: "/onyx-logo.svg",
appName: "App name",
default: () => [
h(OnyxNavItem, { label: "Item", href: "/", active: true }),
h(OnyxNavItem, { label: "Item", href: "/" }),
h(
OnyxNavItem,
{
label: "Item",
href: "/test",
options: [
{ label: "Nested item 1", href: "#" },
{ label: "Nested item 2", href: "#" },
{ label: "Nested item 2", href: "#", active: true },
{ label: "Nested item 3", href: "#" },
],
},
() => ["Item", h(OnyxBadge, { dot: true, color: "warning" })],
),
h(OnyxNavItem, { label: "Item", href: "https://onyx.schwarz" }),
],
mobileActivePage: "Nested item 2",
},
} satisfies Story;

Expand All @@ -78,7 +93,9 @@ export const WithContextArea = {
contextArea: () => [
h(OnyxTag, { label: "QA stage", color: "warning", icon: browserTerminal }),
h(OnyxNavSeparator),
h(OnyxUserMenu, OnyxUserMenuDefault.args),
h(OnyxUserMenu, OnyxUserMenuDefault.args, {
footer: OnyxUserMenuDefault.args.footer,
}),
],
},
} satisfies Story;
Expand Down
Loading

0 comments on commit 0511127

Please sign in to comment.