2
0
mirror of https://github.com/tenrok/vue-select.git synced 2026-05-17 02:29:37 +03:00

separate logic into mixin, complete test coverage with mocks and spies

This commit is contained in:
Jeff Sagal
2016-05-31 13:54:36 -07:00
parent cf87f838fd
commit 737ab8d1fc
3 changed files with 179 additions and 110 deletions
+7 -51
View File
@@ -168,7 +168,11 @@
<script type="text/babel">
import pointerScroll from '../mixins/pointerScroll'
export default {
mixins: [pointerScroll],
props: {
/**
* Contains the currently selected value. Very similar to a
@@ -325,6 +329,7 @@
},
filteredOptions() {
this.typeAheadPointer = 0
this.maybeAdjustScroll()
},
},
@@ -442,7 +447,7 @@
typeAheadUp() {
if (this.typeAheadPointer > 0) {
this.typeAheadPointer--
this.maybeAdjustScrollPosition()
this.maybeAdjustScroll()
}
},
@@ -454,7 +459,7 @@
typeAheadDown() {
if (this.typeAheadPointer < this.filteredOptions.length - 1) {
this.typeAheadPointer++
this.maybeAdjustScrollPosition()
this.maybeAdjustScroll()
}
},
@@ -475,55 +480,6 @@
}
},
maybeAdjustScrollPosition() {
let bounds = this.viewport()
let pixelsToPointerTop = this.pixelsToPointerTop()
let pixelsToPointerBottom = this.pixelsToPointerBottom()
let pointerHeight = this.pointerHeight()
if (pixelsToPointerTop <= bounds.top) {
return this.scrollTo(pixelsToPointerTop)
} else if (pixelsToPointerBottom >= bounds.bottom) {
// return this.scrollTo(( this.pixelsToPointerCenter() - this.$els.dropdownMenu.offsetHeight ) + ( pointerHeight / 2))
return this.scrollTo(( this.pixelsToPointerCenter() - this.$els.dropdownMenu.offsetHeight ) + pointerHeight)
}
},
pixelsToPointerTop() {
let pixelsToPointerTop = 0
for (let i = 0; i < this.typeAheadPointer; i++) {
pixelsToPointerTop += this.$els.dropdownMenu.children[i].offsetHeight
}
return pixelsToPointerTop
},
pixelsToPointerBottom() {
return this.pixelsToPointerTop() + this.pointerHeight()
},
pixelsToPointerCenter() {
return this.pixelsToPointerTop() + ( this.pointerHeight() / 2 )
},
pointerHeight() {
return this.$els.dropdownMenu.children[this.typeAheadPointer].offsetHeight
},
/**
* The viewport is the currently viewable
* portion of the dropdown menu
*/
viewport() {
return {
top: this.$els.dropdownMenu.scrollTop,
bottom: this.$els.dropdownMenu.offsetHeight + this.$els.dropdownMenu.scrollTop
}
},
scrollTo(position) {
return this.$els.dropdownMenu.scrollTop = position
},
/**
* If there is any text in the search input, remove it.
* Otherwise, blur the search input to close the dropdown.
+71
View File
@@ -0,0 +1,71 @@
export default {
methods: {
/**
* Adjust the scroll position of the dropdown list
* if the current pointer is outside of the
* overflow bounds.
* @returns {*}
*/
maybeAdjustScroll() {
let pixelsToPointerTop = this.pixelsToPointerTop()
let pixelsToPointerBottom = this.pixelsToPointerBottom()
if ( pixelsToPointerTop <= this.viewport().top) {
return this.scrollTo( pixelsToPointerTop )
} else if (pixelsToPointerBottom >= this.viewport().bottom) {
return this.scrollTo( this.viewport().top + this.pointerHeight() )
}
},
/**
* The distance in pixels from the top of the dropdown
* list to the top of the current pointer element.
* @returns {number}
*/
pixelsToPointerTop() {
let pixelsToPointerTop = 0
for (let i = 0; i < this.typeAheadPointer; i++) {
pixelsToPointerTop += this.$els.dropdownMenu.children[i].offsetHeight
}
return pixelsToPointerTop
},
/**
* The distance in pixels from the top of the dropdown
* list to the bottom of the current pointer element.
* @returns {*}
*/
pixelsToPointerBottom() {
return this.pixelsToPointerTop() + this.pointerHeight()
},
/**
* The offsetHeight of the current pointer element.
* @returns {number}
*/
pointerHeight() {
let element = this.$els.dropdownMenu.children[this.typeAheadPointer]
return element ? element.offsetHeight : 0
},
/**
* The currently viewable portion of the dropdownMenu.
* @returns {{top: (string|*|number), bottom: *}}
*/
viewport() {
return {
top: this.$els.dropdownMenu.scrollTop,
bottom: this.$els.dropdownMenu.offsetHeight + this.$els.dropdownMenu.scrollTop
}
},
/**
* Scroll the dropdownMenu to a given position.
* @param position
* @returns {*}
*/
scrollTo(position) {
return this.$els.dropdownMenu.scrollTop = position
},
}
}
+101 -59
View File
@@ -2,6 +2,10 @@
import Vue from 'vue'
import vSelect from 'src/components/Select.vue'
import pointerScroll from 'src/mixins/pointerScroll.js'
// http://vue-loader.vuejs.org/en/workflow/testing-with-mocks.html
const Mock = require('!!vue?inject!src/components/Select.vue')
/**
* Simulate a DOM event.
@@ -323,7 +327,7 @@ describe('Select.vue', () => {
})
})
it('should move the pointer visually up the list on up arrow keyup', () => {
it('should move the pointer visually up the list on up arrow keyUp', () => {
const vm = new Vue({
template: '<div><v-select :options="options"></v-select></div>',
components: {vSelect},
@@ -338,7 +342,7 @@ describe('Select.vue', () => {
expect(vm.$children[0].typeAheadPointer).toEqual(0)
})
it('should move the pointer visually down the list on down arrow keyup', () => {
it('should move the pointer visually down the list on down arrow keyUp', () => {
const vm = new Vue({
template: '<div><v-select :options="options"></v-select></div>',
components: {vSelect},
@@ -366,76 +370,117 @@ describe('Select.vue', () => {
expect(vm.$children[0].typeAheadPointer).toEqual(2)
})
it('should check if the scroll position needs to be adjusted on up arrow keyup', () => {
const vm = new Vue({
template: '<div><v-select :options="options"></v-select></div>',
components: {vSelect},
data: {
options: ['one', 'two', 'three']
}
}).$mount()
describe('Automatic Scrolling', () => {
it('should check if the scroll position needs to be adjusted on up arrow keyUp', () => {
const vm = new Vue({
template: '<div><v-select :options="options"></v-select></div>',
components: {vSelect},
data: {
options: ['one', 'two', 'three']
}
}).$mount()
vm.$children[0].typeAheadPointer = 1
spyOn(vm.$children[0], 'maybeAdjustScrollPosition')
trigger(vm.$children[0].$els.search, 'keyup', (e) => e.keyCode = 38)
expect(vm.$children[0].maybeAdjustScrollPosition).toHaveBeenCalled()
})
vm.$children[0].typeAheadPointer = 1
spyOn(vm.$children[0], 'maybeAdjustScroll')
trigger(vm.$children[0].$els.search, 'keyup', (e) => e.keyCode = 38)
expect(vm.$children[0].maybeAdjustScroll).toHaveBeenCalled()
})
it('should check if the scroll position needs to be adjusted on down arrow keyup', () => {
const vm = new Vue({
template: '<div><v-select :options="options" max-height="10"></v-select></div>',
components: {vSelect},
data: {
options: ['one', 'two', 'three']
}
}).$mount()
it('should check if the scroll position needs to be adjusted on down arrow keyUp', () => {
const vm = new Vue({
template: '<div><v-select :options="options"></v-select></div>',
components: {vSelect},
data: {
options: ['one', 'two', 'three']
}
}).$mount()
spyOn(vm.$children[0], 'maybeAdjustScrollPosition')
spyOn(vm.$children[0], 'maybeAdjustScroll')
trigger(vm.$children[0].$els.search, 'keyup', (e) => e.keyCode = 40)
expect(vm.$children[0].maybeAdjustScroll).toHaveBeenCalled()
})
vm.$children[0].$els.search.focus()
it('should check if the scroll position needs to be adjusted when filtered options changes', (done) => {
const vm = new Vue({
template: '<div><v-select :options="options"></v-select></div>',
components: {vSelect},
data: {
options: ['one', 'two', 'three']
}
}).$mount()
trigger(vm.$children[0].$els.search, 'keyup', (e) => e.keyCode = 40)
expect(vm.$children[0].maybeAdjustScrollPosition).toHaveBeenCalled()
})
spyOn(vm.$children[0], 'maybeAdjustScroll')
vm.$children[0].search = 'two'
it('should scroll up if the new pointer is above the current viewport bounds', () => {
Vue.nextTick(() => {
expect(vm.$children[0].maybeAdjustScroll).toHaveBeenCalled()
done()
})
})
it('should scroll up if the pointer is above the current viewport bounds', () => {
let methods = Object.assign(pointerScroll.methods, {
pixelsToPointerTop() {
return 1
},
viewport() {
return { top: 2, bottom: 0 }
}
})
const vm = new Vue({
template: '<div><v-select :options="[\'one\', \'two\', \'three\']"></v-select></div>',
components: {
'v-select': Mock({
'../mixins/pointerScroll': {methods}
})
},
}).$mount()
spyOn(vm.$children[0], 'scrollTo')
vm.$children[0].maybeAdjustScroll()
expect(vm.$children[0].scrollTo).toHaveBeenCalledWith(1)
})
it('should scroll down if the pointer is below the current viewport bounds', () => {
let methods = Object.assign(pointerScroll.methods, {
pixelsToPointerBottom() {
return 2
},
viewport() {
return { top: 0, bottom: 1 }
}
})
const vm = new Vue({
template: '<div><v-select :options="[\'one\', \'two\', \'three\']"></v-select></div>',
components: {
'v-select': Mock({
'../mixins/pointerScroll': {methods}
})
},
}).$mount()
spyOn(vm.$children[0], 'scrollTo')
vm.$children[0].maybeAdjustScroll()
expect(vm.$children[0].scrollTo).toHaveBeenCalledWith(vm.$children[0].viewport().top + vm.$children[0].pointerHeight())
})
})
describe('Measuring pixel distances', () => {
it('should calculate pixelsToPointerTop as the sum of the height all options above the pointer', () => {
it('should calculate pointerHeight as the offsetHeight of the pointer element if it exists', () => {
const vm = new Vue({
template: '<div><v-select :options="options"></v-select></div>',
template: '<div><v-select :options="[\'one\', \'two\', \'three\']""></v-select></div>',
components: {vSelect},
data: {
options: ['one', 'two', 'three']
}
}).$mount()
vm.$children[0].typeAheadPointer = 1
let lineHeight = vm.$children[0].$els.dropdownMenu.children[0].offsetHeight
expect(vm.$children[0].pixelsToPointerTop()).toEqual( lineHeight * 2 )
})
it('should calculate pixelsToPointerBottom as the sum of the height all options above the pointer plus the height of the pointer', () => {
const vm = new Vue({
template: '<div><v-select :options="options"></v-select></div>',
components: {vSelect},
data: {
options: ['one', 'two', 'three']
}
}).$mount()
// Fresh instances start with the pointer at -1
vm.$children[0].typeAheadPointer = -1
expect(vm.$children[0].pointerHeight()).toEqual(0)
vm.$children[0].typeAheadPointer = 100
expect(vm.$children[0].pointerHeight()).toEqual(0)
vm.$children[0].typeAheadPointer = 1
let lineHeight = vm.$children[0].$els.dropdownMenu.children[0].offsetHeight
console.log(lineHeight)
expect(vm.$children[0].pixelsToPointerTop()).toEqual( lineHeight * 10 + lineHeight)
})
it('should calculate pixelsToPointerCenter', () => {
})
it('should calculate pointerHeight', () => {
expect(vm.$children[0].pointerHeight()).toEqual(vm.$children[0].$els.dropdownMenu.children[1].offsetHeight)
})
})
})
@@ -681,7 +726,4 @@ describe('Select.vue', () => {
})
})
})
})
// also see example testing a component with mocks at
// http://vue-loader.vuejs.org/en/workflow/testing-with-mocks.html
})