Last active
July 31, 2018 04:07
-
-
Save catchin/47afe706256604959c13dc25e7bb9383 to your computer and use it in GitHub Desktop.
This is a workaround for the buggy react-native TextInput multiline on Android. Inspired by the comments on https://github.com/facebook/react-native/issues/12717.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, {PropTypes, PureComponent} from 'react'; | |
import {TextInput} from 'react-native'; | |
import debounce from 'debounce'; | |
/** | |
* This is a workaround for the buggy react-native TextInput multiline on Android. | |
* | |
* Can be removed once https://github.com/facebook/react-native/issues/12717 | |
* is fixed. | |
* | |
* Example for usage: | |
* <MultilineTextInput value={this.state.text} onChangeText={text => setState({text})} /> | |
*/ | |
export default class MultilineTextInput extends PureComponent { | |
constructor(props) { | |
super(props); | |
this.state = {selection: {start: 0, end: 0}}; | |
// Prevent 2 newlines for some Android versions, because they dispatch onSubmitEditing twice | |
this.onSubmitEditing = debounce(this.onSubmitEditing.bind(this), 100, true); | |
} | |
onSubmitEditing() { | |
const {selection} = this.state; | |
const {value} = this.props; | |
const newText = `${value.slice(0, selection.start)}\n${value.slice(selection.end)}`; | |
// move cursor only for this case, because in other cases a change of the selection is not allowed by Android | |
if (selection.start !== this.props.value.length && selection.start === selection.end) { | |
this.setState({ | |
selection: { | |
start: selection.start + 1, | |
end: selection.end + 1, | |
}, | |
}); | |
} | |
this.props.onChangeText(newText); | |
} | |
render() { | |
return ( | |
<TextInput | |
multiline | |
blurOnSubmit={false} | |
selection={this.state.selection} | |
value={this.props.value} | |
onSelectionChange={event => this.setState({selection: event.nativeEvent.selection})} | |
onChangeText={this.props.onChangeText} | |
onSubmitEditing={this.onSubmitEditing} | |
{...this.props} | |
/> | |
); | |
} | |
} | |
MultilineTextInput.propTypes = { | |
value: PropTypes.string.isRequired, | |
onChangeText: PropTypes.func.isRequired, | |
}; | |
////////////////// Tests ////////////////// | |
import 'react-native'; | |
// Require after react-native | |
import renderer from 'react-test-renderer'; | |
import React from 'react'; | |
import {shallow} from 'enzyme'; | |
import MultilineTextInput from '../MultilineTextInput'; | |
describe('MultilineTextInput', () => { | |
const text = 'some value'; | |
const onChangeText = jest.fn(); | |
const defaultProps = { | |
value: text, | |
onChangeText, | |
}; | |
it('renders correctly', () => { | |
const tree = renderer.create(<MultilineTextInput {...defaultProps} />).toJSON(); | |
expect(tree).toMatchSnapshot(); | |
}); | |
it('inserts a new line at the end if the "enter" soft key is pressed', () => { | |
const component = shallow(<MultilineTextInput {...defaultProps} />); | |
component.simulate('selectionChange', {nativeEvent: {selection: {start: text.length, end: text.length}}}); | |
component.simulate('submitEditing'); | |
expect(onChangeText).toBeCalledWith('some value\n'); | |
}); | |
it('inserts a new line in the middle if the cursor is in the middle and the "enter" soft key is pressed', () => { | |
const component = shallow(<MultilineTextInput {...defaultProps} />); | |
component.simulate('selectionChange', {nativeEvent: {selection: {start: 5, end: 5}}}); | |
component.simulate('submitEditing'); | |
expect(onChangeText).toBeCalledWith('some \nvalue'); | |
}); | |
it('inserts a new line in the middle if text is selected and the "enter" soft key is pressed', () => { | |
const component = shallow(<MultilineTextInput {...defaultProps} />); | |
component.simulate('selectionChange', {nativeEvent: {selection: {start: 4, end: 7}}}); | |
component.simulate('submitEditing'); | |
expect(onChangeText).toBeCalledWith('some\nlue'); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here's the updated code
https://gist.github.com/codewithpassion/d2d1a3e4d94c547e6b343ea5e9fe9845