Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CircularProgress] Migrate to emotion #24622

Merged
merged 10 commits into from
Jan 27, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/pages/api-docs/circular-progress.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"type": { "name": "union", "description": "number<br>&#124;&nbsp;string" },
"default": "40"
},
"sx": { "type": { "name": "object" } },
"thickness": { "type": { "name": "number" }, "default": "3.6" },
"value": { "type": { "name": "number" }, "default": "0" },
"variant": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"color": "The color of the component. It supports those theme colors that make sense for this component.",
"disableShrink": "If <code>true</code>, the shrink animation is disabled. This only works if variant is <code>indeterminate</code>.",
"size": "The size of the component. If using a number, the pixel unit is assumed. If using a string, you need to provide the CSS unit, e.g &#39;3rem&#39;.",
"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.",
"thickness": "The thickness of the circle.",
"value": "The value of the progress indicator for the determinate variant. Value between 0 and 100.",
"variant": "The variant to use. Use indeterminate when there is no progress value."
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { SxProps } from '@material-ui/system';
import * as React from 'react';
import { InternalStandardProps as StandardProps } from '..';
import { InternalStandardProps as StandardProps, Theme } from '..';

export interface CircularProgressProps
extends StandardProps<React.HTMLAttributes<HTMLSpanElement>, 'children'> {
Expand Down Expand Up @@ -46,6 +47,10 @@ export interface CircularProgressProps
* @default 40
*/
size?: number | string;
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx?: SxProps<Theme>;
/**
* The thickness of the circle.
* @default 3.6
Expand Down
176 changes: 119 additions & 57 deletions packages/material-ui/src/CircularProgress/CircularProgress.js
Original file line number Diff line number Diff line change
@@ -1,62 +1,117 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { chainPropTypes } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import { chainPropTypes, deepmerge } from '@material-ui/utils';
import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled';
import capitalize from '../utils/capitalize';

import useThemeProps from '../styles/useThemeProps';
import experimentalStyled from '../styles/experimentalStyled';
import circularProgressClasses, {
getCircularProgressUtilityClass,
} from './circularProgressClasses';

const SIZE = 44;

export const styles = (theme) => ({
/* Styles applied to the root element. */
root: {
display: 'inline-block',
const overridesResolver = (props, styles) => {
const { styleProps } = props;

return deepmerge(styles.root || {}, {
...styles[styleProps.variant],
...styles[`color${capitalize(styleProps.color)}`],
[`& .${circularProgressClasses.svg}`]: styles.svg,
[`& .${circularProgressClasses.circle}`]: {
...styles.circle,
...styles[`circle${capitalize(styleProps.variant)}`],
...(styleProps.disableShrink && styles.circleDisableShrink),
},
});
};

const useUtilityClasses = (styleProps) => {
const { classes, variant, color, disableShrink } = styleProps;

const slots = {
root: ['root', variant, `color${capitalize(color)}`],
svg: ['svg'],
circle: ['circle', `circle${variant}`, disableShrink && 'circleDisableShrink'],
natac13 marked this conversation as resolved.
Show resolved Hide resolved
};

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

const CircularProgressRoot = experimentalStyled(
'span',
{},
{
name: 'MuiCircularProgress',
slot: 'Root',
overridesResolver,
},
)(({ theme, styleProps }) => ({
/* Styles applied to the root element. */
display: 'inline-block',
/* Styles applied to the root element if `variant="determinate"`. */
determinate: {
...(styleProps.variant === 'determinate' && {
transition: theme.transitions.create('transform'),
},
}),
/* Styles applied to the root element if `variant="indeterminate"`. */
indeterminate: {
...(styleProps.variant === 'indeterminate' && {
animation: '$circular-rotate 1.4s linear infinite',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
animation: '$circular-rotate 1.4s linear infinite',
animation: 'circular-rotate 1.4s linear infinite',

},
}),
/* Styles applied to the root element if `color="primary"`. */
colorPrimary: {
...(styleProps.color === 'primary' && {
color: theme.palette.primary.main,
},
}),
/* Styles applied to the root element if `color="secondary"`. */
colorSecondary: {
...(styleProps.color === 'secondary' && {
color: theme.palette.secondary.main,
}),
'@keyframes circular-rotate': {
'0%': {
transform: 'rotate(0deg)',
},
'100%': {
transform: 'rotate(360deg)',
},
},
}));

const CircularProgressSVG = experimentalStyled(
'svg',
{},
{
name: 'MuiCircularProgress',
slot: 'Svg',
},
)({
/* Styles applied to the svg element. */
svg: {
display: 'block', // Keeps the progress centered
display: 'block', // Keeps the progress centered
});

const CircularProgressCircle = experimentalStyled(
'circle',
{},
{
name: 'MuiCircularProgress',
slot: 'Circle',
},
)(({ theme, styleProps }) => ({
/* Styles applied to the `circle` svg path. */
circle: {
stroke: 'currentColor',
// Use butt to follow the specification, by chance, it's already the default CSS value.
// strokeLinecap: 'butt',
},
stroke: 'currentColor',
// Use butt to follow the specification, by chance, it's already the default CSS value.
// strokeLinecap: 'butt',
/* Styles applied to the `circle` svg path if `variant="determinate"`. */
circleDeterminate: {
...(styleProps.variant === 'determinate' && {
transition: theme.transitions.create('stroke-dashoffset'),
},
}),
/* Styles applied to the `circle` svg path if `variant="indeterminate"`. */
circleIndeterminate: {
...(styleProps.variant === 'indeterminate' && {
animation: '$circular-dash 1.4s ease-in-out infinite',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
animation: '$circular-dash 1.4s ease-in-out infinite',
animation: 'circular-dash 1.4s ease-in-out infinite',

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this the correct?

// Some default value that looks fine waiting for the animation to kicks in.
strokeDasharray: '80px, 200px',
strokeDashoffset: '0px', // Add the unit to fix a Edge 16 and below bug.
},
'@keyframes circular-rotate': {
'0%': {
transform: 'rotate(0deg)',
},
'100%': {
transform: 'rotate(360deg)',
},
},
}),
'@keyframes circular-dash': {
'0%': {
strokeDasharray: '1px, 200px',
Expand All @@ -72,10 +127,10 @@ export const styles = (theme) => ({
},
},
/* Styles applied to the `circle` svg path if `disableShrink={true}`. */
circleDisableShrink: {
...(styleProps.disableShrink && {
animation: 'none',
},
});
}),
}));

/**
* ## ARIA
Expand All @@ -84,9 +139,9 @@ export const styles = (theme) => ({
* you should use `aria-describedby` to point to the progress bar, and set the `aria-busy`
* attribute to `true` on that region until it has finished loading.
*/
const CircularProgress = React.forwardRef(function CircularProgress(props, ref) {
const CircularProgress = React.forwardRef(function CircularProgress(inProps, ref) {
const props = useThemeProps({ props: inProps, name: 'MuiCircularProgress' });
const {
classes,
className,
color = 'primary',
disableShrink = false,
Expand All @@ -98,6 +153,15 @@ const CircularProgress = React.forwardRef(function CircularProgress(props, ref)
...other
} = props;

const styleProps = {
...props,
color,
disableShrink,
variant,
};

const classes = useUtilityClasses(styleProps);

const circleStyle = {};
const rootStyle = {};
const rootProps = {};
Expand All @@ -111,38 +175,32 @@ const CircularProgress = React.forwardRef(function CircularProgress(props, ref)
}

return (
<span
className={clsx(
classes.root,
{
[classes[`color${capitalize(color)}`]]: color !== 'inherit',
[classes.determinate]: variant === 'determinate',
[classes.indeterminate]: variant === 'indeterminate',
},
className,
)}
<CircularProgressRoot
className={clsx(classes.root, className)}
style={{ width: size, height: size, ...rootStyle, ...style }}
styleProps={styleProps}
ref={ref}
role="progressbar"
{...rootProps}
{...other}
>
<svg className={classes.svg} viewBox={`${SIZE / 2} ${SIZE / 2} ${SIZE} ${SIZE}`}>
<circle
className={clsx(classes.circle, {
[classes.circleDeterminate]: variant === 'determinate',
[classes.circleIndeterminate]: variant === 'indeterminate',
[classes.circleDisableShrink]: disableShrink,
})}
<CircularProgressSVG
className={classes.svg}
styleProps={styleProps}
viewBox={`${SIZE / 2} ${SIZE / 2} ${SIZE} ${SIZE}`}
>
<CircularProgressCircle
className={classes.circle}
style={circleStyle}
styleProps={styleProps}
cx={SIZE}
cy={SIZE}
r={(SIZE - thickness) / 2}
fill="none"
strokeWidth={thickness}
/>
</svg>
</span>
</CircularProgressSVG>
</CircularProgressRoot>
);
});

Expand Down Expand Up @@ -190,6 +248,10 @@ CircularProgress.propTypes = {
* @ignore
*/
style: PropTypes.object,
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx: PropTypes.object,
/**
* The thickness of the circle.
* @default 3.6
Expand All @@ -209,4 +271,4 @@ CircularProgress.propTypes = {
variant: PropTypes.oneOf(['determinate', 'indeterminate']),
};

export default withStyles(styles, { name: 'MuiCircularProgress', flip: false })(CircularProgress);
export default CircularProgress;
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
import * as React from 'react';
import { expect } from 'chai';
import { createClientRender, getClasses, createMount, describeConformance } from 'test/utils';
import { createClientRender, createMount, describeConformanceV5 } from 'test/utils';
import CircularProgress from './CircularProgress';
import classes from './circularProgressClasses';

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

before(() => {
classes = getClasses(<CircularProgress />);
});

describeConformance(<CircularProgress />, () => ({
describeConformanceV5(<CircularProgress />, () => ({
classes,
inheritComponent: 'span',
mount,
muiName: 'MuiCircularProgress',
testDeepOverrides: { slotName: 'circle', slotClassName: classes.circle },
testVariantProps: { variant: 'determinate' },
refInstanceof: window.HTMLSpanElement,
skip: ['componentProp'],
skip: ['componentProp', 'componentsProp'],
}));

it('should render with the primary color by default', () => {
Expand Down Expand Up @@ -48,7 +47,7 @@ describe('<CircularProgress />', () => {
expect(svg.firstChild).to.have.class(classes.circle, 'should have the circle class');
});

it('should render intermediate variant by default', () => {
it('should render indeterminate variant by default', () => {
const { container } = render(<CircularProgress />);
const circularProgress = container.firstChild;
expect(circularProgress).to.have.class(classes.root);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export interface CircularProgressClasses {
root: string;
determinate: string;
indeterminate: string;
colorPrimary: string;
colorSecondary: string;
svg: string;
circle: string;
circleDeterminate: string;
circleIndeterminate: string;
circleDisableShrink: string;
}

declare const circularProgressClasses: CircularProgressClasses;

export function getCircularProgressUtilityClass(slot: string): string;

export default circularProgressClasses;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { generateUtilityClass, generateUtilityClasses } from '@material-ui/unstyled';

export function getCircularProgressUtilityClass(slot) {
return generateUtilityClass('MuiCircularProgress', slot);
}

const circularProgressClasses = generateUtilityClasses('MuiCircularProgress', [
'root',
'determinate',
'indeterminate',
'colorPrimary',
'colorSecondary',
'svg',
'circle',
'circleDeterminate',
'circleIndeterminate',
'circleDisableShrink',
]);

export default circularProgressClasses;
3 changes: 3 additions & 0 deletions packages/material-ui/src/CircularProgress/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export { default } from './CircularProgress';
export * from './CircularProgress';

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

export { default as circularProgressClasses } from './circularProgressClasses';
export * from './circularProgressClasses';