Repository: mui/material-ui (TypeScript)
Date: 2018-05-10 10:53:04+00:00
Task type: Bug Fix

Issue:
CircularProgress doesn't animate. Is it the normal behavior?
- [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
When I change a CircularProgress' progress prop, I expect that the progress smoothly animate from the old value to the new one.

## Current Behavior
Progress instantly jump from the old to the new value.

## Steps to Reproduce (for bugs)
Here is a link to a codesandbox that reproduce the problem: https://codesandbox.io/s/v36v5nx695

A timer in componentDidMount adds 20 to progress state every 2 seconds.

As you can see, progress jump from previous value to the new one instantly.

**Is this the normal behavior? Or should it animates smoothly from the previous value to the new one?**

## Context
I want to use this circle progress in a picture upload component.

## Your Environment
<!--- Include as many relevant details about the environment with which you experienced the bug. -->

| Tech         | Version |
|--------------|---------|
| Material-UI  |   1.0.0-beta.47      |
| React        |    16.3.1     |
| browser      |     Chrome v66    |



Issue discussion:
@damien-monni I think that you are right. We could add some transition between the intermediate states :).
Great! I will use a LinearProgress until it is implemented which already uses transition.

Solution patch:
diff --git a/.size-limit b/.size-limit
--- a/.size-limit
+++ b/.size-limit
@@ -9,7 +9,7 @@
     "name": "The size of all the modules of material-ui.",
     "webpack": true,
     "path": "packages/material-ui/build/index.js",
