Repository: mui/material-ui (TypeScript)
Date: 2018-06-28 15:53:17+00:00
Task type: Bug Fix

Issue:
[Snackbar] resumeHideDuration short-circuits on 0 milliseconds
- [x] I have searched the issues of this repository and believe that this is not a duplicate.

## Expected Behavior

When passing a value of `0` into the resumeHideDuration prop of the Snackbar component, the Snackbar will immediately fire `onClose` following a user interaction.

## Current Behavior

When passing a value of `0` into the resumeHideDuration prop of the Snackbar component, the component uses the autoHideDuration prop instead, because the value of `0` evaluates to `false` in [short-circuit](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Short-circuit_evaluation) evaluation

I can confirm this by looking at the source code of the Snackbar component. On Line 182 the component attempts to set a timer using the resumeHideDuration prop

https://github.com/mui-org/material-ui/blob/dce7801ffc1187137301d84bc367e7b8880e522e/packages/material-ui/src/Snackbar/Snackbar.js#L182

Then on Line 146, any value that evaluates to false will be skipped, with the next option being to use this.props.autoHideDuration.

https://github.com/mui-org/material-ui/blob/dce7801ffc1187137301d84bc367e7b8880e522e/packages/material-ui/src/Snackbar/Snackbar.js#L133-L147

## Steps to Reproduce

1. Create `<Snackbar />` component providing proper state handling of the open & onClose props, also supplying an action prop, allowing for user interaction. Then supply an autoHideDuration prop of any number larger than 0, and a resumeHideDuration prop with the value of 0.

2. Activate the snackbar

3. Interact with the snackbar (ie. click the Undo button)

CodeSandbox: https://codesandbox.io/s/04nz3r87qn

## Your Environment

Tech | Version
------------ | -------------
Material-UI | 1.2.3
React | 16.3.2
Browser | Safari 11.0.3 (11604.5.6.1.1)




Issue discussion:
@benz2012 You are right, we need to fix this line: https://github.com/mui-org/material-ui/blob/dce7801ffc1187137301d84bc367e7b8880e522e/packages/material-ui/src/Snackbar/Snackbar.js#L146
Something like this should do it:
```diff
-}, autoHideDuration || this.props.autoHideDuration || 0); 
+}, autoHideDuration !== null ? autoHideDuration : this.props.autoHideDuration); 
```

Solution patch:
diff --git a/packages/material-ui/src/Snackbar/Snackbar.js b/packages/material-ui/src/Snackbar/Snackbar.js
--- a/packages/material-ui/src/Snackbar/Snackbar.js
+++ b/packages/material-ui/src/Snackbar/Snackbar.js
@@ -133,19 +133,24 @@ class Snackbar extends React.Component {
   }
 
   // Timer that controls delay before snackbar auto hides
-  setAutoHideTimer(autoHideDuration = null) {
-    if (!this.props.onClose || this.props.autoHideDuration == null) {
+  setAutoHideTimer(autoHideDuration) {
+    const autoHideDurationBefore =
+      autoHideDuration != null ? autoHideDuration : this.props.autoHideDuration;
+
+    if (!this.props.onClose || autoHideDurationBefore == null) {
       return;
     }
 
     clearTimeout(this.timerAutoHide);
     this.timerAutoHide = setTimeout(() => {
-      if (!this.props.onClose || this.props.autoHideDuration == null) {
+      const autoHideDurationAfter =
+        autoHideDuration != null ? autoHideDuration : this.props.autoHideDuration;
+      if (!this.props.onClose || autoHideDurationAfter == null) {
         return;
       }
 
       this.props.onClose(null, 'timeout');
-    }, autoHideDuration || this.props.autoHideDuration || 0);
+    }, autoHideDurationBefore);
   }
 
   handleMouseEnter = event => {
@@ -178,11 +183,11 @@ class Snackbar extends React.Component {
   // or when the window is shown back.
   handleResume = () => {
     if (this.props.autoHideDuration != null) {
-      if (this.props.resumeHideDuration !== undefined) {
+      if (this.props.resumeHideDuration != null) {
         this.setAutoHideTimer(this.props.resumeHideDuration);
         return;
       }
-      this.setAutoHideTimer((this.props.autoHideDuration || 0) * 0.5);
+      this.setAutoHideTimer(this.props.autoHideDuration * 0.5);
     }
   };
 


Test patch:
diff --git a/packages/material-ui/src/Snackbar/Snackbar.test.js b/packages/material-ui/src/Snackbar/Snackbar.test.js
--- a/packages/material-ui/src/Snackbar/Snackbar.test.js
+++ b/packages/material-ui/src/Snackbar/Snackbar.test.js
@@ -275,6 +275,29 @@ describe('<Snackbar />', () => {
       assert.strictEqual(handleClose.callCount, 1);
       assert.deepEqual(handleClose.args[0], [null, 'timeout']);
     });
+
+    it('should call onClose immediately after user interaction when 0', () => {
+      const handleClose = spy();
+      const autoHideDuration = 6e3;
+      const resumeHideDuration = 0;
+      const wrapper = mount(
+        <Snackbar
+          open
+          onClose={handleClose}
+          message="message"
+          autoHideDuration={autoHideDuration}
+          resumeHideDuration={resumeHideDuration}
+        />,
+      );
+      wrapper.setProps({ open: true });
+      assert.strictEqual(handleClose.callCount, 0);
+      wrapper.simulate('mouseEnter');
+      clock.tick(100);
+      wrapper.simulate('mouseLeave');
+      clock.tick(resumeHideDuration);
+      assert.strictEqual(handleClose.callCount, 1);
+      assert.deepEqual(handleClose.args[0], [null, 'timeout']);
+    });
   });
 
   describe('prop: disableWindowBlurListener', () => {