diff --git a/.travis.yml b/.travis.yml index 15dc6f2..4b6901d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: node_js node_js: - - "5" - - "5.1" + - node + - 8 + - 6 after_success: - codeclimate-test-reporter < ./test/unit/coverage/lcov.info diff --git a/dev.html b/dev.html index 829a8da..af0a322 100644 --- a/dev.html +++ b/dev.html @@ -31,6 +31,7 @@
+ @@ -47,8 +48,14 @@ - + + + +
diff --git a/package.json b/package.json index 60b4a2b..702490c 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "extract-text-webpack-plugin": "^1.0.1", "file-loader": "^0.8.4", "function-bind": "^1.0.2", + "fuse.js": "^3.2.0", "gh-pages": "^0.11.0", "gitbook-plugin-codepen": "^0.1.2", "gitbook-plugin-edit-link": "^2.0.2", diff --git a/src/components/Select.vue b/src/components/Select.vue index d64a2d6..7bfda2f 100644 --- a/src/components/Select.vue +++ b/src/components/Select.vue @@ -525,21 +525,6 @@ } }, - /** - * Callback to filter the search result the label text. - * @type {Function} - * @param {Object || String} option - * @param {String} label - * @param {String} search - * @return {Boolean} - */ - filterFunction: { - type: Function, - default(option, label, search) { - return (label || '').toLowerCase().indexOf(search.toLowerCase()) > -1 - } - }, - /** * An optional callback function that is called each time the selected * value(s) change. When integrating with Vuex, use this callback to trigger @@ -593,6 +578,47 @@ default: true }, + /** + * Callback to determine if the provided option should + * match the current search text. Used to determine + * if the option should be displayed. + * @type {Function} + * @param {Object || String} option + * @param {String} label + * @param {String} search + * @return {Boolean} + */ + filterBy: { + type: Function, + default(option, label, search) { + return (label || '').toLowerCase().indexOf(search.toLowerCase()) > -1 + } + }, + + /** + * Callback to filter results when search text + * is provided. Default implementation loops + * each option, and returns the result of + * this.filterBy. + * @type {Function} + * @param {Array} list of options + * @param {String} search text + * @param {Object} vSelect instance + * @return {Boolean} + */ + filter: { + "type": Function, + default(options, search) { + return options.filter((option) => { + let label = this.getOptionLabel(option) + if (typeof label === 'number') { + label = label.toString() + } + return this.filterBy(option, label, search) + }); + } + }, + /** * User defined function for adding Options * @type {Function} @@ -988,13 +1014,7 @@ if (!this.filterable && !this.taggable) { return this.mutableOptions.slice() } - let options = this.mutableOptions.filter((option) => { - let label = this.getOptionLabel(option) - if (typeof label === 'number') { - label = label.toString() - } - return this.filterFunction(option, label, this.search) - }) + let options = this.search.length ? this.filter(this.mutableOptions, this.search, this) : this.mutableOptions; if (this.taggable && this.search.length && !this.optionExists(this.search)) { options.unshift(this.search) } diff --git a/src/dev.js b/src/dev.js index 314174b..385059b 100644 --- a/src/dev.js +++ b/src/dev.js @@ -1,8 +1,10 @@ import Vue from 'vue' -import vSelect from './components/Select.vue' -import countries from 'docs/data/advanced.js' +import Fuse from 'fuse.js' import debounce from 'lodash/debounce' import resource from 'vue-resource' +import vSelect from './components/Select.vue' +import countries from 'docs/data/advanced.js' +import fuseSearchOptions from './fuseSearchOptions' Vue.use(resource) @@ -18,11 +20,12 @@ new Vue({ value: null, options: countries, ajaxRes: [], - people: [] + people: [], + fuseSearchOptions }, methods: { search(search, loading) { - loading(true) + loading(true); this.getRepositories(search, loading, this) }, searchPeople(search, loading) { @@ -37,9 +40,14 @@ new Vue({ }, 250), getRepositories: debounce((search, loading, vm) => { vm.$http.get(`https://api.github.com/search/repositories?q=${search}`).then(res => { - vm.ajaxRes = res.data.items + vm.ajaxRes = res.data.items; loading(false) }) - }, 250) + }, 250), + fuseSearch(options, search) { + return new Fuse(options, { + keys: ['title', 'author.firstName', 'author.lastName'], + }).search(search); + } } -}) +}); \ No newline at end of file diff --git a/src/fuseSearchOptions.js b/src/fuseSearchOptions.js new file mode 100644 index 0000000..bd4606c --- /dev/null +++ b/src/fuseSearchOptions.js @@ -0,0 +1,163 @@ +export default [ + { + title: "Old Man's War", + author: { + firstName: "John", + lastName: "Scalzi" + } + }, + { + title: "The Lock Artist", + author: { + firstName: "Steve", + lastName: "Hamilton" + } + }, + { + title: "HTML5", + author: { + firstName: "Remy", + lastName: "Sharp" + } + }, + { + title: "Right Ho Jeeves", + author: { + firstName: "P.D", + lastName: "Woodhouse" + } + }, + { + title: "The Code of the Wooster", + author: { + firstName: "P.D", + lastName: "Woodhouse" + } + }, + { + title: "Thank You Jeeves", + author: { + firstName: "P.D", + lastName: "Woodhouse" + } + }, + { + title: "The DaVinci Code", + author: { + firstName: "Dan", + lastName: "Brown" + } + }, + { + title: "Angels & Demons", + author: { + firstName: "Dan", + lastName: "Brown" + } + }, + { + title: "The Silmarillion", + author: { + firstName: "J.R.R", + lastName: "Tolkien" + } + }, + { + title: "Syrup", + author: { + firstName: "Max", + lastName: "Barry" + } + }, + { + title: "The Lost Symbol", + author: { + firstName: "Dan", + lastName: "Brown" + } + }, + { + title: "The Book of Lies", + author: { + firstName: "Brad", + lastName: "Meltzer" + } + }, + { + title: "Lamb", + author: { + firstName: "Christopher", + lastName: "Moore" + } + }, + { + title: "Fool", + author: { + firstName: "Christopher", + lastName: "Moore" + } + }, + { + title: "Incompetence", + author: { + firstName: "Rob", + lastName: "Grant" + } + }, + { + title: "Fat", + author: { + firstName: "Rob", + lastName: "Grant" + } + }, + { + title: "Colony", + author: { + firstName: "Rob", + lastName: "Grant" + } + }, + { + title: "Backwards, Red Dwarf", + author: { + firstName: "Rob", + lastName: "Grant" + } + }, + { + title: "The Grand Design", + author: { + firstName: "Stephen", + lastName: "Hawking" + } + }, + { + title: "The Book of Samson", + author: { + firstName: "David", + lastName: "Maine" + } + }, + { + title: "The Preservationist", + author: { + firstName: "David", + lastName: "Maine" + } + }, + { + title: "Fallen", + author: { + firstName: "David", + lastName: "Maine" + } + }, + { + title: "Monster 1959", + author: { + firstName: "David", + lastName: "Maine" + } + } +] \ No newline at end of file diff --git a/test/unit/specs/Select.spec.js b/test/unit/specs/Select.spec.js index d6184a8..8aa1e82 100644 --- a/test/unit/specs/Select.spec.js +++ b/test/unit/specs/Select.spec.js @@ -322,9 +322,9 @@ describe('Select.vue', () => { expect(JSON.stringify(vm.$refs.select.filteredOptions)).toEqual(JSON.stringify([{label: 'Bar', value: 'bar'}, {label: 'Baz', value: 'baz'}])) }) - it('can use a custom filterFunction passed via props', ()=>{ + it('can determine if a given option should match the current search text', () => { const vm = new Vue({ - template: `
`, + template: `
`, data: {value: 'foo'}, methods:{ customFn: (option, label, search) => label.match(new RegExp('^' + search, 'i')) @@ -333,6 +333,35 @@ describe('Select.vue', () => { vm.$refs.select.search = 'a' expect(JSON.stringify(vm.$refs.select.filteredOptions)).toEqual(JSON.stringify([{label: 'Aoo', value: 'foo'}])) }) + + it('can use a custom filtering method', () => { + const vm = new Vue({ + template: `
`, + data: { + options: ['foo','bar','baz'], + value: 'foo' + }, + methods:{ + customFn(options, search, vm) { + return options.filter(option => option.indexOf(search) > 0) + } + } + }).$mount() + vm.$refs.select.search = 'a' + expect(JSON.stringify(vm.$refs.select.filteredOptions)).toEqual(JSON.stringify(['bar','baz'])) + }) + + it('can filter arrays of numbers', () => { + const vm = new Vue({ + template: `
`, + data: { + options: [1,5,10], + value: 'foo' + }, + }).$mount() + vm.$refs.select.search = '5' + expect(JSON.stringify(vm.$refs.select.filteredOptions)).toEqual(JSON.stringify([5])) + }) }) describe('Toggling Dropdown', () => { @@ -780,6 +809,7 @@ describe('Select.vue', () => { const vm = new Vue({ template: '
', }).$mount() + vm.$children[0].open = true Vue.nextTick(() => { expect(console.warn).toHaveBeenCalledWith( '[vue-select warn]: Label key "option.label" does not exist in options object {}.' + diff --git a/yarn.lock b/yarn.lock index d66667b..d02c3d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1009,8 +1009,8 @@ caniuse-api@^1.5.2: lodash.uniq "^4.5.0" caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: - version "1.0.30000792" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000792.tgz#a7dac6dc9f5181b675fd69e5cb06fefb523157f8" + version "1.0.30000789" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000789.tgz#5cf3fec75480041ab162ca06413153141e234325" caseless@~0.11.0: version "0.11.0" @@ -1076,16 +1076,6 @@ change-case@3.0.x: upper-case "^1.1.1" upper-case-first "^1.1.0" -cheerio@^0.19.0: - version "0.19.0" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.19.0.tgz#772e7015f2ee29965096d71ea4175b75ab354925" - dependencies: - css-select "~1.0.0" - dom-serializer "~0.1.0" - entities "~1.1.1" - htmlparser2 "~3.8.1" - lodash "^3.2.0" - cheerio@^0.20.0: version "0.20.0" resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.20.0.tgz#5c710f2bab95653272842ba01c6ea61b3545ec35" @@ -1244,7 +1234,7 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@2.12.x: +commander@2.12.x, commander@^2.9.0, commander@~2.12.1: version "2.12.2" resolved "https://registry.yarnpkg.com/commander/-/commander-2.12.2.tgz#0f5946c427ed9ec0d91a46bb9def53e54650e555" @@ -1260,10 +1250,6 @@ commander@2.9.0, commander@2.9.x: dependencies: graceful-readlink ">= 1.0.0" -commander@^2.9.0, commander@~2.13.0: - version "2.13.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" - commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -1428,15 +1414,6 @@ css-select@^1.1.0, css-select@~1.2.0: domutils "1.5.1" nth-check "~1.0.1" -css-select@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.0.0.tgz#b1121ca51848dd264e2244d058cee254deeb44b0" - dependencies: - boolbase "~1.0.0" - css-what "1.0" - domutils "1.4" - nth-check "~1.0.0" - css-selector-tokenizer@^0.5.1: version "0.5.4" resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.5.4.tgz#139bafd34a35fd0c1428487049e0699e6f6a2c21" @@ -1452,10 +1429,6 @@ css-selector-tokenizer@^0.7.0: fastparse "^1.1.1" regexpu-core "^1.0.0" -css-what@1.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-1.0.0.tgz#d7cc2df45180666f99d2b14462639469e00f736c" - css-what@2.1: version "2.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" @@ -1601,14 +1574,10 @@ delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" -depd@1.1.1: +depd@1.1.1, depd@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" -depd@~1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" @@ -1679,12 +1648,6 @@ domutils@1.1: dependencies: domelementtype "1" -domutils@1.4: - version "1.4.3" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.4.3.tgz#0865513796c6b306031850e175516baf80b72a6f" - dependencies: - domelementtype "1" - domutils@1.5, domutils@1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" @@ -2168,6 +2131,10 @@ function-bind@^1.0.2: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" +fuse.js@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-3.2.0.tgz#f0448e8069855bf2a3e683cdc1d320e7e2a07ef4" + gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -2227,22 +2194,6 @@ gh-pages@^0.11.0: q-io "1.13.2" wrench "1.5.8" -gitbook-plugin-codepen@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/gitbook-plugin-codepen/-/gitbook-plugin-codepen-0.1.2.tgz#64fb5ba9be9734328a44bab2214422c044a85f16" - dependencies: - cheerio "^0.19.0" - underscore "^1.8.3" - url-parse "^1.0.0" - -gitbook-plugin-edit-link@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/gitbook-plugin-edit-link/-/gitbook-plugin-edit-link-2.0.2.tgz#d8fcd927eced81e7a662a72d59db609eafd7e72f" - -gitbook-plugin-github@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/gitbook-plugin-github/-/gitbook-plugin-github-3.0.0.tgz#67457df98a57ea8ef9b2518b88340db370a5317b" - glob-base@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" @@ -3371,7 +3322,7 @@ lodash.words@^3.0.0: dependencies: lodash._root "^3.0.0" -lodash@^3.2.0, lodash@^3.8.0: +lodash@^3.8.0: version "3.10.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" @@ -3766,10 +3717,6 @@ normalize-url@^1.4.0: query-string "^4.1.0" sort-keys "^1.0.0" -normalize.css@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-7.0.0.tgz#abfb1dd82470674e0322b53ceb1aaf412938e4bf" - "npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" @@ -3779,7 +3726,7 @@ normalize.css@^7.0.0: gauge "~2.7.3" set-blocking "~2.0.0" -nth-check@~1.0.0, nth-check@~1.0.1: +nth-check@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" dependencies: @@ -4418,10 +4365,6 @@ querystring@0.2.0, querystring@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" -querystringify@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-1.0.0.tgz#6286242112c5b712fa654e526652bf6a13ff05cb" - randomatic@^1.1.3: version "1.1.7" resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" @@ -4484,7 +4427,7 @@ readable-stream@1.1: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.2, readable-stream@^2.3.3: +readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.2, readable-stream@^2.2.6: version "2.3.3" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" dependencies: @@ -4730,7 +4673,7 @@ require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" -requires-port@1.x.x, requires-port@~1.0.0: +requires-port@1.x.x: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" @@ -5045,12 +4988,12 @@ stream-browserify@^2.0.1: readable-stream "^2.0.2" stream-http@^2.3.1: - version "2.8.0" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.0.tgz#fd86546dac9b1c91aff8fc5d287b98fafb41bc10" + version "2.7.2" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.2.tgz#40a050ec8dc3b53b33d9909415c02c0bf1abfbad" dependencies: builtin-status-codes "^3.0.0" inherits "^2.0.1" - readable-stream "^2.3.3" + readable-stream "^2.2.6" to-arraybuffer "^1.0.0" xtend "^4.0.0" @@ -5302,10 +5245,10 @@ uglify-js@2.6.x: yargs "~3.10.0" uglify-js@3.3.x: - version "3.3.7" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.3.7.tgz#28463e7c7451f89061d2b235e30925bf5625e14d" + version "3.3.5" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.3.5.tgz#4c4143dfe08e8825746675cc49a6874a933b543e" dependencies: - commander "~2.13.0" + commander "~2.12.1" source-map "~0.6.1" uglify-js@^2.6: @@ -5338,10 +5281,6 @@ ultron@1.0.x: version "1.0.2" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" -underscore@^1.8.3: - version "1.8.3" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" - underscore@~1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" @@ -5387,13 +5326,6 @@ url-parse-lax@^1.0.0: dependencies: prepend-http "^1.0.1" -url-parse@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.2.0.tgz#3a19e8aaa6d023ddd27dcc44cb4fc8f7fec23986" - dependencies: - querystringify "~1.0.0" - requires-port "~1.0.0" - url-to-options@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"