-    "limit": "102.3 KB"
+    "limit": "102.4 KB"
   },
   {
     "name": "The main bundle of the docs",
diff --git a/docs/src/pages/demos/progress/CircularDeterminate.js b/docs/src/pages/demos/progress/CircularDeterminate.js
--- a/docs/src/pages/demos/progress/CircularDeterminate.js
+++ b/docs/src/pages/demos/progress/CircularDeterminate.js
@@ -26,7 +26,7 @@ class CircularDeterminate extends React.Component {
 
   progress = () => {
     const { completed } = this.state;
-    this.setState({ completed: completed === 100 ? 0 : completed + 1 });
+    this.setState({ completed: completed >= 100 ? 0 : completed + 1 });
   };
 
   render() {
diff --git a/docs/src/pages/demos/progress/CircularStatic.js b/docs/src/pages/demos/progress/CircularStatic.js
--- a/docs/src/pages/demos/progress/CircularStatic.js
+++ b/docs/src/pages/demos/progress/CircularStatic.js
@@ -9,17 +9,43 @@ const styles = theme => ({
   },
 });
 
-function CircularStatic(props) {
-  const { classes } = props;
-  return (
-    <div>
-      <CircularProgress className={classes.progress} variant="static" value={5} />
-      <CircularProgress className={classes.progress} variant="static" value={25} />
-      <CircularProgress className={classes.progress} variant="static" value={50} />
-      <CircularProgress className={classes.progress} variant="static" value={75} />
-      <CircularProgress className={classes.progress} variant="static" value={100} />
-    </div>
-  );
+class CircularStatic extends React.Component {
+  state = {
+    completed: 0,
+  };
+
+  componentDidMount() {
+    this.timer = setInterval(this.progress, 1000);
+  }
+
+  componentWillUnmount() {
+    clearInterval(this.timer);
+  }
+
+  timer;
+
+  progress = () => {
+    const { completed } = this.state;
+    this.setState({ completed: completed >= 100 ? 0 : completed + 10 });
+  };
+
+  render() {
+    const { classes } = this.props;
+    return (
+      <div>
+        <CircularProgress className={classes.progress} variant="static" value={5} />
+        <CircularProgress className={classes.progress} variant="static" value={25} />
+        <CircularProgress className={classes.progress} variant="static" value={50} />
+        <CircularProgress className={classes.progress} variant="static" value={75} />
+        <CircularProgress className={classes.progress} variant="static" value={100} />
+        <CircularProgress
+          className={classes.progress}
+          variant="static"
+          value={this.state.completed}
+        />
+      </div>
+    );
+  }
 }
 
 CircularStatic.propTypes = {
diff --git a/packages/material-ui/src/Progress/CircularProgress.js b/packages/material-ui/src/Progress/CircularProgress.js
--- a/packages/material-ui/src/Progress/CircularProgress.js
+++ b/packages/material-ui/src/Progress/CircularProgress.js
@@ -26,6 +26,12 @@ export const styles = theme => ({
   root: {
     display: 'inline-block',
   },
+  static: {
+    transition: theme.transitions.create('transform'),
+  },
+  indeterminate: {
+    animation: 'mui-progress-circular-rotate 1.4s linear infinite',
+  },
   colorPrimary: {
     color: theme.palette.primary.main,
   },
@@ -33,13 +39,13 @@ export const styles = theme => ({
     color: theme.palette.secondary.main,
   },
   svg: {},
-  svgIndeterminate: {
-    animation: 'mui-progress-circular-rotate 1.4s linear infinite',
-  },
   circle: {
     stroke: 'currentColor',
     strokeLinecap: 'round',
   },
+  circleStatic: {
+    transition: theme.transitions.create('stroke-dashoffset'),
+  },
   circleIndeterminate: {
     animation: 'mui-progress-circular-dash 1.4s ease-in-out infinite',
     // Some default value that looks fine waiting for the animation to kicks in.
@@ -103,6 +109,8 @@ function CircularProgress(props) {
         classes.root,
         {
           [classes[`color${capitalize(color)}`]]: color !== 'inherit',
+          [classes.indeterminate]: variant === 'indeterminate',
+          [classes.static]: variant === 'static',
         },
         className,
       )}
@@ -111,15 +119,11 @@ function CircularProgress(props) {
       {...rootProps}
       {...other}
     >
-      <svg
-        className={classNames(classes.svg, {
-          [classes.svgIndeterminate]: variant === 'indeterminate',
-        })}
-        viewBox={`0 0 ${SIZE} ${SIZE}`}
-      >
+      <svg className={classes.svg} viewBox={`0 0 ${SIZE} ${SIZE}`}>
         <circle
           className={classNames(classes.circle, {
             [classes.circleIndeterminate]: variant === 'indeterminate',
+            [classes.circleStatic]: variant === 'static',
           })}
           style={circleStyle}
           cx={SIZE / 2}
diff --git a/packages/material-ui/src/Progress/LinearProgress.js b/packages/material-ui/src/Progress/LinearProgress.js
--- a/packages/material-ui/src/Progress/LinearProgress.js
+++ b/packages/material-ui/src/Progress/LinearProgress.js
@@ -5,7 +5,7 @@ import warning from 'warning';
 import withStyles from '../styles/withStyles';
 import { lighten } from '../styles/colorManipulator';
 
-const TRANSITION_DURATION = 4; // 400ms
+const TRANSITION_DURATION = 4; // seconds
 
 export const styles = theme => ({
   root: {
diff --git a/pages/api/circular-progress.md b/pages/api/circular-progress.md
--- a/pages/api/circular-progress.md
+++ b/pages/api/circular-progress.md
@@ -30,11 +30,13 @@ Any other properties supplied will be [spread to the root element](/guides/api#s
 You can override all the class names injected by Material-UI thanks to the `classes` property.
 This property accepts the following keys:
 - `root`
+- `static`
+- `indeterminate`
 - `colorPrimary`
 - `colorSecondary`
 - `svg`
-- `svgIndeterminate`
 - `circle`
+- `circleStatic`
 - `circleIndeterminate`
 
 Have a look at [overriding with classes](/customization/overrides#overriding-with-classes) section


Test patch:
diff --git a/packages/material-ui/src/Progress/CircularProgress.test.js b/packages/material-ui/src/Progress/CircularProgress.test.js
--- a/packages/material-ui/src/Progress/CircularProgress.test.js
+++ b/packages/material-ui/src/Progress/CircularProgress.test.js
@@ -43,7 +43,7 @@ describe('<CircularProgress />', () => {
     const wrapper = shallow(<CircularProgress />);
     const svg = wrapper.childAt(0);
     assert.strictEqual(svg.name(), 'svg');
-    assert.strictEqual(svg.hasClass(classes.svgIndeterminate), true);
+    assert.strictEqual(wrapper.hasClass(classes.indeterminate), true);
     assert.strictEqual(svg.childAt(0).name(), 'circle', 'should be a circle');
     assert.strictEqual(
       svg.childAt(0).hasClass(classes.circle),