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', () => {