Skip to content

Commit

Permalink
Add inputElement prop (#263)
Browse files Browse the repository at this point in the history
  • Loading branch information
angelo-v authored and moroshko committed Oct 12, 2016
1 parent 5dbd2b8 commit 8c98185
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 3 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Check out the <a href="http://react-autosuggest.js.org" target="_blank">Homepage
* You decide [when to show suggestions](#shouldRenderSuggestionsProp) (e.g. when user types 2 or more characters)
* [Always render suggestions](#alwaysRenderSuggestionsProp) (useful for mobile and modals)
* [Pass through arbitrary props to the input field](#inputPropsProp) (e.g. placeholder, type, [onChange](#inputPropsOnChange), [onBlur](#inputPropsOnBlur), or any other)
* You can use a [custom input element](#inputElementProp)
* Thoroughly tested

## Installation
Expand Down Expand Up @@ -167,6 +168,7 @@ class Example extends React.Component {
| [`getSuggestionValue`](#getSuggestionValueProp) | Function || Implement it to teach Autosuggest what should be the input value when suggestion is clicked. |
| [`renderSuggestion`](#renderSuggestionProp) | Function || Use your imagination to define how suggestions are rendered. |
| [`inputProps`](#inputPropsProp) | Object || Pass through arbitrary props to the input field. It must contain at least `value` and `onChange`. |
| [`inputElement`](#inputElementProp) | String or React Component | | Use a custom input, instead of an html input element. |
| [`onSuggestionSelected`](#onSuggestionSelectedProp) | Function | | Will be called every time suggestion is selected via mouse or keyboard. |
| [`shouldRenderSuggestions`](#shouldRenderSuggestionsProp) | Function | | When input field is focused, Autosuggest will consult this function when to render suggestions. Use it, for example, if you want to display suggestions when input value is at least 2 characters long. |
| [`alwaysRenderSuggestions`](#alwaysRenderSuggestionsProp) | Boolean | | Set it to `true` if you'd like to render suggestions even when the input field is not focused. |
Expand Down Expand Up @@ -354,6 +356,26 @@ where:

* `focusedSuggestion` - the suggestion that was highlighted just before the input field lost focus, or `null` if there was no highlighted suggestion.

<a name="inputElementProp"></a>
#### inputElement (optional)

Define a custom input element that will be used instead of an html `<input />`:

```
<Autosuggest inputElement={MyCustomInput} inputProps={inputProps} />
```

`MyCustomInput` may either be a class extending `React.Component` or a stateless function component.

The `inputProps` will be passed to your component's `props`.
If the custom component wraps an html input, ensure that at least `value` and `onChange` are passed to it.

You may also use a string as `inputElement` to use a different html tag, if that makes sense for you:

```
<Autosuggest inputElement="textarea" inputProps={inputProps} />
```

<a name="onSuggestionSelectedProp"></a>
#### onSuggestionSelected (optional)

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"prepublish": "npm run dist && npm run standalone"
},
"dependencies": {
"react-autowhatever": "^5.3.0",
"react-autowhatever": "^5.4.0",
"react-redux": "^4.4.5",
"redux": "^3.5.1",
"shallow-equal": "^1.0.0"
Expand Down
5 changes: 5 additions & 0 deletions src/Autosuggest.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ class Autosuggest extends Component {
getSuggestionValue: PropTypes.func.isRequired,
renderSuggestion: PropTypes.func.isRequired,
inputProps: PropTypes.object.isRequired,
inputElement: PropTypes.oneOfType([
PropTypes.func,
PropTypes.string
]).isRequired,
shouldRenderSuggestions: PropTypes.func.isRequired,
alwaysRenderSuggestions: PropTypes.bool.isRequired,
multiSection: PropTypes.bool.isRequired,
Expand Down Expand Up @@ -440,6 +444,7 @@ class Autosuggest extends Component {
inputProps={autowhateverInputProps}
itemProps={this.itemProps}
theme={theme}
inputElement={this.props.inputElement}
id={id}
ref={this.storeReferences} />
);
Expand Down
10 changes: 8 additions & 2 deletions src/AutosuggestContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ export default class AutosuggestContainer extends Component {
throw new Error('\'inputProps\' must have \'onChange\'.');
}
},
inputElement: PropTypes.oneOfType([
PropTypes.func,
PropTypes.string
]),
shouldRenderSuggestions: PropTypes.func,
alwaysRenderSuggestions: PropTypes.bool,
multiSection: PropTypes.bool,
Expand Down Expand Up @@ -108,7 +112,8 @@ export default class AutosuggestContainer extends Component {
focusInputOnSuggestionClick: true,
focusFirstSuggestion: false,
theme: defaultTheme,
id: '1'
id: '1',
inputElement: 'input'
};

constructor({ alwaysRenderSuggestions }) {
Expand All @@ -134,7 +139,7 @@ export default class AutosuggestContainer extends Component {
suggestions, onSuggestionsFetchRequested, onSuggestionsClearRequested,
multiSection, shouldRenderSuggestions, renderSuggestionsContainer,
getSuggestionValue, renderSuggestion, renderSectionTitle, getSectionSuggestions,
inputProps, onSuggestionSelected, focusInputOnSuggestionClick, focusFirstSuggestion,
inputProps, inputElement, onSuggestionSelected, focusInputOnSuggestionClick, focusFirstSuggestion,
alwaysRenderSuggestions, theme, id
} = this.props;

Expand All @@ -152,6 +157,7 @@ export default class AutosuggestContainer extends Component {
renderSectionTitle={renderSectionTitle}
getSectionSuggestions={getSectionSuggestions}
inputProps={inputProps}
inputElement={inputElement}
onSuggestionSelected={onSuggestionSelected}
focusInputOnSuggestionClick={focusInputOnSuggestionClick}
focusFirstSuggestion={focusFirstSuggestion}
Expand Down
47 changes: 47 additions & 0 deletions test/custom-input/AutosuggestApp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React, { Component } from 'react';
import sinon from 'sinon';
import Autosuggest from '../../src/AutosuggestContainer';
import CustomInput from './CustomInput';
let app = null;

export const onChange = sinon.spy((event, { newValue }) => {
app.setState({
value: newValue
});
});

const onSuggestionsFetchRequested = () => {};
const getSuggestionValue = () => 'value';
const renderSuggestion = () => <span>test</span>;

export default class AutosuggestApp extends Component {
constructor() {
super();

app = this;

this.state = {
value: '',
suggestions: []
};
}

render() {
const { value, suggestions } = this.state;
const inputProps = {
value,
onChange
};

return (
<Autosuggest
suggestions={suggestions}
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
inputElement={CustomInput}
alwaysRenderSuggestions={true} />
);
}
}
20 changes: 20 additions & 0 deletions test/custom-input/AutosuggestApp.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import {
init,
expectInputElement
} from '../helpers';
import AutosuggestApp from './AutosuggestApp';
import CustomInput from './CustomInput';

describe('Autosuggest with inputElement', () => {
beforeEach(() => {
init(TestUtils.renderIntoDocument(<AutosuggestApp />));
});

describe('initially', () => {
it('should render the inputElement', () => {
expectInputElement(CustomInput);
});
});
});
18 changes: 18 additions & 0 deletions test/custom-input/CustomInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React, { Component } from 'react';

export default class CustomInput extends Component {
constructor() {
super();
this.state = {
custom: 42
};
}
render() {
return (
<div className="my-custom-input">
<input {...this.props} />
<span>(customized)</span>
</div>
);
}
}
6 changes: 6 additions & 0 deletions test/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ export function expectInputAttribute(attributeName, expectedValue) {
expect(input.getAttribute(attributeName)).to.equal(expectedValue);
}

export function expectInputElement(type) {
const customInput = TestUtils.findRenderedComponentWithType(app, type);

expect(customInput).to.exist;
}

export function getSuggestionsContainerAttribute(attributeName) {
return suggestionsContainer.getAttribute(attributeName);
}
Expand Down

0 comments on commit 8c98185

Please sign in to comment.