Skip to content

Commit

Permalink
[Stepper] Migrate StepIcon to emotion (#25281)
Browse files Browse the repository at this point in the history
  • Loading branch information
praveenkumar-kalidass authored Mar 9, 2021
1 parent 9a33e92 commit 446818f
Show file tree
Hide file tree
Showing 10 changed files with 148 additions and 58 deletions.
5 changes: 3 additions & 2 deletions docs/pages/api-docs/step-icon.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"classes": { "type": { "name": "object" } },
"completed": { "type": { "name": "bool" } },
"error": { "type": { "name": "bool" } },
"icon": { "type": { "name": "node" } }
"icon": { "type": { "name": "node" } },
"sx": { "type": { "name": "object" } }
},
"name": "StepIcon",
"styles": {
Expand All @@ -17,6 +18,6 @@
"filename": "/packages/material-ui/src/StepIcon/StepIcon.js",
"inheritance": null,
"demos": "<ul><li><a href=\"/components/steppers/\">Steppers</a></li></ul>",
"styledComponent": false,
"styledComponent": true,
"cssComponent": false
}
3 changes: 2 additions & 1 deletion docs/translations/api-docs/step-icon/step-icon.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"classes": "Override or extend the styles applied to the component. See <a href=\"#css\">CSS API</a> below for more details.",
"completed": "Mark the step as completed. Is passed to child components.",
"error": "If <code>true</code>, the step is marked as failed.",
"icon": "The label displayed in the step icon."
"icon": "The label displayed in the step icon.",
"sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the <a href=\"/system/basics/#the-sx-prop\">`sx` page</a> for more details."
},
"classDescriptions": {
"root": { "description": "Styles applied to the root element." },
Expand Down
6 changes: 6 additions & 0 deletions packages/material-ui/src/StepIcon/StepIcon.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as React from 'react';
import { SxProps } from '@material-ui/system';
import { InternalStandardProps as StandardProps } from '..';
import { Theme } from '../styles';

export interface StepIconProps
extends StandardProps<React.HTMLAttributes<HTMLDivElement>, 'children'> {
Expand Down Expand Up @@ -37,6 +39,10 @@ export interface StepIconProps
* The label displayed in the step icon.
*/
icon: React.ReactNode;
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx?: SxProps<Theme>;
}

export type StepIconClasskey = keyof NonNullable<StepIconProps['classes']>;
Expand Down
144 changes: 101 additions & 43 deletions packages/material-ui/src/StepIcon/StepIcon.js
Original file line number Diff line number Diff line change
@@ -1,76 +1,130 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { deepmerge } from '@material-ui/utils';
import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled';
import experimentalStyled from '../styles/experimentalStyled';
import useThemeProps from '../styles/useThemeProps';
import CheckCircle from '../internal/svg-icons/CheckCircle';
import Warning from '../internal/svg-icons/Warning';
import withStyles from '../styles/withStyles';
import SvgIcon from '../SvgIcon';
import stepIconClasses, { getStepIconUtilityClass } from './stepIconClasses';

export const styles = (theme) => ({
/* Styles applied to the root element. */
root: {
display: 'block',
transition: theme.transitions.create('color', {
duration: theme.transitions.duration.shortest,
}),
color: theme.palette.text.disabled,
'&$completed': {
color: theme.palette.primary.main,
},
'&$active': {
color: theme.palette.primary.main,
},
'&$error': {
color: theme.palette.error.main,
const overridesResolver = (props, styles) => {
return deepmerge(
{
[`& .${stepIconClasses.text}`]: styles.text,
},
styles.root || {},
);
};

const useUtilityClasses = (styleProps) => {
const { classes, active, completed, error } = styleProps;

const slots = {
root: ['root', active && 'active', completed && 'completed', error && 'error'],
text: ['text'],
};

return composeClasses(slots, getStepIconUtilityClass, classes);
};

const StepIconRoot = experimentalStyled(
SvgIcon,
{},
{
name: 'MuiStepIcon',
slot: 'Root',
overridesResolver,
},
/* Styles applied to the SVG text element. */
text: {
fill: theme.palette.primary.contrastText,
fontSize: theme.typography.caption.fontSize,
fontFamily: theme.typography.fontFamily,
)(({ theme }) => ({
/* Styles applied to the root element. */
display: 'block',
transition: theme.transitions.create('color', {
duration: theme.transitions.duration.shortest,
}),
color: theme.palette.text.disabled,
[`&.${stepIconClasses.completed}`]: {
color: theme.palette.primary.main,
},
/* Pseudo-class applied to the root element if `active={true}`. */
active: {},
/* Pseudo-class applied to the root element if `completed={true}`. */
completed: {},
/* Pseudo-class applied to the root element if `error={true}`. */
error: {},
});
[`&.${stepIconClasses.active}`]: {
color: theme.palette.primary.main,
},
[`&.${stepIconClasses.error}`]: {
color: theme.palette.error.main,
},
}));

const StepIconText = experimentalStyled(
'text',
{},
{
name: 'MuiStepIcon',
slot: 'Text',
overridesResolver,
},
)(({ theme }) => ({
/* Styles applied to the SVG text element. */
fill: theme.palette.primary.contrastText,
fontSize: theme.typography.caption.fontSize,
fontFamily: theme.typography.fontFamily,
}));

const StepIcon = React.forwardRef(function StepIcon(props, ref) {
const StepIcon = React.forwardRef(function StepIcon(inProps, ref) {
const props = useThemeProps({ props: inProps, name: 'MuiStepIcon' });
const {
active = false,
classes,
className: classNameProp,
completed = false,
error = false,
icon,
...other
} = props;

const styleProps = { ...props, active, completed, error };
const classes = useUtilityClasses(styleProps);

if (typeof icon === 'number' || typeof icon === 'string') {
const className = clsx(classNameProp, classes.root, {
[classes.active]: active,
[classes.error]: error,
[classes.completed]: completed,
});
const className = clsx(classNameProp, classes.root);

if (error) {
return <Warning className={className} ref={ref} />;
return (
<StepIconRoot
as={Warning}
className={className}
ref={ref}
styleProps={styleProps}
{...other}
/>
);
}

if (completed) {
return <CheckCircle className={className} ref={ref} />;
return (
<StepIconRoot
as={CheckCircle}
className={className}
ref={ref}
styleProps={styleProps}
{...other}
/>
);
}

return (
<SvgIcon className={className} ref={ref} {...other}>
<StepIconRoot className={className} ref={ref} styleProps={styleProps} {...other}>
<circle cx="12" cy="12" r="12" />
<text className={classes.text} x="12" y="16" textAnchor="middle">
<StepIconText
className={classes.text}
x="12"
y="16"
textAnchor="middle"
styleProps={styleProps}
>
{icon}
</text>
</SvgIcon>
</StepIconText>
</StepIconRoot>
);
}

Expand Down Expand Up @@ -109,6 +163,10 @@ StepIcon.propTypes /* remove-proptypes */ = {
* The label displayed in the step icon.
*/
icon: PropTypes.node,
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx: PropTypes.object,
};

export default withStyles(styles, { name: 'MuiStepIcon' })(StepIcon);
export default StepIcon;
16 changes: 7 additions & 9 deletions packages/material-ui/src/StepIcon/StepIcon.test.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
import * as React from 'react';
import { expect } from 'chai';
import { createMount, getClasses, describeConformance, createClientRender } from 'test/utils';
import StepIcon from './StepIcon';
import { createMount, describeConformanceV5, createClientRender } from 'test/utils';
import StepIcon, { stepIconClasses as classes } from '@material-ui/core/StepIcon';

describe('<StepIcon />', () => {
const render = createClientRender();
const mount = createMount();
let classes;

before(() => {
classes = getClasses(<StepIcon icon={1} />);
});

describeConformance(<StepIcon icon={1} />, () => ({
describeConformanceV5(<StepIcon icon={1} />, () => ({
classes,
inheritComponent: 'svg',
render,
mount,
muiName: 'MuiStepIcon',
testVariantProps: { completed: true },
refInstanceof: window.SVGSVGElement,
skip: ['componentProp'],
skip: ['componentProp', 'componentsProp'],
}));

it('renders <CheckCircle> when completed', () => {
Expand Down
3 changes: 3 additions & 0 deletions packages/material-ui/src/StepIcon/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export { default } from './StepIcon';
export * from './StepIcon';

export { default as stepIconClasses } from './stepIconClasses';
export * from './stepIconClasses';
3 changes: 3 additions & 0 deletions packages/material-ui/src/StepIcon/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
export { default } from './StepIcon';

export { default as stepIconClasses } from './stepIconClasses';
export * from './stepIconClasses';
7 changes: 7 additions & 0 deletions packages/material-ui/src/StepIcon/stepIconClasses.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { StepIconClasskey } from './StepIcon';

declare const stepIconClasses: Record<StepIconClasskey, string>;

export function getStepIconUtilityClass(slot: string): string;

export default stepIconClasses;
15 changes: 15 additions & 0 deletions packages/material-ui/src/StepIcon/stepIconClasses.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { generateUtilityClass, generateUtilityClasses } from '@material-ui/unstyled';

export function getStepIconUtilityClass(slot) {
return generateUtilityClass('MuiStepIcon', slot);
}

const stepIconClasses = generateUtilityClasses('MuiStepIcon', [
'root',
'active',
'completed',
'error',
'text',
]);

export default stepIconClasses;
4 changes: 1 addition & 3 deletions packages/material-ui/src/StepLabel/StepLabel.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,16 @@ import { getClasses, createClientRender, createMount, describeConformance } from
import Typography, { typographyClasses } from '../Typography';
import Stepper from '../Stepper';
import Step from '../Step';
import StepIcon from '../StepIcon';
import { stepIconClasses as iconClasses } from '../StepIcon';
import StepLabel from './StepLabel';

describe('<StepLabel />', () => {
let classes;
let iconClasses;
const mount = createMount({ strict: true });
const render = createClientRender();

before(() => {
classes = getClasses(<StepLabel />);
iconClasses = getClasses(<StepIcon />);
});

describeConformance(<StepLabel />, () => ({
Expand Down

0 comments on commit 446818f

Please sign in to comment.