Skip to content

Commit

Permalink
Dang 123/Main vertical navigation (#19)
Browse files Browse the repository at this point in the history
* wip

* initial styles for main nav item

* create plugin for accessing Lob constants

* create component to render top level main nav item

* move example icon to assets

* testing MainNavigationItem

* finish styling main nav item child container

* dynamically render button or link if to prop is present

* integrate main nav with vue router

* test for link or button

* clean up item styles and specs

* initial child item component

* initial Main Nav component

* test nav child item

* test main nav component

* clean up nav desktop styles more

* update stories

* update docs

* create custom decorator for routing state in storybook

* clean up some stories and styles

* update text styles

* consolidate main nav stories

* stop propagation on router link click events

* add more to nav story

* fix caret direction in nav item

* animate drawer opening and closing

* animate drawer with pure CSS, better performance and easier mobile styling and behavior

* add collapsible prop to MainNav and MainNavItem

In case there are some scenarios where we don't want to support collapsing
nav (drawer animation) or nav items (toggling sub navs), add a prop that
defaults to true so that we can choose to turn these features off.

* add more docs to stories for main nav

* add tests for expandable and collapsible logic

* get docs addon and routeDecorator to play nice

* remove deprecated docs

* lint css

* remove gsap package from js animation iteration

* add subcomponents to main nav story

* fix colors

* fix failing spec

* small reorg of main nav doc

* turn off stylelint at-rule-no-unknown
  • Loading branch information
mercedesb committed Jun 2, 2021
1 parent f105627 commit fc2a62b
Show file tree
Hide file tree
Showing 17 changed files with 842 additions and 11 deletions.
6 changes: 2 additions & 4 deletions .storybook/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ import * as configs from '../src/config';

import '../src/assets/styles/main.scss';

for (const configName in configs) {
const config = configs[configName];
config.configure();
}
configs.icons.configure();

Vue.use(configs.constants);
Vue.component('font-awesome-icon', FontAwesomeIcon);

export const parameters = {
Expand Down
21 changes: 21 additions & 0 deletions .storybook/routeDecorator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Vue from 'vue';
import VueRouter from 'vue-router';

// abbreviated example of https://github.com/gvaldambrini/storybook-router/blob/master/packages/vue/vue.js

export default (path = '/') => {
return (storyFn) => {
Vue.use(VueRouter);
const router = new VueRouter({ mode: 'history' });

router.replace(path);

const WrappedComponent = storyFn();

return Vue.extend({
router,
components: { WrappedComponent },
template: '<wrapped-component/>'
});
}
}
5 changes: 4 additions & 1 deletion .stylelintrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
{
"extends": "stylelint-config-standard"
"extends": "stylelint-config-standard",
"rules": {
"at-rule-no-unknown": null
}
}
13 changes: 12 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"@fortawesome/pro-light-svg-icons": "^5.15.3",
"@fortawesome/vue-fontawesome": "^2.0.2",
"core-js": "^3.6.5",
"vue": "^2.6.11"
"vue": "^2.6.11",
"vue-router": "^3.5.1"
},
"devDependencies": {
"@babel/core": "^7.13.16",
Expand Down
20 changes: 20 additions & 0 deletions src/assets/images/iconOverview.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
92 changes: 92 additions & 0 deletions src/components/MainNavigation/MainNavigation.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import {
Preview,
Story,
ArgsTable,
PRIMARY_STORY,
} from "@storybook/addon-docs/blocks";
import MainNavigation from "./MainNavigation.vue";
import MainNavigationItem from "./MainNavigationItem.vue";
import MainNavigationChildItem from "./MainNavigationChildItem.vue";

# MainNavigation

The main navigation component is used for displaying the main, vertical navigation.

<Preview>
<Story id="components-main-navigation--primary" />
</Preview>

## How to Use

The main navigation renders a semantic `nav` element with a `ul`. It is made to work with `main-navigation-item` components. However, you can also write custom components for its slot as long as the root element is an `li` (for accessibility).

The main navigation has a single prop, `collapsible` to control whether it has a click event on desktop that animates the navigation on click to slide in to a smaller width. This can give the user more screen real estate. `collapsible` defaults to true. If you do not want this animation, set this to false.

When `collapsible` is true, the main navigation component passes information about it's expanded/collapsed state through slot props. This requires you passes the expanded prop to the child components in the template (see example below).

Example of using this component in a template
```html
<main-navigation v-bind="$props">
<template v-slot="{ expanded }">
<main-navigation-item title="Overview" iconSrc="overviewIconSrcFile" iconAltText="Overview icon" to="/overview" :expanded="expanded" />
<main-navigation-item title="Address Verification" iconSrc="addressVerificationIconSrcFile" iconAltText="Address verification icon" :subNavCollapsed="false" :expanded="expanded">
<main-navigation-child-item title="US Verifications" to="/us-verifications" />
<main-navigation-child-item title="Int'l Verifications" to="/intl-verifications" />
</main-navigation-item>
</template>
</main-navigation>
```

# Main Navigation Item

The main navigation item component is used for displaying a top level navigation item in the main, vertical navigation.

<Preview>
<Story id="components-main-navigation--item" />
</Preview>

## How to Use

The main navigation item component can render as a traditional link (hooked up to your app's routing) or as a button that will show its child nav items on click.

If you pass a `to` property, it will render as a link. Otherwise, it will render as a button.

The `expanded` prop should come from the parent main navigation component and will control whether the item renders at full width on desktop or only renders wide enough to show its icon.

Example of using this component in a template as a link.
```html
<main-navigation-item title="Overview" iconSrc="overviewIconSrcFile" iconAltText="Overview icon" to="/overview" :expanded="expanded" />
```

If you provide children elements in the default slot, it will bind a click event to the button to hide/show the child nav items. If you do not want to allow the user to collapse this subnavigation (i.e. you want the child items always visible), set the `collapsible` prop on the main navigation item component to false.

If you want the component to render with the child items collapsed by default, you can use the `subNavCollapsed` prop set to true.

Example of using this component in a template as a button with child nav items.
```html
<main-navigation-item title="Address Verification" iconSrc="addressVerificationIconSrcFile" iconAltText="Address verification icon" :expanded="expanded">
<main-navigation-child-item title="US Verifications" to="/us-verifications" />
<main-navigation-child-item title="Int'l Verifications" to="/intl-verifications" />
</main-navigation-item>
```

# MainNavigationChildItem

The main navigation child item component is used for displaying a sub level navigation item in the main, vertical navigation.

<Preview>
<Story id="components-main-navigation--child-item" />
</Preview>

## How to Use

The main navigation child item component renders as link (hooked up to your app's routing).

Example of using this component in a template
```html
<main-navigation-child-item title="US Verifications" to="/us-verifications" />
```

## Props

<ArgsTable story={PRIMARY_STORY} />
109 changes: 109 additions & 0 deletions src/components/MainNavigation/MainNavigation.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import routeDecorator from '../../../.storybook/routeDecorator';

import MainNavigation from './MainNavigation.vue';
import MainNavigationItem from './MainNavigationItem.vue';
import MainNavigationChildItem from './MainNavigationChildItem.vue';
import mdx from './MainNavigation.mdx';
import iconOverview from '../../assets/images/iconOverview.svg';

export default {
title: 'Components/Main Navigation',
component: MainNavigation,
subcomponents: { MainNavigationItem, MainNavigationChildItem },
decorators: [
() => ({ template: '<div class="block bg-offWhite"><story /></div>' }),
routeDecorator()
],
parameters: {
docs: {
page: mdx
}
},
argTypes: {
iconSrc: {
table: {
disable: true
},
control: {
disable: true
}
}
}
};

const primaryTemplateStr = (args) => `
<main-navigation v-bind="$props">
<template v-slot="{ expanded }">
<main-navigation-item title="Overview" iconSrc="${args.iconSrc}" iconAltText="Overview icon" to="/overview" :expanded="expanded" />
<main-navigation-item title="Mail Analytics" iconSrc="${args.iconSrc}" iconAltText="Overview icon" to="/mail-analytics" :expanded="expanded" />
<main-navigation-item title="Address Books" iconSrc="${args.iconSrc}" iconAltText="Overview icon" to="/address-verification" :expanded="expanded" />
<main-navigation-item title="Address Verification" iconSrc="${args.iconSrc}" iconAltText="Overview icon" :expanded="expanded">
<main-navigation-child-item title="US Verifications" to="/us-verifications" />
<main-navigation-child-item title="Int'l Verifications" to="/intl-verifications" />
</main-navigation-item>
<main-navigation-item title="Print & Mail" iconSrc="${args.iconSrc}" iconAltText="Overview icon" :expanded="expanded">
<main-navigation-child-item title="Postcards" to="/postcards" />
<main-navigation-child-item title="Letters" to="/letters" />
</main-navigation-item>
</template>
</main-navigation>
`;

const Template = (args, { argTypes }) => ({
props: Object.keys(argTypes),
components: { MainNavigation, MainNavigationChildItem, MainNavigationItem },
template: primaryTemplateStr(args)
});

export const Primary = Template.bind({});
Primary.args = {
iconSrc: iconOverview
};
Primary.parameters = {
docs: {
source: {
code: primaryTemplateStr({ iconSrc: 'srcFilePath' })
}
}
};

const itemTemplateStr = '<main-navigation-item v-bind="$props" />';
const ItemTemplate = (args, { argTypes }) => ({
props: Object.keys(argTypes),
components: { MainNavigationItem },
template: itemTemplateStr
});
export const Item = ItemTemplate.bind({});
Item.args = {
title: 'Overview',
iconSrc: iconOverview,
iconAltText: 'Overview icon',
to: '/overview',
expanded: true
};
Item.parameters = {
docs: {
source: {
code: itemTemplateStr
}
}
};

const childTemplateStr = '<main-navigation-child-item v-bind="$props" />';
const ChildItemTemplate = (args, { argTypes }) => ({
props: Object.keys(argTypes),
components: { MainNavigationChildItem },
template: childTemplateStr
});
export const ChildItem = ChildItemTemplate.bind({});
ChildItem.args = {
title: 'Postcards',
to: '/postcards'
};
ChildItem.parameters = {
docs: {
source: {
code: childTemplateStr
}
}
};
61 changes: 61 additions & 0 deletions src/components/MainNavigation/MainNavigation.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<template>
<nav>
<ul
:class="[
'bg-white-100 h-screen',
{'cursor-pointer': collapsible},
{'expanded': collapsible && expanded},
{'collapsed': collapsible && !expanded}
]"
@[clickEvent]="animateDrawer"
>
<slot
:expanded="expanded"
/>
</ul>
</nav>
</template>

<script>
export default {
name: 'MainNavigation',
props: {
collapsible: {
type: Boolean,
default: true
}
},
data: function () {
return {
expanded: true
};
},
computed: {
clickEvent () {
return this.collapsible ? 'click' : null;
}
},
methods: {
animateDrawer () {
this.expanded = !this.expanded;
}
}
};
</script>

<style scoped lang="scss">
nav {
width: 100%;
@screen md {
ul:not(.collapsed) {
width: 222px;
}
ul.collapsed {
width: 70px;
}
}
}
</style>

0 comments on commit fc2a62b

Please sign in to comment.