diff --git a/src/hooks/useScrollDrag.ts b/src/hooks/useScrollDrag.ts index 36dfaab..3dedcce 100644 --- a/src/hooks/useScrollDrag.ts +++ b/src/hooks/useScrollDrag.ts @@ -38,6 +38,11 @@ export default function useScrollDrag( }); }; + const clearDragState = () => { + mouseDownLock = false; + stopScroll(); + }; + const onMouseDown = (e: MouseEvent) => { // Skip if element set draggable if ((e.target as HTMLElement).draggable || e.button !== 0) { @@ -52,10 +57,7 @@ export default function useScrollDrag( mouseDownLock = true; } }; - const onMouseUp = () => { - mouseDownLock = false; - stopScroll(); - }; + const onMouseMove = (e: MouseEvent) => { if (mouseDownLock) { const mouseY = getPageXY(e, false); @@ -76,13 +78,18 @@ export default function useScrollDrag( }; ele.addEventListener('mousedown', onMouseDown); - ele.ownerDocument.addEventListener('mouseup', onMouseUp); + ele.ownerDocument.addEventListener('mouseup', clearDragState); ele.ownerDocument.addEventListener('mousemove', onMouseMove); + + ele.ownerDocument.addEventListener('dragstart', clearDragState); + ele.ownerDocument.addEventListener('dragend', clearDragState); return () => { ele.removeEventListener('mousedown', onMouseDown); - ele.ownerDocument.removeEventListener('mouseup', onMouseUp); + ele.ownerDocument.removeEventListener('mouseup', clearDragState); ele.ownerDocument.removeEventListener('mousemove', onMouseMove); + ele.ownerDocument.removeEventListener('dragstart', clearDragState); + ele.ownerDocument.removeEventListener('dragend', clearDragState); stopScroll(); }; } diff --git a/tests/scroll.test.js b/tests/scroll.test.js index e3a7e04..2e30f46 100644 --- a/tests/scroll.test.js +++ b/tests/scroll.test.js @@ -731,4 +731,82 @@ describe('List.Scroll', () => { jest.useRealTimers(); }); + + it('should not scroll after dropping selected list text', () => { + const selectElementText = (element) => { + const range = document.createRange(); + range.selectNodeContents(element); + const selection = window.getSelection(); + selection.removeAllRanges(); + selection.addRange(range); + }; + + const onScroll = jest.fn(); + const onDragStart = jest.fn(); + const onDragEnd = jest.fn(); + document.addEventListener('dragstart', onDragStart); + document.addEventListener('dragend', onDragEnd); + + const { container } = render( + + {({ id }) =>
  • {id}
  • } +
    , + ); + const fixedItems = container.querySelectorAll('.fixed-item'); + const targetItem = fixedItems[0]; + if (targetItem) { + selectElementText(targetItem); + } + const listHolder = container.querySelector('.rc-virtual-list-holder'); + if (targetItem && listHolder) { + selectElementText(targetItem); + + fireEvent.scroll(listHolder, { target: { scrollTop: 100 } }); + expect(onScroll).toHaveBeenCalled(); + const scrollCallCountBeforeDrop = onScroll.mock.calls.length; + + const dragStartEvent = new Event('dragstart', { bubbles: true, cancelable: true }); + targetItem.ownerDocument.dispatchEvent(dragStartEvent); + + const rect = listHolder.getBoundingClientRect(); + fireEvent.dragOver(listHolder, { + clientY: rect.bottom + 10, + bubbles: true, + }); + + fireEvent.drop(listHolder, { + clientY: rect.bottom + 10, + bubbles: true, + }); + + const dragEndEvent = new Event('dragend', { bubbles: true, cancelable: true }); + targetItem.ownerDocument.dispatchEvent(dragEndEvent); + + const afterRect = listHolder.getBoundingClientRect(); + const mouseMoveEvent = new MouseEvent('mousemove', { + bubbles: true, + clientY: afterRect.top - 10, + }); + listHolder.dispatchEvent(mouseMoveEvent); + act(() => { + jest.advanceTimersByTime(100); + }); + expect(onScroll.mock.calls.length).toBe(scrollCallCountBeforeDrop); + } + expect(onDragStart).toHaveBeenCalled(); + expect(onDragEnd).toHaveBeenCalled(); + + const sel = window.getSelection(); + sel && sel.removeAllRanges(); + + document.removeEventListener('dragstart', onDragStart); + document.removeEventListener('dragend', onDragEnd); + }); });