diff --git a/packages/x-data-grid-pro/src/hooks/features/rowReorder/useGridRowReorder.tsx b/packages/x-data-grid-pro/src/hooks/features/rowReorder/useGridRowReorder.tsx index e6c4ed135c1d..866dd4e242a5 100644 --- a/packages/x-data-grid-pro/src/hooks/features/rowReorder/useGridRowReorder.tsx +++ b/packages/x-data-grid-pro/src/hooks/features/rowReorder/useGridRowReorder.tsx @@ -11,7 +11,10 @@ import { useGridApiOptionHandler, GridRowId, } from '@mui/x-data-grid'; -import { gridEditRowsStateSelector } from '@mui/x-data-grid/internals'; +import { + gridEditRowsStateSelector, + gridSortedRowIndexLookupSelector, +} from '@mui/x-data-grid/internals'; import { GridRowOrderChangeParams } from '../../../models/gridRowOrderChangeParams'; import { GridPrivateApiPro } from '../../../models/gridApiPro'; import { DataGridProProcessedProps } from '../../../models/dataGridProProps'; @@ -63,6 +66,7 @@ export const useGridRowReorder = ( const ownerState = { classes: props.classes }; const classes = useUtilityClasses(ownerState); const [dragRowId, setDragRowId] = React.useState(''); + const sortedRowIndexLookup = useGridSelector(apiRef, gridSortedRowIndexLookupSelector); React.useEffect(() => { return () => { @@ -91,17 +95,16 @@ export const useGridRowReorder = ( dragRowNode.current = event.currentTarget; dragRowNode.current.classList.add(classes.rowDragging); - setDragRowId(params.id); removeDnDStylesTimeout.current = setTimeout(() => { dragRowNode.current!.classList.remove(classes.rowDragging); }); - originRowIndex.current = apiRef.current.getRowIndexRelativeToVisibleRows(params.id); + originRowIndex.current = sortedRowIndexLookup[params.id]; apiRef.current.setCellFocus(params.id, GRID_REORDER_COL_DEF.field); }, - [isRowReorderDisabled, classes.rowDragging, logger, apiRef], + [apiRef, isRowReorderDisabled, logger, classes.rowDragging, sortedRowIndexLookup], ); const handleDragOver = React.useCallback>( @@ -127,7 +130,7 @@ export const useGridRowReorder = ( : event.clientY; if (params.id !== dragRowId) { - const targetRowIndex = apiRef.current.getRowIndexRelativeToVisibleRows(params.id); + const targetRowIndex = sortedRowIndexLookup[params.id]; const dragDirection = mouseMovementDiff > 0 ? Direction.DOWN : Direction.UP; const currentReorderState: ReorderStateProps = { @@ -149,7 +152,7 @@ export const useGridRowReorder = ( previousMousePosition = { x: event.clientX, y: event.clientY }; }, - [apiRef, logger, dragRowId], + [dragRowId, apiRef, logger, sortedRowIndexLookup], ); const handleDragEnd = React.useCallback>( @@ -179,7 +182,7 @@ export const useGridRowReorder = ( // Emit the rowOrderChange event only once when the reordering stops. const rowOrderChangeParams: GridRowOrderChangeParams = { row: apiRef.current.getRow(dragRowId)!, - targetIndex: apiRef.current.getRowIndexRelativeToVisibleRows(params.id), + targetIndex: sortedRowIndexLookup[params.id], oldIndex: originRowIndex.current!, }; @@ -188,7 +191,7 @@ export const useGridRowReorder = ( setDragRowId(''); }, - [isRowReorderDisabled, logger, apiRef, dragRowId], + [apiRef, dragRowId, isRowReorderDisabled, logger, sortedRowIndexLookup], ); useGridApiEventHandler(apiRef, 'rowDragStart', handleDragStart); diff --git a/packages/x-data-grid-pro/src/tests/rowReorder.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/rowReorder.DataGridPro.test.tsx index 20c3b1ab0681..83d587752c50 100644 --- a/packages/x-data-grid-pro/src/tests/rowReorder.DataGridPro.test.tsx +++ b/packages/x-data-grid-pro/src/tests/rowReorder.DataGridPro.test.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { createRenderer, fireEvent, createEvent } from '@mui/internal-test-utils'; -import { getCell, getRowsFieldContent } from 'test/utils/helperFn'; +import { createRenderer, fireEvent, screen, createEvent } from '@mui/internal-test-utils'; +import { getCell, getColumnValues, getRowsFieldContent } from 'test/utils/helperFn'; import { useGridApiRef, DataGridPro, gridClasses, GridApi } from '@mui/x-data-grid-pro'; import { useBasicDemoData } from '@mui/x-data-grid-generator'; @@ -200,4 +200,50 @@ describe(' - Row reorder', () => { expect(handleDragOver.callCount).to.equal(0); expect(handleDragEnd.callCount).to.equal(0); }); + + it('should reorder rows correctly on any page when pagination is enabled', () => { + let apiRef: React.MutableRefObject; + const rows = [ + { id: 0, brand: 'Nike' }, + { id: 1, brand: 'Adidas' }, + { id: 2, brand: 'Puma' }, + { id: 3, brand: 'Skechers' }, + ]; + const columns = [{ field: 'brand' }]; + + function Test() { + apiRef = useGridApiRef(); + + return ( +
+ +
+ ); + } + + render(); + fireEvent.click(screen.getByRole('button', { name: /next page/i })); + expect(getColumnValues(0)).to.deep.equal(['2', '3']); + expect(getRowsFieldContent('brand')).to.deep.equal(['Puma', 'Skechers']); + const rowReorderCell = getCell(2, 0).firstChild!; + const targetCell = getCell(3, 0); + + fireEvent.dragStart(rowReorderCell); + fireEvent.dragEnter(targetCell); + const dragOverEvent = createDragOverEvent(targetCell); + fireEvent(targetCell, dragOverEvent); + expect(getRowsFieldContent('brand')).to.deep.equal(['Skechers', 'Puma']); + }); }); diff --git a/packages/x-data-grid/src/hooks/features/sorting/gridSortingSelector.ts b/packages/x-data-grid/src/hooks/features/sorting/gridSortingSelector.ts index 70c9993cac46..1022a5ee6f2c 100644 --- a/packages/x-data-grid/src/hooks/features/sorting/gridSortingSelector.ts +++ b/packages/x-data-grid/src/hooks/features/sorting/gridSortingSelector.ts @@ -3,7 +3,7 @@ import { GridSortDirection, GridSortModel } from '../../../models/gridSortModel' import { gridRowTreeSelector, gridRowsLookupSelector } from '../rows/gridRowsSelector'; import { GRID_ID_AUTOGENERATED, isAutogeneratedRowNode } from '../rows/gridRowsUtils'; import type { GridStateCommunity } from '../../../models/gridStateCommunity'; -import type { GridValidRowModel, GridRowEntry } from '../../../models/gridRows'; +import type { GridValidRowModel, GridRowEntry, GridRowId } from '../../../models/gridRows'; /** * @category Sorting @@ -73,3 +73,17 @@ export const gridSortColumnLookupSelector = createSelectorMemoized( return result; }, ); + +/** + * @category Sorting + * @ignore - do not document. + */ +export const gridSortedRowIndexLookupSelector = createSelectorMemoized( + gridSortedRowIdsSelector, + (sortedIds) => { + return sortedIds.reduce>((acc, id, index) => { + acc[id] = index; + return acc; + }, Object.create(null)); + }, +); diff --git a/packages/x-data-grid/src/hooks/features/sorting/index.ts b/packages/x-data-grid/src/hooks/features/sorting/index.ts index 4a13963b493a..8b879c541b03 100644 --- a/packages/x-data-grid/src/hooks/features/sorting/index.ts +++ b/packages/x-data-grid/src/hooks/features/sorting/index.ts @@ -1,4 +1,10 @@ -export * from './gridSortingSelector'; +export { + gridSortedRowIdsSelector, + gridSortedRowEntriesSelector, + gridSortModelSelector, + gridSortColumnLookupSelector, +} from './gridSortingSelector'; +export type { GridSortColumnLookup } from './gridSortingSelector'; export type { GridSortingState, GridSortingInitialState } from './gridSortingState'; export { gridDateComparator, diff --git a/packages/x-data-grid/src/internals/index.ts b/packages/x-data-grid/src/internals/index.ts index 41f12e166367..8db2e88acaf8 100644 --- a/packages/x-data-grid/src/internals/index.ts +++ b/packages/x-data-grid/src/internals/index.ts @@ -116,6 +116,7 @@ export { export { useGridRowSelectionPreProcessors } from '../hooks/features/rowSelection/useGridRowSelectionPreProcessors'; export { useGridSorting, sortingStateInitializer } from '../hooks/features/sorting/useGridSorting'; export type { GridSortingModelApplier } from '../hooks/features/sorting/gridSortingState'; +export { gridSortedRowIndexLookupSelector } from '../hooks/features/sorting/gridSortingSelector'; export { useGridScroll } from '../hooks/features/scroll/useGridScroll'; export { useGridEvents } from '../hooks/features/events/useGridEvents'; export {