From 69226c41927566b85a7d3b0a220300990f8b04d6 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Fri, 29 Jul 2022 12:33:14 -0500 Subject: [PATCH 01/20] Fix focus after dropping item --- packages/@react-aria/dnd/src/DragManager.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/@react-aria/dnd/src/DragManager.ts b/packages/@react-aria/dnd/src/DragManager.ts index bbd4d34d799..e65e8d2e12d 100644 --- a/packages/@react-aria/dnd/src/DragManager.ts +++ b/packages/@react-aria/dnd/src/DragManager.ts @@ -13,6 +13,7 @@ import {announce} from '@react-aria/live-announcer'; import {ariaHideOutside} from '@react-aria/overlays'; import {DragEndEvent, DragItem, DropActivateEvent, DropEnterEvent, DropEvent, DropExitEvent, DropItem, DropOperation, DropTarget as DroppableCollectionTarget, FocusableElement} from '@react-types/shared'; +import {flushSync} from 'react-dom'; import {getDragModality, getTypes} from './utils'; import {getInteractionModality} from '@react-aria/interactions'; import type {LocalizedStringFormatter} from '@internationalized/string'; @@ -474,7 +475,16 @@ class DragSession { // Blur and re-focus the drop target so that the focus ring appears. if (this.currentDropTarget) { - this.currentDropTarget.element.blur(); + // Since we cancel all focus events in drag sessions, refire blur to make sure state gets updated so drag target doesn't think it's still focused + // i.e. When you from one list to another during a drag session, we need the blur to fire on the first list after the drag. + if (!this.dragTarget.element.contains(this.currentDropTarget.element)) { + this.dragTarget.element.dispatchEvent(new FocusEvent('blur')); + this.dragTarget.element.dispatchEvent(new FocusEvent('focusout', {bubbles: true})); + } + // Re-focus the focusedKey upon reorder. This requires a React rerender between blurring and focusing. + flushSync(() => { + this.currentDropTarget.element.blur(); + }); this.currentDropTarget.element.focus(); } From 08bd91fccefc06c0d7c93e4510410a23c676f19f Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 1 Aug 2022 17:46:45 -0500 Subject: [PATCH 02/20] add tests --- .../list/test/ListViewDnd.test.js | 317 +++++++++++++++++- 1 file changed, 316 insertions(+), 1 deletion(-) diff --git a/packages/@react-spectrum/list/test/ListViewDnd.test.js b/packages/@react-spectrum/list/test/ListViewDnd.test.js index efa465b5bcf..14a79d791ec 100644 --- a/packages/@react-spectrum/list/test/ListViewDnd.test.js +++ b/packages/@react-spectrum/list/test/ListViewDnd.test.js @@ -14,7 +14,7 @@ jest.mock('@react-aria/live-announcer'); import {act, fireEvent, installPointerEvent, render as renderComponent, waitFor, within} from '@react-spectrum/test-utils'; import {CUSTOM_DRAG_TYPE} from '@react-aria/dnd/src/constants'; import {DataTransfer, DataTransferItem, DragEvent} from '@react-aria/dnd/test/mocks'; -import {DragBetweenListsRootOnlyExample, DragExample, DragIntoItemExample, ReorderExample} from '../stories/ListView.stories'; +import {DragBetweenListsExample, DragBetweenListsRootOnlyExample, DragExample, DragIntoItemExample, ReorderExample} from '../stories/ListView.stories'; import {Droppable} from '@react-aria/dnd/test/examples'; import {Provider} from '@react-spectrum/provider'; import React from 'react'; @@ -87,6 +87,13 @@ describe('ListView', function () { ); } + + function DragBetweenLists(props) { + return ( + + ); + } + beforeEach(() => { jest.spyOn(HTMLElement.prototype, 'getBoundingClientRect').mockImplementation(() => ({ left: 0, @@ -335,6 +342,143 @@ describe('ListView', function () { expect(event.defaultPrevented).toBe(true); expect(dataTransfer.items._items).toHaveLength(0); }); + + it('should allow moving item within a list', async function () { + let {getAllByRole} = render(); + + let rows = getAllByRole('row'); + expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); + expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); + expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); + + let cell = within(rows[0]).getByRole('gridcell'); + fireEvent.pointerDown(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + fireEvent.pointerMove(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 40}); + fireEvent.pointerUp(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + + await act(async () => Promise.resolve()); + act(() => jest.runAllTimers()); + + expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); + expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Three'); + expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Two'); + + expect(document.activeElement).toBe(rows[1]); + }); + + it('should allow moving multiple items within a list', async function () { + let {getAllByRole} = render(); + + let rows = getAllByRole('row'); + expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); + expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); + expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); + expect(within(rows[3]).getByRole('gridcell')).toHaveTextContent('Item Four'); + + let cell1 = within(rows[0]).getByRole('gridcell'); + let cell2 = within(rows[1]).getByRole('gridcell'); + fireEvent.pointerDown(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + fireEvent.pointerUp(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + + fireEvent.pointerDown(cell2, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + fireEvent.pointerUp(cell2, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + + fireEvent.pointerDown(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + fireEvent.pointerUp(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + fireEvent.pointerMove(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 80}); + fireEvent.pointerUp(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + + await act(async () => Promise.resolve()); + act(() => jest.runAllTimers()); + + expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); + expect(within(rows[3]).getByRole('gridcell')).toHaveTextContent('Item Four'); + expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); + expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); + + expect(document.activeElement).toBe(rows[2]); + }); + + it('should allow moving one item into another list', async function () { + let {getAllByRole} = render(); + + let list1 = getAllByRole('grid')[0]; + let list2 = getAllByRole('grid')[1]; + + let list1rows = within(list1).getAllByRole('row'); + let list2rows = within(list2).getAllByRole('row'); + expect(within(list1rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); + expect(within(list1rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); + expect(within(list1rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); + + expect(within(list2rows[0]).getByRole('gridcell')).toHaveTextContent('Item Seven'); + expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Eight'); + expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Nine'); + + + let cell = within(list1rows[0]).getByRole('gridcell'); + fireEvent.pointerDown(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + fireEvent.pointerMove(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 400, clientY: 0}); + fireEvent.pointerUp(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + + await act(async () => Promise.resolve()); + act(() => jest.runAllTimers()); + + expect(within(list1rows[0]).getByRole('gridcell')).toHaveTextContent('Item Two'); + expect(within(list1rows[1]).getByRole('gridcell')).toHaveTextContent('Item Three'); + expect(within(list1rows[2]).getByRole('gridcell')).toHaveTextContent('Item Four'); + + expect(within(list2rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); + expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Seven'); + expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Eight'); + + expect(document.activeElement).toBe(list2rows[0]); + }); + + it('should allow moving multiple items into another list', async function () { + let {getAllByRole} = render(); + + let list1 = getAllByRole('grid')[0]; + let list2 = getAllByRole('grid')[1]; + + let list1rows = within(list1).getAllByRole('row'); + let list2rows = within(list2).getAllByRole('row'); + + expect(within(list1rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); + expect(within(list1rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); + expect(within(list1rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); + + expect(within(list2rows[0]).getByRole('gridcell')).toHaveTextContent('Item Seven'); + expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Eight'); + expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Nine'); + + + let cell1 = within(list1rows[0]).getByRole('gridcell'); + let cell2 = within(list1rows[1]).getByRole('gridcell'); + fireEvent.pointerDown(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + fireEvent.pointerUp(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + + fireEvent.pointerDown(cell2, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + fireEvent.pointerUp(cell2, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + + fireEvent.pointerDown(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + fireEvent.pointerUp(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + fireEvent.pointerMove(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 400, clientY: 0}); + fireEvent.pointerUp(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + + await act(async () => Promise.resolve()); + act(() => jest.runAllTimers()); + + expect(within(list1rows[0]).getByRole('gridcell')).toHaveTextContent('Item Two'); + expect(within(list1rows[1]).getByRole('gridcell')).toHaveTextContent('Item Four'); + expect(within(list1rows[2]).getByRole('gridcell')).toHaveTextContent('Item Five'); + + expect(within(list2rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); + expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Three'); + expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Seven'); + + expect(document.activeElement).toBe(list2rows[0]); + }); }); describe('via keyboard', function () { @@ -449,6 +593,177 @@ describe('ListView', function () { dropOperation: 'move' }); }); + + it('should allow moving one item within a list', async function () { + let {getAllByRole} = render(); + + let rows = getAllByRole('row'); + expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); + expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); + expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); + + userEvent.tab(); + + fireEvent.keyDown(document.activeElement, {key: 'ArrowRight'}); + fireEvent.keyUp(document.activeElement, {key: 'ArrowRight'}); + + fireEvent.keyDown(document.activeElement, {key: 'Enter'}); + fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + + fireEvent.keyDown(document.activeElement, {key: 'ArrowDown'}); + fireEvent.keyUp(document.activeElement, {key: 'ArrowDown'}); + + fireEvent.keyDown(document.activeElement, {key: 'Enter'}); + fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + + await act(async () => Promise.resolve()); + act(() => jest.runAllTimers()); + + expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); + expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Three'); + expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Two'); + + expect(document.activeElement).toBe(rows[1]); + }); + + it('should allow moving multiple items within a list', async function () { + let {getAllByRole} = render(); + + let rows = getAllByRole('row'); + expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); + expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); + expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); + expect(within(rows[3]).getByRole('gridcell')).toHaveTextContent('Item Four'); + + userEvent.tab(); + + fireEvent.keyDown(document.activeElement, {key: 'Enter'}); + fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + + fireEvent.keyDown(document.activeElement, {key: 'ArrowDown'}); + fireEvent.keyUp(document.activeElement, {key: 'ArrowDown'}); + + fireEvent.keyDown(document.activeElement, {key: 'Enter'}); + fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + + fireEvent.keyDown(document.activeElement, {key: 'ArrowRight'}); + fireEvent.keyUp(document.activeElement, {key: 'ArrowRight'}); + + fireEvent.keyDown(document.activeElement, {key: 'Enter'}); + fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + + fireEvent.keyDown(document.activeElement, {key: 'ArrowDown'}); + fireEvent.keyUp(document.activeElement, {key: 'ArrowDown'}); + + fireEvent.keyDown(document.activeElement, {key: 'Enter'}); + fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + + await act(async () => Promise.resolve()); + act(() => jest.runAllTimers()); + + expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); + expect(within(rows[3]).getByRole('gridcell')).toHaveTextContent('Item Four'); + expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); + expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); + + expect(document.activeElement).toBe(rows[2]); + }); + + it('should allow moving one item into another list', async function () { + let {getAllByRole} = render(); + + let list1 = getAllByRole('grid')[0]; + let list2 = getAllByRole('grid')[1]; + + let list1rows = within(list1).getAllByRole('row'); + let list2rows = within(list2).getAllByRole('row'); + expect(within(list1rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); + expect(within(list1rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); + expect(within(list1rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); + + expect(within(list2rows[0]).getByRole('gridcell')).toHaveTextContent('Item Seven'); + expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Eight'); + expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Nine'); + + userEvent.tab(); + + fireEvent.keyDown(document.activeElement, {key: 'ArrowRight'}); + fireEvent.keyUp(document.activeElement, {key: 'ArrowRight'}); + + fireEvent.keyDown(document.activeElement, {key: 'Enter'}); + fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + + userEvent.tab(); + + fireEvent.keyDown(document.activeElement, {key: 'Enter'}); + fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + + await act(async () => Promise.resolve()); + act(() => jest.runAllTimers()); + + expect(within(list1rows[0]).getByRole('gridcell')).toHaveTextContent('Item Two'); + expect(within(list1rows[1]).getByRole('gridcell')).toHaveTextContent('Item Three'); + expect(within(list1rows[2]).getByRole('gridcell')).toHaveTextContent('Item Four'); + + expect(within(list2rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); + expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Seven'); + expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Eight'); + + expect(document.activeElement).toBe(list2rows[0]); + }); + + it('should allow moving multiple items into another list', async function () { + let {getAllByRole} = render(); + + let list1 = getAllByRole('grid')[0]; + let list2 = getAllByRole('grid')[1]; + + let list1rows = within(list1).getAllByRole('row'); + let list2rows = within(list2).getAllByRole('row'); + + expect(within(list1rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); + expect(within(list1rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); + expect(within(list1rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); + + expect(within(list2rows[0]).getByRole('gridcell')).toHaveTextContent('Item Seven'); + expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Eight'); + expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Nine'); + + userEvent.tab(); + + fireEvent.keyDown(document.activeElement, {key: 'Enter'}); + fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + + fireEvent.keyDown(document.activeElement, {key: 'ArrowDown'}); + fireEvent.keyUp(document.activeElement, {key: 'ArrowDown'}); + + fireEvent.keyDown(document.activeElement, {key: 'Enter'}); + fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + + fireEvent.keyDown(document.activeElement, {key: 'ArrowRight'}); + fireEvent.keyUp(document.activeElement, {key: 'ArrowRight'}); + + fireEvent.keyDown(document.activeElement, {key: 'Enter'}); + fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + + userEvent.tab(); + + fireEvent.keyDown(document.activeElement, {key: 'Enter'}); + fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + + await act(async () => Promise.resolve()); + act(() => jest.runAllTimers()); + + expect(within(list1rows[0]).getByRole('gridcell')).toHaveTextContent('Item Two'); + expect(within(list1rows[1]).getByRole('gridcell')).toHaveTextContent('Item Four'); + expect(within(list1rows[2]).getByRole('gridcell')).toHaveTextContent('Item Five'); + + expect(within(list2rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); + expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Three'); + expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Seven'); + + expect(document.activeElement).toBe(list2rows[0]); + }); }); it('should make row selection happen on pressUp if list is draggable', function () { From dcdee3b00618682901e1dd3c9a0f2de6a7be9129 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 11 Aug 2022 12:50:11 -0500 Subject: [PATCH 03/20] add drag utility --- .../list/test/ListViewDnd.test.js | 34 +++----- packages/dev/test-utils/src/events.ts | 83 +++++++++++++++++++ 2 files changed, 93 insertions(+), 24 deletions(-) diff --git a/packages/@react-spectrum/list/test/ListViewDnd.test.js b/packages/@react-spectrum/list/test/ListViewDnd.test.js index 77f67ff3675..c625a320e9c 100644 --- a/packages/@react-spectrum/list/test/ListViewDnd.test.js +++ b/packages/@react-spectrum/list/test/ListViewDnd.test.js @@ -11,7 +11,7 @@ */ jest.mock('@react-aria/live-announcer'); -import {act, fireEvent, installPointerEvent, render as renderComponent, waitFor, within} from '@react-spectrum/test-utils'; +import {act, drag, fireEvent, installPointerEvent, render as renderComponent, waitFor, within} from '@react-spectrum/test-utils'; import {CUSTOM_DRAG_TYPE} from '@react-aria/dnd/src/constants'; import {DataTransfer, DataTransferItem, DragEvent} from '@react-aria/dnd/test/mocks'; import {DragBetweenListsExample, DragBetweenListsRootOnlyExample, DragExample, DragIntoItemExample, ReorderExample} from '../stories/ListView.stories'; @@ -351,10 +351,7 @@ describe('ListView', function () { expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); - let cell = within(rows[0]).getByRole('gridcell'); - fireEvent.pointerDown(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); - fireEvent.pointerMove(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 40}); - fireEvent.pointerUp(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + drag(rows[1], {to: rows[2], delta: {x: 0, y: 5}}); await act(async () => Promise.resolve()); act(() => jest.runAllTimers()); @@ -375,18 +372,15 @@ describe('ListView', function () { expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); expect(within(rows[3]).getByRole('gridcell')).toHaveTextContent('Item Four'); - let cell1 = within(rows[0]).getByRole('gridcell'); - let cell2 = within(rows[1]).getByRole('gridcell'); + let cell1 = within(rows[1]).getByRole('gridcell'); + let cell2 = within(rows[2]).getByRole('gridcell'); fireEvent.pointerDown(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); fireEvent.pointerUp(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); fireEvent.pointerDown(cell2, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); fireEvent.pointerUp(cell2, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); - fireEvent.pointerDown(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); - fireEvent.pointerUp(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); - fireEvent.pointerMove(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 80}); - fireEvent.pointerUp(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + drag(rows[1], {to: rows[3], delta: {x: 0, y: 5}}); await act(async () => Promise.resolve()); act(() => jest.runAllTimers()); @@ -415,11 +409,7 @@ describe('ListView', function () { expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Eight'); expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Nine'); - - let cell = within(list1rows[0]).getByRole('gridcell'); - fireEvent.pointerDown(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); - fireEvent.pointerMove(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 400, clientY: 0}); - fireEvent.pointerUp(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + drag(list1rows[0], {to: list2rows[0], delta: {x: 0, y: -5}}); await act(async () => Promise.resolve()); act(() => jest.runAllTimers()); @@ -452,19 +442,15 @@ describe('ListView', function () { expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Eight'); expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Nine'); - let cell1 = within(list1rows[0]).getByRole('gridcell'); - let cell2 = within(list1rows[1]).getByRole('gridcell'); + let cell2 = within(list1rows[2]).getByRole('gridcell'); fireEvent.pointerDown(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); fireEvent.pointerUp(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); fireEvent.pointerDown(cell2, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); fireEvent.pointerUp(cell2, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); - fireEvent.pointerDown(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); - fireEvent.pointerUp(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); - fireEvent.pointerMove(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 400, clientY: 0}); - fireEvent.pointerUp(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + drag(list1rows[0], {to: list2rows[0], delta: {x: 0, y: -5}}); await act(async () => Promise.resolve()); act(() => jest.runAllTimers()); @@ -623,7 +609,7 @@ describe('ListView', function () { expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Three'); expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Two'); - expect(document.activeElement).toBe(rows[1]); + expect(document.activeElement).toBe(rows[2]); }); it('should allow moving multiple items within a list', async function () { @@ -666,7 +652,7 @@ describe('ListView', function () { expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); - expect(document.activeElement).toBe(rows[2]); + expect(document.activeElement).toBe(rows[3]); }); it('should allow moving one item into another list', async function () { diff --git a/packages/dev/test-utils/src/events.ts b/packages/dev/test-utils/src/events.ts index e6faf8ca431..250398f2814 100644 --- a/packages/dev/test-utils/src/events.ts +++ b/packages/dev/test-utils/src/events.ts @@ -121,3 +121,86 @@ export function typeText(el: HTMLElement, value: string, opts?: ITypeOpts) { skipClick = true; } } + +export function getElementCenter(element: Element) { + let {left, top, width, height} = element.getBoundingClientRect(); + return { + x: left + width / 2, + y: top + height / 2 + }; +} + +/** + * Drag an element onto a target element, by a specific delta, or both. + * If target element provided, will drag to center of target element. + * If both provided, delta will be from center of target element. + */ +export async function drag(element: Element, {delta, to: targetElement, steps = 2, duration = 50, type = 'mouse'}) { + if (!delta && !targetElement) { + throw new Error('Must provide a delta or target element.'); + } + + let from = getElementCenter(element); + let to = {x: 0, y: 0}; + + if (delta && targetElement) { + let targetCenter = getElementCenter(targetElement); + to = {x: targetCenter.x + delta.x, y: targetCenter.y + delta.y}; + } else if (targetElement) { + to = getElementCenter(targetElement); + } else if (delta) { + to = {x: from.x + delta.x, y: from.y + delta.y}; + } + + let step = { + x: (to.x - from.x) / steps, + y: (to.y - from.y) / steps + }; + + let current = { + clientX: from.x, + clientY: from.y + }; + + if (type === 'mouse') { + fireEvent.mouseEnter(element, current); + fireEvent.mouseOver(element, current); + fireEvent.mouseMove(element, current); + fireEvent.mouseDown(element, current); + } else if (type === 'pointer') { + fireEvent.pointerEnter(element, current); + fireEvent.pointerOver(element, current); + fireEvent.pointerMove(element, current); + fireEvent.pointerDown(element, current); + } else if (type === 'touch') { + fireEvent.touchStart(element, current); + fireEvent.touchMove(element, current); + fireEvent.touchEnd(element, current); + } + + for (let i = 0; i <= steps; i++) { + current.clientX += step.x; + current.clientY += step.y; + + await new Promise(resolve => { + setTimeout(resolve, duration / steps); + }); + + if (type === 'mouse') { + fireEvent.mouseMove(element, current); + } else if (type === 'pointer') { + fireEvent.pointerMove(element, current); + } else if (type === 'touch') { + fireEvent.touchMove(element, current); + } + + } + + if (type === 'mouse') { + fireEvent.mouseUp(element, current); + } else if (type === 'pointer') { + fireEvent.pointerUp(element, current); + } else if (type === 'touch') { + fireEvent.touchEnd(element, current); + } +} From 64950b040ab2edd9412a5dbb3d83af572bf133db Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 11 Aug 2022 14:00:06 -0500 Subject: [PATCH 04/20] fix touch event in drag function --- packages/dev/test-utils/src/events.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/dev/test-utils/src/events.ts b/packages/dev/test-utils/src/events.ts index 250398f2814..1c28cb75b89 100644 --- a/packages/dev/test-utils/src/events.ts +++ b/packages/dev/test-utils/src/events.ts @@ -175,7 +175,6 @@ export async function drag(element: Element, {delta, to: targetElement, steps = } else if (type === 'touch') { fireEvent.touchStart(element, current); fireEvent.touchMove(element, current); - fireEvent.touchEnd(element, current); } for (let i = 0; i <= steps; i++) { From a936d9adc54ebdc68c3e7ce1973c89de353a3c0b Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 11 Aug 2022 17:51:05 -0500 Subject: [PATCH 05/20] fix test --- .../list/test/ListViewDnd.test.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/@react-spectrum/list/test/ListViewDnd.test.js b/packages/@react-spectrum/list/test/ListViewDnd.test.js index c625a320e9c..afaed19f541 100644 --- a/packages/@react-spectrum/list/test/ListViewDnd.test.js +++ b/packages/@react-spectrum/list/test/ListViewDnd.test.js @@ -343,7 +343,7 @@ describe('ListView', function () { expect(dataTransfer.items._items).toHaveLength(0); }); - it('should allow moving item within a list', async function () { + it('should allow moving one item within a list', async function () { let {getAllByRole} = render(); let rows = getAllByRole('row'); @@ -386,9 +386,9 @@ describe('ListView', function () { act(() => jest.runAllTimers()); expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); - expect(within(rows[3]).getByRole('gridcell')).toHaveTextContent('Item Four'); - expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); - expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); + expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Four'); + expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Two'); + expect(within(rows[3]).getByRole('gridcell')).toHaveTextContent('Item Three'); expect(document.activeElement).toBe(rows[2]); }); @@ -596,11 +596,13 @@ describe('ListView', function () { fireEvent.keyDown(document.activeElement, {key: 'Enter'}); fireEvent.keyUp(document.activeElement, {key: 'Enter'}); - fireEvent.keyDown(document.activeElement, {key: 'ArrowDown'}); - fireEvent.keyUp(document.activeElement, {key: 'ArrowDown'}); + let droppable = getAllByRole('button')[0]; - fireEvent.keyDown(document.activeElement, {key: 'Enter'}); - fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + // fireEvent.keyDown(droppable, {key: 'ArrowDown'}); + // fireEvent.keyUp(droppable, {key: 'ArrowDown'}); + + fireEvent.keyDown(droppable, {key: 'Enter'}); + fireEvent.keyUp(droppable, {key: 'Enter'}); await act(async () => Promise.resolve()); act(() => jest.runAllTimers()); From bcefff7e941d7439048b11b2bf89ad443963835a Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Fri, 12 Aug 2022 13:03:20 -0500 Subject: [PATCH 06/20] update mouse drag tests --- .../list/test/ListViewDnd.test.js | 131 ++++++++++++++---- 1 file changed, 105 insertions(+), 26 deletions(-) diff --git a/packages/@react-spectrum/list/test/ListViewDnd.test.js b/packages/@react-spectrum/list/test/ListViewDnd.test.js index afaed19f541..e2deae23685 100644 --- a/packages/@react-spectrum/list/test/ListViewDnd.test.js +++ b/packages/@react-spectrum/list/test/ListViewDnd.test.js @@ -344,47 +344,84 @@ describe('ListView', function () { }); it('should allow moving one item within a list', async function () { - let {getAllByRole} = render(); + let {getAllByRole, getByRole} = render(); + let grid = getByRole('grid'); let rows = getAllByRole('row'); + let cell = within(rows[0]).getByRole('gridcell'); expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); - drag(rows[1], {to: rows[2], delta: {x: 0, y: 5}}); + let dataTransfer = new DataTransfer(); + fireEvent.pointerDown(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + fireEvent(cell, new DragEvent('dragstart', {dataTransfer, clientX: 0, clientY: 0})); + expect(onDragStart).toHaveBeenCalledTimes(1); + act(() => jest.runAllTimers()); await act(async () => Promise.resolve()); + + fireEvent.pointerMove(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 1, clientY: 75}); + fireEvent(cell, new DragEvent('drag', {dataTransfer, clientX: 1, clientY: 75})); + fireEvent(grid, new DragEvent('dragover', {dataTransfer, clientX: 1, clientY: 75})); + fireEvent.pointerUp(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 1, clientY: 75}); + fireEvent(cell, new DragEvent('dragend', {dataTransfer, clientX: 1, clientY: 75})); + expect(onDragEnd).toHaveBeenCalledTimes(1); + fireEvent(grid, new DragEvent('drop', {dataTransfer, clientX: 1, clientY: 75})); + act(() => jest.runAllTimers()); + await act(async () => Promise.resolve()); - expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); - expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Three'); - expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Two'); + // expect(onDrop).toHaveBeenCalledTimes(1); + + rows = getAllByRole('row'); + expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item Two'); + expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item One'); + expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); expect(document.activeElement).toBe(rows[1]); }); it('should allow moving multiple items within a list', async function () { - let {getAllByRole} = render(); + let {getAllByRole, getByRole} = render(); + let grid = getByRole('grid'); let rows = getAllByRole('row'); + let cell1 = within(rows[1]).getByRole('gridcell'); + let cell2 = within(rows[2]).getByRole('gridcell'); expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); expect(within(rows[3]).getByRole('gridcell')).toHaveTextContent('Item Four'); - let cell1 = within(rows[1]).getByRole('gridcell'); - let cell2 = within(rows[2]).getByRole('gridcell'); fireEvent.pointerDown(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); fireEvent.pointerUp(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); fireEvent.pointerDown(cell2, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); fireEvent.pointerUp(cell2, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); - drag(rows[1], {to: rows[3], delta: {x: 0, y: 5}}); + let dataTransfer = new DataTransfer(); + fireEvent.pointerDown(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + fireEvent(cell1, new DragEvent('dragstart', {dataTransfer, clientX: 0, clientY: 0})); + expect(onDragStart).toHaveBeenCalledTimes(1); + + act(() => jest.runAllTimers()); + await act(async () => Promise.resolve()); + + fireEvent.pointerMove(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 1, clientY: 125}); + fireEvent(cell1, new DragEvent('drag', {dataTransfer, clientX: 1, clientY: 125})); + fireEvent(grid, new DragEvent('dragover', {dataTransfer, clientX: 1, clientY: 125})); + fireEvent.pointerUp(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 1, clientY: 125}); + fireEvent(cell1, new DragEvent('dragend', {dataTransfer, clientX: 1, clientY: 125})); + expect(onDragEnd).toHaveBeenCalledTimes(1); + fireEvent(grid, new DragEvent('drop', {dataTransfer, clientX: 1, clientY: 125})); await act(async () => Promise.resolve()); act(() => jest.runAllTimers()); + expect(onDrop).toHaveBeenCalledTimes(1); + + rows = getAllByRole('row'); expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Four'); expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Two'); @@ -396,11 +433,12 @@ describe('ListView', function () { it('should allow moving one item into another list', async function () { let {getAllByRole} = render(); - let list1 = getAllByRole('grid')[0]; - let list2 = getAllByRole('grid')[1]; + let grid1 = getAllByRole('grid')[0]; + let grid2 = getAllByRole('grid')[1]; + let list1rows = within(grid1).getAllByRole('row'); + let list2rows = within(grid2).getAllByRole('row'); + let cell = within(list1rows[0]).getByRole('gridcell'); - let list1rows = within(list1).getAllByRole('row'); - let list2rows = within(list2).getAllByRole('row'); expect(within(list1rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); expect(within(list1rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); expect(within(list1rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); @@ -409,10 +447,31 @@ describe('ListView', function () { expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Eight'); expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Nine'); - drag(list1rows[0], {to: list2rows[0], delta: {x: 0, y: -5}}); + let dataTransfer = new DataTransfer(); + fireEvent.pointerDown(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + fireEvent(cell, new DragEvent('dragstart', {dataTransfer, clientX: 0, clientY: 0})); + expect(onDragStart).toHaveBeenCalledTimes(1); + act(() => jest.runAllTimers()); await act(async () => Promise.resolve()); + + fireEvent.pointerMove(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 200, clientY: -1}); + fireEvent(cell, new DragEvent('drag', {dataTransfer, clientX: 200, clientY: -1})); + fireEvent(grid2, new DragEvent('dragover', {dataTransfer, clientX: 200, clientY: -1})); + fireEvent.pointerUp(cell, {pointerType: 'mouse', button: 0, pointerId: 200, clientX: 1, clientY: -1}); + fireEvent(cell, new DragEvent('dragend', {dataTransfer, clientX: 200, clientY: -1})); + expect(onDragEnd).toHaveBeenCalledTimes(1); + fireEvent(grid2, new DragEvent('drop', {dataTransfer, clientX: 200, clientY: -1})); + act(() => jest.runAllTimers()); + await act(async () => Promise.resolve()); + + expect(onDrop).toHaveBeenCalledTimes(1); + + grid1 = getAllByRole('grid')[0]; + grid2 = getAllByRole('grid')[1]; + list1rows = within(grid1).getAllByRole('row'); + list2rows = within(grid2).getAllByRole('row'); expect(within(list1rows[0]).getByRole('gridcell')).toHaveTextContent('Item Two'); expect(within(list1rows[1]).getByRole('gridcell')).toHaveTextContent('Item Three'); @@ -428,11 +487,12 @@ describe('ListView', function () { it('should allow moving multiple items into another list', async function () { let {getAllByRole} = render(); - let list1 = getAllByRole('grid')[0]; - let list2 = getAllByRole('grid')[1]; - - let list1rows = within(list1).getAllByRole('row'); - let list2rows = within(list2).getAllByRole('row'); + let grid1 = getAllByRole('grid')[0]; + let grid2 = getAllByRole('grid')[1]; + let list1rows = within(grid1).getAllByRole('row'); + let list2rows = within(grid2).getAllByRole('row'); + let cell1 = within(list1rows[0]).getByRole('gridcell'); + let cell2 = within(list1rows[2]).getByRole('gridcell'); expect(within(list1rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); expect(within(list1rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); @@ -442,26 +502,45 @@ describe('ListView', function () { expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Eight'); expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Nine'); - let cell1 = within(list1rows[0]).getByRole('gridcell'); - let cell2 = within(list1rows[2]).getByRole('gridcell'); fireEvent.pointerDown(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); fireEvent.pointerUp(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); fireEvent.pointerDown(cell2, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); fireEvent.pointerUp(cell2, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); - drag(list1rows[0], {to: list2rows[0], delta: {x: 0, y: -5}}); + let dataTransfer = new DataTransfer(); + fireEvent.pointerDown(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + fireEvent(cell1, new DragEvent('dragstart', {dataTransfer, clientX: 0, clientY: 0})); + expect(onDragStart).toHaveBeenCalledTimes(1); + act(() => jest.runAllTimers()); await act(async () => Promise.resolve()); + + fireEvent.pointerMove(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 200, clientY: -1}); + fireEvent(cell1, new DragEvent('drag', {dataTransfer, clientX: 200, clientY: -1})); + fireEvent(grid2, new DragEvent('dragover', {dataTransfer, clientX: 200, clientY: -1})); + fireEvent.pointerUp(cell1, {pointerType: 'mouse', button: 0, pointerId: 200, clientX: 1, clientY: -1}); + fireEvent(cell1, new DragEvent('dragend', {dataTransfer, clientX: 200, clientY: -1})); + expect(onDragEnd).toHaveBeenCalledTimes(1); + fireEvent(grid2, new DragEvent('drop', {dataTransfer, clientX: 200, clientY: -1})); + act(() => jest.runAllTimers()); + await act(async () => Promise.resolve()); + + expect(onDrop).toHaveBeenCalledTimes(1); + + grid1 = getAllByRole('grid')[0]; + grid2 = getAllByRole('grid')[1]; + list1rows = within(grid1).getAllByRole('row'); + list2rows = within(grid2).getAllByRole('row'); expect(within(list1rows[0]).getByRole('gridcell')).toHaveTextContent('Item Two'); - expect(within(list1rows[1]).getByRole('gridcell')).toHaveTextContent('Item Four'); - expect(within(list1rows[2]).getByRole('gridcell')).toHaveTextContent('Item Five'); + expect(within(list1rows[1]).getByRole('gridcell')).toHaveTextContent('Item Three'); + expect(within(list1rows[2]).getByRole('gridcell')).toHaveTextContent('Item Four'); expect(within(list2rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); - expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Three'); - expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Seven'); + expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Seven'); + expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Eight'); expect(document.activeElement).toBe(list2rows[0]); }); From d368a297f44d444cbdd573e766703713165142c6 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Fri, 12 Aug 2022 13:06:43 -0500 Subject: [PATCH 07/20] rename drag utility --- packages/dev/test-utils/src/events.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dev/test-utils/src/events.ts b/packages/dev/test-utils/src/events.ts index 1c28cb75b89..62ef2a9bab0 100644 --- a/packages/dev/test-utils/src/events.ts +++ b/packages/dev/test-utils/src/events.ts @@ -135,7 +135,7 @@ export function getElementCenter(element: Element) { * If target element provided, will drag to center of target element. * If both provided, delta will be from center of target element. */ -export async function drag(element: Element, {delta, to: targetElement, steps = 2, duration = 50, type = 'mouse'}) { +export async function dragAndDrop(element: Element, {delta, to: targetElement, steps = 2, duration = 50, type = 'mouse'}) { if (!delta && !targetElement) { throw new Error('Must provide a delta or target element.'); } From db3a91e54c3900ea1f241c256b88062352904210 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Fri, 12 Aug 2022 16:20:49 -0500 Subject: [PATCH 08/20] test fixes --- .../list/test/ListViewDnd.test.js | 65 +++++++++---------- 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/packages/@react-spectrum/list/test/ListViewDnd.test.js b/packages/@react-spectrum/list/test/ListViewDnd.test.js index e2deae23685..47944306be2 100644 --- a/packages/@react-spectrum/list/test/ListViewDnd.test.js +++ b/packages/@react-spectrum/list/test/ListViewDnd.test.js @@ -11,7 +11,7 @@ */ jest.mock('@react-aria/live-announcer'); -import {act, drag, fireEvent, installPointerEvent, render as renderComponent, waitFor, within} from '@react-spectrum/test-utils'; +import {act, fireEvent, installPointerEvent, render as renderComponent, waitFor, within} from '@react-spectrum/test-utils'; import {CUSTOM_DRAG_TYPE} from '@react-aria/dnd/src/constants'; import {DataTransfer, DataTransferItem, DragEvent} from '@react-aria/dnd/test/mocks'; import {DragBetweenListsExample, DragBetweenListsRootOnlyExample, DragExample, DragIntoItemExample, ReorderExample} from '../stories/ListView.stories'; @@ -387,32 +387,28 @@ describe('ListView', function () { let grid = getByRole('grid'); let rows = getAllByRole('row'); - let cell1 = within(rows[1]).getByRole('gridcell'); - let cell2 = within(rows[2]).getByRole('gridcell'); + let cell = within(rows[1]).getByRole('gridcell'); expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); expect(within(rows[3]).getByRole('gridcell')).toHaveTextContent('Item Four'); - fireEvent.pointerDown(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); - fireEvent.pointerUp(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); - - fireEvent.pointerDown(cell2, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); - fireEvent.pointerUp(cell2, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + act(() => userEvent.click(within(rows[1]).getByRole('checkbox'))); + act(() => userEvent.click(within(rows[2]).getByRole('checkbox'))); let dataTransfer = new DataTransfer(); - fireEvent.pointerDown(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); - fireEvent(cell1, new DragEvent('dragstart', {dataTransfer, clientX: 0, clientY: 0})); + fireEvent.pointerDown(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + fireEvent(cell, new DragEvent('dragstart', {dataTransfer, clientX: 0, clientY: 0})); expect(onDragStart).toHaveBeenCalledTimes(1); act(() => jest.runAllTimers()); await act(async () => Promise.resolve()); - fireEvent.pointerMove(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 1, clientY: 125}); - fireEvent(cell1, new DragEvent('drag', {dataTransfer, clientX: 1, clientY: 125})); + fireEvent.pointerMove(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 1, clientY: 125}); + fireEvent(cell, new DragEvent('drag', {dataTransfer, clientX: 1, clientY: 125})); fireEvent(grid, new DragEvent('dragover', {dataTransfer, clientX: 1, clientY: 125})); - fireEvent.pointerUp(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 1, clientY: 125}); - fireEvent(cell1, new DragEvent('dragend', {dataTransfer, clientX: 1, clientY: 125})); + fireEvent.pointerUp(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 1, clientY: 125}); + fireEvent(cell, new DragEvent('dragend', {dataTransfer, clientX: 1, clientY: 125})); expect(onDragEnd).toHaveBeenCalledTimes(1); fireEvent(grid, new DragEvent('drop', {dataTransfer, clientX: 1, clientY: 125})); @@ -491,8 +487,7 @@ describe('ListView', function () { let grid2 = getAllByRole('grid')[1]; let list1rows = within(grid1).getAllByRole('row'); let list2rows = within(grid2).getAllByRole('row'); - let cell1 = within(list1rows[0]).getByRole('gridcell'); - let cell2 = within(list1rows[2]).getByRole('gridcell'); + let cell = within(list1rows[0]).getByRole('gridcell'); expect(within(list1rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); expect(within(list1rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); @@ -502,25 +497,22 @@ describe('ListView', function () { expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Eight'); expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Nine'); - fireEvent.pointerDown(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); - fireEvent.pointerUp(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); - - fireEvent.pointerDown(cell2, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); - fireEvent.pointerUp(cell2, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + act(() => userEvent.click(within(list1rows[0]).getByRole('checkbox'))); + act(() => userEvent.click(within(list1rows[2]).getByRole('checkbox'))); let dataTransfer = new DataTransfer(); - fireEvent.pointerDown(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); - fireEvent(cell1, new DragEvent('dragstart', {dataTransfer, clientX: 0, clientY: 0})); + fireEvent.pointerDown(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + fireEvent(cell, new DragEvent('dragstart', {dataTransfer, clientX: 0, clientY: 0})); expect(onDragStart).toHaveBeenCalledTimes(1); act(() => jest.runAllTimers()); await act(async () => Promise.resolve()); - fireEvent.pointerMove(cell1, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 200, clientY: -1}); - fireEvent(cell1, new DragEvent('drag', {dataTransfer, clientX: 200, clientY: -1})); + fireEvent.pointerMove(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 200, clientY: -1}); + fireEvent(cell, new DragEvent('drag', {dataTransfer, clientX: 200, clientY: -1})); fireEvent(grid2, new DragEvent('dragover', {dataTransfer, clientX: 200, clientY: -1})); - fireEvent.pointerUp(cell1, {pointerType: 'mouse', button: 0, pointerId: 200, clientX: 1, clientY: -1}); - fireEvent(cell1, new DragEvent('dragend', {dataTransfer, clientX: 200, clientY: -1})); + fireEvent.pointerUp(cell, {pointerType: 'mouse', button: 0, pointerId: 200, clientX: 1, clientY: -1}); + fireEvent(cell, new DragEvent('dragend', {dataTransfer, clientX: 200, clientY: -1})); expect(onDragEnd).toHaveBeenCalledTimes(1); fireEvent(grid2, new DragEvent('drop', {dataTransfer, clientX: 200, clientY: -1})); @@ -668,24 +660,27 @@ describe('ListView', function () { expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); userEvent.tab(); + let draghandle = within(getAllByRole('row')[0]).getAllByRole('button')[0]; + expect(draghandle).toBeTruthy(); fireEvent.keyDown(document.activeElement, {key: 'ArrowRight'}); fireEvent.keyUp(document.activeElement, {key: 'ArrowRight'}); - fireEvent.keyDown(document.activeElement, {key: 'Enter'}); - fireEvent.keyUp(document.activeElement, {key: 'Enter'}); - - let droppable = getAllByRole('button')[0]; + fireEvent.keyDown(draghandle, {key: 'Enter'}); + fireEvent.keyUp(draghandle, {key: 'Enter'}); - // fireEvent.keyDown(droppable, {key: 'ArrowDown'}); - // fireEvent.keyUp(droppable, {key: 'ArrowDown'}); + fireEvent.keyDown(document.activeElement, {key: 'ArrowDown'}); + fireEvent.keyUp(document.activeElement, {key: 'ArrowDown'}); - fireEvent.keyDown(droppable, {key: 'Enter'}); - fireEvent.keyUp(droppable, {key: 'Enter'}); + fireEvent.keyDown(document.activeElement, {key: 'Enter'}); + fireEvent.keyUp(document.activeElement, {key: 'Enter'}); await act(async () => Promise.resolve()); act(() => jest.runAllTimers()); + expect(onDrop).toHaveBeenCalledTimes(1); + + rows = getAllByRole('row'); expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Three'); expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Two'); From dbe21f0219224523540b5530392a49dcfc185ff2 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 16 Aug 2022 18:05:11 -0500 Subject: [PATCH 09/20] more test fixes --- .../list/test/ListViewDnd.test.js | 57 ++++++++++++------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/packages/@react-spectrum/list/test/ListViewDnd.test.js b/packages/@react-spectrum/list/test/ListViewDnd.test.js index 47944306be2..afe8790c5d1 100644 --- a/packages/@react-spectrum/list/test/ListViewDnd.test.js +++ b/packages/@react-spectrum/list/test/ListViewDnd.test.js @@ -84,7 +84,7 @@ describe('ListView', function () { function Reorderable(props) { return ( - + ); } @@ -348,7 +348,7 @@ describe('ListView', function () { let grid = getByRole('grid'); let rows = getAllByRole('row'); - let cell = within(rows[0]).getByRole('gridcell'); + let cell = within(rows[1]).getByRole('gridcell'); expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); @@ -368,16 +368,12 @@ describe('ListView', function () { fireEvent(cell, new DragEvent('dragend', {dataTransfer, clientX: 1, clientY: 75})); expect(onDragEnd).toHaveBeenCalledTimes(1); fireEvent(grid, new DragEvent('drop', {dataTransfer, clientX: 1, clientY: 75})); - - act(() => jest.runAllTimers()); - await act(async () => Promise.resolve()); - - // expect(onDrop).toHaveBeenCalledTimes(1); + expect(onDrop).toHaveBeenCalledTimes(1); rows = getAllByRole('row'); - expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item Two'); - expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item One'); - expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); + expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); + expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Three'); + expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Two'); expect(document.activeElement).toBe(rows[1]); }); @@ -660,14 +656,14 @@ describe('ListView', function () { expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); userEvent.tab(); - let draghandle = within(getAllByRole('row')[0]).getAllByRole('button')[0]; + let draghandle = within(getAllByRole('row')[1]).getAllByRole('button')[0]; expect(draghandle).toBeTruthy(); fireEvent.keyDown(document.activeElement, {key: 'ArrowRight'}); fireEvent.keyUp(document.activeElement, {key: 'ArrowRight'}); - fireEvent.keyDown(draghandle, {key: 'Enter'}); - fireEvent.keyUp(draghandle, {key: 'Enter'}); + fireEvent.keyDown(document.activeElement, {key: 'Enter'}); + fireEvent.keyUp(document.activeElement, {key: 'Enter'}); fireEvent.keyDown(document.activeElement, {key: 'ArrowDown'}); fireEvent.keyUp(document.activeElement, {key: 'ArrowDown'}); @@ -711,22 +707,25 @@ describe('ListView', function () { fireEvent.keyDown(document.activeElement, {key: 'ArrowRight'}); fireEvent.keyUp(document.activeElement, {key: 'ArrowRight'}); - fireEvent.keyDown(document.activeElement, {key: 'Enter'}); - fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + fireEvent.keyDown(document.body, {key: 'Enter'}); + fireEvent.keyUp(document.body, {key: 'Enter'}); - fireEvent.keyDown(document.activeElement, {key: 'ArrowDown'}); - fireEvent.keyUp(document.activeElement, {key: 'ArrowDown'}); + fireEvent.keyDown(document.body, {key: 'ArrowDown'}); + fireEvent.keyUp(document.body, {key: 'ArrowDown'}); - fireEvent.keyDown(document.activeElement, {key: 'Enter'}); - fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + fireEvent.keyDown(document.body, {key: 'Enter'}); + fireEvent.keyUp(document.body, {key: 'Enter'}); await act(async () => Promise.resolve()); act(() => jest.runAllTimers()); + expect(onDrop).toHaveBeenCalledTimes(1); + + rows = getAllByRole('row'); expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); - expect(within(rows[3]).getByRole('gridcell')).toHaveTextContent('Item Four'); - expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); - expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Three'); + expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Four'); + expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Two'); + expect(within(rows[3]).getByRole('gridcell')).toHaveTextContent('Item Three'); expect(document.activeElement).toBe(rows[3]); }); @@ -763,6 +762,13 @@ describe('ListView', function () { await act(async () => Promise.resolve()); act(() => jest.runAllTimers()); + expect(onDrop).toHaveBeenCalledTimes(1); + + list1 = getAllByRole('grid')[0]; + list2 = getAllByRole('grid')[1]; + list1rows = within(list1).getAllByRole('row'); + list2rows = within(list2).getAllByRole('row'); + expect(within(list1rows[0]).getByRole('gridcell')).toHaveTextContent('Item Two'); expect(within(list1rows[1]).getByRole('gridcell')).toHaveTextContent('Item Three'); expect(within(list1rows[2]).getByRole('gridcell')).toHaveTextContent('Item Four'); @@ -816,6 +822,13 @@ describe('ListView', function () { await act(async () => Promise.resolve()); act(() => jest.runAllTimers()); + expect(onDrop).toHaveBeenCalledTimes(1); + + list1 = getAllByRole('grid')[0]; + list2 = getAllByRole('grid')[1]; + list1rows = within(list1).getAllByRole('row'); + list2rows = within(list2).getAllByRole('row'); + expect(within(list1rows[0]).getByRole('gridcell')).toHaveTextContent('Item Two'); expect(within(list1rows[1]).getByRole('gridcell')).toHaveTextContent('Item Four'); expect(within(list1rows[2]).getByRole('gridcell')).toHaveTextContent('Item Five'); From 4ad078802bbc0a4f613f1078e29c2045bf83dafc Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 18 Aug 2022 14:13:48 -0500 Subject: [PATCH 10/20] fix some tests --- .../list/test/ListViewDnd.test.js | 74 ++++++++++++------- 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/packages/@react-spectrum/list/test/ListViewDnd.test.js b/packages/@react-spectrum/list/test/ListViewDnd.test.js index afe8790c5d1..a7aa2826db4 100644 --- a/packages/@react-spectrum/list/test/ListViewDnd.test.js +++ b/packages/@react-spectrum/list/test/ListViewDnd.test.js @@ -84,13 +84,13 @@ describe('ListView', function () { function Reorderable(props) { return ( - + ); } function DragBetweenLists(props) { return ( - + ); } @@ -358,24 +358,28 @@ describe('ListView', function () { fireEvent(cell, new DragEvent('dragstart', {dataTransfer, clientX: 0, clientY: 0})); expect(onDragStart).toHaveBeenCalledTimes(1); + fireEvent.pointerMove(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 1, clientY: 110}); + fireEvent(cell, new DragEvent('drag', {dataTransfer, clientX: 1, clientY: 110})); + fireEvent(grid, new DragEvent('dragover', {dataTransfer, clientX: 1, clientY: 110})); + fireEvent.pointerUp(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 1, clientY: 110}); + + fireEvent(grid, new DragEvent('drop', {dataTransfer, clientX: 1, clientY: 110})); act(() => jest.runAllTimers()); await act(async () => Promise.resolve()); + expect(onDrop).toHaveBeenCalledTimes(1); - fireEvent.pointerMove(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 1, clientY: 75}); - fireEvent(cell, new DragEvent('drag', {dataTransfer, clientX: 1, clientY: 75})); - fireEvent(grid, new DragEvent('dragover', {dataTransfer, clientX: 1, clientY: 75})); - fireEvent.pointerUp(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 1, clientY: 75}); - fireEvent(cell, new DragEvent('dragend', {dataTransfer, clientX: 1, clientY: 75})); + fireEvent(cell, new DragEvent('dragend', {dataTransfer, clientX: 1, clientY: 110})); expect(onDragEnd).toHaveBeenCalledTimes(1); - fireEvent(grid, new DragEvent('drop', {dataTransfer, clientX: 1, clientY: 75})); - expect(onDrop).toHaveBeenCalledTimes(1); + + act(() => jest.runAllTimers()); + await act(async () => Promise.resolve()); rows = getAllByRole('row'); expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Three'); expect(within(rows[2]).getByRole('gridcell')).toHaveTextContent('Item Two'); - expect(document.activeElement).toBe(rows[1]); + expect(document.activeElement).toBe(rows[2]); }); it('should allow moving multiple items within a list', async function () { @@ -400,19 +404,22 @@ describe('ListView', function () { act(() => jest.runAllTimers()); await act(async () => Promise.resolve()); - fireEvent.pointerMove(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 1, clientY: 125}); - fireEvent(cell, new DragEvent('drag', {dataTransfer, clientX: 1, clientY: 125})); - fireEvent(grid, new DragEvent('dragover', {dataTransfer, clientX: 1, clientY: 125})); - fireEvent.pointerUp(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 1, clientY: 125}); - fireEvent(cell, new DragEvent('dragend', {dataTransfer, clientX: 1, clientY: 125})); - expect(onDragEnd).toHaveBeenCalledTimes(1); - fireEvent(grid, new DragEvent('drop', {dataTransfer, clientX: 1, clientY: 125})); + fireEvent.pointerMove(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 1, clientY: 110}); + fireEvent(cell, new DragEvent('drag', {dataTransfer, clientX: 1, clientY: 110})); + fireEvent(grid, new DragEvent('dragover', {dataTransfer, clientX: 1, clientY: 110})); + fireEvent.pointerUp(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 1, clientY: 110}); - await act(async () => Promise.resolve()); + fireEvent(grid, new DragEvent('drop', {dataTransfer, clientX: 1, clientY: 110})); act(() => jest.runAllTimers()); - + await act(async () => Promise.resolve()); expect(onDrop).toHaveBeenCalledTimes(1); + fireEvent(cell, new DragEvent('dragend', {dataTransfer, clientX: 1, clientY: 110})); + expect(onDragEnd).toHaveBeenCalledTimes(1); + + act(() => jest.runAllTimers()); + await act(async () => Promise.resolve()); + rows = getAllByRole('row'); expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); expect(within(rows[1]).getByRole('gridcell')).toHaveTextContent('Item Four'); @@ -451,15 +458,17 @@ describe('ListView', function () { fireEvent(cell, new DragEvent('drag', {dataTransfer, clientX: 200, clientY: -1})); fireEvent(grid2, new DragEvent('dragover', {dataTransfer, clientX: 200, clientY: -1})); fireEvent.pointerUp(cell, {pointerType: 'mouse', button: 0, pointerId: 200, clientX: 1, clientY: -1}); - fireEvent(cell, new DragEvent('dragend', {dataTransfer, clientX: 200, clientY: -1})); + fireEvent(grid2, new DragEvent('drop', {dataTransfer, clientX: 1, clientY: 110})); + act(() => jest.runAllTimers()); + await act(async () => Promise.resolve()); + expect(onDrop).toHaveBeenCalledTimes(1); + + fireEvent(cell, new DragEvent('dragend', {dataTransfer, clientX: 1, clientY: 110})); expect(onDragEnd).toHaveBeenCalledTimes(1); - fireEvent(grid2, new DragEvent('drop', {dataTransfer, clientX: 200, clientY: -1})); act(() => jest.runAllTimers()); await act(async () => Promise.resolve()); - expect(onDrop).toHaveBeenCalledTimes(1); - grid1 = getAllByRole('grid')[0]; grid2 = getAllByRole('grid')[1]; list1rows = within(grid1).getAllByRole('row'); @@ -662,18 +671,22 @@ describe('ListView', function () { fireEvent.keyDown(document.activeElement, {key: 'ArrowRight'}); fireEvent.keyUp(document.activeElement, {key: 'ArrowRight'}); + expect(document.activeElement).toBe(draghandle); + fireEvent.keyDown(document.activeElement, {key: 'Enter'}); fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + act(() => jest.runAllTimers()); + fireEvent.keyDown(document.activeElement, {key: 'ArrowDown'}); fireEvent.keyUp(document.activeElement, {key: 'ArrowDown'}); + expect(document.activeElement).toHaveAttribute('aria-label', 'Insert between Item Three and Item Four'); + fireEvent.keyDown(document.activeElement, {key: 'Enter'}); fireEvent.keyUp(document.activeElement, {key: 'Enter'}); await act(async () => Promise.resolve()); - act(() => jest.runAllTimers()); - expect(onDrop).toHaveBeenCalledTimes(1); rows = getAllByRole('row'); @@ -704,21 +717,28 @@ describe('ListView', function () { fireEvent.keyDown(document.activeElement, {key: 'Enter'}); fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + let draghandle = within(getAllByRole('row')[2]).getAllByRole('button')[0]; + expect(draghandle).toBeTruthy(); + fireEvent.keyDown(document.activeElement, {key: 'ArrowRight'}); fireEvent.keyUp(document.activeElement, {key: 'ArrowRight'}); + expect(document.activeElement).toBe(draghandle); + fireEvent.keyDown(document.body, {key: 'Enter'}); fireEvent.keyUp(document.body, {key: 'Enter'}); + act(() => jest.runAllTimers()); + fireEvent.keyDown(document.body, {key: 'ArrowDown'}); fireEvent.keyUp(document.body, {key: 'ArrowDown'}); + expect(document.activeElement).toHaveAttribute('aria-label', 'Insert between Item Four and Item Five'); + fireEvent.keyDown(document.body, {key: 'Enter'}); fireEvent.keyUp(document.body, {key: 'Enter'}); await act(async () => Promise.resolve()); - act(() => jest.runAllTimers()); - expect(onDrop).toHaveBeenCalledTimes(1); rows = getAllByRole('row'); From 134cf7a0b6b771b093d4bd9a60fa121802b51f40 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 18 Aug 2022 16:09:41 -0500 Subject: [PATCH 11/20] update dragAndDrop utility --- packages/dev/test-utils/package.json | 1 + packages/dev/test-utils/src/events.ts | 23 ++++++++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/packages/dev/test-utils/package.json b/packages/dev/test-utils/package.json index 8b45ee0c65d..2c4d5d34893 100644 --- a/packages/dev/test-utils/package.json +++ b/packages/dev/test-utils/package.json @@ -33,6 +33,7 @@ }, "dependencies": { "@babel/runtime": "^7.6.2", + "@react-aria/dnd": "3.0.0-alpha.11", "@react-aria/ssr": "^3.0.0", "resolve": "^1.17.0" }, diff --git a/packages/dev/test-utils/src/events.ts b/packages/dev/test-utils/src/events.ts index 62ef2a9bab0..072010d90f4 100644 --- a/packages/dev/test-utils/src/events.ts +++ b/packages/dev/test-utils/src/events.ts @@ -11,6 +11,8 @@ */ import {act, fireEvent} from '@testing-library/react'; +// eslint-disable-next-line monorepo/no-internal-import +import {DataTransfer, DragEvent} from '@react-aria/dnd/test/mocks'; import type {ITypeOpts} from '@testing-library/user-event'; import userEvent from '@testing-library/user-event'; @@ -135,7 +137,7 @@ export function getElementCenter(element: Element) { * If target element provided, will drag to center of target element. * If both provided, delta will be from center of target element. */ -export async function dragAndDrop(element: Element, {delta, to: targetElement, steps = 2, duration = 50, type = 'mouse'}) { +export async function dragAndDrop(element: Element, {delta, to: targetElement, steps = 1, duration = 0, type = 'mouse'}) { if (!delta && !targetElement) { throw new Error('Must provide a delta or target element.'); } @@ -177,13 +179,19 @@ export async function dragAndDrop(element: Element, {delta, to: targetElement, s fireEvent.touchMove(element, current); } - for (let i = 0; i <= steps; i++) { + let dataTransfer = new DataTransfer(); + fireEvent(element, new DragEvent('dragstart', {dataTransfer, clientX: current.clientX, clientY: current.clientY})); + await act(async () => Promise.resolve()); + + for (let i = 0; i < steps; i++) { current.clientX += step.x; current.clientY += step.y; - await new Promise(resolve => { - setTimeout(resolve, duration / steps); - }); + if (duration !== 0 && steps > 1) { + await new Promise(resolve => { + setTimeout(resolve, duration / steps); + }); + } if (type === 'mouse') { fireEvent.mouseMove(element, current); @@ -193,6 +201,8 @@ export async function dragAndDrop(element: Element, {delta, to: targetElement, s fireEvent.touchMove(element, current); } + fireEvent(element, new DragEvent('drag', {dataTransfer, clientX: current.clientX, clientY: current.clientY})); + fireEvent(targetElement, new DragEvent('dragover', {dataTransfer, clientX: current.clientX, clientY: current.clientY})); } if (type === 'mouse') { @@ -202,4 +212,7 @@ export async function dragAndDrop(element: Element, {delta, to: targetElement, s } else if (type === 'touch') { fireEvent.touchEnd(element, current); } + + fireEvent(element, new DragEvent('dragend', {dataTransfer, clientX: current.clientX, clientY: current.clientY})); + fireEvent(targetElement, new DragEvent('drop', {dataTransfer, clientX: current.clientX, clientY: current.clientY})); } From 836b2f44ccc7723942bcc1304666e73111bc177c Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Thu, 18 Aug 2022 17:44:03 -0500 Subject: [PATCH 12/20] fix more tests --- .../list/stories/ListView.stories.tsx | 4 +-- .../list/test/ListViewDnd.test.js | 32 +++++++++++++------ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/packages/@react-spectrum/list/stories/ListView.stories.tsx b/packages/@react-spectrum/list/stories/ListView.stories.tsx index 6503d9dd1c5..483b5da26ca 100644 --- a/packages/@react-spectrum/list/stories/ListView.stories.tsx +++ b/packages/@react-spectrum/list/stories/ListView.stories.tsx @@ -872,7 +872,7 @@ export function DragIntoItemExample(props) { } export function DragBetweenListsExample(props) { - let onDropAction = action('onDrop'); + let onDrop = chain(action('onDrop'), props.onDrop); let list1 = useListData({ initialItems: props.items1 || itemList1 @@ -943,7 +943,7 @@ export function DragBetweenListsExample(props) { } } } - onDropAction(e); + onDrop(e); onMove(keys, e.target); } }, diff --git a/packages/@react-spectrum/list/test/ListViewDnd.test.js b/packages/@react-spectrum/list/test/ListViewDnd.test.js index a7aa2826db4..eb7d489c1b6 100644 --- a/packages/@react-spectrum/list/test/ListViewDnd.test.js +++ b/packages/@react-spectrum/list/test/ListViewDnd.test.js @@ -725,18 +725,18 @@ describe('ListView', function () { expect(document.activeElement).toBe(draghandle); - fireEvent.keyDown(document.body, {key: 'Enter'}); - fireEvent.keyUp(document.body, {key: 'Enter'}); + fireEvent.keyDown(document.activeElement, {key: 'Enter'}); + fireEvent.keyUp(document.activeElement, {key: 'Enter'}); act(() => jest.runAllTimers()); - fireEvent.keyDown(document.body, {key: 'ArrowDown'}); - fireEvent.keyUp(document.body, {key: 'ArrowDown'}); + fireEvent.keyDown(document.activeElement, {key: 'ArrowDown'}); + fireEvent.keyUp(document.activeElement, {key: 'ArrowDown'}); expect(document.activeElement).toHaveAttribute('aria-label', 'Insert between Item Four and Item Five'); - fireEvent.keyDown(document.body, {key: 'Enter'}); - fireEvent.keyUp(document.body, {key: 'Enter'}); + fireEvent.keyDown(document.activeElement, {key: 'Enter'}); + fireEvent.keyUp(document.activeElement, {key: 'Enter'}); await act(async () => Promise.resolve()); expect(onDrop).toHaveBeenCalledTimes(1); @@ -768,20 +768,27 @@ describe('ListView', function () { userEvent.tab(); + let draghandle = within(getAllByRole('row')[0]).getAllByRole('button')[0]; + expect(draghandle).toBeTruthy(); + fireEvent.keyDown(document.activeElement, {key: 'ArrowRight'}); fireEvent.keyUp(document.activeElement, {key: 'ArrowRight'}); + expect(document.activeElement).toBe(draghandle); + fireEvent.keyDown(document.activeElement, {key: 'Enter'}); fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + act(() => jest.runAllTimers()); + userEvent.tab(); + + expect(document.activeElement).toHaveAttribute('aria-label', 'Insert before Item Seven'); fireEvent.keyDown(document.activeElement, {key: 'Enter'}); fireEvent.keyUp(document.activeElement, {key: 'Enter'}); await act(async () => Promise.resolve()); - act(() => jest.runAllTimers()); - expect(onDrop).toHaveBeenCalledTimes(1); list1 = getAllByRole('grid')[0]; @@ -828,20 +835,25 @@ describe('ListView', function () { fireEvent.keyDown(document.activeElement, {key: 'Enter'}); fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + let draghandle = within(list1rows[0]).getAllByRole('button')[0]; + expect(draghandle).toBeTruthy(); + fireEvent.keyDown(document.activeElement, {key: 'ArrowRight'}); fireEvent.keyUp(document.activeElement, {key: 'ArrowRight'}); fireEvent.keyDown(document.activeElement, {key: 'Enter'}); fireEvent.keyUp(document.activeElement, {key: 'Enter'}); + act(() => jest.runAllTimers()); + userEvent.tab(); + expect(document.activeElement).toHaveAttribute('aria-label', 'Insert before Item Seven'); + fireEvent.keyDown(document.activeElement, {key: 'Enter'}); fireEvent.keyUp(document.activeElement, {key: 'Enter'}); await act(async () => Promise.resolve()); - act(() => jest.runAllTimers()); - expect(onDrop).toHaveBeenCalledTimes(1); list1 = getAllByRole('grid')[0]; From 9a13ae47124a44080ad9dc02692251724de41c72 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Fri, 19 Aug 2022 10:28:41 -0500 Subject: [PATCH 13/20] fix more tests --- .../list/stories/ListView.stories.tsx | 9 ++-- .../list/test/ListViewDnd.test.js | 43 ++++++++----------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/packages/@react-spectrum/list/stories/ListView.stories.tsx b/packages/@react-spectrum/list/stories/ListView.stories.tsx index 483b5da26ca..d61c79ad410 100644 --- a/packages/@react-spectrum/list/stories/ListView.stories.tsx +++ b/packages/@react-spectrum/list/stories/ListView.stories.tsx @@ -872,7 +872,10 @@ export function DragIntoItemExample(props) { } export function DragBetweenListsExample(props) { - let onDrop = chain(action('onDrop'), props.onDrop); + let {onDragStart, onDragEnd, onDrop} = props; + onDrop = chain(action('onDrop'), onDrop); + onDragStart = chain(action('dragStart'), onDragStart); + onDragEnd = chain(action('dragEnd'), onDragEnd); let list1 = useListData({ initialItems: props.items1 || itemList1 @@ -918,8 +921,8 @@ export function DragBetweenListsExample(props) { getAllowedDropOperationsAction(); return ['move', 'cancel']; }, - onDragStart: action('dragStart'), - onDragEnd: action('dragEnd') + onDragStart, + onDragEnd }); // Use a random drag type so the items can only be reordered within the two lists and not dragged elsewhere. diff --git a/packages/@react-spectrum/list/test/ListViewDnd.test.js b/packages/@react-spectrum/list/test/ListViewDnd.test.js index eb7d489c1b6..3b1510dec18 100644 --- a/packages/@react-spectrum/list/test/ListViewDnd.test.js +++ b/packages/@react-spectrum/list/test/ListViewDnd.test.js @@ -372,7 +372,6 @@ describe('ListView', function () { expect(onDragEnd).toHaveBeenCalledTimes(1); act(() => jest.runAllTimers()); - await act(async () => Promise.resolve()); rows = getAllByRole('row'); expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); @@ -401,9 +400,6 @@ describe('ListView', function () { fireEvent(cell, new DragEvent('dragstart', {dataTransfer, clientX: 0, clientY: 0})); expect(onDragStart).toHaveBeenCalledTimes(1); - act(() => jest.runAllTimers()); - await act(async () => Promise.resolve()); - fireEvent.pointerMove(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 1, clientY: 110}); fireEvent(cell, new DragEvent('drag', {dataTransfer, clientX: 1, clientY: 110})); fireEvent(grid, new DragEvent('dragover', {dataTransfer, clientX: 1, clientY: 110})); @@ -418,7 +414,6 @@ describe('ListView', function () { expect(onDragEnd).toHaveBeenCalledTimes(1); act(() => jest.runAllTimers()); - await act(async () => Promise.resolve()); rows = getAllByRole('row'); expect(within(rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); @@ -451,23 +446,20 @@ describe('ListView', function () { fireEvent(cell, new DragEvent('dragstart', {dataTransfer, clientX: 0, clientY: 0})); expect(onDragStart).toHaveBeenCalledTimes(1); - act(() => jest.runAllTimers()); - await act(async () => Promise.resolve()); - fireEvent.pointerMove(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 200, clientY: -1}); fireEvent(cell, new DragEvent('drag', {dataTransfer, clientX: 200, clientY: -1})); fireEvent(grid2, new DragEvent('dragover', {dataTransfer, clientX: 200, clientY: -1})); fireEvent.pointerUp(cell, {pointerType: 'mouse', button: 0, pointerId: 200, clientX: 1, clientY: -1}); - fireEvent(grid2, new DragEvent('drop', {dataTransfer, clientX: 1, clientY: 110})); + + fireEvent(cell, new DragEvent('dragend', {dataTransfer, clientX: 200, clientY: -1})); + expect(onDragEnd).toHaveBeenCalledTimes(1); + + fireEvent(grid2, new DragEvent('drop', {dataTransfer, clientX: 200, clientY: -1})); act(() => jest.runAllTimers()); await act(async () => Promise.resolve()); expect(onDrop).toHaveBeenCalledTimes(1); - fireEvent(cell, new DragEvent('dragend', {dataTransfer, clientX: 1, clientY: 110})); - expect(onDragEnd).toHaveBeenCalledTimes(1); - act(() => jest.runAllTimers()); - await act(async () => Promise.resolve()); grid1 = getAllByRole('grid')[0]; grid2 = getAllByRole('grid')[1]; @@ -482,7 +474,7 @@ describe('ListView', function () { expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Seven'); expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Eight'); - expect(document.activeElement).toBe(list2rows[0]); + // expect(document.activeElement).toBe(list2rows[0]); }); it('should allow moving multiple items into another list', async function () { @@ -510,36 +502,35 @@ describe('ListView', function () { fireEvent(cell, new DragEvent('dragstart', {dataTransfer, clientX: 0, clientY: 0})); expect(onDragStart).toHaveBeenCalledTimes(1); - act(() => jest.runAllTimers()); - await act(async () => Promise.resolve()); - fireEvent.pointerMove(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 200, clientY: -1}); fireEvent(cell, new DragEvent('drag', {dataTransfer, clientX: 200, clientY: -1})); fireEvent(grid2, new DragEvent('dragover', {dataTransfer, clientX: 200, clientY: -1})); fireEvent.pointerUp(cell, {pointerType: 'mouse', button: 0, pointerId: 200, clientX: 1, clientY: -1}); + fireEvent(cell, new DragEvent('dragend', {dataTransfer, clientX: 200, clientY: -1})); expect(onDragEnd).toHaveBeenCalledTimes(1); - fireEvent(grid2, new DragEvent('drop', {dataTransfer, clientX: 200, clientY: -1})); + fireEvent(grid2, new DragEvent('drop', {dataTransfer, clientX: 200, clientY: -1})); act(() => jest.runAllTimers()); await act(async () => Promise.resolve()); - expect(onDrop).toHaveBeenCalledTimes(1); + act(() => jest.runAllTimers()); + grid1 = getAllByRole('grid')[0]; grid2 = getAllByRole('grid')[1]; list1rows = within(grid1).getAllByRole('row'); list2rows = within(grid2).getAllByRole('row'); expect(within(list1rows[0]).getByRole('gridcell')).toHaveTextContent('Item Two'); - expect(within(list1rows[1]).getByRole('gridcell')).toHaveTextContent('Item Three'); - expect(within(list1rows[2]).getByRole('gridcell')).toHaveTextContent('Item Four'); + expect(within(list1rows[1]).getByRole('gridcell')).toHaveTextContent('Item Four'); + expect(within(list1rows[2]).getByRole('gridcell')).toHaveTextContent('Item Five'); expect(within(list2rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); - expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Seven'); - expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Eight'); + expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Three'); + expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Seven'); - expect(document.activeElement).toBe(list2rows[0]); + // expect(document.activeElement).toBe(list2rows[0]); }); }); @@ -791,6 +782,8 @@ describe('ListView', function () { await act(async () => Promise.resolve()); expect(onDrop).toHaveBeenCalledTimes(1); + act(() => jest.runAllTimers()); + list1 = getAllByRole('grid')[0]; list2 = getAllByRole('grid')[1]; list1rows = within(list1).getAllByRole('row'); @@ -856,6 +849,8 @@ describe('ListView', function () { await act(async () => Promise.resolve()); expect(onDrop).toHaveBeenCalledTimes(1); + act(() => jest.runAllTimers()); + list1 = getAllByRole('grid')[0]; list2 = getAllByRole('grid')[1]; list1rows = within(list1).getAllByRole('row'); From 1ad5b3b29f79dc71cc33f268fed527cad9628f7d Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Fri, 19 Aug 2022 14:39:03 -0500 Subject: [PATCH 14/20] revert dragAndDrop utility add --- packages/dev/test-utils/src/events.ts | 92 --------------------------- 1 file changed, 92 deletions(-) diff --git a/packages/dev/test-utils/src/events.ts b/packages/dev/test-utils/src/events.ts index 072010d90f4..fec45191067 100644 --- a/packages/dev/test-utils/src/events.ts +++ b/packages/dev/test-utils/src/events.ts @@ -124,95 +124,3 @@ export function typeText(el: HTMLElement, value: string, opts?: ITypeOpts) { } } -export function getElementCenter(element: Element) { - let {left, top, width, height} = element.getBoundingClientRect(); - return { - x: left + width / 2, - y: top + height / 2 - }; -} - -/** - * Drag an element onto a target element, by a specific delta, or both. - * If target element provided, will drag to center of target element. - * If both provided, delta will be from center of target element. - */ -export async function dragAndDrop(element: Element, {delta, to: targetElement, steps = 1, duration = 0, type = 'mouse'}) { - if (!delta && !targetElement) { - throw new Error('Must provide a delta or target element.'); - } - - let from = getElementCenter(element); - let to = {x: 0, y: 0}; - - if (delta && targetElement) { - let targetCenter = getElementCenter(targetElement); - to = {x: targetCenter.x + delta.x, y: targetCenter.y + delta.y}; - } else if (targetElement) { - to = getElementCenter(targetElement); - } else if (delta) { - to = {x: from.x + delta.x, y: from.y + delta.y}; - } - - let step = { - x: (to.x - from.x) / steps, - y: (to.y - from.y) / steps - }; - - let current = { - clientX: from.x, - clientY: from.y - }; - - if (type === 'mouse') { - fireEvent.mouseEnter(element, current); - fireEvent.mouseOver(element, current); - fireEvent.mouseMove(element, current); - fireEvent.mouseDown(element, current); - } else if (type === 'pointer') { - fireEvent.pointerEnter(element, current); - fireEvent.pointerOver(element, current); - fireEvent.pointerMove(element, current); - fireEvent.pointerDown(element, current); - } else if (type === 'touch') { - fireEvent.touchStart(element, current); - fireEvent.touchMove(element, current); - } - - let dataTransfer = new DataTransfer(); - fireEvent(element, new DragEvent('dragstart', {dataTransfer, clientX: current.clientX, clientY: current.clientY})); - await act(async () => Promise.resolve()); - - for (let i = 0; i < steps; i++) { - current.clientX += step.x; - current.clientY += step.y; - - if (duration !== 0 && steps > 1) { - await new Promise(resolve => { - setTimeout(resolve, duration / steps); - }); - } - - if (type === 'mouse') { - fireEvent.mouseMove(element, current); - } else if (type === 'pointer') { - fireEvent.pointerMove(element, current); - } else if (type === 'touch') { - fireEvent.touchMove(element, current); - } - - fireEvent(element, new DragEvent('drag', {dataTransfer, clientX: current.clientX, clientY: current.clientY})); - fireEvent(targetElement, new DragEvent('dragover', {dataTransfer, clientX: current.clientX, clientY: current.clientY})); - } - - if (type === 'mouse') { - fireEvent.mouseUp(element, current); - } else if (type === 'pointer') { - fireEvent.pointerUp(element, current); - } else if (type === 'touch') { - fireEvent.touchEnd(element, current); - } - - fireEvent(element, new DragEvent('dragend', {dataTransfer, clientX: current.clientX, clientY: current.clientY})); - fireEvent(targetElement, new DragEvent('drop', {dataTransfer, clientX: current.clientX, clientY: current.clientY})); -} From b06b08f3445b5673bc86254fb19afb4aa7a1dec2 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Fri, 19 Aug 2022 14:40:03 -0500 Subject: [PATCH 15/20] lint --- packages/dev/test-utils/src/events.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/dev/test-utils/src/events.ts b/packages/dev/test-utils/src/events.ts index fec45191067..91311ae5759 100644 --- a/packages/dev/test-utils/src/events.ts +++ b/packages/dev/test-utils/src/events.ts @@ -11,8 +11,6 @@ */ import {act, fireEvent} from '@testing-library/react'; -// eslint-disable-next-line monorepo/no-internal-import -import {DataTransfer, DragEvent} from '@react-aria/dnd/test/mocks'; import type {ITypeOpts} from '@testing-library/user-event'; import userEvent from '@testing-library/user-event'; From 8d108daf52184d9bcf3cc1b744dd20215d319bf1 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Fri, 19 Aug 2022 14:40:09 -0500 Subject: [PATCH 16/20] lint --- packages/dev/test-utils/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/dev/test-utils/package.json b/packages/dev/test-utils/package.json index 2c4d5d34893..8b45ee0c65d 100644 --- a/packages/dev/test-utils/package.json +++ b/packages/dev/test-utils/package.json @@ -33,7 +33,6 @@ }, "dependencies": { "@babel/runtime": "^7.6.2", - "@react-aria/dnd": "3.0.0-alpha.11", "@react-aria/ssr": "^3.0.0", "resolve": "^1.17.0" }, From d8541cbd099e94828dcd9308e2fd3feaf58fc293 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Fri, 19 Aug 2022 14:43:33 -0500 Subject: [PATCH 17/20] lint --- packages/dev/test-utils/src/events.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/dev/test-utils/src/events.ts b/packages/dev/test-utils/src/events.ts index 91311ae5759..e6faf8ca431 100644 --- a/packages/dev/test-utils/src/events.ts +++ b/packages/dev/test-utils/src/events.ts @@ -121,4 +121,3 @@ export function typeText(el: HTMLElement, value: string, opts?: ITypeOpts) { skipClick = true; } } - From b3666c7ce92bb7118efc532889ea618648909dd9 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Mon, 22 Aug 2022 10:40:46 -0500 Subject: [PATCH 18/20] update todos --- packages/@react-spectrum/list/test/ListViewDnd.test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/@react-spectrum/list/test/ListViewDnd.test.js b/packages/@react-spectrum/list/test/ListViewDnd.test.js index 3b1510dec18..4485c178ad9 100644 --- a/packages/@react-spectrum/list/test/ListViewDnd.test.js +++ b/packages/@react-spectrum/list/test/ListViewDnd.test.js @@ -474,6 +474,7 @@ describe('ListView', function () { expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Seven'); expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Eight'); + // TODO: Focused element should be the dragged item. It's currently showing the first list as focused. // expect(document.activeElement).toBe(list2rows[0]); }); @@ -530,6 +531,7 @@ describe('ListView', function () { expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Three'); expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Seven'); + // TODO: Focused element should be the dragged item. It's currently showing the first list as focused. // expect(document.activeElement).toBe(list2rows[0]); }); }); From ee3a07c22e3065ac900262705cf1475bb989ff5c Mon Sep 17 00:00:00 2001 From: Rob Snow Date: Tue, 23 Aug 2022 16:40:57 -0700 Subject: [PATCH 19/20] fix tests --- .../list/test/ListViewDnd.test.js | 77 +++++++++++-------- 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/packages/@react-spectrum/list/test/ListViewDnd.test.js b/packages/@react-spectrum/list/test/ListViewDnd.test.js index 4485c178ad9..102d86673cf 100644 --- a/packages/@react-spectrum/list/test/ListViewDnd.test.js +++ b/packages/@react-spectrum/list/test/ListViewDnd.test.js @@ -362,7 +362,7 @@ describe('ListView', function () { fireEvent(cell, new DragEvent('drag', {dataTransfer, clientX: 1, clientY: 110})); fireEvent(grid, new DragEvent('dragover', {dataTransfer, clientX: 1, clientY: 110})); fireEvent.pointerUp(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 1, clientY: 110}); - + fireEvent(grid, new DragEvent('drop', {dataTransfer, clientX: 1, clientY: 110})); act(() => jest.runAllTimers()); await act(async () => Promise.resolve()); @@ -431,7 +431,6 @@ describe('ListView', function () { let grid2 = getAllByRole('grid')[1]; let list1rows = within(grid1).getAllByRole('row'); let list2rows = within(grid2).getAllByRole('row'); - let cell = within(list1rows[0]).getByRole('gridcell'); expect(within(list1rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); expect(within(list1rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); @@ -442,24 +441,35 @@ describe('ListView', function () { expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Nine'); let dataTransfer = new DataTransfer(); - fireEvent.pointerDown(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); - fireEvent(cell, new DragEvent('dragstart', {dataTransfer, clientX: 0, clientY: 0})); + fireEvent.pointerDown(list1rows[0], {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + fireEvent(list1rows[0], new DragEvent('dragstart', {dataTransfer, clientX: 0, clientY: 0})); + fireEvent(list1rows[0], new DragEvent('drag', {dataTransfer, clientX: 0, clientY: 0})); + fireEvent(list1rows[0], new DragEvent('dragenter', {dataTransfer, clientX: 0, clientY: 0})); + fireEvent(grid2, new DragEvent('dragover', {dataTransfer, clientX: 0, clientY: 0})); expect(onDragStart).toHaveBeenCalledTimes(1); - fireEvent.pointerMove(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 200, clientY: -1}); - fireEvent(cell, new DragEvent('drag', {dataTransfer, clientX: 200, clientY: -1})); + fireEvent.pointerMove(list1rows[0], {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 200, clientY: -1}); + fireEvent(list1rows[0], new DragEvent('drag', {dataTransfer, clientX: 200, clientY: -1})); + fireEvent(list1rows[0], new DragEvent('dragenter', {dataTransfer, clientX: 200, clientY: -1})); + fireEvent(grid1, new DragEvent('dragleave', {dataTransfer, clientX: 200, clientY: -1})); fireEvent(grid2, new DragEvent('dragover', {dataTransfer, clientX: 200, clientY: -1})); - fireEvent.pointerUp(cell, {pointerType: 'mouse', button: 0, pointerId: 200, clientX: 1, clientY: -1}); + fireEvent.pointerUp(list1rows[0], {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 200, clientY: -1}); - fireEvent(cell, new DragEvent('dragend', {dataTransfer, clientX: 200, clientY: -1})); - expect(onDragEnd).toHaveBeenCalledTimes(1); - fireEvent(grid2, new DragEvent('drop', {dataTransfer, clientX: 200, clientY: -1})); - act(() => jest.runAllTimers()); + // purposefully run these two together inside one act so that the events line up as they do in the dom + // if they are in separate ones, then we create an render cycle in between + // if they are two separate acts + act(() => { + jest.advanceTimersToNextTimer(); + fireEvent(list1rows[0], new DragEvent('dragend', {dataTransfer, clientX: 200, clientY: -1})); + }); + // resolve async drop await act(async () => Promise.resolve()); - expect(onDrop).toHaveBeenCalledTimes(1); + // resolve focus movement + act(() => {jest.runAllTimers();}); - act(() => jest.runAllTimers()); + expect(onDragEnd).toHaveBeenCalledTimes(1); + expect(onDrop).toHaveBeenCalledTimes(1); grid1 = getAllByRole('grid')[0]; grid2 = getAllByRole('grid')[1]; @@ -474,8 +484,7 @@ describe('ListView', function () { expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Seven'); expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Eight'); - // TODO: Focused element should be the dragged item. It's currently showing the first list as focused. - // expect(document.activeElement).toBe(list2rows[0]); + expect(document.activeElement).toBe(list2rows[0]); }); it('should allow moving multiple items into another list', async function () { @@ -485,7 +494,6 @@ describe('ListView', function () { let grid2 = getAllByRole('grid')[1]; let list1rows = within(grid1).getAllByRole('row'); let list2rows = within(grid2).getAllByRole('row'); - let cell = within(list1rows[0]).getByRole('gridcell'); expect(within(list1rows[0]).getByRole('gridcell')).toHaveTextContent('Item One'); expect(within(list1rows[1]).getByRole('gridcell')).toHaveTextContent('Item Two'); @@ -499,24 +507,32 @@ describe('ListView', function () { act(() => userEvent.click(within(list1rows[2]).getByRole('checkbox'))); let dataTransfer = new DataTransfer(); - fireEvent.pointerDown(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); - fireEvent(cell, new DragEvent('dragstart', {dataTransfer, clientX: 0, clientY: 0})); + fireEvent.pointerDown(list1rows[0], {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); + fireEvent(list1rows[0], new DragEvent('dragstart', {dataTransfer, clientX: 0, clientY: 0})); + fireEvent(list1rows[0], new DragEvent('drag', {dataTransfer, clientX: 0, clientY: 0})); + fireEvent(list1rows[0], new DragEvent('dragenter', {dataTransfer, clientX: 0, clientY: 0})); + fireEvent(grid2, new DragEvent('dragover', {dataTransfer, clientX: 0, clientY: 0})); expect(onDragStart).toHaveBeenCalledTimes(1); - fireEvent.pointerMove(cell, {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 200, clientY: -1}); - fireEvent(cell, new DragEvent('drag', {dataTransfer, clientX: 200, clientY: -1})); + fireEvent.pointerMove(list1rows[0], {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 200, clientY: -1}); + fireEvent(list1rows[0], new DragEvent('drag', {dataTransfer, clientX: 200, clientY: -1})); + fireEvent(list1rows[0], new DragEvent('dragenter', {dataTransfer, clientX: 200, clientY: -1})); + fireEvent(grid1, new DragEvent('dragleave', {dataTransfer, clientX: 200, clientY: -1})); fireEvent(grid2, new DragEvent('dragover', {dataTransfer, clientX: 200, clientY: -1})); - fireEvent.pointerUp(cell, {pointerType: 'mouse', button: 0, pointerId: 200, clientX: 1, clientY: -1}); - - fireEvent(cell, new DragEvent('dragend', {dataTransfer, clientX: 200, clientY: -1})); - expect(onDragEnd).toHaveBeenCalledTimes(1); + fireEvent.pointerUp(list1rows[0], {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 200, clientY: -1}); fireEvent(grid2, new DragEvent('drop', {dataTransfer, clientX: 200, clientY: -1})); - act(() => jest.runAllTimers()); + // purposefully run these two together inside one act so that the events line up as they do in the dom + // if they are in separate ones, then we create an render cycle in between + // if they are two separate acts + act(() => { + jest.advanceTimersToNextTimer(); + fireEvent(list1rows[0], new DragEvent('dragend', {dataTransfer, clientX: 200, clientY: -1})); + }); + // resolve async drop await act(async () => Promise.resolve()); - expect(onDrop).toHaveBeenCalledTimes(1); - - act(() => jest.runAllTimers()); + // resolve focus movement + act(() => {jest.runAllTimers();}); grid1 = getAllByRole('grid')[0]; grid2 = getAllByRole('grid')[1]; @@ -531,8 +547,7 @@ describe('ListView', function () { expect(within(list2rows[1]).getByRole('gridcell')).toHaveTextContent('Item Three'); expect(within(list2rows[2]).getByRole('gridcell')).toHaveTextContent('Item Seven'); - // TODO: Focused element should be the dragged item. It's currently showing the first list as focused. - // expect(document.activeElement).toBe(list2rows[0]); + expect(document.activeElement).toBe(list2rows[0]); }); }); @@ -775,7 +790,7 @@ describe('ListView', function () { act(() => jest.runAllTimers()); userEvent.tab(); - + expect(document.activeElement).toHaveAttribute('aria-label', 'Insert before Item Seven'); fireEvent.keyDown(document.activeElement, {key: 'Enter'}); From 311246d7e4706ecf316502b18b19e4643b67d0d3 Mon Sep 17 00:00:00 2001 From: Rob Snow Date: Mon, 29 Aug 2022 16:22:40 -0700 Subject: [PATCH 20/20] fix 16 and 17 tests --- packages/@react-spectrum/list/test/ListViewDnd.test.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/@react-spectrum/list/test/ListViewDnd.test.js b/packages/@react-spectrum/list/test/ListViewDnd.test.js index 102d86673cf..0a114debb08 100644 --- a/packages/@react-spectrum/list/test/ListViewDnd.test.js +++ b/packages/@react-spectrum/list/test/ListViewDnd.test.js @@ -21,6 +21,7 @@ import React from 'react'; import {theme} from '@react-spectrum/theme-default'; import userEvent from '@testing-library/user-event'; +let isReact18 = parseInt(React.version, 10) >= 18; describe('ListView', function () { let offsetWidth, offsetHeight, scrollHeight; @@ -443,6 +444,7 @@ describe('ListView', function () { let dataTransfer = new DataTransfer(); fireEvent.pointerDown(list1rows[0], {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); fireEvent(list1rows[0], new DragEvent('dragstart', {dataTransfer, clientX: 0, clientY: 0})); + act(() => {jest.runAllTimers();}); fireEvent(list1rows[0], new DragEvent('drag', {dataTransfer, clientX: 0, clientY: 0})); fireEvent(list1rows[0], new DragEvent('dragenter', {dataTransfer, clientX: 0, clientY: 0})); fireEvent(grid2, new DragEvent('dragover', {dataTransfer, clientX: 0, clientY: 0})); @@ -457,7 +459,7 @@ describe('ListView', function () { fireEvent(grid2, new DragEvent('drop', {dataTransfer, clientX: 200, clientY: -1})); // purposefully run these two together inside one act so that the events line up as they do in the dom - // if they are in separate ones, then we create an render cycle in between + // if they are in separate ones, then we create a render cycle in between // if they are two separate acts act(() => { jest.advanceTimersToNextTimer(); @@ -509,6 +511,7 @@ describe('ListView', function () { let dataTransfer = new DataTransfer(); fireEvent.pointerDown(list1rows[0], {pointerType: 'mouse', button: 0, pointerId: 1, clientX: 0, clientY: 0}); fireEvent(list1rows[0], new DragEvent('dragstart', {dataTransfer, clientX: 0, clientY: 0})); + act(() => {jest.runAllTimers();}); fireEvent(list1rows[0], new DragEvent('drag', {dataTransfer, clientX: 0, clientY: 0})); fireEvent(list1rows[0], new DragEvent('dragenter', {dataTransfer, clientX: 0, clientY: 0})); fireEvent(grid2, new DragEvent('dragover', {dataTransfer, clientX: 0, clientY: 0})); @@ -529,6 +532,10 @@ describe('ListView', function () { jest.advanceTimersToNextTimer(); fireEvent(list1rows[0], new DragEvent('dragend', {dataTransfer, clientX: 200, clientY: -1})); }); + if (!isReact18) { + // not sure why but in React 17 this blur happens in the browser but not in the test + act(() => list1rows[0].blur()); + } // resolve async drop await act(async () => Promise.resolve()); // resolve focus movement