2
0
mirror of https://github.com/tenrok/vue-select.git synced 2026-06-22 10:30:34 +03:00

feat: Vue 3 Support (#1344)

BREAKING CHANGE: drop vue 2 support
This commit is contained in:
Jeff Sagal
2021-10-19 18:53:22 -07:00
committed by GitHub
parent e8d7abbf33
commit 06177a4d24
29 changed files with 774 additions and 561 deletions
+2
View File
@@ -3,6 +3,8 @@ on:
push: push:
branches: branches:
- master - master
- next
- beta
jobs: jobs:
release: release:
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04
+3 -1
View File
@@ -25,7 +25,7 @@ module.exports = {
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.runtime.esm-bundler.js',
}, },
}, },
module: { module: {
@@ -54,6 +54,8 @@ module.exports = {
plugins: [ plugins: [
new webpack.DefinePlugin({ new webpack.DefinePlugin({
'process.env': env, 'process.env': env,
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false,
}), }),
new MiniCssExtractPlugin({ new MiniCssExtractPlugin({
filename: 'vue-select.css', filename: 'vue-select.css',
+3
View File
@@ -10,6 +10,9 @@ module.exports = merge(baseWebpackConfig, {
libraryTarget: 'umd', libraryTarget: 'umd',
globalObject: "typeof self !== 'undefined' ? self : this", globalObject: "typeof self !== 'undefined' ? self : this",
}, },
externals: {
vue: 'vue',
},
optimization: { optimization: {
minimizer: [ minimizer: [
new TerserPlugin({ new TerserPlugin({
+4 -6
View File
@@ -1,8 +1,6 @@
import Vue from 'vue' import { createApp, h } from 'vue'
import Dev from './Dev.vue' import Dev from './Dev.vue'
Vue.config.productionTip = false createApp({
render: () => h(Dev),
new Vue({ }).mount('#app')
render: (h) => h(Dev),
}).$mount('#app')
+5 -1
View File
@@ -1,5 +1,9 @@
<template> <template>
<v-select :options="paginated" :filterable="false" @search="onSearch"> <v-select
:options="paginated"
:filterable="false"
@search="(query) => (search = query)"
>
<li slot="list-footer" class="pagination"> <li slot="list-footer" class="pagination">
<button :disabled="!hasPrevPage" @click="offset -= limit">Prev</button> <button :disabled="!hasPrevPage" @click="offset -= limit">Prev</button>
<button :disabled="!hasNextPage" @click="offset += limit">Next</button> <button :disabled="!hasNextPage" @click="offset += limit">Next</button>
+4 -4
View File
@@ -188,11 +188,11 @@
v-bind="configuration" v-bind="configuration"
placeholder="country objects, using option scoped slots" placeholder="country objects, using option scoped slots"
> >
<template slot="selected-option" slot-scope="option"> <template slot="selected-option" slot-scope="{ label, value }">
{{ option.label }} -- {{ option.value }} {{ label }} -- {{ value }}
</template> </template>
<template slot="option" slot-scope="option"> <template slot="option" slot-scope="{ label, value }">
{{ option.label }} ({{ option.value }}) {{ label }} ({{ value }})
</template> </template>
</v-select> </v-select>
</div> </div>
+7 -7
View File
@@ -29,7 +29,7 @@
"url": "https://github.com/sagalbot/vue-select.git" "url": "https://github.com/sagalbot/vue-select.git"
}, },
"peerDependencies": { "peerDependencies": {
"vue": "2.x" "vue": "3.x"
}, },
"resolutions": { "resolutions": {
"ajv": "6.8.1" "ajv": "6.8.1"
@@ -42,7 +42,8 @@
"@babel/runtime": "^7.4.2", "@babel/runtime": "^7.4.2",
"@semantic-release/git": "^9.0.0", "@semantic-release/git": "^9.0.0",
"@semantic-release/github": "^7.0.4", "@semantic-release/github": "^7.0.4",
"@vue/test-utils": "^1.2.2", "@vue/compiler-sfc": "^3.2.20",
"@vue/test-utils": "^2.0.0-rc.16",
"autoprefixer": "^9.4.7", "autoprefixer": "^9.4.7",
"babel-core": "^7.0.0-bridge.0", "babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^10.0.3", "babel-eslint": "^10.0.3",
@@ -74,13 +75,12 @@
"semantic-release": "^17.0.4", "semantic-release": "^17.0.4",
"terser-webpack-plugin": "^1.2.3", "terser-webpack-plugin": "^1.2.3",
"url-loader": "^1.1.2", "url-loader": "^1.1.2",
"vue": "^2.6.10", "vue": "^3.2.20",
"vue-html-loader": "^1.2.4", "vue-html-loader": "^1.2.4",
"vue-jest": "^3.0.4", "vue-jest": "5.0.0-alpha.8",
"vue-loader": "^15.7.0", "vue-loader": "^16.8.1",
"vue-server-renderer": "^2.6.10", "vue-server-renderer": "^2.6.10",
"vue-style-loader": "^4.1.2", "vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.10",
"webpack": "^4.29.6", "webpack": "^4.29.6",
"webpack-cli": "^3.3.0", "webpack-cli": "^3.3.0",
"webpack-dev-server": "^3.2.1", "webpack-dev-server": "^3.2.1",
@@ -129,7 +129,7 @@
{ {
"path": "./dist/vue-select.js", "path": "./dist/vue-select.js",
"compression": "none", "compression": "none",
"maxSize": "21 KB" "maxSize": "23 KB"
}, },
{ {
"path": "./dist/vue-select.css", "path": "./dist/vue-select.css",
+1 -3
View File
@@ -1,7 +1,5 @@
module.exports = { module.exports = {
release: { branches: ['master', 'next', { name: 'beta', prerelease: true }],
branch: 'master',
},
plugins: [ plugins: [
'@semantic-release/npm', '@semantic-release/npm',
'@semantic-release/commit-analyzer', '@semantic-release/commit-analyzer',
+35 -16
View File
@@ -154,15 +154,33 @@ export default {
mixins: [pointerScroll, typeAheadPointer, ajax], mixins: [pointerScroll, typeAheadPointer, ajax],
emits: [
'open',
'close',
'update:modelValue',
'search',
'search:compositionstart',
'search:compositionend',
'search:keydown',
'search:blur',
'search:focus',
'search:input',
'option:created',
'option:selecting',
'option:selected',
'option:deselecting',
'option:deselected',
],
props: { props: {
/** /**
* Contains the currently selected value. Very similar to a * Contains the currently selected value. Very similar to a
* `value` attribute on an <input>. You can listen for changes * `value` attribute on an <input>. You can listen for changes
* with the 'input' event. * with the 'input' event.
* @type {Object||String||null} * @type {Object|String|Array|null}
*/ */
// eslint-disable-next-line vue/require-default-prop,vue/require-prop-types // eslint-disable-next-line vue/require-default-prop,vue/require-prop-types
value: {}, modelValue: {},
/** /**
* An object with any custom components that you'd like to overwrite * An object with any custom components that you'd like to overwrite
@@ -679,16 +697,17 @@ export default {
}, },
computed: { computed: {
isReducingValues() {
return this.$props.reduce !== this.$options.props.reduce.default
},
/** /**
* Determine if the component needs to * Determine if the component needs to
* track the state of values internally. * track the state of values internally.
* @return {boolean} * @return {boolean}
*/ */
isTrackingValues() { isTrackingValues() {
return ( return typeof this.modelValue === 'undefined' || this.isReducingValues
typeof this.value === 'undefined' ||
this.$options.propsData.hasOwnProperty('reduce')
)
}, },
/** /**
@@ -696,7 +715,7 @@ export default {
* @return {Array} * @return {Array}
*/ */
selectedValue() { selectedValue() {
let value = this.value let value = this.modelValue
if (this.isTrackingValues) { if (this.isTrackingValues) {
// Vue select has to manage value internally // Vue select has to manage value internally
value = this.$data._value value = this.$data._value
@@ -725,7 +744,7 @@ export default {
* @returns {HTMLInputElement} * @returns {HTMLInputElement}
*/ */
searchEl() { searchEl() {
return !!this.$scopedSlots['search'] return !!this.$slots['search']
? this.$refs.selectedOptions.querySelector( ? this.$refs.selectedOptions.querySelector(
this.searchInputQuerySelector this.searchInputQuerySelector
) )
@@ -733,7 +752,7 @@ export default {
}, },
/** /**
* The object to be bound to the $slots.search scoped slot. * The object to be bound to the $slots.search slot.
* @returns {Object} * @returns {Object}
*/ */
scope() { scope() {
@@ -923,8 +942,8 @@ export default {
this.clearSelection() this.clearSelection()
} }
if (this.value && this.isTrackingValues) { if (this.modelValue && this.isTrackingValues) {
this.setInternalValueFromOptions(this.value) this.setInternalValueFromOptions(this.modelValue)
} }
}, },
@@ -932,7 +951,7 @@ export default {
* Make sure to update internal * Make sure to update internal
* value if prop changes outside * value if prop changes outside
*/ */
value: { modelValue: {
immediate: true, immediate: true,
handler(val) { handler(val) {
if (this.isTrackingValues) { if (this.isTrackingValues) {
@@ -957,8 +976,6 @@ export default {
created() { created() {
this.mutableLoading = this.loading this.mutableLoading = this.loading
this.$on('option:created', this.pushTag)
}, },
methods: { methods: {
@@ -988,7 +1005,9 @@ export default {
this.$emit('option:selecting', option) this.$emit('option:selecting', option)
if (!this.isOptionSelected(option)) { if (!this.isOptionSelected(option)) {
if (this.taggable && !this.optionExists(option)) { if (this.taggable && !this.optionExists(option)) {
/* @TODO: could we use v-model instead of push-tags? */
this.$emit('option:created', option) this.$emit('option:created', option)
this.pushTag(option)
} }
if (this.multiple) { if (this.multiple) {
option = this.selectedValue.concat(option) option = this.selectedValue.concat(option)
@@ -1052,7 +1071,7 @@ export default {
* @param value * @param value
*/ */
updateValue(value) { updateValue(value) {
if (typeof this.value === 'undefined') { if (typeof this.modelValue === 'undefined') {
// Vue select has to manage value // Vue select has to manage value
this.$data._value = value this.$data._value = value
} }
@@ -1065,7 +1084,7 @@ export default {
} }
} }
this.$emit('input', value) this.$emit('update:modelValue', value)
}, },
/** /**
+6 -8
View File
@@ -1,17 +1,15 @@
export default { export default {
inserted(el, bindings, { context }) { mounted(el, { instance }) {
if (context.appendToBody) { if (instance.appendToBody) {
const { const {
height, height,
top, top,
left, left,
width, width,
} = context.$refs.toggle.getBoundingClientRect() } = instance.$refs.toggle.getBoundingClientRect()
let scrollX = window.scrollX || window.pageXOffset let scrollX = window.scrollX || window.pageXOffset
let scrollY = window.scrollY || window.pageYOffset let scrollY = window.scrollY || window.pageYOffset
el.unbindPosition = instance.calculatePosition(el, instance, {
el.unbindPosition = context.calculatePosition(el, context, {
width: width + 'px', width: width + 'px',
left: scrollX + left + 'px', left: scrollX + left + 'px',
top: scrollY + top + height + 'px', top: scrollY + top + height + 'px',
@@ -21,8 +19,8 @@ export default {
} }
}, },
unbind(el, bindings, { context }) { unmounted(el, { instance }) {
if (context.appendToBody) { if (instance.appendToBody) {
if (el.unbindPosition && typeof el.unbindPosition === 'function') { if (el.unbindPosition && typeof el.unbindPosition === 'function') {
el.unbindPosition() el.unbindPosition()
} }
+9 -10
View File
@@ -13,7 +13,7 @@ export const searchSubmit = (Wrapper, searchText = false) => {
if (searchText) { if (searchText) {
Wrapper.vm.search = searchText Wrapper.vm.search = searchText
} }
Wrapper.findComponent({ ref: 'search' }).trigger('keydown.enter') Wrapper.get('input').trigger('keydown.enter')
} }
/** /**
@@ -29,18 +29,17 @@ export const selectTag = async (Wrapper, searchText) => {
Wrapper.vm.search = searchText Wrapper.vm.search = searchText
await Wrapper.vm.$nextTick() await Wrapper.vm.$nextTick()
Wrapper.findComponent({ ref: 'search' }).trigger('keydown.enter') await Wrapper.get('input').trigger('keydown.enter')
await Wrapper.vm.$nextTick()
} }
/** /**
* Create a new VueSelect instance with * Create a new VueSelect instance with
* a provided set of props. * a provided set of props.
* @param propsData * @param props
* @returns {Wrapper<Vue>} * @returns {Wrapper<Vue>}
*/ */
export const selectWithProps = (propsData = {}) => { export const selectWithProps = (props = {}) => {
return shallowMount(VueSelect, { propsData }) return shallowMount(VueSelect, { props })
} }
/** /**
@@ -51,7 +50,7 @@ export const selectWithProps = (propsData = {}) => {
*/ */
export const mountDefault = (props = {}, options = {}) => { export const mountDefault = (props = {}, options = {}) => {
return shallowMount(VueSelect, { return shallowMount(VueSelect, {
propsData: { props: {
options: ['one', 'two', 'three'], options: ['one', 'two', 'three'],
...props, ...props,
}, },
@@ -66,13 +65,13 @@ export const mountDefault = (props = {}, options = {}) => {
* @return {Vue | Element | Vue[] | Element[]} * @return {Vue | Element | Vue[] | Element[]}
*/ */
export const mountWithoutTestUtils = (props = {}, options = {}) => { export const mountWithoutTestUtils = (props = {}, options = {}) => {
return new Vue({ return createApp({
components: { VueSelect },
render: (createEl) => render: (createEl) =>
createEl('vue-select', { createEl('vue-select', {
ref: 'select', ref: 'select',
props: { options: ['one', 'two', 'three'], ...props }, props: { options: ['one', 'two', 'three'], ...props },
...options, ...options,
}), }),
}).$mount().$refs.select components: { VueSelect },
}).mount().$refs.select
} }
+2 -2
View File
@@ -41,8 +41,8 @@ describe('Asynchronous Loading', () => {
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: { props: {
search: (search, loading) => { onSearch: (search, loading) => {
loading(false) loading(false)
}, },
}, },
+12 -8
View File
@@ -1,15 +1,20 @@
import pointerScroll from '../../src/mixins/pointerScroll'
import { mountDefault } from '../helpers' import { mountDefault } from '../helpers'
describe('Automatic Scrolling', () => { describe('Automatic Scrolling', () => {
let spy
afterEach(() => {
if (spy) spy.mockClear()
})
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
spy = jest.spyOn(pointerScroll.methods, 'maybeAdjustScroll')
const Select = mountDefault() const Select = mountDefault()
const spy = jest.spyOn(Select.vm, 'maybeAdjustScroll')
Select.vm.typeAheadPointer = 1 Select.vm.typeAheadPointer = 1
// When // When
Select.findComponent({ ref: 'search' }).trigger('keydown.up') await Select.get('input').trigger('keydown.up')
await Select.vm.$nextTick()
// Then // Then
expect(spy).toHaveBeenCalled() expect(spy).toHaveBeenCalled()
@@ -17,13 +22,12 @@ describe('Automatic Scrolling', () => {
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
spy = jest.spyOn(pointerScroll.methods, 'maybeAdjustScroll')
const Select = mountDefault() const Select = mountDefault()
const spy = jest.spyOn(Select.vm, 'maybeAdjustScroll')
Select.vm.typeAheadPointer = 1 Select.vm.typeAheadPointer = 1
// When // When
Select.findComponent({ ref: 'search' }).trigger('keydown.down') await Select.get('input').trigger('keydown.down')
await Select.vm.$nextTick()
// Then // Then
expect(spy).toHaveBeenCalled() expect(spy).toHaveBeenCalled()
@@ -31,8 +35,8 @@ describe('Automatic Scrolling', () => {
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
spy = jest.spyOn(pointerScroll.methods, 'maybeAdjustScroll')
const Select = mountDefault() const Select = mountDefault()
const spy = jest.spyOn(Select.vm, 'maybeAdjustScroll')
Select.vm.typeAheadPointer = 1 Select.vm.typeAheadPointer = 1
// When // When
@@ -45,10 +49,10 @@ describe('Automatic Scrolling', () => {
it('should not adjust scroll position when autoscroll is false', async () => { it('should not adjust scroll position when autoscroll is false', async () => {
// Given // Given
spy = jest.spyOn(pointerScroll.methods, 'maybeAdjustScroll')
const Select = mountDefault({ const Select = mountDefault({
autoscroll: false, autoscroll: false,
}) })
const spy = jest.spyOn(Select.vm, 'maybeAdjustScroll')
Select.vm.typeAheadPointer = 1 Select.vm.typeAheadPointer = 1
// When // When
+5 -5
View File
@@ -1,9 +1,9 @@
import Vue from 'vue' import { defineComponent } 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 = defineComponent('Deselect', {
render(createElement) { render(createElement) {
return createElement('button', 'remove') return createElement('button', 'remove')
}, },
@@ -11,11 +11,11 @@ describe('Components API', () => {
const Select = selectWithProps({ components: { Deselect } }) const Select = selectWithProps({ components: { Deselect } })
expect(Select.findComponent(Deselect)).toBeTruthy() expect(Select.find(Deselect)).toBeTruthy()
}) })
it('swap the OpenIndicator component', () => { it('swap the OpenIndicator component', () => {
const OpenIndicator = Vue.component('OpenIndicator', { const OpenIndicator = defineComponent('OpenIndicator', {
render(createElement) { render(createElement) {
return createElement('i', '^') return createElement('i', '^')
}, },
@@ -23,6 +23,6 @@ describe('Components API', () => {
const Select = selectWithProps({ components: { OpenIndicator } }) const Select = selectWithProps({ components: { OpenIndicator } })
expect(Select.findComponent(OpenIndicator)).toBeTruthy() expect(Select.find(OpenIndicator)).toBeTruthy()
}) })
}) })
+1 -1
View File
@@ -25,6 +25,6 @@ describe('CreateOption When Tagging Is Enabled', () => {
await selectTag(Select, 'two') await selectTag(Select, 'two')
expect(Select.emitted('input')[0]).toEqual([{ name: 'two' }]) expect(Select.emitted('update:modelValue')[0]).toEqual([{ name: 'two' }])
}) })
}) })
+19 -20
View File
@@ -7,13 +7,13 @@ describe('Removing values', () => {
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()['update:modelValue']).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'], modelValue: ['one'],
options: ['one', 'two', 'three'], options: ['one', 'two', 'three'],
multiple: true, multiple: true,
disabled: true, disabled: true,
@@ -33,7 +33,7 @@ describe('Removing values', () => {
Select.find('.vs__search').trigger('keydown.backspace') Select.find('.vs__search').trigger('keydown.backspace')
expect(Select.emitted().input).toEqual([[['one']]]) expect(Select.emitted()['update:modelValue']).toEqual([[['one']]])
expect(Select.vm.selectedValue).toEqual(['one']) expect(Select.vm.selectedValue).toEqual(['one'])
}) })
@@ -48,21 +48,20 @@ describe('Removing values', () => {
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 update:modelValue 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.get('input').trigger('keydown.backspace')
expect(Select.emitted()['update:modelValue'].length).toBe(1)
Select.findComponent({ ref: 'search' }).trigger('keydown.backspace') Select.get('input').trigger('keydown.backspace')
expect(Select.emitted().input.length).toBe(1) Select.get('input').trigger('keydown.backspace')
expect(Select.emitted()['update:modelValue'].length).toBe(1)
Select.findComponent({ ref: 'search' }).trigger('keydown.backspace')
Select.findComponent({ ref: 'search' }).trigger('keydown.backspace')
expect(Select.emitted().input.length).toBe(1)
}) })
it('should deselect a selected option when clicked and deselectFromDropdown is true', async () => { it('should deselect a selected option when clicked and deselectFromDropdown is true', async () => {
const Select = selectWithProps({ const Select = selectWithProps({
value: 'one', modelValue: 'one',
options: ['one', 'two', 'three'], options: ['one', 'two', 'three'],
deselectFromDropdown: true, deselectFromDropdown: true,
}) })
@@ -79,7 +78,7 @@ describe('Removing values', () => {
it('should not deselect a selected option when clicked if clearable is false', async () => { it('should not deselect a selected option when clicked if clearable is false', async () => {
const Select = selectWithProps({ const Select = selectWithProps({
value: 'one', modelValue: 'one',
options: ['one', 'two', 'three'], options: ['one', 'two', 'three'],
clearable: false, clearable: false,
deselectFromDropdown: true, deselectFromDropdown: true,
@@ -97,7 +96,7 @@ describe('Removing values', () => {
it('should not deselect a selected option when clicked if deselectFromDropdown is false', async () => { it('should not deselect a selected option when clicked if deselectFromDropdown is false', async () => {
const Select = selectWithProps({ const Select = selectWithProps({
value: 'one', modelValue: 'one',
options: ['one', 'two', 'three'], options: ['one', 'two', 'three'],
deselectFromDropdown: false, deselectFromDropdown: false,
}) })
@@ -116,7 +115,7 @@ describe('Removing values', () => {
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', modelValue: 'foo',
}) })
expect(Select.vm.showClearButton).toEqual(true) expect(Select.vm.showClearButton).toEqual(true)
@@ -125,7 +124,7 @@ describe('Removing values', () => {
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', modelValue: 'foo',
multiple: true, multiple: true,
}) })
@@ -141,20 +140,20 @@ describe('Removing values', () => {
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()['update:modelValue']).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', modelValue: 'foo',
disabled: true, disabled: true,
}) })
expect(Select.find('button.vs__clear').attributes().disabled).toEqual( expect(
'disabled' Select.find('button.vs__clear').attributes().disabled
) ).toBeDefined()
}) })
}) })
}) })
+18 -12
View File
@@ -1,5 +1,6 @@
import { selectWithProps } from '../helpers' import { selectWithProps } from '../helpers'
import OpenIndicator from '../../src/components/OpenIndicator' import OpenIndicator from '../../src/components/OpenIndicator'
import VueSelect from '../../src/components/Select'
const preventDefault = jest.fn() const preventDefault = jest.fn()
@@ -8,6 +9,11 @@ function clickEvent(currentTarget) {
} }
describe('Toggling Dropdown', () => { describe('Toggling Dropdown', () => {
let spy
afterEach(() => {
if (spy) spy.mockClear()
})
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))
@@ -16,7 +22,7 @@ describe('Toggling Dropdown', () => {
it('should open the dropdown when the el is clicked', () => { it('should open the dropdown when the el is clicked', () => {
const Select = selectWithProps({ const Select = selectWithProps({
value: [{ label: 'one' }], modelValue: [{ label: 'one' }],
options: [{ label: 'one' }], options: [{ label: 'one' }],
}) })
@@ -26,7 +32,7 @@ describe('Toggling Dropdown', () => {
it('should not close the dropdown when the el is clicked and enableMouseInputSearch is set to true', () => { it('should not close the dropdown when the el is clicked and enableMouseInputSearch is set to true', () => {
const Select = selectWithProps({ const Select = selectWithProps({
value: [{ label: 'one' }], modelValue: [{ label: 'one' }],
options: [{ label: 'one' }], options: [{ label: 'one' }],
enableMouseSearchInput: true, enableMouseSearchInput: true,
}) })
@@ -39,7 +45,7 @@ describe('Toggling Dropdown', () => {
it('should open the dropdown when the selected tag is clicked', () => { it('should open the dropdown when the selected tag is clicked', () => {
const Select = selectWithProps({ const Select = selectWithProps({
value: [{ label: 'one' }], modelValue: [{ label: 'one' }],
options: [{ label: 'one' }], options: [{ label: 'one' }],
}) })
@@ -61,7 +67,7 @@ describe('Toggling Dropdown', () => {
it('closes the dropdown when an option is selected, multiple is true, and closeOnSelect option is true', () => { it('closes the dropdown when an option is selected, multiple is true, and closeOnSelect option is true', () => {
const Select = selectWithProps({ const Select = selectWithProps({
value: [], modelValue: [],
options: ['one', 'two', 'three'], options: ['one', 'two', 'three'],
multiple: true, multiple: true,
}) })
@@ -74,7 +80,7 @@ describe('Toggling Dropdown', () => {
it('does not close the dropdown when the el is clicked, multiple is true, and closeOnSelect option is false', () => { 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({
value: [], modelValue: [],
options: ['one', 'two', 'three'], options: ['one', 'two', 'three'],
multiple: true, multiple: true,
closeOnSelect: false, closeOnSelect: false,
@@ -86,36 +92,36 @@ describe('Toggling Dropdown', () => {
expect(Select.vm.open).toEqual(true) expect(Select.vm.open).toEqual(true)
}) })
it('should close the dropdown on search blur', () => { it('should close the dropdown on search blur', async () => {
const Select = selectWithProps({ const Select = selectWithProps({
options: [{ label: 'one' }], options: [{ label: 'one' }],
}) })
Select.vm.open = true Select.vm.open = true
Select.findComponent({ ref: 'search' }).trigger('blur') await Select.get('input').trigger('blur')
expect(Select.vm.open).toEqual(false) expect(Select.vm.open).toEqual(false)
}) })
it('will close the dropdown and emit the search:blur event from onSearchBlur', () => { it('will close the dropdown and emit the search:blur event from onSearchBlur', () => {
spy = jest.spyOn(VueSelect.methods, 'onSearchBlur')
const Select = selectWithProps() const Select = selectWithProps()
const spy = jest.spyOn(Select.vm, '$emit')
Select.vm.open = true Select.vm.open = true
Select.vm.onSearchBlur() Select.vm.onSearchBlur()
expect(Select.vm.open).toEqual(false) expect(Select.vm.open).toEqual(false)
expect(spy).toHaveBeenCalledWith('search:blur') expect(spy).toHaveBeenCalled()
}) })
it('will open the dropdown and emit the search:focus event from onSearchFocus', () => { it('will open the dropdown and emit the search:focus event from onSearchFocus', () => {
spy = jest.spyOn(VueSelect.methods, 'onSearchFocus')
const Select = selectWithProps() const Select = selectWithProps()
const spy = jest.spyOn(Select.vm, '$emit')
Select.vm.onSearchFocus() Select.vm.onSearchFocus()
expect(Select.vm.open).toEqual(true) expect(Select.vm.open).toEqual(true)
expect(spy).toHaveBeenCalledWith('search:focus') expect(spy).toHaveBeenCalled()
}) })
it('will close the dropdown on escape, if search is empty', () => { it('will close the dropdown on escape, if search is empty', () => {
@@ -130,7 +136,7 @@ describe('Toggling Dropdown', () => {
it('should remove existing search text on escape keydown', () => { it('should remove existing search text on escape keydown', () => {
const Select = selectWithProps({ const Select = selectWithProps({
value: [{ label: 'one' }], modelValue: [{ label: 'one' }],
options: [{ label: 'one' }], options: [{ label: 'one' }],
}) })
+8 -8
View File
@@ -4,7 +4,7 @@ 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'] }, props: { options: ['foo', 'bar', 'baz'] },
}) })
const input = Select.find('.vs__search') const input = Select.find('.vs__search')
@@ -15,7 +15,7 @@ describe('Filtering Options', () => {
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'] }, props: { 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'])
@@ -23,7 +23,7 @@ describe('Filtering Options', () => {
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 }, props: { 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'])
@@ -31,7 +31,7 @@ describe('Filtering Options', () => {
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'] }, props: { 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'])
@@ -39,7 +39,7 @@ describe('Filtering Options', () => {
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: { props: {
options: [{ label: 'Foo' }, { label: 'Bar' }, { label: 'Baz' }], options: [{ label: 'Foo' }, { label: 'Bar' }, { label: 'Baz' }],
}, },
}) })
@@ -52,7 +52,7 @@ describe('Filtering Options', () => {
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: { props: {
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')),
@@ -65,7 +65,7 @@ describe('Filtering Options', () => {
it('can use a custom filtering method', () => { it('can use a custom filtering method', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { props: {
options: ['foo', 'bar', 'baz'], options: ['foo', 'bar', 'baz'],
filterBy: (option, label) => label.includes('o'), filterBy: (option, label) => label.includes('o'),
}, },
@@ -76,7 +76,7 @@ describe('Filtering Options', () => {
it('can filter arrays of numbers', () => { it('can filter arrays of numbers', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { props: {
options: [1, 5, 10], options: [1, 5, 10],
}, },
}) })
+30 -21
View File
@@ -1,69 +1,78 @@
import { DOMWrapper } from '@vue/test-utils'
import typeAheadPointer from '../../src/mixins/typeAheadPointer'
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', () => { let spy
afterEach(() => {
if (spy) spy.mockClear()
})
it('can use the map-keydown prop to trigger custom behaviour', async () => {
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.findComponent({ ref: 'search' }).trigger('keydown.space') await Select.get('input').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', () => {
spy = jest.spyOn(typeAheadPointer.methods, 'typeAheadSelect')
const Select = mountDefault({ const Select = mountDefault({
selectOnKeyCodes: [32], selectOnKeyCodes: [32],
}) })
const spy = jest.spyOn(Select.vm, 'typeAheadSelect') Select.get('input').trigger('keydown.space')
Select.findComponent({ 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', () => {
spy = jest.spyOn(typeAheadPointer.methods, 'typeAheadSelect')
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') Select.get('input').trigger('keydown.space')
Select.findComponent({ ref: 'search' }).trigger('keydown.space')
expect(onKeyDown.mock.calls.length).toBe(1) expect(onKeyDown.mock.calls.length).toBe(1)
Select.findComponent({ ref: 'search' }).trigger('keydown.tab') Select.get('input').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() spy = jest.spyOn(typeAheadPointer.methods, 'typeAheadSelect')
const spy = jest.spyOn(Select.vm, 'typeAheadSelect')
Select.findComponent({ ref: 'search' }).trigger('compositionstart') const Select = mountDefault()
Select.findComponent({ ref: 'search' }).trigger('keydown.enter')
Select.get('input').trigger('compositionstart')
Select.get('input').trigger('keydown.enter')
expect(spy).toHaveBeenCalledTimes(0) expect(spy).toHaveBeenCalledTimes(0)
Select.findComponent({ ref: 'search' }).trigger('compositionend') Select.get('input').trigger('compositionend')
Select.findComponent({ ref: 'search' }).trigger('keydown.enter') Select.get('input').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 }) spy = jest.spyOn(typeAheadPointer.methods, 'typeAheadSelect')
const spy = jest.spyOn(Select.vm, 'typeAheadSelect')
Select.findComponent({ ref: 'search' }).trigger('compositionstart') const Select = mountDefault({ selectOnTab: true })
Select.findComponent({ ref: 'search' }).trigger('keydown.tab')
Select.get('input').trigger('compositionstart')
Select.get('input').trigger('keydown.tab')
expect(spy).toHaveBeenCalledTimes(0) expect(spy).toHaveBeenCalledTimes(0)
Select.findComponent({ ref: 'search' }).trigger('compositionend') Select.get('input').trigger('compositionend')
Select.findComponent({ ref: 'search' }).trigger('keydown.tab') Select.get('input').trigger('keydown.tab')
expect(spy).toHaveBeenCalledTimes(1) expect(spy).toHaveBeenCalledTimes(1)
}) })
}) })
+3 -3
View File
@@ -7,7 +7,7 @@ describe('Labels', () => {
const Select = selectWithProps({ const Select = selectWithProps({
options: [{ name: 'Foo' }], options: [{ name: 'Foo' }],
label: 'name', label: 'name',
value: { name: 'Foo' }, modelValue: { name: 'Foo' },
}) })
expect(Select.find('.vs__selected').text()).toBe('Foo') expect(Select.find('.vs__selected').text()).toBe('Foo')
}) })
@@ -29,7 +29,7 @@ describe('Labels', () => {
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: { props: {
options: ['one'], options: ['one'],
}, },
attrs: { attrs: {
@@ -66,7 +66,7 @@ describe('Labels', () => {
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: { props: {
options: [{ name: 'one' }], options: [{ name: 'one' }],
filter: () => {}, filter: () => {},
}, },
+1 -1
View File
@@ -15,7 +15,7 @@ describe('Single value options', () => {
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 }, props: { value: 'foo', clearSearchOnSelect: false },
}) })
expect(Select.vm.clearSearchOnSelect).toEqual(false) expect(Select.vm.clearSearchOnSelect).toEqual(false)
+34 -35
View File
@@ -3,20 +3,25 @@ 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', async () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { options: ['one'] }, props: { options: ['one'] },
}) })
Select.vm.$data._value = 'one' Select.vm.$data._value = 'one'
Select.setProps({ options: ['four', 'five', 'six'] }) await 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', () => {
let spy
afterEach(() => {
if (spy) spy.mockClear()
})
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(() => {}) spy = jest.spyOn(console, 'warn').mockImplementation(() => {})
mountDefault({ resetOnOptionsChange: 1 }) mountDefault({ resetOnOptionsChange: 1 })
expect(spy.mock.calls[0][0]).toContain( expect(spy.mock.calls[0][0]).toContain(
@@ -44,11 +49,10 @@ describe('Reset on options change', () => {
const Select = mountDefault({ const Select = mountDefault({
resetOnOptionsChange, resetOnOptionsChange,
options: ['bear'], options: ['bear'],
value: 'selected', modelValue: 'selected',
}) })
Select.setProps({ options: ['lake', 'kite'] }) await Select.setProps({ options: ['lake', 'kite'] })
await Select.vm.$nextTick()
expect(resetOnOptionsChange).toHaveBeenCalledTimes(1) expect(resetOnOptionsChange).toHaveBeenCalledTimes(1)
expect(resetOnOptionsChange).toHaveBeenCalledWith( expect(resetOnOptionsChange).toHaveBeenCalledWith(
@@ -60,23 +64,22 @@ describe('Reset on options change', () => {
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
spy = jest.spyOn(VueSelect.methods, 'clearSelection')
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { resetOnOptionsChange, options: ['one'], value: 'one' }, props: { resetOnOptionsChange, options: ['one'], modelValue: 'one' },
}) })
const spy = jest.spyOn(Select.vm, 'clearSelection')
Select.setProps({ options: ['one', 'two'] }) await Select.setProps({ options: ['one', 'two'] })
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
spy = jest.spyOn(VueSelect.methods, 'clearSelection')
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { resetOnOptionsChange, options: ['one'], value: 'one' }, props: { resetOnOptionsChange, options: ['one'], modelValue: 'one' },
}) })
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()
@@ -85,18 +88,16 @@ describe('Reset on options change', () => {
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) => let resetOnOptionsChange = (options, old, val) =>
val.some((val) => options.includes(val)) val.some((val) => options.includes(val))
spy = jest.spyOn(VueSelect.methods, 'clearSelection')
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { resetOnOptionsChange, options: ['one'], value: 'one' }, props: { resetOnOptionsChange, options: ['one'], modelValue: 'one' },
}) })
const spy = jest.spyOn(Select.vm, 'clearSelection')
Select.setProps({ options: ['one', 'two'] }) await Select.setProps({ options: ['one', 'two'] })
await Select.vm.$nextTick()
expect(Select.vm.selectedValue).toEqual(['one']) expect(Select.vm.selectedValue).toEqual(['one'])
Select.setProps({ options: ['two'] }) await Select.setProps({ options: ['two'] })
await Select.vm.$nextTick()
expect(spy).toHaveBeenCalledTimes(1) expect(spy).toHaveBeenCalledTimes(1)
}) })
@@ -104,21 +105,20 @@ describe('Reset on options change', () => {
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'] }, props: { resetOnOptionsChange: true, options: ['one'] },
}) })
Select.vm.$data._value = 'one' Select.vm.$data._value = 'one'
Select.setProps({ options: ['four', 'five', 'six'] }) await Select.setProps({ options: ['four', 'five', 'six'] })
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: { props: {
value: 'one', modelValue: 'one',
options: [], options: [],
reduce(option) { reduce(option) {
return option.value return option.value
@@ -126,20 +126,19 @@ describe('Reset on options change', () => {
}, },
}) })
Select.setProps({ options: [{ label: 'oneLabel', value: 'one' }] }) await Select.setProps({ options: [{ label: 'oneLabel', value: 'one' }] })
await Select.vm.$nextTick()
expect(Select.vm.selectedValue).toEqual([ expect(Select.vm.selectedValue).toEqual([
{ label: 'oneLabel', value: 'one' }, { label: 'oneLabel', value: 'one' },
]) ])
}) })
it('clearSearchOnBlur returns false when multiple is true', () => { it('clearSearchOnBlur returns false when multiple is true', async () => {
const Select = mountDefault({}) const Select = mountDefault({})
let clearSearchOnBlur = jest.spyOn(Select.vm, 'clearSearchOnBlur') let clearSearchOnBlur = jest.spyOn(Select.vm.$.props, 'clearSearchOnBlur')
Select.findComponent({ ref: 'search' }).trigger('click') await Select.get('input').trigger('click')
Select.setData({ search: 'one' }) Select.vm.search = 'one'
Select.findComponent({ ref: 'search' }).trigger('blur') await Select.get('input').trigger('blur')
expect(clearSearchOnBlur).toHaveBeenCalledTimes(1) expect(clearSearchOnBlur).toHaveBeenCalledTimes(1)
expect(clearSearchOnBlur).toHaveBeenCalledWith({ expect(clearSearchOnBlur).toHaveBeenCalledWith({
@@ -149,13 +148,13 @@ describe('Reset on options change', () => {
expect(Select.vm.search).toBe('') expect(Select.vm.search).toBe('')
}) })
it('clearSearchOnBlur accepts a function', () => { it('clearSearchOnBlur accepts a function', async () => {
let clearSearchOnBlur = jest.fn(() => false) let clearSearchOnBlur = jest.fn(() => false)
const Select = mountDefault({ clearSearchOnBlur }) const Select = mountDefault({ clearSearchOnBlur })
Select.findComponent({ ref: 'search' }).trigger('click') await Select.get('input').trigger('click')
Select.setData({ search: 'one' }) Select.vm.search = 'one'
Select.findComponent({ ref: 'search' }).trigger('blur') await Select.get('input').trigger('blur')
expect(clearSearchOnBlur).toHaveBeenCalledTimes(1) expect(clearSearchOnBlur).toHaveBeenCalledTimes(1)
expect(Select.vm.search).toBe('one') expect(Select.vm.search).toBe('one')
+50 -40
View File
@@ -1,12 +1,21 @@
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.js'
describe('When reduce prop is defined', () => { describe('When reduce prop is defined', () => {
it('determines when a reducer has been supplied', async () => {
let Select = mountDefault()
expect(Select.vm.isReducingValues).toBeFalsy()
await Select.setProps({ reduce: () => {} })
expect(Select.vm.isReducingValues).toBeTruthy()
})
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: { props: {
reduce: (option) => option.value, reduce: (option) => option.value,
value: 'foo', modelValue: 'foo',
options: [{ label: 'This is Foo', value: 'foo' }], options: [{ label: 'This is Foo', value: 'foo' }],
}, },
}) })
@@ -17,9 +26,9 @@ describe('When reduce prop is defined', () => {
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: { props: {
reduce: (option) => option.id, reduce: (option) => option.id,
value: 'foo', modelValue: 'foo',
options: [ options: [
{ {
id: 'foo', id: 'foo',
@@ -39,7 +48,7 @@ describe('When reduce prop is defined', () => {
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: { props: {
reduce: (option) => option.id, reduce: (option) => option.id,
options: [{ id: 'foo', label: 'FooBar' }], options: [{ id: 'foo', label: 'FooBar' }],
}, },
@@ -57,10 +66,10 @@ describe('When reduce prop is defined', () => {
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: { props: {
multiple: true, multiple: true,
reduce: (option) => option.value, reduce: (option) => option.value,
value: ['foo'], modelValue: ['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' },
@@ -75,7 +84,7 @@ describe('When reduce prop is defined', () => {
it('can deselect a pre-selected object', () => { it('can deselect a pre-selected object', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { props: {
multiple: true, multiple: true,
reduce: (option) => option.value, reduce: (option) => option.value,
options: [ options: [
@@ -93,7 +102,7 @@ describe('When reduce prop is defined', () => {
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: { props: {
reduce: (option) => option.value, reduce: (option) => option.value,
options: [ options: [
{ label: 'This is Foo', value: 'foo' }, { label: 'This is Foo', value: 'foo' },
@@ -137,24 +146,24 @@ describe('When reduce prop is defined', () => {
/> />
`, `,
}) })
const Select = Parent.vm.$children[0] const Select = Parent.getComponent({ name: 'v-select' })
expect(Select.value).toEqual('foo') expect(Select.vm.modelValue).toEqual('foo')
expect(Select.selectedValue).toEqual([ expect(Select.vm.selectedValue).toEqual([
{ label: 'This is Foo', value: 'foo' }, { label: 'This is Foo', value: 'foo' },
]) ])
Select.select({ label: 'This is Bar', value: 'bar' }) Select.vm.select({ label: 'This is Bar', value: 'bar' })
await Select.$nextTick() await Select.vm.$nextTick()
expect(Parent.vm.value).toEqual('bar') expect(Parent.vm.value).toEqual('bar')
expect(Select.selectedValue).toEqual([ expect(Select.vm.selectedValue).toEqual([
{ label: 'This is Bar', value: 'bar' }, { 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.vm.select({ label: 'This is Baz', value: 'baz' })
await Select.$nextTick() await Select.vm.$nextTick()
expect(Select.selectedValue).toEqual([ expect(Select.vm.selectedValue).toEqual([
{ label: 'This is Bar', value: 'bar' }, { label: 'This is Bar', value: 'bar' },
]) ])
expect(Parent.vm.value).toEqual('bar') expect(Parent.vm.value).toEqual('bar')
@@ -162,10 +171,10 @@ describe('When reduce prop is defined', () => {
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: { props: {
multiple: true, multiple: true,
reduce: (option) => option.value, reduce: (option) => option.value,
value: ['CA'], modelValue: ['CA'],
label: 'name', label: 'name',
options: [ options: [
{ value: 'CA', name: 'Canada' }, { value: 'CA', name: 'Canada' },
@@ -180,7 +189,7 @@ describe('When reduce prop is defined', () => {
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: { props: {
reduce: (option) => option.id, reduce: (option) => option.id,
options: [optionToFind, { id: 2, label: 'Bar' }], options: [optionToFind, { id: 2, label: 'Bar' }],
}, },
@@ -195,10 +204,10 @@ describe('When reduce prop is defined', () => {
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: { props: {
reduce: (option) => option.value, reduce: (option) => option.value,
options: [option, { value: 1, label: 'Yes' }], options: [option, { value: 1, label: 'Yes' }],
value: 0, modelValue: 0,
}, },
}) })
@@ -209,10 +218,10 @@ describe('When reduce prop is defined', () => {
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: { props: {
reduce: (option) => option.value, reduce: (option) => option.value,
options: [option, { value: 1, label: 'Yes' }], options: [option, { value: 1, label: 'Yes' }],
value: null, modelValue: null,
}, },
}) })
@@ -224,9 +233,9 @@ describe('When reduce prop is defined', () => {
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: { props: {
reduce: (option) => option.value, reduce: (option) => option.value,
value: { modelValue: {
nested: true, nested: true,
}, },
options: [nestedOption], options: [nestedOption],
@@ -239,7 +248,7 @@ describe('When reduce prop is defined', () => {
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: { props: {
reduce: (option) => option.value, reduce: (option) => option.value,
options: [nestedOption], options: [nestedOption],
}, },
@@ -253,14 +262,14 @@ describe('When reduce prop is defined', () => {
it('reacts correctly when value property changes', async () => { it('reacts correctly when value property changes', async () => {
const optionToChangeTo = { id: 1, label: 'Foo' } const optionToChangeTo = { id: 1, label: 'Foo' }
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { props: {
value: 2, modelValue: 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({ modelValue: optionToChangeTo.id })
await Select.vm.$nextTick() await Select.vm.$nextTick()
expect(Select.vm.selectedValue).toEqual([optionToChangeTo]) expect(Select.vm.selectedValue).toEqual([optionToChangeTo])
@@ -281,21 +290,22 @@ describe('When reduce prop is defined', () => {
`, `,
components: { 'v-select': VueSelect }, components: { 'v-select': VueSelect },
}) })
const Select = Parent.vm.$children[0] const Select = Parent.getComponent({ name: 'v-select' })
// When // When
Select.$refs.search.focus() await Select.get('input').trigger('focus')
await Select.$nextTick()
Select.search = 'hello' Select.vm.search = 'hello'
await Select.$nextTick() await Select.vm.$nextTick()
Select.typeAheadSelect() Select.vm.typeAheadSelect()
await Select.$nextTick() await Select.vm.$nextTick()
// Then // Then
expect(Select.selectedValue).toEqual([{ label: 'hello', value: -1 }]) expect(Select.vm.selectedValue).toEqual([{ label: 'hello', value: -1 }])
expect(Select.$refs.selectedOptions.textContent.trim()).toEqual('hello') expect(Select.vm.$refs.selectedOptions.textContent.trim()).toEqual(
'hello'
)
expect(Parent.vm.selected).toEqual(-1) expect(Parent.vm.selected).toEqual(-1)
}) })
}) })
+8 -10
View File
@@ -7,12 +7,11 @@ describe('Selectable prop', () => {
selectable: (option) => option === 'one', selectable: (option) => option === 'one',
}) })
Select.vm.$data.open = true Select.vm.open = true
await Select.vm.$nextTick() await Select.vm.$nextTick()
Select.find('.vs__dropdown-menu li:first-child').trigger('click') await Select.find('.vs__dropdown-menu li:first-child').trigger('click')
await Select.vm.$nextTick()
expect(Select.vm.selectedValue).toEqual(['one']) expect(Select.vm.selectedValue).toEqual(['one'])
}) })
@@ -22,16 +21,15 @@ describe('Selectable prop', () => {
selectable: (option) => option === 'one', selectable: (option) => option === 'one',
}) })
Select.vm.$data.open = true Select.vm.open = true
await Select.vm.$nextTick() await Select.vm.$nextTick()
Select.find('.vs__dropdown-menu li:last-child').trigger('click') await Select.find('.vs__dropdown-menu li:last-child').trigger('click')
await Select.vm.$nextTick()
expect(Select.vm.selectedValue).toEqual([]) expect(Select.vm.selectedValue).toEqual([])
}) })
it('should skip non-selectable option on down arrow keyDown', () => { it('should skip non-selectable option on down arrow keyDown', async () => {
const Select = selectWithProps({ const Select = selectWithProps({
options: ['one', 'two', 'three'], options: ['one', 'two', 'three'],
selectable: (option) => option !== 'two', selectable: (option) => option !== 'two',
@@ -39,12 +37,12 @@ describe('Selectable prop', () => {
Select.vm.typeAheadPointer = 1 Select.vm.typeAheadPointer = 1
Select.findComponent({ ref: 'search' }).trigger('keydown.down') await Select.get('input').trigger('keydown.down')
expect(Select.vm.typeAheadPointer).toEqual(2) expect(Select.vm.typeAheadPointer).toEqual(2)
}) })
it('should skip non-selectable option on up arrow keyDown', () => { it('should skip non-selectable option on up arrow keyDown', async () => {
const Select = selectWithProps({ const Select = selectWithProps({
options: ['one', 'two', 'three'], options: ['one', 'two', 'three'],
selectable: (option) => option !== 'two', selectable: (option) => option !== 'two',
@@ -52,7 +50,7 @@ describe('Selectable prop', () => {
Select.vm.typeAheadPointer = 2 Select.vm.typeAheadPointer = 2
Select.findComponent({ ref: 'search' }).trigger('keydown.up') await Select.get('input').trigger('keydown.up')
expect(Select.vm.typeAheadPointer).toEqual(0) expect(Select.vm.typeAheadPointer).toEqual(0)
}) })
+35 -30
View File
@@ -1,41 +1,47 @@
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 typeAheadPointer from '../../src/mixins/typeAheadPointer'
import { mountDefault } from '../helpers' import { mountDefault } from '../helpers'
describe('VS - Selecting Values', () => { describe('VS - Selecting Values', () => {
let defaultProps let defaultProps
let spy
beforeEach(() => { beforeEach(() => {
defaultProps = { defaultProps = {
value: 'one', modelValue: 'one',
options: ['one', 'two', 'three'], options: ['one', 'two', 'three'],
} }
}) })
afterEach(() => {
if (spy) spy.mockClear()
})
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, props: defaultProps,
}) })
expect(Select.selectedValue).toEqual(Select.value) expect(Select.vm.selectedValue[0]).toEqual(Select.vm.modelValue)
}) })
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: { props: {
value: { label: 'This is Foo', value: 'foo' }, modelValue: { 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.vm.selectedValue[0]).toEqual(Select.vm.modelValue)
}) })
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: { props: {
value: [ modelValue: [
{ label: 'This is Foo', value: 'foo' }, { label: 'This is Foo', value: 'foo' },
{ label: 'This is Bar', value: 'bar' }, { label: 'This is Bar', value: 'bar' },
], ],
@@ -47,26 +53,25 @@ describe('VS - Selecting Values', () => {
multiple: true, multiple: true,
}) })
expect(Select.selectedValue).toEqual(Select.value) expect(Select.vm.selectedValue).toEqual(Select.vm.modelValue)
}) })
it('can select an option on tab', () => { it('can select an option on tab', () => {
spy = jest.spyOn(typeAheadPointer.methods, 'typeAheadSelect')
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { props: {
selectOnTab: true, selectOnTab: true,
}, },
}) })
const spy = jest.spyOn(Select.vm, 'typeAheadSelect') Select.get('input').trigger('keydown.tab')
Select.findComponent({ 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: { props: {
multiple: true, multiple: true,
options: [ options: [
{ label: 'This is Foo', value: 'foo' }, { label: 'This is Foo', value: 'foo' },
@@ -88,7 +93,7 @@ describe('VS - Selecting Values', () => {
it('can deselect a pre-selected string', () => { it('can deselect a pre-selected string', () => {
const Select = shallowMount(VueSelect, { const Select = shallowMount(VueSelect, {
propsData: { props: {
multiple: true, multiple: true,
options: ['foo', 'bar'], options: ['foo', 'bar'],
}, },
@@ -111,7 +116,7 @@ describe('VS - Selecting Values', () => {
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: { props: {
options: ['one', 'two', 'three'], options: ['one', 'two', 'three'],
}, },
}) })
@@ -137,7 +142,7 @@ describe('VS - Selecting Values', () => {
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: { props: {
multiple: true, multiple: true,
options: ['one', 'two', 'three'], options: ['one', 'two', 'three'],
}, },
@@ -152,8 +157,8 @@ describe('VS - Selecting Values', () => {
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: { props: {
value: ['one'], modelValue: ['one'],
options: ['one', 'two', 'three'], options: ['one', 'two', 'three'],
}, },
}) })
@@ -164,8 +169,8 @@ describe('VS - Selecting Values', () => {
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: { props: {
value: [{ label: 'one' }], modelValue: [{ label: 'one' }],
options: [{ label: 'one' }], options: [{ label: 'one' }],
}, },
}) })
@@ -179,20 +184,20 @@ describe('VS - Selecting Values', () => {
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.getComponent({ name: 'v-select' })
expect(Select.value).toEqual('foo') expect(Select.vm.modelValue).toEqual('foo')
expect(Select.selectedValue).toEqual(['foo']) expect(Select.vm.selectedValue).toEqual(['foo'])
Select.select('bar') Select.vm.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: { props: {
multiple: true, multiple: true,
value: [{ label: 'foo', value: 'bar' }], modelValue: [{ label: 'foo', value: 'bar' }],
}, },
}) })
expect(Select.vm.isOptionSelected({ label: 'foo', value: 'bar' })).toEqual( expect(Select.vm.isOptionSelected({ label: 'foo', value: 'bar' })).toEqual(
@@ -217,15 +222,15 @@ describe('VS - Selecting Values', () => {
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('update:modelValue')[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'] }, props: { multiple: true, modelValue: ['foo'], options: ['foo', 'bar'] },
}) })
Select.vm.select('bar') Select.vm.select('bar')
expect(Select.emitted('input')[0]).toEqual([['foo', 'bar']]) expect(Select.emitted('update:modelValue')[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', () => {
+29 -25
View File
@@ -1,56 +1,62 @@
import { h } from 'vue'
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' }, { modelValue: 'one' },
{ {
scopedSlots: { slots: {
'selected-option-container': `<span slot="selected-option-container" slot-scope="{option}">{{ option.label }}</span>`, 'selected-option-container': (slotProps) =>
h(
'span',
{ slot: 'selected-option-container' },
slotProps.option.label
),
}, },
} }
) )
expect(Select.findComponent({ ref: 'selectedOptions' }).text()).toEqual( expect(Select.get('.vs__selected-options').text()).toEqual('one')
'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' }, { modelValue: 'one' },
{ {
scopedSlots: { slots: {
'selected-option': `<span slot="selected-option" slot-scope="option">{{ option.label }}</span>`, 'selected-option': (slotProps) =>
h('span', { slot: 'selected-option' }, slotProps.label),
}, },
} }
) )
expect(Select.find('.vs__selected').text()).toEqual('one') expect(Select.get('.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( const Select = mountDefault(
{ value: 'one' }, { modelValue: 'one' },
{ {
scopedSlots: { slots: {
'selected-option': `<span class="my-option" slot-scope="option">{{ option.label }}</span>`, 'selected-option': (slotProps) =>
h('span', { class: 'my-option' }, slotProps.label),
}, },
} }
) )
Select.find('.my-option').trigger('mousedown') Select.get('.my-option').trigger('mousedown')
expect(Select.vm.open).toEqual(true) expect(Select.vm.open).toEqual(true)
}) })
}) })
it('receives an option object to the option slot in the dropdown menu', async () => { it('receives an option object to the option slot in the dropdown menu', async () => {
const Select = mountDefault( const Select = mountDefault(
{ value: 'one' }, { modelValue: 'one' },
{ {
scopedSlots: { slots: {
option: `<span slot="option" slot-scope="option">{{ option.label }}</span>`, option: (slotProps) => h('span', { slot: 'option' }, slotProps.label),
}, },
} }
) )
@@ -58,9 +64,7 @@ describe('Scoped Slots', () => {
Select.vm.open = true Select.vm.open = true
await Select.vm.$nextTick() await Select.vm.$nextTick()
expect(Select.findComponent({ ref: 'dropdownMenu' }).text()).toEqual( expect(Select.get('.vs__dropdown-menu').text()).toEqual('onetwothree')
'onetwothree'
)
}) })
it('noOptions slot receives the current search text', async () => { it('noOptions slot receives the current search text', async () => {
@@ -68,7 +72,7 @@ describe('Scoped Slots', () => {
const Select = mountDefault( const Select = mountDefault(
{}, {},
{ {
scopedSlots: { 'no-options': noOptions }, slots: { 'no-options': noOptions },
} }
) )
@@ -88,7 +92,7 @@ describe('Scoped Slots', () => {
const Select = mountDefault( const Select = mountDefault(
{}, {},
{ {
scopedSlots: { header: header }, slots: { header: header },
} }
) )
await Select.vm.$nextTick() await Select.vm.$nextTick()
@@ -106,7 +110,7 @@ describe('Scoped Slots', () => {
const Select = mountDefault( const Select = mountDefault(
{}, {},
{ {
scopedSlots: { footer: footer }, slots: { footer: footer },
} }
) )
await Select.vm.$nextTick() await Select.vm.$nextTick()
@@ -124,7 +128,7 @@ describe('Scoped Slots', () => {
const Select = mountDefault( const Select = mountDefault(
{}, {},
{ {
scopedSlots: { 'list-header': header }, slots: { 'list-header': header },
} }
) )
Select.vm.open = true Select.vm.open = true
@@ -142,7 +146,7 @@ describe('Scoped Slots', () => {
const Select = mountDefault( const Select = mountDefault(
{}, {},
{ {
scopedSlots: { 'list-footer': footer }, slots: { 'list-footer': footer },
} }
) )
Select.vm.open = true Select.vm.open = true
+10 -11
View File
@@ -4,7 +4,7 @@ import {
selectTag, selectTag,
selectWithProps, selectWithProps,
} from '../helpers' } from '../helpers'
import Select from '../../src/components/Select' import VueSelect from '../../src/components/Select'
describe('When Tagging Is Enabled', () => { describe('When Tagging Is Enabled', () => {
it('can determine if a given option string already exists', () => { it('can determine if a given option string already exists', () => {
@@ -40,7 +40,7 @@ describe('When Tagging Is Enabled', () => {
const Select = selectWithProps({ const Select = selectWithProps({
taggable: true, taggable: true,
multiple: true, multiple: true,
value: ['one'], modelValue: ['one'],
options: ['one', 'two'], options: ['one', 'two'],
}) })
@@ -78,7 +78,7 @@ describe('When Tagging Is Enabled', () => {
pushTags: true, pushTags: true,
taggable: true, taggable: true,
multiple: true, multiple: true,
value: ['one'], modelValue: ['one'],
options: ['one', 'two'], options: ['one', 'two'],
}) })
@@ -107,7 +107,7 @@ describe('When Tagging Is Enabled', () => {
pushTags: true, pushTags: true,
taggable: true, taggable: true,
multiple: true, multiple: true,
value: ['one'], modelValue: ['one'],
options: ['one', 'two'], options: ['one', 'two'],
}) })
@@ -122,7 +122,7 @@ describe('When Tagging Is Enabled', () => {
pushTags: false, pushTags: false,
taggable: true, taggable: true,
multiple: true, multiple: true,
value: ['one'], modelValue: ['one'],
options: ['one', 'two'], options: ['one', 'two'],
}) })
@@ -136,7 +136,7 @@ describe('When Tagging Is Enabled', () => {
pushTags: false, pushTags: false,
taggable: true, taggable: true,
multiple: true, multiple: true,
value: ['one'], modelValue: ['one'],
options: ['one', 'two'], options: ['one', 'two'],
}) })
@@ -191,7 +191,7 @@ describe('When Tagging Is Enabled', () => {
const Select = selectWithProps({ const Select = selectWithProps({
taggable: true, taggable: true,
multiple: true, multiple: true,
value: [{ label: 'one' }], modelValue: [{ label: 'one' }],
options: [{ label: 'one' }], options: [{ label: 'one' }],
}) })
@@ -204,7 +204,7 @@ describe('When Tagging Is Enabled', () => {
taggable: true, taggable: true,
multiple: true, multiple: true,
filterable: false, filterable: false,
value: [{ label: 'one' }], modelValue: [{ label: 'one' }],
options: [{ label: 'one' }], options: [{ label: 'one' }],
}) })
@@ -228,12 +228,12 @@ describe('When Tagging Is Enabled', () => {
}) })
it('should not allow duplicate tags when using object options', async () => { it('should not allow duplicate tags when using object options', async () => {
const spy = jest.spyOn(VueSelect.methods, 'select')
const Select = selectWithProps({ const Select = selectWithProps({
taggable: true, taggable: true,
multiple: true, multiple: true,
options: [{ label: 'two' }], 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([{ label: 'one' }])
@@ -252,9 +252,8 @@ describe('When Tagging Is Enabled', () => {
}) })
Select.vm.typeAheadPointer = 0 Select.vm.typeAheadPointer = 0
Select.findComponent({ ref: 'search' }).trigger('keydown.tab') await Select.get('input').trigger('keydown.tab')
await Select.vm.$nextTick()
expect(Select.vm.selectedValue).toEqual(['one']) expect(Select.vm.selectedValue).toEqual(['one'])
}) })
}) })
+3 -3
View File
@@ -7,7 +7,7 @@ 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'] }, props: { options: ['one', 'two', 'three'] },
sync: false, sync: false,
}) })
@@ -22,7 +22,7 @@ describe('Moving the Typeahead Pointer', () => {
Select.vm.typeAheadPointer = 1 Select.vm.typeAheadPointer = 1
Select.findComponent({ ref: 'search' }).trigger('keydown.up') Select.get('input').trigger('keydown.up')
expect(Select.vm.typeAheadPointer).toEqual(0) expect(Select.vm.typeAheadPointer).toEqual(0)
}) })
@@ -32,7 +32,7 @@ describe('Moving the Typeahead Pointer', () => {
Select.vm.typeAheadPointer = 1 Select.vm.typeAheadPointer = 1
Select.findComponent({ ref: 'search' }).trigger('keydown.down') Select.get('input').trigger('keydown.down')
expect(Select.vm.typeAheadPointer).toEqual(2) expect(Select.vm.typeAheadPointer).toEqual(2)
}) })
+427 -270
View File
File diff suppressed because it is too large Load Diff