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

Merge branch 'master' into feature/custom-filter

# Conflicts:
#	dev.html
#	src/components/Select.vue
#	src/dev.js
This commit is contained in:
Jeff
2018-01-12 18:59:27 -08:00
9 changed files with 246 additions and 33 deletions
+19 -24
View File
@@ -29,33 +29,28 @@
<body>
<div id="app">
<!--<v-select placeholder="default" label="title" :options="fuseSearchOptions" :filter-function="fuse"></v-select>-->
<v-select label="title" :options="fuseSearchOptions" :filter="fuse">
<v-select placeholder="default" :options="options"></v-select>
<v-select placeholder="default, RTL" :options="options" dir="rtl"></v-select>
<v-select placeholder="multiple" multiple :options="options"></v-select>
<v-select placeholder="multiple, taggable" multiple taggable :options="options" no-drop></v-select>
<v-select placeholder="multiple, taggable, push-tags" multiple push-tags taggable :options="[{label: 'Foo', value: 'foo'}]"></v-select>
<v-select placeholder="multiple, closeOnSelect=true" multiple :options="['cat', 'dog', 'bear']"></v-select>
<v-select placeholder="multiple, closeOnSelect=false" multiple :close-on-select="false" :options="['cat', 'dog', 'bear']"></v-select>
<v-select placeholder="searchable=false" :options="options" :searchable="false"></v-select>
<v-select placeholder="search github.." label="full_name" @search="search" :options="ajaxRes"></v-select>
<v-select placeholder="custom option template" :options="options" multiple>
<template slot="selected-option" scope="option">
<img :src='"https://www.kidlink.org/icons/f0-" + option.value.toLowerCase() + ".gif"'/>
{{option.label}}
</template>
<template slot="option" scope="option">
<strong>{{ option.title }}</strong><br>
<em>{{ option.author.firstName }} {{ option.author.lastName }}</em>
<img :src='"https://www.kidlink.org/icons/f0-" + option.value.toLowerCase() + ".gif"'/>
{{option.label}} ({{option.value}})
</template>
</v-select>
<!--<v-select placeholder="default, RTL" :options="options" dir="rtl"></v-select>-->
<!--<v-select placeholder="multiple" multiple :options="options"></v-select>-->
<!--<v-select placeholder="multiple, taggable" multiple taggable :options="options" no-drop></v-select>-->
<!--<v-select placeholder="multiple, taggable, push-tags" multiple push-tags taggable :options="[{label: 'Foo', value: 'foo'}]"></v-select>-->
<!--<v-select placeholder="multiple, closeOnSelect=true" multiple :options="['cat', 'dog', 'bear']"></v-select>-->
<!--<v-select placeholder="multiple, closeOnSelect=false" multiple :close-on-select="false" :options="['cat', 'dog', 'bear']"></v-select>-->
<!--<v-select placeholder="unsearchable" :options="options" :searchable="false"></v-select>-->
<!--<v-select placeholder="search github.." label="full_name" @search="search" :options="ajaxRes"></v-select>-->
<!--<v-select placeholder="custom option template" :options="options" multiple>-->
<!--<template slot="selected-option" scope="option">-->
<!--<img :src='"https://www.kidlink.org/icons/f0-" + option.value.toLowerCase() + ".gif"'/> -->
<!--{{option.label}}-->
<!--</template>-->
<!--<template slot="option" scope="option">-->
<!--<img :src='"https://www.kidlink.org/icons/f0-" + option.value.toLowerCase() + ".gif"'/> -->
<!--{{option.label}} ({{option.value}})-->
<!--</template>-->
<!--</v-select>-->
<!--<v-select disabled placeholder="disabled" value="disabled"></v-select>-->
<!--<v-select disabled multiple placeholder="disabled" :value="['disabled', 'multiple']"></v-select>-->
<v-select placeholder="disabled" disabled value="disabled"></v-select>
<v-select placeholder="disabled multiple" disabled multiple :value="['disabled', 'multiple']"></v-select>
<v-select placeholder="filterable=false, @search=searchPeople" label="first_name" :filterable="false" @search="searchPeople" :options="people"></v-select>
</div>
</body>
+2 -2
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -122,7 +122,7 @@
</div>
<div class="col-md-6">
<pre><v-code lang="markup">&#x3C;v-select v-on:change=&#x22;consoleCallback&#x22; :options=&#x22;countries&#x22;&#x3E;&#x3C;/v-select&#x3E;</v-code></pre>
<pre><v-code lang="markup">&#x3C;v-select :on-change=&#x22;consoleCallback&#x22; :options=&#x22;countries&#x22;&#x3E;&#x3C;/v-select&#x3E;</v-code></pre>
<pre><v-code lang="javascript">methods: {
consoleCallback(val) {
console.dir(JSON.stringify(val))
+1 -1
View File
@@ -10,7 +10,7 @@ This is very useful when integrating with Vuex, as it will allow your to trigger
</div>
```html
<v-select v-on:change="consoleCallback" :options="countries"></v-select>
<v-select :on-change="consoleCallback" :options="countries"></v-select>
```
```js
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "vue-select",
"version": "2.3.0",
"version": "2.4.0",
"description": "A native Vue.js component that provides similar functionality to Select2 without the overhead of jQuery.",
"author": "Jeff Sagal <sagalbot@gmail.com>",
"private": false,
+77 -3
View File
@@ -23,6 +23,10 @@
.v-select.rtl .dropdown-menu {
text-align: right;
}
.v-select.rtl .dropdown-toggle .clear {
left: 30px;
right: auto;
}
/* Open Indicator */
.v-select .open-indicator {
position: absolute;
@@ -80,6 +84,22 @@
clear: both;
height: 0;
}
/* Clear Button */
.v-select .dropdown-toggle .clear {
position: absolute;
bottom: 9px;
right: 30px;
font-size: 23px;
font-weight: 700;
line-height: 1;
color: rgba(60, 60, 60, .5);
padding: 0;
border: 0;
background-color: transparent;
cursor: pointer;
}
/* Dropdown Toggle States */
.v-select.searchable .dropdown-toggle {
cursor: text;
@@ -186,10 +206,14 @@
background: none;
position: relative;
box-shadow: none;
float: left;
clear: none;
}
/* List Items */
.v-select.unsearchable input[type="search"] {
opacity: 0;
}
.v-select.unsearchable input[type="search"]:hover {
cursor: pointer;
}
/* List Items */
.v-select li {
line-height: 1.42857143; /* Normalize line height */
}
@@ -244,6 +268,7 @@
/* Disabled state */
.v-select.disabled .dropdown-toggle,
.v-select.disabled .dropdown-toggle .clear,
.v-select.disabled .dropdown-toggle input,
.v-select.disabled .selected-tag .close,
.v-select.disabled .open-indicator {
@@ -308,14 +333,27 @@
@focus="onSearchFocus"
type="search"
class="form-control"
autocomplete="false"
:disabled="disabled"
:placeholder="searchPlaceholder"
:tabindex="tabindex"
:readonly="!searchable"
:style="{ width: isValueEmpty ? '100%' : 'auto' }"
:id="inputId"
aria-label="Search for option"
>
<button
v-show="showClearButton"
:disabled="disabled"
@click="clearSelection"
type="button"
class="clear"
title="Clear selection"
>
<span aria-hidden="true">&times;</span>
</button>
<i v-if="!noDrop" ref="openIndicator" role="presentation" class="open-indicator"></i>
<slot name="spinner">
@@ -535,6 +573,15 @@
default: false
},
/**
* Set the tabindex for the input field.
* @type {Number}
*/
tabindex: {
type: Number,
default: null
},
/**
* When true, newly created tags will be added to
* the options list.
@@ -545,6 +592,17 @@
default: false
},
/**
* When true, existing options will be filtered
* by the search text. Should not be used in conjunction
* with taggable.
* @type {Boolean}
*/
filterable: {
type: Boolean,
default: true
},
/**
* User defined function for adding Options
* @type {Function}
@@ -724,6 +782,14 @@
}
},
/**
* Clears the currently selected value(s)
* @return {void}
*/
clearSelection() {
this.mutableValue = this.multiple ? [] : null
},
/**
* Called from this.select after each selection.
* @param {Object|String} option
@@ -963,6 +1029,14 @@
}
return []
},
/**
* Determines if the clear button should be displayed.
* @return {Boolean}
*/
showClearButton() {
return !this.multiple && !this.open && this.mutableValue != null
}
},
+10
View File
@@ -27,6 +27,16 @@ new Vue({
loading(true);
this.getRepositories(search, loading, this)
},
searchPeople(search, loading) {
loading(true)
this.getPeople(loading, this)
},
getPeople: debounce((loading, vm) => {
vm.$http.get(`https://reqres.in/api/users?per_page=10`).then(res => {
vm.people = res.data.data
loading(false)
})
}, 250),
getRepositories: debounce((search, loading, vm) => {
vm.$http.get(`https://api.github.com/search/repositories?q=${search}`).then(res => {
vm.ajaxRes = res.data.items;
+134
View File
@@ -295,6 +295,15 @@ describe('Select.vue', () => {
expect(vm.$refs.select.filteredOptions).toEqual(['bar','baz'])
})
it('should not filter the array of strings if filterable is false', () => {
const vm = new Vue({
template: `<div><v-select ref="select" :filterable="false" :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(['foo','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>`,
@@ -900,6 +909,21 @@ describe('Select.vue', () => {
expect(vm.$children[0].mutableOptions).toEqual(['one', 'two', 'three'])
})
it('should add a freshly created option/tag to the options list when pushTags is true and filterable is false', () => {
const vm = new Vue({
template: '<div><v-select :options="options" push-tags :value="value" :filterable="false" :multiple="true" :taggable="true"></v-select></div>',
components: {vSelect},
data: {
value: ['one'],
options: ['one', 'two']
}
}).$mount()
searchSubmit(vm, 'three')
expect(vm.$children[0].mutableOptions).toEqual(['one', 'two', 'three'])
expect(vm.$children[0].filteredOptions).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="value" :multiple="true" :taggable="true"></v-select></div>',
@@ -914,6 +938,21 @@ describe('Select.vue', () => {
expect(vm.$children[0].mutableOptions).toEqual(['one', 'two'])
})
it('wont add a freshly created option/tag to the options list when pushTags is false and filterable is false', () => {
const vm = new Vue({
template: '<div><v-select :options="options" :value="value" :multiple="true" :filterable="false" :taggable="true"></v-select></div>',
components: {vSelect},
data: {
value: ['one'],
options: ['one', 'two']
}
}).$mount()
searchSubmit(vm, 'three')
expect(vm.$children[0].mutableOptions).toEqual(['one', 'two'])
expect(vm.$children[0].filteredOptions).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({
@@ -955,6 +994,28 @@ describe('Select.vue', () => {
})
})
it('should select an existing option if the search string matches an objects label from options when filter-options is false', (done) => {
let two = {label: 'two'}
const vm = new Vue({
template: '<div><v-select :options="options" taggable :filterable="false"></v-select></div>',
data: {
options: [{label: 'one'}, two]
}
}).$mount()
vm.$children[0].search = 'two'
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(() => {
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="value" :multiple="true" taggable></v-select></div>',
@@ -971,6 +1032,22 @@ describe('Select.vue', () => {
})
})
it('should not reset the selected value when the options property changes when filterable is false', (done) => {
const vm = new Vue({
template: '<div><v-select :options="options" :value="value" :multiple="true" :filterable="false" taggable></v-select></div>',
components: {vSelect},
data: {
value: [{label: 'one'}],
options: [{label: 'one'}]
}
}).$mount()
vm.$children[0].mutableOptions = [{label: 'two'}]
Vue.nextTick(() => {
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>`,
@@ -1234,4 +1311,61 @@ describe('Select.vue', () => {
})
})
})
describe( 'Clear button', () => {
it( 'should be displayed on single select when value is selected', () => {
const VueSelect = Vue.extend( vSelect )
const vm = new VueSelect({
propsData: {
options: ['foo','bar'],
value: 'foo'
}
}).$mount()
expect(vm.showClearButton).toEqual(true)
})
it( 'should not be displayed on multiple select', () => {
const VueSelect = Vue.extend( vSelect )
const vm = new VueSelect({
propsData: {
options: ['foo','bar'],
value: 'foo',
multiple: true
}
}).$mount()
expect(vm.showClearButton).toEqual(false)
})
it( 'should remove selected value when clicked', () => {
const VueSelect = Vue.extend( vSelect )
const vm = new VueSelect({
propsData: {
options: ['foo','bar'],
value: 'foo'
}
}).$mount()
expect(vm.mutableValue).toEqual('foo')
vm.$el.querySelector( 'button.clear' ).click()
expect(vm.mutableValue).toEqual(null)
})
it( 'should be disabled when component is disabled', () => {
const VueSelect = Vue.extend( vSelect )
const vm = new VueSelect({
propsData: {
options: ['foo','bar'],
value: 'foo',
disabled: true
}
}).$mount()
const buttonEl = vm.$el.querySelector( 'button.clear' )
expect(buttonEl.disabled).toEqual(true);
})
});
})