diff --git a/README.md b/README.md index 1e4b842f..d5d6494e 100644 --- a/README.md +++ b/README.md @@ -148,10 +148,12 @@ If provided as a string, it will interpret it as a field name and fuzzy filter o Type: `String` or `Function` -A function to map an option onto a string for display in the list. Receives `(option, index)` where index is relative to the results list, not all the options. Must return a string. +A function to map an option onto a string for display in the list. Receives `(option, index)` where index is relative to the results list, not all the options. Must return a string or an element. If provided as a string, it will interpret it as a field name and use that field from each option object. +If provided function returns an element, toString method of the option is used to determine the value set to the input and passed to onOptionSelected. If toString is not implemented, the value will most likely be `[object Object]`. + #### props.formInputOption Type: `String` or `Function` diff --git a/dist/react-typeahead.js b/dist/react-typeahead.js index f00d84d3..c65b704b 100644 --- a/dist/react-typeahead.js +++ b/dist/react-typeahead.js @@ -99,7 +99,7 @@ fuzzy.match = function(pattern, string, opts) { pattern = opts.caseSensitive && pattern || pattern.toLowerCase(); // For each character in the string, either add it to the result - // or wrap in template if its the next string in the pattern + // or wrap in template if it's the next string in the pattern for(var idx = 0; idx < len; idx++) { ch = string[idx]; if(compareString[idx] === pattern[patternIdx]) { @@ -141,8 +141,8 @@ fuzzy.match = function(pattern, string, opts) { // // string to put after matching character // , post: '' // -// // Optional function. Input is an element from the passed in -// // `arr`, output should be the string to test `pattern` against. +// // Optional function. Input is an entry in the given arr`, +// // output should be the string to test `pattern` against. // // In this example, if `arr = [{crying: 'koala'}]` we would return // // 'koala'. // , extract: function(arg) { return arg.crying; } @@ -150,31 +150,31 @@ fuzzy.match = function(pattern, string, opts) { fuzzy.filter = function(pattern, arr, opts) { opts = opts || {}; return arr - .reduce(function(prev, element, idx, arr) { - var str = element; - if(opts.extract) { - str = opts.extract(element); - } - var rendered = fuzzy.match(pattern, str, opts); - if(rendered != null) { - prev[prev.length] = { - string: rendered.rendered - , score: rendered.score - , index: idx - , original: element - }; - } - return prev; - }, []) - - // Sort by score. Browsers are inconsistent wrt stable/unstable - // sorting, so force stable by using the index in the case of tie. - // See http://ofb.net/~sethml/is-sort-stable.html - .sort(function(a,b) { - var compare = b.score - a.score; - if(compare) return compare; - return a.index - b.index; - }); + .reduce(function(prev, element, idx, arr) { + var str = element; + if(opts.extract) { + str = opts.extract(element); + } + var rendered = fuzzy.match(pattern, str, opts); + if(rendered != null) { + prev[prev.length] = { + string: rendered.rendered + , score: rendered.score + , index: idx + , original: element + }; + } + return prev; + }, []) + + // Sort by score. Browsers are inconsistent wrt stable/unstable + // sorting, so force stable by using the index in the case of tie. + // See http://ofb.net/~sethml/is-sort-stable.html + .sort(function(a,b) { + var compare = b.score - a.score; + if(compare) return compare; + return a.index - b.index; + }); }; diff --git a/src/typeahead/index.js b/src/typeahead/index.js index c13d2dd0..5e3b2c5e 100644 --- a/src/typeahead/index.js +++ b/src/typeahead/index.js @@ -171,6 +171,9 @@ var Typeahead = React.createClass({ var displayOption = this._generateOptionToStringFor(this.props.displayOption); var optionString = displayOption(option, 0); + if (typeof optionString !== 'string') { + optionString = String(option) + } var formInputOption = this._generateOptionToStringFor(this.props.formInputOption || displayOption); var formInputOptionString = formInputOption(option); diff --git a/src/typeahead/option.js b/src/typeahead/option.js index ff035b53..44c02178 100644 --- a/src/typeahead/option.js +++ b/src/typeahead/option.js @@ -13,7 +13,9 @@ var TypeaheadOption = React.createClass({ customClasses: React.PropTypes.object, customValue: React.PropTypes.string, onClick: React.PropTypes.func, - children: React.PropTypes.string, + children: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.element]), hover: React.PropTypes.bool }, diff --git a/test/typeahead-test.js b/test/typeahead-test.js index 83fb658f..eb6858a7 100644 --- a/test/typeahead-test.js +++ b/test/typeahead-test.js @@ -193,6 +193,41 @@ describe('Typeahead Component', function() { var results = simulateTextInput(component, 'john'); assert.equal(results[0].getDOMNode().textContent, '0 John Lennon'); }); + + it('renders custom element options when specified as a function returning an element', function() { + var component = TestUtils.renderIntoDocument({i + ' ' + o.firstName + ' ' + o.lastName}; } } + />); + var results = simulateTextInput(component, 'john'); + assert.equal(results[0].getDOMNode().firstChild.firstChild.nodeName, 'SPAN'); + assert.equal(results[0].getDOMNode().textContent, '0 John Lennon'); + }); + + it('uses toString of the option if displayOption returns an element', function() { + var component = TestUtils.renderIntoDocument({i + ' ' + o.firstName + ' ' + o.lastName}; } } + />); + var results = simulateTextInput(component, 'john'); + var node = component.refs.entry; + assert.equal(results[0].getDOMNode().firstChild.firstChild.nodeName, 'SPAN'); + assert.equal(results[0].getDOMNode().textContent, '0 John Lennon'); + console.log(typeof 'string', typeof {}) + TestUtils.Simulate.click(results[0].getDOMNode().firstChild); + assert.equal(node.value, 'John Lennon'); + }); }); context('allowCustomValues', function() {