diff --git a/packages/mui-base/src/useSlider/useSlider.ts b/packages/mui-base/src/useSlider/useSlider.ts index 8e1f2c9631799f..a70d9af425b8dc 100644 --- a/packages/mui-base/src/useSlider/useSlider.ts +++ b/packages/mui-base/src/useSlider/useSlider.ts @@ -16,7 +16,7 @@ import { UseSliderRootSlotProps, UseSliderThumbSlotProps, } from './useSlider.types'; -import { EventHandlers } from '../utils'; +import { areArraysEqual, EventHandlers } from '../utils'; const INTENTIONAL_DRAG_COUNT_THRESHOLD = 2; @@ -140,6 +140,19 @@ function focusThumb({ } } +function areValuesEqual( + newValue: number | Array, + oldValue: number | Array, +): boolean { + if (typeof newValue === 'number' && typeof oldValue === 'number') { + return newValue === oldValue; + } + if (typeof newValue === 'object' && typeof oldValue === 'object') { + return areArraysEqual(newValue, oldValue); + } + return false; +} + const axisProps = { horizontal: { offset: (percent: number) => ({ left: `${percent}%` }), @@ -356,7 +369,7 @@ export default function useSlider(parameters: UseSliderParameters): UseSliderRet setValueState(newValue); setFocusedThumbIndex(index); - if (handleChange) { + if (handleChange && !areValuesEqual(newValue, valueDerived)) { handleChange(event, newValue, index); } @@ -466,7 +479,7 @@ export default function useSlider(parameters: UseSliderParameters): UseSliderRet setDragging(true); } - if (handleChange && newValue !== valueDerived) { + if (handleChange && !areValuesEqual(newValue, valueDerived)) { handleChange(nativeEvent, newValue, activeIndex); } }); @@ -517,7 +530,7 @@ export default function useSlider(parameters: UseSliderParameters): UseSliderRet setValueState(newValue); - if (handleChange) { + if (handleChange && !areValuesEqual(newValue, valueDerived)) { handleChange(nativeEvent, newValue, activeIndex); } } @@ -584,7 +597,7 @@ export default function useSlider(parameters: UseSliderParameters): UseSliderRet setValueState(newValue); - if (handleChange) { + if (handleChange && !areValuesEqual(newValue, valueDerived)) { handleChange(event, newValue, activeIndex); } } diff --git a/packages/mui-material/src/Slider/Slider.test.js b/packages/mui-material/src/Slider/Slider.test.js index bf64fc50669e5c..0fd532131a5eae 100644 --- a/packages/mui-material/src/Slider/Slider.test.js +++ b/packages/mui-material/src/Slider/Slider.test.js @@ -119,23 +119,23 @@ describe('', () => { })); fireEvent.touchStart(container.firstChild, createTouches([{ identifier: 1, clientX: 0 }])); - expect(handleChange.callCount).to.equal(1); + expect(handleChange.callCount).to.equal(0); expect(handleChangeCommitted.callCount).to.equal(0); fireEvent.touchStart(document.body, createTouches([{ identifier: 2, clientX: 40 }])); - expect(handleChange.callCount).to.equal(1); + expect(handleChange.callCount).to.equal(0); expect(handleChangeCommitted.callCount).to.equal(0); fireEvent.touchMove(document.body, createTouches([{ identifier: 1, clientX: 1 }])); - expect(handleChange.callCount).to.equal(2); + expect(handleChange.callCount).to.equal(1); expect(handleChangeCommitted.callCount).to.equal(0); fireEvent.touchMove(document.body, createTouches([{ identifier: 2, clientX: 41 }])); - expect(handleChange.callCount).to.equal(2); + expect(handleChange.callCount).to.equal(1); expect(handleChangeCommitted.callCount).to.equal(0); fireEvent.touchEnd(document.body, createTouches([{ identifier: 1, clientX: 2 }])); - expect(handleChange.callCount).to.equal(2); + expect(handleChange.callCount).to.equal(1); expect(handleChangeCommitted.callCount).to.equal(1); }); @@ -330,25 +330,27 @@ describe('', () => { left: 0, })); - fireEvent.touchStart( - container.firstChild, - createTouches([{ identifier: 1, clientX: 21, clientY: 0 }]), - ); + fireEvent.touchStart(container.firstChild, createTouches([{ identifier: 1, clientX: 20 }])); - fireEvent.touchMove( - document.body, - createTouches([{ identifier: 1, clientX: 22, clientY: 0 }]), - ); - fireEvent.touchMove( - document.body, - createTouches([{ identifier: 1, clientX: 22.1, clientY: 0 }]), - ); + fireEvent.touchMove(document.body, createTouches([{ identifier: 1, clientX: 21 }])); + + fireEvent.touchEnd(document.body, createTouches([{ identifier: 1, clientX: 21 }])); + + fireEvent.touchStart(container.firstChild, createTouches([{ identifier: 1, clientX: 21 }])); - expect(handleChange.callCount).to.equal(3); + fireEvent.touchMove(document.body, createTouches([{ identifier: 1, clientX: 22 }])); + + fireEvent.touchEnd(document.body, createTouches([{ identifier: 1, clientX: 22 }])); + + fireEvent.touchStart(container.firstChild, createTouches([{ identifier: 1, clientX: 22 }])); + + fireEvent.touchMove(document.body, createTouches([{ identifier: 1, clientX: 22.1 }])); + + fireEvent.touchEnd(document.body, createTouches([{ identifier: 1, clientX: 22.1 }])); + + expect(handleChange.callCount).to.equal(2); expect(handleChange.args[0][1]).to.deep.equal([21, 30]); expect(handleChange.args[1][1]).to.deep.equal([22, 30]); - // TODO, consider not firing this change event since the values are the same to improve the DX. - expect(handleChange.args[2][1]).to.deep.equal([22, 30]); }); it('should not react to right clicks', () => { @@ -1116,12 +1118,20 @@ describe('', () => { ); } + render(); const slider = screen.getByTestId('slider'); - fireEvent.touchStart(slider, createTouches([{ identifier: 1 }])); + stub(slider, 'getBoundingClientRect').callsFake(() => ({ + width: 100, + height: 10, + bottom: 10, + left: 0, + })); - expect(handleChange.callCount).to.equal(1); + fireEvent.touchStart(slider, createTouches([{ identifier: 1, clientX: 0 }])); + + expect(handleChange.callCount).to.equal(0); expect(handleNativeEvent.callCount).to.equal(1); expect(handleNativeEvent.firstCall.args[0]).to.have.property('target', slider); expect(handleEvent.callCount).to.equal(1); @@ -1149,9 +1159,16 @@ describe('', () => { render(); const slider = screen.getByTestId('slider'); + stub(slider, 'getBoundingClientRect').callsFake(() => ({ + width: 100, + height: 10, + bottom: 10, + left: 0, + })); + fireEvent.mouseDown(slider); - expect(handleChange.callCount).to.equal(1); + expect(handleChange.callCount).to.equal(0); expect(handleNativeEvent.callCount).to.equal(1); expect(handleNativeEvent.firstCall.args[0]).to.have.property('target', slider); expect(handleEvent.callCount).to.equal(1);