Add breadcrumb component (#42)

* added breadcrumb, removed customSize from icon

* Use proptype + use name for for key

* Change name of arrow to divider

Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com>
This commit is contained in:
Nitwel
2020-02-17 21:30:16 +01:00
committed by GitHub
parent a071730068
commit 42ad64f5e7
6 changed files with 261 additions and 1 deletions

View File

@@ -0,0 +1,4 @@
import VBreadcrumb from './v-breadcrumb.vue';
export { VBreadcrumb };
export default VBreadcrumb;

View File

@@ -0,0 +1,28 @@
# Breadcrumb
```html
<v-breadcrumb :items="[{name: 'Collections', to: '/collections'}]" />
```
## Props
| Prop | Description | Default |
|-----------------|-----------------------------------------------------------|---------|
| `items` | An array of objects which information about each section | `[]` |
| `items.name` | The name which will be displayed | `''` |
| `items.to` | The reroute link | `''` |
| `items.disabled`| If the router link should be clickable | `false` |
| `items.icon` | Displays an icon with the given name in front of the name | `''` |
## Events
n/a
## Slots
n/a
## CSS Variables
| Prop | Default |
|---------------------------------|-------------------------------------|
| `--v-breadcrumb-color` | `var(--foreground-color-secondary)` |
| `--v-breadcrumb-color-hover` | `var(--foreground-color)` |
| `--v-breadcrumb-color-disabled` | `var(--foreground-color-tertiary)` |
| `--v-breadcrumb-divider-color` | `var(--foreground-color-tertiary)` |

View File

@@ -0,0 +1,37 @@
import { array, optionsKnob as options } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import Vue from 'vue';
import markdown from './v-breadcrumb.readme.md';
import VIcon from '../v-icon/';
import VBreadcrumb from './v-breadcrumb.vue';
import withPadding from '../../../.storybook/decorators/with-padding';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
const router = new VueRouter();
Vue.component('v-breadcrumb', VBreadcrumb);
Vue.component('v-icon', VIcon);
export default {
title: 'Components / Breadcrumb',
component: VBreadcrumb,
decorators: [withPadding],
parameters: {
notes: markdown
}
};
export const example = () => ({
router,
template: `
<v-breadcrumb :items="[
{disabled: false, to: 'Test', name: 'Collections', icon: 'home'},
{disabled: false, to: 'Test', name: 'Locations'},
{disabled: false, to: 'Test', name: 'New York'},
{disabled: true, to: 'Test', name: 'Berlin'}
]"/>
`
});

View File

@@ -0,0 +1,97 @@
import VueCompositionAPI from '@vue/composition-api';
import { mount, createLocalVue, Wrapper } from '@vue/test-utils';
import VBreadcrumb from './v-breadcrumb.vue';
import VIcon from '../v-icon/';
import VueRouter from 'vue-router';
const router = new VueRouter();
const localVue = createLocalVue();
localVue.use(VueCompositionAPI);
localVue.component('v-icon', VIcon);
localVue.use(VueRouter);
describe('Breadcrumb', () => {
let component: Wrapper<Vue>;
beforeEach(() => {
component = mount(VBreadcrumb, { localVue, router });
jest.useFakeTimers();
});
it('Renders the whole breadcrump', async () => {
component.setProps({
items: [
{ name: 'A', to: 'linkA' },
{ name: 'B', to: 'linkB' },
{ name: 'C', to: 'linkC' }
]
});
await component.vm.$nextTick();
const sections = component.findAll('.section a.section-link');
expect(sections.at(0).text()).toBe('A');
expect(sections.at(0).attributes().href).toContain('linkA');
expect(sections.at(1).text()).toBe('B');
expect(sections.at(1).attributes().href).toContain('linkB');
expect(sections.at(2).text()).toBe('C');
expect(sections.at(2).attributes().href).toContain('linkC');
});
it('Renders breadcrumb with icon ', async () => {
component.setProps({
items: [
{ name: 'A', to: 'linkA' },
{ name: 'B', to: 'linkB', icon: 'home' },
{ name: 'C', to: 'linkC', icon: 'add' }
]
});
await component.vm.$nextTick();
const sections = component.findAll('.section a.section-link');
expect(
sections
.at(0)
.find('.v-icon')
.exists()
).toBe(false);
expect(
sections
.at(1)
.find('.v-icon')
.text()
).toBe('home');
expect(
sections
.at(2)
.find('.v-icon')
.text()
).toBe('add');
});
it('Renders breadcrumb with disabled section ', async () => {
component.setProps({
items: [
{ name: 'A', to: 'linkA' },
{ name: 'B', to: 'linkB', disabled: true },
{ name: 'C', to: 'linkC' }
]
});
await component.vm.$nextTick();
expect(
component
.findAll('.section')
.at(1)
.classes()
).toContain('disabled');
});
});

View File

@@ -0,0 +1,94 @@
<template>
<span class="v-breadcrumb">
<span
v-for="(item, index) in items"
:key="item.name"
class="section"
:class="{ disabled: item.disabled }"
>
<v-icon v-if="index > 0" name="chevron_right" small />
<router-link v-if="!item.disabled" :to="item.to" class="section-link">
<v-icon v-if="item.icon" :name="item.icon" />
{{ item.name }}
</router-link>
<span v-else class="section-link">
<v-icon v-if="item.icon" :name="item.icon" />
{{ item.name }}
</span>
</span>
</span>
</template>
<script lang="ts">
import { createComponent, ref, PropType } from '@vue/composition-api';
interface Breadcrumb {
to: string;
name: string;
icon?: string;
disabled?: boolean;
}
export default createComponent({
props: {
items: {
type: Array as PropType<Breadcrumb[]>,
default: () => []
}
},
setup(props) {
return {};
}
});
</script>
<style lang="scss" scoped>
.v-breadcrumb {
--v-breadcrumb-color: var(--foreground-color-secondary);
--v-breadcrumb-color-hover: var(--foreground-color);
--v-breadcrumb-color-disabled: var(--foreground-color-tertiary);
--v-breadcrumb-divider-color: var(--foreground-color-tertiary);
display: inline-block;
.section {
display: inline-flex;
align-items: center;
.v-icon {
--v-icon-color: var(--v-breadcrumb-divider-color);
margin: 0 4px;
}
&-link {
display: inline-flex;
align-items: center;
color: var(--v-breadcrumb-color);
text-decoration: none;
.v-icon {
--v-icon-color: var(--v-breadcrumb-color);
margin: 0 2px;
}
&:hover {
color: var(--v-breadcrumb-color-hover);
.v-icon {
--v-icon-color: var(--v-breadcrumb-color-hover);
}
}
}
&.disabled {
.section-link,
.section-link:hover,
.section-link .v-icon {
color: var(--v-breadcrumb-color-disabled);
cursor: default;
}
}
}
}
</style>

View File

@@ -6,7 +6,7 @@
@click="emitClick"
>
<component v-if="customIconName" :is="customIconName" />
<i v-else :style="{ fontSize: customSize }" :class="{ outline }">{{ name }}</i>
<i v-else :class="{ outline }">{{ name }}</i>
</span>
</template>