Skip to content

Commit

Permalink
Merge pull request #9 from cburbank/custom-input
Browse files Browse the repository at this point in the history
feat(input): Allow custom input element
  • Loading branch information
hibiken authored Jan 9, 2017
2 parents a570253 + 0ff0299 commit a0ca540
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 10 deletions.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,31 @@ export default SimpleForm

### Props for `PlacesAutocomplete`

#### children
Type: `Element`
Required: `false`

You can add autocomplete functionality to an existing input element by wrapping it in `<PlacesAutocomplete>`.
The wrapper will pass `onChange`, `onKeyDown`, and `value` props down to the child component.

```js
// custom input element example
import MyCustomInput from 'my-custom-input'

...

render() {
return (
<PlacesAutocomplete
value={this.state.value}
onChange={this.onChange}
>
<MyCustomInput/>
</PlacesAutocomplete>
)
}
```

#### value
Type: `String`,
Required: `true`
Expand Down
37 changes: 27 additions & 10 deletions src/PlacesAutocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,24 +202,40 @@ class PlacesAutocomplete extends React.Component {
)
}

renderCustomInput() {
const { children, value } = this.props
return React.cloneElement(children, {
onChange: this.handleInputChange,
onKeyDown: this.handleInputKeyDown,
value
})
}

renderDefaultInput() {
const { classNames, placeholder, styles, value } = this.props
return (
<input
type="text"
placeholder={placeholder}
className={classNames.input || ''}
value={value}
onChange={this.handleInputChange}
onKeyDown={this.handleInputKeyDown}
style={styles.input}
/>
)
}

// TODO: remove `classNames.container` in the next version release.
render() {
const { classNames, placeholder, styles, value } = this.props
const { classNames, children, styles } = this.props
return (
<div
style={{ ...defaultStyles.root, ...styles.root }}
className={classNames.root || classNames.container || ''}
>
{this.renderLabel()}
<input
type="text"
placeholder={placeholder}
className={classNames.input || ''}
value={value}
onChange={this.handleInputChange}
onKeyDown={this.handleInputKeyDown}
style={styles.input}
/>
{children ? this.renderCustomInput() : this.renderDefaultInput()}
{this.renderOverlay()}
{this.renderAutocomplete()}
</div>
Expand All @@ -228,6 +244,7 @@ class PlacesAutocomplete extends React.Component {
}

PlacesAutocomplete.propTypes = {
children: React.PropTypes.element,
value: React.PropTypes.string.isRequired,
onChange: React.PropTypes.func.isRequired,
onSelect: React.PropTypes.func,
Expand Down
33 changes: 33 additions & 0 deletions src/tests/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,39 @@ describe('AutocompletionRequest options', () => {
})
})

describe('custom input component', () => {
it('renders a custom input component passed as a child', () => {
const wrapper = shallow(<PlacesAutocomplete value="LA" onChange={() => {}}><input className="test-input" type="text" onChange={() => {}}/></PlacesAutocomplete>)
expect(wrapper.find('.test-input')).to.have.length(1)
})

it('adds the correct props to the child component', () => {
const wrapper = shallow(<PlacesAutocomplete value="LA" onChange={() => {}}><input className="test-input" type="text"/></PlacesAutocomplete>)
expect(wrapper.find('.test-input').props().onChange).to.be.defined
expect(wrapper.find('.test-input').props().onKeyDown).to.be.defined
expect(wrapper.find('.test-input').props().value).to.be.defined
})

it('correctly sets the value prop of the custom input component', () => {
const wrapper = shallow(<PlacesAutocomplete value="LA" onChange={() => {}}><input className="test-input" type="text" onChange={() => {}}/></PlacesAutocomplete>)
expect(wrapper.find('.test-input').props().value).to.equal('LA')
})

it('executes the onChange callback when the custom input is changed', () => {
const spy = sinon.spy()
const wrapper = shallow(<PlacesAutocomplete value="LA" onChange={spy}><input className="test-input" type="text"/></PlacesAutocomplete>)
wrapper.find('.test-input').simulate('change', { target: { value: null } })
expect(spy.calledOnce).to.equal(true)
})

it('executes handleInputKeyDown when a keyDown event happens on the custom input', () => {
const spy = sinon.spy(PlacesAutocomplete.prototype, 'handleInputKeyDown')
const wrapper = shallow(<PlacesAutocomplete value="LA" onChange={() => {}}><input className="test-input" type="text"/></PlacesAutocomplete>)
wrapper.find('.test-input').simulate('keyDown', { keyCode: null })
expect(spy.calledOnce).to.equal(true)
})
})

// TODO: test geocodeByAddress function
describe('geocodeByAddress', () => {
it('should be true', () => {
Expand Down

0 comments on commit a0ca540

Please sign in to comment.