Repository: mui/material-ui (TypeScript)
Date: 2018-07-01 19:26:30+00:00
Task type: Feature

Issue:
How to apply a custom className to StepPositionIcon?
I am trying to customize a vertical stepper like this:

```
const theme = createMuiTheme({

  overrides: {
    MuiStepConnector: {
      lineVertical: {
        minHeight: '',
      }
    },
    MuiStepContent: {
      last: {
        borderLeft: ''
      },
    },
    MuiStepIcon: {
      root: {
        '&$active': {
          color: "#fff",
          boxShadow: "0 0 20px rgba(0, 0, 0, 0.14)",
          borderRadius: "100px",
        },
      },
    },
    MuiExpansionPanelSummary: {
      expandIcon: {
        left: 8 * -2,
        transition: 'all .5s ease',
      }
    }
  }
});

```
But I cannot override StepPositionIcon properties like:

```
text: {
    fill: theme.palette.primary.contrastText,
    fontSize: theme.typography.caption.fontSize,
    fontFamily: theme.typography.fontFamily
}
```

I want to apply a custom className to StepPositionIcon but don't know where to apply this change? in Stepper, Step or StepLabel?

## Your Environment
<!--- Include as many relevant details about the environment with which you experienced the bug. -->

| Tech         | Version |
|--------------|---------|
| Material-UI  | v1.2.1  |
| React        |     16.4.0    |
| browser      |     Chrome    |
| etc          |         |



Issue discussion:
@bousejin The `StepPositionIcon` component is private, it might change in the future. You can't target it directly:
https://github.com/mui-org/material-ui/blob/5798bf644981a22459469e8bfb5d50f5287ab560/packages/material-ui/src/StepIcon/StepPositionIcon.js#L19
@oliviertassinari Thank you so much. I have some card inside a stepper that should be ordered, but I want the StepIcon background to be of a white color and the number in it to be of a blue one.

```jsx
<MuiThemeProvider theme={theme}>
        <Stepper activeStep={activeStep} orientation="vertical">
          {cards.map((prop, key) => {
            return (
              <Step key={key}>
                <StepLabel active></StepLabel>
                <StepContent TransitionComponent="None">
                  <Card className={classes.card}>
                    <div className={classes.details}>
                      <CardMedia
                        className={
                          classes.cover + " " +
                          classes.imgRaised + " " +
                          classes.imgRounded + " " +
                          classes.imgFluid
                        }
                        image={prop.image}
                        title={prop.title}
                      />
                      <CardContent className={classes.content}>
                        <Typography variant="subheading">
                          <a href={prop.link} className={classes.a} target="_blank">
                            {prop.title}
                          </a>
                        </Typography>
                        <Typography variant="body1">
                          {prop.authors}
                        </Typography>
                        <Typography variant="body1" color="textSecondary">
                          {prop.venue}
                        </Typography>
                      </CardContent>
                    </div>
                    <div className={classes.controls}>
                      <ExpansionPanel>
                        <ExpansionPanelSummary
                          className={classes.abstract}
                          expandIcon={<ExpandMoreIcon className={classes.icon} />}>
                          <a className={classes.a + " " + classes.heading}>Abstract</a>
                        </ExpansionPanelSummary>
                        <ExpansionPanelDetails>
                          <Typography>
                            {prop.abstract}
                          </Typography>
                        </ExpansionPanelDetails>
                      </ExpansionPanel>
                    </div>
                  </Card>

                </StepContent>
              </Step>
            );
          })}
        </Stepper>
      </MuiThemeProvider>
```
@bousejin Alright, I think that the best approach to fix the issue is to remove the `StepPositionIcon` component. We can merge the source code of this component with the `StepIcon` component.
It should be much simpler this way.
Do you want to work on it? :)
@oliviertassinari Thanks again. I want but my knowledge of react is at the beginner level:) After looking at the StepIcon and StepPositionIcon components, I've merged these components like below, but didn't test it:(

```
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import CheckCircle from '../internal/svg-icons/CheckCircle';
import Warning from '../internal/svg-icons/Warning';
import withStyles from '../styles/withStyles';
import SvgIcon from '../SvgIcon';

export const styles = theme => ({
  root: {
    display: 'block',
    color: theme.palette.text.disabled,
    '&$active': {
      color: theme.palette.primary.main,
    },
    '&$completed': {
      color: theme.palette.primary.main,
    },
    '&$error': {
      color: theme.palette.error.main,
    },
  },
  text: {
    fill: theme.palette.primary.contrastText,
    fontSize: theme.typography.caption.fontSize,
    fontFamily: theme.typography.fontFamily,
  },
  active: {},
  completed: {},
  error: {},
});

function StepIcon(props) {
  const { completed, icon, active, error, classes } = props;

  if (typeof icon === 'number' || typeof icon === 'string') {
    if (error) {
      return <Warning className={classNames(classes.root, classes.error)} />;
    }
    if (completed) {
      return <CheckCircle className={classNames(classes.root, classes.completed)} />;
    }
    return (
      <SvgIcon
        className={classNames(classes.root, {
          [classes.active]: active,
        })}
      >
        <circle cx="12" cy="12" r="12" />
        <text className={classes.text} x="12" y="16" textAnchor="middle">
          {icon}
        </text>
      </SvgIcon>
    );
  }

  return icon;
}

StepIcon.propTypes = {
  /**
   * Whether this step is active.
   */
  active: PropTypes.bool,
  /**
   * Override or extend the styles applied to the component.
   * See [CSS API](#css-api) below for more details.
   */
  classes: PropTypes.object.isRequired,
  /**
   * Mark the step as completed. Is passed to child components.
   */
  completed: PropTypes.bool,
  /**
   * Mark the step as failed.
   */
  error: PropTypes.bool,
  /**
   * The icon displayed by the step label.
   */
  icon: PropTypes.node.isRequired,
};

StepIcon.defaultProps = {
  active: false,
  completed: false,
  error: false,
};

export default withStyles(styles, { name: 'MuiStepIcon' })(StepIcon);
```




@bousejin This looks good. Do you want to submit a pull request? I will help with the missing parts.

Solution patch:
diff --git a/packages/material-ui/src/StepIcon/StepIcon.d.ts b/packages/material-ui/src/StepIcon/StepIcon.d.ts
--- a/packages/material-ui/src/StepIcon/StepIcon.d.ts
+++ b/packages/material-ui/src/StepIcon/StepIcon.d.ts
@@ -9,7 +9,7 @@ export interface StepIconProps
   icon: React.ReactNode;
 }
 
