Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement helper and error text support in TextInput #243

Merged
merged 1 commit into from
May 5, 2018

Conversation

kpsroka
Copy link
Contributor

@kpsroka kpsroka commented Feb 16, 2018

Please take a look at the example app for a demo of how it looks like/works. For now, I didn't want to refactor this into a separate element -- we should do that once we know what it should look like in other components. One not implemented feature is to have an icon in the error text. The material docs use error-outline, but we might consider allowing any (or any material?) icon source.

(Resolves #175)

@kpsroka kpsroka requested a review from satya164 February 16, 2018 15:42
@callstack-bot
Copy link

callstack-bot commented Feb 16, 2018

Hey @kpsroka, thank you for your pull request 🤗. The documentation from this branch can be viewed here.

@kpsroka kpsroka changed the title feat: implement helper and error text support in TextField feat: implement helper and error text support in TextInput Feb 19, 2018
@kpsroka
Copy link
Contributor Author

kpsroka commented Feb 26, 2018

@satya164 Can you please take a look at this?

Copy link
Member

@satya164 satya164 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think it's better to just separate helper text into a separate component.

<TextInput error={true} text={this.state.text} onTextChange={text => this.setState({ text })} />
<HelperText error={true}>
  Please enter at least 3 characters
</HelperText>

This looks a little bit verbose, but we can add a Form component later which handles validation for various components and reduces boilerplate.

For now, I didn't want to refactor this into a separate element -- we should do that once we know what it should look like in other components

Keep in mind that once this is merged to master, we can't change it without making it a breaking change, and I'd like to avoid that at such a short interval after the release. If you want to check how other libs do it, here is an example: https://material-ui-next.com/api/form-helper-text/

/**
* Whether to style the TextInput with error style.
*/
hasError: boolean,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think instead of this, a validate prop might be better,

validate={text => text.length < 3 ? new Error('Please enter at least 3 characters') : null}

There are 2 advantages: first, it can be used with both controlled and uncontrolled inputs, currently, you need to maintain a state in your component to do validation. second, it reduces duplication since the logic of both checking whether there is an error and the error message are in the same place, and makes impossible states impossible, e.g. the user cannot pass hasError={true} without an error text.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should wait for the FormInput helper.

: 0;

/* Move label to top if value is set */
const labelTranslateY = value
? -22
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's extract this number to a constant to the top of the file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

? -22
: this.state.focused.interpolate({
inputRange: [0, 1],
outputRange: [0, -22],
});
const fontSize = value

const labelFontSize = value
? 12
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extract to a constant

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

style={[styles.bottomLine, { backgroundColor: inactiveColor }]}
style={[
styles.bottomLine,
{ backgroundColor: (hasError && errorColor) || inactiveColor },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasError ? errorColor : inactiveColor looks better

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

style={[
styles.bottomLine,
styles.focusLine,
bottomLineStyle(bottomLineColor, this.state.focused),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's rename it to something like getBottomLineStyle

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

/>
</View>
{(helperText || errorText) && (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will return the helperText/errorText if they are empty string and {(helperText || errorText) && (...)} will evaluate to '', RN will throw an error saying that it's not possible to render raw string outside text. let's change this to a ternary

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -22,6 +22,11 @@ const DarkTheme: Theme = {
.alpha(0.38)
.rgb()
.string(),
helperText: color(white)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't put component specific styles in theme. Let's move these to the component

Copy link
Contributor Author

@kpsroka kpsroka Mar 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed helperText from here, however I think that errorText is generic enough and helpful enough (for custom theming) to warrant its inclusion here.

@kpsroka
Copy link
Contributor Author

kpsroka commented Mar 8, 2018

How would you like the extracted component to be used? Let's assume we have a HelperText component. We have the following options:

  1. We let users use HelperText without restrictions, then they need to wrap it in some other View that aligns their components with HelperText. So, there's a lot of repetition on the client side, since there's a very common use case of having them under TextInput. Also, the users need to handle potential resizing of the HelperText, which can lead to error-prone code.
    1. We can add a prop to TextInput (and other common cases) to use a custom component in the underline area, and manage potential issues with resizing as well. This is quite permissive, and can lead to users abusing this and ending up writing crazy issues.
  2. We keep HelperText internal, and either:
    1. spread the props to TextInput (and other form components), or
    2. introduce a helperTextProps prop to pass on to HelperText (and disable HelperText altogether).

I'm in favor of the last option. This makes sure that users don't need to spend time on possibly error-prone code. On the other hand, it encapsulates the HelperText from other components, so that breaking changes in HelperText are restricted to users of that feature.

@satya164
Copy link
Member

satya164 commented Mar 8, 2018

but we can add a Form component later which handles validation for various components and reduces boilerplate

@kpsroka
Copy link
Contributor Author

kpsroka commented Mar 28, 2018

Hi @satya164 ,
As requested, I split most of the functionality into a separate component. The changes in TextInput are to support red coloring and wiggle animation of minimized label. I don't think introducing the 'verify' prop in TextInput is a good think -- the upcoming FormInput seems like a better place for such functionality.

PTAL.

@ferrannp ferrannp removed the wip label Apr 4, 2018
@kpsroka
Copy link
Contributor Author

kpsroka commented Apr 5, 2018

@satya164 Please review this PR again.

src/types.js Outdated
text: string,
disabled: string,
placeholder: string,
errorText: string,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't usually put component specific styles in theme. Also it's not clear what's the difference between error and errorText here. Let's move this one to the component.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


type Props = {
/**
* @optional
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need @optional. React docgen will infer it from flow types.

* @optional
* Text color to use.
*/
color?: string,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we don't need this props since people can already override the color with the theme prop.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which theme color should be used here? The MD specs differentiate between the placeholder and helper text colors. Should we introduce a helper text color in the theme?


state: State;

componentWillReceiveProps(nextProps) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

componentWillReceiveProps is deprecated, so should use getDerivedStateFromProps and use react-lifecycle-compat to support older version.

Though in this case, since it does a side-effect, we should use componentDidUpdate instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


componentWillReceiveProps(nextProps) {
if (nextProps.hasError !== this.props.hasError) {
(nextProps.hasError ? this._animateFocus : this._animateBlur)(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be clearer to do:

if (nextProps.hasError) {
  this._animateFocus();
} else {
  this._animateBlur();
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -13,6 +13,11 @@ import type { Theme } from '../types';

const AnimatedText = Animated.createAnimatedComponent(Text);

const minimizedLabelYOffset = -22;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually use UPPER_SNAKE_CASE for constants

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


getHelperTextColor(dark: boolean) {
return dark
? colorModule(white)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do:

color(theme.colors.text)
  .alpha(dark ? 0.7 : 0.54)
  .rgb()
  .string();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return (
text && (
<Animated.View style={containerStyle}>
{text && <Text style={[styles.helperText, { color }]}>{text}</Text>}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant check

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/**
* Whether to style the TextInput with error style.
*/
hasError: boolean,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just error: boolean since we usually skip prefixes everywhere else, like disabled, multiline

const styles = StyleSheet.create({
helperText: {
fontSize: 12,
marginTop: 4,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be marginVertical: 4?

@kpsroka
Copy link
Contributor Author

kpsroka commented Apr 20, 2018

I applied the requested changes, let's take a look at it together on Tuesday and merge.

@kpsroka kpsroka removed the wip label Apr 20, 2018
@kpsroka
Copy link
Contributor Author

kpsroka commented Apr 26, 2018

@satya164 I applied the changes that we discussed today, but I kept the hasError prop name.

@satya164 satya164 force-pushed the feat/helper-text branch from 7cb9ffc to aa3bee7 Compare May 1, 2018 09:03
Copy link
Member

@satya164 satya164 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The screenshots are too small in retina. The size should be same as the ones in TextInput docs. Also, need some simple snapshot tests.

image

If you have time to fix these, let's do it, or I'll do it when I'm back.

@satya164 satya164 force-pushed the feat/helper-text branch 2 times, most recently from 9e27da8 to 2fda6f8 Compare May 1, 2018 09:37
@satya164 satya164 force-pushed the feat/helper-text branch from 2fda6f8 to ea7109e Compare May 5, 2018 16:03
@satya164 satya164 merged commit 7c054c0 into master May 5, 2018
@satya164 satya164 deleted the feat/helper-text branch May 5, 2018 16:06
eriveltonelias pushed a commit to eriveltonelias/react-native-paper that referenced this pull request Sep 21, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants