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

enforce value param to be of type array

This commit is contained in:
Jeff Sagal
2016-03-07 16:42:49 -08:00
parent a3d2a739ab
commit 50d1991641
5 changed files with 219 additions and 108 deletions
+48 -38
View File
File diff suppressed because one or more lines are too long
+76 -14
View File
@@ -4,22 +4,12 @@
<h1>Vue Select</h1>
<p class="lead">A simple component that provides similar functionality to Select2 without the overhead of jQuery.</p>
<div class="row">
<div class="col-md-6">
<h3>Options</h3>
<div class="checkbox">
<label>
<input v-model="multiple" type="checkbox"> Allow Multiple
</label>
</div>
</div>
</div>
<hr>
<div class="row">
<div class="col-md-6">
<v-select
placeholder="choose something"
:placeholder="placeholder"
:value.sync="select"
:options="options"
:multiple="multiple">
@@ -30,20 +20,92 @@
<pre><strong>Selected:</strong>{{ select | json }}</pre>
</div>
</div>
<hr>
<div class="row">
<div class="col-md-6">
<h4>Todos:</h4>
<ul>
<li>Fix layout issue where selected tags & text input overflow outside <code>.dropdown</code>.</li>
<li>Clicking the 'caret' icon should toggle the dropdown.</li>
<li>Add boolean prop to disable search.</li>
<li>Add a 'simple' prop that disables search, and the selected 'tags'. Uses an active class on the selected item(s) while keeping the placeholder constant.</li>
</ul>
</div>
<div class="col-md-6">
<h4>Component Props</h4>
<div class="form-group">
<label class="control-label">Max Height</label>
<input type="text" v-model="maxHeight" class="form-control">
<span class="help-block">Limit the height of the dropdown menu.</span>
</div>
<div class="form-group">
<label class="control-label">Placeholder</label>
<input type="text" v-model="placeholder" class="form-control">
<span class="help-block">Equivalent to the <code>placeholder</code> attribute.</span>
</div>
<div class="checkbox">
<label class="control-label">
<input v-model="multiple" type="checkbox"> Multiple
</label>
<span class="help-block">Equivalent to the <code>multiple</code> attribute to a <code>&#x3C;select&#x3E;</code></span>
</div>
<div class="form-group">
</div>
</div>
</div>
<hr>
<h3>Build Setup</h3>
<h5>install dependencies</h5>
<p><code>npm install</code></p>
<h5>serve with hot reload at localhost:8080</h5>
<p><code>npm run dev</code></p>
<h5>build for production with minification</h5>
<p><code>npm run build</code></p>
<h5>lint all *.js and *.vue files</h5>
<p><code>npm run lint</code></p>
<h5>run unit tests</h5>
<p><code>npm test</code></p>
</div>
</template>
<script>
import Vue from 'vue'
import vSelect from './components/Select.vue'
Vue.config.debug = true
export default {
components: {vSelect},
data() {
return {
select: [],
// select: [{label: 'This is Foo', value: 'foo'}, {label: 'This is Bar', value: 'bar'}],
select: ['one'],
placeholder: 'Choose a Country',
multiple: true,
options: require('./countries.js')
maxHeight: '400px',
// options: require('./countries.js')
options: ['one','two','three']
// options: [{label: 'This is Foo', value: 'foo'}, {label: 'This is Bar', value: 'bar'}, {label: 'This is Baz', value: 'baz'}]
}
}
}
+55 -39
View File
@@ -1,10 +1,13 @@
<style scoped>
.dropdown {
position: relative;
}
.dropdown-toggle {
display: block;
padding: 0;
max-width: 100%;
background: none;
text-align: left;
border: 1px solid rgba(60,60,60,.26);
}
.dropdown-toggle:hover,
@@ -27,7 +30,7 @@
.dropdown-menu {
margin: 0;
max-height: 400px;
width: 100%;
overflow-y: scroll;
border-top: none;
border-color: #337ab7;
@@ -37,7 +40,7 @@
.alert {
margin: 0;
margin-left: .25em;
margin-left: 6px;
padding: .25em;
}
@@ -55,6 +58,7 @@
width: 100%;
background: none;
position: relative;
box-shadow: none;
}
input[type=search]:focus {
@@ -67,8 +71,6 @@
.dropdown-toggle:after {
display: block;
/*width: 10px;
height: 10px;*/
position: absolute;
top: 10px;
right: 10px;
@@ -79,7 +81,9 @@
line-height: 1;
-webkit-font-smoothing: antialiased;
content: "\e114";
transition: all .25s;
transition: all 150ms cubic-bezier(1.000, -0.115, 0.975, 0.855);
transition-timing-function: cubic-bezier(1.000, -0.115, 0.975, 0.855);
}
.open .dropdown-toggle:after {
@@ -94,17 +98,22 @@
cursor: pointer;
}
.highlight a,
.active.highlight a {
.active a {
background: rgba(50,50,50,.1);
color: #333;
}
.highlight a,
li:hover a {
background: #337ab7;
color: #fff;
}
</style>
<template>
<div class="dropdown" :class="{open: open, }">
<div v-el:toggle @click="toggleDropdown" class="btn btn-default dropdown-toggle clearfix" type="button">
<span class="form-control" v-if="! searchable && placeholder && ! selected.length">
<div v-el:toggle @click="toggleDropdown" class="btn dropdown-toggle clearfix" type="button">
<span class="form-control" v-if="! searchable && placeholder && ! value.length">
{{ placeholder }}
</span>
@@ -116,22 +125,25 @@
</span>
<input
v-el:search
v-show="searchable"
v-model="search"
@keydown.delete="maybeDeleteValue"
@keydown.esc="onEscape"
@keydown.up.prevent="typeAheadUp"
@keydown.down.prevent="typeAheadDown"
@keydown.enter.prevent="typeAheadSelect"
v-bind:placeholder="searchPlaceholder"
@focus="open = true"
@blur="open = false"
type="search"
class="form-control"
:class="{inline: selected.length}"
v-el:search type="search"
v-show="searchable"
v-model="search"
:placeholder="searchPlaceholder"
>
</div>
<ul v-if="open" :style="{width: dropdownWidth + 'px'}" :transition="transition" class="dropdown-menu animated">
<li v-for="option in filteredOptions" :class="{ active: value.indexOf(option.value) !== -1, highlight: $index === typeAheadPointer }">
<ul v-show="open" :transition="transition" :style="{ 'max-height': maxHeight }" class="dropdown-menu animated">
<li v-for="option in filteredOptions" :class="{ active: value.indexOf(option) !== -1, highlight: $index === typeAheadPointer }">
<a @mousedown.prevent="select(option)">
{{ getOptionLabel(option) }}
</a>
@@ -148,9 +160,14 @@
props: {
value: {
type: Array,
twoway: true,
required: true
},
maxHeight: {
type: String,
default: '400px'
},
searchable: {
type: Boolean,
default: true
@@ -161,7 +178,7 @@
},
placeholder: {
type: String,
default: null
default: ''
},
transition: {
type: String,
@@ -216,9 +233,9 @@
},
toggleDropdown( e ) {
if( e.target == this.$els.toggle || e.target == this.$els.search ) {
this.open = !this.open
}
// if( e.target == this.$els.toggle || e.target == this.$els.search ) {
// this.open = !this.open
// }
},
getOptionValue( option ) {
@@ -247,7 +264,7 @@
typeAheadSelect() {
if( this.filteredOptions[ this.typeAheadPointer ] ) {
this.select( this.filteredOptions[ this.typeAheadPointer ] );
this.select( this.filteredOptions[ this.typeAheadPointer ] );
}
this.search = "";
},
@@ -268,18 +285,21 @@
},
computed: {
selected() {
let foundItems = []
if (this.value.length) {
for (let item in this.value) {
if (typeof this.value[item] === "string") {
foundItems.push(this.value[item])
}
}
}
return foundItems
},
// selected() {
// let foundItems = []
// if (this.value && this.value.length) {
// for (let item in this.value) {
// if (typeof this.value[item] === "string") {
// foundItems.push(this.value[item])
// }
//
// console.log(this.value[item])
// // this.$log('value')
// }
// }
//
// return foundItems
// },
searchPlaceholder() {
if( ! this.value.length && this.placeholder ) {
@@ -287,10 +307,6 @@
}
},
dropdownWidth() {
return this.$els.toggle.offsetWidth
},
filteredOptions() {
return this.$options.filters.filterBy(this.options, this.search)
}
-17
View File
@@ -1,17 +0,0 @@
/* global describe, it, expect */
import Vue from 'vue'
import Hello from '../../src/components/Hello.vue'
describe('Hello.vue', () => {
it('should render correct contents', () => {
const vm = new Vue({
template: '<div><hello></hello></div>',
components: { Hello }
}).$mount()
expect(vm.$el.querySelector('.hello h1').textContent).toBe('Hello World!')
})
})
// also see example testing a component with mocks at
// https://github.com/vuejs/vueify-example/blob/master/test/unit/a.spec.js#L22-L43
+40
View File
@@ -0,0 +1,40 @@
/* global describe, it, expect */
import Vue from 'vue'
import vSelect from '../../src/components/Select.vue'
describe('Select.vue', () => {
it('can accept an array with pre-selected values', () => {
const vm = new Vue({
template: '<div><v-select :value.sync="value"></v-select></div>',
components: { vSelect },
data: {
value: ['one'],
options: ['one','two','three']
}
}).$mount()
expect(vm.$children[0].value).toEqual(['one'])
})
/**
* TODO: Right now this only works for arrays of strings.. But the same method
* should apply to arrays of objects.
*/
it('can accept an array of objects and pre-selected values', () => {
const vm = new Vue({
template: '<div><v-select :value.sync="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].$get('value')).toEqual({label: 'This is Foo', value: 'foo'})
})
})
// also see example testing a component with mocks at
// https://github.com/vuejs/vueify-example/blob/master/test/unit/a.spec.js#L22-L43