Repository: react-materialize/react-materialize
Pull request #1153 (2020-10-05 12:47:43)

fix: pass HTML element props to `i` tag for Icon
# Description

Addresses #922 by passing HTML Element props to the `i` tag created by `Icon`.

## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)

# How Has This Been Tested?

Added a test case to `Icon.spec.js` and also independently updated the `Icon.d.ts` and verified that the following is valid:

```typescript
    let props: IconProps  ={
        className: "test",
        left: true,
        large: true,
        style: {
            fontSize: "30px"
        },
        onClick: ()=>undefined,
    }
```

But something like this is still invalid:
```typescript
    let props: IconProps  ={
        className: "test",
        left: true,
        large: true,
        style: {
            fontSize: "30px"
        },
        onClick: ()=>undefined,
        // Prop that isn't permitted
        foo: ""
    }
```
# 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/Icon.js b/src/Icon.js
index 061bbf1..cfd1a20 100644
--- a/src/Icon.js
+++ b/src/Icon.js
@@ -3,17 +3,24 @@ import PropTypes from 'prop-types';
 import constants from './constants';
 import cx from 'classnames';
 
-const Icon = props => {
+const Icon = ({ className, children, ...props }) => {
   const classes = {
     'material-icons': true
   };
+  const rest = Object.assign(props, {});
   constants.PLACEMENTS.forEach(p => {
     classes[p] = props[p];
+    delete rest[p];
   });
   constants.ICON_SIZES.forEach(s => {
     classes[s] = props[s];
+    delete rest[s];
   });
-  return <i className={cx(classes, props.className)}>{props.children}</i>;
+  return (
+    <i className={cx(classes, className)} {...rest}>
+      {children}
+    </i>
+  );
 };
 
 Icon.propTypes = {
diff --git a/types/components/Icon.d.ts b/types/components/Icon.d.ts
index cc50a0d..1fc4317 100644
--- a/types/components/Icon.d.ts
+++ b/types/components/Icon.d.ts
@@ -1,6 +1,6 @@
 import * as React from "react";
 
-export interface IconProps {
+export interface IconProps extends React.HTMLAttributes<React.ReactHTMLElement<any>>{
   left?: boolean;
   center?: boolean;
   right?: boolean;


Test patch:
diff --git a/test/Icon.spec.js b/test/Icon.spec.js
index 4c4a3fd..118a970 100644
--- a/test/Icon.spec.js
+++ b/test/Icon.spec.js
@@ -18,4 +18,8 @@ describe('<Icon />', () => {
     wrapper = shallow(<Icon left>cloud</Icon>);
     expect(wrapper).toMatchSnapshot();
   });
+  test('accepts style size as a prop', () => {
+    wrapper = shallow(<Icon style={{ fontSize: '30px' }}>cloud</Icon>);
+    expect(wrapper).toMatchSnapshot();
+  });
 });
diff --git a/test/__snapshots__/Icon.spec.js.snap b/test/__snapshots__/Icon.spec.js.snap
index 09c0d8d..ee529a6 100644
--- a/test/__snapshots__/Icon.spec.js.snap
+++ b/test/__snapshots__/Icon.spec.js.snap
@@ -16,6 +16,19 @@ exports[`<Icon /> accepts size as a prop 1`] = `
 </i>
 `;
 
+exports[`<Icon /> accepts style size as a prop 1`] = `
+<i
+  className="material-icons"
+  style={
+    Object {
+      "fontSize": "30px",
+    }
+  }
+>
+  cloud
+</i>
+`;
+
 exports[`<Icon /> renders an icon 1`] = `
 <i
   className="material-icons"