2
0
mirror of https://github.com/tenrok/vue-select.git synced 2026-06-04 06:32:23 +03:00

Merge remote-tracking branch 'upstream/master'

This commit is contained in:
es
2017-03-21 15:23:05 +08:00
39 changed files with 1492 additions and 1095 deletions
+3
View File
@@ -64,6 +64,9 @@ module.exports = function (config) {
webpackMiddleware: {
noInfo: true
},
specReporter: {
suppressSkipped: true
},
coverageReporter: {
dir: './coverage',
reporters: [
+289 -150
View File
@@ -1,15 +1,15 @@
// flow
/* global describe, it, expect */
import Vue from 'vue'
import vSelect from 'src/components/Select.vue'
// import vSelect from '../../../dist/vue-select'
import pointerScroll from 'src/mixins/pointerScroll.js'
Vue.component('v-select', vSelect)
// http://vue-loader.vuejs.org/en/workflow/testing-with-mocks.html
const Mock = require('!!vue?inject!src/components/Select.vue')
Vue.component('v-select', vSelect)
/**
* Simulate a DOM event.
* @param target
@@ -25,6 +25,13 @@ function trigger(target, event, process) {
return e
}
/**
* Simulate a Mouse event.
* @param target
* @param event
* @param process
* @returns {Event}
*/
function triggerMouse(target, event, process) {
var e = document.createEvent('MouseEvent')
e.initEvent('event', true, true)
@@ -33,6 +40,13 @@ function triggerMouse(target, event, process) {
return e
}
/**
* Simulate a Focus event.
* @param target
* @param event
* @param process
* @returns {Event}
*/
function triggerFocusEvent(target, event, process) {
var e = document.createEvent('FocusEvent')
e.initEvent('event', true, true)
@@ -51,7 +65,7 @@ function searchSubmit(vm, search = false) {
vm.$children[0].search = search
}
trigger(vm.$children[0].$els.search, 'keyup', function (e) {
trigger(vm.$children[0].$refs.search, 'keyup', function (e) {
e.keyCode = 13
})
}
@@ -61,67 +75,75 @@ describe('Select.vue', () => {
describe('Selecting values', () => {
it('can accept an array with pre-selected values', () => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value"></v-select></div>',
template: '<div><v-select :options="options" :value="value"></v-select></div>',
components: {vSelect},
data: {
value: ['one'],
value: 'one',
options: ['one', 'two', 'three']
}
}).$mount()
expect(vm.$children[0].value).toEqual(vm.value)
expect(vm.$children[0].mutableValue).toEqual(vm.value)
})
it('can accept an array of objects and pre-selected value (single)', () => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value"></v-select></div>',
template: '<div><v-select :options="options" :value="value"></v-select></div>',
components: {vSelect},
data: {
value: {label: 'This is Foo', value: 'foo'},
options: [{label: 'This is Foo', value: 'foo'}, {label: 'This is Bar', value: 'bar'}]
}
}).$mount()
expect(vm.$children[0].value).toEqual(vm.value)
expect(vm.$children[0].mutableValue).toEqual(vm.value)
})
it('can accept an array of objects and pre-selected values (multiple)', () => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value" :multiple="true"></v-select></div>',
template: '<div><v-select :options="options" :value="value" :multiple="true"></v-select></div>',
components: {vSelect},
data: {
value: [{label: 'This is Foo', value: 'foo'}, {label: 'This is Bar', value: 'bar'}],
options: [{label: 'This is Foo', value: 'foo'}, {label: 'This is Bar', value: 'bar'}]
}
}).$mount()
expect(vm.$children[0].value).toEqual(vm.value)
expect(vm.$children[0].mutableValue).toEqual(vm.value)
})
it('can deselect a pre-selected object', () => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value" :multiple="true"></v-select></div>',
template: '<div><v-select :options="options" :value="value" :multiple="true"></v-select></div>',
data: {
value: [{label: 'This is Foo', value: 'foo'}, {label: 'This is Bar', value: 'bar'}],
options: [{label: 'This is Foo', value: 'foo'}, {label: 'This is Bar', value: 'bar'}]
}
}).$mount()
vm.$children[0].select({label: 'This is Foo', value: 'foo'})
expect(vm.$children[0].value.length).toEqual(1)
expect(vm.$children[0].mutableValue.length).toEqual(1)
})
it('can deselect a pre-selected string', () => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value" :multiple="true"></v-select></div>',
template: '<div><v-select :options="options" :value="value" :multiple="true"></v-select></div>',
data: {
value: ['foo', 'bar'],
options: ['foo','bar']
}
}).$mount()
vm.$children[0].select('foo')
expect(vm.$children[0].value.length).toEqual(1)
expect(vm.$children[0].mutableValue.length).toEqual(1)
}),
it('can deselect an option when multiple is false', () => {
const vm = new Vue({
template: `<div><v-select :value="'foo'"></v-select></div>`,
}).$mount()
vm.$children[0].deselect('foo')
expect(vm.$children[0].mutableValue).toEqual(null)
})
it('can determine if the value prop is empty', () => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value"></v-select></div>',
template: '<div><v-select :options="options" :value="value"></v-select></div>',
components: {vSelect},
data: {
value: [],
@@ -131,28 +153,28 @@ describe('Select.vue', () => {
var select = vm.$children[0]
expect(select.isValueEmpty).toEqual(true)
select.$set('value', ['one'])
select.select(['one'])
expect(select.isValueEmpty).toEqual(false)
select.$set('value', [{l: 'f'}])
select.select([{l: 'f'}])
expect(select.isValueEmpty).toEqual(false)
select.$set('value', 'one')
select.select('one')
expect(select.isValueEmpty).toEqual(false)
select.$set('value', {label: 'foo', value: 'foo'})
select.select({label: 'foo', value: 'foo'})
expect(select.isValueEmpty).toEqual(false)
select.$set('value', '')
select.select('')
expect(select.isValueEmpty).toEqual(true)
select.$set('value', null)
select.select(null)
expect(select.isValueEmpty).toEqual(true)
})
it('should reset the selected values when the multiple property changes', (done) => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value" :multiple="multiple"></v-select></div>',
template: '<div><v-select :options="options" :value="value" :multiple="multiple"></v-select></div>',
components: {vSelect},
data: {
value: ['one'],
@@ -164,10 +186,10 @@ describe('Select.vue', () => {
vm.multiple = false
Vue.nextTick(() => {
expect(vm.$children[0].value).toEqual(null)
expect(vm.$children[0].mutableValue).toEqual(null)
vm.multiple = true
Vue.nextTick(() => {
expect(vm.$children[0].value).toEqual([])
expect(vm.$children[0].mutableValue).toEqual([])
done()
})
})
@@ -175,20 +197,20 @@ describe('Select.vue', () => {
it('can retain values present in a new array of options', () => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value"></v-select></div>',
template: '<div><v-select :options="options" v-model="value"></v-select></div>',
components: {vSelect},
data: {
value: ['one'],
options: ['one', 'two', 'three']
}
}).$mount()
vm.$children[0].$set('options', ['one', 'five', 'six'])
expect(vm.$children[0].value).toEqual(['one'])
vm.options = ['one', 'five', 'six']
expect(vm.$children[0].mutableValue).toEqual(['one'])
})
it('can determine if an object is already selected', () => {
const vm = new Vue({
template: '<div><v-select :options="options" multiple :value.sync="value"></v-select></div>',
template: '<div><v-select :options="options" multiple v-model="value"></v-select></div>',
components: {vSelect},
data: {
value: [{label: 'one'}],
@@ -199,59 +221,99 @@ describe('Select.vue', () => {
expect(vm.$children[0].isOptionSelected({label: 'one'})).toEqual(true)
})
describe('onChange Callback', () => {
it('can run a callback when the selection changes', (done) => {
it('can use v-model syntax for a two way binding to a parent component', (done) => {
const vm = new Vue({
template: '<div><v-select :options="options" v-model="value"></v-select></div>',
components: {vSelect},
data: {
value: 'foo',
options: ['foo','bar','baz']
}
}).$mount()
expect(vm.$children[0].value).toEqual('foo')
expect(vm.$children[0].mutableValue).toEqual('foo')
vm.$children[0].mutableValue = 'bar'
Vue.nextTick(() => {
expect(vm.value).toEqual('bar')
done()
})
}),
it('can check if a string value is selected when the value is an object and multiple is true', () => {
const vm = new Vue({
template: `<div><v-select multiple :value="[{label: 'foo', value: 'bar'}]"></v-select></div>`,
}).$mount()
expect(vm.$children[0].isOptionSelected('foo')).toEqual(true)
}),
describe('change Event', () => {
it('will trigger the input event when the selection changes', (done) => {
const vm = new Vue({
template: `<div><v-select :value="['foo']" :options="['foo','bar','baz']" :on-change="cb"></v-select></div>`,
components: {vSelect},
methods: {
cb(val) {
}
template: `<div><v-select ref="select" :value="['foo']" :options="['foo','bar','baz']" v-on:input="foo = arguments[0]"></v-select></div>`,
data: {
foo: ''
}
}).$mount()
spyOn(vm.$children[0], 'onChange')
vm.$children[0].select('bar')
vm.$refs.select.select('bar')
Vue.nextTick(() => {
expect(vm.$children[0].onChange).toHaveBeenCalledWith('bar')
vm.$children[0].select('baz')
Vue.nextTick(() => {
expect(vm.$children[0].onChange).toHaveBeenCalledWith('baz')
done()
})
expect(vm.foo).toEqual('bar')
done()
})
})
it('should run onChange when multiple is true and the value changes', (done) => {
it('should run change when multiple is true and the value changes', (done) => {
const vm = new Vue({
template: `<div><v-select v-ref:select :value="['foo']" :options="['foo','bar','baz']" multiple :on-change="cb"></v-select></div>`,
methods: {
cb(val) {
}
template: `<div><v-select ref="select" :value="['foo']" :options="['foo','bar','baz']" multiple v-on:input="foo = arguments[0]"></v-select></div>`,
data: {
foo: ''
}
}).$mount()
spyOn(vm.$children[0], 'onChange')
vm.$children[0].select('bar')
vm.$refs.select.select('bar')
Vue.nextTick(() => {
expect(vm.$children[0].onChange).toHaveBeenCalledWith(['foo','bar'])
vm.$children[0].select('baz')
Vue.nextTick(() => {
expect(vm.$children[0].onChange).toHaveBeenCalledWith(['foo','bar','baz'])
done()
})
expect(vm.foo).toEqual(['foo','bar'])
done()
})
})
})
})
describe('Filtering Options', () => {
it('should filter an array of strings', () => {
const vm = new Vue({
template: `<div><v-select ref="select" :options="['foo','bar','baz']" v-model="value"></v-select></div>`,
data: {value: 'foo'}
}).$mount()
vm.$refs.select.search = 'ba'
expect(vm.$refs.select.filteredOptions).toEqual(['bar','baz'])
})
it('should filter without case-sensitivity', () => {
const vm = new Vue({
template: `<div><v-select ref="select" :options="['Foo','Bar','Baz']" v-model="value"></v-select></div>`,
data: {value: 'foo'}
}).$mount()
vm.$refs.select.search = 'ba'
expect(vm.$refs.select.filteredOptions).toEqual(['Bar','Baz'])
})
it('can filter an array of objects based on the objects label key', () => {
const vm = new Vue({
template: `<div><v-select ref="select" :options="[{label: 'Foo', value: 'foo'}, {label: 'Bar', value: 'bar'}, {label: 'Baz', value: 'baz'}]" v-model="value"></v-select></div>`,
data: {value: 'foo'}
}).$mount()
vm.$refs.select.search = 'ba'
expect(JSON.stringify(vm.$refs.select.filteredOptions)).toEqual(JSON.stringify([{label: 'Bar', value: 'bar'}, {label: 'Baz', value: 'baz'}]))
})
})
describe('Toggling Dropdown', () => {
it('should not open the dropdown when the el is clicked but the component is disabled', (done) => {
const vm = new Vue({
@@ -274,7 +336,7 @@ describe('Select.vue', () => {
it('should open the dropdown when the el is clicked', (done) => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value"></v-select></div>',
template: '<div><v-select :options="options" :value="value"></v-select></div>',
components: {vSelect},
data: {
value: [{label: 'one'}],
@@ -282,7 +344,7 @@ describe('Select.vue', () => {
}
}).$mount()
vm.$children[0].toggleDropdown({target: vm.$children[0].$els.search})
vm.$children[0].toggleDropdown({target: vm.$children[0].$refs.search})
Vue.nextTick(() => {
Vue.nextTick(() => {
expect(vm.$children[0].open).toEqual(true)
@@ -297,20 +359,20 @@ describe('Select.vue', () => {
components: {vSelect},
}).$mount()
spyOn(vm.$children[0].$els.search, 'blur')
spyOn(vm.$children[0].$refs.search, 'blur')
vm.$children[0].open = true
vm.$children[0].toggleDropdown({target: vm.$children[0].$el})
Vue.nextTick(() => {
expect(vm.$children[0].$els.search.blur).toHaveBeenCalled()
expect(vm.$children[0].$refs.search.blur).toHaveBeenCalled()
done()
})
})
it('should close the dropdown on search blur', () => {
const vm = new Vue({
template: '<div><v-select :options="options" multiple :value.sync="value"></v-select></div>',
template: '<div><v-select :options="options" multiple :value="value"></v-select></div>',
components: {vSelect},
data: {
value: [{label: 'one'}],
@@ -319,30 +381,55 @@ describe('Select.vue', () => {
}).$mount()
vm.$children[0].open = true
triggerFocusEvent(vm.$children[0].$els.toggle, 'blur')
triggerFocusEvent(vm.$children[0].$refs.toggle, 'blur')
expect(vm.$children[0].open).toEqual(true)
})
it('will close the dropdown and emit the search:blur event from onSearchBlur', () => {
const vm = new Vue({
template: '<div><v-select></v-select></div>',
}).$mount()
spyOn(vm.$children[0], '$emit')
vm.$children[0].open = true
vm.$children[0].onSearchBlur()
expect(vm.$children[0].open).toEqual(false)
expect(vm.$children[0].$emit).toHaveBeenCalledWith('search:blur')
})
it('will open the dropdown and emit the search:focus event from onSearchFocus', () => {
const vm = new Vue({
template: '<div><v-select></v-select></div>',
}).$mount()
spyOn(vm.$children[0], '$emit')
vm.$children[0].onSearchFocus()
expect(vm.$children[0].open).toEqual(true)
expect(vm.$children[0].$emit).toHaveBeenCalledWith('search:focus')
})
it('will close the dropdown on escape, if search is empty', (done) => {
const vm = new Vue({
template: '<div><v-select></v-select></div>',
components: {vSelect},
}).$mount()
spyOn(vm.$children[0].$els.search, 'blur')
spyOn(vm.$children[0].$refs.search, 'blur')
vm.$children[0].open = true
vm.$children[0].onEscape()
Vue.nextTick(() => {
expect(vm.$children[0].$els.search.blur).toHaveBeenCalled()
expect(vm.$children[0].$refs.search.blur).toHaveBeenCalled()
done()
})
})
it('should remove existing search text on escape keyup', () => {
const vm = new Vue({
template: '<div><v-select :options="options" multiple :value.sync="value"></v-select></div>',
template: '<div><v-select :options="options" multiple :value="value"></v-select></div>',
components: {vSelect},
data: {
value: [{label: 'one'}],
@@ -393,7 +480,7 @@ describe('Select.vue', () => {
vm.$children[0].typeAheadPointer = 1
trigger(vm.$children[0].$els.search, 'keydown', (e) => e.keyCode = 38)
trigger(vm.$children[0].$refs.search, 'keydown', (e) => e.keyCode = 38)
expect(vm.$children[0].typeAheadPointer).toEqual(0)
})
@@ -407,7 +494,7 @@ describe('Select.vue', () => {
}).$mount()
vm.$children[0].typeAheadPointer = 1
trigger(vm.$children[0].$els.search, 'keydown', (e) => e.keyCode = 40)
trigger(vm.$children[0].$refs.search, 'keydown', (e) => e.keyCode = 40)
expect(vm.$children[0].typeAheadPointer).toEqual(2)
})
@@ -437,7 +524,7 @@ describe('Select.vue', () => {
vm.$children[0].typeAheadPointer = 1
spyOn(vm.$children[0], 'maybeAdjustScroll')
trigger(vm.$children[0].$els.search, 'keydown', (e) => e.keyCode = 38)
trigger(vm.$children[0].$refs.search, 'keydown', (e) => e.keyCode = 38)
expect(vm.$children[0].maybeAdjustScroll).toHaveBeenCalled()
})
@@ -451,7 +538,7 @@ describe('Select.vue', () => {
}).$mount()
spyOn(vm.$children[0], 'maybeAdjustScroll')
trigger(vm.$children[0].$els.search, 'keydown', (e) => e.keyCode = 40)
trigger(vm.$children[0].$refs.search, 'keydown', (e) => e.keyCode = 40)
expect(vm.$children[0].maybeAdjustScroll).toHaveBeenCalled()
})
@@ -496,21 +583,26 @@ describe('Select.vue', () => {
expect(vm.$children[0].scrollTo).toHaveBeenCalledWith(1)
})
it('should scroll down if the pointer is below the current viewport bounds', () => {
/**
* @link https://github.com/vuejs/vue-loader/issues/434
* @todo vue-loader/inject-loader fails when used twice in the same file,
* so the mock here is abastracted to a separate file.
*/
xit('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}
}
pixelsToPointerBottom() {
return 2
},
viewport() {
return {top: 0, bottom: 1}
}
})
const vm = new Vue({
template: '<div><v-select :options="[\'one\', \'two\', \'three\']"></v-select></div>',
template: `<div><v-select :options="['one', 'two', 'three']"></v-select></div>`,
components: {
'v-select': Mock({
'../mixins/pointerScroll': {methods}
})
'v-select': Mock({
'../mixins/pointerScroll': {methods}
})
},
}).$mount()
@@ -523,19 +615,23 @@ describe('Select.vue', () => {
describe('Measuring pixel distances', () => {
it('should calculate pointerHeight as the offsetHeight of the pointer element if it exists', () => {
const vm = new Vue({
template: '<div><v-select :options="[\'one\', \'two\', \'three\']""></v-select></div>',
components: {vSelect},
template: `<div><v-select :options="['one', 'two', 'three']"></v-select></div>`,
}).$mount()
// Fresh instances start with the pointer at -1
vm.$children[0].typeAheadPointer = -1
expect(vm.$children[0].pointerHeight()).toEqual(0)
// dropdown must be open for $refs to exist
vm.$children[0].open = true
vm.$children[0].typeAheadPointer = 100
expect(vm.$children[0].pointerHeight()).toEqual(0)
Vue.nextTick(() => {
// Fresh instances start with the pointer at -1
vm.$children[0].typeAheadPointer = -1
expect(vm.$children[0].pointerHeight()).toEqual(0)
vm.$children[0].typeAheadPointer = 1
expect(vm.$children[0].pointerHeight()).toEqual(vm.$children[0].$els.dropdownMenu.children[1].offsetHeight)
vm.$children[0].typeAheadPointer = 100
expect(vm.$children[0].pointerHeight()).toEqual(0)
vm.$children[0].typeAheadPointer = 1
expect(vm.$children[0].pointerHeight()).toEqual(vm.$children[0].$refs.dropdownMenu.children[1].offsetHeight)
})
})
})
})
@@ -543,16 +639,16 @@ describe('Select.vue', () => {
describe('Removing values', () => {
it('can remove the given tag when its close icon is clicked', (done) => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value" :multiple="true"></v-select></div>',
template: '<div><v-select :options="options" v-model="value" :multiple="true"></v-select></div>',
components: {vSelect},
data: {
value: ['one'],
options: ['one', 'two', 'three']
}
}).$mount()
vm.$children[0].$els.toggle.querySelector('.close').click()
vm.$children[0].$refs.toggle.querySelector('.close').click()
Vue.nextTick(() => {
expect(vm.$children[0].value).toEqual([])
expect(vm.$children[0].mutableValue).toEqual([])
done()
})
})
@@ -560,7 +656,7 @@ describe('Select.vue', () => {
it('should remove the last item in the value array on delete keypress when multiple is true', () => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value" :multiple="true"></v-select></div>',
template: '<div><v-select :options="options" v-model="value" :multiple="true"></v-select></div>',
components: {vSelect},
data: {
value: ['one', 'two'],
@@ -569,13 +665,13 @@ describe('Select.vue', () => {
}).$mount()
vm.$children[0].maybeDeleteValue()
Vue.nextTick(() => {
expect(vm.$children[0].value).toEqual(['one'])
expect(vm.$children[0].mutableValue).toEqual(['one'])
})
})
it('should set value to null on delete keypress when multiple is false', () => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value"></v-select></div>',
template: '<div><v-select :options="options" v-model="value"></v-select></div>',
components: {vSelect},
data: {
value: 'one',
@@ -584,7 +680,7 @@ describe('Select.vue', () => {
}).$mount()
vm.$children[0].maybeDeleteValue()
Vue.nextTick(() => {
expect(vm.$children[0].value).toEqual(null)
expect(vm.$children[0].mutableValue).toEqual(null)
})
})
})
@@ -592,14 +688,14 @@ describe('Select.vue', () => {
describe('Labels', () => {
it('can generate labels using a custom label key', () => {
const vm = new Vue({
template: '<div><v-select label="name" :options="options" :value.sync="value" :multiple="true"></v-select></div>',
template: '<div><v-select label="name" :options="options" v-model="value" :multiple="true"></v-select></div>',
components: {vSelect},
data: {
value: [{name: 'Baz'}],
options: [{name: 'Foo'}, {name: 'Baz'}]
}
}).$mount()
expect(vm.$children[0].$els.toggle.querySelector('.selected-tag').textContent).toContain('Baz')
expect(vm.$children[0].$refs.toggle.querySelector('.selected-tag').textContent).toContain('Baz')
})
it('should display a placeholder if the value is empty', (done) => {
@@ -612,20 +708,18 @@ describe('Select.vue', () => {
}).$mount()
expect(vm.$children[0].searchPlaceholder).toEqual('foo')
vm.$children[0].value = {label: 'one'}
vm.$children[0].mutableValue = {label: 'one'}
Vue.nextTick(() => {
expect(vm.$children[0].searchPlaceholder).not.toBeDefined()
done()
})
// expect(vm.$children[0].searchPlaceholder()).toEqual('foo')
})
})
describe('When Tagging Is Enabled', () => {
it('can determine if a given option string already exists', () => {
const vm = new Vue({
template: '<div><v-select v-ref:select :options="options" taggable></v-select></div>',
template: '<div><v-select ref="select" :options="options" taggable></v-select></div>',
components: {vSelect},
data: {
options: ['one', 'two']
@@ -638,7 +732,7 @@ describe('Select.vue', () => {
it('can determine if a given option object already exists', () => {
const vm = new Vue({
template: '<div><v-select v-ref:select :options="options" taggable></v-select></div>',
template: '<div><v-select ref="select" :options="options" taggable></v-select></div>',
components: {vSelect},
data: {
options: [{label: 'one'}, {label: 'two'}]
@@ -651,7 +745,7 @@ describe('Select.vue', () => {
it('can determine if a given option object already exists when using custom labels', () => {
const vm = new Vue({
template: '<div><v-select v-ref:select :options="options" label="foo" taggable></v-select></div>',
template: '<div><v-select ref="select" :options="options" label="foo" taggable></v-select></div>',
components: {vSelect},
data: {
options: [{foo: 'one'}, {foo: 'two'}]
@@ -664,7 +758,7 @@ describe('Select.vue', () => {
it('can add the current search text as the first item in the options list', () => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value" :multiple="true" taggable></v-select></div>',
template: '<div><v-select :options="options" :value="value" :multiple="true" taggable></v-select></div>',
components: {vSelect},
data: {
value: ['one'],
@@ -678,7 +772,7 @@ describe('Select.vue', () => {
it('can select the current search text as a string', (done) => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value" :multiple="true" taggable></v-select></div>',
template: '<div><v-select :options="options" :value="value" :multiple="true" taggable></v-select></div>',
components: {vSelect},
data: {
value: ['one'],
@@ -688,14 +782,14 @@ describe('Select.vue', () => {
searchSubmit(vm, 'three')
Vue.nextTick(() => {
expect(vm.$children[0].value).toEqual(['one', 'three'])
expect(vm.$children[0].mutableValue).toEqual(['one', 'three'])
done()
})
})
it('can select the current search text as an object', (done) => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value" :multiple="true" taggable></v-select></div>',
template: '<div><v-select :options="options" :value="value" :multiple="true" taggable></v-select></div>',
components: {vSelect},
data: {
value: [{label: 'one'}],
@@ -705,14 +799,14 @@ describe('Select.vue', () => {
searchSubmit(vm, 'two')
Vue.nextTick(() => {
expect(vm.$children[0].value).toEqual([{label: 'one'}, {label: 'two'}])
expect(vm.$children[0].mutableValue).toEqual([{label: 'one'}, {label: 'two'}])
done()
})
})
it('should add a freshly created option/tag to the options list when pushTags is true', () => {
const vm = new Vue({
template: '<div><v-select :options="options" push-tags :value.sync="value" :multiple="true" taggable></v-select></div>',
template: '<div><v-select :options="options" push-tags :value="value" :multiple="true" taggable></v-select></div>',
components: {vSelect},
data: {
value: ['one'],
@@ -721,12 +815,12 @@ describe('Select.vue', () => {
}).$mount()
searchSubmit(vm, 'three')
expect(vm.$children[0].options).toEqual(['one', 'two', 'three'])
expect(vm.$children[0].mutableOptions).toEqual(['one', 'two', 'three'])
})
it('wont add a freshly created option/tag to the options list when pushTags is false', () => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value" :multiple="true" :taggable="true"></v-select></div>',
template: '<div><v-select :options="options" :value="value" :multiple="true" :taggable="true"></v-select></div>',
components: {vSelect},
data: {
value: ['one'],
@@ -735,14 +829,13 @@ describe('Select.vue', () => {
}).$mount()
searchSubmit(vm, 'three')
expect(vm.$children[0].options).toEqual(['one', 'two'])
expect(vm.$children[0].mutableOptions).toEqual(['one', 'two'])
})
it('should select an existing option if the search string matches a string from options', (done) => {
let two = 'two'
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value" :multiple="true" taggable></v-select></div>',
components: {vSelect},
template: '<div><v-select :options="options" :value="value" :multiple="true" taggable></v-select></div>',
data: {
value: null,
options: ['one', two]
@@ -753,7 +846,7 @@ describe('Select.vue', () => {
searchSubmit(vm)
Vue.nextTick(() => {
expect(vm.$children[0].value[0]).toBe(two)
expect(vm.$children[0].mutableValue[0]).toBe(two)
done()
})
})
@@ -762,7 +855,6 @@ describe('Select.vue', () => {
let two = {label: 'two'}
const vm = new Vue({
template: '<div><v-select :options="options" taggable></v-select></div>',
components: {vSelect},
data: {
options: [{label: 'one'}, two]
}
@@ -775,80 +867,127 @@ describe('Select.vue', () => {
// This needs to be wrapped in nextTick() twice so that filteredOptions can
// calculate after setting the search text, and move the typeAheadPointer index to 0.
Vue.nextTick(() => {
expect(vm.$children[0].value.label).toBe(two.label)
expect(vm.$children[0].mutableValue.label).toBe(two.label)
done()
})
})
})
it('should not reset the selected value when the options property changes', (done) => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value" :multiple="true" taggable></v-select></div>',
template: '<div><v-select :options="options" :value="value" :multiple="true" taggable></v-select></div>',
components: {vSelect},
data: {
value: [{label: 'one'}],
options: [{label: 'one'}]
}
}).$mount()
vm.$children[0].options = [{label: 'two'}]
vm.$children[0].mutableOptions = [{label: 'two'}]
Vue.nextTick(() => {
expect(vm.$children[0].value).toEqual([{label: 'one'}])
expect(vm.$children[0].mutableValue).toEqual([{label: 'one'}])
done()
})
})
it('should not allow duplicate tags when using string options', (done) => {
const vm = new Vue({
template: `<div><v-select ref="select" taggable multiple></v-select></div>`,
}).$mount()
vm.$refs.select.search = 'one'
searchSubmit(vm)
Vue.nextTick(() => {
expect(vm.$refs.select.mutableValue).toEqual(['one'])
expect(vm.$refs.select.search).toEqual('')
vm.$refs.select.search = 'one'
searchSubmit(vm)
Vue.nextTick(() => {
expect(vm.$refs.select.mutableValue).toEqual([])
expect(vm.$refs.select.search).toEqual('')
done()
})
})
})
it('should not allow duplicate tags when using object options', (done) => {
const vm = new Vue({
template: `<div><v-select ref="select" taggable multiple></v-select></div>`,
}).$mount()
vm.$refs.select.search = 'one'
searchSubmit(vm)
Vue.nextTick(() => {
expect(vm.$refs.select.mutableValue).toEqual(['one'])
expect(vm.$refs.select.search).toEqual('')
vm.$refs.select.search = 'one'
searchSubmit(vm)
Vue.nextTick(() => {
expect(vm.$refs.select.mutableValue).toEqual([])
expect(vm.$refs.select.search).toEqual('')
done()
})
})
})
})
describe('Asynchronous Loading', () => {
it('can toggle the loading class', () => {
const vm = new Vue({
template: '<div><v-select v-ref:select></v-select></div>',
template: '<div><v-select ref="select"></v-select></div>',
}).$mount()
vm.$refs.select.toggleLoading()
expect(vm.$refs.select.loading).toEqual(true)
expect(vm.$refs.select.showLoading).toEqual(true)
vm.$refs.select.toggleLoading(true)
expect(vm.$refs.select.loading).toEqual(true)
expect(vm.$refs.select.showLoading).toEqual(true)
})
it('should trigger the onSearch callback when the search text changes', (done) => {
const vm = new Vue({
template: '<div><v-select v-ref:select :on-search="foo"></v-select></div>',
template: '<div><v-select ref="select" :on-search="foo"></v-select></div>',
data: {
called: false
},
methods: {
foo() {
foo(val) {
this.called = val
}
}
}).$mount()
spyOn(vm.$refs.select, 'onSearch')
vm.$refs.select.search = 'foo'
Vue.nextTick(() => {
expect(vm.$refs.select.onSearch).toHaveBeenCalledWith('foo', vm.$refs.select.toggleLoading)
expect(vm.called).toEqual('foo')
done()
})
})
it('should not trigger the onSearch callback if the search text is empty', (done) => {
const vm = new Vue({
template: '<div><v-select v-ref:select search="foo" :on-search="foo"></v-select></div>',
template: '<div><v-select ref="select" search="foo" :on-search="foo"></v-select></div>',
data: { called: false },
methods: {
foo() {
foo(val) {
this.called = ! this.called
}
}
}).$mount()
spyOn(vm.$refs.select, 'onSearch')
vm.$refs.select.search = ''
vm.$refs.select.search = 'foo'
Vue.nextTick(() => {
expect(vm.$refs.select.onSearch).not.toHaveBeenCalled()
done()
expect(vm.called).toBe(true)
vm.$refs.select.search = ''
Vue.nextTick(() => {
expect(vm.called).toBe(true)
done()
})
})
})
it('can set loading to false from the onSearch callback', (done) => {
const vm = new Vue({
template: '<div><v-select loading v-ref:select :on-search="foo"></v-select></div>',
template: '<div><v-select loading ref="select" :on-search="foo"></v-select></div>',
methods: {
foo(search, loading) {
loading(false)
@@ -858,14 +997,14 @@ describe('Select.vue', () => {
vm.$refs.select.search = 'foo'
Vue.nextTick(() => {
expect(vm.$refs.select.loading).toEqual(false)
expect(vm.$refs.select.showLoading).toEqual(false)
done()
})
})
it('can set loading to true from the onSearch callback', (done) => {
const vm = new Vue({
template: '<div><v-select loading v-ref:select :on-search="foo"></v-select></div>',
template: '<div><v-select loading ref="select" :on-search="foo"></v-select></div>',
methods: {
foo(search, loading) {
loading(true)
@@ -877,7 +1016,7 @@ describe('Select.vue', () => {
select.onSearch(select.search, select.toggleLoading)
Vue.nextTick(() => {
expect(vm.$refs.select.loading).toEqual(true)
expect(vm.$refs.select.showLoading).toEqual(true)
done()
})
})
@@ -886,31 +1025,31 @@ describe('Select.vue', () => {
describe('Reset on options change', () => {
it('should not reset the selected value by default when the options property changes', (done) => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value"></v-select></div>',
template: '<div><v-select :options="options" :value="value"></v-select></div>',
data: {
value: 'one',
options: ['one', 'two', 'three']
}
}).$mount()
vm.$children[0].options = ['four', 'five', 'six']
vm.$children[0].mutableOptions = ['four', 'five', 'six']
Vue.nextTick(() => {
expect(vm.$children[0].value).toEqual('one')
expect(vm.$children[0].mutableValue).toEqual('one')
done()
})
})
it('should reset the selected value when the options property changes', (done) => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value" reset-on-options-change></v-select></div>',
template: '<div><v-select :options="options" :value="value" reset-on-options-change></v-select></div>',
components: {vSelect},
data: {
value: 'one',
options: ['one', 'two', 'three']
}
}).$mount()
vm.$children[0].options = ['four', 'five', 'six']
vm.$children[0].mutableOptions = ['four', 'five', 'six']
Vue.nextTick(() => {
expect(vm.$children[0].value).toEqual(null)
expect(vm.$children[0].mutableValue).toEqual(null)
done()
})
})