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

refactor: apply eslint fixes (#1469)

This commit is contained in:
Jeff Sagal
2021-08-01 12:30:50 -07:00
committed by GitHub
parent a64ffc32d0
commit dd281aa8c6
85 changed files with 3616 additions and 3208 deletions
+14 -17
View File
@@ -1,17 +1,14 @@
const path = require('path'); const path = require('path')
const webpack = require('webpack'); const webpack = require('webpack')
const VueLoaderPlugin = require('vue-loader').VueLoaderPlugin; const VueLoaderPlugin = require('vue-loader').VueLoaderPlugin
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const env = process.env.NODE_ENV === 'production' const env = process.env.NODE_ENV === 'production' ? 'production' : 'development'
? 'production'
: 'development';
const devtool = env === 'production' ? 'source-map' : 'eval-source-map'; const devtool = env === 'production' ? 'source-map' : 'eval-source-map'
const extractOrInjectStyles = env !== 'production' const extractOrInjectStyles =
? 'vue-style-loader' env !== 'production' ? 'vue-style-loader' : MiniCssExtractPlugin.loader
: MiniCssExtractPlugin.loader;
module.exports = { module.exports = {
mode: env, mode: env,
@@ -24,11 +21,11 @@ module.exports = {
resolve: { resolve: {
extensions: ['.js', '.vue'], extensions: ['.js', '.vue'],
alias: { alias: {
'src': path.resolve(__dirname, '../src'), src: path.resolve(__dirname, '../src'),
'assets': path.resolve(__dirname, '../docs/assets'), assets: path.resolve(__dirname, '../docs/assets'),
'mixins': path.resolve(__dirname, '../src/mixins'), mixins: path.resolve(__dirname, '../src/mixins'),
'components': path.resolve(__dirname, '../src/components'), components: path.resolve(__dirname, '../src/components'),
'vue$': 'vue/dist/vue.esm.js', vue$: 'vue/dist/vue.esm.js',
}, },
}, },
module: { module: {
@@ -67,4 +64,4 @@ module.exports = {
children: false, children: false,
modules: false, modules: false,
}, },
}; }
+9 -11
View File
@@ -1,7 +1,7 @@
const merge = require('webpack-merge'); const merge = require('webpack-merge')
const chokidar = require('chokidar'); const chokidar = require('chokidar')
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin')
const baseWebpackConfig = require('./webpack.base.conf'); const baseWebpackConfig = require('./webpack.base.conf')
/** /**
* *
@@ -28,12 +28,10 @@ module.exports = merge(baseWebpackConfig, {
chunks: false, chunks: false,
}, },
port: 8080, port: 8080,
before (app, server) { before(app, server) {
chokidar.watch([ chokidar.watch(['./**/*.html']).on('all', function () {
'./**/*.html', server.sockWrite(server.sockets, 'content-changed')
]).on('all', function () { })
server.sockWrite(server.sockets, 'content-changed');
});
}, },
}, },
}); })
+6 -6
View File
@@ -1,6 +1,6 @@
const TerserPlugin = require('terser-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin')
const merge = require('webpack-merge'); const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf'); const baseWebpackConfig = require('./webpack.base.conf')
module.exports = merge(baseWebpackConfig, { module.exports = merge(baseWebpackConfig, {
entry: './src/index.js', entry: './src/index.js',
@@ -8,7 +8,7 @@ module.exports = merge(baseWebpackConfig, {
filename: 'vue-select.js', filename: 'vue-select.js',
library: 'VueSelect', library: 'VueSelect',
libraryTarget: 'umd', libraryTarget: 'umd',
globalObject: 'typeof self !== \'undefined\' ? self : this', globalObject: "typeof self !== 'undefined' ? self : this",
}, },
optimization: { optimization: {
minimizer: [ minimizer: [
@@ -16,5 +16,5 @@ module.exports = merge(baseWebpackConfig, {
sourceMap: true, sourceMap: true,
}), }),
], ],
} },
}); })
+7 -7
View File
@@ -5,19 +5,19 @@
</template> </template>
<script> <script>
import vSelect from "../src/components/Select"; import vSelect from '../src/components/Select'
import countries from "../docs/.vuepress/data/countryCodes"; import countries from '../docs/.vuepress/data/countryCodes'
import books from "../docs/.vuepress/data/books"; import books from '../docs/.vuepress/data/books'
export default { export default {
components: { vSelect }, components: { vSelect },
data: () => ({ data: () => ({
selected: null, selected: null,
config: { config: {
options: countries options: countries,
} },
}) }),
}; }
</script> </script>
<style> <style>
+5 -5
View File
@@ -1,8 +1,8 @@
import Vue from "vue"; import Vue from 'vue'
import Dev from "./Dev.vue"; import Dev from './Dev.vue'
Vue.config.productionTip = false; Vue.config.productionTip = false
new Vue({ new Vue({
render: h => h(Dev) render: (h) => h(Dev),
}).$mount("#app"); }).$mount('#app')
@@ -2,9 +2,10 @@
<v-select> <v-select>
<template v-slot:no-options="{ search, searching }"> <template v-slot:no-options="{ search, searching }">
<template v-if="searching"> <template v-if="searching">
No results found for <em>{{ search }}</em>. No results found for <em>{{ search }}</em
>.
</template> </template>
<em style="opacity: 0.5;" v-else>Start typing to search for a country.</em> <em v-else style="opacity: 0.5">Start typing to search for a country.</em>
</template> </template>
</v-select> </v-select>
</template> </template>
@@ -12,5 +13,5 @@
<script> <script>
export default { export default {
name: 'BetterNoOptions', name: 'BetterNoOptions',
}; }
</script> </script>
@@ -1,10 +1,10 @@
<template> <template>
<div> <div>
<v-select <v-select
placeholder="choose a country" v-model="selected"
v-model="selected" placeholder="choose a country"
:components="{Deselect}" :components="{ Deselect }"
:options="['Canada', 'United States']" :options="['Canada', 'United States']"
/> />
</div> </div>
</template> </template>
@@ -14,8 +14,8 @@ export default {
data: () => ({ data: () => ({
selected: 'Canada', selected: 'Canada',
Deselect: { Deselect: {
render: createElement => createElement('span', '❌'), render: (createElement) => createElement('span', '❌'),
}, },
}), }),
}; }
</script> </script>
+15 -13
View File
@@ -1,15 +1,16 @@
<template> <template>
<p :data-height="height" <p
data-theme-id="32252" :data-height="height"
:data-slug-hash="url" data-theme-id="32252"
data-default-tab="result" :data-slug-hash="url"
data-user="sagalbot" data-default-tab="result"
class="codepen"> data-user="sagalbot"
</p> class="codepen"
></p>
</template> </template>
<script> <script>
import mountCodePen from '../utils/codePen'; import mountCodePen from '../utils/codePen'
export default { export default {
props: { props: {
@@ -18,11 +19,12 @@ export default {
required: true, required: true,
}, },
height: { height: {
default: 250 type: [String, Number],
} default: 250,
},
}, },
mounted () { mounted() {
mountCodePen(); mountCodePen()
}, },
}; }
</script> </script>
+10 -9
View File
@@ -1,14 +1,15 @@
<template> <template>
<ul> <ul>
<li v-for="{ login, avatar_url, html_url, contributions } in contributors"> <li
v-for="{ login, avatar_url, html_url, contributions } in contributors"
:key="login"
>
<img :src="`${avatar_url}&s=75`" :alt="`${login}'s Avatar`" /> <img :src="`${avatar_url}&s=75`" :alt="`${login}'s Avatar`" />
<div> <div>
<a :href="html_url">@{{ login }}</a> <a :href="html_url">@{{ login }}</a>
<br /><a <br /><a
class="contributions-link" class="contributions-link"
:href=" :href="`https://github.com/sagalbot/vue-select/commits?author=${login}`"
`https://github.com/sagalbot/vue-select/commits?author=${login}`
"
>{{ contributions }} contributions</a >{{ contributions }} contributions</a
> >
</div> </div>
@@ -17,15 +18,15 @@
</template> </template>
<script> <script>
import { CONTRIBUTORS } from "@dynamic/constants"; import { CONTRIBUTORS } from '@dynamic/constants'
export default { export default {
data: () => ({ data: () => ({
contributors: CONTRIBUTORS.filter( contributors: CONTRIBUTORS.filter(
({ login }) => login !== "semantic-release-bot" ({ login }) => login !== 'semantic-release-bot'
) ),
}) }),
}; }
</script> </script>
<style scoped> <style scoped>
+3 -5
View File
@@ -3,16 +3,14 @@
</template> </template>
<script> <script>
import countryCodes from '../data/countryCodes'; import countryCodes from '../data/countryCodes'
export default { export default {
name: 'CountrySelect', name: 'CountrySelect',
data: () => ({ data: () => ({
options: countryCodes, options: countryCodes,
}), }),
}; }
</script> </script>
<style scoped> <style scoped></style>
</style>
+13 -13
View File
@@ -9,18 +9,18 @@
</template> </template>
<style> <style>
.style-chooser .vs__search::placeholder, .style-chooser .vs__search::placeholder,
.style-chooser .vs__dropdown-toggle, .style-chooser .vs__dropdown-toggle,
.style-chooser .vs__dropdown-menu { .style-chooser .vs__dropdown-menu {
background: #dfe5fb; background: #dfe5fb;
border: none; border: none;
color: #394066; color: #394066;
text-transform: lowercase; text-transform: lowercase;
font-variant: small-caps; font-variant: small-caps;
} }
.style-chooser .vs__clear, .style-chooser .vs__clear,
.style-chooser .vs__open-indicator { .style-chooser .vs__open-indicator {
fill: #394066; fill: #394066;
} }
</style> </style>
@@ -1,7 +1,7 @@
<template> <template>
<v-select <v-select
:options="['Vue.js', 'React', 'Angular']" :options="['Vue.js', 'React', 'Angular']"
:components="{Deselect, OpenIndicator}" :components="{ Deselect, OpenIndicator }"
/> />
</template> </template>
@@ -9,11 +9,11 @@
export default { export default {
data: () => ({ data: () => ({
Deselect: { Deselect: {
render: createElement => createElement('span', '❌'), render: (createElement) => createElement('span', '❌'),
}, },
OpenIndicator: { OpenIndicator: {
render: createElement => createElement('span', '🔽'), render: (createElement) => createElement('span', '🔽'),
}, },
}), }),
}; }
</script> </script>
+11 -10
View File
@@ -1,10 +1,10 @@
<template> <template>
<v-select <v-select
taggable taggable
multiple multiple
no-drop no-drop
:map-keydown="handlers" :map-keydown="handlers"
placeholder="enter an email" placeholder="enter an email"
/> />
</template> </template>
@@ -13,13 +13,14 @@ export default {
name: 'CustomHandlers', name: 'CustomHandlers',
methods: { methods: {
handlers: (map, vm) => ({ handlers: (map, vm) => ({
...map, 50: e => { ...map,
e.preventDefault(); 50: (e) => {
if( e.key === '@' && vm.search.length > 0 ) { e.preventDefault()
vm.search = `${vm.search}@gmail.com`; if (e.key === '@' && vm.search.length > 0) {
vm.search = `${vm.search}@gmail.com`
} }
}, },
}), }),
}, },
}; }
</script> </script>
+15 -11
View File
@@ -1,5 +1,9 @@
<template> <template>
<v-select :filter="fuseSearch" :options="books" :getOptionLabel="option => option.title"> <v-select
:filter="fuseSearch"
:options="books"
:get-option-label="(option) => option.title"
>
<template #option="{ author, title }"> <template #option="{ author, title }">
{{ title }} {{ title }}
<br /> <br />
@@ -9,23 +13,23 @@
</template> </template>
<script> <script>
import Fuse from "fuse.js"; import Fuse from 'fuse.js'
import books from "../data/books"; import books from '../data/books'
export default { export default {
computed: { computed: {
books: () => books books: () => books,
}, },
methods: { methods: {
fuseSearch(options, search) { fuseSearch(options, search) {
const fuse = new Fuse(options, { const fuse = new Fuse(options, {
keys: ["title", "author.firstName", "author.lastName"], keys: ['title', 'author.firstName', 'author.lastName'],
shouldSort: true shouldSort: true,
}); })
return search.length return search.length
? fuse.search(search).map(({ item }) => item) ? fuse.search(search).map(({ item }) => item)
: fuse.list; : fuse.list
} },
} },
}; }
</script> </script>
+34 -34
View File
@@ -4,10 +4,10 @@
:filterable="false" :filterable="false"
@open="onOpen" @open="onOpen"
@close="onClose" @close="onClose"
@search="query => search = query" @search="(query) => (search = query)"
> >
<template #list-footer> <template #list-footer>
<li ref="load" class="loader" v-show="hasNextPage"> <li v-show="hasNextPage" ref="load" class="loader">
Loading more options... Loading more options...
</li> </li>
</template> </template>
@@ -15,60 +15,60 @@
</template> </template>
<script> <script>
import countries from '../data/countries'; import countries from '../data/countries'
export default { export default {
name: "InfiniteScroll", name: 'InfiniteScroll',
data: () => ({ data: () => ({
observer: null, observer: null,
limit: 10, limit: 10,
search: '' search: '',
}), }),
mounted () { computed: {
filtered() {
return countries.filter((country) => country.includes(this.search))
},
paginated() {
return this.filtered.slice(0, this.limit)
},
hasNextPage() {
return this.paginated.length < this.filtered.length
},
},
mounted() {
/** /**
* You could do this directly in data(), but since these docs * You could do this directly in data(), but since these docs
* are server side rendered, IntersectionObserver doesn't exist * are server side rendered, IntersectionObserver doesn't exist
* in that environment, so we need to do it in mounted() instead. * in that environment, so we need to do it in mounted() instead.
*/ */
this.observer = new IntersectionObserver(this.infiniteScroll); this.observer = new IntersectionObserver(this.infiniteScroll)
},
computed: {
filtered () {
return countries.filter(country => country.includes(this.search));
},
paginated () {
return this.filtered.slice(0, this.limit);
},
hasNextPage () {
return this.paginated.length < this.filtered.length;
},
}, },
methods: { methods: {
async onOpen () { async onOpen() {
if (this.hasNextPage) { if (this.hasNextPage) {
await this.$nextTick(); await this.$nextTick()
this.observer.observe(this.$refs.load) this.observer.observe(this.$refs.load)
} }
}, },
onClose () { onClose() {
this.observer.disconnect(); this.observer.disconnect()
}, },
async infiniteScroll ([{isIntersecting, target}]) { async infiniteScroll([{ isIntersecting, target }]) {
if (isIntersecting) { if (isIntersecting) {
const ul = target.offsetParent; const ul = target.offsetParent
const scrollTop = target.offsetParent.scrollTop; const scrollTop = target.offsetParent.scrollTop
this.limit += 10; this.limit += 10
await this.$nextTick(); await this.$nextTick()
ul.scrollTop = scrollTop; ul.scrollTop = scrollTop
} }
} },
} },
} }
</script> </script>
<style scoped> <style scoped>
.loader { .loader {
text-align: center; text-align: center;
color: #bbbbbb; color: #bbbbbb;
} }
</style> </style>
@@ -1,21 +1,21 @@
<template> <template>
<v-select <v-select
v-model="selected"
multiple multiple
placeholder="Choose up to 3 books!" placeholder="Choose up to 3 books!"
label="title" label="title"
v-model="selected"
:options="books" :options="books"
:selectable="() => selected.length < 3" :selectable="() => selected.length < 3"
/> />
</template> </template>
<script> <script>
import books from '../data/books'; import books from '../data/books'
export default { export default {
data() { data() {
return { selected: [] } return { selected: [] }
}, },
computed: { computed: {
books: () => books, books: () => books,
} },
} }
</script> </script>
+17 -14
View File
@@ -4,13 +4,13 @@
<th>Name</th> <th>Name</th>
<th>Country</th> <th>Country</th>
</tr> </tr>
<tr v-for="person in people"> <tr v-for="person in people" :key="person.name">
<td>{{ person.name }}</td> <td>{{ person.name }}</td>
<td> <td>
<v-select <v-select
:options="options" :options="options"
:value="person.country" :value="person.country"
@input="country => updateCountry(person, country)" @input="(country) => updateCountry(person, country)"
/> />
</td> </td>
</tr> </tr>
@@ -18,26 +18,29 @@
</template> </template>
<script> <script>
import countries from '../data/countries'; import countries from '../data/countries'
export default { export default {
data: () => ({ data: () => ({
people: [{name: 'John', country: ''}, {name: 'Jane', country: ''}], people: [
{ name: 'John', country: '' },
{ name: 'Jane', country: '' },
],
}), }),
methods: {
updateCountry (person, country) {
person.country = country;
},
},
computed: { computed: {
options: () => countries, options: () => countries,
}, },
}; methods: {
updateCountry(person, country) {
person.country = country
},
},
}
</script> </script>
<style scoped> <style scoped>
table { table {
display: table; display: table;
width: 100%; width: 100%;
} }
</style> </style>
@@ -1,10 +1,10 @@
<template> <template>
<div> <div>
<v-select <v-select
multiple v-model="selected"
v-model="selected" multiple
:options="['Canada', 'United States']" :options="['Canada', 'United States']"
:components="{Deselect}" :components="{ Deselect }"
/> />
</div> </div>
</template> </template>
@@ -14,8 +14,8 @@ export default {
data: () => ({ data: () => ({
selected: ['Canada'], selected: ['Canada'],
Deselect: { Deselect: {
render: createElement => createElement('span', '❌'), render: (createElement) => createElement('span', '❌'),
}, },
}), }),
}; }
</script> </script>
@@ -1,8 +1,8 @@
<template> <template>
<div> <div>
<v-select <v-select
:options="['Canada', 'United States']" :options="['Canada', 'United States']"
:components="{OpenIndicator}" :components="{ OpenIndicator }"
/> />
</div> </div>
</template> </template>
@@ -11,8 +11,8 @@
export default { export default {
data: () => ({ data: () => ({
OpenIndicator: { OpenIndicator: {
render: createElement => createElement('span', '🔽'), render: (createElement) => createElement('span', '🔽'),
}, },
}), }),
}; }
</script> </script>
+34 -26
View File
@@ -1,14 +1,18 @@
<template> <template>
<v-select :options="paginated" @search="query => search = query" :filterable="false"> <v-select
:options="paginated"
:filterable="false"
@search="(query) => (search = query)"
>
<li slot="list-footer" class="pagination"> <li slot="list-footer" class="pagination">
<button @click="offset -= 10" :disabled="!hasPrevPage">Prev</button> <button :disabled="!hasPrevPage" @click="offset -= 10">Prev</button>
<button @click="offset += 10" :disabled="!hasNextPage">Next</button> <button :disabled="!hasNextPage" @click="offset += 10">Next</button>
</li> </li>
</v-select> </v-select>
</template> </template>
<script> <script>
import countries from '../data/countries'; import countries from '../data/countries'
export default { export default {
data: () => ({ data: () => ({
countries, countries,
@@ -17,33 +21,37 @@ export default {
limit: 10, limit: 10,
}), }),
computed: { computed: {
filtered () { filtered() {
return this.countries.filter(country => country.includes(this.search)); return this.countries.filter((country) => country.includes(this.search))
}, },
paginated () { paginated() {
return this.filtered.slice(this.offset, this.limit + this.offset); return this.filtered.slice(this.offset, this.limit + this.offset)
}, },
hasNextPage () { hasNextPage() {
const nextOffset = this.offset + 10; const nextOffset = this.offset + 10
return Boolean(this.filtered.slice(nextOffset, this.limit + nextOffset).length); return Boolean(
this.filtered.slice(nextOffset, this.limit + nextOffset).length
)
},
hasPrevPage() {
const prevOffset = this.offset - 10
return Boolean(
this.filtered.slice(prevOffset, this.limit + prevOffset).length
)
}, },
hasPrevPage () {
const prevOffset = this.offset - 10;
return Boolean(this.filtered.slice(prevOffset, this.limit + prevOffset).length);
}
}, },
}; }
</script> </script>
<style scoped> <style scoped>
.pagination { .pagination {
display: flex; display: flex;
margin: .25rem .25rem 0; margin: 0.25rem 0.25rem 0;
} }
.pagination button { .pagination button {
flex-grow: 1; flex-grow: 1;
} }
.pagination button:hover { .pagination button:hover {
cursor: pointer; cursor: pointer;
} }
</style> </style>
@@ -1,15 +1,19 @@
<template> <template>
<div> <div>
<v-select :options="countries" append-to-body :calculate-position="withPopper" /> <v-select
:options="countries"
append-to-body
:calculate-position="withPopper"
/>
<label for="position" style="display: block; margin: 1rem 0;"> <label for="position" style="display: block; margin: 1rem 0">
<input <input
type="checkbox"
id="position" id="position"
v-model="placement" v-model="placement"
type="checkbox"
true-value="top" true-value="top"
false-value="bottom" false-value="bottom"
> />
Position dropdown above Position dropdown above
</label> </label>
</div> </div>
@@ -17,17 +21,17 @@
<script> <script>
import countries from '../data/countries' import countries from '../data/countries'
import { createPopper } from '@popperjs/core'; import { createPopper } from '@popperjs/core'
export default { export default {
data: () => ({countries, placement: 'top'}), data: () => ({ countries, placement: 'top' }),
methods: { methods: {
withPopper (dropdownList, component, {width}) { withPopper(dropdownList, component, { width }) {
/** /**
* We need to explicitly define the dropdown width since * We need to explicitly define the dropdown width since
* it is usually inherited from the parent with CSS. * it is usually inherited from the parent with CSS.
*/ */
dropdownList.style.width = width; dropdownList.style.width = width
/** /**
* Here we position the dropdownList relative to the $refs.toggle Element. * Here we position the dropdownList relative to the $refs.toggle Element.
@@ -43,41 +47,46 @@ export default {
placement: this.placement, placement: this.placement,
modifiers: [ modifiers: [
{ {
name: 'offset', options: { name: 'offset',
offset: [0, -1] options: {
} offset: [0, -1],
},
}, },
{ {
name: 'toggleClass', name: 'toggleClass',
enabled: true, enabled: true,
phase: 'write', phase: 'write',
fn ({state}) { fn({ state }) {
component.$el.classList.toggle('drop-up', state.placement === 'top') component.$el.classList.toggle(
'drop-up',
state.placement === 'top'
)
}, },
}] },
}); ],
})
/** /**
* To prevent memory leaks Popper needs to be destroyed. * To prevent memory leaks Popper needs to be destroyed.
* If you return function, it will be called just before dropdown is removed from DOM. * If you return function, it will be called just before dropdown is removed from DOM.
*/ */
return () => popper.destroy(); return () => popper.destroy()
} },
} },
}; }
</script> </script>
<style> <style>
.v-select.drop-up.vs--open .vs__dropdown-toggle { .v-select.drop-up.vs--open .vs__dropdown-toggle {
border-radius: 0 0 4px 4px; border-radius: 0 0 4px 4px;
border-top-color: transparent; border-top-color: transparent;
border-bottom-color: rgba(60, 60, 60, 0.26); border-bottom-color: rgba(60, 60, 60, 0.26);
} }
[data-popper-placement='top'] { [data-popper-placement='top'] {
border-radius: 4px 4px 0 0; border-radius: 4px 4px 0 0;
border-top-style: solid; border-top-style: solid;
border-bottom-style: none; border-bottom-style: none;
box-shadow: 0 -3px 6px rgba(0, 0, 0, 0.15) box-shadow: 0 -3px 6px rgba(0, 0, 0, 0.15);
} }
</style> </style>
@@ -2,10 +2,10 @@
<div class="flex"> <div class="flex">
<div> <div>
<v-select <v-select
label="country" v-model="selected"
v-model="selected" label="country"
:reduce="opt => opt.meta.id" :reduce="(opt) => opt.meta.id"
:options="options" :options="options"
/> />
</div> </div>
<div> <div>
@@ -26,35 +26,36 @@ export default {
id: '1', id: '1',
code: 'ca', code: 'ca',
}, },
}], },
],
}), }),
}; }
</script> </script>
<style scoped> <style scoped>
.flex { .flex {
margin-bottom: 2rem; margin-bottom: 2rem;
border: 1px solid #eaecef; border: 1px solid #eaecef;
/*padding: 1rem;*/ /*padding: 1rem;*/
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.flex > div { .flex > div {
flex-grow: 1; flex-grow: 1;
width: 50%; width: 50%;
padding: 0 1rem; padding: 0 1rem;
} }
pre { pre {
margin: 0; margin: 0;
background: #fff; background: #fff;
} }
code { code {
color: #635762 !important; color: #635762 !important;
color: #5b2d2d !important; color: #5b2d2d !important;
/*color: #7ec699 !important;*/ /*color: #7ec699 !important;*/
} }
</style> </style>
+205 -106
View File
@@ -1,49 +1,85 @@
<template> <template>
<div id="sandbox-wrap"> <div id="sandbox-wrap">
<div id="config"> <div id="config">
<div v-if="!hideHelp" class="list-item">
<div class="list-item" v-if="!hideHelp"> <p>
<p>Use the controls below to adjust the props used Use the controls below to adjust the props used by the vue-select
by the vue-select components.</p> components.
<p>The API provides </p>
more props than are shown here, these are some <p>
commonly adjusted settings.</p> The API provides more props than are shown here, these are some
commonly adjusted settings.
</p>
</div> </div>
<h5 class="list-item">Basic Features</h5> <h5 class="list-item">Basic Features</h5>
<div class="list-item"> <div class="list-item">
<label for="multiple"> <label for="multiple">
<input id="multiple" type="checkbox" v-model="configuration.multiple"> <input
<code>:multiple="{{ configuration.multiple ? 'true' : 'false' }}"</code> id="multiple"
v-model="configuration.multiple"
type="checkbox"
/>
<code
>:multiple="{{ configuration.multiple ? 'true' : 'false' }}"</code
>
</label> </label>
</div> </div>
<div class="list-item"> <div class="list-item">
<label for="disabled"> <label for="disabled">
<input id="disabled" type="checkbox" v-model="configuration.disabled"> <input
<code>:disabled="{{ configuration.disabled ? 'true' : 'false' }}"</code> id="disabled"
v-model="configuration.disabled"
type="checkbox"
/>
<code
>:disabled="{{ configuration.disabled ? 'true' : 'false' }}"</code
>
</label> </label>
</div> </div>
<div class="list-item"> <div class="list-item">
<label for="clearable"> <label for="clearable">
<input id="clearable" type="checkbox" v-model="configuration.clearable"> <input
<code>:clearable="{{ configuration.clearable ? 'true' : 'false' }}"</code> id="clearable"
v-model="configuration.clearable"
type="checkbox"
/>
<code
>:clearable="{{ configuration.clearable ? 'true' : 'false' }}"</code
>
</label> </label>
</div> </div>
<div class="list-item"> <div class="list-item">
<label for="searchable"> <label for="searchable">
<input id="searchable" type="checkbox" v-model="configuration.searchable"> <input
<code>:searchable="{{ configuration.searchable ? 'true' : 'false' }}"</code> id="searchable"
v-model="configuration.searchable"
type="checkbox"
/>
<code
>:searchable="{{
configuration.searchable ? 'true' : 'false'
}}"</code
>
</label> </label>
</div> </div>
<div class="list-item"> <div class="list-item">
<label for="filterable"> <label for="filterable">
<input id="filterable" type="checkbox" v-model="configuration.filterable"> <input
<code>:filterable="{{ configuration.searchable ? 'true' : 'false' }}"</code> id="filterable"
v-model="configuration.filterable"
type="checkbox"
/>
<code
>:filterable="{{
configuration.searchable ? 'true' : 'false'
}}"</code
>
</label> </label>
</div> </div>
@@ -51,22 +87,34 @@
<div class="list-item"> <div class="list-item">
<label for="taggable"> <label for="taggable">
<input id="taggable" type="checkbox" v-model="configuration.taggable"> <input
<code>:taggable="{{ configuration.taggable ? 'true' : 'false' }}"</code> id="taggable"
v-model="configuration.taggable"
type="checkbox"
/>
<code
>:taggable="{{ configuration.taggable ? 'true' : 'false' }}"</code
>
</label> </label>
</div> </div>
<div class="list-item"> <div class="list-item">
<label for="noDrop"> <label for="noDrop">
<input id="noDrop" type="checkbox" v-model="configuration.noDrop"> <input id="noDrop" v-model="configuration.noDrop" type="checkbox" />
<code>:no-drop="{{ configuration.noDrop ? 'true' : 'false' }}"</code> <code>:no-drop="{{ configuration.noDrop ? 'true' : 'false' }}"</code>
</label> </label>
</div> </div>
<div class="list-item"> <div class="list-item">
<label for="pushTags"> <label for="pushTags">
<input id="pushTags" type="checkbox" v-model="configuration.pushTags"> <input
<code>:push-tags="{{ configuration.pushTags ? 'true' : 'false' }}"</code> id="pushTags"
v-model="configuration.pushTags"
type="checkbox"
/>
<code
>:push-tags="{{ configuration.pushTags ? 'true' : 'false' }}"</code
>
</label> </label>
</div> </div>
@@ -74,15 +122,31 @@
<div class="list-item"> <div class="list-item">
<label for="selectOnTab"> <label for="selectOnTab">
<input id="selectOnTab" type="checkbox" v-model="configuration.selectOnTab"> <input
<code>:select-on-tab="{{ configuration.selectOnTab ? 'true' : 'false' }}"</code> id="selectOnTab"
v-model="configuration.selectOnTab"
type="checkbox"
/>
<code
>:select-on-tab="{{
configuration.selectOnTab ? 'true' : 'false'
}}"</code
>
</label> </label>
</div> </div>
<div class="list-item"> <div class="list-item">
<label for="closeOnSelect"> <label for="closeOnSelect">
<input id="closeOnSelect" type="checkbox" v-model="configuration.closeOnSelect"> <input
<code>:close-on-select="{{ configuration.closeOnSelect ? 'true' : 'false' }}"</code> id="closeOnSelect"
v-model="configuration.closeOnSelect"
type="checkbox"
/>
<code
>:close-on-select="{{
configuration.closeOnSelect ? 'true' : 'false'
}}"</code
>
</label> </label>
</div> </div>
@@ -90,11 +154,21 @@
<div class="list-item"> <div class="list-item">
<label for="rtl"> <label for="rtl">
<input id="rtl" type="radio" v-model="configuration.dir" value="rtl"> <input
id="rtl"
v-model="configuration.dir"
type="radio"
value="rtl"
/>
<code>dir="rtl"</code> <code>dir="rtl"</code>
</label> </label>
<label for="ltr"> <label for="ltr">
<input id="ltr" type="radio" v-model="configuration.dir" value="ltr"> <input
id="ltr"
v-model="configuration.dir"
type="radio"
value="ltr"
/>
<code>dir="ltr"</code> <code>dir="ltr"</code>
</label> </label>
</div> </div>
@@ -103,22 +177,32 @@
<div id="sandbox"> <div id="sandbox">
<slot v-bind="configuration"> <slot v-bind="configuration">
<div class="example"> <div class="example">
<v-select v-bind="configuration" placeholder="country objects"></v-select> <v-select
v-bind="configuration"
placeholder="country objects"
></v-select>
</div> </div>
<div class="example"> <div class="example">
<v-select v-bind="configuration" placeholder="country objects, using option scoped slots"> <v-select
<template slot="selected-option" slot-scope="{ label, value }"> v-bind="configuration"
{{ label }} -- {{ value }} placeholder="country objects, using option scoped slots"
>
<template slot="selected-option" slot-scope="option">
{{ option.label }} -- {{ option.value }}
</template> </template>
<template slot="option" slot-scope="{ label, value }"> <template slot="option" slot-scope="option">
{{ label }} ({{ value }}) {{ option.label }} ({{ option.value }})
</template> </template>
</v-select> </v-select>
</div> </div>
<div class="example"> <div class="example">
<v-select v-bind="configuration" :options="['cat', 'dog', 'bear']" placeholder="string options, option slots"> <v-select
v-bind="configuration"
:options="['cat', 'dog', 'bear']"
placeholder="string options, option slots"
>
<template slot="selected-option" slot-scope="{ label }"> <template slot="selected-option" slot-scope="{ label }">
{{ label }} {{ label }}
</template> </template>
@@ -129,32 +213,48 @@
</div> </div>
<div class="example"> <div class="example">
<v-select v-bind="configuration" :options="[1,5,10]" placeholder="options=[1,5,10]"></v-select> <v-select
v-bind="configuration"
:options="[1, 5, 10]"
placeholder="options=[1,5,10]"
></v-select>
</div> </div>
<div class="example"> <div class="example">
<v-select v-bind="configuration" label="title" :options="optionDataSets.books" :filter="fuseSearch" <v-select
placeholder="advanced filtering w/ fuse.js + scoped slots"> v-bind="configuration"
label="title"
:options="optionDataSets.books"
:filter="fuseSearch"
placeholder="advanced filtering w/ fuse.js + scoped slots"
>
<template slot="option" slot-scope="option"> <template slot="option" slot-scope="option">
<strong>{{ option.title }}</strong><br> <strong>{{ option.title }}</strong
<em>{{ `${option.author.firstName} ${option.author.lastName}` }}</em> ><br />
<em>{{
`${option.author.firstName} ${option.author.lastName}`
}}</em>
</template> </template>
</v-select> </v-select>
</div> </div>
<div class="example"> <div class="example">
<v-select <v-select
v-bind="configuration" v-bind="configuration"
placeholder="search github repositories.." placeholder="search github repositories.."
label="full_name" label="full_name"
@search="search" :options="ajaxRes"
:options="ajaxRes" @search="search"
> >
</v-select> </v-select>
</div> </div>
<div class="example"> <div class="example">
<v-select v-bind="configuration" :options="[]" placeholder="options=[]"></v-select> <v-select
v-bind="configuration"
:options="[]"
placeholder="options=[]"
></v-select>
</div> </div>
</slot> </slot>
</div> </div>
@@ -162,11 +262,11 @@
</template> </template>
<script> <script>
import Fuse from 'fuse.js'; import Fuse from 'fuse.js'
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce'
import vSelect from '../../../src/components/Select.vue'; import vSelect from '../../../src/components/Select.vue'
import countries from '../data/countryCodes'; import countries from '../data/countryCodes'
import books from '../data/books'; import books from '../data/books'
const defaultConfig = () => ({ const defaultConfig = () => ({
options: countries, options: countries,
@@ -180,17 +280,17 @@ const defaultConfig = () => ({
disabled: false, disabled: false,
selectOntab: false, selectOntab: false,
placeholder: 'Select a Country...', placeholder: 'Select a Country...',
}); })
export default { export default {
components: { vSelect },
props: { props: {
hideHelp: { hideHelp: {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
}, },
components: {vSelect}, data() {
data () {
return { return {
configuration: defaultConfig(), configuration: defaultConfig(),
value: null, value: null,
@@ -201,76 +301,75 @@ export default {
countries, countries,
books, books,
}, },
}; }
}, },
methods: { methods: {
search (search, loading) { search(search, loading) {
loading(true); loading(true)
this.getRepositories(search, loading, this); this.getRepositories(search, loading, this)
}, },
searchPeople (search, loading) { searchPeople(search, loading) {
loading(true); loading(true)
this.getPeople(loading, this); this.getPeople(loading, this)
}, },
getPeople: debounce((loading, vm) => { getPeople: debounce((loading, vm) => {
vm.$http.get(`https://reqres.in/api/users?per_page=10`).then(res => { vm.$http.get(`https://reqres.in/api/users?per_page=10`).then((res) => {
vm.people = res.data.data; vm.people = res.data.data
loading(false); loading(false)
}); })
}, 250), }, 250),
getRepositories: debounce((search, loading, vm) => { getRepositories: debounce((search, loading, vm) => {
vm.$http vm.$http
.get(`https://api.github.com/search/repositories?q=${search}`) .get(`https://api.github.com/search/repositories?q=${search}`)
.then(res => { .then((res) => {
vm.ajaxRes = res.data.items; vm.ajaxRes = res.data.items
loading(false); loading(false)
}); })
}, 250), }, 250),
}, },
}; }
</script> </script>
<style scoped> <style scoped>
#sandbox-wrap { #sandbox-wrap {
min-height: 100%; min-height: 100%;
display: grid; display: grid;
grid-template-columns: auto 75%; grid-template-columns: auto 75%;
grid-template-rows: auto; grid-template-rows: auto;
grid-template-areas: grid-template-areas: 'sidebar component';
"sidebar component" }
}
#config { #config {
grid-area: sidebar; grid-area: sidebar;
border-right: 1px solid #eaecef; border-right: 1px solid #eaecef;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
} }
#sandbox { #sandbox {
grid-area: component; grid-area: component;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.list-item { .list-item {
margin-top: 0; margin-top: 0;
margin-bottom: 1rem; margin-bottom: 1rem;
padding: 1rem 1rem 0; padding: 1rem 1rem 0;
} }
.list-item:not(:first-child) { .list-item:not(:first-child) {
border-top: 1px solid #eaecef; border-top: 1px solid #eaecef;
} }
.example { .example {
margin-bottom: 2rem; margin-bottom: 2rem;
} }
.v-select { .v-select {
width: 25em; width: 25em;
} }
</style> </style>
+3 -1
View File
@@ -1,7 +1,9 @@
<template> <template>
<v-select append-to-body> <v-select append-to-body>
<template #footer> <template #footer>
<div style="opacity: .8">Bottom of the component, in the footer slot!</div> <div style="opacity: 0.8">
Bottom of the component, in the footer slot!
</div>
</template> </template>
</v-select> </v-select>
</template> </template>
+1 -1
View File
@@ -1,7 +1,7 @@
<template> <template>
<v-select> <v-select>
<template #header> <template #header>
<div style="opacity: .8">Top of the component, in the header slot!</div> <div style="opacity: 0.8">Top of the component, in the header slot!</div>
</template> </template>
</v-select> </v-select>
</template> </template>
@@ -1,5 +1,6 @@
<template> <template>
<v-select> <v-select>
<!-- eslint-disable-next-line vue/no-unused-vars -->
<template #no-options="{ search, searching, loading }"> <template #no-options="{ search, searching, loading }">
This is the no options slot. This is the no options slot.
</template> </template>
+6 -6
View File
@@ -14,11 +14,11 @@ export default {
{ {
title: "Old Man's War", title: "Old Man's War",
author: { author: {
firstName: "John", firstName: 'John',
lastName: "Scalzi" lastName: 'Scalzi',
} },
} },
] ],
}) }),
} }
</script> </script>
+1 -1
View File
@@ -6,7 +6,7 @@
class="vs__search" class="vs__search"
v-bind="attributes" v-bind="attributes"
v-on="events" v-on="events"
> />
</template> </template>
</v-select> </v-select>
</template> </template>
@@ -1,9 +1,11 @@
<template> <template>
<v-select v-model="selected" :options="books" label="title"> <v-select v-model="selected" :options="books" label="title">
<template #selected-option="{ title, author }"> <template #selected-option="{ title, author }">
<div style="display: flex; align-items: baseline;"> <div style="display: flex; align-items: baseline">
<strong>{{ title }}</strong> <strong>{{ title }}</strong>
<em style="margin-left: .5rem;">by {{ author.firstName }} {{ author.lastName }}</em> <em style="margin-left: 0.5rem"
>by {{ author.firstName }} {{ author.lastName }}</em
>
</div> </div>
</template> </template>
</v-select> </v-select>
@@ -13,14 +15,14 @@
const book = { const book = {
title: "Old Man's War", title: "Old Man's War",
author: { author: {
firstName: "John", firstName: 'John',
lastName: "Scalzi" lastName: 'Scalzi',
} },
}; }
export default { export default {
data: () => ({ data: () => ({
books: [book], books: [book],
selected: book selected: book,
}) }),
} }
</script> </script>
@@ -1,6 +1,9 @@
<template> <template>
<!-- eslint-disable vue/no-unused-vars -->
<v-select :options="books" label="title"> <v-select :options="books" label="title">
<template #selected-option-container="{ option, deselect, multiple, disabled }"> <template
#selected-option-container="{ option, deselect, multiple, disabled }"
>
<div class="vs__selected">{{ option.title }}</div> <div class="vs__selected">{{ option.title }}</div>
</template> </template>
</v-select> </v-select>
@@ -13,11 +16,11 @@ export default {
{ {
title: "Old Man's War", title: "Old Man's War",
author: { author: {
firstName: "John", firstName: 'John',
lastName: "Scalzi" lastName: 'Scalzi',
} },
} },
] ],
}) }),
} }
</script> </script>
+5 -1
View File
@@ -1,7 +1,11 @@
<template> <template>
<v-select :loading="true"> <v-select :loading="true">
<template #spinner="{ loading }"> <template #spinner="{ loading }">
<div v-if="loading" style="border-left-color: rgba(88,151,251,0.71)" class="vs__spinner"> <div
v-if="loading"
style="border-left-color: rgba(88, 151, 251, 0.71)"
class="vs__spinner"
>
The .vs__spinner class will hide the text for me. The .vs__spinner class will hide the text for me.
</div> </div>
</template> </template>
+1 -1
View File
@@ -20,6 +20,6 @@
color: #8492a4; color: #8492a4;
} }
p a { p a {
color: #48BB78; color: #48bb78;
} }
</style> </style>
+6 -2
View File
@@ -9,7 +9,7 @@
</a> </a>
</div> </div>
<div class="cta"> <div class="cta">
<p style="font-size: 1.2rem; font-weight: 600;"> <p style="font-size: 1.2rem; font-weight: 600">
Hi! I'm Jeff Sagal, the author of Vue Select. Hi! I'm Jeff Sagal, the author of Vue Select.
</p> </p>
<p> <p>
@@ -35,7 +35,11 @@
</svg> </svg>
Sponsor Vue Select! Sponsor Vue Select!
</a> </a>
<a href="https://twitter.com/sagalbot" target="_blank" class="social-link"> <a
href="https://twitter.com/sagalbot"
target="_blank"
class="social-link"
>
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path <path
d="M23.954 4.569a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.691 8.094 4.066 6.13 1.64 3.161a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.061a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.937 4.937 0 004.604 3.417 9.868 9.868 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.054 0 13.999-7.496 13.999-13.986 0-.209 0-.42-.015-.63a9.936 9.936 0 002.46-2.548l-.047-.02z" d="M23.954 4.569a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.691 8.094 4.066 6.13 1.64 3.161a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.061a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.937 4.937 0 004.604 3.417 9.868 9.868 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.054 0 13.999-7.496 13.999-13.986 0-.209 0-.42-.015-.63a9.936 9.936 0 002.46-2.548l-.047-.02z"
+7 -7
View File
@@ -1,6 +1,6 @@
<template> <template>
<ul> <ul>
<li v-for="{ createdAt, login, avatarUrl } in sponsors"> <li v-for="{ createdAt, login, avatarUrl } in sponsors" :key="login">
<img :src="avatarUrl + '&s=150'" :alt="`@${login}'s avatar`" /> <img :src="avatarUrl + '&s=150'" :alt="`@${login}'s avatar`" />
<p> <p>
<a :href="`https://github.com/${login}`">@{{ login }}</a> <br /> <a :href="`https://github.com/${login}`">@{{ login }}</a> <br />
@@ -11,17 +11,17 @@
</template> </template>
<script> <script>
import { SPONSORS } from "@dynamic/constants"; import { SPONSORS } from '@dynamic/constants'
import { format } from "date-fns"; import { format } from 'date-fns'
export default { export default {
data: () => ({ data: () => ({
sponsors: SPONSORS.map(({ createdAt, sponsorEntity }) => ({ sponsors: SPONSORS.map(({ createdAt, sponsorEntity }) => ({
createdAt: format(new Date(createdAt), "LLL yyyy"), createdAt: format(new Date(createdAt), 'LLL yyyy'),
...sponsorEntity ...sponsorEntity,
})) })),
}), }),
}; }
</script> </script>
<style scoped> <style scoped>
@@ -3,14 +3,14 @@
placeholder="Choose a book to read" placeholder="Choose a book to read"
label="title" label="title"
:options="books" :options="books"
:selectable="option => ! option.author.lastName.includes('Woodhouse')" :selectable="(option) => !option.author.lastName.includes('Woodhouse')"
/> />
</template> </template>
<script> <script>
import books from '../data/books'; import books from '../data/books'
export default { export default {
computed: { computed: {
books: () => books, books: () => books,
} },
} }
</script> </script>
@@ -1,7 +1,7 @@
<template> <template>
<form @submit.stop="onSubmit"> <form @submit.stop="onSubmit">
<v-select :options="books" label="title" v-model="selected"> <v-select v-model="selected" :options="books" label="title">
<template #search="{attributes, events}"> <template #search="{ attributes, events }">
<input <input
:required="!selected" :required="!selected"
class="vs__search" class="vs__search"
@@ -11,7 +11,7 @@
</template> </template>
</v-select> </v-select>
<input type="submit"> <input type="submit" />
</form> </form>
</template> </template>
@@ -24,28 +24,28 @@ export default {
}), }),
methods: { methods: {
onSubmit() { onSubmit() {
alert('Submitted!'); alert('Submitted!')
} },
} },
}; }
</script> </script>
<style scoped> <style scoped>
form { form {
display: flex; display: flex;
align-items: stretch; align-items: stretch;
} }
.v-select { .v-select {
width: 75%; width: 75%;
} }
input[type="submit"] { input[type='submit'] {
margin-left: 1rem; margin-left: 1rem;
background: #44ae7d; background: #44ae7d;
border: none; border: none;
border-radius: 3px; border-radius: 3px;
color: #fff; color: #fff;
width: 20%; width: 20%;
} }
</style> </style>
+5 -6
View File
@@ -1,7 +1,7 @@
const {description} = require('./config/meta'); const { description } = require('./config/meta')
const head = require('./config/head'); const head = require('./config/head')
const plugins = require('./config/plugins'); const plugins = require('./config/plugins')
const themeConfig = require('./config/themeConfig'); const themeConfig = require('./config/themeConfig')
module.exports = { module.exports = {
title: 'Vue Select', title: 'Vue Select',
@@ -9,5 +9,4 @@ module.exports = {
head, head,
plugins, plugins,
themeConfig, themeConfig,
}; }
+40 -30
View File
@@ -1,57 +1,67 @@
const isDeployPreview = require('./isDeployPreview'); const isDeployPreview = require('./isDeployPreview')
const meta = require('./meta'); const meta = require('./meta')
const head = [ const head = [
[ [
'link', 'link',
{ {
href: '//fonts.googleapis.com/css?family=Source+Sans+Pro:400,600|Roboto Mono', href:
'//fonts.googleapis.com/css?family=Source+Sans+Pro:400,600|Roboto Mono',
rel: 'stylesheet', rel: 'stylesheet',
type: 'text/css', type: 'text/css',
}], },
],
[ [
'link', 'link',
{ {
href: '//fonts.googleapis.com/css?family=Dosis:300&amp;text=Vue Select', href: '//fonts.googleapis.com/css?family=Dosis:300&amp;text=Vue Select',
rel: 'stylesheet', rel: 'stylesheet',
type: 'text/css', type: 'text/css',
}], },
['link', {rel: 'icon', href: `/vue-logo.png`}], ],
['meta', {name: 'theme-color', content: '#3eaf7c'}], ['link', { rel: 'icon', href: `/vue-logo.png` }],
['meta', {name: 'apple-mobile-web-app-capable', content: 'yes'}], ['meta', { name: 'theme-color', content: '#3eaf7c' }],
['meta', {name: 'apple-mobile-web-app-status-bar-style', content: 'black'}], ['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }],
['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }],
[ [
'link', 'link',
{rel: 'apple-touch-icon', href: `/icons/apple-touch-icon-152x152.png`}], { rel: 'apple-touch-icon', href: `/icons/apple-touch-icon-152x152.png` },
],
[ [
'link', 'link',
{rel: 'mask-icon', href: '/icons/safari-pinned-tab.svg', color: '#3eaf7c'}], {
rel: 'mask-icon',
href: '/icons/safari-pinned-tab.svg',
color: '#3eaf7c',
},
],
[ [
'meta', 'meta',
{ {
name: 'msapplication-TileImage', name: 'msapplication-TileImage',
content: '/icons/msapplication-icon-144x144.png', content: '/icons/msapplication-icon-144x144.png',
}], },
['meta', {name: 'msapplication-TileColor', content: '#000000'}], ],
['meta', {name: 'title', content: meta.title}], ['meta', { name: 'msapplication-TileColor', content: '#000000' }],
['meta', {name: 'description', content: meta.description}], ['meta', { name: 'title', content: meta.title }],
['link', {rel: 'icon', href: meta.icon, type: 'image/png'}], ['meta', { name: 'description', content: meta.description }],
['meta', {property: 'og:image', content: meta.icon}], ['link', { rel: 'icon', href: meta.icon, type: 'image/png' }],
['meta', {property: 'twitter:image', content: meta.icon}], ['meta', { property: 'og:image', content: meta.icon }],
['meta', {name: 'description', content: meta.description}], ['meta', { property: 'twitter:image', content: meta.icon }],
['meta', {property: 'og:description', content: ''}], ['meta', { name: 'description', content: meta.description }],
['meta', {property: 'twitter:description', content: meta.description}], ['meta', { property: 'og:description', content: '' }],
['meta', {property: 'twitter:title', content: meta.title}], ['meta', { property: 'twitter:description', content: meta.description }],
['meta', {property: 'og:title', content: meta.title}], ['meta', { property: 'twitter:title', content: meta.title }],
['meta', {property: 'og:site_name', content: meta.title}], ['meta', { property: 'og:title', content: meta.title }],
['meta', {property: 'og:url', content: meta.url}], ['meta', { property: 'og:site_name', content: meta.title }],
]; ['meta', { property: 'og:url', content: meta.url }],
]
if (isDeployPreview) { if (isDeployPreview) {
head.push( head.push(
['meta', {name: 'robots', content: 'noindex'}], ['meta', { name: 'robots', content: 'noindex' }],
['meta', {name: 'googlebot', content: 'noindex'}], ['meta', { name: 'googlebot', content: 'noindex' }]
); )
} }
module.exports = head; module.exports = head
+1 -1
View File
@@ -1 +1 @@
module.exports = process.env.hasOwnProperty('DEPLOY_PREVIEW'); module.exports = process.env.hasOwnProperty('DEPLOY_PREVIEW')
+4 -3
View File
@@ -1,6 +1,7 @@
module.exports = { module.exports = {
title: 'Vue Select | VueJS Select2/Chosen Component', title: 'Vue Select | VueJS Select2/Chosen Component',
description: 'Everything you wish the HTML select element could do, wrapped up into a lightweight, extensible Vue component.', description:
'Everything you wish the HTML select element could do, wrapped up into a lightweight, extensible Vue component.',
url: 'https://vue-select.org', url: 'https://vue-select.org',
icon: '/vue-logo.png' icon: '/vue-logo.png',
}; }
+13 -13
View File
@@ -1,22 +1,22 @@
const isDeployPreview = require("./isDeployPreview"); const isDeployPreview = require('./isDeployPreview')
module.exports = [ module.exports = [
[ [
"@vuepress/google-analytics", '@vuepress/google-analytics',
{ {
ga: isDeployPreview ? "" : "UA-12818324-8" ga: isDeployPreview ? '' : 'UA-12818324-8',
} },
], ],
[ [
"@vuepress/pwa", '@vuepress/pwa',
{ {
serviceWorker: false, serviceWorker: false,
updatePopup: true updatePopup: true,
} },
], ],
"@vuepress/plugin-register-components", '@vuepress/plugin-register-components',
"@vuepress/plugin-active-header-links", '@vuepress/plugin-active-header-links',
"@vuepress/plugin-search", '@vuepress/plugin-search',
"@vuepress/plugin-nprogress", '@vuepress/plugin-nprogress',
require('../github/index') require('../github/index'),
]; ]
+2 -4
View File
@@ -2,9 +2,7 @@ module.exports = {
repo: 'sagalbot/vue-select', repo: 'sagalbot/vue-select',
editLinks: true, editLinks: true,
docsDir: 'docs', docsDir: 'docs',
nav: [ nav: [{ text: 'Sandbox', link: '/sandbox' }],
{text: 'Sandbox', link: '/sandbox'},
],
sidebar: { sidebar: {
'/': [ '/': [
{ {
@@ -75,4 +73,4 @@ module.exports = {
}, },
], ],
}, },
}; }
+8 -9
View File
@@ -1,20 +1,19 @@
import vSelect from '../../src/components/Select'; import vSelect from '../../src/components/Select'
export default ({Vue, options, router, siteData}) => { export default ({ Vue, options, router, siteData }) => {
Vue.component('v-select', vSelect); Vue.component('v-select', vSelect)
/** /**
* Remove service workers. * Remove service workers.
*/ */
if (typeof navigator !== 'undefined' && 'serviceWorker' in navigator) { if (typeof navigator !== 'undefined' && 'serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(registrations => { navigator.serviceWorker.getRegistrations().then((registrations) => {
registrations.forEach(registration => { registrations.forEach((registration) => {
registration registration
.unregister() .unregister()
.then(() => console.log('Service worker unregistered.')) .then(() => console.log('Service worker unregistered.'))
.catch(() => console.log('Error unregistering service worker.')); .catch(() => console.log('Error unregistering service worker.'))
}); })
}); })
} }
} }
+16 -16
View File
@@ -1,14 +1,14 @@
require("dotenv").config(); require('dotenv').config()
const axios = require("axios"); const axios = require('axios')
const { graphql } = require("@octokit/graphql"); const { graphql } = require('@octokit/graphql')
module.exports = async () => ({ module.exports = async () => ({
name: "constants.js", name: 'constants.js',
content: ` content: `
export const SPONSORS = ${JSON.stringify(await getSponsors())}; export const SPONSORS = ${JSON.stringify(await getSponsors())};
export const CONTRIBUTORS = ${JSON.stringify(await getContributors())}; export const CONTRIBUTORS = ${JSON.stringify(await getContributors())};
` `,
}); })
/** /**
* Get a list of vue select contributors. * Get a list of vue select contributors.
@@ -16,10 +16,10 @@ module.exports = async () => ({
*/ */
async function getContributors() { async function getContributors() {
const { data } = await axios.get( const { data } = await axios.get(
"https://api.github.com/repos/sagalbot/vue-select/contributors?per_page=100" 'https://api.github.com/repos/sagalbot/vue-select/contributors?per_page=100'
); )
return data; return data
} }
/** /**
@@ -49,17 +49,17 @@ async function getSponsors() {
} }
} }
} }
`; `
try { try {
const { user } = await graphql(query, { const { user } = await graphql(query, {
headers: { headers: {
authorization: `token ${process.env.GITHUB_TOKEN || ""}` authorization: `token ${process.env.GITHUB_TOKEN || ''}`,
} },
}); })
return user.sponsorshipsAsMaintainer.nodes; return user.sponsorshipsAsMaintainer.nodes
} catch (e) { } catch (e) {
console.log(`${e.status} ${e.name} - Couldn't fetch sponsor data.`); console.log(`${e.status} ${e.name} - Couldn't fetch sponsor data.`)
return []; return []
} }
} }
+2 -2
View File
@@ -1,5 +1,5 @@
const clientDynamicModules = require('./clientDynamicModules'); const clientDynamicModules = require('./clientDynamicModules')
module.exports = { module.exports = {
clientDynamicModules: async () => await clientDynamicModules(), clientDynamicModules: async () => await clientDynamicModules(),
}; }
+27 -27
View File
@@ -3,19 +3,6 @@
* @see https://ethical-ad-client.readthedocs.io/en/latest/ * @see https://ethical-ad-client.readthedocs.io/en/latest/
*/ */
export default { export default {
render(h) {
return h("div", {
attrs: {
id: "ads",
"data-ea-publisher": "vue-select",
"data-ea-type": "image"
},
class: "flat horizontal"
});
},
mounted() {
this.load();
},
watch: { watch: {
$route(to, from) { $route(to, from) {
if ( if (
@@ -23,24 +10,37 @@ export default {
// Only reload if the ad has been loaded // Only reload if the ad has been loaded
// otherwise it's possible that the script is appended but // otherwise it's possible that the script is appended but
// the ads are not loaded yet. This would result in duplicated ads. // the ads are not loaded yet. This would result in duplicated ads.
[...this.$el.classList].includes("loaded") [...this.$el.classList].includes('loaded')
) { ) {
this.$el.innerHTML = ""; this.$el.innerHTML = ''
this.$el.classList.remove("loaded"); this.$el.classList.remove('loaded')
this.load(); this.load()
} }
} },
},
mounted() {
this.load()
}, },
methods: { methods: {
load() { load() {
const s = document.createElement("script"); const s = document.createElement('script')
s.id = "_ads_js"; s.id = '_ads_js'
s.src = `https://media.ethicalads.io/media/client/ethicalads.min.js`; s.src = `https://media.ethicalads.io/media/client/ethicalads.min.js`
s.async = "async"; s.async = 'async'
this.$el.appendChild(s); this.$el.appendChild(s)
} },
} },
}; render(h) {
return h('div', {
attrs: {
id: 'ads',
'data-ea-publisher': 'vue-select',
'data-ea-type': 'image',
},
class: 'flat horizontal',
})
},
}
</script> </script>
<style scoped> <style scoped>
@@ -51,4 +51,4 @@ export default {
width: 100%; width: 100%;
min-height: 264px; min-height: 264px;
} }
</style> </style>
+1 -1
View File
@@ -1,3 +1,3 @@
module.exports = { module.exports = {
extend: '@vuepress/theme-default', extend: '@vuepress/theme-default',
}; }
+3 -3
View File
@@ -3,13 +3,13 @@
</template> </template>
<script> <script>
import ParentLayout from "@parent-theme/layouts/Layout.vue"; import ParentLayout from '@parent-theme/layouts/Layout.vue'
export default { export default {
components: { components: {
ParentLayout, ParentLayout,
} },
}; }
</script> </script>
<style> <style>
+162 -98
View File
@@ -1,111 +1,175 @@
export default () => { export default () => {
document.getElementsByClassName ||
(document.getElementsByClassName = function (e) {
var n,
t,
r,
a = document,
o = []
if (a.querySelectorAll) return a.querySelectorAll('.' + e)
if (a.evaluate)
for (
t = ".//*[contains(concat(' ', @class, ' '), ' " + e + " ')]",
n = a.evaluate(t, a, null, 0, null);
(r = n.iterateNext());
document.getElementsByClassName || (document.getElementsByClassName = function (e) { )
var n, t, r, a = document, o = []; o.push(r)
if (a.querySelectorAll) return a.querySelectorAll('.' + e); else
if (a.evaluate) for (t = './/*[contains(concat(\' \', @class, \' \'), \' ' + e + ' \')]', n = a.evaluate(t, a, for (
null, 0, n = a.getElementsByTagName('*'),
null); r = n.iterateNext();) o.push(r); else for (n = a.getElementsByTagName('*'), t = new RegExp( t = new RegExp('(^|\\s)' + e + '(\\s|$)'),
'(^|\\s)' + e + '(\\s|$)'), r = 0; r < n.length; r++) t.test(n[r].className) && o.push(n[r]); r = 0;
return o; r < n.length;
}), function () { r++
function e () { )
function e () { t.test(n[r].className) && o.push(n[r])
for (var e = document.getElementsByClassName('codepen'), t = e.length - 1; t > -1; t--) { return o
var u = a(e[t]); }),
if (0 !== Object.keys(u).length && (u = o(u), u.user = n(u, e[t]), r(u))) { (function () {
var c = i(u), l = s(u, c); function e() {
f(e[t], l); function e() {
for (
var e = document.getElementsByClassName('codepen'),
t = e.length - 1;
t > -1;
t--
) {
var u = a(e[t])
if (
0 !== Object.keys(u).length &&
((u = o(u)), (u.user = n(u, e[t])), r(u))
) {
var c = i(u),
l = s(u, c)
f(e[t], l)
}
} }
m()
} }
m();
}
function n (e, n) { function n(e, n) {
if ('string' == typeof e.user) return e.user; if ('string' == typeof e.user) return e.user
for (var t = 0, r = n.children.length; t < r; t++) { for (var t = 0, r = n.children.length; t < r; t++) {
var a = n.children[t], o = a.href || '', i = o.match(/codepen\.(io|dev)\/(\w+)\/pen\//i); var a = n.children[t],
if (i) return i[2]; o = a.href || '',
i = o.match(/codepen\.(io|dev)\/(\w+)\/pen\//i)
if (i) return i[2]
}
return 'anon'
} }
return 'anon';
}
function r (e) {return e['slug-hash'];} function r(e) {
return e['slug-hash']
function a (e) {
for (var n = {}, t = e.attributes, r = 0, a = t.length; r < a; r++) {
var o = t[r].name;
0 === o.indexOf('data-') && (n[o.replace('data-', '')] = t[r].value);
} }
return n;
function a(e) {
for (var n = {}, t = e.attributes, r = 0, a = t.length; r < a; r++) {
var o = t[r].name
0 === o.indexOf('data-') && (n[o.replace('data-', '')] = t[r].value)
}
return n
}
function o(e) {
return (
e.href && (e['slug-hash'] = e.href),
e.type && (e['default-tab'] = e.type),
e.safe &&
('true' === e.safe
? (e.animations = 'run')
: (e.animations = 'stop-after-5')),
e
)
}
function i(e) {
var n = u(e),
t = e.user ? e.user : 'anon',
r = '?' + l(e),
a = e.preview && 'true' === e.preview ? 'embed/preview' : 'embed',
o = [n, t, a, e['slug-hash'] + r].join('/')
return o.replace(/\/\//g, '//')
}
function u(e) {
return e.host
? c(e.host)
: 'file:' === document.location.protocol
? 'https://codepen.io'
: '//codepen.io'
}
function c(e) {
return e.match(/^\/\//) || !e.match(/https?:/)
? document.location.protocol + '//' + e
: e
}
function l(e) {
var n = ''
for (var t in e)
'' !== n && (n += '&'), (n += t + '=' + encodeURIComponent(e[t]))
return n
}
function s(e, n) {
var r
e['pen-title']
? (r = e['pen-title'])
: ((r = 'CodePen Embed ' + t), t++)
var a = {
id: 'cp_embed_' + e['slug-hash'].replace('/', '_'),
src: n,
scrolling: 'no',
frameborder: '0',
height: d(e),
allowTransparency: 'true',
allowfullscreen: 'true',
allowpaymentrequest: 'true',
name: 'CodePen Embed',
title: r,
class: 'cp_embed_iframe ' + (e['class'] ? e['class'] : ''),
style: 'width: ' + p + '; overflow: hidden;',
},
o = '<iframe '
for (var i in a) o += i + '="' + a[i] + '" '
return (o += '></iframe>')
}
function d(e) {
return e.height ? e.height : 300
}
function f(e, n) {
if (e.parentNode) {
var t = document.createElement('div')
;(t.className = 'cp_embed_wrapper'),
(t.innerHTML = n),
e.parentNode.replaceChild(t, e)
} else e.innerHTML = n
}
function m() {
'function' == typeof __CodePenIFrameAddedToPage &&
__CodePenIFrameAddedToPage()
}
var p = '100%'
e()
} }
function o (e) { function n(e) {
return e.href && (e['slug-hash'] = e.href), e.type && (e['default-tab'] = e.type), e.safe && ;/in/.test(document.readyState)
('true' === e.safe ? e.animations = 'run' : e.animations = 'stop-after-5'), e; ? setTimeout('window.__cp_domReady(' + e + ')', 9)
: e()
} }
function i (e) { var t = 1
var n = u(e), t = e.user ? e.user : 'anon', r = '?' + l(e), ;(window.__cp_domReady = n),
a = e.preview && 'true' === e.preview ? 'embed/preview' : 'embed', (window.__CPEmbed = e),
o = [n, t, a, e['slug-hash'] + r].join('/'); n(function () {
return o.replace(/\/\//g, '//'); new __CPEmbed()
} })
})()
function u (e) {
return e.host ? c(e.host) : 'file:' === document.location.protocol
? 'https://codepen.io'
: '//codepen.io';
}
function c (e) {return e.match(/^\/\//) || !e.match(/https?:/) ? document.location.protocol + '//' + e : e;}
function l (e) {
var n = '';
for (var t in e) '' !== n && (n += '&'), n += t + '=' + encodeURIComponent(e[t]);
return n;
}
function s (e, n) {
var r;
e['pen-title'] ? r = e['pen-title'] : (r = 'CodePen Embed ' + t, t++);
var a = {
id: 'cp_embed_' + e['slug-hash'].replace('/', '_'),
src: n,
scrolling: 'no',
frameborder: '0',
height: d(e),
allowTransparency: 'true',
allowfullscreen: 'true',
allowpaymentrequest: 'true',
name: 'CodePen Embed',
title: r,
'class': 'cp_embed_iframe ' + (e['class'] ? e['class'] : ''),
style: 'width: ' + p + '; overflow: hidden;',
}, o = '<iframe ';
for (var i in a) o += i + '="' + a[i] + '" ';
return o += '></iframe>';
}
function d (e) {return e.height ? e.height : 300;}
function f (e, n) {
if (e.parentNode) {
var t = document.createElement('div');
t.className = 'cp_embed_wrapper', t.innerHTML = n, e.parentNode.replaceChild(t, e);
} else e.innerHTML = n;
}
function m () {'function' == typeof __CodePenIFrameAddedToPage && __CodePenIFrameAddedToPage();}
var p = '100%';
e();
}
function n (e) {/in/.test(document.readyState) ? setTimeout('window.__cp_domReady(' + e + ')', 9) : e();}
var t = 1;
window.__cp_domReady = n, window.__CPEmbed = e, n(function () {new __CPEmbed;});
}();
} }
+1 -1
View File
@@ -5,4 +5,4 @@ module.exports = {
preset: 'default', preset: 'default',
}), }),
], ],
}; }
+14 -14
View File
@@ -1,23 +1,23 @@
module.exports = { module.exports = {
release: { release: {
branch: "master" branch: 'master',
}, },
plugins: [ plugins: [
"@semantic-release/npm", '@semantic-release/npm',
"@semantic-release/commit-analyzer", '@semantic-release/commit-analyzer',
"@semantic-release/release-notes-generator", '@semantic-release/release-notes-generator',
[ [
"@semantic-release/github", '@semantic-release/github',
{ {
assets: ["dist/**"] assets: ['dist/**'],
} },
], ],
[ [
"@semantic-release/git", '@semantic-release/git',
{ {
assets: ["package.json"], assets: ['package.json'],
message: "chore(🚀): ${nextRelease.version}" message: 'chore(🚀): ${nextRelease.version}',
} },
] ],
] ],
}; }
+3 -1
View File
@@ -1,5 +1,7 @@
<template> <template>
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10"> <svg xmlns="http://www.w3.org/2000/svg" width="10" height="10">
<path d="M6.895455 5l2.842897-2.842898c.348864-.348863.348864-.914488 0-1.263636L9.106534.261648c-.348864-.348864-.914489-.348864-1.263636 0L5 3.104545 2.157102.261648c-.348863-.348864-.914488-.348864-1.263636 0L.261648.893466c-.348864.348864-.348864.914489 0 1.263636L3.104545 5 .261648 7.842898c-.348864.348863-.348864.914488 0 1.263636l.631818.631818c.348864.348864.914773.348864 1.263636 0L5 6.895455l2.842898 2.842897c.348863.348864.914772.348864 1.263636 0l.631818-.631818c.348864-.348864.348864-.914489 0-1.263636L6.895455 5z"/> <path
d="M6.895455 5l2.842897-2.842898c.348864-.348863.348864-.914488 0-1.263636L9.106534.261648c-.348864-.348864-.914489-.348864-1.263636 0L5 3.104545 2.157102.261648c-.348863-.348864-.914488-.348864-1.263636 0L.261648.893466c-.348864.348864-.348864.914489 0 1.263636L3.104545 5 .261648 7.842898c-.348864.348863-.348864.914488 0 1.263636l.631818.631818c.348864.348864.914773.348864 1.263636 0L5 6.895455l2.842898 2.842897c.348863.348864.914772.348864 1.263636 0l.631818-.631818c.348864-.348864.348864-.914489 0-1.263636L6.895455 5z"
/>
</svg> </svg>
</template> </template>
+3 -1
View File
@@ -1,5 +1,7 @@
<template> <template>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="10"> <svg xmlns="http://www.w3.org/2000/svg" width="14" height="10">
<path d="M9.211364 7.59931l4.48338-4.867229c.407008-.441854.407008-1.158247 0-1.60046l-.73712-.80023c-.407008-.441854-1.066904-.441854-1.474243 0L7 5.198617 2.51662.33139c-.407008-.441853-1.066904-.441853-1.474243 0l-.737121.80023c-.407008.441854-.407008 1.158248 0 1.600461l4.48338 4.867228L7 10l2.211364-2.40069z"/> <path
d="M9.211364 7.59931l4.48338-4.867229c.407008-.441854.407008-1.158247 0-1.60046l-.73712-.80023c-.407008-.441854-1.066904-.441854-1.474243 0L7 5.198617 2.51662.33139c-.407008-.441853-1.066904-.441853-1.474243 0l-.737121.80023c-.407008.441854-.407008 1.158248 0 1.600461l4.48338 4.867228L7 10l2.211364-2.40069z"
/>
</svg> </svg>
</template> </template>
+1199 -1100
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -1,7 +1,7 @@
import Deselect from './Deselect'; import Deselect from './Deselect'
import OpenIndicator from './OpenIndicator'; import OpenIndicator from './OpenIndicator'
export default { export default {
Deselect, Deselect,
OpenIndicator OpenIndicator,
} }
+31 -24
View File
@@ -1,27 +1,34 @@
export default { export default {
inserted (el, bindings, {context}) { inserted(el, bindings, { context }) {
if (context.appendToBody) { if (context.appendToBody) {
const {height, top, left, width} = context.$refs.toggle.getBoundingClientRect(); const {
let scrollX = window.scrollX || window.pageXOffset; height,
let scrollY = window.scrollY || window.pageYOffset; top,
el.unbindPosition = context.calculatePosition(el, context, { left,
width: width + 'px', width,
left: (scrollX + left) + 'px', } = context.$refs.toggle.getBoundingClientRect()
top: (scrollY + top + height) + 'px',
});
document.body.appendChild(el);
}
},
unbind (el, bindings, {context}) { let scrollX = window.scrollX || window.pageXOffset
if (context.appendToBody) { let scrollY = window.scrollY || window.pageYOffset
if (el.unbindPosition && typeof el.unbindPosition === 'function') {
el.unbindPosition(); el.unbindPosition = context.calculatePosition(el, context, {
} width: width + 'px',
if (el.parentNode) { left: scrollX + left + 'px',
el.parentNode.removeChild(el); top: scrollY + top + height + 'px',
} })
}
}, document.body.appendChild(el)
}
},
unbind(el, bindings, { context }) {
if (context.appendToBody) {
if (el.unbindPosition && typeof el.unbindPosition === 'function') {
el.unbindPosition()
}
if (el.parentNode) {
el.parentNode.removeChild(el)
}
}
},
} }
+10 -10
View File
@@ -11,10 +11,10 @@ export default {
}, },
}, },
data () { data() {
return { return {
mutableLoading: false, mutableLoading: false,
}; }
}, },
watch: { watch: {
@@ -27,8 +27,8 @@ export default {
* *
* @emits search * @emits search
*/ */
search () { search() {
this.$emit('search', this.search, this.toggleLoading); this.$emit('search', this.search, this.toggleLoading)
}, },
/** /**
@@ -36,8 +36,8 @@ export default {
* mutable loading value. * mutable loading value.
* @param val * @param val
*/ */
loading (val) { loading(val) {
this.mutableLoading = val; this.mutableLoading = val
}, },
}, },
@@ -49,11 +49,11 @@ export default {
* @param toggle Boolean * @param toggle Boolean
* @returns {*} * @returns {*}
*/ */
toggleLoading (toggle = null) { toggleLoading(toggle = null) {
if (toggle == null) { if (toggle == null) {
return (this.mutableLoading = !this.mutableLoading); return (this.mutableLoading = !this.mutableLoading)
} }
return (this.mutableLoading = toggle); return (this.mutableLoading = toggle)
}, },
}, },
}; }
+13 -13
View File
@@ -2,14 +2,14 @@ export default {
props: { props: {
autoscroll: { autoscroll: {
type: Boolean, type: Boolean,
default: true default: true,
} },
}, },
watch: { watch: {
typeAheadPointer() { typeAheadPointer() {
if (this.autoscroll) { if (this.autoscroll) {
this.maybeAdjustScroll(); this.maybeAdjustScroll()
} }
}, },
}, },
@@ -23,17 +23,17 @@ export default {
*/ */
maybeAdjustScroll() { maybeAdjustScroll() {
const optionEl = const optionEl =
this.$refs.dropdownMenu?.children[this.typeAheadPointer] || false; this.$refs.dropdownMenu?.children[this.typeAheadPointer] || false
if (optionEl) { if (optionEl) {
const bounds = this.getDropdownViewport(); const bounds = this.getDropdownViewport()
const { top, bottom, height } = optionEl.getBoundingClientRect(); const { top, bottom, height } = optionEl.getBoundingClientRect()
if (top < bounds.top) { if (top < bounds.top) {
return (this.$refs.dropdownMenu.scrollTop = optionEl.offsetTop); return (this.$refs.dropdownMenu.scrollTop = optionEl.offsetTop)
} else if (bottom > bounds.bottom) { } else if (bottom > bounds.bottom) {
return (this.$refs.dropdownMenu.scrollTop = return (this.$refs.dropdownMenu.scrollTop =
optionEl.offsetTop - (bounds.height - height)); optionEl.offsetTop - (bounds.height - height))
} }
} }
}, },
@@ -48,8 +48,8 @@ export default {
: { : {
height: 0, height: 0,
top: 0, top: 0,
bottom: 0 bottom: 0,
}; }
} },
} },
}; }
+17 -13
View File
@@ -1,7 +1,7 @@
export default { export default {
data() { data() {
return { return {
typeAheadPointer: -1 typeAheadPointer: -1,
} }
}, },
@@ -9,11 +9,11 @@ export default {
filteredOptions() { filteredOptions() {
for (let i = 0; i < this.filteredOptions.length; i++) { for (let i = 0; i < this.filteredOptions.length; i++) {
if (this.selectable(this.filteredOptions[i])) { if (this.selectable(this.filteredOptions[i])) {
this.typeAheadPointer = i; this.typeAheadPointer = i
break; break
} }
} }
} },
}, },
methods: { methods: {
@@ -25,8 +25,8 @@ export default {
typeAheadUp() { typeAheadUp() {
for (let i = this.typeAheadPointer - 1; i >= 0; i--) { for (let i = this.typeAheadPointer - 1; i >= 0; i--) {
if (this.selectable(this.filteredOptions[i])) { if (this.selectable(this.filteredOptions[i])) {
this.typeAheadPointer = i; this.typeAheadPointer = i
break; break
} }
} }
}, },
@@ -37,10 +37,14 @@ export default {
* @return {void} * @return {void}
*/ */
typeAheadDown() { typeAheadDown() {
for (let i = this.typeAheadPointer + 1; i < this.filteredOptions.length; i++) { for (
let i = this.typeAheadPointer + 1;
i < this.filteredOptions.length;
i++
) {
if (this.selectable(this.filteredOptions[i])) { if (this.selectable(this.filteredOptions[i])) {
this.typeAheadPointer = i; this.typeAheadPointer = i
break; break
} }
} }
}, },
@@ -51,11 +55,11 @@ export default {
* @return {void} * @return {void}
*/ */
typeAheadSelect() { typeAheadSelect() {
const typeAheadOption = this.filteredOptions[this.typeAheadPointer]; const typeAheadOption = this.filteredOptions[this.typeAheadPointer]
if (typeAheadOption) { if (typeAheadOption) {
this.select(typeAheadOption); this.select(typeAheadOption)
} }
} },
} },
} }
+8 -6
View File
@@ -3,13 +3,15 @@
* @return {string} * @return {string}
*/ */
function sortAndStringify(sortable) { function sortAndStringify(sortable) {
const ordered = {}; const ordered = {}
Object.keys(sortable).sort().forEach(key => { Object.keys(sortable)
ordered[key] = sortable[key]; .sort()
}); .forEach((key) => {
ordered[key] = sortable[key]
})
return JSON.stringify(ordered); return JSON.stringify(ordered)
} }
export default sortAndStringify; export default sortAndStringify
+3 -3
View File
@@ -1,4 +1,4 @@
let idCount = 0; let idCount = 0
/** /**
* Dead simple unique ID implementation. * Dead simple unique ID implementation.
@@ -6,7 +6,7 @@ let idCount = 0;
* @return {number} * @return {number}
*/ */
function uniqueId() { function uniqueId() {
return ++idCount; return ++idCount
} }
export default uniqueId; export default uniqueId
+26 -26
View File
@@ -1,6 +1,6 @@
import { shallowMount } from "@vue/test-utils"; import { shallowMount } from '@vue/test-utils'
import VueSelect from "../src/components/Select.vue"; import VueSelect from '../src/components/Select.vue'
import Vue from 'vue'; import Vue from 'vue'
/** /**
* Trigger a submit event on the search * Trigger a submit event on the search
@@ -11,10 +11,10 @@ import Vue from 'vue';
*/ */
export const searchSubmit = (Wrapper, searchText = false) => { export const searchSubmit = (Wrapper, searchText = false) => {
if (searchText) { if (searchText) {
Wrapper.vm.search = searchText; Wrapper.vm.search = searchText
} }
Wrapper.find({ ref: "search" }).trigger("keydown.enter") Wrapper.find({ ref: 'search' }).trigger('keydown.enter')
}; }
/** /**
* Focus the input, enter some search text, hit return. * Focus the input, enter some search text, hit return.
@@ -23,15 +23,15 @@ export const searchSubmit = (Wrapper, searchText = false) => {
* @return {Promise<void>} * @return {Promise<void>}
*/ */
export const selectTag = async (Wrapper, searchText) => { export const selectTag = async (Wrapper, searchText) => {
Wrapper.vm.$refs.search.focus(); Wrapper.vm.$refs.search.focus()
await Wrapper.vm.$nextTick(); await Wrapper.vm.$nextTick()
Wrapper.vm.search = searchText; Wrapper.vm.search = searchText
await Wrapper.vm.$nextTick(); await Wrapper.vm.$nextTick()
Wrapper.find({ ref: "search" }).trigger("keydown.enter"); Wrapper.find({ ref: 'search' }).trigger('keydown.enter')
await Wrapper.vm.$nextTick(); await Wrapper.vm.$nextTick()
}; }
/** /**
* Create a new VueSelect instance with * Create a new VueSelect instance with
@@ -40,8 +40,8 @@ export const selectTag = async (Wrapper, searchText) => {
* @returns {Wrapper<Vue>} * @returns {Wrapper<Vue>}
*/ */
export const selectWithProps = (propsData = {}) => { export const selectWithProps = (propsData = {}) => {
return shallowMount(VueSelect, { propsData }); return shallowMount(VueSelect, { propsData })
}; }
/** /**
* Returns a Wrapper with a v-select component. * Returns a Wrapper with a v-select component.
@@ -56,9 +56,8 @@ export const mountDefault = (props = {}, options = {}) => {
...props, ...props,
}, },
...options, ...options,
}); })
}; }
/** /**
* Returns a v-select component directly. * Returns a v-select component directly.
@@ -68,11 +67,12 @@ export const mountDefault = (props = {}, options = {}) => {
*/ */
export const mountWithoutTestUtils = (props = {}, options = {}) => { export const mountWithoutTestUtils = (props = {}, options = {}) => {
return new Vue({ return new Vue({
render: createEl => createEl('vue-select', { components: { VueSelect },
ref: 'select', render: (createEl) =>
props: {options: ['one', 'two', 'three'], ...props}, createEl('vue-select', {
...options ref: 'select',
}), props: { options: ['one', 'two', 'three'], ...props },
components: {VueSelect}, ...options,
}).$mount().$refs.select; }),
}; }).$mount().$refs.select
}
+4 -4
View File
@@ -1,8 +1,8 @@
module.exports = { module.exports = {
env: { env: {
jest: true jest: true,
}, },
rules: { rules: {
'import/no-extraneous-dependencies': 'off' 'import/no-extraneous-dependencies': 'off',
} },
} }
+21 -21
View File
@@ -1,35 +1,35 @@
import { mountDefault } from "../helpers"; import { mountDefault } from '../helpers'
describe("Search Slot Scope", () => { describe('Search Slot Scope', () => {
/** /**
* @see https://www.w3.org/WAI/PF/aria/states_and_properties#aria-activedescendant * @see https://www.w3.org/WAI/PF/aria/states_and_properties#aria-activedescendant
*/ */
describe("aria-activedescendant", () => { describe('aria-activedescendant', () => {
it("adds the active descendant attribute only when the dropdown is open and there is a typeAheadPointer value", async () => { it('adds the active descendant attribute only when the dropdown is open and there is a typeAheadPointer value', async () => {
const Select = mountDefault(); const Select = mountDefault()
expect( expect(
Select.vm.scope.search.attributes["aria-activedescendant"] Select.vm.scope.search.attributes['aria-activedescendant']
).toEqual(undefined); ).toEqual(undefined)
Select.vm.open = true; Select.vm.open = true
await Select.vm.$nextTick(); await Select.vm.$nextTick()
expect( expect(
Select.vm.scope.search.attributes["aria-activedescendant"] Select.vm.scope.search.attributes['aria-activedescendant']
).toEqual(undefined); ).toEqual(undefined)
}); })
it("adds the active descendant attribute when there's a typeahead value and an open dropdown", async () => { it("adds the active descendant attribute when there's a typeahead value and an open dropdown", async () => {
const Select = mountDefault(); const Select = mountDefault()
Select.vm.open = true; Select.vm.open = true
Select.vm.typeAheadPointer = 1; Select.vm.typeAheadPointer = 1
await Select.vm.$nextTick(); await Select.vm.$nextTick()
expect( expect(
Select.vm.scope.search.attributes["aria-activedescendant"] Select.vm.scope.search.attributes['aria-activedescendant']
).toEqual(`vs${Select.vm.uid}__option-1`); ).toEqual(`vs${Select.vm.uid}__option-1`)
}); })
}); })
}); })
+42 -42
View File
@@ -1,65 +1,65 @@
import { selectWithProps } from "../helpers"; import { selectWithProps } from '../helpers'
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils'
import vSelect from '../../src/components/Select'; import vSelect from '../../src/components/Select'
describe("Asynchronous Loading", () => { describe('Asynchronous Loading', () => {
it("can toggle the loading class", () => { it('can toggle the loading class', () => {
const Select = selectWithProps(); const Select = selectWithProps()
Select.vm.toggleLoading(); Select.vm.toggleLoading()
expect(Select.vm.mutableLoading).toEqual(true); expect(Select.vm.mutableLoading).toEqual(true)
Select.vm.toggleLoading(true); Select.vm.toggleLoading(true)
expect(Select.vm.mutableLoading).toEqual(true); expect(Select.vm.mutableLoading).toEqual(true)
}); })
it("should trigger the search event when the search text changes", async () => { it('should trigger the search event when the search text changes', async () => {
const Select = selectWithProps(); const Select = selectWithProps()
Select.vm.search = "foo"; Select.vm.search = 'foo'
await Select.vm.$nextTick(); await Select.vm.$nextTick()
const events = Select.emitted("search"); const events = Select.emitted('search')
expect(events).toContainEqual(["foo", Select.vm.toggleLoading]); expect(events).toContainEqual(['foo', Select.vm.toggleLoading])
expect(events.length).toEqual(1); expect(events.length).toEqual(1)
}); })
it("should trigger the search event if the search text is empty", async () => { it('should trigger the search event if the search text is empty', async () => {
const Select = selectWithProps(); const Select = selectWithProps()
Select.vm.search = "foo"; Select.vm.search = 'foo'
await Select.vm.$nextTick(); await Select.vm.$nextTick()
Select.vm.search = ""; Select.vm.search = ''
await Select.vm.$nextTick(); await Select.vm.$nextTick()
const events = Select.emitted("search"); const events = Select.emitted('search')
expect(events).toContainEqual(["", Select.vm.toggleLoading]); expect(events).toContainEqual(['', Select.vm.toggleLoading])
expect(events.length).toEqual(2); expect(events.length).toEqual(2)
}); })
it("can set loading to false from the @search event callback", async () => { it('can set loading to false from the @search event callback', async () => {
const Select = shallowMount(vSelect, { const Select = shallowMount(vSelect, {
listeners: { listeners: {
search: (search, loading) => { search: (search, loading) => {
loading(false) loading(false)
}, },
}, },
}); })
Select.vm.mutableLoading = true; Select.vm.mutableLoading = true
Select.vm.search = 'foo'; Select.vm.search = 'foo'
await Select.vm.$nextTick(); await Select.vm.$nextTick()
expect(Select.vm.mutableLoading).toEqual(false); expect(Select.vm.mutableLoading).toEqual(false)
}); })
it('will sync mutable loading with the loading prop', async () => { it('will sync mutable loading with the loading prop', async () => {
const Select = selectWithProps({ loading: false }); const Select = selectWithProps({ loading: false })
Select.setProps({ loading: true }); Select.setProps({ loading: true })
await Select.vm.$nextTick(); await Select.vm.$nextTick()
expect(Select.vm.mutableLoading).toEqual(true); expect(Select.vm.mutableLoading).toEqual(true)
}); })
}); })
+36 -36
View File
@@ -1,61 +1,61 @@
import { mountDefault } from "../helpers"; import { mountDefault } from '../helpers'
describe("Automatic Scrolling", () => { describe('Automatic Scrolling', () => {
it("should check if the scroll position needs to be adjusted on up arrow keyUp", async () => { it('should check if the scroll position needs to be adjusted on up arrow keyUp', async () => {
// Given // Given
const Select = mountDefault(); const Select = mountDefault()
const spy = jest.spyOn(Select.vm, "maybeAdjustScroll"); const spy = jest.spyOn(Select.vm, 'maybeAdjustScroll')
Select.vm.typeAheadPointer = 1; Select.vm.typeAheadPointer = 1
// When // When
Select.find({ ref: "search" }).trigger("keydown.up"); Select.find({ ref: 'search' }).trigger('keydown.up')
await Select.vm.$nextTick(); await Select.vm.$nextTick()
// Then // Then
expect(spy).toHaveBeenCalled(); expect(spy).toHaveBeenCalled()
}); })
it("should check if the scroll position needs to be adjusted on down arrow keyUp", async () => { it('should check if the scroll position needs to be adjusted on down arrow keyUp', async () => {
// Given // Given
const Select = mountDefault(); const Select = mountDefault()
const spy = jest.spyOn(Select.vm, "maybeAdjustScroll"); const spy = jest.spyOn(Select.vm, 'maybeAdjustScroll')
Select.vm.typeAheadPointer = 1; Select.vm.typeAheadPointer = 1
// When // When
Select.find({ ref: "search" }).trigger("keydown.down"); Select.find({ ref: 'search' }).trigger('keydown.down')
await Select.vm.$nextTick(); await Select.vm.$nextTick()
// Then // Then
expect(spy).toHaveBeenCalled(); expect(spy).toHaveBeenCalled()
}); })
it("should check if the scroll position needs to be adjusted when filtered options changes", async () => { it('should check if the scroll position needs to be adjusted when filtered options changes', async () => {
// Given // Given
const Select = mountDefault(); const Select = mountDefault()
const spy = jest.spyOn(Select.vm, "maybeAdjustScroll"); const spy = jest.spyOn(Select.vm, 'maybeAdjustScroll')
Select.vm.typeAheadPointer = 1; Select.vm.typeAheadPointer = 1
// When // When
Select.vm.search = "two"; Select.vm.search = 'two'
await Select.vm.$nextTick(); await Select.vm.$nextTick()
// Then // Then
expect(spy).toHaveBeenCalled(); expect(spy).toHaveBeenCalled()
}); })
it("should not adjust scroll position when autoscroll is false", async () => { it('should not adjust scroll position when autoscroll is false', async () => {
// Given // Given
const Select = mountDefault({ const Select = mountDefault({
autoscroll: false autoscroll: false,
}); })
const spy = jest.spyOn(Select.vm, "maybeAdjustScroll"); const spy = jest.spyOn(Select.vm, 'maybeAdjustScroll')
Select.vm.typeAheadPointer = 1; Select.vm.typeAheadPointer = 1
// When // When
Select.vm.search = "two"; Select.vm.search = 'two'
await Select.vm.$nextTick(); await Select.vm.$nextTick()
// Then // Then
expect(spy).toHaveBeenCalledTimes(0); expect(spy).toHaveBeenCalledTimes(0)
}); })
}); })
+15 -17
View File
@@ -1,30 +1,28 @@
import Vue from 'vue'; import Vue from 'vue'
import { selectWithProps } from '../helpers'; import { selectWithProps } from '../helpers'
describe('Components API', () => { describe('Components API', () => {
it('swap the Deselect component', () => { it('swap the Deselect component', () => {
const Deselect = Vue.component('Deselect', { const Deselect = Vue.component('Deselect', {
render (createElement) { render(createElement) {
return createElement('button', 'remove'); return createElement('button', 'remove')
}, },
}); })
const Select = selectWithProps({components: {Deselect}}); const Select = selectWithProps({ components: { Deselect } })
expect(Select.contains(Deselect)).toBeTruthy(); expect(Select.contains(Deselect)).toBeTruthy()
}); })
it('swap the OpenIndicator component', () => { it('swap the OpenIndicator component', () => {
const OpenIndicator = Vue.component('OpenIndicator', { const OpenIndicator = Vue.component('OpenIndicator', {
render (createElement) { render(createElement) {
return createElement('i', '^'); return createElement('i', '^')
}, },
}); })
const Select = selectWithProps({components: {OpenIndicator}}); const Select = selectWithProps({ components: { OpenIndicator } })
expect(Select.contains(OpenIndicator)).toBeTruthy(); expect(Select.contains(OpenIndicator)).toBeTruthy()
}); })
})
});
+17 -17
View File
@@ -1,30 +1,30 @@
import { searchSubmit, selectTag, selectWithProps } from "../helpers"; import { searchSubmit, selectTag, selectWithProps } from '../helpers'
describe("CreateOption When Tagging Is Enabled", () => { describe('CreateOption When Tagging Is Enabled', () => {
it("can select the current search text as a string", async () => { it('can select the current search text as a string', async () => {
const Select = selectWithProps({ const Select = selectWithProps({
taggable: true, taggable: true,
multiple: true, multiple: true,
options: ["one", "two"], options: ['one', 'two'],
createOption: option => "four" createOption: (option) => 'four',
}); })
await selectTag(Select, "three"); await selectTag(Select, 'three')
expect(Select.vm.selectedValue).toEqual(["four"]); expect(Select.vm.selectedValue).toEqual(['four'])
}); })
it("can select the current search text as an object", async () => { it('can select the current search text as an object', async () => {
const Select = selectWithProps({ const Select = selectWithProps({
taggable: true, taggable: true,
multiple: false, multiple: false,
value: null, value: null,
options: [], options: [],
label: "name", label: 'name',
createOption: title => ({ name: title }) createOption: (title) => ({ name: title }),
}); })
await selectTag(Select, "two"); await selectTag(Select, 'two')
expect(Select.emitted("input")[0]).toEqual([{ name: "two" }]); expect(Select.emitted('input')[0]).toEqual([{ name: 'two' }])
}); })
}); })
+74 -74
View File
@@ -1,107 +1,107 @@
import { mountDefault, selectWithProps } from '../helpers'; import { mountDefault, selectWithProps } from '../helpers'
describe("Removing values", () => { describe('Removing values', () => {
it("can remove the given tag when its close icon is clicked", async () => { it('can remove the given tag when its close icon is clicked', async () => {
const Select = selectWithProps({ multiple: true }); const Select = selectWithProps({ multiple: true })
Select.vm.$data._value = 'one'; Select.vm.$data._value = 'one'
await Select.vm.$nextTick(); await Select.vm.$nextTick()
Select.find(".vs__deselect").trigger("click"); Select.find('.vs__deselect').trigger('click')
expect(Select.emitted().input).toEqual([[[]]]); expect(Select.emitted().input).toEqual([[[]]])
expect(Select.vm.selectedValue).toEqual([]); expect(Select.vm.selectedValue).toEqual([])
}); })
it("should not remove tag when close icon is clicked and component is disabled", () => { it('should not remove tag when close icon is clicked and component is disabled', () => {
const Select = selectWithProps({ const Select = selectWithProps({
value: ["one"], value: ['one'],
options: ["one", "two", "three"], options: ['one', 'two', 'three'],
multiple: true, multiple: true,
disabled: true disabled: true,
}); })
Select.find(".vs__deselect").trigger("click"); Select.find('.vs__deselect').trigger('click')
expect(Select.vm.selectedValue).toEqual(["one"]); expect(Select.vm.selectedValue).toEqual(['one'])
}); })
it("should remove the last item in the value array on delete keypress when multiple is true", () => { it('should remove the last item in the value array on delete keypress when multiple is true', () => {
const Select = selectWithProps({ const Select = selectWithProps({
multiple: true, multiple: true,
options: ["one", "two", "three"] options: ['one', 'two', 'three'],
}); })
Select.vm.$data._value = ["one", "two"]; Select.vm.$data._value = ['one', 'two']
Select.find('.vs__search').trigger('keydown.backspace') Select.find('.vs__search').trigger('keydown.backspace')
expect(Select.emitted().input).toEqual([[['one']]]); expect(Select.emitted().input).toEqual([[['one']]])
expect(Select.vm.selectedValue).toEqual(["one"]); expect(Select.vm.selectedValue).toEqual(['one'])
}); })
it("should set value to null on delete keypress when multiple is false", () => { it('should set value to null on delete keypress when multiple is false', () => {
const Select = selectWithProps({ const Select = selectWithProps({
options: ["one", "two", "three"] options: ['one', 'two', 'three'],
}); })
Select.vm.$data._value = 'one'; Select.vm.$data._value = 'one'
Select.vm.maybeDeleteValue(); Select.vm.maybeDeleteValue()
expect(Select.vm.selectedValue).toEqual([]); expect(Select.vm.selectedValue).toEqual([])
}); })
it('will not emit input event if value has not changed with backspace', () => { it('will not emit input event if value has not changed with backspace', () => {
const Select = mountDefault(); const Select = mountDefault()
Select.vm.$data._value = 'one'; Select.vm.$data._value = 'one'
Select.find({ ref: 'search' }).trigger('keydown.backspace'); Select.find({ ref: 'search' }).trigger('keydown.backspace')
expect(Select.emitted().input.length).toBe(1); expect(Select.emitted().input.length).toBe(1)
Select.find({ ref: 'search' }).trigger('keydown.backspace'); Select.find({ ref: 'search' }).trigger('keydown.backspace')
Select.find({ ref: 'search' }).trigger('keydown.backspace'); Select.find({ ref: 'search' }).trigger('keydown.backspace')
expect(Select.emitted().input.length).toBe(1); expect(Select.emitted().input.length).toBe(1)
}); })
describe("Clear button", () => { describe('Clear button', () => {
it("should be displayed on single select when value is selected", () => { it('should be displayed on single select when value is selected', () => {
const Select = selectWithProps({ const Select = selectWithProps({
options: ["foo", "bar"], options: ['foo', 'bar'],
value: "foo" value: 'foo',
}); })
expect(Select.vm.showClearButton).toEqual(true); expect(Select.vm.showClearButton).toEqual(true)
}); })
it("should not be displayed on multiple select", () => { it('should not be displayed on multiple select', () => {
const Select = selectWithProps({ const Select = selectWithProps({
options: ["foo", "bar"], options: ['foo', 'bar'],
value: "foo", value: 'foo',
multiple: true multiple: true,
}); })
expect(Select.vm.showClearButton).toEqual(false); expect(Select.vm.showClearButton).toEqual(false)
}); })
it("should remove selected value when clicked", () => { it('should remove selected value when clicked', () => {
const Select = selectWithProps({ const Select = selectWithProps({
options: ["foo", "bar"], options: ['foo', 'bar'],
}); })
Select.vm.$data._value = 'foo'; Select.vm.$data._value = 'foo'
expect(Select.vm.selectedValue).toEqual(["foo"]); expect(Select.vm.selectedValue).toEqual(['foo'])
Select.find("button.vs__clear").trigger("click"); Select.find('button.vs__clear').trigger('click')
expect(Select.emitted().input).toEqual([[null]]); expect(Select.emitted().input).toEqual([[null]])
expect(Select.vm.selectedValue).toEqual([]); expect(Select.vm.selectedValue).toEqual([])
}); })
it("should be disabled when component is disabled", () => { it('should be disabled when component is disabled', () => {
const Select = selectWithProps({ const Select = selectWithProps({
options: ["foo", "bar"], options: ['foo', 'bar'],
value: "foo", value: 'foo',
disabled: true disabled: true,
}); })
expect(Select.find("button.vs__clear").attributes().disabled).toEqual( expect(Select.find('button.vs__clear').attributes().disabled).toEqual(
"disabled" 'disabled'
); )
}); })
}); })
}); })
+147 -147
View File
@@ -1,201 +1,201 @@
import { selectWithProps } from "../helpers"; import { selectWithProps } from '../helpers'
import OpenIndicator from "../../src/components/OpenIndicator"; import OpenIndicator from '../../src/components/OpenIndicator'
const preventDefault = jest.fn() const preventDefault = jest.fn()
function clickEvent (currentTarget) { function clickEvent(currentTarget) {
return { currentTarget, preventDefault } return { currentTarget, preventDefault }
} }
describe("Toggling Dropdown", () => { describe('Toggling Dropdown', () => {
it("should not open the dropdown when the el is clicked but the component is disabled", () => { it('should not open the dropdown when the el is clicked but the component is disabled', () => {
const Select = selectWithProps({ disabled: true }); const Select = selectWithProps({ disabled: true })
Select.vm.toggleDropdown(clickEvent(Select.vm.$refs.search)); Select.vm.toggleDropdown(clickEvent(Select.vm.$refs.search))
expect(Select.vm.open).toEqual(false);
});
it("should open the dropdown when the el is clicked", () => {
const Select = selectWithProps({
value: [{ label: "one" }],
options: [{ label: "one" }]
});
Select.vm.toggleDropdown(clickEvent(Select.vm.$refs.search));
expect(Select.vm.open).toEqual(true);
});
it("should not close the dropdown when the el is clicked and enableMouseInputSearch is set to true", () => {
const Select = selectWithProps({
value: [{ label: "one" }],
options: [{ label: "one" }],
enableMouseSearchInput: true
});
Select.vm.toggleDropdown(clickEvent(Select.vm.$refs.search));
expect(Select.vm.open).toEqual(true);
Select.vm.toggleDropdown(clickEvent(Select.vm.$el));
expect(Select.vm.open).toEqual(false) expect(Select.vm.open).toEqual(false)
}); })
it("should open the dropdown when the selected tag is clicked", () => { it('should open the dropdown when the el is clicked', () => {
const Select = selectWithProps({ const Select = selectWithProps({
value: [{ label: "one" }], value: [{ label: 'one' }],
options: [{ label: "one" }] options: [{ label: 'one' }],
}); })
const selectedTag = Select.find(".vs__selected").element; Select.vm.toggleDropdown(clickEvent(Select.vm.$refs.search))
expect(Select.vm.open).toEqual(true)
})
Select.vm.toggleDropdown(clickEvent(selectedTag)); it('should not close the dropdown when the el is clicked and enableMouseInputSearch is set to true', () => {
expect(Select.vm.open).toEqual(true); const Select = selectWithProps({
}); value: [{ label: 'one' }],
options: [{ label: 'one' }],
enableMouseSearchInput: true,
})
it("can close the dropdown when the el is clicked", () => { Select.vm.toggleDropdown(clickEvent(Select.vm.$refs.search))
const Select = selectWithProps(); expect(Select.vm.open).toEqual(true)
const spy = jest.spyOn(Select.vm.$refs.search, "blur"); Select.vm.toggleDropdown(clickEvent(Select.vm.$el))
expect(Select.vm.open).toEqual(false)
})
Select.vm.open = true; it('should open the dropdown when the selected tag is clicked', () => {
Select.vm.toggleDropdown(clickEvent(Select.vm.$el)); const Select = selectWithProps({
value: [{ label: 'one' }],
options: [{ label: 'one' }],
})
expect(spy).toHaveBeenCalled(); const selectedTag = Select.find('.vs__selected').element
});
it("closes the dropdown when an option is selected, multiple is true, and closeOnSelect option is true", () => { Select.vm.toggleDropdown(clickEvent(selectedTag))
expect(Select.vm.open).toEqual(true)
})
it('can close the dropdown when the el is clicked', () => {
const Select = selectWithProps()
const spy = jest.spyOn(Select.vm.$refs.search, 'blur')
Select.vm.open = true
Select.vm.toggleDropdown(clickEvent(Select.vm.$el))
expect(spy).toHaveBeenCalled()
})
it('closes the dropdown when an option is selected, multiple is true, and closeOnSelect option is true', () => {
const Select = selectWithProps({ const Select = selectWithProps({
value: [], value: [],
options: ["one", "two", "three"], options: ['one', 'two', 'three'],
multiple: true
});
Select.vm.open = true;
Select.vm.select("one");
expect(Select.vm.open).toEqual(false);
});
it("does not close the dropdown when the el is clicked, multiple is true, and closeOnSelect option is false", () => {
const Select = selectWithProps({
value: [],
options: ["one", "two", "three"],
multiple: true, multiple: true,
closeOnSelect: false })
});
Select.vm.open = true; Select.vm.open = true
Select.vm.select("one"); Select.vm.select('one')
expect(Select.vm.open).toEqual(true); expect(Select.vm.open).toEqual(false)
}); })
it("should close the dropdown on search blur", () => { it('does not close the dropdown when the el is clicked, multiple is true, and closeOnSelect option is false', () => {
const Select = selectWithProps({ const Select = selectWithProps({
options: [{ label: "one" }] value: [],
}); options: ['one', 'two', 'three'],
multiple: true,
closeOnSelect: false,
})
Select.vm.open = true; Select.vm.open = true
Select.find({ ref: "search" }).trigger("blur"); Select.vm.select('one')
expect(Select.vm.open).toEqual(false); expect(Select.vm.open).toEqual(true)
}); })
it("will close the dropdown and emit the search:blur event from onSearchBlur", () => { it('should close the dropdown on search blur', () => {
const Select = selectWithProps();
const spy = jest.spyOn(Select.vm, "$emit");
Select.vm.open = true;
Select.vm.onSearchBlur();
expect(Select.vm.open).toEqual(false);
expect(spy).toHaveBeenCalledWith("search:blur");
});
it("will open the dropdown and emit the search:focus event from onSearchFocus", () => {
const Select = selectWithProps();
const spy = jest.spyOn(Select.vm, "$emit");
Select.vm.onSearchFocus();
expect(Select.vm.open).toEqual(true);
expect(spy).toHaveBeenCalledWith("search:focus");
});
it("will close the dropdown on escape, if search is empty", () => {
const Select = selectWithProps();
const spy = jest.spyOn(Select.vm.$refs.search, "blur");
Select.vm.open = true;
Select.vm.onEscape();
expect(spy).toHaveBeenCalled();
});
it("should remove existing search text on escape keydown", () => {
const Select = selectWithProps({ const Select = selectWithProps({
value: [{ label: "one" }], options: [{ label: 'one' }],
options: [{ label: "one" }] })
});
Select.vm.search = "foo"; Select.vm.open = true
Select.find({ ref: 'search' }).trigger('blur')
expect(Select.vm.open).toEqual(false)
})
it('will close the dropdown and emit the search:blur event from onSearchBlur', () => {
const Select = selectWithProps()
const spy = jest.spyOn(Select.vm, '$emit')
Select.vm.open = true
Select.vm.onSearchBlur()
expect(Select.vm.open).toEqual(false)
expect(spy).toHaveBeenCalledWith('search:blur')
})
it('will open the dropdown and emit the search:focus event from onSearchFocus', () => {
const Select = selectWithProps()
const spy = jest.spyOn(Select.vm, '$emit')
Select.vm.onSearchFocus()
expect(Select.vm.open).toEqual(true)
expect(spy).toHaveBeenCalledWith('search:focus')
})
it('will close the dropdown on escape, if search is empty', () => {
const Select = selectWithProps()
const spy = jest.spyOn(Select.vm.$refs.search, 'blur')
Select.vm.open = true
Select.vm.onEscape()
expect(spy).toHaveBeenCalled()
})
it('should remove existing search text on escape keydown', () => {
const Select = selectWithProps({
value: [{ label: 'one' }],
options: [{ label: 'one' }],
})
Select.vm.search = 'foo'
Select.find('.vs__search').trigger('keydown.esc') Select.find('.vs__search').trigger('keydown.esc')
expect(Select.vm.search).toEqual(""); expect(Select.vm.search).toEqual('')
}); })
it("should have an open class when dropdown is active", () => { it('should have an open class when dropdown is active', () => {
const Select = selectWithProps(); const Select = selectWithProps()
expect(Select.vm.stateClasses['vs--open']).toEqual(false); expect(Select.vm.stateClasses['vs--open']).toEqual(false)
Select.vm.open = true; Select.vm.open = true
expect(Select.vm.stateClasses['vs--open']).toEqual(true); expect(Select.vm.stateClasses['vs--open']).toEqual(true)
}); })
it("should not display the dropdown if noDrop is true", async () => { it('should not display the dropdown if noDrop is true', async () => {
const Select = selectWithProps({ const Select = selectWithProps({
noDrop: true, noDrop: true,
}); })
Select.vm.toggleDropdown(clickEvent(Select.vm.$refs.search)); Select.vm.toggleDropdown(clickEvent(Select.vm.$refs.search))
expect(Select.vm.open).toEqual(true); expect(Select.vm.open).toEqual(true)
await Select.vm.$nextTick(); await Select.vm.$nextTick()
expect(Select.contains('.vs__dropdown-menu')).toBeFalsy(); expect(Select.contains('.vs__dropdown-menu')).toBeFalsy()
expect(Select.contains('.vs__dropdown-option')).toBeFalsy(); expect(Select.contains('.vs__dropdown-option')).toBeFalsy()
expect(Select.contains('.vs__no-options')).toBeFalsy(); expect(Select.contains('.vs__no-options')).toBeFalsy()
expect(Select.vm.stateClasses['vs--open']).toBeFalsy(); expect(Select.vm.stateClasses['vs--open']).toBeFalsy()
}); })
it("should hide the open indicator if noDrop is true", () => { it('should hide the open indicator if noDrop is true', () => {
const Select = selectWithProps({ const Select = selectWithProps({
noDrop: true, noDrop: true,
}); })
expect(Select.contains(OpenIndicator)).toBeFalsy(); expect(Select.contains(OpenIndicator)).toBeFalsy()
}); })
it("should not add the searchable state class when noDrop is true", () => { it('should not add the searchable state class when noDrop is true', () => {
const Select = selectWithProps({ const Select = selectWithProps({
noDrop: true, noDrop: true,
}); })
expect(Select.classes('vs--searchable')).toBeFalsy(); expect(Select.classes('vs--searchable')).toBeFalsy()
}); })
it("should not add the searching state class when noDrop is true", () => { it('should not add the searching state class when noDrop is true', () => {
const Select = selectWithProps({ const Select = selectWithProps({
noDrop: true, noDrop: true,
}); })
Select.vm.search = 'Canada'; Select.vm.search = 'Canada'
expect(Select.classes('vs--searching')).toBeFalsy(); expect(Select.classes('vs--searching')).toBeFalsy()
}); })
it("can be opened with dropdownShouldOpen", () => { it('can be opened with dropdownShouldOpen', () => {
const Select = selectWithProps({ const Select = selectWithProps({
noDrop: true, noDrop: true,
dropdownShouldOpen: () => true, dropdownShouldOpen: () => true,
options: ['one'] options: ['one'],
}); })
expect(Select.classes('vs--open')).toBeTruthy(); expect(Select.classes('vs--open')).toBeTruthy()
expect(Select.find('.vs__dropdown-menu li')).toBeTruthy(); expect(Select.find('.vs__dropdown-menu li')).toBeTruthy()
}) })
}); })
+60 -60
View File
@@ -1,86 +1,86 @@
import { shallowMount } from "@vue/test-utils"; import { shallowMount } from '@vue/test-utils'
import VueSelect from "../../src/components/Select"; import VueSelect from '../../src/components/Select'
describe("Filtering Options", () => { describe('Filtering Options', () => {
it("should update the search value when the input element receives the 'input' event", () => { it("should update the search value when the input element receives the 'input' event", () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { options: ["foo", "bar", "baz"] } propsData: { options: ['foo', 'bar', 'baz'] },
}); })
const input = Select.find('.vs__search'); const input = Select.find('.vs__search')
input.element.value = 'a' input.element.value = 'a'
input.trigger('input') input.trigger('input')
expect(Select.vm.search).toEqual('a'); expect(Select.vm.search).toEqual('a')
}); })
it("should filter an array of strings", () => { it('should filter an array of strings', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { options: ["foo", "bar", "baz"] } propsData: { options: ['foo', 'bar', 'baz'] },
}); })
Select.vm.search = "ba"; Select.vm.search = 'ba'
expect(Select.vm.filteredOptions).toEqual(["bar", "baz"]); expect(Select.vm.filteredOptions).toEqual(['bar', 'baz'])
}); })
it("should not filter the array of strings if filterable is false", () => { it('should not filter the array of strings if filterable is false', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { options: ["foo", "bar", "baz"], filterable: false } propsData: { options: ['foo', 'bar', 'baz'], filterable: false },
}); })
Select.vm.search = "ba"; Select.vm.search = 'ba'
expect(Select.vm.filteredOptions).toEqual(["foo", "bar", "baz"]); expect(Select.vm.filteredOptions).toEqual(['foo', 'bar', 'baz'])
}); })
it("should filter without case-sensitivity", () => { it('should filter without case-sensitivity', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { options: ["Foo", "Bar", "Baz"] } propsData: { options: ['Foo', 'Bar', 'Baz'] },
}); })
Select.vm.search = "ba"; Select.vm.search = 'ba'
expect(Select.vm.filteredOptions).toEqual(["Bar", "Baz"]); expect(Select.vm.filteredOptions).toEqual(['Bar', 'Baz'])
}); })
it("can filter an array of objects based on the objects label key", () => { it('can filter an array of objects based on the objects label key', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
options: [{ label: "Foo" }, { label: "Bar" }, { label: "Baz" }] options: [{ label: 'Foo' }, { label: 'Bar' }, { label: 'Baz' }],
} },
}); })
Select.vm.search = "ba"; Select.vm.search = 'ba'
expect(Select.vm.filteredOptions).toEqual([ expect(Select.vm.filteredOptions).toEqual([
{ label: "Bar" }, { label: 'Bar' },
{ label: "Baz" } { label: 'Baz' },
]); ])
}); })
it("can determine if a given option should match the current search text", () => { it('can determine if a given option should match the current search text', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
options: [{ label: "Aoo" }, { label: "Bar" }, { label: "Baz" }], options: [{ label: 'Aoo' }, { label: 'Bar' }, { label: 'Baz' }],
filterBy: (option, label, search) => filterBy: (option, label, search) =>
label.match(new RegExp("^" + search, "i")) label.match(new RegExp('^' + search, 'i')),
} },
}); })
Select.vm.search = "a"; Select.vm.search = 'a'
expect(Select.vm.filteredOptions).toEqual([{ label: "Aoo" }]); expect(Select.vm.filteredOptions).toEqual([{ label: 'Aoo' }])
}); })
it("can use a custom filtering method", () => { it('can use a custom filtering method', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
options: ["foo", "bar", "baz"], options: ['foo', 'bar', 'baz'],
filterBy: (option, label) => label.includes("o") filterBy: (option, label) => label.includes('o'),
} },
}); })
Select.vm.search = "a"; Select.vm.search = 'a'
expect(Select.vm.filteredOptions).toEqual(["foo"]); expect(Select.vm.filteredOptions).toEqual(['foo'])
}); })
it("can filter arrays of numbers", () => { it('can filter arrays of numbers', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
options: [1, 5, 10] options: [1, 5, 10],
} },
}); })
Select.vm.search = "1"; Select.vm.search = '1'
expect(Select.vm.filteredOptions).toEqual([1, 10]); expect(Select.vm.filteredOptions).toEqual([1, 10])
}); })
}); })
+39 -43
View File
@@ -1,74 +1,70 @@
import { mountDefault } from '../helpers'; import { mountDefault } from '../helpers'
describe('Custom Keydown Handlers', () => { describe('Custom Keydown Handlers', () => {
it('can use the map-keydown prop to trigger custom behaviour', () => { it('can use the map-keydown prop to trigger custom behaviour', () => {
const onKeyDown = jest.fn(); const onKeyDown = jest.fn()
const Select = mountDefault({ const Select = mountDefault({
mapKeydown: (defaults, vm) => ({ ...defaults, 32: onKeyDown }), mapKeydown: (defaults, vm) => ({ ...defaults, 32: onKeyDown }),
}); })
Select.find({ ref: 'search' }).trigger('keydown.space'); Select.find({ ref: 'search' }).trigger('keydown.space')
expect(onKeyDown.mock.calls.length).toBe(1); expect(onKeyDown.mock.calls.length).toBe(1)
}); })
it('selectOnKeyCodes should trigger a selection for custom keycodes', () => { it('selectOnKeyCodes should trigger a selection for custom keycodes', () => {
const Select = mountDefault({ const Select = mountDefault({
selectOnKeyCodes: [32], selectOnKeyCodes: [32],
}); })
const spy = jest.spyOn(Select.vm, 'typeAheadSelect'); const spy = jest.spyOn(Select.vm, 'typeAheadSelect')
Select.find({ ref: 'search' }).trigger('keydown.space'); Select.find({ ref: 'search' }).trigger('keydown.space')
expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledTimes(1)
}); })
it('even works when combining selectOnKeyCodes with map-keydown', () => { it('even works when combining selectOnKeyCodes with map-keydown', () => {
const onKeyDown = jest.fn(); const onKeyDown = jest.fn()
const Select = mountDefault({ const Select = mountDefault({
mapKeydown: (defaults, vm) => ({ ...defaults, 32: onKeyDown }), mapKeydown: (defaults, vm) => ({ ...defaults, 32: onKeyDown }),
selectOnKeyCodes: [9], selectOnKeyCodes: [9],
}); })
const spy = jest.spyOn(Select.vm, 'typeAheadSelect'); const spy = jest.spyOn(Select.vm, 'typeAheadSelect')
Select.find({ ref: 'search' }).trigger('keydown.space'); Select.find({ ref: 'search' }).trigger('keydown.space')
expect(onKeyDown.mock.calls.length).toBe(1); expect(onKeyDown.mock.calls.length).toBe(1)
Select.find({ ref: 'search' }).trigger('keydown.tab'); Select.find({ ref: 'search' }).trigger('keydown.tab')
expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledTimes(1)
}); })
describe('CompositionEvent support', () => { describe('CompositionEvent support', () => {
it('will not select a value with enter if the user is composing', () => { it('will not select a value with enter if the user is composing', () => {
const Select = mountDefault(); const Select = mountDefault()
const spy = jest.spyOn(Select.vm, 'typeAheadSelect'); const spy = jest.spyOn(Select.vm, 'typeAheadSelect')
Select.find({ ref: 'search' }).trigger('compositionstart'); Select.find({ ref: 'search' }).trigger('compositionstart')
Select.find({ ref: 'search' }).trigger('keydown.enter'); Select.find({ ref: 'search' }).trigger('keydown.enter')
expect(spy).toHaveBeenCalledTimes(0); expect(spy).toHaveBeenCalledTimes(0)
Select.find({ ref: 'search' }).trigger('compositionend'); Select.find({ ref: 'search' }).trigger('compositionend')
Select.find({ ref: 'search' }).trigger('keydown.enter'); Select.find({ ref: 'search' }).trigger('keydown.enter')
expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledTimes(1)
}); })
it('will not select a value with tab if the user is composing', () => { it('will not select a value with tab if the user is composing', () => {
const Select = mountDefault({ selectOnTab: true }); const Select = mountDefault({ selectOnTab: true })
const spy = jest.spyOn(Select.vm, 'typeAheadSelect'); const spy = jest.spyOn(Select.vm, 'typeAheadSelect')
Select.find({ ref: 'search' }).trigger('compositionstart'); Select.find({ ref: 'search' }).trigger('compositionstart')
Select.find({ ref: 'search' }).trigger('keydown.tab'); Select.find({ ref: 'search' }).trigger('keydown.tab')
expect(spy).toHaveBeenCalledTimes(0); expect(spy).toHaveBeenCalledTimes(0)
Select.find({ ref: 'search' }).trigger('compositionend'); Select.find({ ref: 'search' }).trigger('compositionend')
Select.find({ ref: 'search' }).trigger('keydown.tab'); Select.find({ ref: 'search' }).trigger('keydown.tab')
expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledTimes(1)
}); })
})
}); })
});
+49 -45
View File
@@ -1,57 +1,61 @@
import VueSelect from "../../src/components/Select"; import VueSelect from '../../src/components/Select'
import { shallowMount } from "@vue/test-utils"; import { shallowMount } from '@vue/test-utils'
import { selectWithProps } from "../helpers"; import { selectWithProps } from '../helpers'
describe("Labels", () => { describe('Labels', () => {
it("can generate labels using a custom label key", () => { it('can generate labels using a custom label key', () => {
const Select = selectWithProps({ const Select = selectWithProps({
options: [{ name: "Foo" }], options: [{ name: 'Foo' }],
label: "name", label: 'name',
value: { name: "Foo" } value: { name: 'Foo' },
}); })
expect(Select.find(".vs__selected").text()).toBe("Foo"); expect(Select.find('.vs__selected').text()).toBe('Foo')
}); })
it("will console.warn when options contain objects without a valid label key", async () => { it('will console.warn when options contain objects without a valid label key', async () => {
const spy = jest.spyOn(console, "warn").mockImplementation(() => {}); const spy = jest.spyOn(console, 'warn').mockImplementation(() => {})
const Select = selectWithProps({ const Select = selectWithProps({
options: [{}] options: [{}],
}); })
Select.vm.open = true; Select.vm.open = true
await Select.vm.$nextTick(); await Select.vm.$nextTick()
expect(spy).toHaveBeenCalledWith( expect(spy).toHaveBeenCalledWith(
'[vue-select warn]: Label key "option.label" does not exist in options object {}.' + '[vue-select warn]: Label key "option.label" does not exist in options object {}.' +
"\nhttps://vue-select.org/api/props.html#getoptionlabel" '\nhttps://vue-select.org/api/props.html#getoptionlabel'
); )
}); })
it("should display a placeholder if the value is empty", () => { it('should display a placeholder if the value is empty', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
options: ["one"] options: ['one'],
}, },
attrs: { attrs: {
placeholder: "foo" placeholder: 'foo',
} },
}); })
expect(Select.vm.searchPlaceholder).toEqual("foo"); expect(Select.vm.searchPlaceholder).toEqual('foo')
Select.vm.$data._value = "one"; Select.vm.$data._value = 'one'
expect(Select.vm.searchPlaceholder).not.toBeDefined(); expect(Select.vm.searchPlaceholder).not.toBeDefined()
}); })
describe('getOptionLabel', () => { describe('getOptionLabel', () => {
it('will return undefined if the option lacks the label key', () => { it('will return undefined if the option lacks the label key', () => {
const getOptionLabel = VueSelect.props.getOptionLabel.default.bind({ label: 'label' }); const getOptionLabel = VueSelect.props.getOptionLabel.default.bind({
expect(getOptionLabel({name: 'vue'})).toEqual(undefined); label: 'label',
}); })
expect(getOptionLabel({ name: 'vue' })).toEqual(undefined)
})
it('will return a string value for a valid key', () => { it('will return a string value for a valid key', () => {
const getOptionLabel = VueSelect.props.getOptionLabel.default.bind({ label: 'label' }); const getOptionLabel = VueSelect.props.getOptionLabel.default.bind({
expect(getOptionLabel({label: 'vue'})).toEqual('vue'); label: 'label',
}); })
expect(getOptionLabel({ label: 'vue' })).toEqual('vue')
})
/** /**
* this test fails because of a bug where Vue executes the default contents * this test fails because of a bug where Vue executes the default contents
@@ -60,22 +64,22 @@ describe("Labels", () => {
* @see https://github.com/vuejs/vue/pull/10229 * @see https://github.com/vuejs/vue/pull/10229
*/ */
xit('will not call getOptionLabel if both scoped option slots are used and a filter is provided', () => { xit('will not call getOptionLabel if both scoped option slots are used and a filter is provided', () => {
const spy = spyOn(VueSelect.props.getOptionLabel, 'default'); const spy = spyOn(VueSelect.props.getOptionLabel, 'default')
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
options: [{name: 'one'}], options: [{ name: 'one' }],
filter: () => {}, filter: () => {},
}, },
scopedSlots: { scopedSlots: {
'option': '<span class="option">{{ props.name }}</span>', option: '<span class="option">{{ props.name }}</span>',
'selected-option': '<span class="selected">{{ props.name }}</span>', 'selected-option': '<span class="selected">{{ props.name }}</span>',
}, },
}); })
Select.vm.select({name: 'one'}); Select.vm.select({ name: 'one' })
expect(spy).toHaveBeenCalledTimes(0); expect(spy).toHaveBeenCalledTimes(0)
expect(Select.find('.selected').exists()).toBeTruthy(); expect(Select.find('.selected').exists()).toBeTruthy()
}); })
}); })
}); })
+22 -22
View File
@@ -1,30 +1,30 @@
import { shallowMount } from "@vue/test-utils"; import { shallowMount } from '@vue/test-utils'
import VueSelect from "../../src/components/Select"; import VueSelect from '../../src/components/Select'
describe("Single value options", () => { describe('Single value options', () => {
it("should reset the search input on focus lost", () => { it('should reset the search input on focus lost', () => {
const Select = shallowMount(VueSelect); const Select = shallowMount(VueSelect)
Select.vm.open = true; Select.vm.open = true
Select.vm.search = "t"; Select.vm.search = 't'
expect(Select.vm.search).toEqual("t"); expect(Select.vm.search).toEqual('t')
Select.vm.onSearchBlur(); Select.vm.onSearchBlur()
expect(Select.vm.search).toEqual(""); expect(Select.vm.search).toEqual('')
}); })
it("should not reset the search input on focus lost when clearSearchOnSelect is false", () => { it('should not reset the search input on focus lost when clearSearchOnSelect is false', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { value: "foo", clearSearchOnSelect: false } propsData: { value: 'foo', clearSearchOnSelect: false },
}); })
expect(Select.vm.clearSearchOnSelect).toEqual(false); expect(Select.vm.clearSearchOnSelect).toEqual(false)
Select.vm.open = true; Select.vm.open = true
Select.vm.search = "t"; Select.vm.search = 't'
expect(Select.vm.search).toEqual("t"); expect(Select.vm.search).toEqual('t')
Select.vm.onSearchBlur(); Select.vm.onSearchBlur()
expect(Select.vm.search).toEqual("t"); expect(Select.vm.search).toEqual('t')
}); })
}); })
+19 -18
View File
@@ -1,31 +1,32 @@
import Select from '../../src/components/Select'; import Select from '../../src/components/Select'
describe('Comparing Options', () => { describe('Comparing Options', () => {
const comparator = Select.methods.optionComparator.bind({ const comparator = Select.methods.optionComparator.bind({
getOptionKey: Select.props.getOptionKey.default, getOptionKey: Select.props.getOptionKey.default,
}); })
it('can compare numbers', () => { it('can compare numbers', () => {
expect(comparator(1, 2)).toBeFalsy(); expect(comparator(1, 2)).toBeFalsy()
expect(comparator(1, 1)).toBeTruthy(); expect(comparator(1, 1)).toBeTruthy()
}); })
it('can compare strings', () => { it('can compare strings', () => {
expect(comparator('one', 'one')).toBeTruthy(); expect(comparator('one', 'one')).toBeTruthy()
expect(comparator('one', 'two')).toBeFalsy(); expect(comparator('one', 'two')).toBeFalsy()
}); })
it('can compare objects', () => { it('can compare objects', () => {
// compare ID keys // compare ID keys
expect(comparator({label: 'halo', id: 1}, {label: 'halo', id: 2})) expect(
.toBeFalsy(); comparator({ label: 'halo', id: 1 }, { label: 'halo', id: 2 })
).toBeFalsy()
// compare objects // compare objects
expect(comparator({label: 'halo', value: 1}, {label: 'halo', value: 1})) expect(
.toBeTruthy(); comparator({ label: 'halo', value: 1 }, { label: 'halo', value: 1 })
).toBeTruthy()
// compare objects with different orders // compare objects with different orders
expect(comparator({value: 1, label: 'halo'}, {label: 'halo', value: 1})) expect(
.toBeTruthy(); comparator({ value: 1, label: 'halo' }, { label: 'halo', value: 1 })
}); ).toBeTruthy()
})
}); })
+15 -15
View File
@@ -1,25 +1,25 @@
import Select from '../../src/components/Select.vue'; import Select from '../../src/components/Select.vue'
describe('Serializing Option Keys', () => { describe('Serializing Option Keys', () => {
const getOptionKey = Select.props.getOptionKey.default
const getOptionKey = Select.props.getOptionKey.default;
it('can serialize strings to a key', () => { it('can serialize strings to a key', () => {
expect(getOptionKey('vue')).toBe('vue'); expect(getOptionKey('vue')).toBe('vue')
}); })
it('can serialize integers to a key', () => { it('can serialize integers to a key', () => {
expect(getOptionKey(1)).toBe(1); expect(getOptionKey(1)).toBe(1)
}); })
it('can serialize objects to a key', () => { it('can serialize objects to a key', () => {
expect(getOptionKey({label: 'vue'})).toBe('{"label":"vue"}'); expect(getOptionKey({ label: 'vue' })).toBe('{"label":"vue"}')
}); })
it('will use an ID property if the object contains one', () => { it('will use an ID property if the object contains one', () => {
expect(getOptionKey({id: 1})).toBe(1); expect(getOptionKey({ id: 1 })).toBe(1)
expect(getOptionKey({id: 'one'})).toBe('one'); expect(getOptionKey({ id: 'one' })).toBe('one')
expect(getOptionKey({id: {im: 'a nested object'}})) expect(getOptionKey({ id: { im: 'a nested object' } })).toEqual({
.toEqual({im: 'a nested object'}); im: 'a nested object',
}); })
}); })
})
+113 -91
View File
@@ -1,141 +1,163 @@
import { mount, shallowMount } from '@vue/test-utils'; import { mount, shallowMount } from '@vue/test-utils'
import VueSelect from "../../src/components/Select"; import VueSelect from '../../src/components/Select'
import { mountDefault } from '../helpers'; import { mountDefault } from '../helpers'
describe("Reset on options change", () => { describe('Reset on options change', () => {
it("should not reset the selected value by default when the options property changes", () => { it('should not reset the selected value by default when the options property changes', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { options: ["one"] } propsData: { options: ['one'] },
}); })
Select.vm.$data._value = 'one'; Select.vm.$data._value = 'one'
Select.setProps({options: ["four", "five", "six"]}); Select.setProps({ options: ['four', 'five', 'six'] })
expect(Select.vm.selectedValue).toEqual(["one"]); expect(Select.vm.selectedValue).toEqual(['one'])
}); })
describe('resetOnOptionsChange as a function', () => { describe('resetOnOptionsChange as a function', () => {
it('will yell at you if resetOnOptionsChange is not a function or boolean', () => { it('will yell at you if resetOnOptionsChange is not a function or boolean', () => {
const spy = jest.spyOn(console, 'error').mockImplementation(() => {}); const spy = jest.spyOn(console, 'error').mockImplementation(() => {})
mountDefault({resetOnOptionsChange: 1}); mountDefault({ resetOnOptionsChange: 1 })
expect(spy.mock.calls[0][0]).toContain('Invalid prop: custom validator check failed for prop "resetOnOptionsChange"') expect(spy.mock.calls[0][0]).toContain(
'Invalid prop: custom validator check failed for prop "resetOnOptionsChange"'
)
mountDefault({resetOnOptionsChange: 'one'}); mountDefault({ resetOnOptionsChange: 'one' })
expect(spy.mock.calls[1][0]).toContain('Invalid prop: custom validator check failed for prop "resetOnOptionsChange"') expect(spy.mock.calls[1][0]).toContain(
'Invalid prop: custom validator check failed for prop "resetOnOptionsChange"'
)
mountDefault({resetOnOptionsChange: []}); mountDefault({ resetOnOptionsChange: [] })
expect(spy.mock.calls[2][0]).toContain('Invalid prop: custom validator check failed for prop "resetOnOptionsChange"') expect(spy.mock.calls[2][0]).toContain(
'Invalid prop: custom validator check failed for prop "resetOnOptionsChange"'
)
mountDefault({resetOnOptionsChange: {}}); mountDefault({ resetOnOptionsChange: {} })
expect(spy.mock.calls[3][0]).toContain('Invalid prop: custom validator check failed for prop "resetOnOptionsChange"') expect(spy.mock.calls[3][0]).toContain(
}); 'Invalid prop: custom validator check failed for prop "resetOnOptionsChange"'
)
})
it('should receive the new options, old options, and current value', async () => { it('should receive the new options, old options, and current value', async () => {
let resetOnOptionsChange = jest.fn(option => option); let resetOnOptionsChange = jest.fn((option) => option)
const Select = mountDefault( const Select = mountDefault({
{resetOnOptionsChange, options: ['bear'], value: 'selected'}, resetOnOptionsChange,
); options: ['bear'],
value: 'selected',
})
Select.setProps({options: ['lake', 'kite']}); Select.setProps({ options: ['lake', 'kite'] })
await Select.vm.$nextTick(); await Select.vm.$nextTick()
expect(resetOnOptionsChange).toHaveBeenCalledTimes(1); expect(resetOnOptionsChange).toHaveBeenCalledTimes(1)
expect(resetOnOptionsChange) expect(resetOnOptionsChange).toHaveBeenCalledWith(
.toHaveBeenCalledWith(['lake', 'kite'], ['bear'], ['selected']); ['lake', 'kite'],
}); ['bear'],
['selected']
)
})
it('should allow resetOnOptionsChange to be a function that returns true', async () => { it('should allow resetOnOptionsChange to be a function that returns true', async () => {
let resetOnOptionsChange = () => true; let resetOnOptionsChange = () => true
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: {resetOnOptionsChange, options: ['one'], value: 'one'}, propsData: { resetOnOptionsChange, options: ['one'], value: 'one' },
}); })
const spy = jest.spyOn(Select.vm, 'clearSelection'); const spy = jest.spyOn(Select.vm, 'clearSelection')
Select.setProps({options: ['one', 'two']}); Select.setProps({ options: ['one', 'two'] })
await Select.vm.$nextTick(); await Select.vm.$nextTick()
expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledTimes(1)
}); })
it('should allow resetOnOptionsChange to be a function that returns false', () => { it('should allow resetOnOptionsChange to be a function that returns false', () => {
let resetOnOptionsChange = () => false; let resetOnOptionsChange = () => false
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: {resetOnOptionsChange, options: ['one'], value: 'one'}, propsData: { resetOnOptionsChange, options: ['one'], value: 'one' },
}); })
const spy = jest.spyOn(Select.vm, 'clearSelection'); const spy = jest.spyOn(Select.vm, 'clearSelection')
Select.setProps({options: ['one', 'two']}); Select.setProps({ options: ['one', 'two'] })
expect(spy).not.toHaveBeenCalled(); expect(spy).not.toHaveBeenCalled()
}); })
it('should reset the options if the selectedValue does not exist in the new options', async () => { it('should reset the options if the selectedValue does not exist in the new options', async () => {
let resetOnOptionsChange = (options, old, val) => val.some(val => options.includes(val)); let resetOnOptionsChange = (options, old, val) =>
val.some((val) => options.includes(val))
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: {resetOnOptionsChange, options: ['one'], value: 'one'}, propsData: { resetOnOptionsChange, options: ['one'], value: 'one' },
}); })
const spy = jest.spyOn(Select.vm, 'clearSelection'); const spy = jest.spyOn(Select.vm, 'clearSelection')
Select.setProps({options: ['one', 'two']}); Select.setProps({ options: ['one', 'two'] })
await Select.vm.$nextTick(); await Select.vm.$nextTick()
expect(Select.vm.selectedValue).toEqual(['one']); expect(Select.vm.selectedValue).toEqual(['one'])
Select.setProps({options: ['two']}); Select.setProps({ options: ['two'] })
await Select.vm.$nextTick(); await Select.vm.$nextTick()
expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledTimes(1)
}); })
}); })
it("should reset the selected value when the options property changes", async () => { it('should reset the selected value when the options property changes', async () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { resetOnOptionsChange: true, options: ["one"] } propsData: { resetOnOptionsChange: true, options: ['one'] },
}); })
Select.vm.$data._value = 'one'; Select.vm.$data._value = 'one'
Select.setProps({options: ["four", "five", "six"]}); Select.setProps({ options: ['four', 'five', 'six'] })
await Select.vm.$nextTick(); await Select.vm.$nextTick()
expect(Select.vm.selectedValue).toEqual([]); expect(Select.vm.selectedValue).toEqual([])
}); })
it("should return correct selected value when the options property changes and a new option matches", async () => { it('should return correct selected value when the options property changes and a new option matches', async () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { value: "one", options: [], reduce(option) { return option.value } } propsData: {
}); value: 'one',
options: [],
reduce(option) {
return option.value
},
},
})
Select.setProps({options: [{ label: "oneLabel", value: "one" }]}); Select.setProps({ options: [{ label: 'oneLabel', value: 'one' }] })
await Select.vm.$nextTick(); await Select.vm.$nextTick()
expect(Select.vm.selectedValue).toEqual([{ label: "oneLabel", value: "one" }]); expect(Select.vm.selectedValue).toEqual([
}); { label: 'oneLabel', value: 'one' },
])
})
it('clearSearchOnBlur returns false when multiple is true', () => { it('clearSearchOnBlur returns false when multiple is true', () => {
const Select = mountDefault({}); const Select = mountDefault({})
let clearSearchOnBlur = jest.spyOn(Select.vm, 'clearSearchOnBlur'); let clearSearchOnBlur = jest.spyOn(Select.vm, 'clearSearchOnBlur')
Select.find({ref: 'search'}).trigger('click'); Select.find({ ref: 'search' }).trigger('click')
Select.setData({search: 'one'}); Select.setData({ search: 'one' })
Select.find({ref: 'search'}).trigger('blur'); Select.find({ ref: 'search' }).trigger('blur')
expect(clearSearchOnBlur).toHaveBeenCalledTimes(1); expect(clearSearchOnBlur).toHaveBeenCalledTimes(1)
expect(clearSearchOnBlur).toHaveBeenCalledWith({ expect(clearSearchOnBlur).toHaveBeenCalledWith({
clearSearchOnSelect: true, clearSearchOnSelect: true,
multiple: false, multiple: false,
}); })
expect(Select.vm.search).toBe(''); expect(Select.vm.search).toBe('')
}); })
it('clearSearchOnBlur accepts a function', () => { it('clearSearchOnBlur accepts a function', () => {
let clearSearchOnBlur = jest.fn(() => false); let clearSearchOnBlur = jest.fn(() => false)
const Select = mountDefault({clearSearchOnBlur}); const Select = mountDefault({ clearSearchOnBlur })
Select.find({ref: 'search'}).trigger('click'); Select.find({ ref: 'search' }).trigger('click')
Select.setData({search: 'one'}); Select.setData({ search: 'one' })
Select.find({ref: 'search'}).trigger('blur'); Select.find({ ref: 'search' }).trigger('blur')
expect(clearSearchOnBlur).toHaveBeenCalledTimes(1); expect(clearSearchOnBlur).toHaveBeenCalledTimes(1)
expect(Select.vm.search).toBe('one'); expect(Select.vm.search).toBe('one')
}); })
}); })
+185 -171
View File
@@ -1,127 +1,133 @@
import { mount, shallowMount } from "@vue/test-utils"; import { mount, shallowMount } from '@vue/test-utils'
import VueSelect from "../../src/components/Select"; import VueSelect from '../../src/components/Select'
describe("When reduce prop is defined", () => { describe('When reduce prop is defined', () => {
it("can accept an array of objects and pre-selected value (single)", () => { it('can accept an array of objects and pre-selected value (single)', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
reduce: option => option.value, reduce: (option) => option.value,
value: "foo", value: 'foo',
options: [{ label: "This is Foo", value: "foo" }] options: [{ label: 'This is Foo', value: 'foo' }],
} },
}); })
expect(Select.vm.selectedValue).toEqual([{ label: "This is Foo", value: "foo" }]); expect(Select.vm.selectedValue).toEqual([
}); { label: 'This is Foo', value: 'foo' },
])
})
it("can determine if an object is pre-selected", () => { it('can determine if an object is pre-selected', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
reduce: option => option.id, reduce: (option) => option.id,
value: "foo", value: 'foo',
options: [ options: [
{ {
id: "foo", id: 'foo',
label: "This is Foo" label: 'This is Foo',
} },
] ],
} },
}); })
expect( expect(
Select.vm.isOptionSelected({ Select.vm.isOptionSelected({
id: "foo", id: 'foo',
label: "This is Foo" label: 'This is Foo',
}) })
).toEqual(true); ).toEqual(true)
}); })
it('can determine if an object is selected after its been chosen', () => { it('can determine if an object is selected after its been chosen', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
reduce: option => option.id, reduce: (option) => option.id,
options: [{id: 'foo', label: 'FooBar'}], options: [{ id: 'foo', label: 'FooBar' }],
}, },
}); })
Select.vm.select({id: 'foo', label: 'FooBar'}); Select.vm.select({ id: 'foo', label: 'FooBar' })
expect(Select.vm.isOptionSelected({ expect(
Select.vm.isOptionSelected({
id: 'foo', id: 'foo',
label: 'This is FooBar', label: 'This is FooBar',
})).toEqual(true); })
}); ).toEqual(true)
})
it("can accept an array of objects and pre-selected values (multiple)", () => { it('can accept an array of objects and pre-selected values (multiple)', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
multiple: true, multiple: true,
reduce: option => option.value, reduce: (option) => option.value,
value: ["foo"], value: ['foo'],
options: [ options: [
{ label: "This is Foo", value: "foo" }, { label: 'This is Foo', value: 'foo' },
{ label: "This is Bar", value: "bar" } { label: 'This is Bar', value: 'bar' },
] ],
} },
}); })
expect(Select.vm.selectedValue).toEqual([{ label: "This is Foo", value: "foo" }]); expect(Select.vm.selectedValue).toEqual([
}); { label: 'This is Foo', value: 'foo' },
])
})
it("can deselect a pre-selected object", () => { it('can deselect a pre-selected object', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
multiple: true, multiple: true,
reduce: option => option.value, reduce: (option) => option.value,
options: [ options: [
{ label: "This is Foo", value: "foo" }, { label: 'This is Foo', value: 'foo' },
{ label: "This is Bar", value: "bar" } { label: 'This is Bar', value: 'bar' },
] ],
} },
}); })
Select.vm.$data._value = ['foo', 'bar']; Select.vm.$data._value = ['foo', 'bar']
Select.vm.deselect("foo"); Select.vm.deselect('foo')
expect(Select.vm.selectedValue).toEqual(["bar"]); expect(Select.vm.selectedValue).toEqual(['bar'])
}); })
it("can deselect an option when multiple is false", () => { it('can deselect an option when multiple is false', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
reduce: option => option.value, reduce: (option) => option.value,
options: [ options: [
{ label: "This is Foo", value: "foo" }, { label: 'This is Foo', value: 'foo' },
{ label: "This is Bar", value: "bar" } { label: 'This is Bar', value: 'bar' },
] ],
} },
}); })
Select.vm.deselect("foo"); Select.vm.deselect('foo')
expect(Select.vm.selectedValue).toEqual([]); expect(Select.vm.selectedValue).toEqual([])
}); })
it("can use v-model syntax for a two way binding to a parent component", async () => { it('can use v-model syntax for a two way binding to a parent component', async () => {
const Parent = mount({ const Parent = mount({
data: () => ({ data: () => ({
reduce: option => option.value, reduce: (option) => option.value,
current: "foo", current: 'foo',
options: [ options: [
{ label: "This is Foo", value: "foo" }, { label: 'This is Foo', value: 'foo' },
{ label: "This is Bar", value: "bar" }, { label: 'This is Bar', value: 'bar' },
{ label: "This is Baz", value: "baz" } { label: 'This is Baz', value: 'baz' },
] ],
}), }),
components: { "v-select": VueSelect }, components: { 'v-select': VueSelect },
computed: { computed: {
value: { value: {
get() { get() {
return this.current; return this.current
}, },
set(value) { set(value) {
if (value == 'baz') return; if (value == 'baz') return
this.current = value; this.current = value
} },
} },
}, },
template: ` template: `
<v-select <v-select
@@ -129,133 +135,141 @@ describe("When reduce prop is defined", () => {
:reduce="option => option.value" :reduce="option => option.value"
:options="options" :options="options"
/> />
` `,
}); })
const Select = Parent.vm.$children[0]; const Select = Parent.vm.$children[0]
expect(Select.value).toEqual("foo"); expect(Select.value).toEqual('foo')
expect(Select.selectedValue).toEqual([{ label: "This is Foo", value: "foo" }]); expect(Select.selectedValue).toEqual([
{ label: 'This is Foo', value: 'foo' },
])
Select.select({ label: "This is Bar", value: "bar" }); Select.select({ label: 'This is Bar', value: 'bar' })
await Select.$nextTick(); await Select.$nextTick()
expect(Parent.vm.value).toEqual("bar"); expect(Parent.vm.value).toEqual('bar')
expect(Select.selectedValue).toEqual([{ label: "This is Bar", value: "bar" }]); expect(Select.selectedValue).toEqual([
{ label: 'This is Bar', value: 'bar' },
])
// Parent denies to set baz // Parent denies to set baz
Select.select({ label: "This is Baz", value: "baz" }); Select.select({ label: 'This is Baz', value: 'baz' })
await Select.$nextTick(); await Select.$nextTick()
expect(Select.selectedValue).toEqual([{ label: "This is Bar", value: "bar" }]); expect(Select.selectedValue).toEqual([
expect(Parent.vm.value).toEqual('bar'); { label: 'This is Bar', value: 'bar' },
}); ])
expect(Parent.vm.value).toEqual('bar')
})
it("can generate labels using a custom label key", () => { it('can generate labels using a custom label key', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
multiple: true, multiple: true,
reduce: option => option.value, reduce: (option) => option.value,
value: ["CA"], value: ['CA'],
label: "name", label: 'name',
options: [{ value: "CA", name: "Canada" }, { value: "US", name: "United States" }] options: [
} { value: 'CA', name: 'Canada' },
}); { value: 'US', name: 'United States' },
],
},
})
expect(Select.find(".vs__selected").text()).toContain("Canada"); expect(Select.find('.vs__selected').text()).toContain('Canada')
}); })
it("can find the original option within this.options", () => { it('can find the original option within this.options', () => {
const optionToFind = { id: 1, label: "Foo" }; const optionToFind = { id: 1, label: 'Foo' }
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
reduce: option => option.id, reduce: (option) => option.id,
options: [optionToFind, { id: 2, label: "Bar" }] options: [optionToFind, { id: 2, label: 'Bar' }],
} },
}); })
expect(Select.vm.findOptionFromReducedValue(1)).toEqual(optionToFind); expect(Select.vm.findOptionFromReducedValue(1)).toEqual(optionToFind)
expect(Select.vm.findOptionFromReducedValue(optionToFind)).toEqual( expect(Select.vm.findOptionFromReducedValue(optionToFind)).toEqual(
optionToFind optionToFind
); )
}); })
it('can work with falsey values', () => { it('can work with falsey values', () => {
const option = {value: 0, label: 'No'}; const option = { value: 0, label: 'No' }
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
reduce: option => option.value, reduce: (option) => option.value,
options: [option, {value: 1, label: 'Yes'}], options: [option, { value: 1, label: 'Yes' }],
value: 0, value: 0,
}, },
}); })
expect(Select.vm.findOptionFromReducedValue(option)).toEqual(option); expect(Select.vm.findOptionFromReducedValue(option)).toEqual(option)
expect(Select.vm.selectedValue).toEqual([option]); expect(Select.vm.selectedValue).toEqual([option])
}); })
it('works with null values', () => { it('works with null values', () => {
const option = {value: null, label: 'No'}; const option = { value: null, label: 'No' }
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
reduce: option => option.value, reduce: (option) => option.value,
options: [option, {value: 1, label: 'Yes'}], options: [option, { value: 1, label: 'Yes' }],
value: null, value: null,
}, },
}); })
expect(Select.vm.findOptionFromReducedValue(option)).toEqual(option); expect(Select.vm.findOptionFromReducedValue(option)).toEqual(option)
expect(Select.vm.selectedValue).toEqual([option]); expect(Select.vm.selectedValue).toEqual([option])
}); })
describe("And when a reduced option is a nested object", () => { describe('And when a reduced option is a nested object', () => {
it("can determine if an object is pre-selected", () => { it('can determine if an object is pre-selected', () => {
const nestedOption = { value: { nested: true }, label: "foo" }; const nestedOption = { value: { nested: true }, label: 'foo' }
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
reduce: option => option.value, reduce: (option) => option.value,
value: { value: {
nested: true nested: true,
}, },
options: [nestedOption] options: [nestedOption],
} },
}); })
expect(Select.vm.selectedValue).toEqual([nestedOption]); expect(Select.vm.selectedValue).toEqual([nestedOption])
}); })
it("can determine if an object is selected after it is chosen", () => { it('can determine if an object is selected after it is chosen', () => {
const nestedOption = { value: { nested: true }, label: "foo" }; const nestedOption = { value: { nested: true }, label: 'foo' }
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
reduce: option => option.value, reduce: (option) => option.value,
options: [nestedOption] options: [nestedOption],
} },
}); })
Select.vm.select(nestedOption); Select.vm.select(nestedOption)
expect(Select.vm.isOptionSelected(nestedOption)).toEqual(true); expect(Select.vm.isOptionSelected(nestedOption)).toEqual(true)
}); })
})
}); it('reacts correctly when value property changes', async () => {
const optionToChangeTo = { id: 1, label: 'Foo' }
it("reacts correctly when value property changes", async () => {
const optionToChangeTo = { id: 1, label: "Foo" };
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
value: 2, value: 2,
reduce: option => option.id, reduce: (option) => option.id,
options: [optionToChangeTo, { id: 2, label: "Bar" }] options: [optionToChangeTo, { id: 2, label: 'Bar' }],
} },
}); })
Select.setProps({ value: optionToChangeTo.id }); Select.setProps({ value: optionToChangeTo.id })
await Select.vm.$nextTick(); await Select.vm.$nextTick()
expect(Select.vm.selectedValue).toEqual([optionToChangeTo]); expect(Select.vm.selectedValue).toEqual([optionToChangeTo])
}); })
describe('Reducing Tags', () => { describe('Reducing Tags', () => {
it('tracks values that have been created by the user', async () => { it('tracks values that have been created by the user', async () => {
const Parent = mount({ const Parent = mount({
data: () => ({selected: null, options: []}), data: () => ({ selected: null, options: [] }),
template: ` template: `
<v-select <v-select
v-model="selected" v-model="selected"
@@ -265,24 +279,24 @@ describe("When reduce prop is defined", () => {
:create-option="label => ({ label, value: -1 })" :create-option="label => ({ label, value: -1 })"
/> />
`, `,
components: {'v-select': VueSelect}, components: { 'v-select': VueSelect },
}); })
const Select = Parent.vm.$children[0]; const Select = Parent.vm.$children[0]
// When // When
Select.$refs.search.focus(); Select.$refs.search.focus()
await Select.$nextTick(); await Select.$nextTick()
Select.search = 'hello'; Select.search = 'hello'
await Select.$nextTick(); await Select.$nextTick()
Select.typeAheadSelect(); Select.typeAheadSelect()
await Select.$nextTick(); await Select.$nextTick()
// Then // Then
expect(Select.selectedValue).toEqual([{label: 'hello', value: -1}]); expect(Select.selectedValue).toEqual([{ label: 'hello', value: -1 }])
expect(Select.$refs.selectedOptions.textContent.trim()).toEqual('hello'); expect(Select.$refs.selectedOptions.textContent.trim()).toEqual('hello')
expect(Parent.vm.selected).toEqual(-1); expect(Parent.vm.selected).toEqual(-1)
}); })
}); })
}); })
+40 -40
View File
@@ -1,59 +1,59 @@
import { selectWithProps } from "../helpers"; import { selectWithProps } from '../helpers'
describe("Selectable prop", () => { describe('Selectable prop', () => {
it("should select selectable option if clicked", async () => { it('should select selectable option if clicked', async () => {
const Select = selectWithProps({ const Select = selectWithProps({
options: ["one", "two", "three"], options: ['one', 'two', 'three'],
selectable: (option) => option === "one" selectable: (option) => option === 'one',
}); })
Select.vm.$data.open = true; Select.vm.$data.open = true
await Select.vm.$nextTick(); await Select.vm.$nextTick()
Select.find(".vs__dropdown-menu li:first-child").trigger("mousedown"); Select.find('.vs__dropdown-menu li:first-child').trigger('mousedown')
await Select.vm.$nextTick(); await Select.vm.$nextTick()
expect(Select.vm.selectedValue).toEqual(["one"]); expect(Select.vm.selectedValue).toEqual(['one'])
}) })
it("should not select not selectable option if clicked", async () => { it('should not select not selectable option if clicked', async () => {
const Select = selectWithProps({ const Select = selectWithProps({
options: ["one", "two", "three"], options: ['one', 'two', 'three'],
selectable: (option) => option === "one" selectable: (option) => option === 'one',
}); })
Select.vm.$data.open = true; Select.vm.$data.open = true
await Select.vm.$nextTick(); await Select.vm.$nextTick()
Select.find(".vs__dropdown-menu li:last-child").trigger("mousedown"); Select.find('.vs__dropdown-menu li:last-child').trigger('mousedown')
await Select.vm.$nextTick(); await Select.vm.$nextTick()
expect(Select.vm.selectedValue).toEqual([]); expect(Select.vm.selectedValue).toEqual([])
});
it("should skip non-selectable option on down arrow keyDown", () => {
const Select = selectWithProps({
options: ["one", "two", "three"],
selectable: (option) => option !== "two"
});
Select.vm.typeAheadPointer = 1;
Select.find({ ref: "search" }).trigger("keydown.down");
expect(Select.vm.typeAheadPointer).toEqual(2);
}) })
it("should skip non-selectable option on up arrow keyDown", () => { it('should skip non-selectable option on down arrow keyDown', () => {
const Select = selectWithProps({ const Select = selectWithProps({
options: ["one", "two", "three"], options: ['one', 'two', 'three'],
selectable: (option) => option !== "two" selectable: (option) => option !== 'two',
}); })
Select.vm.typeAheadPointer = 2; Select.vm.typeAheadPointer = 1
Select.find({ ref: "search" }).trigger("keydown.up"); Select.find({ ref: 'search' }).trigger('keydown.down')
expect(Select.vm.typeAheadPointer).toEqual(0); expect(Select.vm.typeAheadPointer).toEqual(2)
})
it('should skip non-selectable option on up arrow keyDown', () => {
const Select = selectWithProps({
options: ['one', 'two', 'three'],
selectable: (option) => option !== 'two',
})
Select.vm.typeAheadPointer = 2
Select.find({ ref: 'search' }).trigger('keydown.up')
expect(Select.vm.typeAheadPointer).toEqual(0)
}) })
}) })
+224 -205
View File
@@ -1,303 +1,322 @@
import { mount, shallowMount } from "@vue/test-utils"; import { mount, shallowMount } from '@vue/test-utils'
import VueSelect from "../../src/components/Select.vue"; import VueSelect from '../../src/components/Select.vue'
import { mountDefault } from '../helpers'; import { mountDefault } from '../helpers'
describe("VS - Selecting Values", () => { describe('VS - Selecting Values', () => {
let defaultProps; let defaultProps
beforeEach(() => { beforeEach(() => {
defaultProps = { defaultProps = {
value: "one", value: 'one',
options: ["one", "two", "three"] options: ['one', 'two', 'three'],
}; }
}); })
it("can accept an array with pre-selected values", () => { it('can accept an array with pre-selected values', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: defaultProps propsData: defaultProps,
}); })
expect(Select.selectedValue).toEqual(Select.value); expect(Select.selectedValue).toEqual(Select.value)
}); })
it("can accept an array of objects and pre-selected value (single)", () => { it('can accept an array of objects and pre-selected value (single)', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
value: { label: "This is Foo", value: "foo" }, value: { label: 'This is Foo', value: 'foo' },
options: [ options: [
{ label: "This is Foo", value: "foo" }, { label: 'This is Foo', value: 'foo' },
{ label: "This is Bar", value: "bar" } { label: 'This is Bar', value: 'bar' },
] ],
} },
}); })
expect(Select.selectedValue).toEqual(Select.value); expect(Select.selectedValue).toEqual(Select.value)
}); })
it("can accept an array of objects and pre-selected values (multiple)", () => { it('can accept an array of objects and pre-selected values (multiple)', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
value: [ value: [
{ label: "This is Foo", value: "foo" }, { label: 'This is Foo', value: 'foo' },
{ label: "This is Bar", value: "bar" } { label: 'This is Bar', value: 'bar' },
], ],
options: [ options: [
{ label: "This is Foo", value: "foo" }, { label: 'This is Foo', value: 'foo' },
{ label: "This is Bar", value: "bar" } { label: 'This is Bar', value: 'bar' },
] ],
}, },
multiple: true multiple: true,
}); })
expect(Select.selectedValue).toEqual(Select.value); expect(Select.selectedValue).toEqual(Select.value)
}); })
it("can select an option on tab", () => { it('can select an option on tab', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
selectOnTab: true selectOnTab: true,
} },
}); })
const spy = jest.spyOn(Select.vm, "typeAheadSelect"); const spy = jest.spyOn(Select.vm, 'typeAheadSelect')
Select.find({ ref: "search" }).trigger("keydown.tab"); Select.find({ ref: 'search' }).trigger('keydown.tab')
expect(spy).toHaveBeenCalledWith(); expect(spy).toHaveBeenCalledWith()
}); })
it("can deselect a pre-selected object", () => { it('can deselect a pre-selected object', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
multiple: true, multiple: true,
options: [ options: [
{ label: "This is Foo", value: "foo" }, { label: 'This is Foo', value: 'foo' },
{ label: "This is Bar", value: "bar" } { label: 'This is Bar', value: 'bar' },
] ],
} },
}); })
Select.vm.$data._value = [ Select.vm.$data._value = [
{ label: "This is Foo", value: "foo" }, { label: 'This is Foo', value: 'foo' },
{ label: "This is Bar", value: "bar" } { label: 'This is Bar', value: 'bar' },
]; ]
Select.vm.deselect({ label: "This is Foo", value: "foo" }); Select.vm.deselect({ label: 'This is Foo', value: 'foo' })
expect(Select.vm.selectedValue).toEqual([{ label: "This is Bar", value: "bar" }]); expect(Select.vm.selectedValue).toEqual([
}); { label: 'This is Bar', value: 'bar' },
])
})
it("can deselect a pre-selected string", () => { it('can deselect a pre-selected string', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
multiple: true, multiple: true,
options: ["foo", "bar"] options: ['foo', 'bar'],
} },
}); })
Select.vm.$data._value = "foo"; Select.vm.$data._value = 'foo'
Select.vm.deselect("foo"); Select.vm.deselect('foo')
expect(Select.vm.selectedValue).toEqual([]); expect(Select.vm.selectedValue).toEqual([])
}); })
it("can deselect an option when multiple is false", () => { it('can deselect an option when multiple is false', () => {
const Select = shallowMount(VueSelect); const Select = shallowMount(VueSelect)
Select.vm.$data._value = "foo"; Select.vm.$data._value = 'foo'
Select.vm.deselect("foo"); Select.vm.deselect('foo')
expect(Select.vm.selectedValue).toEqual([]); expect(Select.vm.selectedValue).toEqual([])
}); })
it("can determine if the value prop is empty", () => { it('can determine if the value prop is empty', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
options: ["one", "two", "three"] options: ['one', 'two', 'three'],
} },
}); })
const select = Select.vm; const select = Select.vm
expect(select.isValueEmpty).toEqual(true); expect(select.isValueEmpty).toEqual(true)
select.select(["one"]); select.select(['one'])
expect(select.isValueEmpty).toEqual(false); expect(select.isValueEmpty).toEqual(false)
select.select("one"); select.select('one')
expect(select.isValueEmpty).toEqual(false); expect(select.isValueEmpty).toEqual(false)
select.select({ label: "foo", value: "foo" }); select.select({ label: 'foo', value: 'foo' })
expect(select.isValueEmpty).toEqual(false); expect(select.isValueEmpty).toEqual(false)
select.select(""); select.select('')
expect(select.isValueEmpty).toEqual(true); expect(select.isValueEmpty).toEqual(true)
select.select(null); select.select(null)
expect(select.isValueEmpty).toEqual(true); expect(select.isValueEmpty).toEqual(true)
}); })
it("should reset the selected values when the multiple property changes", () => { it('should reset the selected values when the multiple property changes', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
multiple: true, multiple: true,
options: ["one", "two", "three"] options: ['one', 'two', 'three'],
} },
}); })
Select.setProps({ multiple: false }); Select.setProps({ multiple: false })
expect(Select.vm.selectedValue).toEqual([]); expect(Select.vm.selectedValue).toEqual([])
Select.setProps({ multiple: true }); Select.setProps({ multiple: true })
expect(Select.vm.selectedValue).toEqual([]); expect(Select.vm.selectedValue).toEqual([])
}); })
it("can retain values present in a new array of options", () => { it('can retain values present in a new array of options', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
value: ["one"], value: ['one'],
options: ["one", "two", "three"] options: ['one', 'two', 'three'],
} },
}); })
Select.setProps({ options: ["one", "five", "six"] }); Select.setProps({ options: ['one', 'five', 'six'] })
expect(Select.vm.selectedValue).toEqual(["one"]); expect(Select.vm.selectedValue).toEqual(['one'])
}); })
it("can determine if an object is already selected", () => { it('can determine if an object is already selected', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
value: [{ label: "one" }], value: [{ label: 'one' }],
options: [{ label: "one" }] options: [{ label: 'one' }],
} },
}); })
expect(Select.vm.isOptionSelected({ label: "one" })).toEqual(true); expect(Select.vm.isOptionSelected({ label: 'one' })).toEqual(true)
}); })
it("can use v-model syntax for a two way binding to a parent component", () => { it('can use v-model syntax for a two way binding to a parent component', () => {
const Parent = mount({ const Parent = mount({
data: () => ({ value: "foo", options: ["foo", "bar", "baz"] }), data: () => ({ value: 'foo', options: ['foo', 'bar', 'baz'] }),
template: `<div><v-select :options="options" v-model="value" /></div>`, template: `<div><v-select :options="options" v-model="value" /></div>`,
components: { "v-select": VueSelect } components: { 'v-select': VueSelect },
}); })
const Select = Parent.vm.$children[0]; const Select = Parent.vm.$children[0]
expect(Select.value).toEqual("foo"); expect(Select.value).toEqual('foo')
expect(Select.selectedValue).toEqual(["foo"]); expect(Select.selectedValue).toEqual(['foo'])
Select.select("bar"); Select.select('bar')
expect(Parent.vm.value).toEqual("bar"); expect(Parent.vm.value).toEqual('bar')
}); })
it("can check if a string value is selected when the value is an object and multiple is true", () => { it('can check if a string value is selected when the value is an object and multiple is true', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { propsData: {
multiple: true, multiple: true,
value: [{ label: "foo", value: "bar" }] value: [{ label: 'foo', value: 'bar' }],
} },
}); })
expect(Select.vm.isOptionSelected({ label: "foo", value: "bar" })).toEqual(true); expect(Select.vm.isOptionSelected({ label: 'foo', value: 'bar' })).toEqual(
}); true
)
})
it('can select two options with the same label', () => { it('can select two options with the same label', () => {
const options = [{label: 'one', id: 1}, {label: 'one', id: 2}]; const options = [
const Select = mountDefault({options, multiple: true}); { label: 'one', id: 1 },
{ label: 'one', id: 2 },
]
const Select = mountDefault({ options, multiple: true })
Select.vm.select({label: 'one', id: 1}); Select.vm.select({ label: 'one', id: 1 })
Select.vm.select({label: 'one', id: 2}); Select.vm.select({ label: 'one', id: 2 })
expect(Select.vm.selectedValue).toEqual(options); expect(Select.vm.selectedValue).toEqual(options)
}); })
describe("input Event", () => { describe('input Event', () => {
it("will trigger the input event when the selection changes", () => { it('will trigger the input event when the selection changes', () => {
const Select = shallowMount(VueSelect); const Select = shallowMount(VueSelect)
Select.vm.select("bar"); Select.vm.select('bar')
expect(Select.emitted("input")[0]).toEqual(["bar"]); expect(Select.emitted('input')[0]).toEqual(['bar'])
}); })
it("will trigger the input event when the selection changes and multiple is true", () => { it('will trigger the input event when the selection changes and multiple is true', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { multiple: true, value: ["foo"], options: ["foo", "bar"] } propsData: { multiple: true, value: ['foo'], options: ['foo', 'bar'] },
}); })
Select.vm.select("bar"); Select.vm.select('bar')
expect(Select.emitted("input")[0]).toEqual([["foo", "bar"]]); expect(Select.emitted('input')[0]).toEqual([['foo', 'bar']])
}); })
it("will not trigger the input event when multiple is true and selection is repeated", () => { it('will not trigger the input event when multiple is true and selection is repeated', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { multiple: true, value: ["foo ", "bar"], options: ["foo", "bar", "baz"] } propsData: {
}); multiple: true,
value: ['foo ', 'bar'],
options: ['foo', 'bar', 'baz'],
},
})
Select.vm.select("bar"); Select.vm.select('bar')
expect(Select.emitted("input")).toBeFalsy(); expect(Select.emitted('input')).toBeFalsy()
}); })
}); })
describe("option:selecting Event", () => { describe('option:selecting Event', () => {
it("will trigger the option:selecting event when an option is selected", () => { it('will trigger the option:selecting event when an option is selected', () => {
const Select = shallowMount(VueSelect); const Select = shallowMount(VueSelect)
Select.vm.select("bar"); Select.vm.select('bar')
expect(Select.emitted("option:selecting")[0]).toEqual(["bar"]); expect(Select.emitted('option:selecting')[0]).toEqual(['bar'])
}); })
it("will trigger the option:selecting event regardless of current value", () => { it('will trigger the option:selecting event regardless of current value', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { value: ["foo"], options: ["foo", "bar"] } propsData: { value: ['foo'], options: ['foo', 'bar'] },
}); })
Select.vm.select("foo"); Select.vm.select('foo')
Select.vm.select("bar"); Select.vm.select('bar')
expect(Select.emitted("option:selecting")).toEqual([["foo"], ["bar"]]); expect(Select.emitted('option:selecting')).toEqual([['foo'], ['bar']])
}); })
it("will trigger the option:selecting event with current selected item when multiple is true", () => { it('will trigger the option:selecting event with current selected item when multiple is true', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { multiple: true, value: ["foo"], options: ["foo", "bar"] } propsData: { multiple: true, value: ['foo'], options: ['foo', 'bar'] },
}); })
Select.vm.select("bar"); Select.vm.select('bar')
expect(Select.emitted("option:selecting")[0]).toEqual(["bar"]); expect(Select.emitted('option:selecting')[0]).toEqual(['bar'])
}); })
it("will trigger the option:selecting event regardless of current value when multiple is true", () => { it('will trigger the option:selecting event regardless of current value when multiple is true', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { multiple: true, value: ["foo", "bar"], options: ["foo", "bar"] } propsData: {
}); multiple: true,
Select.vm.select("bar"); value: ['foo', 'bar'],
Select.vm.select("bar"); options: ['foo', 'bar'],
expect(Select.emitted("option:selecting")).toEqual([["bar"], ["bar"]]); },
}); })
}); Select.vm.select('bar')
Select.vm.select('bar')
expect(Select.emitted('option:selecting')).toEqual([['bar'], ['bar']])
})
})
describe("option:deselected Event", () => { describe('option:deselected Event', () => {
it("will trigger the option:deselected event when an option is deselected", () => { it('will trigger the option:deselected event when an option is deselected', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { value: ["foo"], options: ["foo", "bar"] } propsData: { value: ['foo'], options: ['foo', 'bar'] },
}); })
Select.vm.deselect("foo"); Select.vm.deselect('foo')
expect(Select.emitted("option:deselected")[0]).toEqual(["foo"]); expect(Select.emitted('option:deselected')[0]).toEqual(['foo'])
}); })
it("will trigger the option:deselected event regardless of current value", () => { it('will trigger the option:deselected event regardless of current value', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { value: ["foo"], options: ["foo", "bar"] } propsData: { value: ['foo'], options: ['foo', 'bar'] },
}); })
Select.vm.deselect("foo"); Select.vm.deselect('foo')
Select.vm.deselect("bar"); Select.vm.deselect('bar')
expect(Select.emitted("option:deselected")).toEqual([["foo"], ["bar"]]); expect(Select.emitted('option:deselected')).toEqual([['foo'], ['bar']])
}); })
it("will trigger the option:selected event with current selected item when multiple is true", () => { it('will trigger the option:selected event with current selected item when multiple is true', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { multiple: true, value: ["foo"], options: ["foo", "bar"] } propsData: { multiple: true, value: ['foo'], options: ['foo', 'bar'] },
}); })
Select.vm.deselect("bar"); Select.vm.deselect('bar')
expect(Select.emitted("option:deselected")[0]).toEqual(["bar"]); expect(Select.emitted('option:deselected')[0]).toEqual(['bar'])
}); })
it("will trigger the option:selected event regardless of current value when multiple is true", () => { it('will trigger the option:selected event regardless of current value when multiple is true', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { multiple: true, value: ["foo", "bar"], options: ["foo", "bar"] } propsData: {
}); multiple: true,
Select.vm.deselect("bar"); value: ['foo', 'bar'],
Select.vm.deselect("bar"); options: ['foo', 'bar'],
expect(Select.emitted("option:deselected")).toEqual([["bar"], ["bar"]]); },
}); })
}); Select.vm.deselect('bar')
}); Select.vm.deselect('bar')
expect(Select.emitted('option:deselected')).toEqual([['bar'], ['bar']])
})
})
})
+107 -76
View File
@@ -1,122 +1,153 @@
import { mountDefault } from '../helpers'; import { mountDefault } from '../helpers'
describe('Scoped Slots', () => { describe('Scoped Slots', () => {
it('receives an option object to the selected-option-container slot', () => { it('receives an option object to the selected-option-container slot', () => {
const Select = mountDefault( const Select = mountDefault(
{value: 'one'}, { value: 'one' },
{ {
scopedSlots: { scopedSlots: {
'selected-option-container': `<span slot="selected-option-container" slot-scope="{option}">{{ option.label }}</span>`, 'selected-option-container': `<span slot="selected-option-container" slot-scope="{option}">{{ option.label }}</span>`,
}, },
}); }
)
expect(Select.find({ref: 'selectedOptions'}).text()).toEqual('one'); expect(Select.find({ ref: 'selectedOptions' }).text()).toEqual('one')
}); })
describe('Slot: selected-option', () => { describe('Slot: selected-option', () => {
it('receives an option object to the selected-option slot', () => { it('receives an option object to the selected-option slot', () => {
const Select = mountDefault( const Select = mountDefault(
{value: 'one'}, { value: 'one' },
{ {
scopedSlots: { scopedSlots: {
'selected-option': `<span slot="selected-option" slot-scope="option">{{ option.label }}</span>`, 'selected-option': `<span slot="selected-option" slot-scope="option">{{ option.label }}</span>`,
}, },
}); }
)
expect(Select.find('.vs__selected').text()).toEqual('one'); expect(Select.find('.vs__selected').text()).toEqual('one')
}); })
it('opens the dropdown when clicking an option in selected-option slot', it('opens the dropdown when clicking an option in selected-option slot', () => {
() => {
const Select = mountDefault(
{value: 'one'},
{
scopedSlots: {
'selected-option': `<span class="my-option" slot-scope="option">{{ option.label }}</span>`,
},
});
Select.find('.my-option').trigger('mousedown');
expect(Select.vm.open).toEqual(true);
});
});
it('receives an option object to the option slot in the dropdown menu',
async () => {
const Select = mountDefault( const Select = mountDefault(
{value: 'one'}, { value: 'one' },
{ {
scopedSlots: { scopedSlots: {
'option': `<span slot="option" slot-scope="option">{{ option.label }}</span>`, 'selected-option': `<span class="my-option" slot-scope="option">{{ option.label }}</span>`,
}, },
}); }
)
Select.vm.open = true; Select.find('.my-option').trigger('mousedown')
await Select.vm.$nextTick(); expect(Select.vm.open).toEqual(true)
})
})
expect(Select.find({ref: 'dropdownMenu'}).text()).toEqual('onetwothree'); it('receives an option object to the option slot in the dropdown menu', async () => {
}); const Select = mountDefault(
{ value: 'one' },
{
scopedSlots: {
option: `<span slot="option" slot-scope="option">{{ option.label }}</span>`,
},
}
)
Select.vm.open = true
await Select.vm.$nextTick()
expect(Select.find({ ref: 'dropdownMenu' }).text()).toEqual('onetwothree')
})
it('noOptions slot receives the current search text', async () => { it('noOptions slot receives the current search text', async () => {
const noOptions = jest.fn(); const noOptions = jest.fn()
const Select = mountDefault({}, { const Select = mountDefault(
scopedSlots: {'no-options': noOptions}, {},
}); {
scopedSlots: { 'no-options': noOptions },
}
)
Select.vm.search = 'something not there'; Select.vm.search = 'something not there'
Select.vm.open = true; Select.vm.open = true
await Select.vm.$nextTick(); await Select.vm.$nextTick()
expect(noOptions).toHaveBeenCalledWith({ expect(noOptions).toHaveBeenCalledWith({
loading: false, loading: false,
search: 'something not there', search: 'something not there',
searching: true, searching: true,
}) })
}); })
test('header slot props', async () => { test('header slot props', async () => {
const header = jest.fn(); const header = jest.fn()
const Select = mountDefault({}, { const Select = mountDefault(
scopedSlots: {header: header}, {},
}); {
await Select.vm.$nextTick(); scopedSlots: { header: header },
}
)
await Select.vm.$nextTick()
expect(Object.keys(header.mock.calls[0][0])).toEqual([ expect(Object.keys(header.mock.calls[0][0])).toEqual([
'search', 'loading', 'searching', 'filteredOptions', 'deselect', 'search',
]); 'loading',
}); 'searching',
'filteredOptions',
'deselect',
])
})
test('footer slot props', async () => { test('footer slot props', async () => {
const footer = jest.fn(); const footer = jest.fn()
const Select = mountDefault({}, { const Select = mountDefault(
scopedSlots: {footer: footer}, {},
}); {
await Select.vm.$nextTick(); scopedSlots: { footer: footer },
}
)
await Select.vm.$nextTick()
expect(Object.keys(footer.mock.calls[0][0])).toEqual([ expect(Object.keys(footer.mock.calls[0][0])).toEqual([
'search', 'loading', 'searching', 'filteredOptions', 'deselect', 'search',
]); 'loading',
}); 'searching',
'filteredOptions',
'deselect',
])
})
test('list-header slot props', async () => { test('list-header slot props', async () => {
const header = jest.fn(); const header = jest.fn()
const Select = mountDefault({}, { const Select = mountDefault(
scopedSlots: {'list-header': header}, {},
}); {
Select.vm.open = true; scopedSlots: { 'list-header': header },
await Select.vm.$nextTick(); }
)
Select.vm.open = true
await Select.vm.$nextTick()
expect(Object.keys(header.mock.calls[0][0])).toEqual([ expect(Object.keys(header.mock.calls[0][0])).toEqual([
'search', 'loading', 'searching', 'filteredOptions', 'search',
]); 'loading',
}); 'searching',
'filteredOptions',
])
})
test('list-footer slot props', async () => { test('list-footer slot props', async () => {
const footer = jest.fn(); const footer = jest.fn()
const Select = mountDefault({}, { const Select = mountDefault(
scopedSlots: {'list-footer': footer}, {},
}); {
Select.vm.open = true; scopedSlots: { 'list-footer': footer },
await Select.vm.$nextTick(); }
)
Select.vm.open = true
await Select.vm.$nextTick()
expect(Object.keys(footer.mock.calls[0][0])).toEqual([ expect(Object.keys(footer.mock.calls[0][0])).toEqual([
'search', 'loading', 'searching', 'filteredOptions', 'search',
]); 'loading',
}); 'searching',
}); 'filteredOptions',
])
})
})
+156 -159
View File
@@ -3,261 +3,258 @@ import {
searchSubmit, searchSubmit,
selectTag, selectTag,
selectWithProps, selectWithProps,
} from '../helpers'; } from '../helpers'
import Select from '../../src/components/Select'; import Select from '../../src/components/Select'
describe("When Tagging Is Enabled", () => { describe('When Tagging Is Enabled', () => {
it('can determine if a given option string already exists', () => {
const Select = selectWithProps({ taggable: true, options: ['one', 'two'] })
expect(Select.vm.optionExists('one')).toEqual(true)
expect(Select.vm.optionExists('three')).toEqual(false)
})
it("can determine if a given option string already exists", () => { it('can determine if a given option object already exists', () => {
const Select = selectWithProps({ taggable: true, options: ["one", "two"] });
expect(Select.vm.optionExists("one")).toEqual(true);
expect(Select.vm.optionExists("three")).toEqual(false);
});
it("can determine if a given option object already exists", () => {
const Select = selectWithProps({ const Select = selectWithProps({
taggable: true, taggable: true,
options: [{ label: "one" }, { label: "two" }] options: [{ label: 'one' }, { label: 'two' }],
}); })
expect(Select.vm.optionExists({label: "one"})).toEqual(true); expect(Select.vm.optionExists({ label: 'one' })).toEqual(true)
expect(Select.vm.optionExists({label: "three"})).toEqual(false); expect(Select.vm.optionExists({ label: 'three' })).toEqual(false)
}); })
it("can determine if a given option object already exists when using custom labels", () => { it('can determine if a given option object already exists when using custom labels', () => {
const Select = selectWithProps({ const Select = selectWithProps({
taggable: true, taggable: true,
options: [{ foo: "one" }, { foo: "two" }], options: [{ foo: 'one' }, { foo: 'two' }],
label: "foo" label: 'foo',
}); })
const createOption = (text) => Select.vm.createOption(text); const createOption = (text) => Select.vm.createOption(text)
expect(Select.vm.optionExists(createOption("one"))).toEqual(true); expect(Select.vm.optionExists(createOption('one'))).toEqual(true)
expect(Select.vm.optionExists(createOption("three"))).toEqual(false); expect(Select.vm.optionExists(createOption('three'))).toEqual(false)
}); })
it("can add the current search text as the first item in the options list", async () => { it('can add the current search text as the first item in the options list', async () => {
const Select = selectWithProps({ const Select = selectWithProps({
taggable: true, taggable: true,
multiple: true, multiple: true,
value: ["one"], value: ['one'],
options: ["one", "two"] options: ['one', 'two'],
}); })
Select.vm.search = "three"; Select.vm.search = 'three'
await Select.vm.$nextTick(); await Select.vm.$nextTick()
expect(Select.vm.filteredOptions).toEqual(["three"]); expect(Select.vm.filteredOptions).toEqual(['three'])
}); })
it("can select the current search text as a string", async () => { it('can select the current search text as a string', async () => {
const Select = selectWithProps({ const Select = selectWithProps({
taggable: true, taggable: true,
multiple: true, multiple: true,
options: ["one", "two"] options: ['one', 'two'],
}); })
await selectTag(Select, "three"); await selectTag(Select, 'three')
expect(Select.vm.selectedValue).toEqual(["three"]); expect(Select.vm.selectedValue).toEqual(['three'])
}); })
it("can select the current search text as an object", async () => { it('can select the current search text as an object', async () => {
const Select = selectWithProps({ const Select = selectWithProps({
taggable: true, taggable: true,
multiple: true, multiple: true,
options: [{ label: "one" }] options: [{ label: 'one' }],
}); })
await selectTag(Select, "two"); await selectTag(Select, 'two')
expect(Select.vm.selectedValue).toEqual([ expect(Select.vm.selectedValue).toEqual([{ label: 'two' }])
{ label: "two" } })
]);
});
it("should add a freshly created option/tag to the options list when pushTags is true", async () => { it('should add a freshly created option/tag to the options list when pushTags is true', async () => {
const Select = selectWithProps({ const Select = selectWithProps({
pushTags: true, pushTags: true,
taggable: true, taggable: true,
multiple: true, multiple: true,
value: ["one"], value: ['one'],
options: ["one", "two"] options: ['one', 'two'],
}); })
await selectTag(Select, "three"); await selectTag(Select, 'three')
expect(Select.vm.pushedTags).toEqual(["three"]); expect(Select.vm.pushedTags).toEqual(['three'])
expect(Select.vm.optionList).toEqual(["one", "two", "three"]); expect(Select.vm.optionList).toEqual(['one', 'two', 'three'])
}); })
it("should pushTags even if the consumer has defined a createOption callback", async () => { it('should pushTags even if the consumer has defined a createOption callback', async () => {
const Select = selectWithProps({ const Select = selectWithProps({
pushTags: true, pushTags: true,
taggable: true, taggable: true,
createOption: option => option, createOption: (option) => option,
options: ["one", "two"] options: ['one', 'two'],
}); })
await selectTag(Select, "three"); await selectTag(Select, 'three')
expect(Select.vm.pushedTags).toEqual(["three"]); expect(Select.vm.pushedTags).toEqual(['three'])
expect(Select.vm.optionList).toEqual(["one", "two", "three"]); expect(Select.vm.optionList).toEqual(['one', 'two', 'three'])
}); })
it("should add a freshly created option/tag to the options list when pushTags is true and filterable is false", async () => { it('should add a freshly created option/tag to the options list when pushTags is true and filterable is false', async () => {
const Select = selectWithProps({ const Select = selectWithProps({
filterable: false, filterable: false,
pushTags: true, pushTags: true,
taggable: true, taggable: true,
multiple: true, multiple: true,
value: ["one"], value: ['one'],
options: ["one", "two"] options: ['one', 'two'],
}); })
await selectTag(Select, "three"); await selectTag(Select, 'three')
expect(Select.vm.pushedTags).toEqual(["three"]); expect(Select.vm.pushedTags).toEqual(['three'])
expect(Select.vm.optionList).toEqual(["one", "two", "three"]); expect(Select.vm.optionList).toEqual(['one', 'two', 'three'])
expect(Select.vm.filteredOptions).toEqual(["one", "two", "three"]); expect(Select.vm.filteredOptions).toEqual(['one', 'two', 'three'])
}); })
it("wont add a freshly created option/tag to the options list when pushTags is false", async () => { it('wont add a freshly created option/tag to the options list when pushTags is false', async () => {
const Select = selectWithProps({ const Select = selectWithProps({
pushTags: false, pushTags: false,
taggable: true, taggable: true,
multiple: true, multiple: true,
value: ["one"], value: ['one'],
options: ["one", "two"] options: ['one', 'two'],
}); })
await selectTag(Select, "three"); await selectTag(Select, 'three')
expect(Select.vm.optionList).toEqual(["one", "two"]); expect(Select.vm.optionList).toEqual(['one', 'two'])
}); })
it("wont add a freshly created option/tag to the options list when pushTags is false and filterable is false", async () => { it('wont add a freshly created option/tag to the options list when pushTags is false and filterable is false', async () => {
const Select = selectWithProps({ const Select = selectWithProps({
filterable: false, filterable: false,
pushTags: false, pushTags: false,
taggable: true, taggable: true,
multiple: true, multiple: true,
value: ["one"], value: ['one'],
options: ["one", "two"] options: ['one', 'two'],
}); })
await selectTag(Select, "three"); await selectTag(Select, 'three')
expect(Select.vm.optionList).toEqual(["one", "two"]); expect(Select.vm.optionList).toEqual(['one', 'two'])
expect(Select.vm.filteredOptions).toEqual(["one", "two"]); expect(Select.vm.filteredOptions).toEqual(['one', 'two'])
}); })
it("should select an existing option if the search string matches a string from options", async () => { it('should select an existing option if the search string matches a string from options', async () => {
let two = "two"; let two = 'two'
const Select = selectWithProps({ const Select = selectWithProps({
taggable: true, taggable: true,
multiple: true, multiple: true,
options: ["one", two] options: ['one', two],
}); })
await selectTag(Select, "two"); await selectTag(Select, 'two')
expect(Select.vm.selectedValue).toEqual([two]); expect(Select.vm.selectedValue).toEqual([two])
}); })
it("should select an existing option if the search string matches an objects label from options", async () => { it('should select an existing option if the search string matches an objects label from options', async () => {
let two = { label: "two" }; let two = { label: 'two' }
const Select = selectWithProps({ const Select = selectWithProps({
taggable: true, taggable: true,
options: [{ label: "one" }, two] options: [{ label: 'one' }, two],
}); })
Select.vm.search = "two"; Select.vm.search = 'two'
await Select.vm.$nextTick(); await Select.vm.$nextTick()
searchSubmit(Select); searchSubmit(Select)
expect(Select.vm.selectedValue).toEqual([two]); expect(Select.vm.selectedValue).toEqual([two])
}); })
it("should select an existing option if the search string matches an objects label from options when filter-options is false", async () => { it('should select an existing option if the search string matches an objects label from options when filter-options is false', async () => {
let two = { label: "two" }; let two = { label: 'two' }
const Select = selectWithProps({ const Select = selectWithProps({
taggable: true, taggable: true,
filterable: false, filterable: false,
options: [{ label: "one" }, two] options: [{ label: 'one' }, two],
}); })
Select.vm.search = "two"; Select.vm.search = 'two'
await Select.vm.$nextTick(); await Select.vm.$nextTick()
searchSubmit(Select); searchSubmit(Select)
expect(Select.vm.selectedValue).toEqual([two]); expect(Select.vm.selectedValue).toEqual([two])
}); })
it("should not reset the selected value when the options property changes", () => { it('should not reset the selected value when the options property changes', () => {
const Select = selectWithProps({ const Select = selectWithProps({
taggable: true, taggable: true,
multiple: true, multiple: true,
value: [{ label: "one" }], value: [{ label: 'one' }],
options: [{ label: "one" }] options: [{ label: 'one' }],
}); })
Select.setProps({ options: [{ label: "two" }] }); Select.setProps({ options: [{ label: 'two' }] })
expect(Select.vm.selectedValue).toEqual([{ label: "one" }]); expect(Select.vm.selectedValue).toEqual([{ label: 'one' }])
}); })
it("should not reset the selected value when the options property changes when filterable is false", () => { it('should not reset the selected value when the options property changes when filterable is false', () => {
const Select = selectWithProps({ const Select = selectWithProps({
taggable: true, taggable: true,
multiple: true, multiple: true,
filterable: false, filterable: false,
value: [{ label: "one" }], value: [{ label: 'one' }],
options: [{ label: "one" }] options: [{ label: 'one' }],
}); })
Select.setProps({ options: [{ label: "two" }] }); Select.setProps({ options: [{ label: 'two' }] })
expect(Select.vm.selectedValue).toEqual([{ label: "one" }]); expect(Select.vm.selectedValue).toEqual([{ label: 'one' }])
}); })
it("should not allow duplicate tags when using string options", async () => { it('should not allow duplicate tags when using string options', async () => {
const Select = selectWithProps({
taggable: true,
multiple: true
});
await selectTag(Select, "one");
expect(Select.vm.selectedValue).toEqual(["one"]);
expect(Select.vm.search).toEqual("");
await selectTag(Select, "one");
expect(Select.vm.selectedValue).toEqual(["one"]);
expect(Select.vm.search).toEqual("");
});
it("should not allow duplicate tags when using object options", async () => {
const Select = selectWithProps({ const Select = selectWithProps({
taggable: true, taggable: true,
multiple: true, multiple: true,
options: [{ label: "two" }] })
});
const spy = jest.spyOn(Select.vm, 'select');
await selectTag(Select, "one"); await selectTag(Select, 'one')
expect(Select.vm.selectedValue).toEqual([{ label: "one" }]); expect(Select.vm.selectedValue).toEqual(['one'])
expect(spy).lastCalledWith({label: 'one'}); expect(Select.vm.search).toEqual('')
expect(Select.vm.search).toEqual("");
await selectTag(Select, "one"); await selectTag(Select, 'one')
expect(Select.vm.selectedValue).toEqual([{ label: "one" }]); expect(Select.vm.selectedValue).toEqual(['one'])
expect(Select.vm.search).toEqual(""); expect(Select.vm.search).toEqual('')
}); })
it("will select an existing option on tab", async () => { it('should not allow duplicate tags when using object options', async () => {
const Select = selectWithProps({
taggable: true,
multiple: true,
options: [{ label: 'two' }],
})
const spy = jest.spyOn(Select.vm, 'select')
await selectTag(Select, 'one')
expect(Select.vm.selectedValue).toEqual([{ label: 'one' }])
expect(spy).lastCalledWith({ label: 'one' })
expect(Select.vm.search).toEqual('')
await selectTag(Select, 'one')
expect(Select.vm.selectedValue).toEqual([{ label: 'one' }])
expect(Select.vm.search).toEqual('')
})
it('will select an existing option on tab', async () => {
const Select = mountDefault({ const Select = mountDefault({
taggable: true, taggable: true,
selectOnTab: true selectOnTab: true,
}); })
Select.vm.typeAheadPointer = 0; Select.vm.typeAheadPointer = 0
Select.find({ ref: "search" }).trigger("keydown.tab"); Select.find({ ref: 'search' }).trigger('keydown.tab')
await Select.vm.$nextTick(); await Select.vm.$nextTick()
expect(Select.vm.selectedValue).toEqual(['one']); expect(Select.vm.selectedValue).toEqual(['one'])
}) })
}); })
+33 -33
View File
@@ -1,47 +1,47 @@
import { shallowMount } from "@vue/test-utils"; import { shallowMount } from '@vue/test-utils'
import VueSelect from "../../src/components/Select"; import VueSelect from '../../src/components/Select'
import { mountDefault, mountWithoutTestUtils } from "../helpers"; import { mountDefault, mountWithoutTestUtils } from '../helpers'
import typeAheadMixin from "../../src/mixins/typeAheadPointer"; import typeAheadMixin from '../../src/mixins/typeAheadPointer'
import Vue from "vue"; import Vue from 'vue'
describe("Moving the Typeahead Pointer", () => { describe('Moving the Typeahead Pointer', () => {
it("should set the pointer to zero when the filteredOptions watcher is called", async () => { it('should set the pointer to zero when the filteredOptions watcher is called', async () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { options: ["one", "two", "three"] }, propsData: { options: ['one', 'two', 'three'] },
sync: false sync: false,
}); })
Select.vm.search = "one"; Select.vm.search = 'one'
await Select.vm.$nextTick(); await Select.vm.$nextTick()
expect(Select.vm.typeAheadPointer).toEqual(0); expect(Select.vm.typeAheadPointer).toEqual(0)
}); })
it("should move the pointer visually up the list on up arrow keyUp", () => { it('should move the pointer visually up the list on up arrow keyUp', () => {
const Select = mountDefault(); const Select = mountDefault()
Select.vm.typeAheadPointer = 1; Select.vm.typeAheadPointer = 1
Select.find({ ref: "search" }).trigger("keydown.up"); Select.find({ ref: 'search' }).trigger('keydown.up')
expect(Select.vm.typeAheadPointer).toEqual(0); expect(Select.vm.typeAheadPointer).toEqual(0)
}); })
it("should move the pointer visually down the list on down arrow keyUp", () => { it('should move the pointer visually down the list on down arrow keyUp', () => {
const Select = mountDefault(); const Select = mountDefault()
Select.vm.typeAheadPointer = 1; Select.vm.typeAheadPointer = 1
Select.find({ ref: "search" }).trigger("keydown.down"); Select.find({ ref: 'search' }).trigger('keydown.down')
expect(Select.vm.typeAheadPointer).toEqual(2); expect(Select.vm.typeAheadPointer).toEqual(2)
}); })
it("should not move the pointer past the end of the list", () => { it('should not move the pointer past the end of the list', () => {
const Select = mountDefault(); const Select = mountDefault()
Select.vm.typeAheadPointer = 2; Select.vm.typeAheadPointer = 2
Select.vm.typeAheadDown(); Select.vm.typeAheadDown()
expect(Select.vm.typeAheadPointer).toEqual(2); expect(Select.vm.typeAheadPointer).toEqual(2)
}); })
}); })
+9 -8
View File
@@ -1,14 +1,15 @@
import sortAndStringify from '../../../src/utility/sortAndStringify'; import sortAndStringify from '../../../src/utility/sortAndStringify'
test('it will stringify an object', () => { test('it will stringify an object', () => {
expect(sortAndStringify({hello: 'world'})).toEqual('{"hello":"world"}'); expect(sortAndStringify({ hello: 'world' })).toEqual('{"hello":"world"}')
}); })
test('it will sort attributes alphabetically', () => { test('it will sort attributes alphabetically', () => {
expect(sortAndStringify({b: 'b', a: 'a'})).toEqual('{"a":"a","b":"b"}'); expect(sortAndStringify({ b: 'b', a: 'a' })).toEqual('{"a":"a","b":"b"}')
}); })
test('comparing two objects with unsorted keys', () => { test('comparing two objects with unsorted keys', () => {
expect(sortAndStringify({b: 'b', a: 'a'})) expect(sortAndStringify({ b: 'b', a: 'a' })).toEqual(
.toEqual(sortAndStringify({a: 'a', b: 'b'})) sortAndStringify({ a: 'a', b: 'b' })
}); )
})
+3 -3
View File
@@ -1,5 +1,5 @@
import uniqueId from '../../../src/utility/uniqueId'; import uniqueId from '../../../src/utility/uniqueId'
test('it generates a unique number', () => { test('it generates a unique number', () => {
expect(uniqueId()).not.toEqual(uniqueId()); expect(uniqueId()).not.toEqual(uniqueId())
}); })