Repository: mui/material-ui (TypeScript) — Bug Fix

Issue:
Slider mark formatting not honouring change to min/max values
When changing the default min/max values on a slider, the mark formatting stops working and appears to work based on default values.

https://codesandbox.io/s/material-demo-mg72p



Discussion:
@Ajaay Can you explain explicitly in a bit more detail what the issue is? The issue template helps with this e.g. observed behaviour and expected behaviour
Hi Josh,

If you check the code sandbox and slide the slider, you’ll see that the marks are not formatting at the correct points.

Thanks
@Ajaay Your issue has been closed because it does not conform to our issue requirements. Everything looks fine to me. The marks show `1 min`, `2 min` and `3 min` correctly. Please follow the issue template next time.
I do see one problem:

![Material demo - CodeSandbox 2019-06-15 17-54-23](https://user-images.githubusercontent.com/3165635/59553555-b2414880-8f96-11e9-8c11-a71338003d2b.png)

@Ajaay Is this related? Could you please provide detail (not spending time to explain what the problem is, signals that it's not really important to you).
The fix should be as simple as:
``` diff
diff --git a/packages/material-ui-lab/src/Slider/Slider.js b/packages/material-ui-lab/src/Slider/Slider.js
index 9fde8f282..56e447482 100644
--- a/packages/material-ui-lab/src/Slider/Slider.js
+++ b/packages/material-ui-lab/src/Slider/Slider.js
@@ -641,8 +641,8 @@ const Slider = React.forwardRef(function Slider(props, ref) {
         const percent = valueToPercent(mark.value, min, max);
         const style = axisProps[axis].offset(percent);
         const markActive = range
-          ? percent >= values[0] && percent <= values[values.length - 1]
-          : percent <= values[0];
+          ? mark.value >= values[0] && mark.value <= values[values.length - 1]
+          : mark.value <= values[0];

         return (
           <React.Fragment key={mark.value}>
```

@Ajaay Do you want to submit a pull request? :)

Resolving patch:
diff --git a/docs/src/pages/components/slider/DiscreteSlider.js b/docs/src/pages/components/slider/DiscreteSlider.js
--- a/docs/src/pages/components/slider/DiscreteSlider.js
+++ b/docs/src/pages/components/slider/DiscreteSlider.js
@@ -48,12 +48,14 @@ export default function DiscreteSlider() {
         Temperature
       </Typography>
       <Slider
-        defaultValue={20}
+        defaultValue={30}
         getAriaValueText={valuetext}
         aria-labelledby="discrete-slider"
         valueLabelDisplay="auto"
         step={10}
         marks
+        min={10}
+        max={110}
       />
       <div className={classes.margin} />
       <Typography id="discrete-slider-custom" gutterBottom>
diff --git a/docs/src/pages/components/slider/DiscreteSlider.tsx b/docs/src/pages/components/slider/DiscreteSlider.tsx
--- a/docs/src/pages/components/slider/DiscreteSlider.tsx
+++ b/docs/src/pages/components/slider/DiscreteSlider.tsx
@@ -50,12 +50,14 @@ export default function DiscreteSlider() {
         Temperature
       </Typography>
       <Slider
-        defaultValue={20}
+        defaultValue={30}
         getAriaValueText={valuetext}
         aria-labelledby="discrete-slider"
         valueLabelDisplay="auto"
         step={10}
         marks
+        min={10}
+        max={110}
       />
       <div className={classes.margin} />
       <Typography id="discrete-slider-custom" gutterBottom>
diff --git a/packages/material-ui-lab/src/Slider/Slider.js b/packages/material-ui-lab/src/Slider/Slider.js
--- a/packages/material-ui-lab/src/Slider/Slider.js
+++ b/packages/material-ui-lab/src/Slider/Slider.js
@@ -1,5 +1,3 @@
-/* eslint-disable no-use-before-define */
-
 import React from 'react';
 import PropTypes from 'prop-types';
 import clsx from 'clsx';
@@ -104,20 +102,20 @@ function focusThumb({ sliderRef, activeIndex, setActive }) {
 
 const axisProps = {
   horizontal: {
-    offset: value => ({ left: `${value}%` }),
-    leap: value => ({ width: `${value}%` }),
+    offset: percent => ({ left: `${percent}%` }),
+    leap: percent => ({ width: `${percent}%` }),
   },
   'horizontal-reverse': {
-    offset: value => ({ right: `${value}%` }),
-    leap: value => ({ width: `${value}%` }),
+    offset: percent => ({ right: `${percent}%` }),
+    leap: percent => ({ width: `${percent}%` }),
   },
   vertical: {
-    offset: value => ({ bottom: `${value}%` }),
-    leap: value => ({ height: `${value}%` }),
+    offset: percent => ({ bottom: `${percent}%` }),
+    leap: percent => ({ height: `${percent}%` }),
   },
   'vertical-reverse': {
-    offset: value => ({ top: `${value}%` }),
-    leap: value => ({ height: `${value}%` }),
+    offset: percent => ({ top: `${percent}%` }),
+    leap: percent => ({ height: `${percent}%` }),
   },
 };
 
@@ -323,8 +321,8 @@ const Slider = React.forwardRef(function Slider(props, ref) {
   values = values.map(value => clamp(value, min, max));
   const marks =
     marksProp === true && step !== null
-      ? [...Array(Math.floor(max / step) + 1)].map((_, index) => ({
-          value: step * index,
+      ? [...Array(Math.floor((max - min) / step) + 1)].map((_, index) => ({
+          value: min + step * index,
         }))
       : marksProp;
 
@@ -335,6 +333,10 @@ const Slider = React.forwardRef(function Slider(props, ref) {
   const { isFocusVisible, onBlurVisible, ref: focusVisibleRef } = useIsFocusVisible();
   const [focusVisible, setFocusVisible] = React.useState(-1);
 
+  const sliderRef = React.useRef();
+  const handleFocusRef = useForkRef(focusVisibleRef, sliderRef);
+  const handleRef = useForkRef(ref, handleFocusRef);
+
   const handleFocus = useEventCallback(event => {
     const index = Number(event.currentTarget.getAttribute('data-index'));
     if (isFocusVisible(event)) {
@@ -435,9 +437,6 @@ const Slider = React.forwardRef(function Slider(props, ref) {
     }
   });
 
-  const sliderRef = React.useRef();
-  const handleFocusRef = useForkRef(focusVisibleRef, sliderRef);
-  const handleRef = useForkRef(ref, handleFocusRef);
   const previousIndex = React.useRef();
   let axis = orientation;
   if (theme.direction === 'rtl' && orientation === 'horizontal') {
@@ -619,11 +618,11 @@ const Slider = React.forwardRef(function Slider(props, ref) {
     document.body.addEventListener('mouseup', handleTouchEnd);
   });
 
-  const offset = range ? valueToPercent(values[0], min, max) : 0;
-  const leap = valueToPercent(values[values.length - 1], min, max) - offset;
+  const trackOffset = valueToPercent(range ? values[0] : min, min, max);
+  const trackLeap = valueToPercent(values[values.length - 1], min, max) - trackOffset;
   const trackStyle = {
-    ...axisProps[axis].offset(offset),
-    ...axisProps[axis].leap(leap),
+    ...axisProps[axis].offset(trackOffset),
+    ...axisProps[axis].leap(trackLeap),
   };
 
   return (
@@ -648,8 +647,8 @@ const Slider = React.forwardRef(function Slider(props, ref) {
         const percent = valueToPercent(mark.value, min, max);
         const style = axisProps[axis].offset(percent);
         const markActive = range
-          ? percent >= values[0] && percent <= values[values.length - 1]
-          : percent <= values[0];
+          ? mark.value >= values[0] && mark.value <= values[values.length - 1]
+          : mark.value <= values[0];
 
         return (
           <React.Fragment key={mark.value}>


Test patch:
diff --git a/packages/material-ui-lab/src/Slider/Slider.test.js b/packages/material-ui-lab/src/Slider/Slider.test.js
--- a/packages/material-ui-lab/src/Slider/Slider.test.js
+++ b/packages/material-ui-lab/src/Slider/Slider.test.js
@@ -53,17 +53,6 @@ describe('<Slider />', () => {
     return wrapper.find('[role="slider"]').filterWhere(wrapsIntrinsicElement);
   }
 
-  it('should render with the default and user classes', () => {
-    const wrapper = mount(<Slider value={0} className="mySliderClass" />);
-    assert.strictEqual(
-      wrapper
-        .find(`.${classes.root}`)
-        .first()
-        .hasClass('mySliderClass'),
-      true,
-    );
-  });
-
   it('should call handlers', () => {
     const handleChange = spy();
     const handleChangeCommitted = spy();
@@ -275,19 +264,36 @@ describe('<Slider />', () => {
   });
 
   describe('markActive state', () => {
-    it('should set the mark active', () => {
-      function getActives(wrapper) {
-        return wrapper
-          .find(`.${classes.markLabel}`)
-          .map(node => node.hasClass(classes.markLabelActive));
-      }
+    function getActives(wrapper) {
+      return wrapper
+        .find(`.${classes.markLabel}`)
+        .map(node => node.hasClass(classes.markLabelActive));
+    }
+
+    it('sets the marks active that are `within` the value', () => {
       const marks = [{ value: 5 }, { value: 10 }, { value: 15 }];
 
-      const wrapper1 = mount(<Slider disabled value={12} marks={marks} />);
-      assert.deepEqual(getActives(wrapper1), [true, true, false]);
+      const singleValueWrapper = mount(
+        <Slider disabled min={0} max={20} value={12} marks={marks} />,
+      );
+      assert.deepEqual(getActives(singleValueWrapper), [true, true, false]);
 
-      const wrapper2 = mount(<Slider disabled value={[8, 12]} marks={marks} />);
-      assert.deepEqual(getActives(wrapper2), [false, true, false]);
+      const rangeValueWrapper = mount(
+        <Slider disabled min={0} max={20} value={[8, 12]} marks={marks} />,
+      );
+      assert.deepEqual(getActives(rangeValueWrapper), [false, true, false]);
+    });
+
+    it('uses closed intervals for the within check', () => {
+      const exactMarkWrapper = mount(
+        <Slider disabled value={10} min={0} max={10} marks step={5} />,
+      );
+      assert.deepEqual(getActives(exactMarkWrapper), [true, true, true]);
+
+      const ofByOneWrapper = mount(
+        <Slider disabled value={9.99999} min={0} max={10} marks step={5} />,
+      );
+      assert.deepEqual(getActives(ofByOneWrapper), [true, true, false]);
     });
   });
 });