Files
keeplearning66 8b7d8eacf5 feat(components): [timeline] support mode prop (#22929)
* feat(components): [timeline] support item-placement prop

* feat: update

* feat: update

* feat: update

* feat: update

* feat: update

* test: update

* feat: update

* feat: update

* feat: update

* docs: update version number

* feat: update

* feat: revert padding

* feat: update

* chore: format

* docs: update version number

* chore: use ExtractPublicPropTypes

* Update docs/en-US/component/timeline.md

Co-authored-by: rzzf <cszhjh@gmail.com>

* style: use content-box

* docs: add an example

* style: add padding-right: 0

* docs: remove max-width: 600px

* feat: add alternate-left and alternate-right

* feat: modify the value of mode

---------

Co-authored-by: rzzf <cszhjh@gmail.com>
2026-01-08 21:13:52 +08:00

310 lines
8.3 KiB
TypeScript

import { markRaw, nextTick, ref } from 'vue'
import { mount } from '@vue/test-utils'
import { describe, expect, test } from 'vitest'
import { MoreFilled } from '@element-plus/icons-vue'
import TimeLine from '../src/timeline'
import TimeLineItem from '../src/timeline-item.vue'
import type { TimelineItemProps } from '../src/timeline-item'
const iconMoreFilled = markRaw(MoreFilled)
const activities: {
content: string
timestamp: string
placement?: TimelineItemProps['placement']
hideTimestamp?: boolean
}[] = [
{
content: 'Step 1: xxxxxx',
timestamp: '2018-04-11',
},
{
content: 'Step 2: xxxxxx',
timestamp: '2018-04-13',
},
{
content: 'Step 3: xxxxxx',
timestamp: '2018-04-15',
},
]
describe('TimeLine.vue', () => {
test('create', () => {
const wrapper = mount(() => (
<TimeLine>
{activities.map((activity, index) => (
<TimeLineItem key={index}>{activity.content}</TimeLineItem>
))}
</TimeLine>
))
const contentWrappers = wrapper.findAll('.el-timeline-item__content')
contentWrappers.forEach((content, index) => {
expect(content.text()).toEqual(activities[index].content)
})
})
test('placement', () => {
activities[0].placement = 'top'
const wrapper = mount(() => (
<TimeLine>
{activities.map((activity, index) => (
<TimeLineItem key={index} placement={activity.placement} />
))}
</TimeLine>
))
const timestampWrapper = wrapper.findAll('.el-timeline-item__timestamp')[0]
expect(timestampWrapper.classes('is-top')).toBe(true)
})
describe('mode', () => {
test('mode-start', () => {
const wrapper = mount(() => (
<TimeLine>
{activities.map((activity, index) => (
<TimeLineItem key={index}>{activity.content}</TimeLineItem>
))}
</TimeLine>
))
const timeline = wrapper.find('.el-timeline')
const timelineItems = wrapper.findAll('.el-timeline-item')
expect(timeline.classes('is-start')).toBe(true)
timelineItems.forEach((item) => {
expect(item.classes('is-start')).toBe(true)
})
})
test('mode-alternate', () => {
const wrapper = mount(() => (
<TimeLine mode="alternate">
{activities.map((activity, index) => (
<TimeLineItem key={index}>{activity.content}</TimeLineItem>
))}
</TimeLine>
))
const timeline = wrapper.find('.el-timeline')
const timelineItems = wrapper.findAll('.el-timeline-item')
expect(timeline.classes('is-alternate')).toBe(true)
timelineItems.forEach((item) => {
expect(item.classes('is-alternate')).toBe(true)
})
})
test('mode-alternate-reverse', () => {
const wrapper = mount(() => (
<TimeLine mode="alternate-reverse">
{activities.map((activity, index) => (
<TimeLineItem key={index}>{activity.content}</TimeLineItem>
))}
</TimeLine>
))
const timeline = wrapper.find('.el-timeline')
const timelineItems = wrapper.findAll('.el-timeline-item')
expect(timeline.classes('is-alternate-reverse')).toBe(true)
timelineItems.forEach((item) => {
expect(item.classes('is-alternate-reverse')).toBe(true)
})
})
test('mode-end', () => {
const wrapper = mount(() => (
<TimeLine mode="end">
{activities.map((activity, index) => (
<TimeLineItem key={index}>{activity.content}</TimeLineItem>
))}
</TimeLine>
))
const timeline = wrapper.find('.el-timeline')
const timelineItems = wrapper.findAll('.el-timeline-item')
expect(timeline.classes('is-end')).toBe(true)
timelineItems.forEach((item) => {
expect(item.classes('is-end')).toBe(true)
})
})
})
test('hide-timestamp', () => {
activities[0].hideTimestamp = true
const wrapper = mount(() => (
<TimeLine>
{activities.map((activity, index) => (
<TimeLineItem key={index} hide-timestamp={activity.hideTimestamp} />
))}
</TimeLine>
))
const timestampWrappers = wrapper.findAll('.el-timeline-item__timestamp')
expect(timestampWrappers.length).toEqual(2)
})
test('color', () => {
const wrapper = mount(() => (
<TimeLine>
<TimeLineItem color="#f00" />
</TimeLine>
))
const vm = wrapper.vm
const nodeElm = vm.$el.querySelector('.el-timeline-item__node')
expect(nodeElm.style.backgroundColor).toEqual('rgb(255, 0, 0)')
})
test('type', () => {
const wrapper = mount(() => (
<TimeLine>
<TimeLineItem type="primary" />
</TimeLine>
))
const nodeWrapper = wrapper.find('.el-timeline-item__node')
expect(nodeWrapper.classes('el-timeline-item__node--primary')).toBe(true)
})
test('size', () => {
const wrapper = mount(() => (
<TimeLine>
<TimeLineItem size="large" />
</TimeLine>
))
const nodeWrapper = wrapper.find('.el-timeline-item__node')
expect(nodeWrapper.classes('el-timeline-item__node--large')).toBe(true)
})
test('icon', () => {
const wrapper = mount(() => (
<TimeLine>
<TimeLineItem icon={iconMoreFilled} />
</TimeLine>
))
expect(wrapper.find('.el-timeline-item__icon').exists()).toBe(true)
expect(wrapper.findComponent(MoreFilled).exists()).toBe(true)
})
test('hollow', () => {
const wrapper = mount(() => (
<TimeLine>
<TimeLineItem hollow />
</TimeLine>
))
const nodeWrapper = wrapper.find('.el-timeline-item__node')
expect(nodeWrapper.classes('is-hollow')).toBe(true)
})
test('dot', () => {
const wrapper = mount(() => (
<TimeLine>
<TimeLineItem
v-slots={{
dot: () => 'dot',
}}
/>
</TimeLine>
))
const dotWrapper = wrapper.find('.el-timeline-item__dot')
expect(dotWrapper.text()).toEqual('dot')
expect(wrapper.find('.el-timeline-item__node').exists()).toBe(false)
})
test('center', () => {
const wrapper = mount(() => (
<TimeLine>
{activities.map((_, index) => (
<TimeLineItem key={index} center={index === 1} />
))}
</TimeLine>
))
const timestampWrappers = wrapper.findAll('.el-timeline-item')
expect(timestampWrappers[1].classes()).toContain('el-timeline-item__center')
})
test('The content that is not a timeline item in the sub-slot should be rendered', () => {
const wrapper = mount(() => (
<TimeLine>
<div class="custom-content">Custom Content</div>
</TimeLine>
))
// It appears there's a problem with the DOM hierarchy structure being rendered by the test framework.
expect(wrapper.element.innerHTML).toBe('Custom Content')
})
describe('reverse', () => {
test('v-for children', async () => {
const wrapper = mount({
template: `
<TimeLine :reverse="reverse">
<TimeLineItem
v-for="(item, index) in activities"
:key="index"
>
{{ item.content }}
</TimeLineItem>
</TimeLine>
`,
components: {
TimeLine,
TimeLineItem,
},
data() {
return {
reverse: true,
activities,
}
},
})
let firstTimelineItem = wrapper.find('.el-timeline-item__content')
expect(firstTimelineItem.text()).toMatchInlineSnapshot(`"Step 3: xxxxxx"`)
await wrapper.setData({ reverse: false })
firstTimelineItem = wrapper.find('.el-timeline-item__content')
expect(firstTimelineItem.text()).toMatchInlineSnapshot(`"Step 1: xxxxxx"`)
})
test('manual children', async () => {
const reverse = ref(true)
const wrapper = mount(() => (
<TimeLine reverse={reverse.value}>
<TimeLineItem>Step 1: xxxxxx</TimeLineItem>
<TimeLineItem>Step 2: xxxxxx</TimeLineItem>
<TimeLineItem>Step 3: xxxxxx</TimeLineItem>
</TimeLine>
))
let firstTimelineItem = wrapper.find('.el-timeline-item__content')
expect(firstTimelineItem.text()).toMatchInlineSnapshot(`"Step 3: xxxxxx"`)
reverse.value = false
await nextTick()
firstTimelineItem = wrapper.find('.el-timeline-item__content')
expect(firstTimelineItem.text()).toMatchInlineSnapshot(`"Step 1: xxxxxx"`)
})
})
})