Repository: react-materialize/react-materialize
Pull request #617 (2018-07-20 15:45:36)

3.0.0 text input.js
Input.js was very big, i'm extract TextInput


Patch:
diff --git a/.eslintrc b/.eslintrc
index a82b6ff..3692bd6 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -9,6 +9,9 @@ globals:
   React: true
   ReactDOM: true
   M: true
+  document: true
+  global: true
+  window: true
 
 plugins: ['prettier']
 
@@ -29,3 +32,4 @@ rules:
   standard/array-bracket-even-spacing: [0]
   standard/computed-property-even-spacing: [0]
   standard/object-curly-even-spacing: [0]
+  no-undef: [2]
diff --git a/src/TextInput.js b/src/TextInput.js
new file mode 100644
index 0000000..106ad0f
--- /dev/null
+++ b/src/TextInput.js
@@ -0,0 +1,168 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import cx from 'classnames';
+import idgen from './idgen';
+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 { onChange } = this.props;
+    if (onChange) onChange(event);
+
+    this.setState({ value: event.target.value });
+  }
+
+  componentDidUpdate(prevProps) {
+    if (this.props.value !== prevProps.value) {
+      M.updateTextFields();
+    }
+  }
+
+  render() {
+    const {
+      s,
+      m,
+      l,
+      disabled,
+      noLayout,
+      placeholder,
+      icon,
+      label,
+      inputClassName,
+      success,
+      error,
+      validate,
+      value,
+      type
+    } = this.props;
+
+    const sizes = { s, m, l };
+
+    let responsiveClasses;
+    if (!noLayout) {
+      responsiveClasses = { col: true };
+      constants.SIZES.forEach(size => {
+        responsiveClasses[size + sizes[size]] = sizes[size];
+      });
+    }
+
+    const wrapperClasses = cx('input-field', responsiveClasses);
+
+    const inputProps = {
+      placeholder,
+      type: type || 'text',
+      id: this.id,
+      value: value || this.state.value,
+      disabled
+    };
+
+    const renderLabel = () =>
+      label && (
+        <label
+          data-success={success}
+          data-error={error}
+          htmlFor={inputProps.id}
+        >
+          {label}
+        </label>
+      );
+
+    const renderIcon = () =>
+      icon && <i className="material-icons prefix">{icon}</i>;
+
+    return (
+      <div className={wrapperClasses}>
+        {renderIcon()}
+        <input
+          ref={el => {
+            this.inputRef = el;
+          }}
+          onChange={this.handleChange}
+          className={cx({ validate }, inputClassName)}
+          {...inputProps}
+        />
+        {renderLabel()}
+      </div>
+    );
+  }
+}
+
+TextInput.propTypes = {
+  /*
+   * Strip away all layout classes such as col and sX
+   */
+  noLayout: PropTypes.bool,
+  /*
+   * Responsive size for Small
+   */
+  s: PropTypes.number,
+  /*
+   * Responsive size for Medium
+   */
+  m: PropTypes.number,
+  /*
+   * Responsive size for Large
+   */
+  l: PropTypes.number,
+  /*
+   * disabled input
+   */
+  disabled: PropTypes.bool,
+  /*
+   * Placeholder string
+   */
+  placeholder: PropTypes.string,
+  /*
+   * override id
+   * @default idgen()
+   */
+  id: PropTypes.string,
+  /*
+   * prefix icon
+   */
+  icon: PropTypes.string,
+  /*
+   * label text
+   */
+  label: PropTypes.string,
+  /*
+   * Input initial value
+   */
+  value: PropTypes.string,
+  /*
+   * Add validate class to input
+   */
+  validate: PropTypes.bool,
+  /*
+   * Custom success message
+   */
+  success: PropTypes.string,
+  /*
+   * Custom error message
+   */
+  error: PropTypes.string,
+  /*
+   * Additional classes for input
+   */
+  inputClassName: PropTypes.string,
+  /*
+   * override type="text"
+   */
+  type: PropTypes.string,
+  /*
+   * onChange callback
+   */
+  onChange: PropTypes.func
+};
+
+export default TextInput;
diff --git a/src/index.js b/src/index.js
index 399459f..adb03e1 100644
--- a/src/index.js
+++ b/src/index.js
@@ -39,6 +39,7 @@ import Tab from './Tab';
 import Table from './Table';
 import Tabs from './Tabs';
 import Tag from './Tag';
+import TextInput from './TextInput';
 import Toast from './Toast';
 
 export {
@@ -83,5 +84,6 @@ export {
   Table,
   Tabs,
   Tag,
+  TextInput,
   Toast
 };


Test patch:
diff --git a/test/TextInput.spec.js b/test/TextInput.spec.js
new file mode 100644
index 0000000..4a082d8
--- /dev/null
+++ b/test/TextInput.spec.js
@@ -0,0 +1,61 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+import TextInput from '../src/TextInput';
+
+describe('<TextInput />', () => {
+  let wrapper;
+
+  test('handles layout classes', () => {
+    wrapper = shallow(<TextInput s={4} m={6} />);
+
+    expect(wrapper.hasClass('input-field')).toBeTruthy();
+    expect(wrapper.hasClass('col')).toBeTruthy();
+    expect(wrapper.hasClass('s4')).toBeTruthy();
+    expect(wrapper.hasClass('m6')).toBeTruthy();
+  });
+
+  test('handles removing layout classes', () => {
+    wrapper = shallow(<TextInput s={4} m={6} noLayout />);
+
+    expect(wrapper.hasClass('input-field')).toBeTruthy();
+    expect(wrapper.hasClass('col')).toBeFalsy();
+    expect(wrapper.hasClass('s4')).toBeFalsy();
+    expect(wrapper.hasClass('m6')).toBeFalsy();
+  });
+
+  test('renders placeholder', () => {
+    wrapper = shallow(<TextInput placeholder="Name" />);
+    expect(wrapper.find('input').prop('placeholder')).toEqual('Name');
+  });
+
+  test('renders label', () => {
+    wrapper = shallow(<TextInput label="Name" />);
+    expect(wrapper.find('label').text()).toEqual('Name');
+  });
+
+  test('handles validate', () => {
+    wrapper = shallow(<TextInput validate />);
+    expect(wrapper.find('input.validate')).toHaveLength(1);
+  });
+
+  test('handles inputClassName', () => {
+    wrapper = shallow(<TextInput validate inputClassName={'hello red'} />);
+    expect(wrapper.find('input.hello.red.validate')).toHaveLength(1);
+  });
+
+  test('handles other types', () => {
+    wrapper = shallow(<TextInput type="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);
+  });
+});