diff --git a/src/hooks/useDrag.ts b/src/hooks/useDrag.ts index a497e3972..0257c6f08 100644 --- a/src/hooks/useDrag.ts +++ b/src/hooks/useDrag.ts @@ -58,7 +58,7 @@ function useDrag( document.removeEventListener('mousemove', mouseMoveEventRef.current); } if (mouseUpEventRef.current) { - document.removeEventListener('mouseup', mouseUpEventRef.current); + document.removeEventListener('mouseup', mouseUpEventRef.current, true); } if (touchEventTargetRef.current) { if (mouseMoveEventRef.current) { @@ -211,9 +211,11 @@ function useDrag( // End const onMouseUp: EventListener = (event) => { - event.preventDefault(); + if (event.type === 'touchend') { + event.preventDefault(); + } - document.removeEventListener('mouseup', onMouseUp); + document.removeEventListener('mouseup', onMouseUp, true); document.removeEventListener('mousemove', onMouseMove); if (touchEventTargetRef.current) { if (mouseMoveEventRef.current) { @@ -233,7 +235,7 @@ function useDrag( setDraggingDelete(false); }; - document.addEventListener('mouseup', onMouseUp); + document.addEventListener('mouseup', onMouseUp, true); document.addEventListener('mousemove', onMouseMove); e.currentTarget.addEventListener('touchend', onMouseUp); e.currentTarget.addEventListener('touchmove', onMouseMove); diff --git a/tests/Range.test.tsx b/tests/Range.test.tsx index dd6ad0044..32323733b 100644 --- a/tests/Range.test.tsx +++ b/tests/Range.test.tsx @@ -94,6 +94,28 @@ describe('Range', () => { fireEvent(container.getElementsByClassName(element)[0], touchMove); } + it('prevents the native default action when a touch drag ends', () => { + const onChangeComplete = jest.fn(); + const { container } = render( + , + ); + const handle = container.getElementsByClassName('rc-slider-handle')[0]; + const touchStart = createEvent.touchStart(handle, { + touches: [{}], + targetTouches: [{}], + }); + (touchStart as any).targetTouches[0].pageX = 20; + fireEvent(handle, touchStart); + + const touchEnd = createEvent.touchEnd(handle); + const preventDefault = jest.fn(); + Object.defineProperty(touchEnd, 'preventDefault', { value: preventDefault }); + fireEvent(handle, touchEnd); + + expect(preventDefault).toHaveBeenCalledTimes(1); + expect(onChangeComplete).toHaveBeenCalledWith([20, 40]); + }); + it('should render Range with correct DOM structure', () => { const { asFragment } = render(); expect(asFragment().firstChild).toMatchSnapshot(); @@ -140,9 +162,7 @@ describe('Range', () => { }); it('should render Range without tabIndex (equal null) correctly', () => { - const { container } = render( - , - ); + const { container } = render(); expect(container.getElementsByClassName('rc-slider-handle')[0]).not.toHaveAttribute('tabIndex'); expect(container.getElementsByClassName('rc-slider-handle')[1]).not.toHaveAttribute('tabIndex'); }); @@ -872,7 +892,12 @@ describe('Range', () => { it('respects handle disabled state and boolean disabled fallback', () => { const onChange = jest.fn(); const { container, rerender } = render( - , + , ); const disabledHandle = getHandle(container, 0); @@ -909,17 +934,23 @@ describe('Range', () => { expect(onChange).toHaveBeenCalledWith([0, 10, 100]); onChange.mockClear(); - rerender(); + rerender( + , + ); doMouseDown(container, 10, 'rc-slider', true); fireEvent.mouseUp(document); expect(onChange).not.toHaveBeenCalled(); - rerender(); + rerender( + , + ); doMouseDown(container, 90, 'rc-slider', true); fireEvent.mouseUp(document); expect(onChange).not.toHaveBeenCalled(); - rerender(); + rerender( + , + ); doMouseDown(container, 10, 'rc-slider', true); fireEvent.mouseUp(document); expect(onChange).not.toHaveBeenCalled(); @@ -963,7 +994,12 @@ describe('Range', () => { it('disables draggableTrack only when rendered handles are disabled', () => { const onChange = jest.fn(); const { container, unmount } = render( - , + , ); const track = container.getElementsByClassName('rc-slider-track')[0]; @@ -1002,7 +1038,12 @@ describe('Range', () => { it('keeps keyboard movement inside disabled handle boundaries', () => { const onChange = jest.fn(); const { container, unmount } = render( - , + , ); repeatKeyDown(getHandle(container, 0), keyCode.RIGHT, 50); @@ -1012,7 +1053,12 @@ describe('Range', () => { onChange.mockClear(); const { container: boundaryContainer } = render( - , + , ); const middleHandle = getHandle(boundaryContainer, 1); @@ -1029,7 +1075,13 @@ describe('Range', () => { it('respects pushable boundaries around disabled handles', () => { const onChange = jest.fn(); const { container } = render( - , + , ); repeatKeyDown(getHandle(container, 0), keyCode.UP, 30); @@ -1046,7 +1098,13 @@ describe('Range', () => { it('respects disabled boundaries with allowCross=false and step=null', () => { const onChange = jest.fn(); const { container, unmount } = render( - , + , ); repeatKeyDown(getHandle(container, 0), keyCode.RIGHT, 50); @@ -1056,7 +1114,15 @@ describe('Range', () => { onChange.mockClear(); const { container: stepContainer } = render( - , + , ); fireEvent.keyDown(getHandle(stepContainer, 0), { keyCode: keyCode.RIGHT }); diff --git a/tests/Slider.test.js b/tests/Slider.test.js index a145d0300..1adebb330 100644 --- a/tests/Slider.test.js +++ b/tests/Slider.test.js @@ -659,6 +659,35 @@ describe('Slider', () => { expect(onAfterChange).toHaveBeenCalledWith(20); }); + it('should finish dragging when a parent stops mouseup propagation', () => { + const onChange = jest.fn(); + const onChangeComplete = jest.fn(); + const onParentMouseUp = jest.fn((event) => event.stopPropagation()); + const { container, getByTestId } = render( +
+ +
, + ); + const handle = container.querySelector('.rc-slider-handle'); + + fireEvent.mouseDown(handle, { pageX: 50 }); + const firstMove = createEvent.mouseMove(document); + firstMove.pageX = 60; + fireEvent(document, firstMove); + expect(onChange).toHaveBeenCalled(); + const lastDraggedValue = onChange.mock.calls[onChange.mock.calls.length - 1][0]; + + onChange.mockClear(); + fireEvent.mouseUp(getByTestId('parent')); + expect(onParentMouseUp.mock.calls[0][0].defaultPrevented).toBe(false); + expect(onChangeComplete).toHaveBeenCalledWith(lastDraggedValue); + + const moveAfterRelease = createEvent.mouseMove(document); + moveAfterRelease.pageX = 70; + fireEvent(document, moveAfterRelease); + expect(onChange).not.toHaveBeenCalled(); + }); + // https://github.com/react-component/slider/pull/948 it('could drag handler after click tracker', () => { const onChange = jest.fn();