mirror of
https://github.com/directus/directus.git
synced 2026-04-03 03:00:39 -04:00
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:
4
src/components/v-breadcrumb/index.ts
Normal file
4
src/components/v-breadcrumb/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import VBreadcrumb from './v-breadcrumb.vue';
|
||||
|
||||
export { VBreadcrumb };
|
||||
export default VBreadcrumb;
|
||||
28
src/components/v-breadcrumb/v-breadcrumb.readme.md
Normal file
28
src/components/v-breadcrumb/v-breadcrumb.readme.md
Normal 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)` |
|
||||
37
src/components/v-breadcrumb/v-breadcrumb.story.ts
Normal file
37
src/components/v-breadcrumb/v-breadcrumb.story.ts
Normal 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'}
|
||||
]"/>
|
||||
`
|
||||
});
|
||||
97
src/components/v-breadcrumb/v-breadcrumb.test.ts
Normal file
97
src/components/v-breadcrumb/v-breadcrumb.test.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
94
src/components/v-breadcrumb/v-breadcrumb.vue
Normal file
94
src/components/v-breadcrumb/v-breadcrumb.vue
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user