Repository: mlaursen/react-md (ts)

Issue:
[Slider] step behavior wrong in cases where min not zero
### Description
When using a slider with a step defined the increments are miscalculated and do not match the value provided by step.
### Images/Screenshots
![Image of Slider Error](https://i.imgur.com/H0wNuK6.png)
The incorrect bottom slider cannot be set to 2.5 or 2.75, despite those being evenly divisible by the 0.25 step starting at 0.5.
### Link to a gist or code sample where the issue can be reproduced
Issue is displayed on the forked codepen template:
[https://codepen.io/strickc/pen/VMdOLX](https://codepen.io/strickc/pen/VMdOLX)
<!--
You can save me even more time and help debugging process by using this codepen template:
https://codepen.io/mlaursen03/pen/ZLMWwX
-->
### Version
- React - 15
- React-MD - 1.2
- Browser - Chrome v61

I'd be interested in providing a fix for this bug if a pull request is welcome.

**Edited** to correct my confusion between the step and discreteTicks properties. 

Pull request:
[Slider] Fix value miscalculation when min and step are defined  (fixes #585)
Update the `calculateValueDistance `utility to apply scale to the value and _then_ add the minumum.  Previously the scale was applied after adding the minimum, leading to incorrect slider behavior.

- two new tests to test `calculateValueDistance `when a minimum and a step is specified, with `normalized `both `true` and `false`
- Updated docs to show example of a discrete slider with min and step defined and the correct values displayed
- Passes all tests
- All modified files lint with no warnings or errors.

fixes #585

Patch:
diff --git a/docs/src/components/Components/Sliders/CustomRangeAndTicks.jsx b/docs/src/components/Components/Sliders/CustomRangeAndTicks.jsx
index 2ee37e390c..1ed14673f6 100644
--- a/docs/src/components/Components/Sliders/CustomRangeAndTicks.jsx
+++ b/docs/src/components/Components/Sliders/CustomRangeAndTicks.jsx
@@ -10,6 +10,15 @@ const CustomRangeAndTicks = () => (
       max={3}
       step={0.5}
     />
+    <Slider
+      id="custom-range-step-slider"
+      label="Discrete Min = 1, Max = 3, Step = 0.25"
+      min={1}
+      max={3}
+      step={0.25}
+      valuePrecision={2}
+      discrete
+    />
     <Slider
       id="disctete-ticks-slider"
       label="Discrete with ticks and precision"
diff --git a/src/js/utils/NumberUtils/calculateValueDistance.js b/src/js/utils/NumberUtils/calculateValueDistance.js
index 0b7e70a0ad..1f8ed375f6 100644
--- a/src/js/utils/NumberUtils/calculateValueDistance.js
+++ b/src/js/utils/NumberUtils/calculateValueDistance.js
@@ -65,15 +65,9 @@ export default function calculateValueDistance(x, width, left, scale, step, min,
     }
 
     distance = value / scale * 100;
-    value += min;
+    value = (value * step) + min;
   } else {
-    value = min + Math.round(distance / 100 * scale);
-  }
-
-  if (step > 1) {
-    value *= step;
-  } else if (step > 0 && step < 1) {
-    value *= step;
+    value = min + step * Math.round(distance / 100 * scale);
   }
 
   return {


Test patch:
diff --git a/src/js/utils/NumberUtils/__tests__/calculateValueDistance.js b/src/js/utils/NumberUtils/__tests__/calculateValueDistance.js
index 2d2df28a0a..793f7def33 100644
--- a/src/js/utils/NumberUtils/__tests__/calculateValueDistance.js
+++ b/src/js/utils/NumberUtils/__tests__/calculateValueDistance.js
@@ -45,4 +45,34 @@ describe('calculateValueDistance', () => {
     });
     expect(calculateValueDistance(pageX, width, left, scale, step, min, max, normalized)).toEqual(expected);
   });
+
+  describe('handle min values with step < 1', () => {
+    const value = 1.75;
+    const pxOffset = 5;
+    const width = 210;
+    const left = 0;
+    const step = 0.25;
+    const min = 1;
+    const max = 3;
+    // introduce an error of pxOffset to demonstrate it is rounding to nearest step
+    const pageX = (value - min) / (max - min) * width + left - pxOffset;
+    const scale = (max - min) / step;
+    it('should correctly round the value to the nearest step with normalized = false', () => {
+      const normalized = false;
+      const expected = expect.objectContaining({
+        value,
+        distance: (pageX - left) / width * 100,
+      });
+      expect(calculateValueDistance(pageX, width, left, scale, step, min, max, normalized)).toEqual(expected);
+    });
+    it('should correctly round the value and the distance to the nearest step with normalized = true', () => {
+      // modify to test normalized calculation
+      const normalized = true;
+      const expected = expect.objectContaining({
+        value,
+        distance: (pageX + pxOffset - left) / width * 100, // it should compensate for the error introduced above
+      });
+      expect(calculateValueDistance(pageX, width, left, scale, step, min, max, normalized)).toEqual(expected);
+    });
+  });
 });