diff --git a/docs/pages/api/slider.md b/docs/pages/api/slider.md
index 9abaed322be30c..ac7f04f7b3a76d 100644
--- a/docs/pages/api/slider.md
+++ b/docs/pages/api/slider.md
@@ -41,7 +41,7 @@ You can learn more about the difference by [reading this guide](/guides/minimizi
| onChange | func | | Callback function that is fired when the slider's value changed.
**Signature:**
`function(event: object, value: any) => void`
*event:* The event source of the callback.
*value:* The new value. |
| onChangeCommitted | func | | Callback function that is fired when the `mouseup` is triggered.
**Signature:**
`function(event: object, value: any) => void`
*event:* The event source of the callback.
*value:* The new value. |
| orientation | 'horizontal'
| 'vertical' | 'horizontal' | The slider orientation. |
-| step | number | 1 | The granularity with which the slider can step through values. (A "discrete" slider.) When step is `null`, the thumb can only be slid onto marks provided with the `marks` prop. |
+| step | number | 1 | The granularity with which the slider can step through values. (A "discrete" slider.) The `min` prop serves as the origin for the valid values. We recommend (max - min) to be evenly divisible by the step.
When step is `null`, the thumb can only be slid onto marks provided with the `marks` prop. |
| ThumbComponent | elementType | 'span' | The component used to display the value label. |
| track | 'normal'
| false
| 'inverted' | 'normal' | The track presentation:
- `normal` the track will render a bar representing the slider value. - `inverted` the track will render a bar representing the remaining slider value. - `false` the track will render without a bar. |
| value | number
| Array<number> | | The value of the slider. For ranged sliders, provide an array with two values. |
diff --git a/packages/material-ui/src/Slider/Slider.js b/packages/material-ui/src/Slider/Slider.js
index ad7f144e18055f..49afa013b91133 100644
--- a/packages/material-ui/src/Slider/Slider.js
+++ b/packages/material-ui/src/Slider/Slider.js
@@ -83,8 +83,8 @@ function getDecimalPrecision(num) {
return decimalPart ? decimalPart.length : 0;
}
-function roundValueToStep(value, step) {
- const nearest = Math.round(value / step) * step;
+function roundValueToStep(value, step, min) {
+ const nearest = Math.round((value - min) / step) * step + min;
return Number(nearest.toFixed(getDecimalPrecision(step)));
}
@@ -492,7 +492,7 @@ const Slider = React.forwardRef(function Slider(props, ref) {
event.preventDefault();
if (step) {
- newValue = roundValueToStep(newValue, step);
+ newValue = roundValueToStep(newValue, step, min);
}
newValue = clamp(newValue, min, max);
@@ -547,7 +547,7 @@ const Slider = React.forwardRef(function Slider(props, ref) {
let newValue;
newValue = percentToValue(percent, min, max);
if (step) {
- newValue = roundValueToStep(newValue, step);
+ newValue = roundValueToStep(newValue, step, min);
} else {
const marksValues = marks.map(mark => mark.value);
const closestIndex = findClosest(marksValues, newValue);
@@ -946,6 +946,9 @@ Slider.propTypes = {
orientation: PropTypes.oneOf(['horizontal', 'vertical']),
/**
* The granularity with which the slider can step through values. (A "discrete" slider.)
+ * The `min` prop serves as the origin for the valid values.
+ * We recommend (max - min) to be evenly divisible by the step.
+ *
* When step is `null`, the thumb can only be slid onto marks provided with the `marks` prop.
*/
step: PropTypes.number,
diff --git a/packages/material-ui/src/Slider/Slider.test.js b/packages/material-ui/src/Slider/Slider.test.js
index d33596eea2fb7f..b7161f924cce7c 100644
--- a/packages/material-ui/src/Slider/Slider.test.js
+++ b/packages/material-ui/src/Slider/Slider.test.js
@@ -306,22 +306,37 @@ describe('', () => {
key: 'ArrowRight',
};
+ it('should use min as the step origin', () => {
+ const { getByRole } = render();
+ const thumb = getByRole('slider');
+ thumb.focus();
+
+ fireEvent.keyDown(document.activeElement, moveRightEvent);
+ expect(thumb).to.have.attribute('aria-valuenow', '250');
+
+ fireEvent.keyDown(document.activeElement, moveLeftEvent);
+ expect(thumb).to.have.attribute('aria-valuenow', '150');
+ });
+
it('should reach right edge value', () => {
const { getByRole } = render();
const thumb = getByRole('slider');
thumb.focus();
fireEvent.keyDown(document.activeElement, moveRightEvent);
- expect(thumb).to.have.attribute('aria-valuenow', '100');
+ expect(thumb).to.have.attribute('aria-valuenow', '96');
+
+ fireEvent.keyDown(document.activeElement, moveRightEvent);
+ expect(thumb).to.have.attribute('aria-valuenow', '106');
fireEvent.keyDown(document.activeElement, moveRightEvent);
expect(thumb).to.have.attribute('aria-valuenow', '108');
fireEvent.keyDown(document.activeElement, moveLeftEvent);
- expect(thumb).to.have.attribute('aria-valuenow', '100');
+ expect(thumb).to.have.attribute('aria-valuenow', '96');
fireEvent.keyDown(document.activeElement, moveLeftEvent);
- expect(thumb).to.have.attribute('aria-valuenow', '90');
+ expect(thumb).to.have.attribute('aria-valuenow', '86');
});
it('should reach left edge value', () => {
@@ -329,17 +344,14 @@ describe('', () => {
const thumb = getByRole('slider');
thumb.focus();
- fireEvent.keyDown(document.activeElement, moveLeftEvent);
- expect(thumb).to.have.attribute('aria-valuenow', '10');
-
fireEvent.keyDown(document.activeElement, moveLeftEvent);
expect(thumb).to.have.attribute('aria-valuenow', '6');
fireEvent.keyDown(document.activeElement, moveRightEvent);
- expect(thumb).to.have.attribute('aria-valuenow', '20');
+ expect(thumb).to.have.attribute('aria-valuenow', '16');
fireEvent.keyDown(document.activeElement, moveRightEvent);
- expect(thumb).to.have.attribute('aria-valuenow', '30');
+ expect(thumb).to.have.attribute('aria-valuenow', '26');
});
it('should round value to step precision', () => {