diff --git a/docs/components/TextInput.md b/docs/components/TextInput.md index 769d9658..a8a7db9c 100644 --- a/docs/components/TextInput.md +++ b/docs/components/TextInput.md @@ -6,12 +6,10 @@ such as auto-complete, auto-focus, placeholder text, and event callbacks. Note: some props are exclusive to or excluded from `multiline`. Unsupported React Native props: -`autoCapitalize`, -`autoCorrect`, `onEndEditing`, -`onSubmitEditing`, `clearButtonMode` (ios), `enablesReturnKeyAutomatically` (ios), +`placeholderTextColor`, `returnKeyType` (ios), `selectionState` (ios), `underlineColorAndroid` (android) @@ -128,10 +126,6 @@ Callback that is called when the keyboard's submit button is pressed. The string that will be rendered in an empty `TextInput` before text has been entered. -**placeholderTextColor**: string - -The text color of the placeholder string. - **secureTextEntry**: bool = false If true, the text input obscures the text entered so that sensitive text like diff --git a/examples/components/TextInput/TextInputExample.js b/examples/components/TextInput/TextInputExample.js index df7f813f..8727cf6a 100644 --- a/examples/components/TextInput/TextInputExample.js +++ b/examples/components/TextInput/TextInputExample.js @@ -210,25 +210,16 @@ class TokenizedTextExample extends React.Component { } parts.push(_text); - //highlight hashtags - parts = parts.map((text) => { - if (/^#/.test(text)) { - return {text}; - } else { - return text; - } - }); - return ( { this.setState({text}); - }}> - {parts} - + }} + /> ); } @@ -279,7 +270,7 @@ class BlurOnSubmitExample extends React.Component { @@ -519,15 +510,15 @@ const examples = [ render: function() { var keyboardTypes = [ 'default', - 'ascii-capable', - 'numbers-and-punctuation', + //'ascii-capable', + //'numbers-and-punctuation', 'url', 'number-pad', 'phone-pad', - 'name-phone-pad', + //'name-phone-pad', 'email-address', - 'decimal-pad', - 'twitter', + //'decimal-pad', + //'twitter', 'web-search', 'numeric', ]; @@ -776,14 +767,6 @@ const examples = [ style={styles.multiline} dataDetectorTypes="phoneNumber" /> - - - ); } diff --git a/src/components/TextInput/__tests__/index-test.js b/src/components/TextInput/__tests__/index-test.js index 3f7e7c6b..eae052cc 100644 --- a/src/components/TextInput/__tests__/index-test.js +++ b/src/components/TextInput/__tests__/index-test.js @@ -1,15 +1,12 @@ /* eslint-env jasmine, jest */ import React from 'react'; -import StyleSheet from '../../../apis/StyleSheet'; import TextareaAutosize from 'react-textarea-autosize'; import TextInput from '..'; import { mount, shallow } from 'enzyme'; -const placeholderText = 'placeholderText'; const findNativeInput = (wrapper) => wrapper.find('input'); const findNativeTextarea = (wrapper) => wrapper.find(TextareaAutosize); -const findPlaceholder = (wrapper) => wrapper.find({ children: placeholderText }); const testIfDocumentIsFocused = (message, fn) => { if (document.hasFocus && document.hasFocus()) { @@ -184,24 +181,6 @@ describe('components/TextInput', () => { } }); - test('prop "placeholder"', () => { - let textInput = shallow(); - expect(findPlaceholder(textInput).length).toEqual(0); - - textInput = shallow(); - expect(findPlaceholder(textInput).length).toEqual(1); - }); - - test('prop "placeholderTextColor"', () => { - let placeholderElement = findPlaceholder(shallow()); - expect(StyleSheet.flatten(placeholderElement.prop('style')).color).toEqual('darkgray'); - - placeholderElement = findPlaceholder( - shallow() - ); - expect(StyleSheet.flatten(placeholderElement.prop('style')).color).toEqual('red'); - }); - test('prop "secureTextEntry"', () => { let input = findNativeInput(shallow()); expect(input.prop('type')).toEqual('password'); @@ -224,20 +203,6 @@ describe('components/TextInput', () => { // assert.equal(input.node.selectionStart, 0) }); - test('prop "style"', () => { - const styles = StyleSheet.create({ - root: { - borderWidth: 1, - textAlign: 'center' - } - }); - const textInput = shallow(); - const input = findNativeInput(textInput); - const borderWidth = StyleSheet.flatten(textInput.prop('style')).borderWidth; - expect(borderWidth).toEqual(1); - expect(input.prop('style').textAlign).toEqual('center'); - }); - test('prop "value"', () => { const value = 'value'; const input = findNativeInput(shallow()); diff --git a/src/components/TextInput/index.js b/src/components/TextInput/index.js index 9dc98b7f..f60a1277 100644 --- a/src/components/TextInput/index.js +++ b/src/components/TextInput/index.js @@ -1,18 +1,14 @@ +import applyLayout from '../../modules/applyLayout'; import applyNativeMethods from '../../modules/applyNativeMethods'; import createDOMElement from '../../modules/createDOMElement'; import findNodeHandle from '../../modules/findNodeHandle'; -import omit from 'lodash/omit'; -import pick from 'lodash/pick'; import StyleSheet from '../../apis/StyleSheet'; import Text from '../Text'; import TextareaAutosize from 'react-textarea-autosize'; import TextInputState from './TextInputState'; import UIManager from '../../apis/UIManager'; import View from '../View'; -import ViewStylePropTypes from '../View/ViewStylePropTypes'; -import React, { Component, PropTypes } from 'react'; - -const viewStyleProps = Object.keys(ViewStylePropTypes); +import { Component, PropTypes } from 'react'; /** * React Native events differ from W3C events. @@ -25,7 +21,7 @@ const normalizeEventHandler = (handler) => (e) => { }; /** - * Determins whether a 'selection' prop differs from a node's existing + * Determines whether a 'selection' prop differs from a node's existing * selection state. */ const isSelectionStale = (node, selection) => { @@ -64,7 +60,7 @@ class TextInput extends Component { defaultValue: PropTypes.string, editable: PropTypes.bool, keyboardType: PropTypes.oneOf([ - 'default', 'email-address', 'numeric', 'phone-pad', 'search', 'url', 'web-search' + 'default', 'email-address', 'number-pad', 'numeric', 'phone-pad', 'search', 'url', 'web-search' ]), maxLength: PropTypes.number, maxNumberOfLines: PropTypes.number, @@ -85,7 +81,6 @@ class TextInput extends Component { end: PropTypes.number }), style: Text.propTypes.style, - testID: Text.propTypes.testID, value: PropTypes.string }; @@ -101,60 +96,63 @@ class TextInput extends Component { style: {} }; - constructor(props, context) { - super(props, context); - this.state = { showPlaceholder: !props.value && !props.defaultValue }; - } - blur() { - TextInputState.blurTextInput(findNodeHandle(this._inputRef)); + TextInputState.blurTextInput(this._node); } clear() { - this.setNativeProps({ text: '' }); + this._node.value = ''; } focus() { - TextInputState.focusTextInput(findNodeHandle(this._inputRef)); + TextInputState.focusTextInput(this._node); } isFocused() { - return TextInputState.currentlyFocusedField() === findNodeHandle(this._inputRef); + return TextInputState.currentlyFocusedField() === this._node; } setNativeProps(props) { - UIManager.updateView(this._inputRef, props, this); + UIManager.updateView(this._node, props, this); } componentDidMount() { - setSelection(findNodeHandle(this._inputRef), this.props.selection); + setSelection(this._node, this.props.selection); } componentDidUpdate() { - setSelection(findNodeHandle(this._inputRef), this.props.selection); + setSelection(this._node, this.props.selection); } render() { const { - accessibilityLabel, // eslint-disable-line - autoCapitalize, - autoComplete, autoCorrect, - autoFocus, - defaultValue, editable, keyboardType, - maxLength, maxNumberOfLines, multiline, numberOfLines, - onLayout, - placeholder, - placeholderTextColor, secureTextEntry, style, - testID, - value + /* eslint-disable */ + blurOnSubmit, + clearTextOnFocus, + dataDetectorTypes, + enablesReturnKeyAutomatically, + keyboardAppearance, + onChangeText, + onContentSizeChange, + onEndEditing, + onLayout, + onSelectionChange, + onSubmitEditing, + placeholderTextColor, + returnKeyType, + selection, + selectionColor, + selectTextOnFocus, + /* eslint-enable */ + ...other } = this.props; let type; @@ -163,6 +161,7 @@ class TextInput extends Component { case 'email-address': type = 'email'; break; + case 'number-pad': case 'numeric': type = 'number'; break; @@ -184,29 +183,22 @@ class TextInput extends Component { type = 'password'; } - // In order to support 'Text' styles on 'TextInput', we split the 'Text' - // and 'View' styles and apply them to the 'Text' and 'View' components - // used in the implementation - const flattenedStyle = StyleSheet.flatten(style); - const rootStyles = pick(flattenedStyle, viewStyleProps); - const textStyles = omit(flattenedStyle, viewStyleProps); + const component = multiline ? TextareaAutosize : 'input'; const props = { - autoCapitalize, - autoComplete, + ...other, autoCorrect: autoCorrect ? 'on' : 'off', - autoFocus, - defaultValue, - maxLength, onBlur: normalizeEventHandler(this._handleBlur), onChange: normalizeEventHandler(this._handleChange), onFocus: normalizeEventHandler(this._handleFocus), onKeyPress: normalizeEventHandler(this._handleKeyPress), onSelect: normalizeEventHandler(this._handleSelectionChange), readOnly: !editable, - ref: this._setInputRef, - style: [ styles.input, textStyles, { outline: style.outline } ], - value + ref: this._setNode, + style: [ + styles.initial, + style + ] }; if (multiline) { @@ -216,66 +208,27 @@ class TextInput extends Component { props.type = type; } - const component = multiline ? TextareaAutosize : 'input'; - - const optionalPlaceholder = placeholder && this.state.showPlaceholder && ( - - - - ); - - return ( - - - {createDOMElement(component, props)} - {optionalPlaceholder} - - - ); + return createDOMElement(component, props); } _handleBlur = (e) => { const { onBlur } = this.props; - const { text } = e.nativeEvent; - this.setState({ showPlaceholder: text === '' }); if (onBlur) { onBlur(e); } } _handleChange = (e) => { const { onChange, onChangeText } = this.props; const { text } = e.nativeEvent; - this.setState({ showPlaceholder: text === '' }); if (onChange) { onChange(e); } if (onChangeText) { onChangeText(text); } } - _handleClick = (e) => { - if (this.props.editable) { - this.focus(); - } - } - _handleFocus = (e) => { const { clearTextOnFocus, onFocus, selectTextOnFocus } = this.props; - const { text } = e.nativeEvent; - const node = findNodeHandle(this._inputRef); + const node = this._node; if (onFocus) { onFocus(e); } if (clearTextOnFocus) { this.clear(); } if (selectTextOnFocus) { node && node.select(); } - this.setState({ showPlaceholder: text === '' }); } _handleKeyPress = (e) => { @@ -303,43 +256,24 @@ class TextInput extends Component { } } - _setInputRef = (component) => { - this._inputRef = component; + _setNode = (component) => { + this._node = findNodeHandle(component); } } const styles = StyleSheet.create({ initial: { - borderColor: 'black' - }, - wrapper: { - flex: 1 - }, - input: { appearance: 'none', backgroundColor: 'transparent', + borderColor: 'black', borderRadius: 0, borderWidth: 0, boxSizing: 'border-box', color: 'inherit', flex: 1, font: 'inherit', - minHeight: '100%', // center small inputs (fix #139) - padding: 0, - zIndex: 1 - }, - placeholder: { - bottom: 0, - left: 0, - position: 'absolute', - right: 0, - top: 0 - }, - placeholderText: { - color: 'darkgray', - overflow: 'hidden', - whiteSpace: 'pre' + padding: 0 } }); -module.exports = applyNativeMethods(TextInput); +module.exports = applyLayout(applyNativeMethods(TextInput)); diff --git a/src/modules/applyLayout/index.js b/src/modules/applyLayout/index.js index 8cb3de08..7213773a 100644 --- a/src/modules/applyLayout/index.js +++ b/src/modules/applyLayout/index.js @@ -12,13 +12,13 @@ const applyLayout = (Component) => { const componentDidUpdate = Component.prototype.componentDidUpdate || emptyFunction; Component.prototype.componentDidMount = function () { - componentDidMount(); + componentDidMount.call(this); this._layoutState = {}; this._handleLayout(); }; Component.prototype.componentDidUpdate = function () { - componentDidUpdate(); + componentDidUpdate.call(this); this._handleLayout(); };