Repository: react-materialize/react-materialize
Pull request #722 (2018-12-16 12:30:38)

3.0.0 text input stories
TextInput is not uncontrolled + added stories


Patch:
diff --git a/src/TextInput.js b/src/TextInput.js
index c92e46a..8c64784 100644
--- a/src/TextInput.js
+++ b/src/TextInput.js
@@ -7,20 +7,16 @@ import constants from './constants';
 class TextInput extends Component {
   constructor(props) {
     super(props);
-    this.state = {
-      value: ''
-    };
 
     this.id = props.id || idgen();
-    this.handleChange = this.handleChange.bind(this);
-  }
 
-  handleChange(event) {
-    const { target: { value } } = event;
-    const { onChange } = this.props;
-    if (onChange) onChange(event);
+    if (props.password) {
+      this.id = 'password';
+    }
 
-    this.setState({ value });
+    if (props.email) {
+      this.id = 'email';
+    }
   }
 
   componentDidUpdate(prevProps) {
@@ -45,6 +41,8 @@ class TextInput extends Component {
       inputClassName,
       success,
       error,
+      password,
+      email,
       validate,
       value,
       type
@@ -62,17 +60,29 @@ class TextInput extends Component {
 
     const wrapperClasses = cx('input-field', responsiveClasses);
 
+    let computedType;
+    if (type) {
+      computedType = type;
+    } else if (password) {
+      computedType = 'password';
+    } else if (email) {
+      computedType = 'email';
+    } else {
+      computedType = 'text';
+    }
+
     const inputProps = {
       placeholder,
-      type: type || 'text',
+      type: computedType,
       id: this.id,
-      value: value || this.state.value,
+      defaultValue: value,
       disabled
     };
 
     const renderLabel = () =>
       label && (
         <label
+          className={cx({ active: value || placeholder })}
           data-success={success}
           data-error={error}
           htmlFor={inputProps.id}
@@ -81,6 +91,15 @@ class TextInput extends Component {
         </label>
       );
 
+    const renderHelper = () =>
+      [error || success] && (
+        <span
+          className="helper-text"
+          data-error={error}
+          data-success={success}
+        />
+      );
+
     const renderIcon = () =>
       icon && <i className="material-icons prefix">{icon}</i>;
 
@@ -96,6 +115,7 @@ class TextInput extends Component {
           {...inputProps}
         />
         {renderLabel()}
+        {renderHelper()}
       </div>
     );
   }
@@ -170,7 +190,15 @@ TextInput.propTypes = {
   /*
    * onChange callback
    */
-  onChange: PropTypes.func
+  onChange: PropTypes.func,
+  /*
+   * password type
+   */
+  password: PropTypes.bool,
+  /*
+   * email type
+   */
+  email: PropTypes.bool
 };
 
 export default TextInput;
diff --git a/stories/Checkbox.stories.js b/stories/Checkbox.stories.js
index f162bb7..5b489bd 100644
--- a/stories/Checkbox.stories.js
+++ b/stories/Checkbox.stories.js
@@ -2,7 +2,7 @@ import React from 'react';
 import { storiesOf } from '@storybook/react';
 import Checkbox from '../src/Checkbox';
 
-const stories = storiesOf('javascript/Checkbox', module);
+const stories = storiesOf('components/Checkbox', module);
 
 stories.addParameters({
   info: {
diff --git a/stories/TextInput.stories.js b/stories/TextInput.stories.js
new file mode 100644
index 0000000..d48974c
--- /dev/null
+++ b/stories/TextInput.stories.js
@@ -0,0 +1,52 @@
+import React from 'react';
+import { storiesOf } from '@storybook/react';
+import TextInput from '../src/TextInput';
+
+const stories = storiesOf('components/TextInput', module);
+
+stories.addParameters({
+  info: {
+    text: `Text fields allow user input. The border should light
+     up simply and clearly indicating which field the user is 
+     currently editing.
+     The validate prop leverages HTML5 validation and will add
+      a valid and invalid class accordingly. If you don't want 
+      the Green and Red validation states, just remove the validate prop.`
+  }
+});
+
+stories.add('Default', () => (
+  <TextInput />
+));
+
+stories.add('Label', () => (
+  <TextInput label="First Name" />
+));
+
+stories.add('Placeholder', () => (
+  <TextInput placeholder="First Name" />
+));
+
+stories.add('Predefined value', () => (
+  <TextInput value="John" />
+));
+
+stories.add('Disabled', () => (
+  <TextInput disabled value="John" />
+));
+
+stories.add('Password', () => (
+  <TextInput password label="Password" />
+));
+
+stories.add('Email validate', () => (
+  <TextInput email validate label="Email" />
+));
+
+stories.add('with custom error/success', () => (
+  <TextInput email validate label="Email" error="Wrong Email sir" success="Great" />
+));
+
+stories.add('with Icon', () => (
+  <TextInput icon="email" label="Email" />
+));


Test patch:
diff --git a/test/TextInput.spec.js b/test/TextInput.spec.js
index 4a082d8..8d4eea9 100644
--- a/test/TextInput.spec.js
+++ b/test/TextInput.spec.js
@@ -44,16 +44,10 @@ describe('<TextInput />', () => {
   });
 
   test('handles other types', () => {
-    wrapper = shallow(<TextInput type="password" />);
+    wrapper = shallow(<TextInput password />);
     expect(wrapper.find('input').prop('type')).toEqual('password');
   });
 
-  test('calls onChange if provided', () => {
-    wrapper = shallow(<TextInput />);
-    wrapper.setState({ value: 'hello' });
-    expect(wrapper.find('input').prop('value')).toEqual('hello');
-  });
-
   test('with icon', () => {
     wrapper = shallow(<TextInput icon="cloud" />);
     expect(wrapper.find('i.material-icons.prefix')).toHaveLength(1);