diff --git a/.eslintrc b/.eslintrc
index 6ddab43..7f2ceaf 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -3,17 +3,183 @@
"html"
],
- "parserOptions": {
- "ecmaVersion": 6,
- "sourceType": "module"
- },
-
"env": {
"browser": true,
- "node": true,
- "es6": true,
- "jasmine": true
+ "node": true
},
- "extends": "standard"
+ "ecmaFeatures": {
+ "arrowFunctions": true,
+ "destructuring": true,
+ "classes": true,
+ "defaultParams": true,
+ "blockBindings": true,
+ "modules": true,
+ "objectLiteralComputedProperties": true,
+ "objectLiteralShorthandMethods": true,
+ "objectLiteralShorthandProperties": true,
+ "restParams": true,
+ "spread": true,
+ "forOf": true,
+ "generators": true,
+ "templateStrings": true,
+ "superInFunctions": true,
+ "experimentalObjectRestSpread": true
+ },
+
+ "rules": {
+ "accessor-pairs": 2,
+ "array-bracket-spacing": 0,
+ "block-scoped-var": 0,
+ "brace-style": [2, "1tbs", { "allowSingleLine": true }],
+ "camelcase": 0,
+ "comma-dangle": [2, "never"],
+ "comma-spacing": [2, { "before": false, "after": true }],
+ "comma-style": [2, "last"],
+ "complexity": 0,
+ "computed-property-spacing": 0,
+ "consistent-return": 0,
+ "consistent-this": 0,
+ "constructor-super": 2,
+ "curly": [2, "multi-line"],
+ "default-case": 0,
+ "dot-location": [2, "property"],
+ "dot-notation": 0,
+ "eol-last": 2,
+ "eqeqeq": [2, "allow-null"],
+ "func-names": 0,
+ "func-style": 0,
+ "generator-star-spacing": [2, { "before": true, "after": true }],
+ "guard-for-in": 0,
+ "handle-callback-err": [2, "^(err|error)$" ],
+ "indent": [2, 2, { "SwitchCase": 1 }],
+ "key-spacing": [2, { "beforeColon": false, "afterColon": true }],
+ "linebreak-style": 0,
+ "lines-around-comment": 0,
+ "max-nested-callbacks": 0,
+ "new-cap": [2, { "newIsCap": true, "capIsNew": false }],
+ "new-parens": 2,
+ "newline-after-var": 0,
+ "no-alert": 0,
+ "no-array-constructor": 2,
+ "no-caller": 2,
+ "no-catch-shadow": 0,
+ "no-cond-assign": 2,
+ "no-console": 0,
+ "no-constant-condition": 0,
+ "no-continue": 0,
+ "no-control-regex": 2,
+ "no-debugger": 2,
+ "no-delete-var": 2,
+ "no-div-regex": 0,
+ "no-dupe-args": 2,
+ "no-dupe-keys": 2,
+ "no-duplicate-case": 2,
+ "no-else-return": 0,
+ "no-empty": 0,
+ "no-empty-character-class": 2,
+ "no-empty-label": 2,
+ "no-eq-null": 0,
+ "no-eval": 2,
+ "no-ex-assign": 2,
+ "no-extend-native": 2,
+ "no-extra-bind": 2,
+ "no-extra-boolean-cast": 2,
+ "no-extra-parens": 0,
+ "no-extra-semi": 0,
+ "no-fallthrough": 2,
+ "no-floating-decimal": 2,
+ "no-func-assign": 2,
+ "no-implied-eval": 2,
+ "no-inline-comments": 0,
+ "no-inner-declarations": [2, "functions"],
+ "no-invalid-regexp": 2,
+ "no-irregular-whitespace": 2,
+ "no-iterator": 2,
+ "no-label-var": 2,
+ "no-labels": 2,
+ "no-lone-blocks": 2,
+ "no-lonely-if": 0,
+ "no-loop-func": 0,
+ "no-mixed-requires": 0,
+ "no-mixed-spaces-and-tabs": 2,
+ "no-multi-spaces": 2,
+ "no-multi-str": 2,
+ "no-multiple-empty-lines": [2, { "max": 1 }],
+ "no-native-reassign": 2,
+ "no-negated-in-lhs": 2,
+ "no-nested-ternary": 0,
+ "no-new": 2,
+ "no-new-func": 0,
+ "no-new-object": 2,
+ "no-new-require": 2,
+ "no-new-wrappers": 2,
+ "no-obj-calls": 2,
+ "no-octal": 2,
+ "no-octal-escape": 2,
+ "no-param-reassign": 0,
+ "no-path-concat": 0,
+ "no-process-env": 0,
+ "no-process-exit": 0,
+ "no-proto": 0,
+ "no-redeclare": 2,
+ "no-regex-spaces": 2,
+ "no-restricted-modules": 0,
+ "no-return-assign": 2,
+ "no-script-url": 0,
+ "no-self-compare": 2,
+ "no-sequences": 2,
+ "no-shadow": 0,
+ "no-shadow-restricted-names": 2,
+ "no-spaced-func": 2,
+ "no-sparse-arrays": 2,
+ "no-sync": 0,
+ "no-ternary": 0,
+ "no-this-before-super": 2,
+ "no-throw-literal": 2,
+ "no-trailing-spaces": 2,
+ "no-undef": 2,
+ "no-undef-init": 2,
+ "no-undefined": 0,
+ "no-underscore-dangle": 0,
+ "no-unexpected-multiline": 2,
+ "no-unneeded-ternary": 2,
+ "no-unreachable": 2,
+ "no-unused-expressions": 0,
+ "no-unused-vars": [2, { "vars": "all", "args": "none" }],
+ "no-use-before-define": 0,
+ "no-var": 0,
+ "no-void": 0,
+ "no-warning-comments": 0,
+ "no-with": 2,
+ "object-curly-spacing": 0,
+ "object-shorthand": 0,
+ "one-var": [2, { "initialized": "never" }],
+ "operator-assignment": 0,
+ "operator-linebreak": [2, "after", { "overrides": { "?": "before", ":": "before" } }],
+ "padded-blocks": 0,
+ "prefer-const": 0,
+ "quote-props": 0,
+ "quotes": [2, "single", "avoid-escape"],
+ "radix": 2,
+ "semi": [2, "never"],
+ "semi-spacing": 0,
+ "sort-vars": 0,
+ "space-after-keywords": [2, "always"],
+ "space-before-blocks": [2, "always"],
+ "space-before-function-paren": [2, "always"],
+ "space-in-parens": [2, "never"],
+ "space-infix-ops": 2,
+ "space-return-throw-case": 2,
+ "space-unary-ops": [2, { "words": true, "nonwords": false }],
+ "spaced-comment": [2, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!"] }],
+ "strict": 0,
+ "use-isnan": 2,
+ "valid-jsdoc": 0,
+ "valid-typeof": 2,
+ "vars-on-top": 0,
+ "wrap-iife": [2, "any"],
+ "wrap-regex": 0,
+ "yoda": [2, "never"]
+ }
}
diff --git a/package.json b/package.json
index eee278f..ea2ca1a 100644
--- a/package.json
+++ b/package.json
@@ -31,13 +31,6 @@
"codeclimate-test-reporter": "^0.3.1",
"connect-history-api-fallback": "^1.1.0",
"css-loader": "^0.23.0",
- "eslint": "^4.14.0",
- "eslint-config-standard": "^11.0.0-beta.0",
- "eslint-plugin-html": "^4.0.1",
- "eslint-plugin-import": "^2.8.0",
- "eslint-plugin-node": "^5.2.1",
- "eslint-plugin-promise": "^3.6.0",
- "eslint-plugin-standard": "^3.0.1",
"eventsource-polyfill": "^0.9.6",
"express": "^4.13.3",
"extract-text-webpack-plugin": "^1.0.1",
@@ -81,6 +74,5 @@
"webpack-dev-middleware": "^1.4.0",
"webpack-hot-middleware": "^2.6.0",
"webpack-merge": "^0.13.0"
- },
- "dependencies": {}
+ }
}
diff --git a/src/components/Select.vue b/src/components/Select.vue
index b966671..372662f 100644
--- a/src/components/Select.vue
+++ b/src/components/Select.vue
@@ -370,9 +370,9 @@
*/
options: {
type: Array,
- default () {
+ default() {
return []
- }
+ },
},
/**
@@ -469,13 +469,13 @@
*/
getOptionLabel: {
type: Function,
- default (option) {
+ default(option) {
if (typeof option === 'object') {
if (this.label && option[this.label]) {
return option[this.label]
}
}
- return option
+ return option;
}
},
@@ -538,7 +538,7 @@
*/
createOption: {
type: Function,
- default (newOption) {
+ default(newOption) {
if (typeof this.mutableOptions[0] === 'object') {
newOption = {[this.label]: newOption}
}
@@ -583,10 +583,10 @@
dir: {
type: String,
default: 'auto'
- }
+ },
},
- data () {
+ data() {
return {
search: '',
open: false,
@@ -598,12 +598,12 @@
watch: {
/**
* When the value prop changes, update
- * the internal mutableValue.
+ * the internal mutableValue.
* @param {mixed} val
* @return {void}
*/
- value (val) {
- this.mutableValue = val
+ value(val) {
+ this.mutableValue = val
},
/**
@@ -612,11 +612,11 @@
* @param {string|object} old
* @return {void}
*/
- mutableValue (val, old) {
+ mutableValue(val, old) {
if (this.multiple) {
- if (this.onChange) this.onChange(val)
+ this.onChange ? this.onChange(val) : null
} else {
- if (this.onChange && val !== old) this.onChange(val)
+ this.onChange && val !== old ? this.onChange(val) : null
}
},
@@ -626,29 +626,29 @@
* @param {array} val
* @return {void}
*/
- options (val) {
+ options(val) {
this.mutableOptions = val
},
/**
- * Maybe reset the mutableValue
+ * Maybe reset the mutableValue
* when mutableOptions change.
* @return {[type]} [description]
*/
- mutableOptions () {
+ mutableOptions() {
if (!this.taggable && this.resetOnOptionsChange) {
- this.mutableValue = this.multiple ? [] : null
+ this.mutableValue = this.multiple ? [] : null
}
},
/**
- * Always reset the mutableValue when
+ * Always reset the mutableValue when
* the multiple prop changes.
* @param {Boolean} val
* @return {void}
*/
- multiple (val) {
- this.mutableValue = val ? [] : null
+ multiple(val) {
+ this.mutableValue = val ? [] : null
}
},
@@ -656,10 +656,10 @@
* Clone props into mutable values,
* attach any event listeners.
*/
- created () {
- this.mutableValue = this.value
+ created() {
+ this.mutableValue = this.value
this.mutableOptions = this.options.slice(0)
- this.mutableLoading = this.loading
+ this.mutableLoading = this.loading
this.$on('option:created', this.maybePushTag)
},
@@ -671,7 +671,7 @@
* @param {Object|String} option
* @return {void}
*/
- select (option) {
+ select(option) {
if (this.isOptionSelected(option)) {
this.deselect(option)
} else {
@@ -696,15 +696,15 @@
* @param {Object|String} option
* @return {void}
*/
- deselect (option) {
+ deselect(option) {
if (this.multiple) {
let ref = -1
this.mutableValue.forEach((val) => {
- if (val === option || (typeof val === 'object' && val[this.label] === option[this.label])) {
+ if (val === option || typeof val === 'object' && val[this.label] === option[this.label]) {
ref = val
}
})
- const index = this.mutableValue.indexOf(ref)
+ var index = this.mutableValue.indexOf(ref)
this.mutableValue.splice(index, 1)
} else {
this.mutableValue = null
@@ -716,7 +716,7 @@
* @param {Object|String} option
* @return {void}
*/
- onAfterSelect (option) {
+ onAfterSelect(option) {
if (this.closeOnSelect) {
this.open = !this.open
this.$refs.search.blur()
@@ -732,9 +732,8 @@
* @param {Event} e
* @return {void}
*/
- toggleDropdown (e) {
- if (e.target === this.$refs.openIndicator || e.target === this.$refs.search ||
- e.target === this.$refs.toggle || e.target === this.$el) {
+ toggleDropdown(e) {
+ if (e.target === this.$refs.openIndicator || e.target === this.$refs.search || e.target === this.$refs.toggle || e.target === this.$el) {
if (this.open) {
this.$refs.search.blur() // dropdown will close on blur
} else {
@@ -751,7 +750,7 @@
* @param {Object|String} option
* @return {Boolean} True when selected | False otherwise
*/
- isOptionSelected (option) {
+ isOptionSelected(option) {
if (this.multiple && this.mutableValue) {
let selected = false
this.mutableValue.forEach(opt => {
@@ -759,7 +758,8 @@
selected = true
} else if (typeof opt === 'object' && opt[this.label] === option) {
selected = true
- } else if (opt === option) {
+ }
+ else if (opt === option) {
selected = true
}
})
@@ -774,7 +774,7 @@
* Otherwise, blur the search input to close the dropdown.
* @return {void}
*/
- onEscape () {
+ onEscape() {
if (!this.search.length) {
this.$refs.search.blur()
} else {
@@ -787,7 +787,7 @@
* @emits {search:blur}
* @return {void}
*/
- onSearchBlur () {
+ onSearchBlur() {
if (this.clearSearchOnBlur) {
this.search = ''
}
@@ -800,7 +800,7 @@
* @emits {search:focus}
* @return {void}
*/
- onSearchFocus () {
+ onSearchFocus() {
this.open = true
this.$emit('search:focus')
},
@@ -810,18 +810,10 @@
* text in the search input, & there's tags to delete
* @return {this.value}
*/
- maybeDeleteValue () {
- let value = null
-
+ maybeDeleteValue() {
if (!this.$refs.search.value.length && this.mutableValue) {
- if (this.multiple) {
- value = this.mutableValue.pop()
- } else {
- value = this.mutableValue = null
- }
+ return this.multiple ? this.mutableValue.pop() : this.mutableValue = null
}
-
- return value
},
/**
@@ -831,7 +823,7 @@
* @param {Object || String} option
* @return {boolean}
*/
- optionExists (option) {
+ optionExists(option) {
let exists = false
this.mutableOptions.forEach(opt => {
@@ -852,7 +844,7 @@
* @param {Object || String} option
* @return {void}
*/
- maybePushTag (option) {
+ maybePushTag(option) {
if (this.pushTags) {
this.mutableOptions.push(option)
}
@@ -865,7 +857,7 @@
* Classes to be output on .dropdown
* @return {Object}
*/
- dropdownClasses () {
+ dropdownClasses() {
return {
open: this.dropdownOpen,
single: !this.multiple,
@@ -882,7 +874,7 @@
* If search text should clear on blur
* @return {Boolean} True when single and clearSearchOnSelect
*/
- clearSearchOnBlur () {
+ clearSearchOnBlur() {
return this.clearSearchOnSelect && !this.multiple
},
@@ -891,7 +883,7 @@
* search input
* @return {Boolean} True if non empty value
*/
- searching () {
+ searching() {
return !!this.search
},
@@ -900,7 +892,7 @@
* dropdown menu.
* @return {Boolean} True if open
*/
- dropdownOpen () {
+ dropdownOpen() {
return this.noDrop ? false : this.open && !this.mutableLoading
},
@@ -909,9 +901,9 @@
* & there is no value selected.
* @return {String} Placeholder text
*/
- searchPlaceholder () {
+ searchPlaceholder() {
if (this.isValueEmpty && this.placeholder) {
- return this.placeholder
+ return this.placeholder;
}
},
@@ -923,7 +915,7 @@
*
* @return {array}
*/
- filteredOptions () {
+ filteredOptions() {
if (!this.filterable && !this.taggable) {
return this.mutableOptions.slice()
}
@@ -945,7 +937,7 @@
* Check if there aren't any options selected.
* @return {Boolean}
*/
- isValueEmpty () {
+ isValueEmpty() {
if (this.mutableValue) {
if (typeof this.mutableValue === 'object') {
return !Object.keys(this.mutableValue).length
@@ -953,14 +945,14 @@
return !this.mutableValue.length
}
- return true
+ return true;
},
/**
* Return the current value in array format.
* @return {Array}
*/
- valueAsArray () {
+ valueAsArray() {
if (this.multiple) {
return this.mutableValue
} else if (this.mutableValue) {
@@ -969,7 +961,7 @@
return []
}
- }
+ },
}
diff --git a/src/dev.js b/src/dev.js
index f1bd4e7..314174b 100644
--- a/src/dev.js
+++ b/src/dev.js
@@ -14,18 +14,18 @@ Vue.config.devtools = true
new Vue({
el: '#app',
data: {
- placeholder: 'placeholder',
+ placeholder: "placeholder",
value: null,
options: countries,
ajaxRes: [],
people: []
},
methods: {
- search (search, loading) {
+ search(search, loading) {
loading(true)
this.getRepositories(search, loading, this)
},
- searchPeople (search, loading) {
+ searchPeople(search, loading) {
loading(true)
this.getPeople(loading, this)
},
diff --git a/src/mixins/ajax.js b/src/mixins/ajax.js
index 44dbaba..b897053 100644
--- a/src/mixins/ajax.js
+++ b/src/mixins/ajax.js
@@ -1,75 +1,72 @@
module.exports = {
- props: {
- /**
- * Toggles the adding of a 'loading' class to the main
- * .v-select wrapper. Useful to control UI state when
- * results are being processed through AJAX.
- */
- loading: {
- type: Boolean,
- default: false
- },
+ props: {
+ /**
+ * Toggles the adding of a 'loading' class to the main
+ * .v-select wrapper. Useful to control UI state when
+ * results are being processed through AJAX.
+ */
+ loading: {
+ type: Boolean,
+ default: false
+ },
- /**
- * Accept a callback function that will be
- * run when the search text changes.
- *
- * loading() accepts a boolean value, and can
- * be used to toggle a loading class from
- * the onSearch callback.
- *
- * @param {search} String Current search text
- * @param {loading} Function(bool) Toggle loading class
- */
- onSearch: {
- type: Function,
- default: function (search, loading) {}
- }
- },
+ /**
+ * Accept a callback function that will be
+ * run when the search text changes.
+ *
+ * loading() accepts a boolean value, and can
+ * be used to toggle a loading class from
+ * the onSearch callback.
+ *
+ * @param {search} String Current search text
+ * @param {loading} Function(bool) Toggle loading class
+ */
+ onSearch: {
+ type: Function,
+ default: function(search, loading){}
+ }
+ },
- data () {
- return {
+ data() {
+ return {
mutableLoading: false
}
- },
+ },
- watch: {
- /**
- * If a callback & search text has been provided,
- * invoke the onSearch callback.
- */
- search () {
- if (this.search.length > 0) {
- this.onSearch(this.search, this.toggleLoading)
+ watch: {
+ /**
+ * If a callback & search text has been provided,
+ * invoke the onSearch callback.
+ */
+ search() {
+ if (this.search.length > 0) {
+ this.onSearch(this.search, this.toggleLoading)
this.$emit('search', this.search, this.toggleLoading)
}
- },
+ },
/**
- * Sync the loading prop with the internal
- * mutable loading value.
+ * Sync the loading prop with the internal
+ * mutable loading value.
* @param val
*/
- loading (val) {
- this.mutableLoading = val
- }
- },
+ loading(val) {
+ this.mutableLoading = val
+ }
+ },
- methods: {
- /**
- * Toggle this.loading. Optionally pass a boolean
- * value. If no value is provided, this.loading
- * will be set to the opposite of it's current value.
- * @param toggle Boolean
- * @returns {*}
- */
- toggleLoading (toggle = null) {
- if (toggle == null) {
- this.mutableLoading = !this.mutableLoading
- } else {
- this.mutableLoading = toggle
- }
-
- return this.mutableLoading
- }
- }
+ methods: {
+ /**
+ * Toggle this.loading. Optionally pass a boolean
+ * value. If no value is provided, this.loading
+ * will be set to the opposite of it's current value.
+ * @param toggle Boolean
+ * @returns {*}
+ */
+ toggleLoading(toggle = null) {
+ if (toggle == null) {
+ return this.mutableLoading = !this.mutableLoading
+ }
+ return this.mutableLoading = toggle
+ }
+ }
}
diff --git a/src/mixins/pointerScroll.js b/src/mixins/pointerScroll.js
index 84875d0..5950959 100644
--- a/src/mixins/pointerScroll.js
+++ b/src/mixins/pointerScroll.js
@@ -2,7 +2,7 @@
module.exports = {
watch: {
- typeAheadPointer () {
+ typeAheadPointer() {
this.maybeAdjustScroll()
}
},
@@ -14,14 +14,14 @@ module.exports = {
* overflow bounds.
* @returns {*}
*/
- maybeAdjustScroll () {
+ maybeAdjustScroll() {
let pixelsToPointerTop = this.pixelsToPointerTop()
let pixelsToPointerBottom = this.pixelsToPointerBottom()
- if (pixelsToPointerTop <= this.viewport().top) {
- return this.scrollTo(pixelsToPointerTop)
+ if ( pixelsToPointerTop <= this.viewport().top) {
+ return this.scrollTo( pixelsToPointerTop )
} else if (pixelsToPointerBottom >= this.viewport().bottom) {
- return this.scrollTo(this.viewport().top + this.pointerHeight())
+ return this.scrollTo( this.viewport().top + this.pointerHeight() )
}
},
@@ -30,10 +30,9 @@ module.exports = {
* list to the top of the current pointer element.
* @returns {number}
*/
- pixelsToPointerTop () {
+ pixelsToPointerTop() {
let pixelsToPointerTop = 0
-
- if (this.$refs.dropdownMenu) {
+ if( this.$refs.dropdownMenu ) {
for (let i = 0; i < this.typeAheadPointer; i++) {
pixelsToPointerTop += this.$refs.dropdownMenu.children[i].offsetHeight
}
@@ -46,7 +45,7 @@ module.exports = {
* list to the bottom of the current pointer element.
* @returns {*}
*/
- pixelsToPointerBottom () {
+ pixelsToPointerBottom() {
return this.pixelsToPointerTop() + this.pointerHeight()
},
@@ -54,7 +53,7 @@ module.exports = {
* The offsetHeight of the current pointer element.
* @returns {number}
*/
- pointerHeight () {
+ pointerHeight() {
let element = this.$refs.dropdownMenu ? this.$refs.dropdownMenu.children[this.typeAheadPointer] : false
return element ? element.offsetHeight : 0
},
@@ -63,9 +62,9 @@ module.exports = {
* The currently viewable portion of the dropdownMenu.
* @returns {{top: (string|*|number), bottom: *}}
*/
- viewport () {
+ viewport() {
return {
- top: this.$refs.dropdownMenu ? this.$refs.dropdownMenu.scrollTop : 0,
+ top: this.$refs.dropdownMenu ? this.$refs.dropdownMenu.scrollTop: 0,
bottom: this.$refs.dropdownMenu ? this.$refs.dropdownMenu.offsetHeight + this.$refs.dropdownMenu.scrollTop : 0
}
},
@@ -75,14 +74,8 @@ module.exports = {
* @param position
* @returns {*}
*/
- scrollTo (position) {
- let result = null
-
- if (this.$refs.dropdownMenu) {
- result = this.$refs.dropdownMenu.scrollTop = position
- }
-
- return result
- }
+ scrollTo(position) {
+ return this.$refs.dropdownMenu ? this.$refs.dropdownMenu.scrollTop = position : null
+ },
}
}
diff --git a/src/mixins/typeAheadPointer.js b/src/mixins/typeAheadPointer.js
index a038aca..26c9374 100644
--- a/src/mixins/typeAheadPointer.js
+++ b/src/mixins/typeAheadPointer.js
@@ -1,12 +1,12 @@
module.exports = {
- data () {
+ data() {
return {
typeAheadPointer: -1
}
},
watch: {
- filteredOptions () {
+ filteredOptions() {
this.typeAheadPointer = 0
}
},
@@ -17,10 +17,10 @@ module.exports = {
* subtracting the current index by one.
* @return {void}
*/
- typeAheadUp () {
+ typeAheadUp() {
if (this.typeAheadPointer > 0) {
this.typeAheadPointer--
- if (this.maybeAdjustScroll) {
+ if( this.maybeAdjustScroll ) {
this.maybeAdjustScroll()
}
}
@@ -31,10 +31,10 @@ module.exports = {
* adding the current index by one.
* @return {void}
*/
- typeAheadDown () {
+ typeAheadDown() {
if (this.typeAheadPointer < this.filteredOptions.length - 1) {
this.typeAheadPointer++
- if (this.maybeAdjustScroll) {
+ if( this.maybeAdjustScroll ) {
this.maybeAdjustScroll()
}
}
@@ -45,16 +45,16 @@ module.exports = {
* Optionally clear the search input on selection.
* @return {void}
*/
- typeAheadSelect () {
- if (this.filteredOptions[ this.typeAheadPointer ]) {
- this.select(this.filteredOptions[this.typeAheadPointer])
- } else if (this.taggable && this.search.length) {
+ typeAheadSelect() {
+ if( this.filteredOptions[ this.typeAheadPointer ] ) {
+ this.select( this.filteredOptions[ this.typeAheadPointer ] );
+ } else if (this.taggable && this.search.length){
this.select(this.search)
}
- if (this.clearSearchOnSelect) {
- this.search = ''
+ if( this.clearSearchOnSelect ) {
+ this.search = "";
}
- }
+ },
}
-}
+}
\ No newline at end of file
diff --git a/test/unit/specs/Select.spec.js b/test/unit/specs/Select.spec.js
index 1bf7805..33d954c 100644
--- a/test/unit/specs/Select.spec.js
+++ b/test/unit/specs/Select.spec.js
@@ -17,12 +17,12 @@ Vue.component('v-select', vSelect)
* @param process
* @returns {Event}
*/
-function trigger (target, event, process) {
- const e = document.createEvent('HTMLEvents')
- e.initEvent(event, true, true)
- if (process) process(e)
- target.dispatchEvent(e)
- return e
+function trigger(target, event, process) {
+ var e = document.createEvent('HTMLEvents')
+ e.initEvent(event, true, true)
+ if (process) process(e)
+ target.dispatchEvent(e)
+ return e
}
/**
@@ -32,12 +32,12 @@ function trigger (target, event, process) {
* @param process
* @returns {Event}
*/
-function triggerMouse (target, event, process) {
- const e = document.createEvent('MouseEvent')
- e.initEvent('event', true, true)
- if (process) process(e)
- target.dispatchEvent(e)
- return e
+function triggerMouse(target, event, process) {
+ var e = document.createEvent('MouseEvent')
+ e.initEvent('event', true, true)
+ if (process) process(e)
+ target.dispatchEvent(e)
+ return e
}
/**
@@ -47,12 +47,12 @@ function triggerMouse (target, event, process) {
* @param process
* @returns {Event}
*/
-function triggerFocusEvent (target, event, process) {
- const e = document.createEvent('FocusEvent')
- e.initEvent('event', true, true)
- if (process) process(e)
- target.dispatchEvent(e)
- return e
+function triggerFocusEvent(target, event, process) {
+ var e = document.createEvent('FocusEvent')
+ e.initEvent('event', true, true)
+ if (process) process(e)
+ target.dispatchEvent(e)
+ return e
}
/**
@@ -60,1071 +60,1077 @@ function triggerFocusEvent (target, event, process) {
* @param vm
* @param search
*/
-function searchSubmit (vm, search = false) {
- if (search) {
- vm.$children[0].search = search
- }
+function searchSubmit(vm, search = false) {
+ if (search) {
+ vm.$children[0].search = search
+ }
- trigger(vm.$children[0].$refs.search, 'keydown', function (e) {
- e.keyCode = 13
- })
+ trigger(vm.$children[0].$refs.search, 'keydown', function (e) {
+ e.keyCode = 13
+ })
}
describe('Select.vue', () => {
- describe('Selecting values', () => {
- it('can accept an array with pre-selected values', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: 'one',
- options: ['one', 'two', 'three']
- }
- }).$mount()
- expect(vm.$children[0].mutableValue).toEqual(vm.value)
- })
-
- it('can accept an array of objects and pre-selected value (single)', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: {label: 'This is Foo', value: 'foo'},
- options: [{label: 'This is Foo', value: 'foo'}, {label: 'This is Bar', value: 'bar'}]
- }
- }).$mount()
- expect(vm.$children[0].mutableValue).toEqual(vm.value)
- })
-
- it('can accept an array of objects and pre-selected values (multiple)', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: [{label: 'This is Foo', value: 'foo'}, {label: 'This is Bar', value: 'bar'}],
- options: [{label: 'This is Foo', value: 'foo'}, {label: 'This is Bar', value: 'bar'}]
- }
- }).$mount()
- expect(vm.$children[0].mutableValue).toEqual(vm.value)
- })
-
- it('can deselect a pre-selected object', () => {
- const vm = new Vue({
- template: '
',
- data: {
- value: [{label: 'This is Foo', value: 'foo'}, {label: 'This is Bar', value: 'bar'}],
- options: [{label: 'This is Foo', value: 'foo'}, {label: 'This is Bar', value: 'bar'}]
- }
- }).$mount()
- vm.$children[0].select({label: 'This is Foo', value: 'foo'})
- expect(vm.$children[0].mutableValue.length).toEqual(1)
- })
-
- it('can deselect a pre-selected string', () => {
- const vm = new Vue({
- template: '
',
- data: {
- value: ['foo', 'bar'],
- options: ['foo', 'bar']
- }
- }).$mount()
- vm.$children[0].select('foo')
- expect(vm.$children[0].mutableValue.length).toEqual(1)
- })
-
- it('can deselect an option when multiple is false', () => {
- const vm = new Vue({
- template: `
`
- }).$mount()
- vm.$children[0].deselect('foo')
- expect(vm.$children[0].mutableValue).toEqual(null)
- })
-
- it('can determine if the value prop is empty', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: [],
- options: ['one', 'two', 'three']
- }
- }).$mount()
- var select = vm.$children[0]
- expect(select.isValueEmpty).toEqual(true)
-
- select.select(['one'])
- expect(select.isValueEmpty).toEqual(false)
-
- select.select([{l: 'f'}])
- expect(select.isValueEmpty).toEqual(false)
-
- select.select('one')
- expect(select.isValueEmpty).toEqual(false)
-
- select.select({label: 'foo', value: 'foo'})
- expect(select.isValueEmpty).toEqual(false)
-
- select.select('')
- expect(select.isValueEmpty).toEqual(true)
-
- select.select(null)
- expect(select.isValueEmpty).toEqual(true)
- })
-
- it('should reset the selected values when the multiple property changes', (done) => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: ['one'],
- multiple: true,
- options: ['one', 'two', 'three']
- }
- }).$mount()
-
- vm.multiple = false
-
- Vue.nextTick(() => {
- expect(vm.$children[0].mutableValue).toEqual(null)
- vm.multiple = true
- Vue.nextTick(() => {
- expect(vm.$children[0].mutableValue).toEqual([])
- done()
- })
- })
- })
-
- it('can retain values present in a new array of options', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: ['one'],
- options: ['one', 'two', 'three']
- }
- }).$mount()
- vm.options = ['one', 'five', 'six']
- expect(vm.$children[0].mutableValue).toEqual(['one'])
- })
-
- it('can determine if an object is already selected', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: [{label: 'one'}],
- options: [{label: 'one'}]
- }
- }).$mount()
-
- expect(vm.$children[0].isOptionSelected({label: 'one'})).toEqual(true)
- })
-
- it('can use v-model syntax for a two way binding to a parent component', (done) => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: 'foo',
- options: ['foo', 'bar', 'baz']
- }
- }).$mount()
-
- expect(vm.$children[0].value).toEqual('foo')
- expect(vm.$children[0].mutableValue).toEqual('foo')
-
- vm.$children[0].mutableValue = 'bar'
-
- Vue.nextTick(() => {
- expect(vm.value).toEqual('bar')
- done()
- })
- })
-
- it('can check if a string value is selected when the value is an object and multiple is true', () => {
- const vm = new Vue({
- template: `
`
- }).$mount()
- expect(vm.$children[0].isOptionSelected('foo')).toEqual(true)
- })
-
- describe('change Event', () => {
- it('will trigger the input event when the selection changes', (done) => {
- const vm = new Vue({
- template: `
`,
- data: {
- foo: ''
- }
- }).$mount()
-
- vm.$refs.select.select('bar')
-
- Vue.nextTick(() => {
- expect(vm.foo).toEqual('bar')
- done()
- })
- })
-
- it('should run change when multiple is true and the value changes', (done) => {
- const vm = new Vue({
- template: `
`,
- data: {
- foo: ''
- }
- }).$mount()
-
- vm.$refs.select.select('bar')
-
- Vue.nextTick(() => {
- expect(vm.foo).toEqual(['foo', 'bar'])
- done()
- })
- })
- })
- })
-
- describe('Filtering Options', () => {
- it('should filter an array of strings', () => {
- const vm = new Vue({
- template: `
`,
- data: {value: 'foo'}
- }).$mount()
- vm.$refs.select.search = 'ba'
- expect(vm.$refs.select.filteredOptions).toEqual(['bar', 'baz'])
- })
-
- it('should not filter the array of strings if filterable is false', () => {
- const vm = new Vue({
- template: `
`,
- data: {value: 'foo'}
- }).$mount()
- vm.$refs.select.search = 'ba'
- expect(vm.$refs.select.filteredOptions).toEqual(['foo', 'bar', 'baz'])
- })
-
- it('should filter without case-sensitivity', () => {
- const vm = new Vue({
- template: `
`,
- data: {value: 'foo'}
- }).$mount()
- vm.$refs.select.search = 'ba'
- expect(vm.$refs.select.filteredOptions).toEqual(['Bar', 'Baz'])
- })
-
- it('can filter an array of objects based on the objects label key', () => {
- const vm = new Vue({
- template: `
`,
- data: {value: 'foo'}
- }).$mount()
- vm.$refs.select.search = 'ba'
- expect(JSON.stringify(vm.$refs.select.filteredOptions)).toEqual(JSON.stringify([{label: 'Bar', value: 'bar'}, {label: 'Baz', value: 'baz'}]))
- })
- })
-
- describe('Toggling Dropdown', () => {
- it('should not open the dropdown when the el is clicked but the component is disabled', (done) => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: [{label: 'one'}],
- options: [{label: 'one'}]
- }
- }).$mount()
-
- vm.$children[0].toggleDropdown({target: vm.$children[0].$refs.search})
- Vue.nextTick(() => {
- Vue.nextTick(() => {
- expect(vm.$children[0].open).toEqual(false)
- done()
- })
- })
- })
-
- it('should open the dropdown when the el is clicked', (done) => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: [{label: 'one'}],
- options: [{label: 'one'}]
- }
- }).$mount()
-
- vm.$children[0].toggleDropdown({target: vm.$children[0].$refs.search})
- Vue.nextTick(() => {
- Vue.nextTick(() => {
- expect(vm.$children[0].open).toEqual(true)
- done()
- })
- })
- })
-
- it('can close the dropdown when the el is clicked', (done) => {
- const vm = new Vue({
- template: '
',
- components: {vSelect}
- }).$mount()
-
- spyOn(vm.$children[0].$refs.search, 'blur')
-
- vm.$children[0].open = true
- vm.$children[0].toggleDropdown({target: vm.$children[0].$el})
-
- Vue.nextTick(() => {
- expect(vm.$children[0].$refs.search.blur).toHaveBeenCalled()
- done()
- })
- })
-
- it('closes the dropdown when an option is selected, multiple is true, and closeOnSelect option is true', (done) => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: [],
- options: ['one', 'two', 'three']
- }
- }).$mount()
-
- vm.$children[0].open = true
- vm.$refs.select.select('one')
-
- Vue.nextTick(() => {
- expect(vm.$children[0].open).toEqual(false)
- done()
- })
- })
-
- it('does not close the dropdown when the el is clicked, multiple is true, and closeOnSelect option is false', (done) => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: [],
- options: ['one', 'two', 'three']
- }
- }).$mount()
-
- vm.$children[0].open = true
- vm.$refs.select.select('one')
-
- Vue.nextTick(() => {
- expect(vm.$children[0].open).toEqual(true)
- done()
- })
- })
-
- it('should close the dropdown on search blur', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: [{label: 'one'}],
- options: [{label: 'one'}]
- }
- }).$mount()
-
- vm.$children[0].open = true
- triggerFocusEvent(vm.$children[0].$refs.toggle, 'blur')
- expect(vm.$children[0].open).toEqual(true)
- })
-
- it('will close the dropdown and emit the search:blur event from onSearchBlur', () => {
- const vm = new Vue({
- template: '
'
- }).$mount()
-
- spyOn(vm.$children[0], '$emit')
- vm.$children[0].open = true
- vm.$children[0].onSearchBlur()
-
- expect(vm.$children[0].open).toEqual(false)
- expect(vm.$children[0].$emit).toHaveBeenCalledWith('search:blur')
- })
-
- it('will open the dropdown and emit the search:focus event from onSearchFocus', () => {
- const vm = new Vue({
- template: '
'
- }).$mount()
-
- spyOn(vm.$children[0], '$emit')
- vm.$children[0].onSearchFocus()
-
- expect(vm.$children[0].open).toEqual(true)
- expect(vm.$children[0].$emit).toHaveBeenCalledWith('search:focus')
- })
-
- it('will close the dropdown on escape, if search is empty', (done) => {
- const vm = new Vue({
- template: '
',
- components: {vSelect}
- }).$mount()
-
- spyOn(vm.$children[0].$refs.search, 'blur')
-
- vm.$children[0].open = true
- vm.$children[0].onEscape()
-
- Vue.nextTick(() => {
- expect(vm.$children[0].$refs.search.blur).toHaveBeenCalled()
- done()
- })
- })
-
- it('should remove existing search text on escape keyup', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: [{label: 'one'}],
- options: [{label: 'one'}]
- }
- }).$mount()
-
- vm.$children[0].search = 'foo'
- vm.$children[0].onEscape()
- expect(vm.$children[0].search).toEqual('')
- })
-
- it('should have an open class when dropdown is active', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect}
- }).$mount()
-
- expect(vm.$children[0].dropdownClasses.open).toEqual(false)
- })
- })
-
- describe('Moving the Typeahead Pointer', () => {
- it('should set the pointer to zero when the filteredOptions change', (done) => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- options: ['one', 'two', 'three']
- }
- }).$mount()
-
- vm.$children[0].search = 'two'
- Vue.nextTick(() => {
- expect(vm.$children[0].typeAheadPointer).toEqual(0)
- done()
- })
- })
-
- it('should move the pointer visually up the list on up arrow keyDown', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- options: ['one', 'two', 'three']
- }
- }).$mount()
-
- vm.$children[0].typeAheadPointer = 1
-
- trigger(vm.$children[0].$refs.search, 'keydown', (e) => { e.keyCode = 38 })
- expect(vm.$children[0].typeAheadPointer).toEqual(0)
- })
-
- it('should move the pointer visually down the list on down arrow keyDown', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- options: ['one', 'two', 'three']
- }
- }).$mount()
-
- vm.$children[0].typeAheadPointer = 1
- trigger(vm.$children[0].$refs.search, 'keydown', (e) => { e.keyCode = 40 })
- expect(vm.$children[0].typeAheadPointer).toEqual(2)
- })
-
- it('should not move the pointer past the end of the list', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- options: ['one', 'two', 'three']
- }
- }).$mount()
-
- vm.$children[0].typeAheadPointer = 2
- vm.$children[0].typeAheadDown()
- expect(vm.$children[0].typeAheadPointer).toEqual(2)
- })
-
- describe('Automatic Scrolling', () => {
- it('should check if the scroll position needs to be adjusted on up arrow keyDown', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- options: ['one', 'two', 'three']
- }
- }).$mount()
-
- vm.$children[0].typeAheadPointer = 1
- spyOn(vm.$children[0], 'maybeAdjustScroll')
- trigger(vm.$children[0].$refs.search, 'keydown', (e) => { e.keyCode = 38 })
- expect(vm.$children[0].maybeAdjustScroll).toHaveBeenCalled()
- })
-
- it('should check if the scroll position needs to be adjusted on down arrow keyDown', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- options: ['one', 'two', 'three']
- }
- }).$mount()
-
- spyOn(vm.$children[0], 'maybeAdjustScroll')
- trigger(vm.$children[0].$refs.search, 'keydown', (e) => { e.keyCode = 40 })
- expect(vm.$children[0].maybeAdjustScroll).toHaveBeenCalled()
- })
-
- it('should check if the scroll position needs to be adjusted when filtered options changes', (done) => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- options: ['one', 'two', 'three']
- }
- }).$mount()
-
- spyOn(vm.$children[0], 'maybeAdjustScroll')
- vm.$children[0].search = 'two'
-
- Vue.nextTick(() => {
- expect(vm.$children[0].maybeAdjustScroll).toHaveBeenCalled()
- done()
- })
- })
-
- it('should scroll up if the pointer is above the current viewport bounds', () => {
- let methods = Object.assign(pointerScroll.methods, {
- pixelsToPointerTop () {
- return 1
- },
- viewport () {
- return {top: 2, bottom: 0}
- }
- })
- const vm = new Vue({
- template: '
',
- components: {
- 'v-select': Mock({
- '../mixins/pointerScroll': {methods}
- })
- }
- }).$mount()
-
- spyOn(vm.$children[0], 'scrollTo')
- vm.$children[0].maybeAdjustScroll()
- expect(vm.$children[0].scrollTo).toHaveBeenCalledWith(1)
- })
-
- /**
- * @link https://github.com/vuejs/vue-loader/issues/434
- * @todo vue-loader/inject-loader fails when used twice in the same file,
- * so the mock here is abastracted to a separate file.
- */
- xit('should scroll down if the pointer is below the current viewport bounds', () => {
- let methods = Object.assign(pointerScroll.methods, {
- pixelsToPointerBottom () {
- return 2
- },
- viewport () {
- return {top: 0, bottom: 1}
- }
- })
- const vm = new Vue({
- template: `
`,
- components: {
- 'v-select': Mock({
- '../mixins/pointerScroll': {methods}
- })
- }
- }).$mount()
-
- spyOn(vm.$children[0], 'scrollTo')
- vm.$children[0].maybeAdjustScroll()
- expect(vm.$children[0].scrollTo).toHaveBeenCalledWith(vm.$children[0].viewport().top + vm.$children[0].pointerHeight())
- })
- })
-
- describe('Measuring pixel distances', () => {
- it('should calculate pointerHeight as the offsetHeight of the pointer element if it exists', () => {
- const vm = new Vue({
- template: `
`
- }).$mount()
-
- // dropdown must be open for $refs to exist
- vm.$children[0].open = true
-
- Vue.nextTick(() => {
- // Fresh instances start with the pointer at -1
- vm.$children[0].typeAheadPointer = -1
- expect(vm.$children[0].pointerHeight()).toEqual(0)
-
- vm.$children[0].typeAheadPointer = 100
- expect(vm.$children[0].pointerHeight()).toEqual(0)
-
- vm.$children[0].typeAheadPointer = 1
- expect(vm.$children[0].pointerHeight()).toEqual(vm.$children[0].$refs.dropdownMenu.children[1].offsetHeight)
- })
- })
- })
- })
-
- describe('Removing values', () => {
- it('can remove the given tag when its close icon is clicked', (done) => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: ['one'],
- options: ['one', 'two', 'three']
- }
- }).$mount()
- vm.$children[0].$refs.toggle.querySelector('.close').click()
- Vue.nextTick(() => {
- expect(vm.$children[0].mutableValue).toEqual([])
- done()
- })
- })
-
- it('should not remove tag when close icon is clicked and component is disabled', (done) => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: ['one'],
- options: ['one', 'two', 'three']
- }
- }).$mount()
- vm.$children[0].$refs.toggle.querySelector('.close').click()
- Vue.nextTick(() => {
- expect(vm.$children[0].mutableValue).toEqual(['one'])
- done()
- })
- })
-
- it('should remove the last item in the value array on delete keypress when multiple is true', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: ['one', 'two'],
- options: ['one', 'two', 'three']
- }
- }).$mount()
- vm.$children[0].maybeDeleteValue()
- Vue.nextTick(() => {
- expect(vm.$children[0].mutableValue).toEqual(['one'])
- })
- })
-
- it('should set value to null on delete keypress when multiple is false', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: 'one',
- options: ['one', 'two', 'three']
- }
- }).$mount()
- vm.$children[0].maybeDeleteValue()
- Vue.nextTick(() => {
- expect(vm.$children[0].mutableValue).toEqual(null)
- })
- })
- })
-
- describe('Labels', () => {
- it('can generate labels using a custom label key', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: [{name: 'Baz'}],
- options: [{name: 'Foo'}, {name: 'Baz'}]
- }
- }).$mount()
- expect(vm.$children[0].$refs.toggle.querySelector('.selected-tag').textContent).toContain('Baz')
- })
-
- it('will console.warn when options contain objects without a valid label key', (done) => {
- spyOn(console, 'warn')
- const vm = new Vue({
- template: '
'
- }).$mount()
- Vue.nextTick(() => {
- expect(console.warn).toHaveBeenCalledWith(
- '[vue-select warn]: Label key "option.label" does not exist in options object.' +
- '\nhttp://sagalbot.github.io/vue-select/#ex-labels'
- )
- done()
- })
- })
-
- it('should display a placeholder if the value is empty', (done) => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- options: [{label: 'one'}]
- }
- }).$mount()
-
- expect(vm.$children[0].searchPlaceholder).toEqual('foo')
- vm.$children[0].mutableValue = {label: 'one'}
- Vue.nextTick(() => {
- expect(vm.$children[0].searchPlaceholder).not.toBeDefined()
- done()
- })
- })
- })
-
- describe('When Tagging Is Enabled', () => {
- it('can determine if a given option string already exists', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- options: ['one', 'two']
- }
- }).$mount()
-
- expect(vm.$refs.select.optionExists('one')).toEqual(true)
- expect(vm.$refs.select.optionExists('three')).toEqual(false)
- })
-
- it('can determine if a given option object already exists', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- options: [{label: 'one'}, {label: 'two'}]
- }
- }).$mount()
-
- expect(vm.$refs.select.optionExists('one')).toEqual(true)
- expect(vm.$refs.select.optionExists('three')).toEqual(false)
- })
-
- it('can determine if a given option object already exists when using custom labels', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- options: [{foo: 'one'}, {foo: 'two'}]
- }
- }).$mount()
-
- expect(vm.$refs.select.optionExists('one')).toEqual(true)
- expect(vm.$refs.select.optionExists('three')).toEqual(false)
- })
-
- it('can add the current search text as the first item in the options list', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: ['one'],
- options: ['one', 'two']
- }
- }).$mount()
-
- vm.$children[0].search = 'three'
- expect(vm.$children[0].filteredOptions).toEqual(['three'])
- })
-
- it('can select the current search text as a string', (done) => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: ['one'],
- options: ['one', 'two']
- }
- }).$mount()
-
- searchSubmit(vm, 'three')
- Vue.nextTick(() => {
- expect(vm.$children[0].mutableValue).toEqual(['one', 'three'])
- done()
- })
- })
-
- it('can select the current search text as an object', (done) => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: [{label: 'one'}],
- options: [{label: 'one'}]
- }
- }).$mount()
-
- searchSubmit(vm, 'two')
- Vue.nextTick(() => {
- expect(vm.$children[0].mutableValue).toEqual([{label: 'one'}, {label: 'two'}])
- done()
- })
- })
-
- it('should add a freshly created option/tag to the options list when pushTags is true', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: ['one'],
- options: ['one', 'two']
- }
- }).$mount()
-
- searchSubmit(vm, 'three')
- expect(vm.$children[0].mutableOptions).toEqual(['one', 'two', 'three'])
- })
-
- it('should add a freshly created option/tag to the options list when pushTags is true and filterable is false', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: ['one'],
- options: ['one', 'two']
- }
- }).$mount()
-
- searchSubmit(vm, 'three')
- expect(vm.$children[0].mutableOptions).toEqual(['one', 'two', 'three'])
- expect(vm.$children[0].filteredOptions).toEqual(['one', 'two', 'three'])
- })
-
- it('wont add a freshly created option/tag to the options list when pushTags is false', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: ['one'],
- options: ['one', 'two']
- }
- }).$mount()
-
- searchSubmit(vm, 'three')
- expect(vm.$children[0].mutableOptions).toEqual(['one', 'two'])
- })
-
- it('wont add a freshly created option/tag to the options list when pushTags is false and filterable is false', () => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: ['one'],
- options: ['one', 'two']
- }
- }).$mount()
-
- searchSubmit(vm, 'three')
- expect(vm.$children[0].mutableOptions).toEqual(['one', 'two'])
- expect(vm.$children[0].filteredOptions).toEqual(['one', 'two'])
- })
-
- it('should select an existing option if the search string matches a string from options', (done) => {
- let two = 'two'
- const vm = new Vue({
- template: '
',
- data: {
- value: null,
- options: ['one', two]
- }
- }).$mount()
- vm.$children[0].search = 'two'
-
- searchSubmit(vm)
-
- Vue.nextTick(() => {
- expect(vm.$children[0].mutableValue[0]).toBe(two)
- done()
- })
- })
-
- it('should select an existing option if the search string matches an objects label from options', (done) => {
- let two = {label: 'two'}
- const vm = new Vue({
- template: '
',
- data: {
- options: [{label: 'one'}, two]
- }
- }).$mount()
-
- vm.$children[0].search = 'two'
-
- Vue.nextTick(() => {
- searchSubmit(vm)
- // This needs to be wrapped in nextTick() twice so that filteredOptions can
- // calculate after setting the search text, and move the typeAheadPointer index to 0.
- Vue.nextTick(() => {
- expect(vm.$children[0].mutableValue.label).toBe(two.label)
- done()
- })
- })
- })
-
- it('should select an existing option if the search string matches an objects label from options when filter-options is false', (done) => {
- let two = {label: 'two'}
- const vm = new Vue({
- template: '
',
- data: {
- options: [{label: 'one'}, two]
- }
- }).$mount()
-
- vm.$children[0].search = 'two'
-
- Vue.nextTick(() => {
- searchSubmit(vm)
- // This needs to be wrapped in nextTick() twice so that filteredOptions can
- // calculate after setting the search text, and move the typeAheadPointer index to 0.
- Vue.nextTick(() => {
- expect(vm.$children[0].mutableValue.label).toBe(two.label)
- done()
- })
- })
- })
-
- it('should not reset the selected value when the options property changes', (done) => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: [{label: 'one'}],
- options: [{label: 'one'}]
- }
- }).$mount()
- vm.$children[0].mutableOptions = [{label: 'two'}]
- Vue.nextTick(() => {
- expect(vm.$children[0].mutableValue).toEqual([{label: 'one'}])
- done()
- })
- })
-
- it('should not reset the selected value when the options property changes when filterable is false', (done) => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: [{label: 'one'}],
- options: [{label: 'one'}]
- }
- }).$mount()
- vm.$children[0].mutableOptions = [{label: 'two'}]
- Vue.nextTick(() => {
- expect(vm.$children[0].mutableValue).toEqual([{label: 'one'}])
- done()
- })
- })
-
- it('should not allow duplicate tags when using string options', (done) => {
- const vm = new Vue({
- template: `
`
- }).$mount()
- vm.$refs.select.search = 'one'
- searchSubmit(vm)
- Vue.nextTick(() => {
- expect(vm.$refs.select.mutableValue).toEqual(['one'])
- expect(vm.$refs.select.search).toEqual('')
- vm.$refs.select.search = 'one'
- searchSubmit(vm)
- Vue.nextTick(() => {
- expect(vm.$refs.select.mutableValue).toEqual([])
- expect(vm.$refs.select.search).toEqual('')
- done()
- })
- })
- })
-
- it('should not allow duplicate tags when using object options', (done) => {
- const vm = new Vue({
- template: `
`
- }).$mount()
- vm.$refs.select.search = 'one'
- searchSubmit(vm)
- Vue.nextTick(() => {
- expect(vm.$refs.select.mutableValue).toEqual(['one'])
- expect(vm.$refs.select.search).toEqual('')
- vm.$refs.select.search = 'one'
- searchSubmit(vm)
- Vue.nextTick(() => {
- expect(vm.$refs.select.mutableValue).toEqual([])
- expect(vm.$refs.select.search).toEqual('')
- done()
- })
- })
- })
- })
-
- describe('Asynchronous Loading', () => {
- it('can toggle the loading class', () => {
- const vm = new Vue({
- template: '
'
- }).$mount()
-
- vm.$refs.select.toggleLoading()
- expect(vm.$refs.select.mutableLoading).toEqual(true)
-
- vm.$refs.select.toggleLoading(true)
- expect(vm.$refs.select.mutableLoading).toEqual(true)
- })
-
- it('should trigger the onSearch callback when the search text changes', (done) => {
- const vm = new Vue({
- template: '
',
- data: {
- called: false
- },
- methods: {
- foo (val) {
- this.called = val
- }
- }
- }).$mount()
-
- vm.$refs.select.search = 'foo'
-
- Vue.nextTick(() => {
- expect(vm.called).toEqual('foo')
- done()
- })
- })
-
- it('should not trigger the onSearch callback if the search text is empty', (done) => {
- const vm = new Vue({
- template: '
',
- data: { called: false },
- methods: {
- foo (val) {
- this.called = !this.called
- }
- }
- }).$mount()
-
- vm.$refs.select.search = 'foo'
- Vue.nextTick(() => {
- expect(vm.called).toBe(true)
- vm.$refs.select.search = ''
- Vue.nextTick(() => {
- expect(vm.called).toBe(true)
- done()
- })
- })
- })
+
+ describe('Selecting values', () => {
+ it('can accept an array with pre-selected values', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: 'one',
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+ expect(vm.$children[0].mutableValue).toEqual(vm.value)
+ })
+
+ it('can accept an array of objects and pre-selected value (single)', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: {label: 'This is Foo', value: 'foo'},
+ options: [{label: 'This is Foo', value: 'foo'}, {label: 'This is Bar', value: 'bar'}]
+ }
+ }).$mount()
+ expect(vm.$children[0].mutableValue).toEqual(vm.value)
+ })
+
+ it('can accept an array of objects and pre-selected values (multiple)', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: [{label: 'This is Foo', value: 'foo'}, {label: 'This is Bar', value: 'bar'}],
+ options: [{label: 'This is Foo', value: 'foo'}, {label: 'This is Bar', value: 'bar'}]
+ }
+ }).$mount()
+ expect(vm.$children[0].mutableValue).toEqual(vm.value)
+ })
+
+ it('can deselect a pre-selected object', () => {
+ const vm = new Vue({
+ template: '
',
+ data: {
+ value: [{label: 'This is Foo', value: 'foo'}, {label: 'This is Bar', value: 'bar'}],
+ options: [{label: 'This is Foo', value: 'foo'}, {label: 'This is Bar', value: 'bar'}]
+ }
+ }).$mount()
+ vm.$children[0].select({label: 'This is Foo', value: 'foo'})
+ expect(vm.$children[0].mutableValue.length).toEqual(1)
+ })
+
+ it('can deselect a pre-selected string', () => {
+ const vm = new Vue({
+ template: '
',
+ data: {
+ value: ['foo', 'bar'],
+ options: ['foo','bar']
+ }
+ }).$mount()
+ vm.$children[0].select('foo')
+ expect(vm.$children[0].mutableValue.length).toEqual(1)
+ }),
+
+ it('can deselect an option when multiple is false', () => {
+ const vm = new Vue({
+ template: `
`,
+ }).$mount()
+ vm.$children[0].deselect('foo')
+ expect(vm.$children[0].mutableValue).toEqual(null)
+ })
+
+ it('can determine if the value prop is empty', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: [],
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+ var select = vm.$children[0]
+ expect(select.isValueEmpty).toEqual(true)
+
+ select.select(['one'])
+ expect(select.isValueEmpty).toEqual(false)
+
+ select.select([{l: 'f'}])
+ expect(select.isValueEmpty).toEqual(false)
+
+ select.select('one')
+ expect(select.isValueEmpty).toEqual(false)
+
+ select.select({label: 'foo', value: 'foo'})
+ expect(select.isValueEmpty).toEqual(false)
+
+ select.select('')
+ expect(select.isValueEmpty).toEqual(true)
+
+ select.select(null)
+ expect(select.isValueEmpty).toEqual(true)
+ })
+
+ it('should reset the selected values when the multiple property changes', (done) => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: ['one'],
+ multiple: true,
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+
+ vm.multiple = false
+
+ Vue.nextTick(() => {
+ expect(vm.$children[0].mutableValue).toEqual(null)
+ vm.multiple = true
+ Vue.nextTick(() => {
+ expect(vm.$children[0].mutableValue).toEqual([])
+ done()
+ })
+ })
+ })
+
+ it('can retain values present in a new array of options', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: ['one'],
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+ vm.options = ['one', 'five', 'six']
+ expect(vm.$children[0].mutableValue).toEqual(['one'])
+ })
+
+ it('can determine if an object is already selected', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: [{label: 'one'}],
+ options: [{label: 'one'}]
+ }
+ }).$mount()
+
+ expect(vm.$children[0].isOptionSelected({label: 'one'})).toEqual(true)
+ })
+
+ it('can use v-model syntax for a two way binding to a parent component', (done) => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: 'foo',
+ options: ['foo','bar','baz']
+ }
+ }).$mount()
+
+ expect(vm.$children[0].value).toEqual('foo')
+ expect(vm.$children[0].mutableValue).toEqual('foo')
+
+ vm.$children[0].mutableValue = 'bar'
+
+ Vue.nextTick(() => {
+ expect(vm.value).toEqual('bar')
+ done()
+ })
+ }),
+
+ it('can check if a string value is selected when the value is an object and multiple is true', () => {
+ const vm = new Vue({
+ template: `
`,
+ }).$mount()
+ expect(vm.$children[0].isOptionSelected('foo')).toEqual(true)
+ }),
+
+ describe('change Event', () => {
+ it('will trigger the input event when the selection changes', (done) => {
+ const vm = new Vue({
+ template: `
`,
+ data: {
+ foo: ''
+ }
+ }).$mount()
+
+ vm.$refs.select.select('bar')
+
+ Vue.nextTick(() => {
+ expect(vm.foo).toEqual('bar')
+ done()
+ })
+ })
+
+ it('should run change when multiple is true and the value changes', (done) => {
+ const vm = new Vue({
+ template: `
`,
+ data: {
+ foo: ''
+ }
+ }).$mount()
+
+ vm.$refs.select.select('bar')
+
+ Vue.nextTick(() => {
+ expect(vm.foo).toEqual(['foo','bar'])
+ done()
+ })
+
+ })
+ })
+ })
+
+ describe('Filtering Options', () => {
+ it('should filter an array of strings', () => {
+ const vm = new Vue({
+ template: `
`,
+ data: {value: 'foo'}
+ }).$mount()
+ vm.$refs.select.search = 'ba'
+ expect(vm.$refs.select.filteredOptions).toEqual(['bar','baz'])
+ })
+
+ it('should not filter the array of strings if filterable is false', () => {
+ const vm = new Vue({
+ template: `
`,
+ data: {value: 'foo'}
+ }).$mount()
+ vm.$refs.select.search = 'ba'
+ expect(vm.$refs.select.filteredOptions).toEqual(['foo','bar','baz'])
+ })
+
+ it('should filter without case-sensitivity', () => {
+ const vm = new Vue({
+ template: `
`,
+ data: {value: 'foo'}
+ }).$mount()
+ vm.$refs.select.search = 'ba'
+ expect(vm.$refs.select.filteredOptions).toEqual(['Bar','Baz'])
+ })
+
+ it('can filter an array of objects based on the objects label key', () => {
+ const vm = new Vue({
+ template: `
`,
+ data: {value: 'foo'}
+ }).$mount()
+ vm.$refs.select.search = 'ba'
+ expect(JSON.stringify(vm.$refs.select.filteredOptions)).toEqual(JSON.stringify([{label: 'Bar', value: 'bar'}, {label: 'Baz', value: 'baz'}]))
+ })
+ })
+
+ describe('Toggling Dropdown', () => {
+ it('should not open the dropdown when the el is clicked but the component is disabled', (done) => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: [{label: 'one'}],
+ options: [{label: 'one'}]
+ }
+ }).$mount()
+
+ vm.$children[0].toggleDropdown({target: vm.$children[0].$refs.search})
+ Vue.nextTick(() => {
+ Vue.nextTick(() => {
+ expect(vm.$children[0].open).toEqual(false)
+ done()
+ })
+ })
+ })
+
+ it('should open the dropdown when the el is clicked', (done) => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: [{label: 'one'}],
+ options: [{label: 'one'}]
+ }
+ }).$mount()
+
+ vm.$children[0].toggleDropdown({target: vm.$children[0].$refs.search})
+ Vue.nextTick(() => {
+ Vue.nextTick(() => {
+ expect(vm.$children[0].open).toEqual(true)
+ done()
+ })
+ })
+ })
+
+ it('can close the dropdown when the el is clicked', (done) => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ }).$mount()
+
+ spyOn(vm.$children[0].$refs.search, 'blur')
+
+ vm.$children[0].open = true
+ vm.$children[0].toggleDropdown({target: vm.$children[0].$el})
+
+ Vue.nextTick(() => {
+ expect(vm.$children[0].$refs.search.blur).toHaveBeenCalled()
+ done()
+ })
+ })
+
+
+ it('closes the dropdown when an option is selected, multiple is true, and closeOnSelect option is true', (done) => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: [],
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+
+ vm.$children[0].open = true
+ vm.$refs.select.select('one')
+
+ Vue.nextTick(() => {
+ expect(vm.$children[0].open).toEqual(false)
+ done()
+ })
+ })
+
+ it('does not close the dropdown when the el is clicked, multiple is true, and closeOnSelect option is false', (done) => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: [],
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+
+ vm.$children[0].open = true
+ vm.$refs.select.select('one')
+
+ Vue.nextTick(() => {
+ expect(vm.$children[0].open).toEqual(true)
+ done()
+ })
+ })
+
+
+ it('should close the dropdown on search blur', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: [{label: 'one'}],
+ options: [{label: 'one'}]
+ }
+ }).$mount()
+
+ vm.$children[0].open = true
+ triggerFocusEvent(vm.$children[0].$refs.toggle, 'blur')
+ expect(vm.$children[0].open).toEqual(true)
+ })
+
+ it('will close the dropdown and emit the search:blur event from onSearchBlur', () => {
+ const vm = new Vue({
+ template: '
',
+ }).$mount()
+
+ spyOn(vm.$children[0], '$emit')
+ vm.$children[0].open = true
+ vm.$children[0].onSearchBlur()
+
+ expect(vm.$children[0].open).toEqual(false)
+ expect(vm.$children[0].$emit).toHaveBeenCalledWith('search:blur')
+ })
+
+ it('will open the dropdown and emit the search:focus event from onSearchFocus', () => {
+ const vm = new Vue({
+ template: '
',
+ }).$mount()
+
+ spyOn(vm.$children[0], '$emit')
+ vm.$children[0].onSearchFocus()
+
+ expect(vm.$children[0].open).toEqual(true)
+ expect(vm.$children[0].$emit).toHaveBeenCalledWith('search:focus')
+ })
+
+ it('will close the dropdown on escape, if search is empty', (done) => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ }).$mount()
+
+ spyOn(vm.$children[0].$refs.search, 'blur')
+
+ vm.$children[0].open = true
+ vm.$children[0].onEscape()
+
+ Vue.nextTick(() => {
+ expect(vm.$children[0].$refs.search.blur).toHaveBeenCalled()
+ done()
+ })
+ })
+
+ it('should remove existing search text on escape keyup', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: [{label: 'one'}],
+ options: [{label: 'one'}]
+ }
+ }).$mount()
+
+ vm.$children[0].search = 'foo'
+ vm.$children[0].onEscape()
+ expect(vm.$children[0].search).toEqual('')
+ })
+
+ it('should have an open class when dropdown is active', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect}
+ }).$mount()
+
+ expect(vm.$children[0].dropdownClasses.open).toEqual(false)
+ })
+ })
+
+ describe('Moving the Typeahead Pointer', () => {
+ it('should set the pointer to zero when the filteredOptions change', (done) => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+
+ vm.$children[0].search = 'two'
+ Vue.nextTick(() => {
+ expect(vm.$children[0].typeAheadPointer).toEqual(0)
+ done()
+ })
+ })
+
+ it('should move the pointer visually up the list on up arrow keyDown', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+
+ vm.$children[0].typeAheadPointer = 1
+
+ trigger(vm.$children[0].$refs.search, 'keydown', (e) => e.keyCode = 38)
+ expect(vm.$children[0].typeAheadPointer).toEqual(0)
+ })
+
+ it('should move the pointer visually down the list on down arrow keyDown', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+
+ vm.$children[0].typeAheadPointer = 1
+ trigger(vm.$children[0].$refs.search, 'keydown', (e) => e.keyCode = 40)
+ expect(vm.$children[0].typeAheadPointer).toEqual(2)
+ })
+
+ it('should not move the pointer past the end of the list', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+
+ vm.$children[0].typeAheadPointer = 2
+ vm.$children[0].typeAheadDown()
+ expect(vm.$children[0].typeAheadPointer).toEqual(2)
+ })
+
+ describe('Automatic Scrolling', () => {
+ it('should check if the scroll position needs to be adjusted on up arrow keyDown', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+
+ vm.$children[0].typeAheadPointer = 1
+ spyOn(vm.$children[0], 'maybeAdjustScroll')
+ trigger(vm.$children[0].$refs.search, 'keydown', (e) => e.keyCode = 38)
+ expect(vm.$children[0].maybeAdjustScroll).toHaveBeenCalled()
+ })
+
+ it('should check if the scroll position needs to be adjusted on down arrow keyDown', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+
+ spyOn(vm.$children[0], 'maybeAdjustScroll')
+ trigger(vm.$children[0].$refs.search, 'keydown', (e) => e.keyCode = 40)
+ expect(vm.$children[0].maybeAdjustScroll).toHaveBeenCalled()
+ })
+
+ it('should check if the scroll position needs to be adjusted when filtered options changes', (done) => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+
+ spyOn(vm.$children[0], 'maybeAdjustScroll')
+ vm.$children[0].search = 'two'
+
+ Vue.nextTick(() => {
+ expect(vm.$children[0].maybeAdjustScroll).toHaveBeenCalled()
+ done()
+ })
+ })
+
+ it('should scroll up if the pointer is above the current viewport bounds', () => {
+ let methods = Object.assign(pointerScroll.methods, {
+ pixelsToPointerTop() {
+ return 1
+ },
+ viewport() {
+ return {top: 2, bottom: 0}
+ }
+ })
+ const vm = new Vue({
+ template: '
',
+ components: {
+ 'v-select': Mock({
+ '../mixins/pointerScroll': {methods}
+ })
+ },
+ }).$mount()
+
+ spyOn(vm.$children[0], 'scrollTo')
+ vm.$children[0].maybeAdjustScroll()
+ expect(vm.$children[0].scrollTo).toHaveBeenCalledWith(1)
+ })
+
+ /**
+ * @link https://github.com/vuejs/vue-loader/issues/434
+ * @todo vue-loader/inject-loader fails when used twice in the same file,
+ * so the mock here is abastracted to a separate file.
+ */
+ xit('should scroll down if the pointer is below the current viewport bounds', () => {
+ let methods = Object.assign(pointerScroll.methods, {
+ pixelsToPointerBottom() {
+ return 2
+ },
+ viewport() {
+ return {top: 0, bottom: 1}
+ }
+ })
+ const vm = new Vue({
+ template: `
`,
+ components: {
+ 'v-select': Mock({
+ '../mixins/pointerScroll': {methods}
+ })
+ },
+ }).$mount()
+
+ spyOn(vm.$children[0], 'scrollTo')
+ vm.$children[0].maybeAdjustScroll()
+ expect(vm.$children[0].scrollTo).toHaveBeenCalledWith(vm.$children[0].viewport().top + vm.$children[0].pointerHeight())
+ })
+ })
+
+ describe('Measuring pixel distances', () => {
+ it('should calculate pointerHeight as the offsetHeight of the pointer element if it exists', () => {
+ const vm = new Vue({
+ template: `
`,
+ }).$mount()
+
+ // dropdown must be open for $refs to exist
+ vm.$children[0].open = true
+
+ Vue.nextTick(() => {
+ // Fresh instances start with the pointer at -1
+ vm.$children[0].typeAheadPointer = -1
+ expect(vm.$children[0].pointerHeight()).toEqual(0)
+
+ vm.$children[0].typeAheadPointer = 100
+ expect(vm.$children[0].pointerHeight()).toEqual(0)
+
+ vm.$children[0].typeAheadPointer = 1
+ expect(vm.$children[0].pointerHeight()).toEqual(vm.$children[0].$refs.dropdownMenu.children[1].offsetHeight)
+ })
+ })
+ })
+ })
+
+ describe('Removing values', () => {
+ it('can remove the given tag when its close icon is clicked', (done) => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: ['one'],
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+ vm.$children[0].$refs.toggle.querySelector('.close').click()
+ Vue.nextTick(() => {
+ expect(vm.$children[0].mutableValue).toEqual([])
+ done()
+ })
+ })
+
+ it('should not remove tag when close icon is clicked and component is disabled', (done) => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: ['one'],
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+ vm.$children[0].$refs.toggle.querySelector('.close').click()
+ Vue.nextTick(() => {
+ expect(vm.$children[0].mutableValue).toEqual(['one'])
+ done()
+ })
+ })
+
+ it('should remove the last item in the value array on delete keypress when multiple is true', () => {
+
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: ['one', 'two'],
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+ vm.$children[0].maybeDeleteValue()
+ Vue.nextTick(() => {
+ expect(vm.$children[0].mutableValue).toEqual(['one'])
+ })
+ })
+
+ it('should set value to null on delete keypress when multiple is false', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: 'one',
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+ vm.$children[0].maybeDeleteValue()
+ Vue.nextTick(() => {
+ expect(vm.$children[0].mutableValue).toEqual(null)
+ })
+ })
+ })
+
+ describe('Labels', () => {
+ it('can generate labels using a custom label key', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: [{name: 'Baz'}],
+ options: [{name: 'Foo'}, {name: 'Baz'}]
+ }
+ }).$mount()
+ expect(vm.$children[0].$refs.toggle.querySelector('.selected-tag').textContent).toContain('Baz')
+ })
+
+ it('will console.warn when options contain objects without a valid label key', (done) => {
+ spyOn(console, 'warn')
+ const vm = new Vue({
+ template: '
',
+ }).$mount()
+ Vue.nextTick(() => {
+ expect(console.warn).toHaveBeenCalledWith(
+ '[vue-select warn]: Label key "option.label" does not exist in options object.' +
+ '\nhttp://sagalbot.github.io/vue-select/#ex-labels'
+ )
+ done()
+ })
+ })
+
+ it('should display a placeholder if the value is empty', (done) => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ options: [{label: 'one'}]
+ }
+ }).$mount()
+
+ expect(vm.$children[0].searchPlaceholder).toEqual('foo')
+ vm.$children[0].mutableValue = {label: 'one'}
+ Vue.nextTick(() => {
+ expect(vm.$children[0].searchPlaceholder).not.toBeDefined()
+ done()
+ })
+ })
+ })
+
+ describe('When Tagging Is Enabled', () => {
+ it('can determine if a given option string already exists', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ options: ['one', 'two']
+ }
+ }).$mount()
+
+ expect(vm.$refs.select.optionExists('one')).toEqual(true)
+ expect(vm.$refs.select.optionExists('three')).toEqual(false)
+ })
+
+ it('can determine if a given option object already exists', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ options: [{label: 'one'}, {label: 'two'}]
+ }
+ }).$mount()
+
+ expect(vm.$refs.select.optionExists('one')).toEqual(true)
+ expect(vm.$refs.select.optionExists('three')).toEqual(false)
+ })
+
+ it('can determine if a given option object already exists when using custom labels', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ options: [{foo: 'one'}, {foo: 'two'}]
+ }
+ }).$mount()
+
+ expect(vm.$refs.select.optionExists('one')).toEqual(true)
+ expect(vm.$refs.select.optionExists('three')).toEqual(false)
+ })
+
+ it('can add the current search text as the first item in the options list', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: ['one'],
+ options: ['one', 'two']
+ }
+ }).$mount()
+
+ vm.$children[0].search = 'three'
+ expect(vm.$children[0].filteredOptions).toEqual(['three'])
+ })
+
+ it('can select the current search text as a string', (done) => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: ['one'],
+ options: ['one', 'two']
+ }
+ }).$mount()
+
+ searchSubmit(vm, 'three')
+ Vue.nextTick(() => {
+ expect(vm.$children[0].mutableValue).toEqual(['one', 'three'])
+ done()
+ })
+ })
+
+ it('can select the current search text as an object', (done) => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: [{label: 'one'}],
+ options: [{label: 'one'}]
+ }
+ }).$mount()
+
+ searchSubmit(vm, 'two')
+ Vue.nextTick(() => {
+ expect(vm.$children[0].mutableValue).toEqual([{label: 'one'}, {label: 'two'}])
+ done()
+ })
+ })
+
+ it('should add a freshly created option/tag to the options list when pushTags is true', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: ['one'],
+ options: ['one', 'two']
+ }
+ }).$mount()
+
+ searchSubmit(vm, 'three')
+ expect(vm.$children[0].mutableOptions).toEqual(['one', 'two', 'three'])
+ })
+
+ it('should add a freshly created option/tag to the options list when pushTags is true and filterable is false', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: ['one'],
+ options: ['one', 'two']
+ }
+ }).$mount()
+
+ searchSubmit(vm, 'three')
+ expect(vm.$children[0].mutableOptions).toEqual(['one', 'two', 'three'])
+ expect(vm.$children[0].filteredOptions).toEqual(['one', 'two', 'three'])
+ })
+
+ it('wont add a freshly created option/tag to the options list when pushTags is false', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: ['one'],
+ options: ['one', 'two']
+ }
+ }).$mount()
+
+ searchSubmit(vm, 'three')
+ expect(vm.$children[0].mutableOptions).toEqual(['one', 'two'])
+ })
+
+ it('wont add a freshly created option/tag to the options list when pushTags is false and filterable is false', () => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: ['one'],
+ options: ['one', 'two']
+ }
+ }).$mount()
+
+ searchSubmit(vm, 'three')
+ expect(vm.$children[0].mutableOptions).toEqual(['one', 'two'])
+ expect(vm.$children[0].filteredOptions).toEqual(['one', 'two'])
+ })
+
+ it('should select an existing option if the search string matches a string from options', (done) => {
+ let two = 'two'
+ const vm = new Vue({
+ template: '
',
+ data: {
+ value: null,
+ options: ['one', two]
+ }
+ }).$mount()
+ vm.$children[0].search = 'two'
+
+ searchSubmit(vm)
+
+ Vue.nextTick(() => {
+ expect(vm.$children[0].mutableValue[0]).toBe(two)
+ done()
+ })
+ })
+
+ it('should select an existing option if the search string matches an objects label from options', (done) => {
+ let two = {label: 'two'}
+ const vm = new Vue({
+ template: '
',
+ data: {
+ options: [{label: 'one'}, two]
+ }
+ }).$mount()
+
+ vm.$children[0].search = 'two'
+
+ Vue.nextTick(() => {
+ searchSubmit(vm)
+ // This needs to be wrapped in nextTick() twice so that filteredOptions can
+ // calculate after setting the search text, and move the typeAheadPointer index to 0.
+ Vue.nextTick(() => {
+ expect(vm.$children[0].mutableValue.label).toBe(two.label)
+ done()
+ })
+ })
+ })
+
+ it('should select an existing option if the search string matches an objects label from options when filter-options is false', (done) => {
+ let two = {label: 'two'}
+ const vm = new Vue({
+ template: '
',
+ data: {
+ options: [{label: 'one'}, two]
+ }
+ }).$mount()
+
+ vm.$children[0].search = 'two'
+
+ Vue.nextTick(() => {
+ searchSubmit(vm)
+ // This needs to be wrapped in nextTick() twice so that filteredOptions can
+ // calculate after setting the search text, and move the typeAheadPointer index to 0.
+ Vue.nextTick(() => {
+ expect(vm.$children[0].mutableValue.label).toBe(two.label)
+ done()
+ })
+ })
+ })
+
+ it('should not reset the selected value when the options property changes', (done) => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: [{label: 'one'}],
+ options: [{label: 'one'}]
+ }
+ }).$mount()
+ vm.$children[0].mutableOptions = [{label: 'two'}]
+ Vue.nextTick(() => {
+ expect(vm.$children[0].mutableValue).toEqual([{label: 'one'}])
+ done()
+ })
+ })
+
+ it('should not reset the selected value when the options property changes when filterable is false', (done) => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: [{label: 'one'}],
+ options: [{label: 'one'}]
+ }
+ }).$mount()
+ vm.$children[0].mutableOptions = [{label: 'two'}]
+ Vue.nextTick(() => {
+ expect(vm.$children[0].mutableValue).toEqual([{label: 'one'}])
+ done()
+ })
+ })
+
+ it('should not allow duplicate tags when using string options', (done) => {
+ const vm = new Vue({
+ template: `
`,
+ }).$mount()
+ vm.$refs.select.search = 'one'
+ searchSubmit(vm)
+ Vue.nextTick(() => {
+ expect(vm.$refs.select.mutableValue).toEqual(['one'])
+ expect(vm.$refs.select.search).toEqual('')
+ vm.$refs.select.search = 'one'
+ searchSubmit(vm)
+ Vue.nextTick(() => {
+ expect(vm.$refs.select.mutableValue).toEqual([])
+ expect(vm.$refs.select.search).toEqual('')
+ done()
+ })
+ })
+ })
+
+ it('should not allow duplicate tags when using object options', (done) => {
+ const vm = new Vue({
+ template: `
`,
+ }).$mount()
+ vm.$refs.select.search = 'one'
+ searchSubmit(vm)
+ Vue.nextTick(() => {
+ expect(vm.$refs.select.mutableValue).toEqual(['one'])
+ expect(vm.$refs.select.search).toEqual('')
+ vm.$refs.select.search = 'one'
+ searchSubmit(vm)
+ Vue.nextTick(() => {
+ expect(vm.$refs.select.mutableValue).toEqual([])
+ expect(vm.$refs.select.search).toEqual('')
+ done()
+ })
+ })
+
+ })
+ })
+
+ describe('Asynchronous Loading', () => {
+ it('can toggle the loading class', () => {
+ const vm = new Vue({
+ template: '
',
+ }).$mount()
+
+ vm.$refs.select.toggleLoading()
+ expect(vm.$refs.select.mutableLoading).toEqual(true)
+
+ vm.$refs.select.toggleLoading(true)
+ expect(vm.$refs.select.mutableLoading).toEqual(true)
+ })
+
+ it('should trigger the onSearch callback when the search text changes', (done) => {
+ const vm = new Vue({
+ template: '
',
+ data: {
+ called: false
+ },
+ methods: {
+ foo(val) {
+ this.called = val
+ }
+ }
+ }).$mount()
+
+ vm.$refs.select.search = 'foo'
+
+ Vue.nextTick(() => {
+ expect(vm.called).toEqual('foo')
+ done()
+ })
+ })
+
+ it('should not trigger the onSearch callback if the search text is empty', (done) => {
+ const vm = new Vue({
+ template: '
',
+ data: { called: false },
+ methods: {
+ foo(val) {
+ this.called = ! this.called
+ }
+ }
+ }).$mount()
+
+ vm.$refs.select.search = 'foo'
+ Vue.nextTick(() => {
+ expect(vm.called).toBe(true)
+ vm.$refs.select.search = ''
+ Vue.nextTick(() => {
+ expect(vm.called).toBe(true)
+ done()
+ })
+ })
+ })
it('should trigger the search event when the search text changes', (done) => {
const vm = new Vue({
@@ -1133,7 +1139,7 @@ describe('Select.vue', () => {
called: false
},
methods: {
- foo (val) {
+ foo(val) {
this.called = val
}
}
@@ -1152,8 +1158,8 @@ describe('Select.vue', () => {
template: '
',
data: { called: false },
methods: {
- foo (val) {
- this.called = !this.called
+ foo(val) {
+ this.called = ! this.called
}
}
}).$mount()
@@ -1169,128 +1175,128 @@ describe('Select.vue', () => {
})
})
- it('can set loading to false from the onSearch callback', (done) => {
- const vm = new Vue({
- template: '
',
- methods: {
- foo (search, loading) {
- loading(false)
- }
- }
- }).$mount()
+ it('can set loading to false from the onSearch callback', (done) => {
+ const vm = new Vue({
+ template: '
',
+ methods: {
+ foo(search, loading) {
+ loading(false)
+ }
+ }
+ }).$mount()
- vm.$refs.select.search = 'foo'
- Vue.nextTick(() => {
- expect(vm.$refs.select.mutableLoading).toEqual(false)
- done()
- })
- })
+ vm.$refs.select.search = 'foo'
+ Vue.nextTick(() => {
+ expect(vm.$refs.select.mutableLoading).toEqual(false)
+ done()
+ })
+ })
- it('can set loading to true from the onSearch callback', (done) => {
- const vm = new Vue({
- template: '
',
- methods: {
- foo (search, loading) {
- loading(true)
- }
- }
- }).$mount()
+ it('can set loading to true from the onSearch callback', (done) => {
+ const vm = new Vue({
+ template: '
',
+ methods: {
+ foo(search, loading) {
+ loading(true)
+ }
+ }
+ }).$mount()
- let select = vm.$refs.select
- select.onSearch(select.search, select.toggleLoading)
+ let select = vm.$refs.select
+ select.onSearch(select.search, select.toggleLoading)
- Vue.nextTick(() => {
- expect(vm.$refs.select.mutableLoading).toEqual(true)
- done()
- })
- })
+ Vue.nextTick(() => {
+ expect(vm.$refs.select.mutableLoading).toEqual(true)
+ done()
+ })
+ })
- it('will sync mutable loading with the loading prop', (done) => {
+ it('will sync mutable loading with the loading prop', (done) => {
const vm = new Vue({
template: '
',
- data: {loading: false}
+ data: {loading:false}
}).$mount()
- vm.loading = true
- Vue.nextTick(() => {
- expect(vm.$refs.select.mutableLoading).toEqual(true)
- done()
- })
- })
- })
+ vm.loading = true
+ Vue.nextTick(() => {
+ expect(vm.$refs.select.mutableLoading).toEqual(true)
+ done()
+ })
+ })
+ })
- describe('Reset on options change', () => {
- it('should not reset the selected value by default when the options property changes', (done) => {
- const vm = new Vue({
- template: '
',
- data: {
- value: 'one',
- options: ['one', 'two', 'three']
- }
- }).$mount()
- vm.$children[0].mutableOptions = ['four', 'five', 'six']
- Vue.nextTick(() => {
- expect(vm.$children[0].mutableValue).toEqual('one')
- done()
- })
- })
+ describe('Reset on options change', () => {
+ it('should not reset the selected value by default when the options property changes', (done) => {
+ const vm = new Vue({
+ template: '
',
+ data: {
+ value: 'one',
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+ vm.$children[0].mutableOptions = ['four', 'five', 'six']
+ Vue.nextTick(() => {
+ expect(vm.$children[0].mutableValue).toEqual('one')
+ done()
+ })
+ })
- it('should reset the selected value when the options property changes', (done) => {
- const vm = new Vue({
- template: '
',
- components: {vSelect},
- data: {
- value: 'one',
- options: ['one', 'two', 'three']
- }
- }).$mount()
- vm.$children[0].mutableOptions = ['four', 'five', 'six']
- Vue.nextTick(() => {
- expect(vm.$children[0].mutableValue).toEqual(null)
- done()
- })
- })
- })
+ it('should reset the selected value when the options property changes', (done) => {
+ const vm = new Vue({
+ template: '
',
+ components: {vSelect},
+ data: {
+ value: 'one',
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+ vm.$children[0].mutableOptions = ['four', 'five', 'six']
+ Vue.nextTick(() => {
+ expect(vm.$children[0].mutableValue).toEqual(null)
+ done()
+ })
+ })
+ })
- describe('Single value options', () => {
- it('should reset the search input on focus lost', (done) => {
- const vm = new Vue({
- template: '
',
- data: {
- value: 'one',
- options: ['one', 'two', 'three']
- }
- }).$mount()
+ describe('Single value options', () => {
+ it('should reset the search input on focus lost', (done) => {
+ const vm = new Vue({
+ template: '
',
+ data: {
+ value: 'one',
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+
+ vm.$children[0].open = true
+ vm.$refs.select.search = "t"
+ expect(vm.$refs.select.search).toEqual('t')
+
+ vm.$children[0].onSearchBlur()
+ Vue.nextTick(() => {
+ expect(vm.$refs.select.search).toEqual('')
+ done()
+ })
+ })
- vm.$children[0].open = true
- vm.$refs.select.search = 't'
- expect(vm.$refs.select.search).toEqual('t')
+ it ('should not reset the search input on focus lost when clearSearchOnSelect is false', (done) => {
+ const vm = new Vue({
+ template: '
',
+ data: {
+ value: 'one',
+ options: ['one', 'two', 'three']
+ }
+ }).$mount()
+ expect(vm.$refs.select.clearSearchOnSelect).toEqual(false)
- vm.$children[0].onSearchBlur()
- Vue.nextTick(() => {
- expect(vm.$refs.select.search).toEqual('')
- done()
- })
- })
+ vm.$children[0].open = true
+ vm.$refs.select.search = "t"
+ expect(vm.$refs.select.search).toEqual('t')
- it('should not reset the search input on focus lost when clearSearchOnSelect is false', (done) => {
- const vm = new Vue({
- template: '
',
- data: {
- value: 'one',
- options: ['one', 'two', 'three']
- }
- }).$mount()
- expect(vm.$refs.select.clearSearchOnSelect).toEqual(false)
-
- vm.$children[0].open = true
- vm.$refs.select.search = 't'
- expect(vm.$refs.select.search).toEqual('t')
-
- vm.$children[0].onSearchBlur()
- Vue.nextTick(() => {
- expect(vm.$refs.select.search).toEqual('t')
- done()
- })
- })
- })
+ vm.$children[0].onSearchBlur()
+ Vue.nextTick(() => {
+ expect(vm.$refs.select.search).toEqual('t')
+ done()
+ })
+ })
+ })
})