mirror of
https://github.com/tenrok/vue-select.git
synced 2026-06-25 11:10:32 +03:00
fix #54 and add regression test, use 2 space indents
This commit is contained in:
@@ -7,7 +7,8 @@ Rather than bringing in jQuery just to use Select2 or Chosen, this Vue.js compon
|
|||||||
|
|
||||||
#### Features
|
#### Features
|
||||||
|
|
||||||
- **Tagging Support (+v.1.1.0)**
|
- **AJAX Support +v1.2.0**
|
||||||
|
- Tagging Support **+v.1.1.0**
|
||||||
- No JS Dependencies
|
- No JS Dependencies
|
||||||
- List Filtering/Searching
|
- List Filtering/Searching
|
||||||
- Supports Vuex
|
- Supports Vuex
|
||||||
@@ -17,9 +18,9 @@ Rather than bringing in jQuery just to use Select2 or Chosen, this Vue.js compon
|
|||||||
|
|
||||||
#### Upcoming/In Progress
|
#### Upcoming/In Progress
|
||||||
|
|
||||||
- ~~Tagging (adding options not present in list, see `taggable` branch)~~ **added in v.1.1.0**
|
- ~~Tagging (adding options not present in list, see `taggable` branch)~~ **+v.1.1.0**
|
||||||
|
- ~~Asyncronous Option Loading~~ **+v.1.2.0**
|
||||||
- Rich Option Templating
|
- Rich Option Templating
|
||||||
- Asyncronous Option Loading
|
|
||||||
|
|
||||||
## Live Examples & Docs
|
## Live Examples & Docs
|
||||||
[http://sagalbot.github.io/vue-select/](http://sagalbot.github.io/vue-select/)
|
[http://sagalbot.github.io/vue-select/](http://sagalbot.github.io/vue-select/)
|
||||||
|
|||||||
+40
-32
@@ -21,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.v-select .open-indicator:before {
|
.v-select .open-indicator:before {
|
||||||
border-color: rgba(60,60,60,.5);
|
border-color: rgba(60, 60, 60, .5);
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: 0.25em 0.25em 0 0;
|
border-width: 0.25em 0.25em 0 0;
|
||||||
content: '';
|
content: '';
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
display: block;
|
display: block;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: none;
|
background: none;
|
||||||
border: 1px solid rgba(60,60,60,.26);
|
border: 1px solid rgba(60, 60, 60, .26);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.v-select .active a {
|
.v-select .active a {
|
||||||
background: rgba(50,50,50,.1);
|
background: rgba(50, 50, 50, .1);
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,23 +130,26 @@
|
|||||||
right: 10px;
|
right: 10px;
|
||||||
font-size: 5px;
|
font-size: 5px;
|
||||||
text-indent: -9999em;
|
text-indent: -9999em;
|
||||||
border-top: .9em solid rgba(100,100,100,.1);
|
border-top: .9em solid rgba(100, 100, 100, .1);
|
||||||
border-right: .9em solid rgba(100,100,100,.1);
|
border-right: .9em solid rgba(100, 100, 100, .1);
|
||||||
border-bottom: .9em solid rgba(100,100,100,.1);
|
border-bottom: .9em solid rgba(100, 100, 100, .1);
|
||||||
border-left: .9em solid rgba(60,60,60,.45);
|
border-left: .9em solid rgba(60, 60, 60, .45);
|
||||||
transform: translateZ(0);
|
transform: translateZ(0);
|
||||||
animation: vSelectSpinner 1.1s infinite linear;
|
animation: vSelectSpinner 1.1s infinite linear;
|
||||||
transition: opacity .1s;
|
transition: opacity .1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-select.loading .spinner {
|
.v-select.loading .spinner {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-select .spinner,
|
.v-select .spinner,
|
||||||
.v-select .spinner:after {
|
.v-select .spinner:after {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
width: 5em;
|
width: 5em;
|
||||||
height: 5em;
|
height: 5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@-webkit-keyframes vSelectSpinner {
|
@-webkit-keyframes vSelectSpinner {
|
||||||
0% {
|
0% {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
@@ -155,6 +158,7 @@
|
|||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes vSelectSpinner {
|
@keyframes vSelectSpinner {
|
||||||
0% {
|
0% {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
@@ -172,7 +176,7 @@
|
|||||||
{{ placeholder }}
|
{{ placeholder }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="selected-tag" v-for="option in valueAsArray">
|
<span class="selected-tag" v-for="option in valueAsArray" track-by="$index">
|
||||||
{{ getOptionLabel(option) }}
|
{{ getOptionLabel(option) }}
|
||||||
<button v-if="multiple" @click="select(option)" type="button" class="close">
|
<button v-if="multiple" @click="select(option)" type="button" class="close">
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
@@ -249,7 +253,9 @@
|
|||||||
*/
|
*/
|
||||||
options: {
|
options: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default() { return [] },
|
default() {
|
||||||
|
return []
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -327,8 +333,8 @@
|
|||||||
getOptionLabel: {
|
getOptionLabel: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default(option) {
|
default(option) {
|
||||||
if( typeof option === 'object' ) {
|
if (typeof option === 'object') {
|
||||||
if( this.label && option[this.label] ) {
|
if (this.label && option[this.label]) {
|
||||||
return option[this.label]
|
return option[this.label]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -388,14 +394,18 @@
|
|||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
value(val, old) {
|
value(val, old) {
|
||||||
|
if (this.multiple) {
|
||||||
|
this.onChange ? this.onChange(val) : null
|
||||||
|
} else {
|
||||||
this.onChange && val !== old ? this.onChange(val) : null
|
this.onChange && val !== old ? this.onChange(val) : null
|
||||||
|
}
|
||||||
},
|
},
|
||||||
options() {
|
options() {
|
||||||
if (!this.taggable) {
|
if (!this.taggable) {
|
||||||
this.$set('value', this.multiple ? [] : null)
|
this.$set('value', this.multiple ? [] : null)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
multiple( val ) {
|
multiple(val) {
|
||||||
this.$set('value', val ? [] : null)
|
this.$set('value', val ? [] : null)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -412,19 +422,17 @@
|
|||||||
if (this.taggable && !this.optionExists(option)) {
|
if (this.taggable && !this.optionExists(option)) {
|
||||||
option = this.createOption(option)
|
option = this.createOption(option)
|
||||||
|
|
||||||
if( this.pushTags ) {
|
if (this.pushTags) {
|
||||||
this.options.push(option)
|
this.options.push(option)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.multiple) {
|
if (this.multiple) {
|
||||||
|
|
||||||
if (!this.value) {
|
if (!this.value) {
|
||||||
this.$set('value', [option])
|
this.$set('value', [option])
|
||||||
} else {
|
} else {
|
||||||
this.value.push(option)
|
this.value.push(option)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.value = option
|
this.value = option
|
||||||
}
|
}
|
||||||
@@ -448,7 +456,7 @@
|
|||||||
this.$els.search.blur()
|
this.$els.search.blur()
|
||||||
}
|
}
|
||||||
|
|
||||||
if( this.clearSearchOnSelect ) {
|
if (this.clearSearchOnSelect) {
|
||||||
this.search = ''
|
this.search = ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -459,8 +467,8 @@
|
|||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
toggleDropdown(e) {
|
toggleDropdown(e) {
|
||||||
if( e.target === this.$els.openIndicator || e.target === this.$els.search || e.target === this.$els.toggle || e.target === this.$el ) {
|
if (e.target === this.$els.openIndicator || e.target === this.$els.search || e.target === this.$els.toggle || e.target === this.$el) {
|
||||||
if( this.open ) {
|
if (this.open) {
|
||||||
this.$els.search.blur() // dropdown will close on blur
|
this.$els.search.blur() // dropdown will close on blur
|
||||||
} else {
|
} else {
|
||||||
this.open = true
|
this.open = true
|
||||||
@@ -474,13 +482,13 @@
|
|||||||
* @param {Object||String} option
|
* @param {Object||String} option
|
||||||
* @return {Boolean} True when selected || False otherwise
|
* @return {Boolean} True when selected || False otherwise
|
||||||
*/
|
*/
|
||||||
isOptionSelected( option ) {
|
isOptionSelected(option) {
|
||||||
if( this.multiple && this.value ) {
|
if (this.multiple && this.value) {
|
||||||
let selected = false
|
let selected = false
|
||||||
this.value.forEach(opt => {
|
this.value.forEach(opt => {
|
||||||
if( typeof opt === 'object' && opt[this.label] === option ) {
|
if (typeof opt === 'object' && opt[this.label] === option) {
|
||||||
selected = true
|
selected = true
|
||||||
} else if( opt === option ) {
|
} else if (opt === option) {
|
||||||
selected = true
|
selected = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -496,7 +504,7 @@
|
|||||||
* @return {[type]} [description]
|
* @return {[type]} [description]
|
||||||
*/
|
*/
|
||||||
onEscape() {
|
onEscape() {
|
||||||
if( ! this.search.length ) {
|
if (!this.search.length) {
|
||||||
this.$els.search.blur()
|
this.$els.search.blur()
|
||||||
} else {
|
} else {
|
||||||
this.search = ''
|
this.search = ''
|
||||||
@@ -509,7 +517,7 @@
|
|||||||
* @return {this.value}
|
* @return {this.value}
|
||||||
*/
|
*/
|
||||||
maybeDeleteValue() {
|
maybeDeleteValue() {
|
||||||
if( ! this.$els.search.value.length && this.value ) {
|
if (!this.$els.search.value.length && this.value) {
|
||||||
return this.multiple ? this.value.pop() : this.$set('value', null)
|
return this.multiple ? this.value.pop() : this.$set('value', null)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -525,9 +533,9 @@
|
|||||||
let exists = false
|
let exists = false
|
||||||
|
|
||||||
this.options.forEach(opt => {
|
this.options.forEach(opt => {
|
||||||
if( typeof opt === 'object' && opt[this.label] === option ) {
|
if (typeof opt === 'object' && opt[this.label] === option) {
|
||||||
exists = true
|
exists = true
|
||||||
} else if( opt === option ) {
|
} else if (opt === option) {
|
||||||
exists = true
|
exists = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -556,7 +564,7 @@
|
|||||||
* @return {String} Placeholder text
|
* @return {String} Placeholder text
|
||||||
*/
|
*/
|
||||||
searchPlaceholder() {
|
searchPlaceholder() {
|
||||||
if( this.isValueEmpty && this.placeholder ) {
|
if (this.isValueEmpty && this.placeholder) {
|
||||||
return this.placeholder;
|
return this.placeholder;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -582,11 +590,11 @@
|
|||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
isValueEmpty() {
|
isValueEmpty() {
|
||||||
if( this.value ) {
|
if (this.value) {
|
||||||
if( typeof this.value === 'object' ) {
|
if (typeof this.value === 'object') {
|
||||||
return ! Object.keys(this.value).length
|
return !Object.keys(this.value).length
|
||||||
}
|
}
|
||||||
return ! this.value.length
|
return !this.value.length
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -597,7 +605,7 @@
|
|||||||
* @return {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
valueAsArray() {
|
valueAsArray() {
|
||||||
if( this.multiple ) {
|
if (this.multiple) {
|
||||||
return this.value
|
return this.value
|
||||||
} else if (this.value) {
|
} else if (this.value) {
|
||||||
return [this.value]
|
return [this.value]
|
||||||
|
|||||||
@@ -190,32 +190,57 @@ describe('Select.vue', () => {
|
|||||||
expect(vm.$children[0].isOptionSelected('one')).toEqual(true)
|
expect(vm.$children[0].isOptionSelected('one')).toEqual(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('onChange Callback', () => {
|
||||||
it('can run a callback when the selection changes', (done) => {
|
it('can run a callback when the selection changes', (done) => {
|
||||||
const vm = new Vue({
|
const vm = new Vue({
|
||||||
template: '<div><v-select :on-change="foo" value="bar" :options="options"></v-select></div>',
|
template: `<div><v-select :value="['foo']" :options="['foo','bar','baz']" :on-change="cb"></v-select></div>`,
|
||||||
components: {vSelect},
|
components: {vSelect},
|
||||||
data: {
|
|
||||||
val: null,
|
|
||||||
options: ['foo', 'bar', 'baz']
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
foo(value) {
|
cb(val) {
|
||||||
this.val = value
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).$mount()
|
}).$mount()
|
||||||
|
|
||||||
vm.$children[0].select('foo')
|
spyOn(vm.$children[0], 'onChange')
|
||||||
Vue.nextTick(function () {
|
|
||||||
expect(vm.$get('val')).toEqual('foo')
|
|
||||||
|
|
||||||
vm.$children[0].$set('value', 'baz')
|
vm.$children[0].select('bar')
|
||||||
Vue.nextTick(function () {
|
|
||||||
expect(vm.$get('val')).toEqual('baz')
|
Vue.nextTick(() => {
|
||||||
|
expect(vm.$children[0].onChange).toHaveBeenCalledWith('bar')
|
||||||
|
vm.$children[0].select('baz')
|
||||||
|
|
||||||
|
Vue.nextTick(() => {
|
||||||
|
expect(vm.$children[0].onChange).toHaveBeenCalledWith('baz')
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should run onChange 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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).$mount()
|
||||||
|
|
||||||
|
spyOn(vm.$children[0], 'onChange')
|
||||||
|
|
||||||
|
vm.$children[0].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()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Toggling Dropdown', () => {
|
describe('Toggling Dropdown', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user