-export type StepIconClasskey = 'root' | 'active' | 'completed' | 'error';
+export type StepIconClasskey = 'root' | 'text' | 'active' | 'completed' | 'error';
 
 declare const StepIcon: React.ComponentType<StepIconProps>;
 
diff --git a/packages/material-ui/src/StepIcon/StepIcon.js b/packages/material-ui/src/StepIcon/StepIcon.js
--- a/packages/material-ui/src/StepIcon/StepIcon.js
+++ b/packages/material-ui/src/StepIcon/StepIcon.js
@@ -4,11 +4,12 @@ import classNames from 'classnames';
 import CheckCircle from '../internal/svg-icons/CheckCircle';
 import Warning from '../internal/svg-icons/Warning';
 import withStyles from '../styles/withStyles';
-import StepPositionIcon from './StepPositionIcon';
+import SvgIcon from '../SvgIcon';
 
 export const styles = theme => ({
   root: {
     display: 'block',
+    color: theme.palette.text.disabled,
     '&$active': {
       color: theme.palette.primary.main,
     },
@@ -19,6 +20,11 @@ export const styles = theme => ({
       color: theme.palette.error.main,
     },
   },
+  text: {
+    fill: theme.palette.primary.contrastText,
+    fontSize: theme.typography.caption.fontSize,
+    fontFamily: theme.typography.fontFamily,
+  },
   active: {},
   completed: {},
   error: {},
@@ -35,12 +41,16 @@ function StepIcon(props) {
       return <CheckCircle className={classNames(classes.root, classes.completed)} />;
     }
     return (
-      <StepPositionIcon
+      <SvgIcon
         className={classNames(classes.root, {
           [classes.active]: active,
         })}
-        position={icon}
-      />
+      >
+        <circle cx="12" cy="12" r="12" />
+        <text className={classes.text} x="12" y="16" textAnchor="middle">
+          {icon}
+        </text>
+      </SvgIcon>
     );
   }
 
diff --git a/packages/material-ui/src/StepIcon/StepPositionIcon.js b/packages/material-ui/src/StepIcon/StepPositionIcon.js
deleted file mode 100644
--- a/packages/material-ui/src/StepIcon/StepPositionIcon.js
+++ /dev/null
@@ -1,50 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-import withStyles from '../styles/withStyles';
-import SvgIcon from '../SvgIcon';
-
-export const styles = theme => ({
-  root: {
-    color: theme.palette.text.disabled,
-  },
-  text: {
-    fill: theme.palette.primary.contrastText,
-    fontSize: theme.typography.caption.fontSize,
-    fontFamily: theme.typography.fontFamily,
-  },
-});
-
-/**
- * @ignore - internal component.
- */
-function StepPositionIcon(props) {
-  const { position, classes, className } = props;
-
-  return (
-    <SvgIcon className={classNames(classes.root, className)}>
-      <circle cx="12" cy="12" r="12" />
-      <text className={classes.text} x="12" y="16" textAnchor="middle">
-        {position}
-      </text>
-    </SvgIcon>
-  );
-}
-
-StepPositionIcon.propTypes = {
-  /**
-   * Override or extend the styles applied to the component.
-   * See [CSS API](#css-api) below for more details.
-   */
-  classes: PropTypes.object.isRequired,
-  /**
-   * @ignore
-   */
-  className: PropTypes.string,
-  /**
-   * The step position as a number.
-   */
-  position: PropTypes.node,
-};
-
-export default withStyles(styles)(StepPositionIcon);
diff --git a/pages/api/step-icon.md b/pages/api/step-icon.md
--- a/pages/api/step-icon.md
+++ b/pages/api/step-icon.md
@@ -28,6 +28,7 @@ Any other properties supplied will be spread to the root element (native element
 You can override all the class names injected by Material-UI thanks to the `classes` property.
 This property accepts the following keys:
 - `root`
+- `text`
 - `active`
 - `completed`
 - `error`


Test patch:
diff --git a/packages/material-ui/src/StepIcon/StepIcon.test.js b/packages/material-ui/src/StepIcon/StepIcon.test.js
--- a/packages/material-ui/src/StepIcon/StepIcon.test.js
+++ b/packages/material-ui/src/StepIcon/StepIcon.test.js
@@ -3,8 +3,8 @@ import { assert } from 'chai';
 import CheckCircle from '../internal/svg-icons/CheckCircle';
 import Warning from '../internal/svg-icons/Warning';
 import { createShallow, createMount } from '../test-utils';
-import StepPositionIcon from './StepPositionIcon';
 import StepIcon from './StepIcon';
+import SvgIcon from '../SvgIcon';
 
 describe('<StepIcon />', () => {
   let shallow;
@@ -31,13 +31,14 @@ describe('<StepIcon />', () => {
     assert.strictEqual(warning.length, 1, 'should have an <Warning />');
   });
 
-  it('renders <StepPositionIcon> when not completed', () => {
-    const wrapper = shallow(<StepIcon icon={1} active />);
-    const checkCircle = wrapper.find(StepPositionIcon);
-    assert.strictEqual(checkCircle.length, 1, 'should have an <StepPositionIcon />');
-    const props = checkCircle.props();
-    assert.strictEqual(props.position, 1, 'should set position');
-    assert.include(props.className, 'active');
+  it('renders a <SvgIcon>', () => {
+    const wrapper = shallow(<StepIcon icon={1} />);
+    assert.strictEqual(wrapper.find(SvgIcon).length, 1);
+  });
+
+  it('contains text "3" when position is "3"', () => {
+    const wrapper = shallow(<StepIcon icon={3} />);
+    assert.strictEqual(wrapper.find('text').text(), '3');
   });
 
   it('renders the custom icon', () => {
diff --git a/packages/material-ui/src/StepIcon/StepPositionIcon.test.js b/packages/material-ui/src/StepIcon/StepPositionIcon.test.js
deleted file mode 100644
--- a/packages/material-ui/src/StepIcon/StepPositionIcon.test.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from 'react';
-import { assert } from 'chai';
-import { createShallow, createMount } from '../test-utils';
-import StepPositionIcon from './StepPositionIcon';
-import SvgIcon from '../SvgIcon';
-
-describe('<StepPositionIcon />', () => {
-  let shallow;
-  let mount;
-
-  before(() => {
-    shallow = createShallow({ dive: true });
-    mount = createMount();
-  });
-
-  after(() => {
-    mount.cleanUp();
-  });
-
-  it('renders a <SvgIcon>', () => {
-    const wrapper = shallow(<StepPositionIcon position={1} />);
-    assert.strictEqual(wrapper.find(SvgIcon).length, 1);
-  });
-
-  it('contains text "3" when position is "3"', () => {
-    const wrapper = shallow(<StepPositionIcon position={3} />);
-    assert.strictEqual(wrapper.find('text').text(), '3');
-  });
-});