Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow displayOption function to return an element #149

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
56 changes: 28 additions & 28 deletions dist/react-typeahead.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]) {
Expand Down Expand Up @@ -141,40 +141,40 @@ fuzzy.match = function(pattern, string, opts) {
// // string to put after matching character
// , post: '</b>'
//
// // 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; }
// }
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;
});
};


Expand Down
3 changes: 3 additions & 0 deletions src/typeahead/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion src/typeahead/option.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
},

Expand Down
35 changes: 35 additions & 0 deletions test/typeahead-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(<Typeahead
options={ BEATLES_COMPLEX }
filterOption='firstName'
displayOption={ function(o, i) { return <span>{i + ' ' + o.firstName + ' ' + o.lastName}</span>; } }
/>);
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(<Typeahead
options={ BEATLES_COMPLEX.map(function (o) {
var newO = {
firstName: o.firstName,
lastName: o.lastName,
}
newO.toString = function () {
return this.firstName + ' ' + this.lastName
}
return newO
}) }
filterOption='firstName'
displayOption={ function(o, i) { return <span>{i + ' ' + o.firstName + ' ' + o.lastName}</span>; } }
/>);
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() {
Expand Down