feat(time-line): add time-line component

fix #56
This commit is contained in:
bastarder 2020-07-29 16:57:17 +08:00 committed by jeremywu
parent 619dedb73e
commit 55afacad18
9 changed files with 425 additions and 2 deletions

View File

@ -4,9 +4,10 @@ import ElBadge from '@element-plus/badge'
import ElCard from '@element-plus/card'
import ElTag from '@element-plus/tag'
import ElDivider from '@element-plus/divider'
import ElTimeLine from '@element-plus/time-line'
export {
ElButton, ElBadge, ElCard, ElDivider, ElTag,
ElButton, ElBadge, ElCard, ElDivider, ElTag, ElTimeLine,
}
export default function install(app: App): void {
@ -15,4 +16,5 @@ export default function install(app: App): void {
ElCard(app)
ElTag(app)
ElDivider(app)
ElTimeLine(app)
}

View File

@ -17,6 +17,8 @@
"@element-plus/badge": "^0.0.0",
"@element-plus/button": "^0.0.0",
"@element-plus/card": "^0.0.0",
"@element-plus/tag": "^0.0.0"
"@element-plus/tag": "^0.0.0",
"@element-plus/time-line": "^0.0.0",
"@element-plus/divider": "^0.0.0"
}
}

View File

@ -0,0 +1,211 @@
import { mount } from '@vue/test-utils'
import TimeLine from '../src/index.vue'
import TimeLineItem from '../src/item.vue'
import { defineComponent } from 'vue'
const Component = defineComponent({
components: {
'el-timeline': TimeLine,
'el-timeline-item': TimeLineItem,
},
props: [],
data() {
return {
activities: [{
content: 'Step 1: xxxxxx',
timestamp: '2018-04-11',
}, {
content: 'Step 2: xxxxxx',
timestamp: '2018-04-13',
}, {
content: 'Step 3: xxxxxx',
timestamp: '2018-04-15',
}],
}
},
template: `
<el-timeline>
<el-timeline-item
v-for="(activity, index) in activities"
:key="index"
:timestamp="activity.timestamp">
{{activity.content}}
</el-timeline-item>
</el-timeline>
`,
})
describe('TimeLine.vue', () => {
test('create', () => {
const wrapper = mount(Component)
const vm = wrapper.vm
const contentWrappers = wrapper.findAll('.el-timeline-item__content')
contentWrappers.forEach((content, index) => {
expect(content.text()).toEqual(vm.activities[index].content)
})
const timestampWrappers = wrapper.findAll('.el-timeline-item__timestamp')
timestampWrappers.forEach((timestamp, index) => {
expect(timestamp.text()).toEqual(vm.activities[index].timestamp)
})
})
test('placement', () => {
const wrapper = mount({
...Component,
template: `
<el-timeline>
<el-timeline-item
v-for="(activity, index) in activities"
:key="index"
:timestamp="activity.timestamp"
:placement="activity.placement">
{{activity.content}}
</el-timeline-item>
</el-timeline>
`,
data() {
return {
activities: [{
content: 'Step 1: xxxxxx',
timestamp: '2018-04-11',
placement: 'top',
}, {
content: 'Step 2: xxxxxx',
timestamp: '2018-04-13',
}, {
content: 'Step 3: xxxxxx',
timestamp: '2018-04-15',
}],
}
},
})
const timestampWrapper = wrapper.findAll('.el-timeline-item__timestamp')[0]
expect(timestampWrapper.classes('is-top')).toBe(true)
})
test('hide-timestamp', () => {
const wrapper = mount({
...Component,
template: `
<el-timeline>
<el-timeline-item
v-for="(activity, index) in activities"
:key="index"
:timestamp="activity.timestamp"
:hide-timestamp="activity.hideTimestamp">
{{activity.content}}
</el-timeline-item>
</el-timeline>
`,
data() {
return {
activities: [{
content: 'Step 1: xxxxxx',
timestamp: '2018-04-11',
hideTimestamp: true,
}, {
content: 'Step 2: xxxxxx',
timestamp: '2018-04-13',
}, {
content: 'Step 3: xxxxxx',
timestamp: '2018-04-15',
}],
}
},
})
const timestampWrappers = wrapper.findAll('.el-timeline-item__timestamp')
expect(timestampWrappers.length).toEqual(2)
})
test('color', () => {
const wrapper = mount({
...Component,
template: `
<el-timeline>
<el-timeline-item
timestamp="2018-04-11"
color="#f00">
Step 1: xxxxxx
</el-timeline-item>
</el-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({
...Component,
template: `
<el-timeline>
<el-timeline-item
timestamp="2018-04-11"
type="primary">
Step 1: xxxxxx
</el-timeline-item>
</el-timeline>
`,
})
const nodeWrapper = wrapper.find('.el-timeline-item__node')
expect(nodeWrapper.classes('el-timeline-item__node--primary')).toBe(true)
})
test('size', () => {
const wrapper = mount({
...Component,
template: `
<el-timeline>
<el-timeline-item
timestamp="2018-04-11"
type="large">
Step 1: xxxxxx
</el-timeline-item>
</el-timeline>
`,
})
const nodeWrapper = wrapper.find('.el-timeline-item__node')
expect(nodeWrapper.classes('el-timeline-item__node--large')).toBe(true)
})
test('icon', () => {
const wrapper = mount({
...Component,
template: `
<el-timeline>
<el-timeline-item
timestamp="2018-04-11"
icon="el-icon-more">
Step 1: xxxxxx
</el-timeline-item>
</el-timeline>
`,
})
const nodeWrapper = wrapper.find('.el-timeline-item__icon')
expect(nodeWrapper.classes('el-icon-more')).toBe(true)
})
test('dot', () => {
const wrapper = mount({
...Component,
template: `
<el-timeline>
<el-timeline-item
timestamp="2018-04-11"
>
<template v-slot:dot>
dot
</template>
</el-timeline-item>
</el-timeline>
`,
})
const dotWrapper = wrapper.find('.el-timeline-item__dot')
expect(dotWrapper.text()).toEqual('dot')
expect(wrapper.find('.el-timeline-item__node').exists()).toBe(false)
})
})

View File

@ -0,0 +1,46 @@
<template>
<div class="block">
<el-time-line>
<el-time-line-item
v-for="(activity, index) in activities"
:key="index"
:timestamp="activity.timestamp"
:placement="activity.placement"
:hide-timestamp="activity.hideTimestamp"
:type="activity.type"
:icon="activity.icon"
>
{{ activity.content }}
</el-time-line-item>
</el-time-line>
</div>
</template>
<script lang='ts'>
import { defineComponent, reactive } from 'vue'
export default defineComponent({
setup() {
return reactive({
activities: [{
content: 'Step 1: xxxxxx',
timestamp: '2018-04-15',
placement: 'top',
}, {
content: 'Step 2: xxxxxx',
timestamp: '2018-04-13',
hideTimestamp: true,
}, {
content: 'Step 3: xxxxxx',
timestamp: '2018-04-11',
type: 'large',
icon: 'el-icon-more',
},{
content: 'Step 4: xxxxxx',
timestamp: '2018-07-11',
type: 'primary',
}],
})
},
})
</script>

View File

@ -0,0 +1,6 @@
export { default as BasicUsage } from './basic.vue'
export default {
title: 'TimeLine',
}

View File

@ -0,0 +1,7 @@
import { App } from 'vue'
import TimeLine from './src/index.vue'
import TimeLineItem from './src/item.vue'
export default (app: App): void => {
app.component(TimeLine.name, TimeLine)
app.component(TimeLineItem.name, TimeLineItem)
}

View File

@ -0,0 +1,12 @@
{
"name": "@element-plus/time-line",
"version": "0.0.0",
"main": "dist/index.js",
"license": "MIT",
"peerDependencies": {
"vue": "^3.0.0-rc.1"
},
"devDependencies": {
"@vue/test-utils": "^2.0.0-beta.0"
}
}

View File

@ -0,0 +1,40 @@
<script lang='ts'>
import { h, provide, defineComponent } from 'vue'
export default defineComponent({
name: 'ElTimeLine',
setup(props, ctx) {
provide('timeline', ctx)
/**
* Maybe ,this component will not support prop 'reverse', why ?
*
* Example 1:
* <component-a>
* <div>1</div>
* <div>2</div>
* </component-a>
*
* Example 2:
* <component-a>
* <div v-for="i in 2" :key="i">{{ i }}</div>
* </component-a>
*
* 'slots.default()' value in example 1 just like [Vnode, Vnode]
* 'slots.default()' value in example 2 just like [Vnode]
*
* so i can't reverse the slots, when i use 'v-for' directive.
*/
return () => {
return h(
'ul',
{
class: { 'el-timeline': true },
},
ctx.slots.default?.(),
)
}
},
})
</script>

View File

@ -0,0 +1,97 @@
<template>
<li class="el-timeline-item">
<div class="el-timeline-item__tail"></div>
<div
v-if="!$slots.dot"
class="el-timeline-item__node"
:class="[
`el-timeline-item__node--${size || ''}`,
`el-timeline-item__node--${type || ''}`
]"
:style="{
backgroundColor: color
}"
>
<i
v-if="icon"
class="el-timeline-item__icon"
:class="icon"
></i>
</div>
<div v-if="$slots.dot" class="el-timeline-item__dot">
<slot name="dot"></slot>
</div>
<div class="el-timeline-item__wrapper">
<div
v-if="!hideTimestamp && placement === 'top'"
class="el-timeline-item__timestamp is-top"
>
{{ timestamp }}
</div>
<div class="el-timeline-item__content">
<slot></slot>
</div>
<div
v-if="!hideTimestamp && placement === 'bottom'"
class="el-timeline-item__timestamp is-bottom"
>
{{ timestamp }}
</div>
</div>
</li>
</template>
<script lang='ts'>
import { inject, defineComponent } from 'vue'
interface ITimeLineItemProps {
timestamp: string,
hideTimestamp: boolean,
placement: string,
type: string,
color: string,
size: string,
icon: string,
}
export default defineComponent({
name: 'ElTimeLineItem',
props: {
timestamp: {
type: String,
default: '',
},
hideTimestamp: {
type: Boolean,
default: false,
},
placement: {
type: String,
default: 'bottom',
},
type: {
type: String,
default: '',
},
color: {
type: String,
default: '',
},
size: {
type: String,
default: 'normal',
},
icon: {
type: String,
default: '',
},
},
setup() {
inject('timeline')
},
})
</script>