Skip to content

Commit

Permalink
feat: implement helper and error text support in TextInput (#243)
Browse files Browse the repository at this point in the history
  • Loading branch information
kpsroka authored and satya164 committed May 5, 2018
1 parent d3e7beb commit 7c054c0
Show file tree
Hide file tree
Showing 11 changed files with 305 additions and 29 deletions.
Binary file added docs/assets/screenshots/helper-text.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 19 additions & 2 deletions example/src/TextInputExample.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/* @flow */

import * as React from 'react';
import { ScrollView, StyleSheet } from 'react-native';
import { TextInput, withTheme } from 'react-native-paper';
import { ScrollView, StyleSheet, View } from 'react-native';
import { TextInput, HelperText, withTheme } from 'react-native-paper';
import type { Theme } from 'react-native-paper/types';

type Props = {
Expand All @@ -11,21 +11,26 @@ type Props = {

type State = {
text: string,
name: string,
};

class TextInputExample extends React.Component<Props, State> {
static title = 'TextInput';

state = {
text: '',
name: '',
};

_isUsernameValid = () => /^[a-z]*$/.test(this.state.name);

render() {
const {
theme: {
colors: { background },
},
} = this.props;

return (
<ScrollView style={[styles.container, { backgroundColor: background }]}>
<TextInput
Expand All @@ -40,6 +45,18 @@ class TextInputExample extends React.Component<Props, State> {
style={styles.inputContainerStyle}
label="Disabled Input"
/>
<View style={styles.inputContainerStyle}>
<TextInput
label="Input with helper text"
placeholder="Enter username, only letters"
value={this.state.name}
error={!this._isUsernameValid()}
onChangeText={name => this.setState({ name })}
/>
<HelperText type="error" visible={!this._isUsernameValid()}>
Error: Only letters are allowed
</HelperText>
</View>
</ScrollView>
);
}
Expand Down
162 changes: 162 additions & 0 deletions src/components/HelperText.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/* @flow */

import * as React from 'react';
import color from 'color';
import { Animated, StyleSheet } from 'react-native';
import Text from './Typography/Text';
import withTheme from '../core/withTheme';
import type { Theme } from '../types';

const AnimatedText = Animated.createAnimatedComponent(Text);

type Props = {
/**
* Type of the helper text.
*/
type: 'error' | 'info',
/**
* Whether to display the helper text.
*/
visible?: boolean,
/**
* Text content of the HelperText.
*/
children: React.Node,
style?: any,
/**
* @optional
*/
theme: Theme,
};

type State = {
shown: Animated.Value,
textHeight: number,
};

/**
* Helper text is used in conjuction with input elements to provide additional hints for the user.
*
* <div class="screenshots">
* <img class="medium" src="screenshots/helper-text.gif" />
* </div>
*
* ## Usage
* ```js
* import * as React from 'react';
* import { HelperText, TextInput } from 'react-native-paper';
*
* class MyComponent extends React.Component {
* state = {
* text: ''
* };
*
* render(){
* return (
* <View>
* <TextInput
* label="Email"
* value={this.state.text}
* onChangeText={text => this.setState({ text })}
* />
* <HelperText
* type="error"
* visible={!this.state.text.includes('@')}
* >
* Email address is invalid!
* </HelperText>
* </View>
* );
* }
* }
* ```
*/
class HelperText extends React.PureComponent<Props, State> {
static defaultProps = {
type: 'info',
visible: true,
};

state = {
shown: new Animated.Value(this.props.visible ? 1 : 0),
textHeight: 0,
};

componentDidUpdate(prevProps) {
if (prevProps.visible !== this.props.visible) {
if (this.props.visible) {
this._animateFocus();
} else {
this._animateBlur();
}
}
}

_animateFocus = () => {
Animated.timing(this.state.shown, {
toValue: 1,
duration: 150,
}).start();
};

_animateBlur = () => {
Animated.timing(this.state.shown, {
toValue: 0,
duration: 180,
}).start();
};

_handleTextLayout = e =>
this.setState({
textHeight: e.nativeEvent.layout.height,
});

render() {
const { style, type, visible, theme } = this.props;
const { colors, dark } = theme;

const textColor =
this.props.type === 'error'
? colors.error
: color(colors.text)
.alpha(dark ? 0.7 : 0.54)
.rgb()
.string();

return (
<AnimatedText
onLayout={this._handleTextLayout}
style={[
styles.text,
{
color: textColor,
opacity: this.state.shown,
transform:
visible && type === 'error'
? [
{
translateY: this.state.shown.interpolate({
inputRange: [0, 1],
outputRange: [-this.state.textHeight, 0],
}),
},
]
: [],
},
style,
]}
>
{this.props.children}
</AnimatedText>
);
}
}

const styles = StyleSheet.create({
text: {
fontSize: 12,
paddingVertical: 4,
},
});

export default withTheme(HelperText);
Loading

0 comments on commit 7c054c0

Please sign in to comment.