2
0
mirror of https://github.com/tenrok/vue-select.git synced 2026-06-19 09:50:33 +03:00

feat: merge upstream into beta channel (#1589)

This commit is contained in:
Jeff Sagal
2022-02-18 13:55:34 -08:00
committed by GitHub
parent 7a3b373d43
commit ff2806876d
40 changed files with 2565 additions and 3146 deletions
+13 -24
View File
@@ -1,38 +1,27 @@
--- ---
name: Bug report name: Bug Report
about: Create a report to help us improve about: Create a bug report to help fix an issue with the component.
title: '' title: ''
labels: '' labels: 'bug'
assignees: '' assignees: ''
--- ---
> Please respect maintainers time by filling in these sections. Your issue will likely be closed without this information.
- Vue Version:
- Vue Select Version:
**Describe the bug** **Describe the bug**
A clear and concise description of what the bug is. A clear and concise description of what the bug is.
**To Reproduce** **Reproduction Link**
Steps to reproduce the behavior: A link to a reproduction of the bug. This is a huge help.
1. Go to '...'
2. Click on '....' **Steps To Reproduce**
3. Scroll down to '....' Outline the steps to reproduce the bug.
4. See error
**Expected behavior** **Expected behavior**
A clear and concise description of what you expected to happen. A clear and concise description of what you expected to happen.
**Screenshots** **Screenshots**
If applicable, add screenshots to help explain your problem. If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
+5
View File
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Discussions
url: https://github.com/sagalbot/vue-select/discussions
about: If you're not submitting a bug or feature request, please use discussions instead.
+5 -3
View File
@@ -41,12 +41,14 @@ module.exports = {
exclude: /node_modules/, exclude: /node_modules/,
}, },
{ {
test: /\.s?css$/, test: /\.css$/i,
use: [ use: [
extractOrInjectStyles, extractOrInjectStyles,
'css-loader', {
loader: 'css-loader',
options: { importLoaders: 1 },
},
'postcss-loader', 'postcss-loader',
'sass-loader',
], ],
}, },
], ],
+9 -25
View File
@@ -1,37 +1,21 @@
const merge = require('webpack-merge') const { merge } = require('webpack-merge')
const chokidar = require('chokidar')
const HtmlWebpackPlugin = require('html-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin')
const baseWebpackConfig = require('./webpack.base.conf') const baseWebpackConfig = require('./webpack.base.conf')
/**
*
*/
module.exports = merge(baseWebpackConfig, { module.exports = merge(baseWebpackConfig, {
entry: './dev/dev.js', entry: './dev/dev.js',
devServer: {
open: true,
static: false,
client: { overlay: true },
watchFiles: ['dev/dev.html'],
},
stats: false,
plugins: [ plugins: [
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
title: 'dev',
template: './dev/dev.html', template: './dev/dev.html',
inject: true, inject: true,
}), }),
], ],
optimization: {
noEmitOnErrors: true,
},
devServer: {
hot: true,
hotOnly: true,
open: true,
inline: true,
stats: {
children: false,
modules: false,
chunks: false,
},
port: 8080,
before(app, server) {
chokidar.watch(['./**/*.html']).on('all', function () {
server.sockWrite(server.sockets, 'content-changed')
})
},
},
}) })
+3 -6
View File
@@ -1,5 +1,5 @@
const TerserPlugin = require('terser-webpack-plugin') const TerserPlugin = require('terser-webpack-plugin')
const merge = require('webpack-merge') const { merge } = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf') const baseWebpackConfig = require('./webpack.base.conf')
module.exports = merge(baseWebpackConfig, { module.exports = merge(baseWebpackConfig, {
@@ -14,10 +14,7 @@ module.exports = merge(baseWebpackConfig, {
vue: 'vue', vue: 'vue',
}, },
optimization: { optimization: {
minimizer: [ minimize: true,
new TerserPlugin({ minimizer: [new TerserPlugin()],
sourceMap: true,
}),
],
}, },
}) })
@@ -0,0 +1,31 @@
<template>
<div style="background: #282c34; padding: 1rem; border-radius: 0.3rem">
<v-select :options="countries" />
</div>
</template>
<script>
import countries from '../data/countries.js'
export default {
data: () => ({ countries }),
}
</script>
<style scoped>
>>> {
--vs-controls-color: #664cc3;
--vs-border-color: #664cc3;
--vs-dropdown-bg: #282c34;
--vs-dropdown-color: #cc99cd;
--vs-dropdown-option-color: #cc99cd;
--vs-selected-bg: #664cc3;
--vs-selected-color: #eeeeee;
--vs-search-input-color: #eeeeee;
--vs-dropdown-option--active-bg: #664cc3;
--vs-dropdown-option--active-color: #eeeeee;
}
</style>
@@ -0,0 +1,20 @@
<template>
<v-select
v-model="selected"
:reduce="(option) => option.id"
:options="[
{ label: 'One', id: 1 },
{ label: 'Two', id: 2 },
]"
/>
</template>
<script>
export default {
data() {
return {
selected: 3,
}
},
}
</script>
+48 -17
View File
@@ -1,17 +1,47 @@
Vue Select offers many APIs for customizing the look and feel from the component. You can use Vue Select offers many APIs for customizing the look and feel from the component. You can use
[scoped slots](../api/slots.md), [custom child components](components.md), or modify the built in [scoped slots](../api/slots.md), [custom child components](components.md), or modify the built in
SCSS variables. CSS properties.
::: tip ## CSS Variables
Support for CSS variables (custom properties) is currently on the road map for those
that are not using sass in their projects.
:::
## SCSS Variables Vue Select uses custom CSS properties throughout the component to handle visual opinions. This
allows for quite a bit of flexibility in styling, without having to hook into a build system for
generating your own styles with something like SASS. If there is a value that you think should use a
CSS property instead of a hardcoded CSS value, please submit a PR.
## Dark Mode Example
Without writing any CSS yourself, you can completely customize the look and feel of Vue Select
through the use of CSS variables. In this example, we adjust the colors of the component to match
for a dark mode application.
In this case, the variables are scoped to only this implementation of the component, but you could
place these variables anywhere in your applications CSS file to implement at a global level for your
app.
Check the MDN docs for more info
about [CSS Custom Properties.](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties)
<CssVariables style="margin-top: 1rem;" />
<<< @/.vuepress/components/CssVariables.vue
### Available CSS Variables <Badge type="primary">3.18+</Badge>
<<< @/../src/css/global/variables.css
## SCSS <Badge type="warning">Deprecated in v3.18</Badge>
::: warning Deprecation Notice
The SCSS build been deprecated for the `v3.x` release, and will be
removed in `v4.0.0`. The files will remain in the v3 codebase if you really need them, but the
recommended approach is to leverage the included CSS variables instead.
:::
Variables are leveraged in as much of the component styles as possible. If you really want to dig Variables are leveraged in as much of the component styles as possible. If you really want to dig
into the SCSS, the files are located in `src/scss`. The variables listed below can be found at into the SCSS, the files are located in `src/scss`. The variables listed below can be found at
[`src/scss/global/_variables`](https://github.com/sagalbot/vue-select/blob/master/src/scss/global/_variables.scss). [`src/scss/global/_variables`](https://github.com/sagalbot/vue-select/blob/master/src/scss/global/_variables.scss)
.
All variables are implemented with `!default` in order to make them easier to override in your All variables are implemented with `!default` in order to make them easier to override in your
application. application.
@@ -23,12 +53,12 @@ application.
Vue Select takes the approach of using selectors with a single level of specificity, while using Vue Select takes the approach of using selectors with a single level of specificity, while using
classes that are very specific to Vue Select to avoid collisions with your app. classes that are very specific to Vue Select to avoid collisions with your app.
All classes within Vue Select use the `vs__` prefix, and selectors are generally a single classname Most classes within Vue Select use the `vs__` prefix, and selectors are generally a single classname
unless there is a state being applied to the component. unless there is a state being applied to the component.
In order to override a default property in your app, you should add one level of specificity. In order to override a default property in your app, you should add one level of specificity. The
The easiest way to do this, is to add `.v-select` before the `vs__*` selector if you want to adjust easiest way to do this, is to add `.v-select` before the `vs__*` selector if you want to adjust all
all instances of Vue Select, or add your own classname if you just want to affect one. instances of Vue Select, or add your own classname if you just want to affect one.
<CssSpecificity /> <CssSpecificity />
@@ -38,17 +68,18 @@ all instances of Vue Select, or add your own classname if you just want to affec
By default, the dropdown transitions with a `.15s` cubic-bezier opacity fade in/out. The component By default, the dropdown transitions with a `.15s` cubic-bezier opacity fade in/out. The component
uses the [VueJS transition system](https://vuejs.org/v2/guide/transitions.html). By default, the uses the [VueJS transition system](https://vuejs.org/v2/guide/transitions.html). By default, the
transition name is `vs__fade`. There's a couple ways to override or change this transition. transition name is `vs__fade`. There's a couple ways to override or change this transition.
1. Use the `transition` prop. Applying this prop will change the name of the animation classes and 1. Use the `transition` prop. Applying this prop will change the name of the animation classes and
negate the default CSS. If you want to remove it entirely, you can set it to an empty string. negate the default CSS. If you want to remove it entirely, you can set it to an empty string.
```html ```html
<v-select transition="" /> <v-select transition="" />
``` ```
2. You can also override the default CSS for the `vs__fade` transition. Again, if you 2. You can also override the default CSS for the `vs__fade` transition. Again, if you wanted to
wanted to eliminate the transition entirely: eliminate the transition entirely:
```css ```css
.vs__fade-enter-active, .vs__fade-enter-active,
+9 -9
View File
@@ -1,8 +1,14 @@
## Yarn / NPM ## Yarn / NPM
Install with yarn:
Install with yarn or npm:
```bash ```bash
# vue 2
yarn add vue-select yarn add vue-select
# vue 3
yarn add vue-select@beta
# or, using NPM # or, using NPM
npm install vue-select npm install vue-select
``` ```
@@ -22,12 +28,6 @@ The component itself does not include any CSS. You'll need to include it separat
import 'vue-select/dist/vue-select.css'; import 'vue-select/dist/vue-select.css';
``` ```
Alternatively, you can import the scss for complete control of the component styles:
```scss
@import "vue-select/src/scss/vue-select.scss";
```
## In the Browser ## In the Browser
vue-select ships as an UMD module that is accessible in the browser. When loaded vue-select ships as an UMD module that is accessible in the browser. When loaded
@@ -56,5 +56,5 @@ Vue.component('v-select', VueSelect.VueSelect);
## Vue Compatibility ## Vue Compatibility
- If you're on Vue `1.x`, use vue-select `1.x`. - Vue `2.x`, use vue-select `3.x`.
- The `1.x` branch has not received updates since the 2.0 release. - Vue `3.x`, use vue-select `3.x@beta`.
+92 -59
View File
@@ -2,11 +2,12 @@
### `v-model` ### `v-model`
The most common use case for vue-select is to have the chosen value synced with a parent component. vue-select The most common use case for vue-select is to have the chosen value synced with a parent component.
takes advantage of the `v-model` syntax to sync values with a parent. The `v-model` syntax works with vue-select takes advantage of the `v-model` syntax to sync values with a parent. The `v-model`
primitives and objects. syntax works with primitives and objects.
```html ```html
<v-select v-model="selected" /> <v-select v-model="selected" />
``` ```
@@ -14,25 +15,26 @@ Note that when using the `multiple` prop, the `v-model` value will always be an
### Props and Events ### Props and Events
Sometimes `v-model` might not fit your use case. For example, when working with [Vuex](https://vuex.vuejs.org), Sometimes `v-model` might not fit your use case. For example, when working
you'll need to trigger a mutation rather than mutating a value directly. In that case, maybe you need with [Vuex](https://vuex.vuejs.org), you'll need to trigger a mutation rather than mutating a value
to bind a pre-selected value, and trigger a mutation when it changes. directly. In that case, maybe you need to bind a pre-selected value, and trigger a mutation when it
changes.
vue-select exposes the `value` prop and an `input` event to enable this. This combo of props and vue-select exposes the `value` prop and an `input` event to enable this. This combo of props and
events is also how Vue wires up the `v-model` syntax internally. events is also how Vue wires up the `v-model` syntax internally.
#### Prop: `value` #### Prop: `value`
The `value` prop lets vue-select know what value is currently selected. It will accept strings, The `value` prop lets vue-select know what value is currently selected. It will accept strings,
numbers or objects. If you're using a `multiple` v-select, you'll want to pass an array. numbers or objects. If you're using a `multiple` v-select, you'll want to pass an array.
```html ```html
<v-select :value="selected" /> <v-select :value="selected" />
``` ```
::: tip 🤓 ::: tip 🤓 Anytime you bind the `value` prop directly, you're responsible for updating the bound
Anytime you bind the `value` prop directly, you're responsible for updating the bound variable variable in your code using the `@input` event.
in your code using the `@input` event.
::: :::
#### Event: `input` #### Event: `input`
@@ -42,50 +44,66 @@ state as it's only parameter.
#### Vuex Support #### Vuex Support
The `value` prop and `emit` event are very useful when using a state management tool, like Vuex. The `value` prop and `emit` event are very useful when using a state management tool, like Vuex. You
You can bind the selected value with `:value="$store.myValue"`, and use the `input` event to can bind the selected value with `:value="$store.myValue"`, and use the `input` event to trigger a
trigger a mutation, or dispatch an action or anything else you might need to do when the selection mutation, or dispatch an action or anything else you might need to do when the selection changes.
changes.
```html ```html
<v-select :value="$store.myValue" @input="setSelected" /> <v-select :value="$store.myValue" @input="setSelected" />
``` ```
```js ```js
methods: { methods: {
setSelected(value) { setSelected(value)
// trigger a mutation, or dispatch an action {
} // trigger a mutation, or dispatch an action
}
} }
``` ```
## Single/Multiple
By default, vue-select supports choosing a single value. If you need multiple values, use the
`multiple` boolean prop, much the same way you would on an HTML `<select>` element. When `multiple`
is true, `v-model` and `value` must be an array.
```html
<v-select multiple v-model="selected" :options="['Canada','United States']" />
```
<v-select multiple :options="['Canada','United States']" />
## Transforming Selections ## Transforming Selections
When the `options` array contains objects, vue-select returns the whole object as dropdown value When the `options` array contains objects, vue-select returns the whole object as dropdown value
upon selection. This approach makes no assumptions about the data you need, and provides a lot of upon selection. This approach makes no assumptions about the data you need, and provides a lot of
flexibility. However, there will be situations where maybe you just need to return a single key flexibility. However, there will be situations where you just need to return a single key from an
from an object. object.
### Returning a single key with `reduce` ### Returning a single key with `reduce`
If you need to return a single key, or transform the selection before it is synced, vue-select If you need to return a single key, or transform the selection before it is synced, vue-select
provides a `reduce` callback that allows you to transform a selected option before it is passed to provides a `reduce` callback that allows you to transform a selected option before it is passed to
the `@input` event. Consider this data structure: the `@input` event. Consider this data structure:
```js ```js
let options = [{code: 'CA', country: 'Canada'}]; let options = [{code: 'CA', country: 'Canada'}];
``` ```
If we want to display the `country`, but return the `code` to `v-model`, we can use the `reduce` If we want to display the `country`, but return the `code` to `v-model`, we can use the `reduce`
prop to receive only the data that's required. prop to receive only the data that's required.
```html ```html
<v-select :options="options" :reduce="country => country.code" label="country" />
<v-select :options="options" :reduce="country => country.code" label="country" />
``` ```
### Deep Nested Values ### Deep Nested Values
The `reduce` property also works well when you have a deeply nested value: The `reduce` property also works well when you have a deeply nested value:
``` ```
{ {
country: 'canada', country: 'canada',
@@ -95,30 +113,42 @@ The `reduce` property also works well when you have a deeply nested value:
} }
} }
``` ```
```html ```html
<v-select :options="options" :reduce="country => country.meta.code" label="country" />
<v-select :options="options" :reduce="country => country.meta.code" label="country" />
``` ```
<reducer-nested-value /> <reducer-nested-value />
## Single/Multiple Selection ## Caveats with `reduce`
By default, vue-select supports choosing a single value. If you need multiple values, use the The most common issue with `reduce` is when the component displays your _reduced_ _value_ instead of
`multiple` boolean prop, much the same way you would on an HTML `<select>` element. When `multiple` it's _label_. This happens when you supply Vue Select a `value` or `v-model` binding with a reduced_
is true, `v-model` and `value` must be an array. value, but the complete option object is not present in the `options` array.
```html <ReducedWithNoMatchingOption />
<v-select multiple v-model="selected" :options="['Canada','United States']" />
``` <<< @/.vuepress/components/ReducedWithNoMatchingOption.vue
<v-select multiple :options="['Canada','United States']" />
In the example above, the component was supplied with an ID that doesn't exist in the `options`
array. When `value` changes, Vue Select searches the supplied options, running each one
through `reduce` until the corresponding option is found. When that option doesn't exist, Vue Select
will end up displaying the `value` supplied.
::: warning
When providing Vue Select with a _reduced_ `value` - the object that the value was reduced from must
exist in the `options` array.
:::
## Tagging ## Tagging
To allow input that's not present within the options, set the `taggable` prop to true. To allow input that's not present within the options, set the `taggable` prop to true.
```html ```html
<v-select taggable multiple /> <v-select taggable multiple />
``` ```
@@ -127,6 +157,7 @@ To allow input that's not present within the options, set the `taggable` prop to
If you want added tags to be pushed to the options array, set `push-tags` to true. If you want added tags to be pushed to the options array, set `push-tags` to true.
```html ```html
<v-select taggable multiple push-tags /> <v-select taggable multiple push-tags />
``` ```
@@ -136,10 +167,11 @@ If you want added tags to be pushed to the options array, set `push-tags` to tru
When combining `taggable` with `reduce`, you must define the `createOption` prop. The When combining `taggable` with `reduce`, you must define the `createOption` prop. The
`createOption` function is responsible for defining the structure of the objects that Vue Select `createOption` function is responsible for defining the structure of the objects that Vue Select
will create for you when adding a tag. It should return a value that has the same properties as the will create for you when adding a tag. It should return a value that has the same properties as the
rest of your `options`. rest of your `options`.
If you don't define `createOption`, Vue Select will construct a simple object following this structure: If you don't define `createOption`, Vue Select will construct a simple object following this
structure:
`{[this.label]: searchText}`. If you're using `reduce`, this is probably not what your options look `{[this.label]: searchText}`. If you're using `reduce`, this is probably not what your options look
like, which is why you'll need to set the function yourself. like, which is why you'll need to set the function yourself.
@@ -147,28 +179,29 @@ like, which is why you'll need to set the function yourself.
We have a taggable select for adding books to a collection. We're just concerned about getting the We have a taggable select for adding books to a collection. We're just concerned about getting the
book title added, and our server side code will add the author details in a background process. The book title added, and our server side code will add the author details in a background process. The
user has already selected a book. user has already selected a book.
```js ```js
const options = [ const options = [
{ {
title: "HTML5", title: "HTML5",
author: { author: {
firstName: "Remy", firstName: "Remy",
lastName: "Sharp" lastName: "Sharp"
}
} }
}
]; ];
``` ```
```html ```html
<v-select <v-select
taggable taggable
multiple multiple
label="title" label="title"
:options="options" :options="options"
:create-option="book => ({ title: book, author: { firstName: '', lastName: '' } })" :create-option="book => ({ title: book, author: { firstName: '', lastName: '' } })"
:reduce="book => `${book.author.firstName} ${book.author.lastName}`" :reduce="book => `${book.author.firstName} ${book.author.lastName}`"
/> />
``` ```
-2
View File
@@ -22,9 +22,7 @@
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"fuse.js": "^6.4.0", "fuse.js": "^6.4.0",
"gh-pages": "^2.2.0", "gh-pages": "^2.2.0",
"node-sass": "^4.12.0",
"octonode": "^0.9.5", "octonode": "^0.9.5",
"sass-loader": "^8.0.2",
"vue": "^2.6.10", "vue": "^2.6.10",
"vuepress": "^1.4.0", "vuepress": "^1.4.0",
"vuex": "^3.1.0" "vuex": "^3.1.0"
+26 -574
View File
File diff suppressed because it is too large Load Diff
+22 -25
View File
@@ -13,7 +13,7 @@
"license": "MIT", "license": "MIT",
"prepare": "npm run build", "prepare": "npm run build",
"scripts": { "scripts": {
"serve": "webpack-dev-server --config build/webpack.dev.conf.js --hot --progress -d", "serve": "webpack-dev-server --config build/webpack.dev.conf.js --hot --progress",
"serve:docs": "cd docs && yarn serve", "serve:docs": "cd docs && yarn serve",
"build": "cross-env NODE_ENV=production webpack --config build/webpack.prod.conf.js --progress", "build": "cross-env NODE_ENV=production webpack --config build/webpack.prod.conf.js --progress",
"build:docs": "cd docs && yarn build", "build:docs": "cd docs && yarn build",
@@ -31,9 +31,6 @@
"peerDependencies": { "peerDependencies": {
"vue": "3.x" "vue": "3.x"
}, },
"resolutions": {
"ajv": "6.8.1"
},
"devDependencies": { "devDependencies": {
"@babel/core": "^7.4.0", "@babel/core": "^7.4.0",
"@babel/eslint-parser": "^7.14.7", "@babel/eslint-parser": "^7.14.7",
@@ -43,48 +40,48 @@
"@semantic-release/git": "^9.0.0", "@semantic-release/git": "^9.0.0",
"@semantic-release/github": "^7.0.4", "@semantic-release/github": "^7.0.4",
"@vue/compiler-sfc": "^3.2.20", "@vue/compiler-sfc": "^3.2.20",
"@vue/test-utils": "^2.0.0-rc.16", "@vue/test-utils": "^2.0.0-rc.17",
"autoprefixer": "^9.4.7", "autoprefixer": "^10.3.7",
"babel-core": "^7.0.0-bridge.0", "babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^10.0.3", "babel-eslint": "^10.0.3",
"babel-loader": "^8.0.5", "babel-loader": "^8.0.5",
"bundlewatch": "^0.2.5", "bundlewatch": "^0.2.5",
"chokidar": "^2.1.5",
"commitizen": "^4.0.3", "commitizen": "^4.0.3",
"coveralls": "^3.0.2", "coveralls": "^3.1.1",
"cross-env": "^5.2.0", "cross-env": "^5.2.0",
"css-loader": "^2.1.0", "css-loader": "^6.4.0",
"cssnano": "^4.1.10", "cssnano": "^5.0.8",
"cz-conventional-changelog": "3.1.0", "cz-conventional-changelog": "3.1.0",
"eslint": "^6.8.0", "eslint": "^6.8.0",
"eslint-config-prettier": "^6.10.0", "eslint-config-prettier": "^6.10.0",
"eslint-loader": "^3.0.3", "eslint-loader": "^3.0.3",
"eslint-plugin-prettier": "^3.1.2", "eslint-plugin-prettier": "^3.1.2",
"eslint-plugin-vue": "^6.2.1", "eslint-plugin-vue": "^6.2.1",
"html-loader": "^0.5.5", "html-loader": "^3.1.0",
"html-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^5.5.0",
"jest": "^24.1.0", "jest": "^24.1.0",
"jest-serializer-vue": "^2.0.2", "jest-serializer-vue": "^2.0.2",
"jest-transform-stub": "^2.0.0", "jest-transform-stub": "^2.0.0",
"mini-css-extract-plugin": "^0.5.0", "mini-css-extract-plugin": "^2.5.3",
"node-sass": "^4.12.0", "postcss": "^8.3.11",
"postcss-loader": "^3.0.0", "postcss-import": "^14.0.2",
"postcss-scss": "^2.0.0", "postcss-loader": "^6.2.1",
"postcss-nested": "^5.0.6",
"prettier": "2.2.1", "prettier": "2.2.1",
"sass-loader": "^7.1.0",
"semantic-release": "^17.0.4", "semantic-release": "^17.0.4",
"terser-webpack-plugin": "^1.2.3", "terser-webpack-plugin": "^5.2.4",
"url-loader": "^1.1.2", "url-loader": "^4.1.1",
"vue": "^3.2.20", "vue": "^3.2.20",
"vue-html-loader": "^1.2.4", "vue-html-loader": "^1.2.4",
"vue-jest": "5.0.0-alpha.8", "vue-jest": "5.0.0-alpha.8",
"vue-loader": "^16.8.1", "vue-loader": "^16.8.1",
"vue-server-renderer": "^2.6.10", "vue-server-renderer": "^2.6.10",
"vue-style-loader": "^4.1.2", "vue-style-loader": "^4.1.3",
"webpack": "^4.29.6", "vue-template-compiler": "^2.6.10",
"webpack-cli": "^3.3.0", "webpack": "^5.69.1",
"webpack-dev-server": "^3.2.1", "webpack-cli": "^4.9.2",
"webpack-merge": "^4.2.1" "webpack-dev-server": "^4.7.4",
"webpack-merge": "^5.8.0"
}, },
"jest": { "jest": {
"moduleFileExtensions": [ "moduleFileExtensions": [
@@ -134,7 +131,7 @@
{ {
"path": "./dist/vue-select.css", "path": "./dist/vue-select.css",
"compression": "none", "compression": "none",
"maxSize": "6 KB" "maxSize": "8 KB"
} }
] ]
} }
+2
View File
@@ -1,6 +1,8 @@
module.exports = { module.exports = {
plugins: [ plugins: [
require('postcss-import'),
require('autoprefixer'), require('autoprefixer'),
require('postcss-nested'),
require('cssnano')({ require('cssnano')({
preset: 'default', preset: 'default',
}), }),
+8 -9
View File
@@ -1,5 +1,5 @@
<style lang="scss"> <style>
@import '../scss/vue-select.scss'; @import '../css/vue-select.css';
</style> </style>
<template> <template>
@@ -33,8 +33,7 @@
</slot> </slot>
<button <button
v-if="multiple" v-if="multiple"
ref="deselectButtons" :ref="(el) => (deselectButtons[i] = el)"
:ref="el => deselectButtons[i] = el"
:disabled="disabled" :disabled="disabled"
type="button" type="button"
class="vs__deselect" class="vs__deselect"
@@ -119,9 +118,9 @@
</slot> </slot>
</li> </li>
<li v-if="filteredOptions.length === 0" class="vs__no-options"> <li v-if="filteredOptions.length === 0" class="vs__no-options">
<slot name="no-options" v-bind="scope.noOptions" <slot name="no-options" v-bind="scope.noOptions">
>Sorry, no matching options.</slot Sorry, no matching options.
> </slot>
</li> </li>
<slot name="list-footer" v-bind="scope.listFooter" /> <slot name="list-footer" v-bind="scope.listFooter" />
</ul> </ul>
@@ -694,7 +693,7 @@ export default {
pushedTags: [], pushedTags: [],
// eslint-disable-next-line vue/no-reserved-keys // eslint-disable-next-line vue/no-reserved-keys
_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
deselectButtons: [] deselectButtons: [],
} }
}, },
@@ -723,7 +722,7 @@ export default {
value = this.$data._value value = this.$data._value
} }
if (value) { if (value !== undefined && value !== null) {
return [].concat(value) return [].concat(value)
} }
+35
View File
@@ -0,0 +1,35 @@
:root {
--vs-transition-timing-function: cubic-bezier(1, 0.5, 0.8, 1);
--vs-transition-duration: 0.15s;
}
/* KeyFrames */
@-webkit-keyframes vSelectSpinner {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@keyframes vSelectSpinner {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* Dropdown Default Transition */
.vs__fade-enter-active,
.vs__fade-leave-active {
pointer-events: none;
transition: opacity var(--vs-transition-duration)
var(--vs-transition-timing-function);
}
.vs__fade-enter,
.vs__fade-leave-to {
opacity: 0;
}
+9
View File
@@ -0,0 +1,9 @@
.v-select {
position: relative;
font-family: inherit;
}
.v-select,
.v-select * {
box-sizing: border-box;
}
+55
View File
@@ -0,0 +1,55 @@
/** Component States */
/*
* Disabled
*
* When the component is disabled, all interaction
* should be prevented. Here we modify the bg color,
* and change the cursor displayed on the interactive
* components.
*/
:root {
--vs-disabled-bg: var(--vs-state-disabled-bg);
--vs-disabled-color: var(--vs-state-disabled-color);
--vs-disabled-cursor: var(--vs-state-disabled-cursor);
}
.vs--disabled {
.vs__dropdown-toggle,
.vs__clear,
.vs__search,
.vs__selected,
.vs__open-indicator {
cursor: var(--vs-disabled-cursor);
background-color: var(--vs-disabled-bg);
}
}
/*
* RTL - Right to Left Support
*
* Because we're using a flexbox layout, the `dir="rtl"`
* HTML attribute does most of the work for us by
* rearranging the child elements visually.
*/
.v-select[dir='rtl'] {
.vs__actions {
padding: 0 3px 0 6px;
}
.vs__clear {
margin-left: 6px;
margin-right: 0;
}
.vs__deselect {
margin-left: 0;
margin-right: 2px;
}
.vs__dropdown-menu {
text-align: right;
}
}
+66
View File
@@ -0,0 +1,66 @@
:root {
--vs-colors--lightest: rgba(60, 60, 60, 0.26);
--vs-colors--light: rgba(60, 60, 60, 0.5);
--vs-colors--dark: #333;
--vs-colors--darkest: rgba(0, 0, 0, 0.15);
/* Search Input */
--vs-search-input-color: inherit;
--vs-search-input-placeholder-color: inherit;
/* Font */
--vs-font-size: 1rem;
--vs-line-height: 1.4;
/* Disabled State */
--vs-state-disabled-bg: rgb(248, 248, 248);
--vs-state-disabled-color: var(--vs-colors--light);
--vs-state-disabled-controls-color: var(--vs-colors--light);
--vs-state-disabled-cursor: not-allowed;
/* Borders */
--vs-border-color: var(--vs-colors--lightest);
--vs-border-width: 1px;
--vs-border-style: solid;
--vs-border-radius: 4px;
/* Actions: house the component controls */
--vs-actions-padding: 4px 6px 0 3px;
/* Component Controls: Clear, Open Indicator */
--vs-controls-color: var(--vs-colors--light);
--vs-controls-size: 1;
--vs-controls--deselect-text-shadow: 0 1px 0 #fff;
/* Selected */
--vs-selected-bg: #f0f0f0;
--vs-selected-color: var(--vs-colors--dark);
--vs-selected-border-color: var(--vs-border-color);
--vs-selected-border-style: var(--vs-border-style);
--vs-selected-border-width: var(--vs-border-width);
/* Dropdown */
--vs-dropdown-bg: #fff;
--vs-dropdown-color: inherit;
--vs-dropdown-z-index: 1000;
--vs-dropdown-min-width: 160px;
--vs-dropdown-max-height: 350px;
--vs-dropdown-box-shadow: 0px 3px 6px 0px var(--vs-colors--darkest);
/* Options */
--vs-dropdown-option-bg: #000;
--vs-dropdown-option-color: var(--vs-dropdown-color);
--vs-dropdown-option-padding: 3px 20px;
/* Active State */
--vs-dropdown-option--active-bg: #5897fb;
--vs-dropdown-option--active-color: #fff;
/* Deselect State */
--vs-dropdown-option--deselect-bg: #fb5858;
--vs-dropdown-option--deselect-color: #fff;
/* Transitions */
--vs-transition-timing-function: cubic-bezier(1, -0.115, 0.975, 0.855);
--vs-transition-duration: 150ms;
}
+10
View File
@@ -0,0 +1,10 @@
/* Clear Button */
.vs__clear {
fill: var(--vs-controls-color);
padding: 0;
border: 0;
background-color: transparent;
cursor: pointer;
margin-right: 8px;
}
+29
View File
@@ -0,0 +1,29 @@
/* Dropdown Menu */
.vs__dropdown-menu {
display: block;
box-sizing: border-box;
position: absolute;
/* calc to ensure the left and right borders of the dropdown appear flush with the toggle. */
top: calc(100% - var(--vs-border-width));
left: 0;
z-index: var(--vs-dropdown-z-index);
padding: 5px 0;
margin: 0;
width: 100%;
max-height: var(--vs-dropdown-max-height);
min-width: var(--vs-dropdown-min-width);
overflow-y: auto;
box-shadow: var(--vs-dropdown-box-shadow);
border: var(--vs-border-width) var(--vs-border-style) var(--vs-border-color);
border-top-style: none;
border-radius: 0 0 var(--vs-border-radius) var(--vs-border-radius);
text-align: left;
list-style: none;
background: var(--vs-dropdown-bg);
color: var(--vs-dropdown-color);
}
.vs__no-options {
text-align: center;
}
+26
View File
@@ -0,0 +1,26 @@
/* List Items */
.vs__dropdown-option {
line-height: 1.42857143; /* Normalize line height */
display: block;
padding: var(--vs-dropdown-option-padding);
clear: both;
color: var(--vs-dropdown-option-color); /* Overrides most CSS frameworks */
white-space: nowrap;
cursor: pointer;
}
.vs__dropdown-option--highlight {
background: var(--vs-dropdown-option--active-bg);
color: var(--vs-dropdown-option--active-color);
}
.vs__dropdown-option--deselect {
background: var(--vs-dropdown-option--deselect-bg);
color: var(--vs-dropdown-option--deselect-color);
}
.vs__dropdown-option--disabled {
background: var(--vs-state-disabled-bg);
color: var(--vs-state-disabled-color);
cursor: var(--vs-state-disabled-cursor);
}
+49
View File
@@ -0,0 +1,49 @@
/**
Dropdown Toggle
The dropdown toggle is the primary wrapper of the component. It
has two direct descendants: .vs__selected-options, and .vs__actions.
.vs__selected-options holds the .vs__selected's as well as the
main search input.
.vs__actions holds the clear button and dropdown toggle.
*/
.vs__dropdown-toggle {
appearance: none;
display: flex;
padding: 0 0 4px 0;
background: none;
border: var(--vs-border-width) var(--vs-border-style) var(--vs-border-color);
border-radius: var(--vs-border-radius);
white-space: normal;
}
.vs__selected-options {
display: flex;
flex-basis: 100%;
flex-grow: 1;
flex-wrap: wrap;
padding: 0 2px;
position: relative;
}
.vs__actions {
display: flex;
align-items: center;
padding: var(--vs-actions-padding);
}
/* Dropdown Toggle States */
.vs--searchable .vs__dropdown-toggle {
cursor: text;
}
.vs--unsearchable .vs__dropdown-toggle {
cursor: pointer;
}
.vs--open .vs__dropdown-toggle {
border-bottom-color: transparent;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
+26
View File
@@ -0,0 +1,26 @@
/* Open Indicator */
/*
The open indicator appears as a down facing
caret on the right side of the select.
*/
.vs__open-indicator {
fill: var(--vs-controls-color);
transform: scale(var(--vs-controls-size));
transition: transform var(--vs-transition-duration)
var(--vs-transition-timing-function);
transition-timing-function: var(--vs-transition-timing-function);
}
/* Open State */
.vs--open .vs__open-indicator {
transform: rotate(180deg) scale(var(--vs-controls-size));
}
/* Loading State */
.vs--loading .vs__open-indicator {
opacity: 0;
}
+61
View File
@@ -0,0 +1,61 @@
/* Search Input */
/**
* Super weird bug... If this declaration is grouped
* below, the cancel button will still appear in chrome.
* If it's up here on it's own, it'll hide it.
*/
.vs__search::-webkit-search-cancel-button {
display: none;
}
.vs__search::-webkit-search-decoration,
.vs__search::-webkit-search-results-button,
.vs__search::-webkit-search-results-decoration,
.vs__search::-ms-clear {
display: none;
}
.vs__search,
.vs__search:focus {
color: var(--vs-search-input-color);
appearance: none;
line-height: var(--vs-line-height);
font-size: var(--vs-font-size);
border: 1px solid transparent;
border-left: none;
outline: none;
margin: 4px 0 0 0;
padding: 0 7px;
background: none;
box-shadow: none;
width: 0;
max-width: 100%;
flex-grow: 1;
z-index: 1;
}
.vs__search::placeholder {
color: var(--vs-search-input-placeholder-color);
}
/**
States
*/
/* Unsearchable */
.vs--unsearchable {
.vs__search {
opacity: 1;
}
&:not(.vs--disabled) .vs__search {
cursor: pointer;
}
}
/* Single, when searching but not loading or open */
.vs--single.vs--searching:not(.vs--open):not(.vs--loading) {
.vs__search {
opacity: 0.2;
}
}
+43
View File
@@ -0,0 +1,43 @@
/* Selected Tags */
.vs__selected {
display: flex;
align-items: center;
background-color: var(--vs-selected-bg);
border: var(--vs-selected-border-width) var(--vs-selected-border-style)
var(--vs-selected-border-color);
border-radius: var(--vs-border-radius);
color: var(--vs-selected-color);
line-height: var(--vs-line-height);
margin: 4px 2px 0px 2px;
padding: 0 0.25em;
z-index: 0;
}
.vs__deselect {
display: inline-flex;
appearance: none;
margin-left: 4px;
padding: 0;
border: 0;
cursor: pointer;
background: none;
fill: var(--vs-controls-color);
text-shadow: var(--vs-controls--deselect-text-shadow);
}
/* States */
.vs--single {
.vs__selected {
background-color: transparent;
border-color: transparent;
}
&.vs--open .vs__selected,
&.vs--loading .vs__selected {
position: absolute;
opacity: 0.4;
}
&.vs--searching .vs__selected {
display: none;
}
}
+28
View File
@@ -0,0 +1,28 @@
/* Loading Spinner */
.vs__spinner {
align-self: center;
opacity: 0;
font-size: 5px;
text-indent: -9999em;
overflow: hidden;
border-top: 0.9em solid rgba(100, 100, 100, 0.1);
border-right: 0.9em solid rgba(100, 100, 100, 0.1);
border-bottom: 0.9em solid rgba(100, 100, 100, 0.1);
border-left: 0.9em solid rgba(60, 60, 60, 0.45);
transform: translateZ(0)
scale(var(--vs-controls--spinner-size, var(--vs-controls-size)));
animation: vSelectSpinner 1.1s infinite linear;
transition: opacity 0.1s;
}
.vs__spinner,
.vs__spinner:after {
border-radius: 50%;
width: 5em;
height: 5em;
transform: scale(var(--vs-controls--spinner-size, var(--vs-controls-size)));
}
/* Loading Spinner States */
.vs--loading .vs__spinner {
opacity: 1;
}
+13
View File
@@ -0,0 +1,13 @@
@import 'global/variables.css';
@import 'global/component.css';
@import 'global/animations.css';
@import 'global/states.css';
@import 'modules/dropdown-toggle.css';
@import 'modules/open-indicator.css';
@import 'modules/clear.css';
@import 'modules/dropdown-menu.css';
@import 'modules/dropdown-option.css';
@import 'modules/selected.css';
@import 'modules/search-input.css';
@import 'modules/spinner.css';
+23 -1
View File
@@ -14,6 +14,16 @@ export default {
} }
} }
}, },
open(open) {
if (open) {
this.typeAheadToLastSelected()
}
},
selectedValue() {
if (this.open) {
this.typeAheadToLastSelected()
}
},
}, },
methods: { methods: {
@@ -57,9 +67,21 @@ export default {
typeAheadSelect() { typeAheadSelect() {
const typeAheadOption = this.filteredOptions[this.typeAheadPointer] const typeAheadOption = this.filteredOptions[this.typeAheadPointer]
if (typeAheadOption) { if (typeAheadOption && this.selectable(typeAheadOption)) {
this.select(typeAheadOption) this.select(typeAheadOption)
} }
}, },
/**
* Moves the pointer to the last selected option.
*/
typeAheadToLastSelected() {
this.typeAheadPointer =
this.selectedValue.length !== 0
? this.filteredOptions.indexOf(
this.selectedValue[this.selectedValue.length - 1]
)
: -1
},
}, },
} }
+5 -4
View File
@@ -1,11 +1,12 @@
$vs-colors: ( $vs-colors: (
lightest: rgba(60, 60, 60, 0.26), lightest: rgba(60, 60, 60, 0.26),
light: rgba(60, 60, 60, 0.5), light: rgba(60, 60, 60, 0.5),
dark: #333, dark: #333,
darkest: rgba(0, 0, 0, .15), darkest: rgba(0, 0, 0, 0.15),
) !default; ) !default;
// Global Component Variables // Global Component Variables
$vs-component-bg: none !default;
$vs-component-line-height: 1.4 !default; $vs-component-line-height: 1.4 !default;
$vs-component-placeholder-color: inherit !default; $vs-component-placeholder-color: inherit !default;
+21 -21
View File
@@ -16,39 +16,39 @@ $border-color: $vs-border-color;
$border-radius: $vs-border-radius; $border-radius: $vs-border-radius;
.vs__dropdown-toggle { .vs__dropdown-toggle {
appearance: none; appearance: none;
display: flex; display: flex;
padding: 0 0 4px 0; padding: 0 0 4px 0;
background: none; background: $vs-component-bg;
border: $border-width $border-style $border-color; border: $border-width $border-style $border-color;
border-radius: $border-radius; border-radius: $border-radius;
white-space: normal; white-space: normal;
} }
.vs__selected-options { .vs__selected-options {
display: flex; display: flex;
flex-basis: 100%; flex-basis: 100%;
flex-grow: 1; flex-grow: 1;
flex-wrap: wrap; flex-wrap: wrap;
padding: 0 2px; padding: 0 2px;
position: relative; position: relative;
} }
.vs__actions { .vs__actions {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 4px 6px 0 3px; padding: 4px 6px 0 3px;
} }
/* Dropdown Toggle States */ /* Dropdown Toggle States */
.vs--searchable .vs__dropdown-toggle { .vs--searchable .vs__dropdown-toggle {
cursor: text; cursor: text;
} }
.vs--unsearchable .vs__dropdown-toggle { .vs--unsearchable .vs__dropdown-toggle {
cursor: pointer; cursor: pointer;
} }
.vs--open .vs__dropdown-toggle { .vs--open .vs__dropdown-toggle {
border-bottom-color: transparent; border-bottom-color: transparent;
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
} }
+22 -12
View File
@@ -1,13 +1,23 @@
@import "global/variables"; /**
@import "global/component"; Support for SASS is deprecated as of v3.18.
@import "global/animations";
@import "global/states";
@import "modules/dropdown-toggle"; The files remain here if your build is dependent on them
@import "modules/open-indicator"; but they will not receive updates in future releases. All
@import "modules/clear"; SASS variables have been translated into CSS variables, so
@import "modules/dropdown-menu"; migration should be quite simple if you'd like to move over.
@import "modules/dropdown-option";
@import "modules/selected"; In v4, these files will be removed.
@import "modules/search-input"; */
@import "modules/spinner"; @import 'global/variables';
@import 'global/component';
@import 'global/animations';
@import 'global/states';
@import 'modules/dropdown-toggle';
@import 'modules/open-indicator';
@import 'modules/clear';
@import 'modules/dropdown-menu';
@import 'modules/dropdown-option';
@import 'modules/selected';
@import 'modules/search-input';
@import 'modules/spinner';
+14 -2
View File
@@ -9,11 +9,23 @@ import Vue from 'vue'
* @param Wrapper {Wrapper<Vue>} * @param Wrapper {Wrapper<Vue>}
* @param searchText * @param searchText
*/ */
export const searchSubmit = (Wrapper, searchText = false) => { export const searchSubmit = async (Wrapper, searchText = undefined) => {
await Wrapper.get('input').trigger('focus')
if (searchText) { if (searchText) {
Wrapper.vm.search = searchText Wrapper.vm.search = searchText
await Wrapper.vm.$nextTick()
} }
Wrapper.get('input').trigger('keydown.enter')
await Wrapper.get('input').trigger('keydown.enter')
}
/**
* Focus the search input
*/
export const searchFocus = async (Wrapper) => {
await Wrapper.get('input').trigger('focus')
await Wrapper.vm.$nextTick()
} }
/** /**
+7 -3
View File
@@ -4,7 +4,7 @@ describe('Search Slot Scope', () => {
/** /**
* @see https://www.w3.org/WAI/PF/aria/states_and_properties#aria-activedescendant * @see https://www.w3.org/WAI/PF/aria/states_and_properties#aria-activedescendant
*/ */
describe('aria-activedescendant', () => { fdescribe('aria-activedescendant', () => {
it('adds the active descendant attribute only when the dropdown is open and there is a typeAheadPointer value', async () => { it('adds the active descendant attribute only when the dropdown is open and there is a typeAheadPointer value', async () => {
const Select = mountDefault() const Select = mountDefault()
@@ -21,7 +21,11 @@ describe('Search Slot Scope', () => {
}) })
it("adds the active descendant attribute when there's a typeahead value and an open dropdown", async () => { it("adds the active descendant attribute when there's a typeahead value and an open dropdown", async () => {
const Select = mountDefault() const Select = mountDefault({ modelValue: 'three' }, [
'one',
'two',
'three',
])
Select.vm.open = true Select.vm.open = true
Select.vm.typeAheadPointer = 1 Select.vm.typeAheadPointer = 1
@@ -29,7 +33,7 @@ describe('Search Slot Scope', () => {
expect( expect(
Select.vm.scope.search.attributes['aria-activedescendant'] Select.vm.scope.search.attributes['aria-activedescendant']
).toEqual(`vs${Select.vm.uid}__option-1`) ).toEqual(`vs${Select.vm.uid}__option-2`)
}) })
}) })
}) })
+1 -1
View File
@@ -1,4 +1,4 @@
import { searchSubmit, selectTag, selectWithProps } from '../helpers' import { selectTag, selectWithProps } from '../helpers'
describe('CreateOption When Tagging Is Enabled', () => { describe('CreateOption When Tagging Is Enabled', () => {
it('can select the current search text as a string', async () => { it('can select the current search text as a string', async () => {
+16 -1
View File
@@ -1,4 +1,4 @@
import { selectWithProps } from '../helpers' import { searchSubmit, selectWithProps } from '../helpers'
describe('Selectable prop', () => { describe('Selectable prop', () => {
it('should select selectable option if clicked', async () => { it('should select selectable option if clicked', async () => {
@@ -54,4 +54,19 @@ describe('Selectable prop', () => {
expect(Select.vm.typeAheadPointer).toEqual(0) expect(Select.vm.typeAheadPointer).toEqual(0)
}) })
it('should not let the user select an unselectable option with return', async () => {
const Select = selectWithProps({
options: ['one', 'two'],
multiple: true,
selectable: (option) => option !== 'two',
})
// this sets the typeAheadPointer to 0
await searchSubmit(Select, 'one')
expect(Select.vm.selectedValue).toEqual(['one'])
await searchSubmit(Select, 'two')
expect(Select.vm.selectedValue).toEqual(['one'])
})
}) })
+17
View File
@@ -218,6 +218,23 @@ describe('VS - Selecting Values', () => {
expect(Select.vm.selectedValue).toEqual(options) expect(Select.vm.selectedValue).toEqual(options)
}) })
fit('can select a false boolean option', async () => {
const Select = mountDefault({
options: [false],
})
expect(Select.vm.isOptionSelected(false)).toBeFalsy()
expect(Select.vm.optionExists(false)).toBeTruthy()
Select.vm.open = true
await Select.vm.$nextTick()
Select.find('.vs__dropdown-option').trigger('click')
await Select.vm.$nextTick()
expect(Select.vm.selectedValue).toEqual([false])
})
describe('input Event', () => { describe('input Event', () => {
it('will trigger the input event when the selection changes', () => { it('will trigger the input event when the selection changes', () => {
const Select = shallowMount(VueSelect) const Select = shallowMount(VueSelect)
+2 -8
View File
@@ -165,10 +165,7 @@ describe('When Tagging Is Enabled', () => {
options: [{ label: 'one' }, two], options: [{ label: 'one' }, two],
}) })
Select.vm.search = 'two' await searchSubmit(Select, 'two')
await Select.vm.$nextTick()
searchSubmit(Select)
expect(Select.vm.selectedValue).toEqual([two]) expect(Select.vm.selectedValue).toEqual([two])
}) })
@@ -180,10 +177,7 @@ describe('When Tagging Is Enabled', () => {
options: [{ label: 'one' }, two], options: [{ label: 'one' }, two],
}) })
Select.vm.search = 'two' await searchSubmit(Select, 'two')
await Select.vm.$nextTick()
searchSubmit(Select)
expect(Select.vm.selectedValue).toEqual([two]) expect(Select.vm.selectedValue).toEqual([two])
}) })
+33
View File
@@ -44,4 +44,37 @@ describe('Moving the Typeahead Pointer', () => {
Select.vm.typeAheadDown() Select.vm.typeAheadDown()
expect(Select.vm.typeAheadPointer).toEqual(2) expect(Select.vm.typeAheadPointer).toEqual(2)
}) })
it('will set the pointer to the selected option when opening', async () => {
const Select = shallowMount(VueSelect, {
propsData: {
modelValue: 'three',
options: ['one', 'two', 'three'],
},
})
Select.get('input').trigger('focus')
await Select.vm.$nextTick()
expect(Select.vm.typeAheadPointer).toEqual(2)
})
it('will set the pointer to the reduced selected option when opening', async () => {
const Select = shallowMount(VueSelect, {
propsData: {
modelValue: 3,
reduce: ({ value }) => value,
options: [
{ label: 'one', value: 1 },
{ label: 'two', value: 2 },
{ label: 'three', value: 3 },
],
},
})
Select.get('input').trigger('focus')
await Select.vm.$nextTick()
expect(Select.vm.typeAheadPointer).toEqual(2)
})
}) })
+1661 -2340
View File
File diff suppressed because it is too large Load Diff