diff --git a/packages/react-aria-components/src/GridList.tsx b/packages/react-aria-components/src/GridList.tsx index 22ea560aef6..f78c9c574be 100644 --- a/packages/react-aria-components/src/GridList.tsx +++ b/packages/react-aria-components/src/GridList.tsx @@ -769,6 +769,7 @@ function GridListDropIndicator(props: GridListDropIndicatorProps, ref: Forwarded let renderProps = useRenderProps({ ...otherProps, defaultClassName: 'react-aria-DropIndicator', + defaultStyle: {position: 'relative'}, values: { isDropTarget } diff --git a/packages/react-aria-components/test/GridList.browser.test.tsx b/packages/react-aria-components/test/GridList.browser.test.tsx index a8f13c11242..4d5daa4c184 100644 --- a/packages/react-aria-components/test/GridList.browser.test.tsx +++ b/packages/react-aria-components/test/GridList.browser.test.tsx @@ -10,6 +10,8 @@ * governing permissions and limitations under the License. */ +import {Button} from '../src/Button'; +import {DropIndicator, useDragAndDrop} from '../src/useDragAndDrop'; import {expect, it} from 'vitest'; import {GridLayout} from '../src/GridLayout'; import {GridList, GridListItem} from '../src/GridList'; @@ -17,8 +19,36 @@ import React, {useState} from 'react'; import {render} from 'vitest-browser-react'; import {Size} from 'react-stately/useVirtualizerState'; import {User} from '@react-aria/test-utils'; +import {userEvent} from 'vitest/browser'; import {Virtualizer} from '../src/Virtualizer'; +const reorderableItems = Array.from({length: 10}, (_, i) => ({id: i, name: `Item ${i}`})); + +function ReorderableGridList() { + let {dragAndDropHooks} = useDragAndDrop({ + getItems: keys => [...keys].map(key => ({'text/plain': String(key)})), + onReorder: () => undefined, + renderDropIndicator: target => ( + + ) + }); + + return ( + + {item => ( + + + {item.name} + + )} + + ); +} + function Grid() { return ( { await button.click(); await expect(tester.getRows().length).toBeGreaterThan(0); }); + +it('scrolls focused drop indicators into view during keyboard reordering', async () => { + let {container} = await render(); + let gridlist = container.querySelector('[role=grid]') as HTMLElement; + let dragButton = container.querySelector('[aria-label="Drag Item 0"]') as HTMLElement; + dragButton.focus(); + + await userEvent.keyboard('{Enter}'); + + for (let i = 1; i <= 4; i++) { + await userEvent.keyboard('{ArrowDown}'); + let dropIndicator = document.activeElement as HTMLElement; + let indicatorRow = dropIndicator.closest('[role=row]') as HTMLElement; + let gridRect = gridlist.getBoundingClientRect(); + let indicatorRect = indicatorRow.getBoundingClientRect(); + + expect(dropIndicator).toHaveAttribute( + 'aria-label', + `Insert between Item ${i} and Item ${i + 1}` + ); + expect(dropIndicator).toHaveAttribute('role', 'button'); + expect(indicatorRow).toHaveStyle({ + backgroundColor: 'rgb(255, 0, 0)', + position: 'relative' + }); + expect(indicatorRect.top).toBeGreaterThanOrEqual(gridRect.top); + expect(indicatorRect.bottom).toBeLessThanOrEqual(gridRect.bottom); + } + + expect(gridlist.scrollTop).toBeGreaterThan(0); +});