Repository: react-materialize/react-materialize (js)

Issue:
Add action on icon in TextInput
Good morning,
first of all i want to congratulate with you for you're gr reat work.

### Description

I have a question about icon in TextInput component.
I want to add an action on that icon, but ther'isn't a prop that allows it to me. 

How can i do?

#### Thanks for helping out!

Pull request:
Allow TextInput to have node as an Icon
# As per title

Fixes #793 

## Type of change

Please delete options that are not relevant.

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update

# How Has This Been Tested?

Checked on storybook

# Checklist:

- [x] I have commented my code, particularly in hard-to-understand areas
- [x] My changes generate no new warnings
- [x] I have not generated a new package version. (the maintainers will handle that)


Patch:
diff --git a/src/TextInput.js b/src/TextInput.js
index 661b098..4241f48 100644
--- a/src/TextInput.js
+++ b/src/TextInput.js
@@ -106,8 +106,15 @@ class TextInput extends Component {
         />
       );
 
-    const renderIcon = () =>
-      icon && <i className="material-icons prefix">{icon}</i>;
+    const renderIcon = () => {
+      if (!icon) return;
+
+      if (typeof icon === 'string') {
+        return <i className="material-icons prefix">{icon}</i>;
+      }
+
+      return React.cloneElement(icon, { className: 'prefix' });
+    };
 
     return (
       <div className={wrapperClasses}>
@@ -163,9 +170,9 @@ TextInput.propTypes = {
    */
   id: PropTypes.string,
   /*
-   * prefix icon
+   * prefix icon, name of the icon or a node
    */
-  icon: PropTypes.string,
+  icon: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
   /*
    * label text
    */
diff --git a/stories/TextInput.stories.js b/stories/TextInput.stories.js
index ec43dd4..606335b 100644
--- a/stories/TextInput.stories.js
+++ b/stories/TextInput.stories.js
@@ -2,6 +2,7 @@ import React from 'react';
 import { storiesOf } from '@storybook/react';
 import { text, boolean, withKnobs } from '@storybook/addon-knobs';
 import TextInput from '../src/TextInput';
+import Icon from '../src/Icon';
 
 const stories = storiesOf('Components|TextInput', module);
 stories.addDecorator(withKnobs);
@@ -55,6 +56,10 @@ stories.add('with custom error/success', () => (
   />
 ));
 
-stories.add('with Icon', () => (
+stories.add('with icon string', () => (
   <TextInput icon={text('icon', 'email')} label="Email" />
 ));
+
+stories.add('with icon node', () => (
+  <TextInput icon={<Icon>{text('icon', 'email')}</Icon>} label="Email" />
+));


Test patch:
diff --git a/test/TextInput.spec.js b/test/TextInput.spec.js
index 3e9ef4e..4d50e2d 100644
--- a/test/TextInput.spec.js
+++ b/test/TextInput.spec.js
@@ -1,6 +1,7 @@
 import React from 'react';
 import { shallow } from 'enzyme';
 import TextInput from '../src/TextInput';
+import Icon from '../src/Icon';
 
 describe('<TextInput />', () => {
   let wrapper;
@@ -48,11 +49,6 @@ describe('<TextInput />', () => {
     expect(wrapper.find('input').prop('type')).toEqual('password');
   });
 
-  test('with icon', () => {
-    wrapper = shallow(<TextInput icon="cloud" />);
-    expect(wrapper.find('i.material-icons.prefix')).toHaveLength(1);
-  });
-
   test('handles other props', () => {
     const mockOnChange = jest.fn();
     wrapper = shallow(<TextInput onChange={mockOnChange} />);
@@ -60,4 +56,24 @@ describe('<TextInput />', () => {
 
     expect(mockOnChange).toHaveBeenCalledTimes(1);
   });
+
+  describe('Icon', () => {
+    test('renders as a string', () => {
+      wrapper = shallow(<TextInput icon="cloud" />);
+      expect(wrapper.find('i.material-icons.prefix')).toHaveLength(1);
+    });
+
+    test('renders as a node', () => {
+      const mockIconClick = jest.fn();
+      wrapper = shallow(
+        <TextInput icon={<Icon onClick={mockIconClick}>cloud</Icon>} />
+      );
+
+      const iconWrapper = wrapper.find('Icon').shallow();
+      expect(iconWrapper.find('i.material-icons.prefix')).toHaveLength(1);
+
+      wrapper.find('Icon').simulate('click');
+      expect(mockIconClick).toBeCalled();
+    });
+  });
 });