mirror of
https://github.com/tenrok/vue-select.git
synced 2026-06-22 10:30:34 +03:00
switch to vue cli for build
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
> 1%
|
||||||
|
last 2 versions
|
||||||
|
not dead
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: {
|
||||||
|
node: true
|
||||||
|
},
|
||||||
|
extends: ["plugin:vue/vue3-essential", "eslint:recommended", "@vue/prettier"],
|
||||||
|
parserOptions: {
|
||||||
|
parser: "babel-eslint"
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||||
|
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off"
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
"**/__tests__/*.{j,t}s?(x)",
|
||||||
|
"**/tests/unit/**/*.spec.{j,t}s?(x)"
|
||||||
|
],
|
||||||
|
env: {
|
||||||
|
jest: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
+7
-11
@@ -1,8 +1,12 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
node_modules
|
node_modules
|
||||||
|
/dist
|
||||||
|
|
||||||
|
/tests/e2e/videos/
|
||||||
|
/tests/e2e/screenshots/
|
||||||
|
|
||||||
|
|
||||||
# local env files
|
# local env files
|
||||||
.env
|
|
||||||
.env.local
|
.env.local
|
||||||
.env.*.local
|
.env.*.local
|
||||||
|
|
||||||
@@ -10,6 +14,7 @@ node_modules
|
|||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.idea
|
.idea
|
||||||
@@ -18,13 +23,4 @@ yarn-error.log*
|
|||||||
*.ntvs*
|
*.ntvs*
|
||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw*
|
*.sw?
|
||||||
|
|
||||||
# Project specific
|
|
||||||
coverage
|
|
||||||
dist
|
|
||||||
test/unit/coverage
|
|
||||||
package-lock.json
|
|
||||||
dev/dist
|
|
||||||
docs/.vuepress/dist
|
|
||||||
.netlify
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: ["@vue/cli-plugin-babel/preset"]
|
||||||
|
};
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"pluginsFile": "tests/e2e/plugins/index.js"
|
||||||
|
}
|
||||||
+56
-30
@@ -1,39 +1,49 @@
|
|||||||
{
|
{
|
||||||
"name": "vue-select",
|
"name": "vue-select",
|
||||||
"version": "3.11.2",
|
"version": "3.11.2",
|
||||||
"description": "Everything you wish the HTML <select> element could do, wrapped up into a lightweight, extensible Vue component.",
|
|
||||||
"author": "Jeff Sagal <sagalbot@gmail.com>",
|
|
||||||
"homepage": "https://vue-select.org",
|
|
||||||
"directories": {
|
|
||||||
"doc": "docs",
|
|
||||||
"test": "tests"
|
|
||||||
},
|
|
||||||
"private": false,
|
"private": false,
|
||||||
"main": "dist/vue-select.js",
|
"description": "Everything you wish the HTML <select> element could do, wrapped up into a lightweight, accessible and composable Vue component.",
|
||||||
"license": "MIT",
|
"author": {
|
||||||
"prepare": "npm run build",
|
"name": "Jeff Sagal",
|
||||||
|
"email": "sagalbot@gmail.com"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "webpack-dev-server --config build/webpack.dev.conf.js --hot --progress -d",
|
"serve": "vue-cli-service serve",
|
||||||
"serve:docs": "cd docs && yarn serve",
|
"build": "vue-cli-service build",
|
||||||
"build": "cross-env NODE_ENV=production webpack --config build/webpack.prod.conf.js --progress",
|
"test:unit": "vue-cli-service test:unit",
|
||||||
|
"test:e2e": "vue-cli-service test:e2e",
|
||||||
|
"lint": "vue-cli-service lint",
|
||||||
"build:docs": "cd docs && yarn build",
|
"build:docs": "cd docs && yarn build",
|
||||||
"build:preview": "cd docs && yarn build",
|
"build:preview": "cd docs && yarn build",
|
||||||
"test": "jest",
|
"commit": "git-cz",
|
||||||
"semantic-release": "semantic-release",
|
"semantic-release": "semantic-release",
|
||||||
"commit": "git-cz"
|
"serve:docs": "cd docs && yarn serve",
|
||||||
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"repository": {
|
"main": "dist/vue-select.js",
|
||||||
"type": "git",
|
"dependencies": {
|
||||||
"url": "https://github.com/sagalbot/vue-select.git"
|
"core-js": "^3.6.5",
|
||||||
},
|
"vue": "^3.0.0"
|
||||||
"peerDependencies": {
|
|
||||||
"vue": "2.x"
|
|
||||||
},
|
|
||||||
"resolutions": {
|
|
||||||
"ajv": "6.8.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@vue/cli-plugin-babel": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-e2e-cypress": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||||
|
"@vue/cli-plugin-unit-jest": "~4.5.0",
|
||||||
|
"@vue/cli-service": "~4.5.0",
|
||||||
|
"@vue/compiler-sfc": "^3.0.0",
|
||||||
|
"@vue/eslint-config-prettier": "^6.0.0",
|
||||||
|
"@vue/test-utils": "^2.0.0-0",
|
||||||
|
"babel-eslint": "^10.1.0",
|
||||||
|
"eslint": "^6.7.2",
|
||||||
|
"eslint-plugin-prettier": "^3.1.3",
|
||||||
|
"eslint-plugin-vue": "^7.0.0-0",
|
||||||
|
"prettier": "^1.19.1",
|
||||||
|
"typescript": "~3.9.3",
|
||||||
|
"vue-jest": "^5.0.0-0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "3.x"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"moduleFileExtensions": [
|
"moduleFileExtensions": [
|
||||||
@@ -66,12 +76,11 @@
|
|||||||
],
|
],
|
||||||
"coverageReporters": [
|
"coverageReporters": [
|
||||||
"text"
|
"text"
|
||||||
]
|
],
|
||||||
|
"preset": "@vue/cli-plugin-unit-jest"
|
||||||
},
|
},
|
||||||
"config": {
|
"bugs": {
|
||||||
"commitizen": {
|
"url": "https://github.com/sagalbot/vue-select/issues"
|
||||||
"path": "./node_modules/cz-conventional-changelog"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"bundlewatch": {
|
"bundlewatch": {
|
||||||
"files": [
|
"files": [
|
||||||
@@ -86,5 +95,22 @@
|
|||||||
"maxSize": "6 KB"
|
"maxSize": "6 KB"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"commitizen": {
|
||||||
|
"path": "./node_modules/cz-conventional-changelog"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"directories": {
|
||||||
|
"doc": "docs",
|
||||||
|
"test": "tests"
|
||||||
|
},
|
||||||
|
"homepage": "https://vue-select.org",
|
||||||
|
"license": "MIT",
|
||||||
|
"prepare": "npm run build",
|
||||||
|
"readme": "ERROR: No README data found!",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/sagalbot/vue-select.git"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -1,8 +1,8 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [
|
plugins: [
|
||||||
require('autoprefixer'),
|
require("autoprefixer"),
|
||||||
require('cssnano')({
|
require("cssnano")({
|
||||||
preset: 'default',
|
preset: "default"
|
||||||
}),
|
})
|
||||||
],
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<img alt="Vue logo" src="./assets/logo.png" />
|
||||||
|
<HelloWorld msg="Welcome to Your Vue.js App" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import HelloWorld from "./components/HelloWorld.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "App",
|
||||||
|
components: {
|
||||||
|
HelloWorld
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#app {
|
||||||
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
text-align: center;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-top: 60px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
@@ -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>
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
<template>
|
||||||
|
<div class="hello">
|
||||||
|
<h1>{{ msg }}</h1>
|
||||||
|
<p>
|
||||||
|
For a guide and recipes on how to configure / customize this project,<br />
|
||||||
|
check out the
|
||||||
|
<a href="https://cli.vuejs.org" target="_blank" rel="noopener"
|
||||||
|
>vue-cli documentation</a
|
||||||
|
>.
|
||||||
|
</p>
|
||||||
|
<h3>Installed CLI Plugins</h3>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>babel</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>eslint</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-unit-jest"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>unit-jest</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-e2e-cypress"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>e2e-cypress</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h3>Essential Links</h3>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://forum.vuejs.org" target="_blank" rel="noopener"
|
||||||
|
>Forum</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://chat.vuejs.org" target="_blank" rel="noopener"
|
||||||
|
>Community Chat</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener"
|
||||||
|
>Twitter</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h3>Ecosystem</h3>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="https://router.vuejs.org" target="_blank" rel="noopener"
|
||||||
|
>vue-router</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://github.com/vuejs/vue-devtools#vue-devtools"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>vue-devtools</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener"
|
||||||
|
>vue-loader</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://github.com/vuejs/awesome-vue"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
>awesome-vue</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "HelloWorld",
|
||||||
|
props: {
|
||||||
|
msg: String
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
<style scoped>
|
||||||
|
h3 {
|
||||||
|
margin: 40px 0 0;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #42b983;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -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>
|
||||||
|
|||||||
+204
-246
@@ -1,104 +1,18 @@
|
|||||||
<style lang="scss">
|
|
||||||
@import '../scss/vue-select.scss';
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :dir="dir" class="v-select" :class="stateClasses">
|
|
||||||
<slot name="header" v-bind="scope.header" />
|
|
||||||
<div :id="`vs${uid}__combobox`" ref="toggle" @mousedown="toggleDropdown($event)" class="vs__dropdown-toggle" role="combobox" :aria-expanded="dropdownOpen.toString()" :aria-owns="`vs${uid}__listbox`" aria-label="Search for option">
|
|
||||||
|
|
||||||
<div class="vs__selected-options" ref="selectedOptions">
|
|
||||||
<slot v-for="option in selectedValue"
|
|
||||||
name="selected-option-container"
|
|
||||||
:option="normalizeOptionForSlot(option)"
|
|
||||||
:deselect="deselect"
|
|
||||||
:multiple="multiple"
|
|
||||||
:disabled="disabled">
|
|
||||||
<span :key="getOptionKey(option)" class="vs__selected">
|
|
||||||
<slot name="selected-option" v-bind="normalizeOptionForSlot(option)">
|
|
||||||
{{ getOptionLabel(option) }}
|
|
||||||
</slot>
|
|
||||||
<button v-if="multiple" :disabled="disabled" @click="deselect(option)" type="button" class="vs__deselect" :title="`Deselect ${getOptionLabel(option)}`" :aria-label="`Deselect ${getOptionLabel(option)}`" ref="deselectButtons">
|
|
||||||
<component :is="childComponents.Deselect" />
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</slot>
|
|
||||||
|
|
||||||
<slot name="search" v-bind="scope.search">
|
|
||||||
<input class="vs__search" v-bind="scope.search.attributes" v-on="scope.search.events">
|
|
||||||
</slot>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="vs__actions" ref="actions">
|
|
||||||
<button
|
|
||||||
v-show="showClearButton"
|
|
||||||
:disabled="disabled"
|
|
||||||
@click="clearSelection"
|
|
||||||
type="button"
|
|
||||||
class="vs__clear"
|
|
||||||
title="Clear Selected"
|
|
||||||
aria-label="Clear Selected"
|
|
||||||
ref="clearButton"
|
|
||||||
>
|
|
||||||
<component :is="childComponents.Deselect" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<slot name="open-indicator" v-bind="scope.openIndicator">
|
|
||||||
<component :is="childComponents.OpenIndicator" v-if="!noDrop" v-bind="scope.openIndicator.attributes"/>
|
|
||||||
</slot>
|
|
||||||
|
|
||||||
<slot name="spinner" v-bind="scope.spinner">
|
|
||||||
<div class="vs__spinner" v-show="mutableLoading">Loading...</div>
|
|
||||||
</slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<transition :name="transition">
|
|
||||||
<ul ref="dropdownMenu" v-if="dropdownOpen" :id="`vs${uid}__listbox`" :key="`vs${uid}__listbox`" class="vs__dropdown-menu" role="listbox" @mousedown.prevent="onMousedown" @mouseup="onMouseUp" tabindex="-1" v-append-to-body>
|
|
||||||
<slot name="list-header" v-bind="scope.listHeader" />
|
|
||||||
<li
|
|
||||||
role="option"
|
|
||||||
v-for="(option, index) in filteredOptions"
|
|
||||||
:key="getOptionKey(option)"
|
|
||||||
:id="`vs${uid}__option-${index}`"
|
|
||||||
class="vs__dropdown-option"
|
|
||||||
:class="{ 'vs__dropdown-option--selected': isOptionSelected(option), 'vs__dropdown-option--highlight': index === typeAheadPointer, 'vs__dropdown-option--disabled': !selectable(option) }"
|
|
||||||
:aria-selected="index === typeAheadPointer ? true : null"
|
|
||||||
@mouseover="selectable(option) ? typeAheadPointer = index : null"
|
|
||||||
@mousedown.prevent.stop="selectable(option) ? select(option) : null"
|
|
||||||
>
|
|
||||||
<slot name="option" v-bind="normalizeOptionForSlot(option)">
|
|
||||||
{{ getOptionLabel(option) }}
|
|
||||||
</slot>
|
|
||||||
</li>
|
|
||||||
<li v-if="filteredOptions.length === 0" class="vs__no-options">
|
|
||||||
<slot name="no-options" v-bind="scope.noOptions">Sorry, no matching options.</slot>
|
|
||||||
</li>
|
|
||||||
<slot name="list-footer" v-bind="scope.listFooter" />
|
|
||||||
</ul>
|
|
||||||
<ul v-else :id="`vs${uid}__listbox`" role="listbox" style="display: none; visibility: hidden;"></ul>
|
|
||||||
</transition>
|
|
||||||
<slot name="footer" v-bind="scope.footer" />
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script type="text/babel">
|
<script>
|
||||||
import pointerScroll from '../mixins/pointerScroll'
|
|
||||||
import typeAheadPointer from '../mixins/typeAheadPointer'
|
|
||||||
import ajax from '../mixins/ajax'
|
|
||||||
import childComponents from './childComponents';
|
|
||||||
import appendToBody from '../directives/appendToBody';
|
|
||||||
import sortAndStringify from '../utility/sortAndStringify'
|
|
||||||
import uniqueId from '../utility/uniqueId';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name VueSelect
|
* @name VueSelect
|
||||||
*/
|
*/
|
||||||
export default {
|
export default {
|
||||||
components: {...childComponents},
|
components: { ...childComponents },
|
||||||
|
|
||||||
mixins: [pointerScroll, typeAheadPointer, ajax],
|
mixins: [pointerScroll, typeAheadPointer, ajax],
|
||||||
|
|
||||||
directives: {appendToBody},
|
directives: { appendToBody },
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
/**
|
/**
|
||||||
@@ -118,7 +32,7 @@
|
|||||||
*/
|
*/
|
||||||
components: {
|
components: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -131,8 +45,8 @@
|
|||||||
options: {
|
options: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default() {
|
default() {
|
||||||
return []
|
return [];
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -177,7 +91,7 @@
|
|||||||
*/
|
*/
|
||||||
placeholder: {
|
placeholder: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ""
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -186,7 +100,7 @@
|
|||||||
*/
|
*/
|
||||||
transition: {
|
transition: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'vs__fade'
|
default: "vs__fade"
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -215,7 +129,7 @@
|
|||||||
*/
|
*/
|
||||||
label: {
|
label: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'label'
|
default: "label"
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -225,7 +139,7 @@
|
|||||||
*/
|
*/
|
||||||
autocomplete: {
|
autocomplete: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'off'
|
default: "off"
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -237,7 +151,7 @@
|
|||||||
*/
|
*/
|
||||||
reduce: {
|
reduce: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default: option => option,
|
default: option => option
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -251,7 +165,7 @@
|
|||||||
*/
|
*/
|
||||||
selectable: {
|
selectable: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default: option => true,
|
default: option => true
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -270,15 +184,15 @@
|
|||||||
getOptionLabel: {
|
getOptionLabel: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default(option) {
|
default(option) {
|
||||||
if (typeof option === 'object') {
|
if (typeof option === "object") {
|
||||||
if (!option.hasOwnProperty(this.label)) {
|
if (!option.hasOwnProperty(this.label)) {
|
||||||
return console.warn(
|
return console.warn(
|
||||||
`[vue-select warn]: Label key "option.${this.label}" does not` +
|
`[vue-select warn]: Label key "option.${this.label}" does not` +
|
||||||
` exist in options object ${JSON.stringify(option)}.\n` +
|
` exist in options object ${JSON.stringify(option)}.\n` +
|
||||||
'https://vue-select.org/api/props.html#getoptionlabel'
|
"https://vue-select.org/api/props.html#getoptionlabel"
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
return option[this.label]
|
return option[this.label];
|
||||||
}
|
}
|
||||||
return option;
|
return option;
|
||||||
}
|
}
|
||||||
@@ -302,21 +216,24 @@
|
|||||||
*/
|
*/
|
||||||
getOptionKey: {
|
getOptionKey: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default (option) {
|
default(option) {
|
||||||
if (typeof option !== 'object') {
|
if (typeof option !== "object") {
|
||||||
return option;
|
return option;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return option.hasOwnProperty('id') ? option.id : sortAndStringify(option);
|
return option.hasOwnProperty("id")
|
||||||
|
? option.id
|
||||||
|
: sortAndStringify(option);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const warning = `[vue-select warn]: Could not stringify this option ` +
|
const warning =
|
||||||
|
`[vue-select warn]: Could not stringify this option ` +
|
||||||
`to generate unique key. Please provide'getOptionKey' prop ` +
|
`to generate unique key. Please provide'getOptionKey' prop ` +
|
||||||
`to return a unique key for each option.\n` +
|
`to return a unique key for each option.\n` +
|
||||||
'https://vue-select.org/api/props.html#getoptionkey';
|
"https://vue-select.org/api/props.html#getoptionkey";
|
||||||
return console.warn(warning, option, e);
|
return console.warn(warning, option, e);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -325,11 +242,11 @@
|
|||||||
*/
|
*/
|
||||||
onTab: {
|
onTab: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default: function () {
|
default: function() {
|
||||||
if (this.selectOnTab && !this.isComposing) {
|
if (this.selectOnTab && !this.isComposing) {
|
||||||
this.typeAheadSelect();
|
this.typeAheadSelect();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -384,7 +301,7 @@
|
|||||||
filterBy: {
|
filterBy: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default(option, label, search) {
|
default(option, label, search) {
|
||||||
return (label || '').toLowerCase().indexOf(search.toLowerCase()) > -1
|
return (label || "").toLowerCase().indexOf(search.toLowerCase()) > -1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -402,12 +319,12 @@
|
|||||||
filter: {
|
filter: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default(options, search) {
|
default(options, search) {
|
||||||
return options.filter((option) => {
|
return options.filter(option => {
|
||||||
let label = this.getOptionLabel(option)
|
let label = this.getOptionLabel(option);
|
||||||
if (typeof label === 'number') {
|
if (typeof label === "number") {
|
||||||
label = label.toString()
|
label = label.toString();
|
||||||
}
|
}
|
||||||
return this.filterBy(option, label, search)
|
return this.filterBy(option, label, search);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -418,9 +335,11 @@
|
|||||||
*/
|
*/
|
||||||
createOption: {
|
createOption: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default (option) {
|
default(option) {
|
||||||
return (typeof this.optionList[0] === 'object') ? {[this.label]: option} : option;
|
return typeof this.optionList[0] === "object"
|
||||||
},
|
? { [this.label]: option }
|
||||||
|
: option;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -437,7 +356,7 @@
|
|||||||
*/
|
*/
|
||||||
resetOnOptionsChange: {
|
resetOnOptionsChange: {
|
||||||
default: false,
|
default: false,
|
||||||
validator: (value) => ['function', 'boolean'].includes(typeof value)
|
validator: value => ["function", "boolean"].includes(typeof value)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -446,8 +365,8 @@
|
|||||||
*/
|
*/
|
||||||
clearSearchOnBlur: {
|
clearSearchOnBlur: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default: function ({ clearSearchOnSelect, multiple }) {
|
default: function({ clearSearchOnSelect, multiple }) {
|
||||||
return clearSearchOnSelect && !multiple
|
return clearSearchOnSelect && !multiple;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -477,7 +396,7 @@
|
|||||||
*/
|
*/
|
||||||
dir: {
|
dir: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'auto'
|
default: "auto"
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -496,7 +415,7 @@
|
|||||||
*/
|
*/
|
||||||
selectOnKeyCodes: {
|
selectOnKeyCodes: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => [13],
|
default: () => [13]
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -510,7 +429,7 @@
|
|||||||
*/
|
*/
|
||||||
searchInputQuerySelector: {
|
searchInputQuerySelector: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '[type=search]'
|
default: "[type=search]"
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -525,7 +444,7 @@
|
|||||||
* @param vm {VueSelect}
|
* @param vm {VueSelect}
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
default: (map, vm) => map,
|
default: (map, vm) => map
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -560,7 +479,7 @@
|
|||||||
* @param left {string} absolute position left value in pixels relative to the document
|
* @param left {string} absolute position left value in pixels relative to the document
|
||||||
* @return {function|void}
|
* @return {function|void}
|
||||||
*/
|
*/
|
||||||
default(dropdownList, component, {width, top, left}) {
|
default(dropdownList, component, { width, top, left }) {
|
||||||
dropdownList.style.top = top;
|
dropdownList.style.top = top;
|
||||||
dropdownList.style.left = left;
|
dropdownList.style.left = left;
|
||||||
dropdownList.style.width = width;
|
dropdownList.style.width = width;
|
||||||
@@ -571,12 +490,12 @@
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
uid: uniqueId(),
|
uid: uniqueId(),
|
||||||
search: '',
|
search: "",
|
||||||
open: false,
|
open: false,
|
||||||
isComposing: false,
|
isComposing: false,
|
||||||
pushedTags: [],
|
pushedTags: [],
|
||||||
_value: [] // Internal value managed by Vue Select if no `value` prop is passed
|
_value: [] // Internal value managed by Vue Select if no `value` prop is passed
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
@@ -587,9 +506,14 @@
|
|||||||
* is correct.
|
* is correct.
|
||||||
* @return {[type]} [description]
|
* @return {[type]} [description]
|
||||||
*/
|
*/
|
||||||
options (newOptions, oldOptions) {
|
options(newOptions, oldOptions) {
|
||||||
let shouldReset = () => typeof this.resetOnOptionsChange === 'function'
|
let shouldReset = () =>
|
||||||
? this.resetOnOptionsChange(newOptions, oldOptions, this.selectedValue)
|
typeof this.resetOnOptionsChange === "function"
|
||||||
|
? this.resetOnOptionsChange(
|
||||||
|
newOptions,
|
||||||
|
oldOptions,
|
||||||
|
this.selectedValue
|
||||||
|
)
|
||||||
: this.resetOnOptionsChange;
|
: this.resetOnOptionsChange;
|
||||||
|
|
||||||
if (!this.taggable && shouldReset()) {
|
if (!this.taggable && shouldReset()) {
|
||||||
@@ -607,7 +531,7 @@
|
|||||||
*/
|
*/
|
||||||
value(val) {
|
value(val) {
|
||||||
if (this.isTrackingValues) {
|
if (this.isTrackingValues) {
|
||||||
this.setInternalValueFromOptions(val)
|
this.setInternalValueFromOptions(val);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -618,11 +542,11 @@
|
|||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
multiple() {
|
multiple() {
|
||||||
this.clearSelection()
|
this.clearSelection();
|
||||||
},
|
},
|
||||||
|
|
||||||
open(isOpen) {
|
open(isOpen) {
|
||||||
this.$emit(isOpen ? 'open' : 'close');
|
this.$emit(isOpen ? "open" : "close");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -630,10 +554,10 @@
|
|||||||
this.mutableLoading = this.loading;
|
this.mutableLoading = this.loading;
|
||||||
|
|
||||||
if (typeof this.value !== "undefined" && this.isTrackingValues) {
|
if (typeof this.value !== "undefined" && this.isTrackingValues) {
|
||||||
this.setInternalValueFromOptions(this.value)
|
this.setInternalValueFromOptions(this.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$on('option:created', this.pushTag)
|
this.$on("option:created", this.pushTag);
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
@@ -645,7 +569,9 @@
|
|||||||
*/
|
*/
|
||||||
setInternalValueFromOptions(value) {
|
setInternalValueFromOptions(value) {
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
this.$data._value = value.map(val => this.findOptionFromReducedValue(val));
|
this.$data._value = value.map(val =>
|
||||||
|
this.findOptionFromReducedValue(val)
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
this.$data._value = this.findOptionFromReducedValue(value);
|
this.$data._value = this.findOptionFromReducedValue(value);
|
||||||
}
|
}
|
||||||
@@ -657,18 +583,18 @@
|
|||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
select(option) {
|
select(option) {
|
||||||
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)) {
|
||||||
this.$emit('option:created', option);
|
this.$emit("option:created", option);
|
||||||
}
|
}
|
||||||
if (this.multiple) {
|
if (this.multiple) {
|
||||||
option = this.selectedValue.concat(option)
|
option = this.selectedValue.concat(option);
|
||||||
}
|
}
|
||||||
this.updateValue(option);
|
this.updateValue(option);
|
||||||
this.$emit('option:selected', option);
|
this.$emit("option:selected", option);
|
||||||
}
|
}
|
||||||
this.onAfterSelect(option)
|
this.onAfterSelect(option);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -676,12 +602,14 @@
|
|||||||
* @param {Object|String} option
|
* @param {Object|String} option
|
||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
deselect (option) {
|
deselect(option) {
|
||||||
this.$emit('option:deselecting', option);
|
this.$emit("option:deselecting", option);
|
||||||
this.updateValue(this.selectedValue.filter(val => {
|
this.updateValue(
|
||||||
|
this.selectedValue.filter(val => {
|
||||||
return !this.optionComparator(val, option);
|
return !this.optionComparator(val, option);
|
||||||
}));
|
})
|
||||||
this.$emit('option:deselected', option);
|
);
|
||||||
|
this.$emit("option:deselected", option);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -689,7 +617,7 @@
|
|||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
clearSelection() {
|
clearSelection() {
|
||||||
this.updateValue(this.multiple ? [] : null)
|
this.updateValue(this.multiple ? [] : null);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -700,11 +628,11 @@
|
|||||||
onAfterSelect(option) {
|
onAfterSelect(option) {
|
||||||
if (this.closeOnSelect) {
|
if (this.closeOnSelect) {
|
||||||
this.open = !this.open;
|
this.open = !this.open;
|
||||||
this.searchEl.blur()
|
this.searchEl.blur();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.clearSearchOnSelect) {
|
if (this.clearSearchOnSelect) {
|
||||||
this.search = ''
|
this.search = "";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -716,8 +644,8 @@
|
|||||||
* @emits input
|
* @emits input
|
||||||
* @param value
|
* @param value
|
||||||
*/
|
*/
|
||||||
updateValue (value) {
|
updateValue(value) {
|
||||||
if (typeof this.value === 'undefined') {
|
if (typeof this.value === "undefined") {
|
||||||
// Vue select has to manage value
|
// Vue select has to manage value
|
||||||
this.$data._value = value;
|
this.$data._value = value;
|
||||||
}
|
}
|
||||||
@@ -730,7 +658,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$emit('input', value);
|
this.$emit("input", value);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -738,7 +666,7 @@
|
|||||||
* @param {Event} event
|
* @param {Event} event
|
||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
toggleDropdown (event) {
|
toggleDropdown(event) {
|
||||||
const targetIsNotSearch = event.target !== this.searchEl;
|
const targetIsNotSearch = event.target !== this.searchEl;
|
||||||
if (targetIsNotSearch) {
|
if (targetIsNotSearch) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -747,11 +675,16 @@
|
|||||||
// don't react to click on deselect/clear buttons,
|
// don't react to click on deselect/clear buttons,
|
||||||
// they dropdown state will be set in their click handlers
|
// they dropdown state will be set in their click handlers
|
||||||
const ignoredButtons = [
|
const ignoredButtons = [
|
||||||
...(this.$refs['deselectButtons'] || []),
|
...(this.$refs["deselectButtons"] || []),
|
||||||
...([this.$refs['clearButton']] || []),
|
...([this.$refs["clearButton"]] || [])
|
||||||
];
|
];
|
||||||
|
|
||||||
if (this.searchEl === undefined || ignoredButtons.filter(Boolean).some(ref => ref.contains(event.target) || ref === event.target)) {
|
if (
|
||||||
|
this.searchEl === undefined ||
|
||||||
|
ignoredButtons
|
||||||
|
.filter(Boolean)
|
||||||
|
.some(ref => ref.contains(event.target) || ref === event.target)
|
||||||
|
) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -770,7 +703,9 @@
|
|||||||
* @return {Boolean} True when selected | False otherwise
|
* @return {Boolean} True when selected | False otherwise
|
||||||
*/
|
*/
|
||||||
isOptionSelected(option) {
|
isOptionSelected(option) {
|
||||||
return this.selectedValue.some(value => this.optionComparator(value, option))
|
return this.selectedValue.some(value =>
|
||||||
|
this.optionComparator(value, option)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -792,13 +727,11 @@
|
|||||||
* @param value {Object}
|
* @param value {Object}
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
findOptionFromReducedValue (value) {
|
findOptionFromReducedValue(value) {
|
||||||
const predicate = option => JSON.stringify(this.reduce(option)) === JSON.stringify(value);
|
const predicate = option =>
|
||||||
|
JSON.stringify(this.reduce(option)) === JSON.stringify(value);
|
||||||
|
|
||||||
const matches = [
|
const matches = [...this.options, ...this.pushedTags].filter(predicate);
|
||||||
...this.options,
|
|
||||||
...this.pushedTags,
|
|
||||||
].filter(predicate);
|
|
||||||
|
|
||||||
if (matches.length === 1) {
|
if (matches.length === 1) {
|
||||||
return matches[0];
|
return matches[0];
|
||||||
@@ -810,7 +743,11 @@
|
|||||||
* unique reduced value.
|
* unique reduced value.
|
||||||
* @see https://github.com/sagalbot/vue-select/issues/1089#issuecomment-597238735
|
* @see https://github.com/sagalbot/vue-select/issues/1089#issuecomment-597238735
|
||||||
*/
|
*/
|
||||||
return matches.find(match => this.optionComparator(match, this.$data._value)) || value;
|
return (
|
||||||
|
matches.find(match =>
|
||||||
|
this.optionComparator(match, this.$data._value)
|
||||||
|
) || value
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -818,9 +755,9 @@
|
|||||||
* @emits {search:blur}
|
* @emits {search:blur}
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
closeSearchOptions(){
|
closeSearchOptions() {
|
||||||
this.open = false
|
this.open = false;
|
||||||
this.$emit('search:blur')
|
this.$emit("search:blur");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -829,12 +766,19 @@
|
|||||||
* @return {this.value}
|
* @return {this.value}
|
||||||
*/
|
*/
|
||||||
maybeDeleteValue() {
|
maybeDeleteValue() {
|
||||||
if (!this.searchEl.value.length && this.selectedValue && this.selectedValue.length && this.clearable) {
|
if (
|
||||||
|
!this.searchEl.value.length &&
|
||||||
|
this.selectedValue &&
|
||||||
|
this.selectedValue.length &&
|
||||||
|
this.clearable
|
||||||
|
) {
|
||||||
let value = null;
|
let value = null;
|
||||||
if (this.multiple) {
|
if (this.multiple) {
|
||||||
value = [...this.selectedValue.slice(0, this.selectedValue.length - 1)]
|
value = [
|
||||||
|
...this.selectedValue.slice(0, this.selectedValue.length - 1)
|
||||||
|
];
|
||||||
}
|
}
|
||||||
this.updateValue(value)
|
this.updateValue(value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -846,7 +790,9 @@
|
|||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
optionExists(option) {
|
optionExists(option) {
|
||||||
return this.optionList.some(_option => this.optionComparator(_option, option))
|
return this.optionList.some(_option =>
|
||||||
|
this.optionComparator(_option, option)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -855,8 +801,8 @@
|
|||||||
* @param option
|
* @param option
|
||||||
* @return {*}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
normalizeOptionForSlot (option) {
|
normalizeOptionForSlot(option) {
|
||||||
return (typeof option === 'object') ? option : {[this.label]: option};
|
return typeof option === "object" ? option : { [this.label]: option };
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -866,7 +812,7 @@
|
|||||||
* @param {Object || String} option
|
* @param {Object || String} option
|
||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
pushTag (option) {
|
pushTag(option) {
|
||||||
this.pushedTags.push(option);
|
this.pushedTags.push(option);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -877,9 +823,9 @@
|
|||||||
*/
|
*/
|
||||||
onEscape() {
|
onEscape() {
|
||||||
if (!this.search.length) {
|
if (!this.search.length) {
|
||||||
this.searchEl.blur()
|
this.searchEl.blur();
|
||||||
} else {
|
} else {
|
||||||
this.search = ''
|
this.search = "";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -890,19 +836,19 @@
|
|||||||
*/
|
*/
|
||||||
onSearchBlur() {
|
onSearchBlur() {
|
||||||
if (this.mousedown && !this.searching) {
|
if (this.mousedown && !this.searching) {
|
||||||
this.mousedown = false
|
this.mousedown = false;
|
||||||
} else {
|
} else {
|
||||||
const { clearSearchOnSelect, multiple } = this;
|
const { clearSearchOnSelect, multiple } = this;
|
||||||
if (this.clearSearchOnBlur({ clearSearchOnSelect, multiple })) {
|
if (this.clearSearchOnBlur({ clearSearchOnSelect, multiple })) {
|
||||||
this.search = ''
|
this.search = "";
|
||||||
}
|
}
|
||||||
this.closeSearchOptions()
|
this.closeSearchOptions();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
// Fixed bug where no-options message could not be closed
|
// Fixed bug where no-options message could not be closed
|
||||||
if (this.search.length === 0 && this.options.length === 0){
|
if (this.search.length === 0 && this.options.length === 0) {
|
||||||
this.closeSearchOptions()
|
this.closeSearchOptions();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -912,8 +858,8 @@
|
|||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
onSearchFocus() {
|
onSearchFocus() {
|
||||||
this.open = true
|
this.open = true;
|
||||||
this.$emit('search:focus')
|
this.$emit("search:focus");
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -925,7 +871,7 @@
|
|||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
onMousedown() {
|
onMousedown() {
|
||||||
this.mousedown = true
|
this.mousedown = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -934,7 +880,7 @@
|
|||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
onMouseUp() {
|
onMouseUp() {
|
||||||
this.mousedown = false
|
this.mousedown = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -942,7 +888,7 @@
|
|||||||
* @param e {KeyboardEvent}
|
* @param e {KeyboardEvent}
|
||||||
* @return {Function}
|
* @return {Function}
|
||||||
*/
|
*/
|
||||||
onSearchKeyDown (e) {
|
onSearchKeyDown(e) {
|
||||||
const preventAndSelect = e => {
|
const preventAndSelect = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
return !this.isComposing && this.typeAheadSelect();
|
return !this.isComposing && this.typeAheadSelect();
|
||||||
@@ -964,14 +910,16 @@
|
|||||||
40: e => {
|
40: e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
return this.typeAheadDown();
|
return this.typeAheadDown();
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.selectOnKeyCodes.forEach(keyCode => defaults[keyCode] = preventAndSelect);
|
this.selectOnKeyCodes.forEach(
|
||||||
|
keyCode => (defaults[keyCode] = preventAndSelect)
|
||||||
|
);
|
||||||
|
|
||||||
const handlers = this.mapKeydown(defaults, this);
|
const handlers = this.mapKeydown(defaults, this);
|
||||||
|
|
||||||
if (typeof handlers[e.keyCode] === 'function') {
|
if (typeof handlers[e.keyCode] === "function") {
|
||||||
return handlers[e.keyCode](e);
|
return handlers[e.keyCode](e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -983,15 +931,18 @@
|
|||||||
* track the state of values internally.
|
* track the state of values internally.
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
isTrackingValues () {
|
isTrackingValues() {
|
||||||
return typeof this.value === 'undefined' || this.$options.propsData.hasOwnProperty('reduce');
|
return (
|
||||||
|
typeof this.value === "undefined" ||
|
||||||
|
this.$options.propsData.hasOwnProperty("reduce")
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The options that are currently selected.
|
* The options that are currently selected.
|
||||||
* @return {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
selectedValue () {
|
selectedValue() {
|
||||||
let value = this.value;
|
let value = this.value;
|
||||||
if (this.isTrackingValues) {
|
if (this.isTrackingValues) {
|
||||||
// Vue select has to manage value internally
|
// Vue select has to manage value internally
|
||||||
@@ -1012,7 +963,7 @@
|
|||||||
*
|
*
|
||||||
* @return {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
optionList () {
|
optionList() {
|
||||||
return this.options.concat(this.pushTags ? this.pushedTags : []);
|
return this.options.concat(this.pushTags ? this.pushedTags : []);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1020,9 +971,11 @@
|
|||||||
* Find the search input DOM element.
|
* Find the search input DOM element.
|
||||||
* @returns {HTMLInputElement}
|
* @returns {HTMLInputElement}
|
||||||
*/
|
*/
|
||||||
searchEl () {
|
searchEl() {
|
||||||
return !!this.$scopedSlots['search']
|
return this.$slots["search"]
|
||||||
? this.$refs.selectedOptions.querySelector(this.searchInputQuerySelector)
|
? this.$refs.selectedOptions.querySelector(
|
||||||
|
this.searchInputQuerySelector
|
||||||
|
)
|
||||||
: this.$refs.search;
|
: this.$refs.search;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1030,7 +983,7 @@
|
|||||||
* The object to be bound to the $slots.search scoped slot.
|
* The object to be bound to the $slots.search scoped slot.
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
scope () {
|
scope() {
|
||||||
const listSlot = {
|
const listSlot = {
|
||||||
search: this.search,
|
search: this.search,
|
||||||
loading: this.loading,
|
loading: this.loading,
|
||||||
@@ -1040,30 +993,32 @@
|
|||||||
return {
|
return {
|
||||||
search: {
|
search: {
|
||||||
attributes: {
|
attributes: {
|
||||||
'disabled': this.disabled,
|
disabled: this.disabled,
|
||||||
'placeholder': this.searchPlaceholder,
|
placeholder: this.searchPlaceholder,
|
||||||
'tabindex': this.tabindex,
|
tabindex: this.tabindex,
|
||||||
'readonly': !this.searchable,
|
readonly: !this.searchable,
|
||||||
'id': this.inputId,
|
id: this.inputId,
|
||||||
'aria-autocomplete': 'list',
|
"aria-autocomplete": "list",
|
||||||
'aria-labelledby': `vs${this.uid}__combobox`,
|
"aria-labelledby": `vs${this.uid}__combobox`,
|
||||||
'aria-controls': `vs${this.uid}__listbox`,
|
"aria-controls": `vs${this.uid}__listbox`,
|
||||||
'ref': 'search',
|
ref: "search",
|
||||||
'type': 'search',
|
type: "search",
|
||||||
'autocomplete': this.autocomplete,
|
autocomplete: this.autocomplete,
|
||||||
'value': this.search,
|
value: this.search,
|
||||||
...(this.dropdownOpen && this.filteredOptions[this.typeAheadPointer] ? {
|
...(this.dropdownOpen && this.filteredOptions[this.typeAheadPointer]
|
||||||
'aria-activedescendant': `vs${this.uid}__option-${this.typeAheadPointer}`
|
? {
|
||||||
} : {}),
|
"aria-activedescendant": `vs${this.uid}__option-${this.typeAheadPointer}`
|
||||||
|
}
|
||||||
|
: {})
|
||||||
},
|
},
|
||||||
events: {
|
events: {
|
||||||
'compositionstart': () => this.isComposing = true,
|
compositionstart: () => (this.isComposing = true),
|
||||||
'compositionend': () => this.isComposing = false,
|
compositionend: () => (this.isComposing = false),
|
||||||
'keydown': this.onSearchKeyDown,
|
keydown: this.onSearchKeyDown,
|
||||||
'blur': this.onSearchBlur,
|
blur: this.onSearchBlur,
|
||||||
'focus': this.onSearchFocus,
|
focus: this.onSearchFocus,
|
||||||
'input': (e) => this.search = e.target.value,
|
input: e => (this.search = e.target.value)
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
spinner: {
|
spinner: {
|
||||||
loading: this.mutableLoading
|
loading: this.mutableLoading
|
||||||
@@ -1071,14 +1026,14 @@
|
|||||||
noOptions: {
|
noOptions: {
|
||||||
search: this.search,
|
search: this.search,
|
||||||
loading: this.loading,
|
loading: this.loading,
|
||||||
searching: this.searching,
|
searching: this.searching
|
||||||
},
|
},
|
||||||
openIndicator: {
|
openIndicator: {
|
||||||
attributes: {
|
attributes: {
|
||||||
'ref': 'openIndicator',
|
ref: "openIndicator",
|
||||||
'role': 'presentation',
|
role: "presentation",
|
||||||
'class': 'vs__open-indicator',
|
class: "vs__open-indicator"
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
listHeader: listSlot,
|
listHeader: listSlot,
|
||||||
listFooter: listSlot,
|
listFooter: listSlot,
|
||||||
@@ -1094,7 +1049,7 @@
|
|||||||
*
|
*
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
childComponents () {
|
childComponents() {
|
||||||
return {
|
return {
|
||||||
...childComponents,
|
...childComponents,
|
||||||
...this.components
|
...this.components
|
||||||
@@ -1107,14 +1062,14 @@
|
|||||||
*/
|
*/
|
||||||
stateClasses() {
|
stateClasses() {
|
||||||
return {
|
return {
|
||||||
'vs--open': this.dropdownOpen,
|
"vs--open": this.dropdownOpen,
|
||||||
'vs--single': !this.multiple,
|
"vs--single": !this.multiple,
|
||||||
'vs--searching': this.searching && !this.noDrop,
|
"vs--searching": this.searching && !this.noDrop,
|
||||||
'vs--searchable': this.searchable && !this.noDrop,
|
"vs--searchable": this.searchable && !this.noDrop,
|
||||||
'vs--unsearchable': !this.searchable,
|
"vs--unsearchable": !this.searchable,
|
||||||
'vs--loading': this.mutableLoading,
|
"vs--loading": this.mutableLoading,
|
||||||
'vs--disabled': this.disabled
|
"vs--disabled": this.disabled
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1123,7 +1078,7 @@
|
|||||||
* @return {Boolean} True if non empty value
|
* @return {Boolean} True if non empty value
|
||||||
*/
|
*/
|
||||||
searching() {
|
searching() {
|
||||||
return !! this.search
|
return !!this.search;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1132,7 +1087,7 @@
|
|||||||
* @return {Boolean} True if open
|
* @return {Boolean} True if open
|
||||||
*/
|
*/
|
||||||
dropdownOpen() {
|
dropdownOpen() {
|
||||||
return this.noDrop ? false : this.open && !this.mutableLoading
|
return this.noDrop ? false : this.open && !this.mutableLoading;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1161,7 +1116,9 @@
|
|||||||
return optionList;
|
return optionList;
|
||||||
}
|
}
|
||||||
|
|
||||||
let options = this.search.length ? this.filter(optionList, this.search, this) : optionList;
|
let options = this.search.length
|
||||||
|
? this.filter(optionList, this.search, this)
|
||||||
|
: optionList;
|
||||||
if (this.taggable && this.search.length) {
|
if (this.taggable && this.search.length) {
|
||||||
const createdOption = this.createOption(this.search);
|
const createdOption = this.createOption(this.search);
|
||||||
if (!this.optionExists(createdOption)) {
|
if (!this.optionExists(createdOption)) {
|
||||||
@@ -1184,9 +1141,10 @@
|
|||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
showClearButton() {
|
showClearButton() {
|
||||||
return !this.multiple && this.clearable && !this.open && !this.isValueEmpty
|
return (
|
||||||
},
|
!this.multiple && this.clearable && !this.open && !this.isValueEmpty
|
||||||
},
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,27 +1,32 @@
|
|||||||
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 {
|
||||||
|
height,
|
||||||
|
top,
|
||||||
|
left,
|
||||||
|
width
|
||||||
|
} = context.$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 = context.calculatePosition(el, context, {
|
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"
|
||||||
});
|
});
|
||||||
|
|
||||||
document.body.appendChild(el);
|
document.body.appendChild(el);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
unbind (el, bindings, {context}) {
|
unbind(el, bindings, { context }) {
|
||||||
if (context.appendToBody) {
|
if (context.appendToBody) {
|
||||||
if (el.unbindPosition && typeof el.unbindPosition === 'function') {
|
if (el.unbindPosition && typeof el.unbindPosition === "function") {
|
||||||
el.unbindPosition();
|
el.unbindPosition();
|
||||||
}
|
}
|
||||||
if (el.parentNode) {
|
if (el.parentNode) {
|
||||||
el.parentNode.removeChild(el);
|
el.parentNode.removeChild(el);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
};
|
||||||
|
|||||||
+4
-4
@@ -1,5 +1,5 @@
|
|||||||
import VueSelect from './components/Select.vue'
|
import VueSelect from "./components/Select.vue";
|
||||||
import mixins from './mixins/index'
|
import mixins from "./mixins/index";
|
||||||
|
|
||||||
export default VueSelect
|
export default VueSelect;
|
||||||
export { VueSelect, mixins }
|
export { VueSelect, mixins };
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { createApp } from "vue";
|
||||||
|
import App from "./App.vue";
|
||||||
|
|
||||||
|
createApp(App).mount("#app");
|
||||||
+11
-11
@@ -7,13 +7,13 @@ export default {
|
|||||||
*/
|
*/
|
||||||
loading: {
|
loading: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
mutableLoading: false,
|
mutableLoading: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -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,9 +36,9 @@ export default {
|
|||||||
* mutable loading value.
|
* mutable loading value.
|
||||||
* @param val
|
* @param val
|
||||||
*/
|
*/
|
||||||
loading (val) {
|
loading(val) {
|
||||||
this.mutableLoading = val;
|
this.mutableLoading = val;
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
@@ -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);
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+4
-4
@@ -1,5 +1,5 @@
|
|||||||
import ajax from './ajax'
|
import ajax from "./ajax";
|
||||||
import pointer from './typeAheadPointer'
|
import pointer from "./typeAheadPointer";
|
||||||
import pointerScroll from './pointerScroll'
|
import pointerScroll from "./pointerScroll";
|
||||||
|
|
||||||
export default { ajax, pointer, pointerScroll }
|
export default { ajax, pointer, pointerScroll };
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default {
|
|||||||
if (this.autoscroll) {
|
if (this.autoscroll) {
|
||||||
this.maybeAdjustScroll();
|
this.maybeAdjustScroll();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
typeAheadPointer: -1
|
typeAheadPointer: -1
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
@@ -37,7 +37,11 @@ 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;
|
||||||
@@ -58,4 +62,4 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
function sortAndStringify(sortable) {
|
function sortAndStringify(sortable) {
|
||||||
const ordered = {};
|
const ordered = {};
|
||||||
|
|
||||||
Object.keys(sortable).sort().forEach(key => {
|
Object.keys(sortable)
|
||||||
|
.sort()
|
||||||
|
.forEach(key => {
|
||||||
ordered[key] = sortable[key];
|
ordered[key] = sortable[key];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: ["cypress"],
|
||||||
|
env: {
|
||||||
|
mocha: true,
|
||||||
|
"cypress/globals": true
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
strict: "off"
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/* eslint-disable arrow-body-style */
|
||||||
|
// https://docs.cypress.io/guides/guides/plugins-guide.html
|
||||||
|
|
||||||
|
// if you need a custom webpack configuration you can uncomment the following import
|
||||||
|
// and then use the `file:preprocessor` event
|
||||||
|
// as explained in the cypress docs
|
||||||
|
// https://docs.cypress.io/api/plugins/preprocessors-api.html#Examples
|
||||||
|
|
||||||
|
// /* eslint-disable import/no-extraneous-dependencies, global-require */
|
||||||
|
// const webpack = require('@cypress/webpack-preprocessor')
|
||||||
|
|
||||||
|
module.exports = (on, config) => {
|
||||||
|
// on('file:preprocessor', webpack({
|
||||||
|
// webpackOptions: require('@vue/cli-service/webpack.config'),
|
||||||
|
// watchOptions: {}
|
||||||
|
// }))
|
||||||
|
|
||||||
|
return Object.assign({}, config, {
|
||||||
|
fixturesFolder: "tests/e2e/fixtures",
|
||||||
|
integrationFolder: "tests/e2e/specs",
|
||||||
|
screenshotsFolder: "tests/e2e/screenshots",
|
||||||
|
videosFolder: "tests/e2e/videos",
|
||||||
|
supportFile: "tests/e2e/support/index.js"
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// https://docs.cypress.io/api/introduction/api.html
|
||||||
|
|
||||||
|
describe("My First Test", () => {
|
||||||
|
it("Visits the app root url", () => {
|
||||||
|
cy.visit("/");
|
||||||
|
cy.contains("h1", "Welcome to Your Vue.js App");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// ***********************************************
|
||||||
|
// This example commands.js shows you how to
|
||||||
|
// create various custom commands and overwrite
|
||||||
|
// existing commands.
|
||||||
|
//
|
||||||
|
// For more comprehensive examples of custom
|
||||||
|
// commands please read more here:
|
||||||
|
// https://on.cypress.io/custom-commands
|
||||||
|
// ***********************************************
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This is a parent command --
|
||||||
|
// Cypress.Commands.add("login", (email, password) => { ... })
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This is a child command --
|
||||||
|
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This is a dual command --
|
||||||
|
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This is will overwrite an existing command --
|
||||||
|
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// ***********************************************************
|
||||||
|
// This example support/index.js is processed and
|
||||||
|
// loaded automatically before your test files.
|
||||||
|
//
|
||||||
|
// This is a great place to put global configuration and
|
||||||
|
// behavior that modifies Cypress.
|
||||||
|
//
|
||||||
|
// You can change the location of this file or turn off
|
||||||
|
// automatically serving support files with the
|
||||||
|
// 'supportFile' configuration option.
|
||||||
|
//
|
||||||
|
// You can read more here:
|
||||||
|
// https://on.cypress.io/configuration
|
||||||
|
// ***********************************************************
|
||||||
|
|
||||||
|
// Import commands.js using ES2015 syntax:
|
||||||
|
import "./commands";
|
||||||
|
|
||||||
|
// Alternatively you can use CommonJS syntax:
|
||||||
|
// require('./commands')
|
||||||
+10
-10
@@ -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
|
||||||
@@ -13,7 +13,7 @@ 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");
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,14 +52,13 @@ export const selectWithProps = (propsData = {}) => {
|
|||||||
export const mountDefault = (props = {}, options = {}) => {
|
export const mountDefault = (props = {}, options = {}) => {
|
||||||
return shallowMount(VueSelect, {
|
return shallowMount(VueSelect, {
|
||||||
propsData: {
|
propsData: {
|
||||||
options: ['one', 'two', 'three'],
|
options: ["one", "two", "three"],
|
||||||
...props,
|
...props
|
||||||
},
|
},
|
||||||
...options,
|
...options
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a v-select component directly.
|
* Returns a v-select component directly.
|
||||||
* @param props
|
* @param props
|
||||||
@@ -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', {
|
render: createEl =>
|
||||||
ref: 'select',
|
createEl("vue-select", {
|
||||||
props: {options: ['one', 'two', 'three'], ...props},
|
ref: "select",
|
||||||
|
props: { options: ["one", "two", "three"], ...props },
|
||||||
...options
|
...options
|
||||||
}),
|
}),
|
||||||
components: {VueSelect},
|
components: { VueSelect }
|
||||||
}).$mount().$refs.select;
|
}).$mount().$refs.select;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ module.exports = {
|
|||||||
jest: true
|
jest: true
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
'import/no-extraneous-dependencies': 'off'
|
"import/no-extraneous-dependencies": "off"
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
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", () => {
|
||||||
@@ -43,19 +43,19 @@ describe("Asynchronous Loading", () => {
|
|||||||
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();
|
||||||
|
|||||||
@@ -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();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
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");
|
||||||
@@ -31,9 +31,9 @@ describe("Removing values", () => {
|
|||||||
|
|
||||||
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"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -42,20 +42,20 @@ describe("Removing values", () => {
|
|||||||
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);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -81,9 +81,9 @@ describe("Removing values", () => {
|
|||||||
|
|
||||||
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");
|
||||||
|
|||||||
+18
-19
@@ -1,10 +1,10 @@
|
|||||||
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", () => {
|
||||||
@@ -34,7 +34,7 @@ describe("Toggling Dropdown", () => {
|
|||||||
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);
|
||||||
Select.vm.toggleDropdown(clickEvent(Select.vm.$el));
|
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 selected tag is clicked", () => {
|
||||||
@@ -135,22 +135,22 @@ describe("Toggling Dropdown", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Select.vm.search = "foo";
|
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));
|
||||||
@@ -158,34 +158,33 @@ describe("Toggling Dropdown", () => {
|
|||||||
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();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ describe("Filtering Options", () => {
|
|||||||
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", () => {
|
||||||
|
|||||||
+27
-31
@@ -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);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
+20
-16
@@ -42,15 +42,19 @@ describe("Labels", () => {
|
|||||||
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");
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,23 +63,23 @@ describe("Labels", () => {
|
|||||||
* @see https://github.com/vuejs/vue/issues/10224
|
* @see https://github.com/vuejs/vue/issues/10224
|
||||||
* @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();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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"
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
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", () => {
|
||||||
@@ -8,80 +8,94 @@ describe("Reset on options change", () => {
|
|||||||
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'});
|
|
||||||
expect(spy.mock.calls[1][0]).toContain('Invalid prop: custom validator check failed for prop "resetOnOptionsChange"')
|
|
||||||
|
|
||||||
mountDefault({resetOnOptionsChange: []});
|
|
||||||
expect(spy.mock.calls[2][0]).toContain('Invalid prop: custom validator check failed for prop "resetOnOptionsChange"')
|
|
||||||
|
|
||||||
mountDefault({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 () => {
|
|
||||||
let resetOnOptionsChange = jest.fn(option => option);
|
|
||||||
const Select = mountDefault(
|
|
||||||
{resetOnOptionsChange, options: ['bear'], value: 'selected'},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Select.setProps({options: ['lake', 'kite']});
|
mountDefault({ resetOnOptionsChange: "one" });
|
||||||
|
expect(spy.mock.calls[1][0]).toContain(
|
||||||
|
'Invalid prop: custom validator check failed for prop "resetOnOptionsChange"'
|
||||||
|
);
|
||||||
|
|
||||||
|
mountDefault({ resetOnOptionsChange: [] });
|
||||||
|
expect(spy.mock.calls[2][0]).toContain(
|
||||||
|
'Invalid prop: custom validator check failed for prop "resetOnOptionsChange"'
|
||||||
|
);
|
||||||
|
|
||||||
|
mountDefault({ 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 () => {
|
||||||
|
let resetOnOptionsChange = jest.fn(option => option);
|
||||||
|
const Select = mountDefault({
|
||||||
|
resetOnOptionsChange,
|
||||||
|
options: ["bear"],
|
||||||
|
value: "selected"
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
@@ -93,9 +107,9 @@ describe("Reset on options change", () => {
|
|||||||
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([]);
|
||||||
@@ -103,39 +117,47 @@ describe("Reset on options change", () => {
|
|||||||
|
|
||||||
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");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+49
-35
@@ -10,7 +10,9 @@ describe("When reduce prop is defined", () => {
|
|||||||
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", () => {
|
||||||
@@ -35,20 +37,22 @@ describe("When reduce prop is defined", () => {
|
|||||||
).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(
|
||||||
id: 'foo',
|
Select.vm.isOptionSelected({
|
||||||
label: 'This is FooBar',
|
id: "foo",
|
||||||
})).toEqual(true);
|
label: "This is FooBar"
|
||||||
|
})
|
||||||
|
).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)", () => {
|
||||||
@@ -64,7 +68,9 @@ describe("When reduce prop is defined", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
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", () => {
|
||||||
@@ -79,7 +85,7 @@ describe("When reduce prop is defined", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
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"]);
|
||||||
@@ -118,7 +124,7 @@ describe("When reduce prop is defined", () => {
|
|||||||
return this.current;
|
return this.current;
|
||||||
},
|
},
|
||||||
set(value) {
|
set(value) {
|
||||||
if (value == 'baz') return;
|
if (value == "baz") return;
|
||||||
this.current = value;
|
this.current = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,18 +140,24 @@ describe("When reduce prop is defined", () => {
|
|||||||
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", () => {
|
||||||
@@ -155,7 +167,10 @@ describe("When reduce prop is defined", () => {
|
|||||||
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" }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -177,28 +192,28 @@ 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: {
|
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);
|
||||||
@@ -233,7 +248,6 @@ describe("When reduce prop is defined", () => {
|
|||||||
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 () => {
|
it("reacts correctly when value property changes", async () => {
|
||||||
@@ -252,10 +266,10 @@ describe("When reduce prop is defined", () => {
|
|||||||
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,7 +279,7 @@ 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];
|
||||||
|
|
||||||
@@ -273,15 +287,15 @@ describe("When reduce prop is defined", () => {
|
|||||||
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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ 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;
|
||||||
@@ -14,12 +14,12 @@ describe("Selectable prop", () => {
|
|||||||
|
|
||||||
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;
|
||||||
@@ -34,7 +34,7 @@ describe("Selectable prop", () => {
|
|||||||
it("should skip non-selectable option on down 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 = 1;
|
Select.vm.typeAheadPointer = 1;
|
||||||
@@ -42,12 +42,12 @@ describe("Selectable prop", () => {
|
|||||||
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 skip non-selectable option on up arrow keyDown", () => {
|
it("should skip non-selectable option on up 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 = 2;
|
||||||
@@ -55,5 +55,5 @@ describe("Selectable prop", () => {
|
|||||||
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);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
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;
|
||||||
@@ -81,7 +81,9 @@ describe("VS - Selecting Values", () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
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", () => {
|
||||||
@@ -193,15 +195,20 @@ describe("VS - Selecting Values", () => {
|
|||||||
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);
|
||||||
});
|
});
|
||||||
@@ -223,7 +230,11 @@ describe("VS - Selecting Values", () => {
|
|||||||
|
|
||||||
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");
|
||||||
@@ -257,7 +268,11 @@ describe("VS - Selecting Values", () => {
|
|||||||
|
|
||||||
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,
|
||||||
|
value: ["foo", "bar"],
|
||||||
|
options: ["foo", "bar"]
|
||||||
|
}
|
||||||
});
|
});
|
||||||
Select.vm.select("bar");
|
Select.vm.select("bar");
|
||||||
Select.vm.select("bar");
|
Select.vm.select("bar");
|
||||||
@@ -293,7 +308,11 @@ describe("VS - Selecting Values", () => {
|
|||||||
|
|
||||||
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,
|
||||||
|
value: ["foo", "bar"],
|
||||||
|
options: ["foo", "bar"]
|
||||||
|
}
|
||||||
});
|
});
|
||||||
Select.vm.deselect("bar");
|
Select.vm.deselect("bar");
|
||||||
Select.vm.deselect("bar");
|
Select.vm.deselect("bar");
|
||||||
|
|||||||
+90
-59
@@ -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", () => {
|
||||||
});
|
it("receives an option object to the selected-option slot", () => {
|
||||||
|
|
||||||
describe('Slot: selected-option', () => {
|
|
||||||
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(
|
const Select = mountDefault(
|
||||||
{value: 'one'},
|
{ value: "one" },
|
||||||
{
|
{
|
||||||
scopedSlots: {
|
scopedSlots: {
|
||||||
'selected-option': `<span class="my-option" slot-scope="option">{{ option.label }}</span>`,
|
"selected-option": `<span class="my-option" slot-scope="option">{{ option.label }}</span>`
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
Select.find('.my-option').trigger('mousedown');
|
Select.find(".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',
|
it("receives an option object to the option slot in the dropdown menu", async () => {
|
||||||
async () => {
|
|
||||||
const Select = mountDefault(
|
const Select = mountDefault(
|
||||||
{value: 'one'},
|
{ value: "one" },
|
||||||
{
|
{
|
||||||
scopedSlots: {
|
scopedSlots: {
|
||||||
'option': `<span slot="option" slot-scope="option">{{ option.label }}</span>`,
|
option: `<span slot="option" slot-scope="option">{{ option.label }}</span>`
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
Select.vm.open = true;
|
Select.vm.open = true;
|
||||||
await Select.vm.$nextTick();
|
await Select.vm.$nextTick();
|
||||||
|
|
||||||
expect(Select.find({ref: 'dropdownMenu'}).text()).toEqual('onetwothree');
|
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},
|
{},
|
||||||
});
|
{
|
||||||
|
scopedSlots: { header: header }
|
||||||
|
}
|
||||||
|
);
|
||||||
await Select.vm.$nextTick();
|
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},
|
{},
|
||||||
});
|
{
|
||||||
|
scopedSlots: { footer: footer }
|
||||||
|
}
|
||||||
|
);
|
||||||
await Select.vm.$nextTick();
|
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},
|
{},
|
||||||
});
|
{
|
||||||
|
scopedSlots: { "list-header": header }
|
||||||
|
}
|
||||||
|
);
|
||||||
Select.vm.open = true;
|
Select.vm.open = true;
|
||||||
await Select.vm.$nextTick();
|
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},
|
{},
|
||||||
});
|
{
|
||||||
|
scopedSlots: { "list-footer": footer }
|
||||||
|
}
|
||||||
|
);
|
||||||
Select.vm.open = true;
|
Select.vm.open = true;
|
||||||
await Select.vm.$nextTick();
|
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"
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+11
-14
@@ -2,12 +2,11 @@ import {
|
|||||||
mountDefault,
|
mountDefault,
|
||||||
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", () => {
|
it("can determine if a given option string already exists", () => {
|
||||||
const Select = selectWithProps({ taggable: true, options: ["one", "two"] });
|
const Select = selectWithProps({ taggable: true, options: ["one", "two"] });
|
||||||
expect(Select.vm.optionExists("one")).toEqual(true);
|
expect(Select.vm.optionExists("one")).toEqual(true);
|
||||||
@@ -20,8 +19,8 @@ describe("When Tagging Is Enabled", () => {
|
|||||||
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", () => {
|
||||||
@@ -31,7 +30,7 @@ describe("When Tagging Is Enabled", () => {
|
|||||||
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);
|
||||||
@@ -71,9 +70,7 @@ describe("When Tagging Is Enabled", () => {
|
|||||||
|
|
||||||
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 () => {
|
||||||
@@ -236,11 +233,11 @@ describe("When Tagging Is Enabled", () => {
|
|||||||
multiple: true,
|
multiple: true,
|
||||||
options: [{ label: "two" }]
|
options: [{ label: "two" }]
|
||||||
});
|
});
|
||||||
const spy = jest.spyOn(Select.vm, 'select');
|
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" }]);
|
||||||
expect(spy).lastCalledWith({label: 'one'});
|
expect(spy).lastCalledWith({ label: "one" });
|
||||||
expect(Select.vm.search).toEqual("");
|
expect(Select.vm.search).toEqual("");
|
||||||
|
|
||||||
await selectTag(Select, "one");
|
await selectTag(Select, "one");
|
||||||
@@ -258,6 +255,6 @@ describe("When Tagging Is Enabled", () => {
|
|||||||
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"]);
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { shallowMount } from "@vue/test-utils";
|
||||||
|
import HelloWorld from "@/components/HelloWorld.vue";
|
||||||
|
|
||||||
|
describe("HelloWorld.vue", () => {
|
||||||
|
it("renders props.msg when passed", () => {
|
||||||
|
const msg = "new message";
|
||||||
|
const wrapper = shallowMount(HelloWorld, {
|
||||||
|
props: { msg }
|
||||||
|
});
|
||||||
|
expect(wrapper.text()).toMatch(msg);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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" })
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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());
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user