Skip to content

Commit

Permalink
feat(OnyxNavBar): support mobile context area (#1388)
Browse files Browse the repository at this point in the history
Relates to #1073

- implement support for mobile context area inside the nav bar
  • Loading branch information
larsrickert authored Jun 26, 2024
1 parent 6c16b45 commit 1cc020a
Show file tree
Hide file tree
Showing 19 changed files with 245 additions and 53 deletions.
5 changes: 5 additions & 0 deletions .changeset/great-months-serve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"sit-onyx": minor
---

feat(OnyxNavBar): support mobile context area
5 changes: 5 additions & 0 deletions .changeset/hip-candles-unite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"sit-onyx": patch
---

fix: limit width to `max-content` for OnyxTag and OnyxTimer
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.
35 changes: 34 additions & 1 deletion packages/sit-onyx/src/components/OnyxNavBar/OnyxNavBar.ct.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ import {
MOCK_PLAYWRIGHT_LOGO_URL,
defineLogoMockRoutes,
executeMatrixScreenshotTest,
mockPlaywrightIcon,
} from "../../playwright/screenshots";
import { ONYX_BREAKPOINTS } from "../../types";
import OnyxAppLayout from "../OnyxAppLayout/OnyxAppLayout.vue";
import OnyxBadge from "../OnyxBadge/OnyxBadge.vue";
import OnyxIcon from "../OnyxIcon/OnyxIcon.vue";
import OnyxIconButton from "../OnyxIconButton/OnyxIconButton.vue";
import OnyxListItem from "../OnyxListItem/OnyxListItem.vue";
import OnyxPageLayout from "../OnyxPageLayout/OnyxPageLayout.vue";
import OnyxTag from "../OnyxTag/OnyxTag.vue";
import OnyxUserMenu from "../OnyxUserMenu/OnyxUserMenu.vue";

test.beforeEach(async ({ page }) => {
Expand Down Expand Up @@ -85,8 +90,30 @@ test("Screenshot tests (mobile)", async ({ mount, page }) => {

<template v-slot:mobileActivePage>Nested item 2</template>

<template v-slot:globalContextArea>
<OnyxIconButton label="Search" icon={mockPlaywrightIcon} color="neutral" />
</template>

<template v-slot:contextArea>
<OnyxUserMenu username="John Doe" />
<OnyxIconButton label="Notification center" icon={mockPlaywrightIcon} color="neutral" />
<OnyxTag icon={mockPlaywrightIcon} color="warning" label="QA stage" />

<OnyxUserMenu username="John Doe" description="Company name">
<OnyxListItem>
<OnyxIcon icon={mockPlaywrightIcon} />
Settings
</OnyxListItem>

<OnyxListItem color="danger">
<OnyxIcon icon={mockPlaywrightIcon} />
Logout
</OnyxListItem>

<template v-slot:footer>
App version
<span class="onyx-text--monospace">1.0.0</span>
</template>
</OnyxUserMenu>
</template>
</OnyxNavBar>,
);
Expand Down Expand Up @@ -130,6 +157,12 @@ test("Screenshot tests (mobile)", async ({ mount, page }) => {
await expect(component.getByLabel("Item 1")).toBeVisible();
await expect(component.getByLabel("Item 2")).toBeVisible();
await expect(component.getByLabel("Item 3")).toBeVisible();

// ACT
await component.getByLabel("Toggle context menu").click();

// ASSERT
await expect(page).toHaveScreenshot("context.png");
});

test("should behave correctly", async ({ mount }) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import browserTerminal from "@sit-onyx/icons/browser-terminal.svg?raw";
import placeholder from "@sit-onyx/icons/placeholder.svg?raw";
import search from "@sit-onyx/icons/search.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 OnyxIconButton from "../OnyxIconButton/OnyxIconButton.vue";
import OnyxNavItem from "../OnyxNavItem/OnyxNavItem.vue";
import OnyxNavSeparator from "../OnyxNavSeparator/OnyxNavSeparator.vue";
import OnyxTag from "../OnyxTag/OnyxTag.vue";
Expand All @@ -24,6 +26,7 @@ const meta: Meta<typeof OnyxNavBar> = {
contextArea: { control: { disable: true } },
appArea: { control: { type: "text" } },
mobileActivePage: { control: { type: "text" } },
globalContextArea: { control: { disable: true } },
mobileBreakpoint: {
options: Object.keys(ONYX_BREAKPOINTS),
control: {
Expand Down Expand Up @@ -90,6 +93,9 @@ const getTimerEndDate = () => {
export const WithContextArea = {
args: {
...Default.args,
globalContextArea: () => [
h(OnyxIconButton, { label: "Search", icon: search, color: "neutral" }),
],
contextArea: () => [
h(OnyxTag, { label: "QA stage", color: "warning", icon: browserTerminal }),
h(OnyxNavSeparator),
Expand All @@ -111,7 +117,10 @@ export const WithLogoutTimer = {
contextArea: () => [
h(OnyxTimer, { endTime: getTimerEndDate(), label: "Logout in:" }),
h(OnyxNavSeparator),
h(OnyxUserMenu, OnyxUserMenuDefault.args),
h(OnyxUserMenu, OnyxUserMenuDefault.args, {
default: OnyxUserMenuDefault.args.default,
footer: OnyxUserMenuDefault.args.footer,
}),
],
},
} satisfies Story;
Expand Down
77 changes: 65 additions & 12 deletions packages/sit-onyx/src/components/OnyxNavBar/OnyxNavBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ const slots = defineSlots<{
* Optional context area on the right to display additional (global) components, like user login, global settings etc.
*/
contextArea?: () => unknown;
/**
* Same as `contextArea` slot on desktop (will be placed next to it) but on mobile, the components inside will stay
* in the mobile nav bar itself and will not be collapsed into the context menu button.
*
* Global actions like e.g. search or notification center can be placed here that should always be directly accessible on mobile.
*/
globalContextArea?: () => unknown;
/**
* Label for displaying the currently active page in mobile mode.
* If a child of a nav item is active, it should displayed the child label instead of the parent.
Expand Down Expand Up @@ -126,20 +133,28 @@ provide(mobileNavBarInjectionKey, isMobile);
</nav>
</template>

<template v-if="slots.contextArea">
<OnyxMobileNavButton
v-if="isMobile"
v-model:open="isContextOpen"
class="onyx-nav-bar__mobile-context"
:icon="moreVertical"
:label="t('navigation.toggleContextMenu')"
@update:open="isBurgerOpen = false"
>
<!-- TODO: implement mobile context menu -->
</OnyxMobileNavButton>
<template v-if="slots.contextArea || slots.globalContextArea">
<div v-if="isMobile" class="onyx-nav-bar__mobile-context">
<div v-if="slots.globalContextArea" class="onyx-nav-bar__mobile-global-context">
<slot name="globalContextArea"></slot>
</div>

<OnyxMobileNavButton
v-if="slots.contextArea"
v-model:open="isContextOpen"
:icon="moreVertical"
:label="t('navigation.toggleContextMenu')"
@update:open="isBurgerOpen = false"
>
<div class="onyx-nav-bar__mobile-context-content">
<slot name="contextArea"></slot>
</div>
</OnyxMobileNavButton>
</div>

<div v-else class="onyx-nav-bar__context">
<slot name="contextArea"></slot>
<slot v-if="slots.globalContextArea" name="globalContextArea"></slot>
<slot v-if="slots.contextArea" name="contextArea"></slot>
</div>
</template>
</div>
Expand Down Expand Up @@ -257,7 +272,45 @@ $height: 3.5rem;
&__mobile-context {
grid-area: mobile-context;
display: flex;
align-items: center;
margin-left: auto;
gap: var(--onyx-spacing-4xs);
}
&__mobile-global-context {
display: flex;
flex-direction: row-reverse;
gap: inherit;
}
&__mobile-context-content {
display: flex;
flex-direction: row-reverse;
flex-wrap: wrap-reverse;
align-items: center;
justify-content: flex-end;
gap: var(--onyx-spacing-2xs);
// add extra spacing after the user menu if it is not immediately followed by a nav separator
// since we are in a row-reverse layout, we need to "separator + user menu" as selector
// to check if a nav separator exists "visually" after the user menu
&:not(:has(.onyx-nav-separator + .onyx-user-menu)) {
.onyx-user-menu {
margin-bottom: var(--onyx-spacing-md);
}
}
&:has(.onyx-user-menu__footer) {
margin-bottom: var(--onyx-spacing-xl);
.onyx-user-menu__footer {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
}
}
}
&__mobile-page {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
<script lang="ts" setup>
import { inject } from "vue";
import { mobileNavBarInjectionKey } from "../OnyxNavBar/types";
import type { OnyxNavSeparatorProps } from "./types";
const props = withDefaults(defineProps<OnyxNavSeparatorProps>(), {
orientation: "vertical",
});
const isMobile = inject(mobileNavBarInjectionKey);
</script>

<template>
<div
class="onyx-separator"
:class="{ 'onyx-separator--horizontal': props.orientation === 'horizontal' }"
class="onyx-nav-separator"
:class="{ 'onyx-nav-separator--horizontal': props.orientation === 'horizontal' || isMobile }"
role="separator"
:aria-orientation="props.orientation"
></div>
Expand All @@ -18,7 +22,7 @@ const props = withDefaults(defineProps<OnyxNavSeparatorProps>(), {
<style lang="scss">
@use "../../styles/mixins/layers";
.onyx-separator {
.onyx-nav-separator {
@include layers.component() {
background-color: var(--onyx-color-base-neutral-300);
margin: 0 var(--onyx-spacing-2xs);
Expand Down
1 change: 1 addition & 0 deletions packages/sit-onyx/src/components/OnyxTag/OnyxTag.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const { densityClass } = useDensity(props);
display: inline-flex;
align-items: center;
gap: var(--onyx-spacing-3xs);
width: max-content;
max-width: 100%;
padding: var(--onyx-tag-padding);
border-radius: var(--onyx-radius-xs);
Expand Down
2 changes: 2 additions & 0 deletions packages/sit-onyx/src/components/OnyxTimer/OnyxTimer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ watchEffect(() => isEnded.value && emit("timerEnded"));
padding: var(--onyx-spacing-2xs) var(--onyx-spacing-md);
font-family: var(--onyx-font-family);
color: var(--onyx-color-text-icons-neutral-intense);
width: max-content;
max-width: 100%;
&__label {
color: var(--onyx-color-text-icons-neutral-soft);
Expand Down
Loading

0 comments on commit 1cc020a

Please sign in to comment.