Repository: mui/material-ui (TypeScript)
Date: 2018-10-02 01:54:30+00:00
Task type: Feature

Issue:
Allow color prop to override icon color when inside ListItemIcon
<!--- Provide a general summary of the feature in the Title above -->
Material design allows for list items to be colored with theme colors (as demonstrated [here](https://material.io/design/components/lists.html#types) under the "Expand and Collapse" header). When nesting an icon in the `<ListItemIcon>` component, whatever `color` prop passed to the icon is overridden by the `ListItemIcon` color.

<!--
    Thank you very much for contributing to Material-UI by creating an issue! ❤️
    To avoid duplicate issues we ask you to check off the following list.
-->

<!-- Checked checkbox should look like this: [x] -->
- [x] This is not a v0.x issue. <!-- (v0.x is no longer maintained) -->
- [x] I have searched the [issues](https://github.com/mui-org/material-ui/issues) of this repository and believe that this is not a duplicate.

## Expected Behavior
<!---
    Describe how it should work.
-->
```
<List>
  <ListItem>
    <ListItemIcon>
      <LinkIcon color="secondary" /> // Any icon from @material-ui/icons
    </ListItemIcon>
  </ListItem>
</List>
```
Expect `LinkIcon` to be colored with theme `secondary` color.
## Current Behavior
<!---
    Explain the difference from current behavior.
-->
Icon is colored with the default list icon color (`rgba(0, 0, 0, 0.54)` in default settings I believe)

## Examples
<!---
    Provide a link to the Material design specification, other implementations, 
    or screenshots of the expected behavior.
-->
As mentioned above, the expected behavior is demonstrated on material design's website in the types section. It is also shown in the [specs](https://material.io/design/components/lists.html#specs) section under the "Collapsed & Expanded" header.

## Context
<!---
    What are you trying to accomplish? How has the lack of this feature affected you?
    Providing context helps us come up with a solution that is most useful in the real world.
-->
I want to have my list items toggle colors when expanded/collapsed, exactly as shown on the material design website. This is not a major blocker to my project, but the workaround is tedious.


Issue discussion:
@izyb The simplest way to solve this problem is by using an intermediary component in the `ListItemIcon` implementation. I like the idea of removing a `React.cloneElement(` call.
@eps1lon What do you think?
Do you have a reproduction? It works on my end™: https://codesandbox.io/s/vmwx51nm07

@oliviertassinari 
Looking at the implementation https://github.com/mui-org/material-ui/blob/0cf866a898a0aeacc20cc0f119b80395e115826d/packages/material-ui/src/ListItemIcon/ListItemIcon.js#L19-L23
this looks overly complicated and documented wrong (e.g. `classes.root` should be applied to the root according to doc but is applied to the child because there isn't actually a root element. Maybe just wrap a div around the child and call it a day?
> Do you have a reproduction? It works on my end™

@eps1lon Oh boy! You have just found our Easter eggs 🥚😆. Enjoy:
```diff
+import ListItemIcon from "@material-ui/core/ListItemIcon";
import LinkIcon from "@material-ui/icons/Link";
-import ListItemIcon from "@material-ui/core/ListItemIcon";
```
https://codesandbox.io/s/1yprl2pln4

> Maybe just wrap a div around the child and call it a day?

Deal! @izyb Do you want to work on it? :)
@oliviertassinari sounds good to me! Thanks :D

Solution patch:
diff --git a/packages/material-ui/src/ListItemIcon/ListItemIcon.d.ts b/packages/material-ui/src/ListItemIcon/ListItemIcon.d.ts
--- a/packages/material-ui/src/ListItemIcon/ListItemIcon.d.ts
+++ b/packages/material-ui/src/ListItemIcon/ListItemIcon.d.ts
@@ -1,6 +1,7 @@
 import { StandardProps } from '..';
 
-export interface ListItemIconProps extends StandardProps<{}, ListItemIconClassKey> {
+export interface ListItemIconProps
+  extends StandardProps<React.HTMLAttributes<HTMLDivElement>, ListItemIconClassKey> {
   children: React.ReactElement<any>;
 }
 
diff --git a/packages/material-ui/src/ListItemIcon/ListItemIcon.js b/packages/material-ui/src/ListItemIcon/ListItemIcon.js
--- a/packages/material-ui/src/ListItemIcon/ListItemIcon.js
+++ b/packages/material-ui/src/ListItemIcon/ListItemIcon.js
@@ -9,6 +9,7 @@ export const styles = theme => ({
     marginRight: 16,
     color: theme.palette.action.active,
     flexShrink: 0,
+    display: 'inline-flex',
   },
 });
 
@@ -18,10 +19,11 @@ export const styles = theme => ({
 function ListItemIcon(props) {
   const { children, classes, className: classNameProp, ...other } = props;
 
-  return React.cloneElement(children, {
-    className: classNames(classes.root, classNameProp, children.props.className),
-    ...other,
-  });
+  return (
+    <div className={classNames(classes.root, classNameProp)} {...other}>
+      {children}
+    </div>
+  );
 }
 
 ListItemIcon.propTypes = {


Test patch:
diff --git a/packages/material-ui/src/ListItemIcon/ListItemIcon.test.js b/packages/material-ui/src/ListItemIcon/ListItemIcon.test.js
--- a/packages/material-ui/src/ListItemIcon/ListItemIcon.test.js
+++ b/packages/material-ui/src/ListItemIcon/ListItemIcon.test.js
@@ -16,23 +16,25 @@ describe('<ListItemIcon />', () => {
     );
   });
 
-  it('should render a span', () => {
+  it('should render a span inside a div', () => {
     const wrapper = shallow(
       <ListItemIcon>
         <span />
       </ListItemIcon>,
     );
-    assert.strictEqual(wrapper.name(), 'span');
+    assert.strictEqual(wrapper.name(), 'div');
+    assert.strictEqual(wrapper.children().name(), 'span');
   });
 
-  it('should render with the user and root classes', () => {
+  it('should render a div with the user and root classes, but not the children classes', () => {
     const wrapper = shallow(
       <ListItemIcon className="foo">
         <span className="bar" />
       </ListItemIcon>,
     );
     assert.strictEqual(wrapper.hasClass('foo'), true);
-    assert.strictEqual(wrapper.hasClass('bar'), true);
+    assert.strictEqual(wrapper.hasClass('bar'), false);
+    assert.strictEqual(wrapper.children().hasClass('bar'), true);
     assert.strictEqual(wrapper.hasClass(classes.root), true);
   });
 });