Skip to content

Commit

Permalink
[DataGrid] Refactor row state propagation (#15627)
Browse files Browse the repository at this point in the history
Co-authored-by: Rom Grk <[email protected]>
Co-authored-by: Andrew Cherniavskii <[email protected]>
  • Loading branch information
3 people authored Jan 24, 2025
1 parent 22f4ca7 commit 2a99d9c
Show file tree
Hide file tree
Showing 40 changed files with 398 additions and 282 deletions.
6 changes: 1 addition & 5 deletions docs/data/data-grid/master-detail/FullWidthDetailPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,7 @@ import {
} from '@mui/x-data-grid-generator';

const getDetailPanelWidth = (gridDimensions) => {
return (
gridDimensions.viewportInnerSize.width +
gridDimensions.leftPinnedWidth +
gridDimensions.rightPinnedWidth
);
return gridDimensions.viewportInnerSize.width;
};

function DetailPanelContent({ row: rowProp }) {
Expand Down
6 changes: 1 addition & 5 deletions docs/data/data-grid/master-detail/FullWidthDetailPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,7 @@ import {
} from '@mui/x-data-grid-generator';

const getDetailPanelWidth = (gridDimensions: GridDimensions) => {
return (
gridDimensions.viewportInnerSize.width +
gridDimensions.leftPinnedWidth +
gridDimensions.rightPinnedWidth
);
return gridDimensions.viewportInnerSize.width;
};

function DetailPanelContent({ row: rowProp }: { row: Customer }) {
Expand Down
14 changes: 13 additions & 1 deletion docs/pages/x/api/data-grid/selectors.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,12 @@
"description": "",
"supportsApiRef": false
},
{
"name": "gridEditCellStateSelector",
"returnType": "GridEditCellProps<any>",
"description": "",
"supportsApiRef": true
},
{
"name": "gridEditRowsStateSelector",
"returnType": "GridEditingState",
Expand Down Expand Up @@ -403,6 +409,12 @@
"description": "",
"supportsApiRef": true
},
{
"name": "gridRowIsEditingSelector",
"returnType": "boolean",
"description": "",
"supportsApiRef": true
},
{
"name": "gridRowMaximumTreeDepthSelector",
"returnType": "number",
Expand Down Expand Up @@ -554,7 +566,7 @@
},
{
"name": "gridVisibleRowsSelector",
"returnType": "{ rows: GridRowEntry<GridValidRowModel>[]; range: { firstRowIndex: number; lastRowIndex: number } | null; rowToIndexMap: Map<GridValidRowModel, number> }",
"returnType": "{ rows: GridRowEntry<GridValidRowModel>[]; range: { firstRowIndex: number; lastRowIndex: number } | null; rowIdToIndexMap: Map<GridRowId, number> }",
"category": "Pagination",
"description": "Get the rows, range and rowIndex lookup map after filtering and sorting.\nDoes not contain the collapsed children.",
"supportsApiRef": true
Expand Down
2 changes: 1 addition & 1 deletion docs/translations/api-docs/data-grid/grid-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"getAllGroupDetails": { "description": "Returns the column group lookup." },
"getAllRowIds": { "description": "Gets the list of row ids." },
"getCellElement": {
"description": "Gets the underlying DOM element for a cell at the given <code>id</code> and <code>field</code>."
"description": "Gets the <a href=\"/x/api/data-grid/grid-cell-params/\">GridCellParams</a> object that is passed as argument in events."
},
"getCellMode": { "description": "Gets the mode of a cell." },
"getCellParams": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ export const useGridAggregationPreProcessors = (

rulesOnLastColumnHydration.current = aggregationRules;

apiRef.current.caches.aggregation.rulesOnLastColumnHydration = aggregationRules;

return columnsState;
},
[apiRef, props.aggregationFunctions, props.disableAggregation, props.unstable_dataSource],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,18 @@ describe('<DataGridPremium /> - Data source aggregation', () => {

it('should show aggregation option in the column menu', async () => {
const { user } = render(<TestDataSourceAggregation />);
await waitFor(() => {
expect(getRowsSpy.callCount).to.be.greaterThan(0);
});
await user.click(within(getColumnHeaderCell(0)).getByLabelText('Menu'));
expect(screen.queryByLabelText('Aggregation')).not.to.equal(null);
});

it('should not show aggregation option in the column menu when no aggregation function is defined', async () => {
const { user } = render(<TestDataSourceAggregation aggregationFunctions={{}} />);
await waitFor(() => {
expect(getRowsSpy.callCount).to.be.greaterThan(0);
});
await user.click(within(getColumnHeaderCell(0)).getByLabelText('Menu'));
expect(screen.queryByLabelText('Aggregation')).to.equal(null);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import composeClasses from '@mui/utils/composeClasses';
import { getDataGridUtilityClass, useGridSelector, GridRenderCellParams } from '@mui/x-data-grid';
import {
getDataGridUtilityClass,
useGridSelector,
GridRenderCellParams,
GridRowId,
} from '@mui/x-data-grid';
import { createSelector } from '@mui/x-data-grid/internals';
import { useGridRootProps } from '../hooks/utils/useGridRootProps';
import { useGridApiContext } from '../hooks/utils/useGridApiContext';
import { DataGridProProcessedProps } from '../models/dataGridProProps';
import { gridDetailPanelExpandedRowsContentCacheSelector } from '../hooks/features/detailPanel/gridDetailPanelSelector';
import {
gridDetailPanelExpandedRowIdsSelector,
gridDetailPanelExpandedRowsContentCacheSelector,
} from '../hooks/features/detailPanel/gridDetailPanelSelector';
import { GridApiPro } from '../models';

type OwnerState = { classes: DataGridProProcessedProps['classes']; isExpanded: boolean };

Expand All @@ -19,8 +29,17 @@ const useUtilityClasses = (ownerState: OwnerState) => {
return composeClasses(slots, getDataGridUtilityClass, classes);
};

const isExpandedSelector = createSelector(
gridDetailPanelExpandedRowIdsSelector,
(expandedRowIds, rowId: GridRowId) => {
return expandedRowIds.has(rowId);
},
);

function GridDetailPanelToggleCell(props: GridRenderCellParams) {
const { id, value: isExpanded } = props;
const { id, row, api } = props;
const rowId = api.getRowId(row);
const isExpanded = useGridSelector({ current: api as GridApiPro }, isExpandedSelector, rowId);

const rootProps = useGridRootProps();
const apiRef = useGridApiContext();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ export const useGridColumnPinning = (
const setPinnedColumns = React.useCallback<GridColumnPinningApi['setPinnedColumns']>(
(newPinnedColumns) => {
setState(apiRef, newPinnedColumns);
apiRef.current.forceUpdate();
apiRef.current.requestPipeProcessorsApplication('hydrateColumns');
},
[apiRef],
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import * as React from 'react';
import { RefObject } from '@mui/x-internals/types';
import {
GridPinnedColumnFields,
GridPipeProcessor,
gridPinnedColumnsSelector,
useGridRegisterPipeProcessor,
eslintUseValue,
gridVisiblePinnedColumnDefinitionsSelector,
} from '@mui/x-data-grid/internals';
import { DataGridProProcessedProps } from '../../../models/dataGridProProps';
Expand All @@ -17,19 +14,10 @@ export const useGridColumnPinningPreProcessors = (
) => {
const { disableColumnPinning } = props;

let pinnedColumns: GridPinnedColumnFields | null;
if (apiRef.current.state.columns) {
pinnedColumns = gridPinnedColumnsSelector(apiRef.current.state);
} else {
pinnedColumns = null;
}

const prevAllPinnedColumns = React.useRef<string[]>([]);

const reorderPinnedColumns = React.useCallback<GridPipeProcessor<'hydrateColumns'>>(
(columnsState) => {
eslintUseValue(pinnedColumns);

if (columnsState.orderedFields.length === 0 || disableColumnPinning) {
return columnsState;
}
Expand Down Expand Up @@ -132,7 +120,7 @@ export const useGridColumnPinningPreProcessors = (
orderedFields: [...leftPinnedColumns, ...centerColumns, ...rightPinnedColumns],
};
},
[apiRef, disableColumnPinning, pinnedColumns],
[apiRef, disableColumnPinning],
);

useGridRegisterPipeProcessor(apiRef, 'hydrateColumns', reorderPinnedColumns);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,13 +235,13 @@ export const useGridDataSourceLazyLoader = (
const newLoadingTrigger =
rowCount === -1 ? LoadingTrigger.SCROLL_END : LoadingTrigger.VIEWPORT;

if (loadingTrigger.current !== newLoadingTrigger) {
loadingTrigger.current = newLoadingTrigger;
}

if (loadingTrigger.current !== null) {
ensureValidRowCount(loadingTrigger.current, newLoadingTrigger);
}

if (loadingTrigger.current !== newLoadingTrigger) {
loadingTrigger.current = newLoadingTrigger;
}
},
[ensureValidRowCount],
);
Expand Down
50 changes: 33 additions & 17 deletions packages/x-data-grid/src/components/GridRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import clsx from 'clsx';
import { unstable_useForkRef as useForkRef } from '@mui/utils';
import { fastMemo } from '@mui/x-internals/fastMemo';
import { forwardRef } from '@mui/x-internals/forwardRef';
import { isObjectEmpty } from '@mui/x-internals/isObjectEmpty';
import { GridRowEventLookup } from '../models/events';
import { GridRowId, GridRowModel } from '../models/gridRows';
import { GridEditModes, GridRowModes, GridCellModes } from '../models/gridEditRowModel';
import { GridEditModes, GridCellModes } from '../models/gridEditRowModel';
import { gridClasses } from '../constants/gridClasses';
import { composeGridClasses } from '../utils/composeGridClasses';
import { useGridRootProps } from '../hooks/utils/useGridRootProps';
Expand All @@ -24,11 +25,26 @@ import { GRID_ACTIONS_COLUMN_TYPE } from '../colDef/gridActionsColDef';
import { GRID_DETAIL_PANEL_TOGGLE_FIELD, PinnedColumnPosition } from '../internals/constants';
import { gridSortModelSelector } from '../hooks/features/sorting/gridSortingSelector';
import { gridRowMaximumTreeDepthSelector } from '../hooks/features/rows/gridRowsSelector';
import { gridEditRowsStateSelector } from '../hooks/features/editing/gridEditingSelectors';
import {
gridEditRowsStateSelector,
gridRowIsEditingSelector,
} from '../hooks/features/editing/gridEditingSelectors';
import { GridScrollbarFillerCell as ScrollbarFiller } from './GridScrollbarFillerCell';
import { getPinnedCellOffset } from '../internals/utils/getPinnedCellOffset';
import { useGridConfiguration } from '../hooks/utils/useGridConfiguration';
import { useGridPrivateApiContext } from '../hooks/utils/useGridPrivateApiContext';
import { createSelector } from '../utils/createSelector';

const isRowReorderingEnabledSelector = createSelector(
gridEditRowsStateSelector,
(editRows, rowReordering: boolean) => {
if (!rowReordering) {
return false;
}
const isEditingRows = !isObjectEmpty(editRows);
return !isEditingRows;
},
);

export interface GridRowProps extends React.HTMLAttributes<HTMLDivElement> {
row: GridRowModel;
Expand Down Expand Up @@ -104,10 +120,15 @@ const GridRow = forwardRef<HTMLDivElement, GridRowProps>(function GridRow(props,
const sortModel = useGridSelector(apiRef, gridSortModelSelector);
const treeDepth = useGridSelector(apiRef, gridRowMaximumTreeDepthSelector);
const columnPositions = useGridSelector(apiRef, gridColumnPositionsSelector);
const editRowsState = useGridSelector(apiRef, gridEditRowsStateSelector);
const rowReordering = (rootProps as any).rowReordering as boolean;
const isRowReorderingEnabled = useGridSelector(
apiRef,
isRowReorderingEnabledSelector,
rowReordering,
);
const handleRef = useForkRef(ref, refProp);
const rowNode = apiRef.current.getRowNode(rowId);
const editing = apiRef.current.getRowMode(rowId) === GridRowModes.Edit;
const editing = useGridSelector(apiRef, gridRowIsEditingSelector, rowId);
const editable = rootProps.editMode === GridEditModes.Row;
const hasFocusCell = focusedColumnIndex !== undefined;
const hasVirtualFocusCellLeft =
Expand Down Expand Up @@ -216,8 +237,6 @@ const GridRow = forwardRef<HTMLDivElement, GridRowProps>(function GridRow(props,

const { slots, slotProps, disableColumnReorder } = rootProps;

const rowReordering = (rootProps as any).rowReordering as boolean;

const heightEntry = useGridSelector(
apiRef,
() => ({ ...apiRef.current.getRowHeightEntry(rowId) }),
Expand Down Expand Up @@ -275,6 +294,11 @@ const GridRow = forwardRef<HTMLDivElement, GridRowProps>(function GridRow(props,
rowClassNames.push(rootProps.getRowClassName(rowParams));
}

/* Start of rendering */
if (!rowNode) {
return null;
}

const getCell = (
column: GridStateColDef,
indexInSection: number,
Expand Down Expand Up @@ -316,15 +340,12 @@ const GridRow = forwardRef<HTMLDivElement, GridRowProps>(function GridRow(props,
);
}

const editCellState = editRowsState[rowId]?.[column.field] ?? null;

// when the cell is a reorder cell we are not allowing to reorder the col
// fixes https://github.com/mui/mui-x/issues/11126
const isReorderCell = column.field === '__reorder__';
const isEditingRows = Object.keys(editRowsState).length > 0;

const canReorderColumn = !(disableColumnReorder || column.disableReorder);
const canReorderRow = rowReordering && !sortModel.length && treeDepth <= 1 && !isEditingRows;
const canReorderRow = isRowReorderingEnabled && !sortModel.length && treeDepth <= 1;

const disableDragEvents = !(canReorderColumn || (isReorderCell && canReorderRow));

Expand All @@ -349,23 +370,18 @@ const GridRow = forwardRef<HTMLDivElement, GridRowProps>(function GridRow(props,
colIndex={indexRelativeToAllColumns}
colSpan={colSpan}
disableDragEvents={disableDragEvents}
editCellState={editCellState}
isNotVisible={cellIsNotVisible}
pinnedOffset={pinnedOffset}
pinnedPosition={pinnedPosition}
showLeftBorder={showLeftBorder}
showRightBorder={showRightBorder}
row={row}
rowNode={rowNode}
{...slotProps?.cell}
/>
);
};

/* Start of rendering */

if (!rowNode) {
return null;
}

const leftCells = pinnedColumns.left.map((column, i) => {
const indexRelativeToAllColumns = i;
return getCell(
Expand Down
Loading

0 comments on commit 2a99d9c

Please sign in to comment.