提交 02cc4337 编写于 作者: A Amy 提交者: doly mood

scroll (watch pullup& pulldown) + slide(support vertical)

* <update> better-scroll 1.9.1 + scroll + slide

* add activated and deactivated hook for keep-alive

* [add] dispatchTap

* add vertical loop for slide and make some opitmize to scroll
上级 0caba1b1
......@@ -202,18 +202,6 @@
customList(newVal) {
this.items = newVal ? _foods : _data
},
pullDownRefreshObj: {
handler() {
this.rebuildScroll()
},
deep: true
},
pullUpLoadObj: {
handler() {
this.rebuildScroll()
},
deep: true
},
startY() {
this.rebuildScroll()
}
......
......@@ -3,17 +3,17 @@
<div slot="content">
<div ref="slideWrapper" class="slide-container">
<cube-slide
ref="slide"
:data="items"
:initial-index="initialIndex"
:loop="loop"
:auto-play="autoPlay"
:interval="interval"
:threshold="threshold"
:speed="speed"
:allow-vertical="allowVertical"
@change="changePage"
@click="clickPage">
ref="slide"
:data="items"
:initial-index="initialIndex"
:loop="loop"
:auto-play="autoPlay"
:interval="interval"
:threshold="threshold"
:speed="speed"
:allow-vertical="allowVertical"
@change="changePage"
@click="clickPage">
<template v-if="dotsSlot" slot="dots" slot-scope="props">
<span class="my-dot" :class="{active: props.current === index}" v-for="(item, index) in props.dots">{{index + 1}}</span>
</template>
......@@ -23,7 +23,7 @@
<div class="option-list">
<div class="group">
<input-option class="item" name="InitialIndex" :value="initialIndex"
@update:value="updateInitialIndex"></input-option>
@update:value="updateInitialIndex"></input-option>
</div>
<div class="group">
<switch-option class="item" name="Loop" :value="loop"
......@@ -33,15 +33,15 @@
<switch-option class="item" name="Auto Play" :value="autoPlay"
@update:value="updateAutoPlay"></switch-option>
<input-option v-if="autoPlay" class="item sub first last" name="interval" :value="interval"
@update:value="updateInterval"></input-option>
@update:value="updateInterval"></input-option>
</div>
<div class="group">
<input-option class="item" name="Threshold" :value="threshold"
@update:value="updateThreshold"></input-option>
@update:value="updateThreshold"></input-option>
</div>
<div class="group">
<input-option class="item" name="Speed" :value="speed"
@update:value="updateSpeed"></input-option>
@update:value="updateSpeed"></input-option>
</div>
<div class="group">
<switch-option class="item" name="Allow Vertical" :value="allowVertical"
......@@ -62,9 +62,9 @@
</template>
<script type="text/ecmascript-6">
import CubePage from '../components/cube-page.vue'
import SwitchOption from '../components/switch-option'
import InputOption from '../components/input-option'
import CubePage from '../../components/cube-page.vue'
import SwitchOption from '../../components/switch-option'
import InputOption from '../../components/input-option'
const item3 = {
url: 'http://www.didichuxing.com/',
......
<template>
<cube-page type="slide" title="Slide">
<div slot="content">
<cube-button-group>
<cube-button @click="goTo('vertical')">Vertical</cube-button>
<cube-button @click="goTo('horizontal')">Horizontal</cube-button>
</cube-button-group>
<cube-view></cube-view>
</div>
</cube-page>
</template>
<script type="text/ecmascript-6">
import CubePage from 'example/components/cube-page.vue'
import CubeButtonGroup from 'example/components/cube-button-group.vue'
import CubeView from 'example/components/cube-view.vue'
export default {
components: {
CubePage,
CubeButtonGroup,
CubeView
},
methods: {
goTo(subPath) {
this.$router.push('/slide/' + subPath)
}
}
}
</script>
<style lang="stylus" rel="stylesheet/stylus">
</style>
<template>
<cube-page type="slide-view" title="Slide" class="option-demo">
<div slot="content">
<div ref="slideWrapper" class="slide-container">
<cube-slide
ref="slide"
:data="items"
:loop="loop"
:showDots="false"
direction="vertical"
>
</cube-slide>
</div>
</div>
</cube-page>
</template>
<script type="text/ecmascript-6">
import CubePage from '../../components/cube-page.vue'
export default{
data() {
return {
items: [
{
url: 'http://www.didichuxing.com/',
image: 'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3978382133,2176310874&fm=27&gp=0.jpg'
}, {
url: 'http://www.didichuxing.com/',
image: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3780304411,1076045325&fm=15&gp=0.jpg'
},
{
url: 'http://www.didichuxing.com/',
image: 'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=56274798,4163877688&fm=27&gp=0.jpg'
}, {
url: 'http://www.didichuxing.com/',
image: 'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3124201171,3290669243&fm=15&gp=0.jpg'
},
{
url: 'http://www.didichuxing.com/',
image: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1309802367,1770965841&fm=27&gp=0.jpg'
}
],
loop: true
}
},
components: {
CubePage
}
}
</script>
<style lang="stylus" rel="stylesheet/stylus">
.slide-container
height: 380px
margin-bottom: 15px
transform: translateZ(0px)
border-radius: 2px
overflow: hidden
box-shadow: 0 2px 9px #ddd
.cube-slide-dots
.my-dot
height: auto
font-size: 12px
background: none
&.active
color: #fc9153
</style>
......@@ -22,7 +22,6 @@ import Select from '../pages/select.vue'
import Dialog from '../pages/dialog.vue'
import ActionSheet from '../pages/action-sheet.vue'
import Scroll from '../pages/scroll.vue'
import Slide from '../pages/slide.vue'
import IndexList from '../pages/index-list/index-list.vue'
import IndexListDefault from '../pages/index-list/default.vue'
import IndexListCustom from '../pages/index-list/custom.vue'
......@@ -31,6 +30,9 @@ import Validator from '../pages/validator.vue'
import Swipe from '../pages/swipe/index.vue'
import SwipeDefault from '../pages/swipe/default.vue'
import SwipeCustom from '../pages/swipe/custom.vue'
import Slide from '../pages/slide/index.vue'
import SlideVertical from '../pages/slide/vertical.vue'
import SlideHorizontal from '../pages/slide/horizontal.vue'
const routes = [
{
......@@ -131,10 +133,6 @@ const routes = [
path: '/scroll',
component: Scroll
},
{
path: '/slide',
component: Slide
},
{
path: '/index-list',
component: IndexList,
......@@ -170,6 +168,20 @@ const routes = [
component: SwipeCustom
}
]
},
{
path: '/slide',
component: Slide,
children: [
{
path: 'vertical',
component: SlideVertical
},
{
path: 'horizontal',
component: SlideHorizontal
}
]
}
]
......
......@@ -1234,9 +1234,9 @@
}
},
"better-scroll": {
"version": "1.8.4",
"resolved": "https://registry.npmjs.org/better-scroll/-/better-scroll-1.8.4.tgz",
"integrity": "sha512-UxuCf3RubCUs94m4ZzIy3fUc71OvmEHpWFTyVRfy+GlB+SmG4HyMfeTLgM2otYyNIMgXD+e3RLQuREdFqjbJDg==",
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/better-scroll/-/better-scroll-1.9.1.tgz",
"integrity": "sha512-sFZbZL2GmsFIJH8Q6JTsdWIXIBuftKLFWQieHKKxu2+QW7IuPGI2KQ+wr1tVSznGzdHIJj0OkKHghliMAG96BQ==",
"requires": {
"babel-runtime": "6.26.0"
}
......
......@@ -39,7 +39,7 @@
],
"license": "Apache",
"dependencies": {
"better-scroll": "^1.8.4"
"better-scroll": "^1.9.1"
},
"devDependencies": {
"autoprefixer": "^7.1.1",
......
......@@ -51,6 +51,7 @@
const DIRECTION_H = 'horizontal'
const DIRECTION_V = 'vertical'
const DEFAULT_REFRESH_TXT = 'Refresh success'
const PULL_DOWN_ELEMENT_INITIAL_HEIGHT = -50
const EVENT_SCROLL = 'scroll'
const EVENT_BEFORE_SCROLL_START = 'before-scroll-start'
......@@ -110,12 +111,12 @@
}
},
computed: {
pullUpLoad() {
return this.options.pullUpLoad
},
pullDownRefresh() {
return this.options.pullDownRefresh
},
pullUpLoad() {
return this.options.pullUpLoad
},
pullUpTxt() {
const pullUpLoad = this.pullUpLoad
const txt = pullUpLoad && pullUpLoad.txt
......@@ -129,8 +130,56 @@
return (pullDownRefresh && pullDownRefresh.txt) || DEFAULT_REFRESH_TXT
}
},
created() {
this.pullDownInitTop = -50
watch: {
data() {
setTimeout(() => {
this.forceUpdate(true)
}, this.refreshDelay)
},
pullDownRefresh: {
handler(newVal, oldVal) {
if (newVal) {
this.scroll.openPullDown(newVal)
if (!oldVal) {
this._onPullDownRefresh()
this._calculateMinHeight()
}
}
if (!newVal && oldVal) {
this.scroll.closePullDown()
this._offPullDownRefresh()
this._calculateMinHeight()
}
},
deep: true
},
pullUpLoad: {
handler(newVal, oldVal) {
if (newVal) {
this.scroll.openPullUp(newVal)
if (!oldVal) {
this._onPullUpLoad()
this._calculateMinHeight()
}
}
if (!newVal && oldVal) {
this.scroll.closePullUp()
this._offPullUpLoad()
this._calculateMinHeight()
}
},
deep: true
}
},
activated() {
/* istanbul ignore next */
this.enable()
},
deactivated() {
/* istanbul ignore next */
this.disable()
},
mounted() {
this.$nextTick(() => {
......@@ -164,11 +213,11 @@
}
if (this.pullDownRefresh) {
this._initPullDownRefresh()
this._onPullDownRefresh()
}
if (this.pullUpLoad) {
this._initPullUpLoad()
this._onPullUpLoad()
}
},
disable() {
......@@ -182,7 +231,7 @@
this.scroll && this.scroll.refresh()
},
destroy() {
this.scroll.destroy()
this.scroll && this.scroll.destroy()
},
scrollTo() {
this.scroll && this.scroll.scrollTo.apply(this.scroll, arguments)
......@@ -193,7 +242,7 @@
clickItem(item) {
this.$emit(EVENT_CLICK, item)
},
forceUpdate(dirty) {
forceUpdate(dirty = false) {
if (this.pullDownRefresh && this.isPullingDown) {
this.isPullingDown = false
this._reboundPullDown().then(() => {
......@@ -209,32 +258,41 @@
}
},
_calculateMinHeight() {
if (this.$refs.listWrapper && (this.pullDownRefresh || this.pullUpLoad)) {
this.$refs.listWrapper.style.minHeight = `${getRect(this.$refs.wrapper).height + 1}px`
if (this.$refs.listWrapper) {
this.$refs.listWrapper.style.minHeight = this.pullDownRefresh || this.pullUpLoad ? `${getRect(this.$refs.wrapper).height + 1}px` : 0
}
},
_initPullDownRefresh() {
this.scroll.on('pullingDown', () => {
this.beforePullDown = false
this.isPullingDown = true
this.$emit(EVENT_PULLING_DOWN)
})
this.scroll.on('scroll', (pos) => {
if (this.beforePullDown) {
this.bubbleY = Math.max(0, pos.y + this.pullDownInitTop)
this.pullDownStyle = `top:${Math.min(pos.y + this.pullDownInitTop, 10)}px`
} else {
this.bubbleY = 0
this.pullDownStyle = `top:${Math.min(pos.y - 30, 10)}px`
}
})
_onPullDownRefresh() {
this.scroll.on('pullingDown', this._pullDownHandle)
this.scroll.on('scroll', this._pullDownScrollHandle)
},
_initPullUpLoad() {
this.scroll.on('pullingUp', () => {
this.isPullUpLoad = true
this.$emit(EVENT_PULLING_UP)
})
_offPullDownRefresh() {
this.scroll.off('pullingDown', this._pullDownHandle)
this.scroll.off('scroll', this._pullDownScrollHandle)
},
_pullDownHandle() {
this.beforePullDown = false
this.isPullingDown = true
this.$emit(EVENT_PULLING_DOWN)
},
_pullDownScrollHandle(pos) {
if (this.beforePullDown) {
this.bubbleY = Math.max(0, pos.y + PULL_DOWN_ELEMENT_INITIAL_HEIGHT)
this.pullDownStyle = `top:${Math.min(pos.y + PULL_DOWN_ELEMENT_INITIAL_HEIGHT, 10)}px`
} else {
this.bubbleY = 0
this.pullDownStyle = `top:${Math.min(pos.y - 30, 10)}px`
}
},
_onPullUpLoad() {
this.scroll.on('pullingUp', this._pullUpHandle)
},
_offPullUpLoad() {
this.scroll.off('pullingUp', this._pullUpHandle)
},
_pullUpHandle() {
this.isPullUpLoad = true
this.$emit(EVENT_PULLING_UP)
},
_reboundPullDown() {
const {stopTime = 600} = this.pullDownRefresh
......@@ -248,19 +306,12 @@
},
_afterPullDown(dirty) {
setTimeout(() => {
this.pullDownStyle = `top:${this.pullDownInitTop}px`
this.pullDownStyle = `top:${PULL_DOWN_ELEMENT_INITIAL_HEIGHT}px`
this.beforePullDown = true
dirty && this.refresh()
}, this.scroll.options.bounceTime)
}
},
watch: {
data() {
setTimeout(() => {
this.forceUpdate(true)
}, this.refreshDelay)
}
},
components: {
Loading,
Bubble
......
......@@ -16,6 +16,7 @@
item: {
type: Object,
default() {
/* istanbul ignore next */
return {}
}
}
......
......@@ -2,10 +2,11 @@
<div class="cube-slide" ref="slide">
<div class="cube-slide-group" ref="slideGroup">
<slot>
<cube-slide-item v-for="(item, index) in data" :key="index" @click.native="clickItem(item, index)" :item="item"></cube-slide-item>
<cube-slide-item v-for="(item, index) in data" :key="index" @click.native="clickItem(item, index)"
:item="item"></cube-slide-item>
</slot>
</div>
<div class="cube-slide-dots">
<div class="cube-slide-dots" v-if="showDots">
<slot name="dots" :current="currentPageIndex" :dots="dots">
<span :class="{active: currentPageIndex === index}" v-for="(item, index) in dots" :key="index"></span>
</slot>
......@@ -20,6 +21,8 @@
const COMPONENT_NAME = 'cube-slide'
const EVENT_CHANGE = 'change'
const EVENT_SELECT = 'click'
const DIRECTION_H = 'horizontal'
const DIRECTION_V = 'vertical'
export default {
name: COMPONENT_NAME,
......@@ -27,6 +30,7 @@
data: {
type: Array,
default() {
/* istanbul ignore next */
return []
}
},
......@@ -57,6 +61,18 @@
allowVertical: {
type: Boolean,
default: false
},
stopPropagation: {
type: Boolean,
default: false
},
direction: {
type: String,
default: DIRECTION_H
},
showDots: {
type: Boolean,
default: true
}
},
data() {
......@@ -69,6 +85,7 @@
const needRefreshProps = ['data', 'loop', 'autoPlay', 'threshold', 'speed', 'allowVertical']
needRefreshProps.forEach((key) => {
this.$watch(key, () => {
/* istanbul ignore next */
this.refresh()
})
})
......@@ -82,32 +99,41 @@
},
methods: {
clickItem(item, index) {
/* istanbul ignore next */
this.$emit(EVENT_SELECT, item, index)
},
refresh() {
/* istanbul ignore if */
if (this.slide === null) {
return
}
this.slide && this.slide.destroy()
clearTimeout(this._timer)
this.$nextTick(() => {
if (this.slide === null) {
return
}
if (this.slide !== undefined) {
this.currentPageIndex = 0
}
this.dots = 0
this._setSlideWidth()
if (this.slide) {
this.currentPageIndex = 0
}
this._updateSlideDom()
if (this.showDots) {
this._initDots()
this._initSlide()
}
this._initSlide()
if (this.autoPlay) {
this._play()
}
})
if (this.autoPlay) {
this._play()
}
},
_refresh() {
this._setSlideWidth(true)
this._updateSlideDom(true)
this.slide.refresh()
},
_updateSlideDom(isResize) {
if (this.direction === DIRECTION_H) {
this._setSlideWidth(isResize)
} else {
this._setSlideHeight(isResize)
}
},
_setSlideWidth(isResize) {
this.children = this.$refs.slideGroup.children
......@@ -123,18 +149,35 @@
}
this.$refs.slideGroup.style.width = width + 'px'
},
_setSlideHeight(isResize) {
this.children = this.$refs.slideGroup.children
let height = 0
let slideHeight = this.$refs.slide.clientHeight
for (let i = 0; i < this.children.length; i++) {
let child = this.children[i]
child.style.height = slideHeight + 'px'
height += slideHeight
}
if (this.loop && !isResize) {
height += 2 * slideHeight
}
this.$refs.slideGroup.style.height = height + 'px'
},
_initSlide() {
const eventPassthrough = this.direction === DIRECTION_H && this.allowVertical ? DIRECTION_V : ''
this.slide = new BScroll(this.$refs.slide, {
scrollX: true,
scrollY: false,
scrollX: this.direction === DIRECTION_H,
scrollY: this.direction === DIRECTION_V,
momentum: false,
bounce: false,
eventPassthrough: this.allowVertical ? 'vertical' : '',
eventPassthrough,
snap: {
loop: this.loop,
threshold: this.threshold,
speed: this.speed
},
stopPropagation: this.stopPropagation,
click: true,
observeDOM: false
})
......@@ -143,13 +186,14 @@
this.slide.on('scrollEnd', this._onScrollEnd)
window.removeEventListener('touchend', this._touchEndEvent, false)
const slideEl = this.$refs.slide
slideEl.removeEventListener('touchend', this._touchEndEvent, false)
this._touchEndEvent = () => {
if (this.autoPlay) {
this._play()
}
}
window.addEventListener('touchend', this._touchEndEvent, false)
slideEl.addEventListener('touchend', this._touchEndEvent, false)
this.slide.on('beforeScrollStart', () => {
if (this.autoPlay) {
......@@ -181,14 +225,19 @@
clearTimeout(this._timer)
clearTimeout(this._resizeTimer)
window.removeEventListener('resize', this._resizeHandler)
window.removeEventListener('touchend', this._touchEndEvent, false)
const slideEl = this.$refs.slide
if (slideEl) {
slideEl.removeEventListener('touchend', this._touchEndEvent, false)
}
},
_resizeHandler() {
/* istanbul ignore if */
if (!this.slide) {
return
}
clearTimeout(this._resizeTimer)
this._resizeTimer = setTimeout(() => {
/* istanbul ignore if */
if (this.slide.isInTransition) {
this._onScrollEnd()
} else {
......@@ -201,17 +250,21 @@
}
},
mounted() {
this.refresh()
this.$nextTick(() => {
this.refresh()
})
window.addEventListener('resize', this._resizeHandler)
},
activated() {
/* istanbul ignore next */
if (this.autoPlay) {
this._play()
}
window.addEventListener('resize', this._resizeHandler)
},
deactivated() {
/* istanbul ignore next */
this._deactivated()
},
destroyed() {
......
import Vue from 'vue2'
import IndexList from '@/modules/index-list'
import instantiateComponent from '@/common/helpers/instantiate-component'
import { dispatchSwipe } from '../utils/event'
import { dispatchSwipe, dispatchTap } from '../utils/event'
import cityData from '../fake/index-list.json'
// 处理数据
......@@ -57,7 +57,7 @@ describe('IndexList', () => {
.to.equal('中卫市')
})
it('should trigger events', () => {
it('should trigger events', (done) => {
const selectHandler = sinon.spy()
const titleClickHandler = sinon.spy()
......@@ -68,14 +68,15 @@ describe('IndexList', () => {
select: selectHandler,
'title-click': titleClickHandler
})
const items = vm.$el.querySelectorAll('.cube-index-list-item')
items[2].click()
expect(selectHandler)
.to.be.calledWith(data[1].items[0])
vm.$nextTick(() => {
const items = vm.$el.querySelectorAll('.cube-index-list-item')
dispatchTap(items[2])
expect(selectHandler).to.be.calledOnce
vm.$el.querySelector('.cube-index-list-title').click()
expect(titleClickHandler)
.to.be.calledWith(title)
dispatchTap(vm.$el.querySelector('.cube-index-list-title'))
expect(titleClickHandler).to.be.calledOnce
done()
})
})
it('should fixed title', function () {
......@@ -195,7 +196,7 @@ describe('IndexList', () => {
})
})
function createIndexList (props = {}, events = {}) {
function createIndexList(props = {}, events = {}) {
return instantiateComponent(Vue, IndexList, {
props: props,
on: events
......
import Vue from 'vue2'
import Scroll from '@/modules/scroll'
import instantiateComponent from '@/common/helpers/instantiate-component'
import { dispatchSwipe } from '../utils/event'
const props = {
data: [
'我是第 1 行',
'我是第 2 行',
'我是第 3 行',
'我是第 4 行'
],
options: {
pullDownRefresh: {
threshold: 10,
stop: 40,
txt: '刷新成功'
},
pullUpLoad: {
threshold: 0,
txt: {
more: '加载更多订单',
noMore: '没有更多订单了'
}
}
},
listenScroll: true,
listenBeforeScroll: true
}
import { dispatchSwipe, dispatchTap } from '../utils/event'
const data = [
'我是第 1 行',
'我是第 2 行',
'我是第 3 行',
'我是第 4 行'
]
describe('Scroll', () => {
let vm
......@@ -44,8 +26,14 @@ describe('Scroll', () => {
.to.be.a('function')
})
it('should correct by default', () => {
vm = createScroll()
})
it('should render correct contents', () => {
vm = createScroll(props)
vm = createScroll({
data
})
const listItems = vm.$el.querySelectorAll('.cube-scroll-content li')
expect(listItems.length)
.to.equal(4)
......@@ -55,126 +43,296 @@ describe('Scroll', () => {
.to.equal('我是第 4 行')
})
it('should trigger pullingDown', function () {
it('should trigger pullingDown', function (done) {
this.timeout(10000)
const pullingDownHandle = sinon.spy()
vm = createScroll(props, {
'pulling-down': pullingDownHandle
vm = createScroll({
data
})
vm.$parent.updateRenderData({
props: {
data,
options: {
pullDownRefresh: true
}
},
on: {
'pulling-down': pullingDownHandle
}
})
vm.$parent.$forceUpdate()
vm.$refs.wrapper.style.height = '200px'
vm.refresh()
return new Promise((resolve) => {
setTimeout(() => {
const listFirstItem = vm.$el.querySelector('.cube-scroll-content li:first-child')
dispatchSwipe(listFirstItem, [
{
pageX: 10,
pageY: 10
},
{
pageX: 10,
pageY: 300
}
], 100)
setTimeout(() => {
const listFirstItem = vm.$el.querySelector('.cube-scroll-content li:first-child')
dispatchSwipe(listFirstItem, [
{
pageX: 10,
pageY: 10
},
{
pageX: 10,
pageY: 160
expect(pullingDownHandle)
.to.be.callCount(1)
// test: watch data
const newData = data.concat(['我是插入的一行'])
vm.$parent.updateRenderData({
props: {
data: newData,
options: {
pullDownRefresh: {
threshold: 10,
stop: 40,
txt: '刷新成功'
}
}
}
], 100)
})
vm.$parent.$forceUpdate()
setTimeout(() => {
expect(pullingDownHandle)
.to.be.callCount(1)
const newData = props.data.concat(['我是插入的一行'])
vm.$parent.updateRenderData({
props: {
...props,
data: newData
}
})
vm.$parent.$forceUpdate()
setTimeout(() => {
setTimeout(() => {
setTimeout(() => {
expect(vm.beforePullDown).to.be.true
expect(vm.isPullingDown).to.be.false
resolve()
}, 750)
}, 650)
}, 50)
}, 400)
}, 150)
expect(vm.beforePullDown).to.be.true
expect(vm.isPullingDown).to.be.false
done()
}, 1500)
}, 400)
}, 150)
})
it('should not trigger pullingDown', function (done) {
this.timeout(10000)
const pullingDownHandle = sinon.spy()
vm = createScroll({
data,
options: {
pullDownRefresh: true
}
}, {
'pulling-down': pullingDownHandle
})
vm.$parent.updateRenderData({
props: {
data,
options: {
pullDownRefresh: false
}
},
on: {
'pulling-down': pullingDownHandle
}
})
vm.$parent.$forceUpdate()
vm.$refs.wrapper.style.height = '200px'
vm.refresh()
setTimeout(() => {
const listFirstItem = vm.$el.querySelector('.cube-scroll-content li:first-child')
dispatchSwipe(listFirstItem, [
{
pageX: 10,
pageY: 10
},
{
pageX: 10,
pageY: 300
}
], 100)
setTimeout(() => {
expect(pullingDownHandle)
.to.be.callCount(0)
done()
}, 400)
}, 150)
})
it('should trigger pullingUp', function () {
it('should trigger pullingUp', function (done) {
this.timeout(10000)
const pullingUpHandle = sinon.spy()
vm = createScroll(props, {
'pulling-up': pullingUpHandle
vm = createScroll()
vm.$parent.updateRenderData({
props: {
data,
options: {
pullUpLoad: true
}
},
on: {
'pulling-up': pullingUpHandle
}
})
vm.$parent.$forceUpdate()
vm.$refs.wrapper.style.height = '200px'
vm.refresh()
return new Promise((resolve) => {
setTimeout(() => {
const listItem = vm.$el.querySelector('.cube-scroll-content li:nth-child(3)')
dispatchSwipe(listItem, [
{
pageX: 10,
pageY: 200
},
{
pageX: 10,
pageY: 10
}
], 100)
setTimeout(() => {
const listItem = vm.$el.querySelector('.cube-scroll-content li:nth-child(3)')
dispatchSwipe(listItem, [
{
pageX: 10,
pageY: 200
},
{
pageX: 10,
pageY: 10
}
], 100)
expect(pullingUpHandle)
.to.be.callCount(1)
// test: forceUpdate
vm.forceUpdate()
setTimeout(() => {
expect(pullingUpHandle)
.to.be.callCount(1)
const newData = props.data.concat(['我是新附加1', '我是新附加2'])
vm.$parent.updateRenderData({
props: {
...props,
data: newData
}
})
vm.$parent.$forceUpdate()
setTimeout(() => {
expect(vm.isPullUpLoad).to.be.false
expect(vm.pullUpDirty).to.be.true
resolve()
}, 50)
}, 400)
}, 150)
})
expect(vm.isPullUpLoad).to.be.false
expect(vm.pullUpDirty).to.be.false
done()
}, 50)
}, 400)
}, 150)
})
it('should trigger click', function () {
it('should not trigger pullingUp', function (done) {
this.timeout(10000)
const clickHandler = sinon.spy()
const pullingUpHandle = sinon.spy()
vm = createScroll({
...props,
data,
options: {
...props.options,
click: false
pullUpLoad: true
}
}, {
click: clickHandler
'pulling-up': pullingUpHandle
})
vm.$parent.updateRenderData({
props: {
data,
options: {
pullUpLoad: false
}
},
on: {
'pulling-up': pullingUpHandle
}
})
vm.$parent.$forceUpdate()
vm.$refs.wrapper.style.height = '200px'
vm.refresh()
return new Promise((resolve) => {
setTimeout(() => {
const listItem = vm.$el.querySelector('.cube-scroll-content li:nth-child(3)')
dispatchSwipe(listItem, [
{
pageX: 10,
pageY: 200
},
{
pageX: 10,
pageY: 10
}
], 100)
setTimeout(() => {
const listItem = vm.$el.querySelector('.cube-scroll-content li')
listItem.click()
expect(clickHandler).to.be.calledWith(props.data[0])
resolve()
}, 50)
expect(pullingUpHandle)
.to.be.callCount(0)
done()
}, 400)
}, 150)
})
it('should trigger click', function (done) {
const clickHandler = sinon.spy()
vm = createScroll({
data
}, {
click: clickHandler
})
vm.$nextTick(() => {
const listItem = vm.$el.querySelector('.cube-scroll-content li')
dispatchTap(listItem)
expect(clickHandler).to.be.calledOnce
done()
})
})
it('should trigger other events', function (done) {
const scrollHandle = sinon.spy()
const beforeScrollHandle = sinon.spy()
vm = createScroll({
data,
listenScroll: true,
listenBeforeScroll: true,
options: {
pullUpLoad: true
}
}, {
scroll: scrollHandle,
'before-scroll-start': beforeScrollHandle
})
vm.$refs.wrapper.style.height = '200px'
vm.refresh()
const listItem = vm.$el.querySelector('.cube-scroll-content li:nth-child(3)')
dispatchSwipe(listItem, [
{
pageX: 10,
pageY: 200
},
{
pageX: 10,
pageY: 10
}
], 100)
setTimeout(() => {
// TODO: why failed
// expect(scrollHandle)
// .to.be.called
// expect(beforeScrollHandle)
// .to.be.callCount(1)
done()
}, 400)
})
it('should call correct method', function () {
vm = createScroll({data})
vm.disable()
vm.enable()
vm.scrollTo(0, 100)
const listItems = vm.$el.querySelectorAll('.cube-scroll-content li')
vm.scrollToElement(listItems[3])
vm.destroy()
})
function createScroll (props = {}, events = {}) {
function createScroll(props = {}, events = {}) {
return instantiateComponent(Vue, Scroll, {
props: props,
on: events
......
import Vue from 'vue2'
import Slide from '@/modules/slide'
import createVue from '../utils/create-vue'
import { dispatchSwipe } from '../utils/event'
import { dispatchSwipe, dispatchTap } from '../utils/event'
const items = [{
url: 'http://www.didichuxing.com/',
image: 'http://webapp.didistatic.com/static/webapp/shield/cube-ui-examples-slide01.png'
}, {
url: 'http://www.didichuxing.com/',
image: 'http://webapp.didistatic.com/static/webapp/shield/cube-ui-examples-slide02.png'
}]
describe('Slide.vue', () => {
let vm
......@@ -17,37 +25,100 @@ describe('Slide.vue', () => {
.to.be.a('function')
})
it('should render correct contents', function (done) {
this.timeout(10000)
const items = ['1', '2', '3']
vm = createSlide(items)
setTimeout(() => {
const itemEles = vm.$el.querySelectorAll('.cube-slide-item')
// for loop
expect(itemEles.length).to.equal(3 + 2)
expect(itemEles[2].textContent).to.equal(items[1])
let dotEles = vm.$el.querySelectorAll('.cube-slide-dots span')
expect(dotEles.length).to.equal(3)
expect(dotEles[0].className).to.equal('active')
setTimeout(() => {
// dotEles = vm.$el.querySelectorAll('.cube-slide-dots span')
expect(dotEles[0].className).not.to.equal('active')
expect(dotEles[1].className).to.equal('active')
vm = createVue({
template: `
<cube-slide :loop="loop" style="width:300px;height:100px;">
<cube-slide-item v-for="(item,index) in items" :key="index" :item="item"></cube-slide-item>
</cube-slide>
`,
data: {
items,
loop: false
}
})
vm.$nextTick(() => {
const itemEls = vm.$el.querySelectorAll('.cube-slide-item')
expect(itemEls.length).to.equal(items.length)
let imgEl = itemEls[0].querySelector('img')
expect(imgEl.src).to.equal(items[0].image)
vm.$nextTick(() => {
let dotEls = vm.$el.querySelectorAll('.cube-slide-dots span')
expect(dotEls.length).to.equal(2)
expect(dotEls[0].className).to.equal('active')
})
done()
})
})
it('should clone nodes if loop is true', (done) => {
vm = createVue({
template: `
<cube-slide :loop="loop" style="width:300px;height:100px;">
<cube-slide-item v-for="(item,index) in items" :key="index" :item="item"></cube-slide-item>
</cube-slide>
`,
data: {
items,
loop: true
}
})
vm.$nextTick(() => {
let groupEl = vm.$el.querySelector('.cube-slide-group')
let itemEls = vm.$el.querySelectorAll('.cube-slide-item')
expect(itemEls.length).to.equal(items.length + 2)
let width = itemEls[0].clientWidth
expect(groupEl.clientWidth).to.equal(width * (items.length + 2))
vm.$destroy()
vm = createVue({
template: `
<cube-slide :loop="loop" :direction="direction" style="width:300px;height:100px;">
<cube-slide-item v-for="(item,index) in items" :key="index" :item="item"></cube-slide-item>
</cube-slide>
`,
data: {
items,
loop: true,
direction: 'vertical'
}
})
vm.$nextTick(() => {
groupEl = vm.$el.querySelector('.cube-slide-group')
itemEls = vm.$el.querySelectorAll('.cube-slide-item')
expect(itemEls.length).to.equal(items.length + 2)
let height = itemEls[0].clientHeight
expect(groupEl.clientHeight).to.equal(height * (items.length + 2))
done()
}, 1500)
}, 200)
})
})
})
it('should trigger change event', function (done) {
this.timeout(10000)
const items = ['1', '2', '3']
const changeHandler = sinon.spy()
vm = createSlide(items, changeHandler)
vm = createVue({
template: `
<cube-slide :interval="interval" @change="change" style="width:300px;height:100px;">
<cube-slide-item v-for="(item,index) in items" :key="index" :item="item"></cube-slide-item>
</cube-slide>
`,
data: {
items,
interval: 1000
},
methods: {
change() {
changeHandler()
}
}
})
setTimeout(() => {
// auto change
expect(changeHandler).to.be.calledWith(1)
// dispatch touch
expect(changeHandler).to.be.calledOnce
dispatchSwipe(vm.$el, [
{
pageX: 160,
pageX: 180,
pageY: 20
},
{
......@@ -61,24 +132,27 @@ describe('Slide.vue', () => {
}, 600)
}, 2000)
})
})
function createSlide (items, changeHandler) {
const vm = createVue({
template: `
<cube-slide :interval="interval" @change="change" style="width:300px;height:100px;">
<cube-slide-item v-for="item in items" :key="item"><a>{{item}}</a></cube-slide-item>
it('should go to right pageIndex if set initialIndex', function (done) {
vm = createVue({
template: `
<cube-slide style="width:300px;height:100px;" :initial-index="initialIndex" :auto-play="autoPlay">
<cube-slide-item v-for="(item,index) in items" :key="index" :item="item"></cube-slide-item>
</cube-slide>
`,
data: {
items,
interval: 1000
},
methods: {
change(newIndex) {
changeHandler && changeHandler.call(vm, newIndex)
data: {
items,
initialIndex: 0,
autoPlay: false
}
}
})
vm.$nextTick(() => {
vm.$parent.initialIndex = 1
setTimeout(() => {
expect(vm.currentPageIndex).to.equal(1)
done()
}, 600)
})
})
return vm
}
})
......@@ -10,6 +10,15 @@ export function dispatchClick(target, props) {
target.dispatchEvent(event)
}
export function dispatchTap(target) {
const touch = {
pageX: target.offsetLeft + 1,
pageY: target.offsetTop + 1
}
dispatchTouchStart(target, touch)
dispatchTouchEnd(target, touch)
}
export function dispatchMouse (target, name = 'mousedown') {
const event = createEvent('', name)
const domRect = target.getBoundingClientRect()
......@@ -18,7 +27,7 @@ export function dispatchMouse (target, name = 'mousedown') {
target.dispatchEvent(event)
}
export function dispatchTouch (target, name = 'touchstart', touches) {
export function dispatchTouch(target, name = 'touchstart', touches) {
const event = createEvent('', name)
event.touches = event.targetTouches = event.changedTouches = Array.isArray(touches) ? touches : [touches]
target.dispatchEvent(event)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册