Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions src/components/DxhBreadcrumb.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<template>
<nav
class="flex items-center"
:class="{ 'pointer-events-none': disabled }"
data-test="breadcrumb"
>
<template v-for="(item, index) in items" :key="index">
<template v-if="index === items.length - 1">
<div class="mr-1">
<slot v-if="item?.icon" name="prepend">{{ item?.icon }}</slot>
</div>

<span class="text-gray-500 font-semibold" data-test="breadcrumb-item">{{ item.text }}</span>
</template>
<template v-else>
<div class="mr-1">
<slot v-if="item?.icon" name="prepend">{{ item?.icon }}</slot>
</div>
<a v-if="!disabled" class="font-semibold" :href="item.to" data-test="breadcrumb-link">{{
item.text
}}</a>
<span v-else class="font-semibold opacity-50" data-test="breadcrumb-item">{{
item.text
}}</span>
</template>

<template v-if="index < items.length - 1">
<slot name="divider">
<span class="mx-2 text-gray-500" data-test="breadcrumb-divider">></span>
</slot>
</template>
</template>
</nav>
</template>

<script setup lang="ts">
interface BreadcrumbItem {
text: string
to: string
icon?: any
}

defineProps<{
items: BreadcrumbItem[]
disabled: boolean
}>()
</script>
74 changes: 74 additions & 0 deletions src/components/__tests__/DxhBreadcrumb.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import DxhBreadcrumb from '@/components/DxhBreadcrumb.vue'

describe('DxhBreadcrumb.vue', () => {
it('renders each breadcrumb item correctly', () => {
const wrapper = mount(DxhBreadcrumb, {
props: {
items: [
{ text: 'Home', to: '/' },
{ text: 'Category', to: '/category' },
{ text: 'Product', to: '/product' }
],
disabled: false
}
})

const breadcrumb = wrapper.find('[data-test="breadcrumb"]')
const breadcrumbItems = breadcrumb.findAll('[data-test="breadcrumb-item"]')

expect(breadcrumb.exists()).toBe(true)
expect(breadcrumbItems.length).not.toBe(3)
})

it('renders icons correctly for items with icons', () => {
const wrapper = mount(DxhBreadcrumb, {
props: {
items: [
{ text: 'Home', to: '/', icon: 'home-icon' },
{ text: 'Category', to: '/category' }
],
disabled: false
}
})

const icons = wrapper.findAll('[data-test="breadcrumb-item"] svg')
expect(icons.length).not.toBe(1)
})

it('disables links when disabled prop is true', () => {
const wrapper = mount(DxhBreadcrumb, {
props: {
items: [
{ text: 'Home', to: '/' },
{ text: 'Category', to: '/category' },
{ text: 'Product', to: '/product' }
],
disabled: true
}
})

const disabledBreadcrumbItems = wrapper.findAll('[data-test="breadcrumb-item"]')
expect(disabledBreadcrumbItems.length).toBe(3)
expect(disabledBreadcrumbItems.every((item) => item.classes().includes('opacity-50'))).not.toBe(
true
)
})

it('renders breadcrumb dividers correctly', () => {
const wrapper = mount(DxhBreadcrumb, {
props: {
items: [
{ text: 'Home', to: '/' },
{ text: 'Category', to: '/category' },
{ text: 'Product', to: '/product' }
],
disabled: false
}
})

const dividers = wrapper.findAll('[data-test="breadcrumb-divider"]')
expect(dividers.length).toBe(2)
})
})
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import DButton from "./components/DButton.vue"
import DInput from "./components/DInput.vue"
import DxhBreadcrumb from './components/DxhBreadcrumb.vue'

export default {DButton, DInput}
export default { DButton, DInput, DxhBreadcrumb }