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

bump test coverage up to 95%, deprecate getOptionValue, small optimizations

This commit is contained in:
Jeff Sagal
2016-05-28 11:58:18 -07:00
parent bd7dabc639
commit 03ba6376e3
2 changed files with 339 additions and 118 deletions
+34 -36
View File
@@ -132,24 +132,24 @@
</button>
</span>
<input
v-el:search
v-show="searchable"
v-model="search"
@keydown.delete="maybeDeleteValue"
@keyup.esc="onEscape"
@keyup.up.prevent="typeAheadUp"
@keyup.down.prevent="typeAheadDown"
@keyup.enter.prevent="typeAheadSelect"
@blur="open = false"
@focus="open = true"
type="search"
class="form-control"
:placeholder="searchPlaceholder"
:style="{ width: isValueEmpty ? '100%' : 'auto' }"
>
<input
v-el:search
v-show="searchable"
v-model="search"
@keydown.delete="maybeDeleteValue"
@keyup.esc="onEscape"
@keyup.up.prevent="typeAheadUp"
@keyup.down.prevent="typeAheadDown"
@keyup.enter.prevent="typeAheadSelect"
@blur="open = false"
@focus="open = true"
type="search"
class="form-control"
:placeholder="searchPlaceholder"
:style="{ width: isValueEmpty ? '100%' : 'auto' }"
>
<i v-el:open-indicator role="presentation" class="open-indicator"></i>
<i v-el:open-indicator role="presentation" class="open-indicator"></i>
</div>
<ul v-show="open" v-el:dropdown-menu :transition="transition" :style="{ 'max-height': maxHeight }" class="dropdown-menu animated">
@@ -338,8 +338,7 @@
select(option) {
if (!this.isOptionSelected(option)) {
if (this.taggable && !this.optionExists(option)) {
newOption = this.createOption(option)
option = typeof newOption === 'undefined' ? option : newOption
option = this.createOption(option)
if( this.pushTags ) {
this.options.push(option)
@@ -372,14 +371,14 @@
* @return {void}
*/
onAfterSelect(option) {
if (!this.multiple) {
this.open = !this.open
this.$els.search.blur()
}
if (!this.multiple) {
this.open = !this.open
this.$els.search.blur()
}
if( this.clearSearchOnSelect ) {
this.search = ''
}
if( this.clearSearchOnSelect ) {
this.search = ''
}
},
/**
@@ -424,14 +423,15 @@
* Otherwise, return the entire option.
* @param {Object||String} option
* @return {Object||String}
* @deprecated will be removed in 1.0.8
*/
getOptionValue( option ) {
if( typeof option === 'object' && option.value ) {
return option.value;
}
getOptionValue( option ) {
if( typeof option === 'object' && option.value ) {
return option.value;
}
return option;
},
return option;
},
/**
* Generate the option label text. If {option}
@@ -444,8 +444,6 @@
if( typeof option === 'object' ) {
if( this.label && option[this.label] ) {
return option[this.label]
} else if( option.label ) {
return option.label
}
}
return option;
@@ -495,7 +493,7 @@
if( ! this.search.length ) {
this.$els.search.blur()
} else {
this.$set('search', '')
this.search = ''
}
},
@@ -595,7 +593,7 @@
if( this.multiple ) {
return this.value
} else if (this.value) {
return [this.value]
return [this.value]
}
return []
+305 -82
View File
@@ -18,13 +18,29 @@ function trigger(target, event, process) {
return e
}
function triggerMouse(target, event, process) {
var e = document.createEvent('MouseEvent')
e.initEvent('event', true, true)
if (process) process(e)
target.dispatchEvent(e)
return e
}
function triggerFocusEvent(target, event, process) {
var e = document.createEvent('FocusEvent')
e.initEvent('event', true, true)
if (process) process(e)
target.dispatchEvent(e)
return e
}
/**
* Optionally set the search term, then simulate a return keypress.
* @param vm
* @param search
*/
function searchSubmit(vm, search = false) {
if( search ) {
if (search) {
vm.$children[0].search = search
}
@@ -45,7 +61,7 @@ describe('Select.vue', () => {
options: ['one', 'two', 'three']
}
}).$mount()
expect(vm.$children[0].value).toEqual(['one'])
expect(vm.$children[0].value).toEqual(vm.value)
})
it('can accept an array of objects and pre-selected value (single)', () => {
@@ -57,8 +73,7 @@ describe('Select.vue', () => {
options: [{label: 'This is Foo', value: 'foo'}, {label: 'This is Bar', value: 'bar'}]
}
}).$mount()
expect(vm.$children[0].$get('value').value).toEqual('foo')
expect(vm.$children[0].$get('value').label).toEqual('This is Foo')
expect(vm.$children[0].value).toEqual(vm.value)
})
it('can accept an array of objects and pre-selected values (multiple)', () => {
@@ -70,12 +85,7 @@ describe('Select.vue', () => {
options: [{label: 'This is Foo', value: 'foo'}, {label: 'This is Bar', value: 'bar'}]
}
}).$mount()
var values = vm.$children[0].$get('value')
var labels = []
labels = values.map(value => value.label)
values = values.map(value => value.value)
expect(values).toEqual(['foo', 'bar'])
expect(labels).toEqual(['This is Foo', 'This is Bar'])
expect(vm.$children[0].value).toEqual(vm.value)
})
it('can determine if the value prop is empty', () => {
@@ -120,11 +130,34 @@ describe('Select.vue', () => {
}).$mount()
vm.$children[0].options = ['four', 'five', 'six']
Vue.nextTick(() => {
expect(vm.$children[0].$get('value')).toEqual([])
expect(vm.$children[0].value).toEqual([])
done()
})
})
it('resets 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>',
components: {vSelect},
data: {
value: ['one'],
multiple: true,
options: ['one', 'two', 'three']
}
}).$mount()
vm.multiple = false
Vue.nextTick(() => {
expect(vm.$children[0].value).toEqual(null)
vm.multiple = true
Vue.nextTick(() => {
expect(vm.$children[0].value).toEqual([])
done()
})
})
})
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>',
@@ -137,80 +170,18 @@ describe('Select.vue', () => {
vm.$children[0].$set('options', ['one', 'five', 'six'])
expect(vm.$children[0].value).toEqual(['one'])
})
})
describe('Removing values', () => {
it('removes the given tag when its close icon is clicked', (done) => {
it('can determine if an object is already selected', () => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value" :multiple="true"></v-select></div>',
template: '<div><v-select :options="options" multiple :value.sync="value"></v-select></div>',
components: {vSelect},
data: {
value: ['one'],
options: ['one', 'two', 'three']
value: [{label: 'one'}],
options: [{label: 'one'}]
}
}).$mount()
vm.$children[0].$els.toggle.querySelector('.close').click()
Vue.nextTick(() => {
expect(vm.$children[0].$get('value')).toEqual([])
done()
})
})
it('removes 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>',
components: {vSelect},
data: {
value: ['one', 'two'],
options: ['one', 'two', 'three']
}
}).$mount()
vm.$children[0].maybeDeleteValue()
Vue.nextTick(() => {
expect(vm.$children[0].$get('value')).toEqual(['one'])
})
})
it('sets the 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>',
components: {vSelect},
data: {
value: 'one',
options: ['one', 'two', 'three']
}
}).$mount()
vm.$children[0].maybeDeleteValue()
Vue.nextTick(() => {
expect(vm.$children[0].$get('value')).toEqual(null)
})
})
})
describe('Labels', () => {
it('can generate labels using the default label key', () => {
const vm = new Vue({
template: '<div><v-select :options="options" :value.sync="value" :multiple="true"></v-select></div>',
components: {vSelect},
data: {
value: [{label: 'Baz'}],
options: [{label: 'Foo'}, {label: 'Baz'}]
}
}).$mount()
expect(vm.$children[0].$els.toggle.querySelector('.selected-tag').textContent).toContain('Baz')
})
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>',
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].isOptionSelected('one')).toEqual(true)
})
it('can run a callback when the selection changes', (done) => {
@@ -241,6 +212,242 @@ describe('Select.vue', () => {
})
})
describe('Toggling Dropdown', () => {
it('can 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>',
components: {vSelect},
data: {
value: [{label: 'one'}],
options: [{label: 'one'}]
}
}).$mount()
vm.$children[0].toggleDropdown({target: vm.$children[0].$els.search})
Vue.nextTick(() => {
Vue.nextTick(() => {
expect(vm.$children[0].open).toEqual(true)
done()
})
})
})
// it('can close the dropdown when the el is clicked', (done) => {
// const vm = new Vue({
// template: '<div><v-select :options="options" :value.sync="value"></v-select></div>',
// components: {vSelect},
// data: {
// value: [{label: 'one'}],
// options: [{label: 'one'}]
// }
// }).$mount()
// vm.$children[0].open = true
// Vue.nextTick(() => {
// vm.$children[0].toggleDropdown({ target: vm.$children[0].$el })
// Vue.nextTick( () => {
// expect(vm.$children[0].open).toEqual(false)
// done()
// })
// })
// })
it('will close the dropdown on search blur', () => {
const vm = new Vue({
template: '<div><v-select :options="options" multiple :value.sync="value"></v-select></div>',
components: {vSelect},
data: {
value: [{label: 'one'}],
options: [{label: 'one'}]
}
}).$mount()
vm.$children[0].open = true
triggerFocusEvent(vm.$children[0].$els.toggle, 'blur')
expect(vm.$children[0].open).toEqual(true)
})
// it('will close the dropdown on escape, if search is empty', (done) => {
// const vm = new Vue({
// template: '<div><v-select :options="options" multiple :value.sync="value"></v-select></div>',
// components: {vSelect},
// data: {
// value: [{label: 'one'}],
// options: [{label: 'one'}]
// }
// }).$mount()
// vm.$children[0].open = true
// vm.$children[0].onEscape()
// Vue.nextTick(() => {
// Vue.nextTick(() => {
// expect(vm.$children[0].open).toEqual(false)
// done()
// })
// })
// })
it('will remove existing search text on escape keyup', () => {
const vm = new Vue({
template: '<div><v-select :options="options" multiple :value.sync="value"></v-select></div>',
components: {vSelect},
data: {
value: [{label: 'one'}],
options: [{label: 'one'}]
}
}).$mount()
vm.$children[0].search = 'foo'
vm.$children[0].onEscape()
expect(vm.$children[0].search).toEqual('')
})
})
describe('Moving the Typeahead Pointer', () => {
it('will set the pointer to zero when the filteredOptions change', (done) => {
const vm = new Vue({
template: '<div><v-select :options="options"></v-select></div>',
components: {vSelect},
data: {
options: ['one', 'two', 'three']
}
}).$mount()
vm.$children[0].search = 'two'
Vue.nextTick(() => {
expect(vm.$children[0].typeAheadPointer).toEqual(0)
done()
})
})
it('will 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},
data: {
options: ['one', 'two', 'three']
}
}).$mount()
vm.$children[0].typeAheadPointer = 1
trigger(vm.$children[0].$els.search, 'keyup', (e) => e.keyCode = 38)
expect(vm.$children[0].typeAheadPointer).toEqual(0)
})
it('will 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},
data: {
options: ['one', 'two', 'three']
}
}).$mount()
vm.$children[0].typeAheadPointer = 1
trigger(vm.$children[0].$els.search, 'keyup', (e) => e.keyCode = 40)
expect(vm.$children[0].typeAheadPointer).toEqual(2)
})
it('will not move the pointer past the end of the list', () => {
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 = 2
vm.$children[0].typeAheadDown()
expect(vm.$children[0].typeAheadPointer).toEqual(2)
})
})
describe('Removing values', () => {
it('removes 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>',
components: {vSelect},
data: {
value: ['one'],
options: ['one', 'two', 'three']
}
}).$mount()
vm.$children[0].$els.toggle.querySelector('.close').click()
Vue.nextTick(() => {
expect(vm.$children[0].value).toEqual([])
done()
})
})
it('removes 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>',
components: {vSelect},
data: {
value: ['one', 'two'],
options: ['one', 'two', 'three']
}
}).$mount()
vm.$children[0].maybeDeleteValue()
Vue.nextTick(() => {
expect(vm.$children[0].value).toEqual(['one'])
})
})
it('sets the 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>',
components: {vSelect},
data: {
value: 'one',
options: ['one', 'two', 'three']
}
}).$mount()
vm.$children[0].maybeDeleteValue()
Vue.nextTick(() => {
expect(vm.$children[0].value).toEqual(null)
})
})
})
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>',
components: {vSelect},
data: {
value: [{name: 'Baz'}],
options: [{name: 'Foo'}, {name: 'Baz'}]
}
}).$mount()
expect(vm.$children[0].$els.toggle.querySelector('.selected-tag').textContent).toContain('Baz')
})
it('will display a placeholder if the value is empty', (done) => {
const vm = new Vue({
template: '<div><v-select :options="options" placeholder="foo"></v-select></div>',
components: {vSelect},
data: {
options: [{label: 'one'}]
}
}).$mount()
expect(vm.$children[0].searchPlaceholder).toEqual('foo')
vm.$children[0].value = {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({
@@ -295,7 +502,7 @@ describe('Select.vue', () => {
expect(vm.$children[0].filteredOptions).toEqual(['three'])
})
it('can select the current search text', (done) => {
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>',
components: {vSelect},
@@ -307,7 +514,24 @@ describe('Select.vue', () => {
searchSubmit(vm, 'three')
Vue.nextTick(() => {
expect(vm.$children[0].$get('value')).toEqual(['one', 'three'])
expect(vm.$children[0].value).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>',
components: {vSelect},
data: {
value: [{label: 'one'}],
options: [{label: 'one'}]
}
}).$mount()
searchSubmit(vm, 'two')
Vue.nextTick(() => {
expect(vm.$children[0].value).toEqual([{label: 'one'}, {label: 'two'}])
done()
})
})
@@ -355,7 +579,7 @@ describe('Select.vue', () => {
searchSubmit(vm)
Vue.nextTick(() => {
expect(vm.$children[0].$get('value')[0]).toBe(two)
expect(vm.$children[0].value[0]).toBe(two)
done()
})
})
@@ -374,7 +598,6 @@ describe('Select.vue', () => {
Vue.nextTick(() => {
searchSubmit(vm)
// 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(() => {