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:
+34
-36
@@ -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
@@ -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(() => {
|
||||
|
||||
Reference in New Issue
Block a user