From 50c185911e01ac5ddedf13a85872037c0f31f21e Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 23 Aug 2021 12:21:26 +0200 Subject: [PATCH 001/390] [core] Remove the options (final step) --- .../grid/_modules_/grid/GridComponentProps.ts | 512 ++++++++++++++- .../_modules_/grid/components/GridFooter.tsx | 6 +- .../_modules_/grid/components/GridHeader.tsx | 14 +- .../_modules_/grid/components/GridRow.tsx | 11 +- .../grid/components/GridViewport.tsx | 6 +- .../grid/components/base/GridErrorHandler.tsx | 2 +- .../components/base/GridFooterPlaceholder.tsx | 2 +- .../components/base/GridHeaderPlaceholder.tsx | 2 +- .../grid/components/base/GridOverlays.tsx | 12 +- .../grid/components/cell/GridBooleanCell.tsx | 8 +- .../columnHeaders/ColumnHeaderFilterIcon.tsx | 9 +- .../columnHeaders/ColumnHeaderMenuIcon.tsx | 5 +- .../columnHeaders/GridColumnHeaderItem.tsx | 29 +- .../GridColumnHeaderSeparator.tsx | 5 +- .../GridColumnHeaderSortIcon.tsx | 6 +- .../GridColumnHeadersItemCollection.tsx | 3 - .../columnHeaders/GridColumnUnsortedIcon.tsx | 6 +- .../GridCellCheckboxRenderer.tsx | 6 +- .../columnSelection/GridHeaderCheckbox.tsx | 6 +- .../grid/components/containers/GridRoot.tsx | 4 +- .../components/panel/GridPreferencesPanel.tsx | 13 +- .../toolbar/GridToolbarColumnsButton.tsx | 3 +- .../toolbar/GridToolbarDensitySelector.tsx | 6 +- .../components/toolbar/GridToolbarExport.tsx | 5 +- .../toolbar/GridToolbarFilterButton.tsx | 3 +- .../grid/hooks/features/core/gridState.ts | 3 - .../_modules_/grid/hooks/features/index.ts | 1 - .../features/localeText/useLocaleText.tsx | 15 +- .../features/selection/useGridSelection.ts | 4 +- .../features/useGridSlotComponentProps.tsx | 8 +- .../grid/hooks/utils/optionsSelector.ts | 3 - .../grid/hooks/utils/useOptionsProp.ts | 62 -- .../useProcessedProps.ts} | 144 +++-- .../grid/_modules_/grid/models/api/gridApi.ts | 2 - .../grid/models/api/gridComponentsApi.ts | 74 --- .../grid/_modules_/grid/models/api/index.ts | 1 - .../grid/_modules_/grid/models/gridClasses.ts | 8 +- .../grid/models/gridIconSlotsComponent.ts | 28 +- .../_modules_/grid/models/gridOptions.tsx | 606 +++--------------- .../grid/models/gridSlotsComponent.ts | 34 +- .../models/params/gridSlotComponentProps.ts | 7 +- .../grid/utils/getGridLocalization.ts | 6 +- packages/grid/data-grid/src/DataGrid.tsx | 3 - packages/grid/data-grid/src/DataGridProps.ts | 4 +- .../data-grid/src/useDataGridComponent.tsx | 6 +- .../grid/data-grid/src/useDataGridProps.ts | 10 +- packages/grid/x-grid/src/DataGridPro.tsx | 4 - packages/grid/x-grid/src/DataGridProProps.ts | 4 +- .../x-grid/src/useDataGridProComponent.tsx | 6 +- .../grid/x-grid/src/useDataGridProProps.ts | 10 +- .../storybook/src/components/feed-grid.tsx | 5 +- .../storybook/src/components/pricing-grid.tsx | 5 +- .../src/stories/grid-data.stories.tsx | 11 +- .../src/stories/grid-events.stories.tsx | 132 ++-- .../src/stories/grid-selection.stories.tsx | 19 +- .../src/stories/grid-streaming.stories.tsx | 28 +- .../src/stories/grid-style.stories.tsx | 24 +- 57 files changed, 936 insertions(+), 1025 deletions(-) delete mode 100644 packages/grid/_modules_/grid/hooks/utils/optionsSelector.ts delete mode 100644 packages/grid/_modules_/grid/hooks/utils/useOptionsProp.ts rename packages/grid/_modules_/grid/hooks/{features/useGridComponents.tsx => utils/useProcessedProps.ts} (50%) delete mode 100644 packages/grid/_modules_/grid/models/api/gridComponentsApi.ts diff --git a/packages/grid/_modules_/grid/GridComponentProps.ts b/packages/grid/_modules_/grid/GridComponentProps.ts index 7ca43da0b09d5..3ec2679435d6d 100644 --- a/packages/grid/_modules_/grid/GridComponentProps.ts +++ b/packages/grid/_modules_/grid/GridComponentProps.ts @@ -1,24 +1,500 @@ +import * as React from 'react'; import { GridState } from './hooks/features/core/gridState'; import { GridApiRef } from './models/api/gridApiRef'; import { GridColumns } from './models/colDef/gridColDef'; -import { GridSlotsComponent } from './models/gridSlotsComponent'; -import { GridOptions, MuiEvent } from './models/gridOptions'; +import { + MuiEvent, + GridSimpleOptions, + GridProcessedMergedOptions, + GridMergedOptions, +} from './models/gridOptions'; +import { GridRowId, GridRowIdGetter, GridRowsProp } from './models/gridRows'; +import { ElementSize } from './models/elementSize'; +import { GridColumnTypesRecord } from './models/colDef/gridColTypeDef'; +import { GridSortModel } from './models/gridSortModel'; +import { GridFilterModel } from './models/gridFilterModel'; +import { GridCellParams } from './models/params/gridCellParams'; +import { GridColumnHeaderParams } from './models/params/gridColumnHeaderParams'; +import { GridEditRowsModel } from './models/gridEditRowModel'; +import { GridSelectionModel, GridInputSelectionModel } from './models/gridSelectionModel'; +import { + GridEditCellPropsParams, + GridEditCellValueParams, + GridCellEditCommitParams, +} from './models/params/gridEditCellParams'; +import { GridRowScrollEndParams } from './models/params/gridRowScrollEndParams'; +import { GridRowParams } from './models/params/gridRowParams'; +import { GridColumnOrderChangeParams } from './models/params/gridColumnOrderChangeParams'; +import { GridColumnResizeParams } from './models/params/gridColumnResizeParams'; +import { GridColumnVisibilityChangeParams } from './models/params/gridColumnVisibilityChangeParams'; +import { GridViewportRowsChangeParams } from './models/params/gridViewportRowsChangeParams'; import { GridSlotsComponentsProps } from './models/gridSlotsComponentsProps'; -import { GridRowIdGetter, GridRowsProp } from './models/gridRows'; /** - * Partial set of [[GridOptions]]. + * The grid component react props before applying the default values. */ -export type GridOptionsProp = Partial; +export interface GridInputComponentProps + extends Partial, + Partial, + GridComponentOtherProps {} /** - * The grid component react props interface. + * The grid component react props after applying the default values. */ -export interface GridComponentProps extends GridOptionsProp { +export interface GridComponentProps + extends GridSimpleOptions, + GridProcessedMergedOptions, + GridComponentOtherProps {} + +interface GridComponentOtherProps { /** * The ref object that allows grid manipulation. Can be instantiated with [[useGridApiRef()]]. */ apiRef?: GridApiRef; + /** + * Signal to the underlying logic what version of the public component API + * of the data grid is exposed [[GridSignature]]. + * @internal + */ + signature?: string; + /** + * Extend native column types with your new column types. + */ + columnTypes?: GridColumnTypesRecord; + /** + * Set the total number of rows, if it is different than the length of the value `rows` prop. + */ + rowCount?: number; + /** + * Override the height/width of the grid inner scrollbar. + */ + scrollbarSize?: number; + /** + * Function that applies CSS classes dynamically on cells. + * @param {GridCellParams} params With all properties from [[GridCellParams]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + getCellClassName?: (params: GridCellParams, details?: any) => string; + /** + * Function that applies CSS classes dynamically on rows. + * @param {GridRowParams} params With all properties from [[GridRowParams]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + getRowClassName?: (params: GridRowParams, details?: any) => string; + /** + * Callback fired when a cell is rendered, returns true if the cell is editable. + * @param {GridCellParams} params With all properties from [[GridCellParams]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + isCellEditable?: (params: GridCellParams, details?: any) => boolean; + /** + * Determines if a row can be selected. + * @param {GridRowParams} params With all properties from [[GridRowParams]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + isRowSelectable?: (params: GridRowParams, details?: any) => boolean; + /** + * Callback fired when the edit cell value changes. + * @param {GridEditCellPropsParams} params With all properties from [[GridEditCellPropsParams]]. + * @param {MuiEvent} event The event that caused this prop to be called. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onEditCellPropsChange?: ( + params: GridEditCellPropsParams, + event: MuiEvent, + details?: any, + ) => void; + /** + * Callback fired when the cell changes are committed. + * @param {GridCellEditCommitParams} params With all properties from [[GridCellEditCommitParams]]. + * @param {MuiEvent} event The event that caused this prop to be called. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onCellEditCommit?: ( + params: GridCellEditCommitParams, + event: MuiEvent, + details?: any, + ) => void; + /** + * Callback fired when the cell turns to edit mode. + * @param {GridCellParams} params With all properties from [[GridCellParams]]. + * @param {MuiEvent} event The event that caused this prop to be called. + */ + onCellEditStart?: (params: GridCellParams, event: MuiEvent) => void; + /** + * Callback fired when the cell turns to view mode. + * @param {GridCellParams} params With all properties from [[GridCellParams]]. + * @param {MuiEvent} event The event that caused this prop to be called. + */ + onCellEditStop?: (params: GridCellParams, event: MuiEvent) => void; + /** + * Callback fired when the row changes are committed. + * @param {GridRowId} id The row id. + * @param {MuiEvent} event The event that caused this prop to be called. + */ + onRowEditCommit?: (id: GridRowId, event: MuiEvent) => void; + /** + * Callback fired when the row turns to edit mode. + * @param {GridRowParams} params With all properties from [[GridRowParams]]. + * @param {MuiEvent} event The event that caused this prop to be called. + */ + onRowEditStart?: (params: GridRowParams, event: MuiEvent) => void; + /** + * Callback fired when the row turns to view mode. + * @param {GridRowParams} params With all properties from [[GridRowParams]]. + * @param {MuiEvent} event The event that caused this prop to be called. + */ + onRowEditStop?: (params: GridRowParams, event: MuiEvent) => void; + /** + * Callback fired when an exception is thrown in the grid, or when the `showError` API method is called. + * @param args + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onError?: (args: any, details?: any) => void; + /** + * Callback fired when the active element leaves a cell. + * @param params With all properties from [[GridCellParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onCellBlur?: ( + params: GridCellParams, + event: MuiEvent, + details?: any, + ) => void; + /** + * Callback fired when a click event comes from a cell element. + * @param params With all properties from [[GridCellParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onCellClick?: (params: GridCellParams, event: MuiEvent, details?: any) => void; + /** + * Callback fired when a double click event comes from a cell element. + * @param params With all properties from [[GridCellParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onCellDoubleClick?: ( + params: GridCellParams, + event: MuiEvent, + details?: any, + ) => void; + /** + * Callback fired when a cell loses focus. + * @param params With all properties from [[GridCellParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onCellFocusOut?: ( + params: GridCellParams, + event: MuiEvent, + details?: any, + ) => void; + /** + * Callback fired when a keydown event comes from a cell element. + * @param params With all properties from [[GridCellParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onCellKeyDown?: ( + params: GridCellParams, + event: MuiEvent, + details?: any, + ) => void; + /** + * Callback fired when a mouseover event comes from a cell element. + * @param params With all properties from [[GridCellParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onCellOver?: (params: GridCellParams, event: MuiEvent, details?: any) => void; + /** + * Callback fired when a mouseout event comes from a cell element. + * @param params With all properties from [[GridCellParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onCellOut?: (params: GridCellParams, event: MuiEvent, details?: any) => void; + /** + * Callback fired when a mouse enter event comes from a cell element. + * @param params With all properties from [[GridCellParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onCellEnter?: (params: GridCellParams, event: MuiEvent, details?: any) => void; + /** + * Callback fired when a mouse leave event comes from a cell element. + * @param params With all properties from [[GridCellParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onCellLeave?: (params: GridCellParams, event: MuiEvent, details?: any) => void; + /** + * Callback fired when the cell value changed. + * @param params With all properties from [[GridEditCellValueParams]]. + * @param event [[MuiEvent<{}>]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onCellValueChange?: (params: GridEditCellValueParams, event: MuiEvent<{}>, details?: any) => void; + /** + * Callback fired when a click event comes from a column header element. + * @param params With all properties from [[GridColumnHeaderParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onColumnHeaderClick?: ( + params: GridColumnHeaderParams, + event: MuiEvent, + details?: any, + ) => void; + /** + * Callback fired when a double click event comes from a column header element. + * @param params With all properties from [[GridColumnHeaderParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onColumnHeaderDoubleClick?: ( + params: GridColumnHeaderParams, + event: MuiEvent, + details?: any, + ) => void; + /** + * Callback fired when a mouseover event comes from a column header element. + * @param params With all properties from [[GridColumnHeaderParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onColumnHeaderOver?: ( + params: GridColumnHeaderParams, + event: MuiEvent, + details?: any, + ) => void; + /** + * Callback fired when a mouseout event comes from a column header element. + * @param params With all properties from [[GridColumnHeaderParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onColumnHeaderOut?: ( + params: GridColumnHeaderParams, + event: MuiEvent, + details?: any, + ) => void; + /** + * Callback fired when a mouse enter event comes from a column header element. + * @param params With all properties from [[GridColumnHeaderParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onColumnHeaderEnter?: ( + params: GridColumnHeaderParams, + event: MuiEvent, + details?: any, + ) => void; + /** + * Callback fired when a mouse leave event comes from a column header element. + * @param params With all properties from [[GridColumnHeaderParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onColumnHeaderLeave?: ( + params: GridColumnHeaderParams, + event: MuiEvent, + details?: any, + ) => void; + /** + * Callback fired when a column is reordered. + * @param params With all properties from [[GridColumnOrderChangeParams]]. + * @param event [[MuiEvent<{}>]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onColumnOrderChange?: ( + params: GridColumnOrderChangeParams, + event: MuiEvent<{}>, + details?: any, + ) => void; + /** + * Callback fired while a column is being resized. + * @param params With all properties from [[GridColumnResizeParams]]. + * @param event [[MuiEvent<{}>]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onColumnResize?: (params: GridColumnResizeParams, event: MuiEvent<{}>, details?: any) => void; + /** + * Callback fired when the width of a column is changed. + * @param params With all properties from [[GridColumnResizeParams]]. + * @param event [[MuiEvent<{}>]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onColumnWidthChange?: ( + params: GridColumnResizeParams, + event: MuiEvent<{}>, + details?: any, + ) => void; + /** + * Callback fired when a column visibility changes. + * @param params With all properties from [[GridColumnVisibilityChangeParams]]. + * @param event [[MuiEvent<{}>]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onColumnVisibilityChange?: ( + params: GridColumnVisibilityChangeParams, + event: MuiEvent<{}>, + details?: any, + ) => void; + /** + * Callback fired when a click event comes from a row container element. + * @param params With all properties from [[GridRowParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onRowClick?: ( + params: GridRowParams, + event: MuiEvent, + details?: any, + ) => void; + /** + * Callback fired when scrolling to the bottom of the grid viewport. + * @param params With all properties from [[GridRowScrollEndParams]]. + * @param event [[MuiEvent<{}>]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onRowsScrollEnd?: (params: GridRowScrollEndParams, event: MuiEvent<{}>, details?: any) => void; + /** + * Callback fired when a double click event comes from a row container element. + * @param params With all properties from [[RowParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onRowDoubleClick?: ( + params: GridRowParams, + event: MuiEvent, + details?: any, + ) => void; + /** + * Callback fired when a mouseover event comes from a row container element. + * @param params With all properties from [[GridRowParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onRowOver?: (params: GridRowParams, event: MuiEvent, details?: any) => void; + /** + * Callback fired when a mouseout event comes from a row container element. + * @param params With all properties from [[GridRowParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onRowOut?: (params: GridRowParams, event: MuiEvent, details?: any) => void; + /** + * Callback fired when a mouse enter event comes from a row container element. + * @param params With all properties from [[GridRowParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onRowEnter?: ( + params: GridRowParams, + event: MuiEvent, + details?: any, + ) => void; + /** + * Callback fired when a mouse leave event comes from a row container element. + * @param params With all properties from [[GridRowParams]]. + * @param event [[MuiEvent]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onRowLeave?: ( + params: GridRowParams, + event: MuiEvent, + details?: any, + ) => void; + /** + * Callback fired when the grid is resized. + * @param containerSize With all properties from [[ElementSize]]. + * @param event [[MuiEvent<{}>]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onResize?: (containerSize: ElementSize, event: MuiEvent<{}>, details?: any) => void; + /** + * Callback fired when the state of the grid is updated. + * @param state The new state. + * @param event [[MuiEvent<{}>]]. + * @param {GridCallbackDetails} details Additional details for this callback. + * @internal + */ + onStateChange?: (state: GridState, event: MuiEvent<{}>, details?: any) => void; + /** + * Callback fired when the rows in the viewport change. + */ + onViewportRowsChange?: ( + params: GridViewportRowsChangeParams, + event: MuiEvent<{}>, + details?: any, + ) => void; + + /** + * Set the current page. + * @default 1 + */ + page?: number; + /** + * Callback fired when the current page has changed. + * @param page Index of the page displayed on the Grid. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onPageChange?: (page: number, details?: any) => void; + /** + * Set the number of rows in one page. + * @default 100 + */ + pageSize?: number; + /** + * Callback fired when the page size has changed. + * @param pageSize Size of the page displayed on the Grid. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onPageSizeChange?: (pageSize: number, details?: any) => void; + + /** + * Set the edit rows model of the grid. + */ + editRowsModel?: GridEditRowsModel; + /** + * Callback fired when the `editRowsModel` changes. + * @param {GridEditRowsModel} editRowsModel With all properties from [[GridEditRowsModel]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onEditRowsModelChange?: (editRowsModel: GridEditRowsModel, details?: any) => void; + /** + * Set the filter model of the grid. + */ + filterModel?: GridFilterModel; + /** + * Callback fired when the Filter model changes before the filters are applied. + * @param model With all properties from [[GridFilterModel]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onFilterModelChange?: (model: GridFilterModel, details?: any) => void; + /** + * Set the selection model of the grid. + */ + selectionModel?: GridInputSelectionModel; + /** + * Callback fired when the selection state of one or multiple rows changes. + * @param selectionModel With all the row ids [[GridSelectionModel]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onSelectionModelChange?: (selectionModel: GridSelectionModel, details?: any) => void; + /** + * Set the sort model of the grid. + */ + sortModel?: GridSortModel; + /** + * Callback fired when the sort model changes before a column is sorted. + * @param model With all properties from [[GridSortModel]]. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onSortModelChange?: (model: GridSortModel, details?: any) => void; /** * The label of the grid. */ @@ -35,14 +511,6 @@ export interface GridComponentProps extends GridOptionsProp { * Set of columns of type [[GridColumns]]. */ columns: GridColumns; - /** - * Overrideable components. - */ - components?: GridSlotsComponent; - /** - * Overrideable components props dynamically passed to the component at rendering. - */ - componentsProps?: GridSlotsComponentsProps; /** * An error that will turn the grid into its error state and display the error component. */ @@ -59,20 +527,10 @@ export interface GridComponentProps extends GridOptionsProp { * Nonce of the inline styles for [Content Security Policy](https://www.w3.org/TR/2016/REC-CSP2-20161215/#script-src-the-nonce-attribute). */ nonce?: string; - /** - * Set a callback fired when the state of the grid is updated. - */ - onStateChange?: (state: GridState, event: MuiEvent<{}>, details: any) => void; // We are overriding the handler in GridOptions to fix the params type and avoid the cycle dependency /** * Set of rows of type [[GridRowsProp]]. */ rows: GridRowsProp; - /** - * Signal to the underlying logic what version of the public component API - * of the data grid is exposed [[GridSignature]]. - * @internal - */ - signature: string; /** * Set the whole state of the grid. */ @@ -81,4 +539,8 @@ export interface GridComponentProps extends GridOptionsProp { * @ignore */ style?: React.CSSProperties; + /** + * Overrideable components props dynamically passed to the component at rendering. + */ + componentsProps?: GridSlotsComponentsProps; } diff --git a/packages/grid/_modules_/grid/components/GridFooter.tsx b/packages/grid/_modules_/grid/components/GridFooter.tsx index e6e2123160c2b..72dda77968ac9 100644 --- a/packages/grid/_modules_/grid/components/GridFooter.tsx +++ b/packages/grid/_modules_/grid/components/GridFooter.tsx @@ -32,13 +32,13 @@ export const GridFooter = React.forwardRef + ); return ( diff --git a/packages/grid/_modules_/grid/components/GridHeader.tsx b/packages/grid/_modules_/grid/components/GridHeader.tsx index 4bb679bf814aa..b7bc8f8352674 100644 --- a/packages/grid/_modules_/grid/components/GridHeader.tsx +++ b/packages/grid/_modules_/grid/components/GridHeader.tsx @@ -1,22 +1,18 @@ import * as React from 'react'; -import { useGridApiContext } from '../hooks/root/useGridApiContext'; +import { useGridRootProps } from '../hooks/utils/useGridRootProps'; export const GridHeader = React.forwardRef>( function GridHeader(props, ref) { - const apiRef = useGridApiContext(); + const rootProps = useGridRootProps(); - const PreferencesPanelComponent = apiRef?.current.components.PreferencesPanel; - const PreferencesPanelElement = PreferencesPanelComponent && ( - - ); - const ToolbarComponent = apiRef?.current.components.Toolbar; + const ToolbarComponent = rootProps.components.Toolbar; const ToolbarElement = ToolbarComponent && ( - + ); return (
- {PreferencesPanelElement} + {ToolbarElement}
); diff --git a/packages/grid/_modules_/grid/components/GridRow.tsx b/packages/grid/_modules_/grid/components/GridRow.tsx index 95f511855aa5a..550cb41356823 100644 --- a/packages/grid/_modules_/grid/components/GridRow.tsx +++ b/packages/grid/_modules_/grid/components/GridRow.tsx @@ -8,7 +8,7 @@ import { isFunction } from '../utils/utils'; import { gridDensityRowHeightSelector } from '../hooks/features/density'; import { useGridApiContext } from '../hooks/root/useGridApiContext'; import { useGridSelector } from '../hooks/features/core/useGridSelector'; -import { optionsSelector } from '../hooks/utils/optionsSelector'; +import { useGridRootProps } from '../hooks/utils/useGridRootProps'; export interface GridRowProps { id: GridRowId; @@ -21,8 +21,8 @@ export function GridRow(props: GridRowProps) { const { selected, id, rowIndex, children } = props; const ariaRowIndex = rowIndex + 2; // 1 for the header row and 1 as it's 1 based const apiRef = useGridApiContext(); + const rootProps = useGridRootProps(); const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector); - const { classes, getRowClassName, editMode } = useGridSelector(apiRef, optionsSelector); const publish = React.useCallback( (eventName: string) => (event: React.MouseEvent) => { @@ -64,11 +64,12 @@ export function GridRow(props: GridRowProps) { }; const rowClassName = - isFunction(getRowClassName) && getRowClassName(apiRef!.current.getRowParams(id)); - const cssClasses = clsx(rowClassName, classes?.row, { + isFunction(rootProps.getRowClassName) && + rootProps.getRowClassName(apiRef!.current.getRowParams(id)); + const cssClasses = clsx(rowClassName, rootProps.classes.row, { 'Mui-selected': selected, [`${GRID_CSS_CLASS_PREFIX}-row--editing`]: apiRef.current.getRowMode(id) === GridRowModes.Edit, - [`${GRID_CSS_CLASS_PREFIX}-row--editable`]: editMode === GridEditModes.Row, + [`${GRID_CSS_CLASS_PREFIX}-row--editable`]: rootProps.editMode === GridEditModes.Row, }); return ( diff --git a/packages/grid/_modules_/grid/components/GridViewport.tsx b/packages/grid/_modules_/grid/components/GridViewport.tsx index a2f93d04efcc9..c1bd87d4086d7 100644 --- a/packages/grid/_modules_/grid/components/GridViewport.tsx +++ b/packages/grid/_modules_/grid/components/GridViewport.tsx @@ -10,7 +10,6 @@ import { import { gridEditRowsStateSelector } from '../hooks/features/rows/gridEditRowsSelector'; import { selectedIdsLookupSelector } from '../hooks/features/selection/gridSelectionSelector'; import { renderStateSelector } from '../hooks/features/virtualization/renderingStateSelector'; -import { optionsSelector } from '../hooks/utils/optionsSelector'; import { useGridApiContext } from '../hooks/root/useGridApiContext'; import { GridDataContainer } from './containers/GridDataContainer'; import { GridEmptyCell } from './cell/GridEmptyCell'; @@ -31,7 +30,6 @@ export const GridViewport: ViewportType = React.forwardRef( function GridViewport(props, renderingZoneRef) { const apiRef = useGridApiContext(); const rootProps = useGridRootProps(); - const options = useGridSelector(apiRef, optionsSelector); const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); const viewportSizes = useGridSelector(apiRef, gridViewportSizesSelector); const scrollBarState = useGridSelector(apiRef, gridScrollBarSizeSelector); @@ -71,14 +69,14 @@ export const GridViewport: ViewportType = React.forwardRef( lastColIdx={renderState.renderContext!.lastColIdx!} hasScrollX={scrollBarState.hasScrollX} hasScrollY={scrollBarState.hasScrollY} - showCellRightBorder={!!rootProps.showCellRightBorder} + showCellRightBorder={rootProps.showCellRightBorder} extendRowFullWidth={!rootProps.disableExtendRowFullWidth} rowIndex={renderState.renderContext!.firstRowIdx! + idx} cellFocus={cellFocus} cellTabIndex={cellTabIndex} isSelected={selectionLookup[id] !== undefined} editRowState={editRowsState[id]} - cellClassName={options.classes?.cell} + cellClassName={rootProps.classes.cell} getCellClassName={rootProps.getCellClassName} /> diff --git a/packages/grid/_modules_/grid/components/base/GridErrorHandler.tsx b/packages/grid/_modules_/grid/components/base/GridErrorHandler.tsx index 1d323fdce1872..b75bb18b0bd79 100644 --- a/packages/grid/_modules_/grid/components/base/GridErrorHandler.tsx +++ b/packages/grid/_modules_/grid/components/base/GridErrorHandler.tsx @@ -21,7 +21,7 @@ export function GridErrorHandler(props) { logger={logger} render={(errorProps) => ( - diff --git a/packages/grid/_modules_/grid/components/base/GridFooterPlaceholder.tsx b/packages/grid/_modules_/grid/components/base/GridFooterPlaceholder.tsx index 46f06844e5d19..fa4c33f44294f 100644 --- a/packages/grid/_modules_/grid/components/base/GridFooterPlaceholder.tsx +++ b/packages/grid/_modules_/grid/components/base/GridFooterPlaceholder.tsx @@ -14,7 +14,7 @@ export function GridFooterPlaceholder() { return (
- +
); } diff --git a/packages/grid/_modules_/grid/components/base/GridHeaderPlaceholder.tsx b/packages/grid/_modules_/grid/components/base/GridHeaderPlaceholder.tsx index 16c2f9633fdd2..63da0f8a20b05 100644 --- a/packages/grid/_modules_/grid/components/base/GridHeaderPlaceholder.tsx +++ b/packages/grid/_modules_/grid/components/base/GridHeaderPlaceholder.tsx @@ -10,7 +10,7 @@ export function GridHeaderPlaceholder() { return (
- +
); } diff --git a/packages/grid/_modules_/grid/components/base/GridOverlays.tsx b/packages/grid/_modules_/grid/components/base/GridOverlays.tsx index f526e03e60ffc..e7aea44f017cb 100644 --- a/packages/grid/_modules_/grid/components/base/GridOverlays.tsx +++ b/packages/grid/_modules_/grid/components/base/GridOverlays.tsx @@ -16,23 +16,17 @@ export function GridOverlays() { const showNoResultsOverlay = !rootProps.loading && totalRowCount > 0 && visibleRowCount === 0; if (showNoRowsOverlay) { - return ( - - ); + return ; } if (showNoResultsOverlay) { return ( - + ); } if (rootProps.loading) { - return ( - - ); + return ; } return null; } diff --git a/packages/grid/_modules_/grid/components/cell/GridBooleanCell.tsx b/packages/grid/_modules_/grid/components/cell/GridBooleanCell.tsx index eb24751fe9998..6efaad3f2052c 100644 --- a/packages/grid/_modules_/grid/components/cell/GridBooleanCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridBooleanCell.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { SvgIconProps } from '@material-ui/core/SvgIcon'; import { gridClasses } from '../../gridClasses'; import { GridRenderCellParams } from '../../models/params/gridCellParams'; +import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; export const GridBooleanCell = React.memo((props: GridRenderCellParams & SvgIconProps) => { const { @@ -20,9 +21,12 @@ export const GridBooleanCell = React.memo((props: GridRenderCellParams & SvgIcon ...other } = props; + const rootProps = useGridRootProps(); + const Icon = React.useMemo( - () => (value ? api.components.BooleanCellTrueIcon! : api.components.BooleanCellFalseIcon!), - [api.components.BooleanCellFalseIcon, api.components.BooleanCellTrueIcon, value], + () => + value ? rootProps.components.BooleanCellTrueIcon : rootProps.components.BooleanCellFalseIcon, + [rootProps.components.BooleanCellFalseIcon, rootProps.components.BooleanCellTrueIcon, value], ); return ( diff --git a/packages/grid/_modules_/grid/components/columnHeaders/ColumnHeaderFilterIcon.tsx b/packages/grid/_modules_/grid/components/columnHeaders/ColumnHeaderFilterIcon.tsx index 143839b4ce5ee..d48e13a9905c3 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/ColumnHeaderFilterIcon.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/ColumnHeaderFilterIcon.tsx @@ -6,6 +6,7 @@ import { gridPreferencePanelStateSelector } from '../../hooks/features/preferenc import { GridPreferencePanelsValue } from '../../hooks/features/preferencesPanel/gridPreferencePanelsValue'; import { useGridApiContext } from '../../hooks/root/useGridApiContext'; import { gridClasses } from '../../gridClasses'; +import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; export interface ColumnHeaderFilterIconProps { counter?: number; @@ -14,8 +15,7 @@ export interface ColumnHeaderFilterIconProps { export function ColumnHeaderFilterIcon(props: ColumnHeaderFilterIconProps) { const { counter } = props; const apiRef = useGridApiContext(); - - const FilteredColumnIconElement = apiRef!.current.components.ColumnFilteredIcon!; + const rootProps = useGridRootProps(); const toggleFilter = React.useCallback( (event: React.MouseEvent) => { @@ -45,7 +45,10 @@ export function ColumnHeaderFilterIcon(props: ColumnHeaderFilterIconProps) { size="small" tabIndex={-1} > - + ); diff --git a/packages/grid/_modules_/grid/components/columnHeaders/ColumnHeaderMenuIcon.tsx b/packages/grid/_modules_/grid/components/columnHeaders/ColumnHeaderMenuIcon.tsx index a13705e531419..81a1526eb4a70 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/ColumnHeaderMenuIcon.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/ColumnHeaderMenuIcon.tsx @@ -4,6 +4,7 @@ import IconButton from '@material-ui/core/IconButton'; import { useGridApiContext } from '../../hooks/root/useGridApiContext'; import { GridStateColDef } from '../../models/colDef/gridColDef'; import { gridClasses } from '../../gridClasses'; +import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; export interface ColumnHeaderMenuIconProps { column: GridStateColDef; @@ -16,7 +17,7 @@ export interface ColumnHeaderMenuIconProps { export const ColumnHeaderMenuIcon = React.memo((props: ColumnHeaderMenuIconProps) => { const { column, open, columnMenuId, columnMenuButtonId, iconButtonRef } = props; const apiRef = useGridApiContext(); - const ColumnMenuIcon = apiRef!.current.components.ColumnMenuIcon!; + const rootProps = useGridRootProps(); const handleMenuIconClick = React.useCallback( (event: React.MouseEvent) => { @@ -42,7 +43,7 @@ export const ColumnHeaderMenuIcon = React.memo((props: ColumnHeaderMenuIconProps aria-controls={columnMenuId} id={columnMenuButtonId} > - + ); diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderItem.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderItem.tsx index 58e9d8c78d4dd..980db1551055b 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderItem.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderItem.tsx @@ -4,7 +4,6 @@ import clsx from 'clsx'; import { unstable_useId as useId } from '@material-ui/core/utils'; import { GridEvents } from '../../constants/eventsConstants'; import { GridStateColDef, GRID_NUMBER_COLUMN_TYPE } from '../../models/colDef/index'; -import { GridOptions } from '../../models/gridOptions'; import { GridSortDirection } from '../../models/gridSortModel'; import { useGridApiContext } from '../../hooks/root/useGridApiContext'; import { GridColumnHeaderSortIcon } from './GridColumnHeaderSortIcon'; @@ -15,6 +14,7 @@ import { ColumnHeaderFilterIcon } from './ColumnHeaderFilterIcon'; import { GridColumnHeaderMenu } from '../menu/columnMenu/GridColumnHeaderMenu'; import { isFunction } from '../../utils/utils'; import { gridClasses } from '../../gridClasses'; +import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; interface GridColumnHeaderItemProps { colIndex: number; @@ -25,7 +25,6 @@ interface GridColumnHeaderItemProps { isResizing: boolean; sortDirection: GridSortDirection; sortIndex?: number; - options: GridOptions; filterItemsCounter?: number; hasFocus?: boolean; tabIndex: 0 | -1; @@ -41,24 +40,16 @@ export function GridColumnHeaderItem(props: GridColumnHeaderItemProps) { isResizing, sortDirection, sortIndex, - options, filterItemsCounter, hasFocus, tabIndex, } = props; const apiRef = useGridApiContext(); + const rootProps = useGridRootProps(); const headerCellRef = React.useRef(null); const columnMenuId: string = useId(); const columnMenuButtonId: string = useId(); const iconButtonRef = React.useRef(null); - const { - classes, - disableColumnReorder, - showColumnRightBorder, - disableColumnResize, - disableColumnMenu, - disableColumnFilter, - } = options; const isColumnSorted = sortDirection != null; // todo refactor to a prop on col isNumeric or ?? ie: coltype===price wont work const isColumnNumeric = column.type === GRID_NUMBER_COLUMN_TYPE; @@ -110,7 +101,7 @@ export function GridColumnHeaderItem(props: GridColumnHeaderItemProps) { [publish], ); - const classNames = [classes?.columnHeader]; + const classNames = [rootProps.classes.columnHeader]; if (column.headerClassName) { const headerClassName = isFunction(column.headerClassName) @@ -128,7 +119,7 @@ export function GridColumnHeaderItem(props: GridColumnHeaderItemProps) { [gridClasses['columnHeader--moving']]: isDragging, [gridClasses['columnHeader--sorted']]: isColumnSorted, [gridClasses['columnHeader--numeric']]: isColumnNumeric, - [gridClasses.withBorder]: showColumnRightBorder, + [gridClasses.withBorder]: rootProps.showColumnRightBorder, }, ...classNames, ); @@ -142,7 +133,7 @@ export function GridColumnHeaderItem(props: GridColumnHeaderItemProps) { }; } - const columnMenuIconButton = !disableColumnMenu && !column.disableColumnMenu && ( + const columnMenuIconButton = !rootProps.disableColumnMenu && !column.disableColumnMenu && ( - {!disableColumnFilter && } + {!rootProps.disableColumnFilter && } {column.sortable && !column.hideSortIcons && ( )} @@ -194,7 +185,7 @@ export function GridColumnHeaderItem(props: GridColumnHeaderItemProps) { >
@@ -210,7 +201,7 @@ export function GridColumnHeaderItem(props: GridColumnHeaderItemProps) { {columnMenuIconButton}
); diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderSeparator.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderSeparator.tsx index eb2e7d2b898af..ec5e19bd05351 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderSeparator.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderSeparator.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import clsx from 'clsx'; -import { useGridApiContext } from '../../hooks/root/useGridApiContext'; import { gridClasses } from '../../gridClasses'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; @@ -14,9 +13,7 @@ export const GridColumnHeaderSeparator = React.memo(function GridColumnHeaderSep props: GridColumnHeaderSeparatorProps, ) { const { resizable, resizing, height, ...other } = props; - const apiRef = useGridApiContext(); const rootProps = useGridRootProps(); - const ColumnResizeIcon = apiRef!.current.components!.ColumnResizeIcon!; const stopClick = React.useCallback((event: React.MouseEvent) => { event.preventDefault(); @@ -34,7 +31,7 @@ export const GridColumnHeaderSeparator = React.memo(function GridColumnHeaderSep {...other} onClick={stopClick} > - + ); }); diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderSortIcon.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderSortIcon.tsx index 3eeaef2c9799b..e6ace4cbb6c46 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderSortIcon.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderSortIcon.tsx @@ -5,6 +5,7 @@ import { GridIconSlotsComponent } from '../../models/gridIconSlotsComponent'; import { GridSortDirection } from '../../models/gridSortModel'; import { useGridApiContext } from '../../hooks/root/useGridApiContext'; import { gridClasses } from '../../gridClasses'; +import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; export interface GridColumnHeaderSortIconProps { direction: GridSortDirection; @@ -26,8 +27,9 @@ export const GridColumnHeaderSortIcon = React.memo(function GridColumnHeaderSort ) { const { direction, index } = props; const apiRef = useGridApiContext(); + const rootProps = useGridRootProps(); - const unsortedIcon = apiRef!.current.components.ColumnUnsortedIcon; + const unsortedIcon = rootProps.components.ColumnUnsortedIcon; if (direction == null && unsortedIcon === null) { return null; } @@ -39,7 +41,7 @@ export const GridColumnHeaderSortIcon = React.memo(function GridColumnHeaderSort title={apiRef!.current.getLocaleText('columnHeaderSortIconLabel')} size="small" > - {getIcon(apiRef!.current.components, direction)} + {getIcon(rootProps.components, direction)} ); diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx index dba66efdf25c7..27bcde4553d0b 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx @@ -12,7 +12,6 @@ import { gridSortColumnLookupSelector } from '../../hooks/features/sorting/gridS import { renderStateSelector } from '../../hooks/features/virtualization/renderingStateSelector'; import { gridDensityHeaderHeightSelector } from '../../hooks/features/density/densitySelector'; import { gridColumnMenuStateSelector } from '../../hooks/features/columnMenu/columnMenuSelector'; -import { optionsSelector } from '../../hooks/utils/optionsSelector'; import { GridStateColDef } from '../../models/colDef/gridColDef'; import { useGridApiContext } from '../../hooks/root/useGridApiContext'; import { GridColumnHeaderItem } from './GridColumnHeaderItem'; @@ -24,7 +23,6 @@ export interface GridColumnHeadersItemCollectionProps { export function GridColumnHeadersItemCollection(props: GridColumnHeadersItemCollectionProps) { const { columns } = props; const apiRef = useGridApiContext(); - const options = useGridSelector(apiRef, optionsSelector); const sortColumnLookup = useGridSelector(apiRef, gridSortColumnLookupSelector); const filterColumnLookup = useGridSelector(apiRef, filterGridColumnLookupSelector); const dragCol = useGridSelector(apiRef, gridColumnReorderDragColSelector); @@ -62,7 +60,6 @@ export function GridColumnHeadersItemCollection(props: GridColumnHeadersItemColl {...sortColumnLookup[col.field]} columnMenuOpen={open} filterItemsCounter={filterColumnLookup[col.field] && filterColumnLookup[col.field].length} - options={options} headerHeight={headerHeight} isDragging={col.field === dragCol} column={col} diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnUnsortedIcon.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnUnsortedIcon.tsx index 0325319b16c45..5217093237b8c 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnUnsortedIcon.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnUnsortedIcon.tsx @@ -1,16 +1,14 @@ import * as React from 'react'; -import { useGridApiContext } from '../../hooks/root/useGridApiContext'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; export const GridColumnUnsortedIcon = React.memo(function GridColumnHeaderSortIcon(props) { - const apiRef = useGridApiContext(); const rootProps = useGridRootProps(); const [nextSortDirection] = rootProps.sortingOrder!; const Icon = nextSortDirection === 'asc' - ? apiRef?.current.components.ColumnSortedAscendingIcon - : apiRef?.current.components.ColumnSortedDescendingIcon; + ? rootProps.components.ColumnSortedAscendingIcon + : rootProps.components.ColumnSortedDescendingIcon; return Icon ? : null; }); diff --git a/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx b/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx index 438bc752e90c7..64fac3ab624a3 100644 --- a/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx +++ b/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx @@ -50,13 +50,11 @@ export const GridCellCheckboxForwardRef = React.forwardRef ); }, diff --git a/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx b/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx index bce7b0b440ba7..87e3858b95d04 100644 --- a/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx +++ b/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx @@ -61,10 +61,8 @@ export const GridHeaderCheckbox = React.forwardRef ); }, diff --git a/packages/grid/_modules_/grid/components/containers/GridRoot.tsx b/packages/grid/_modules_/grid/components/containers/GridRoot.tsx index ebec346d11f7a..c494b1529df01 100644 --- a/packages/grid/_modules_/grid/components/containers/GridRoot.tsx +++ b/packages/grid/_modules_/grid/components/containers/GridRoot.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import clsx from 'clsx'; import { useForkRef } from '@material-ui/core/utils'; import NoSsr from '@material-ui/core/NoSsr'; -import { optionsSelector } from '../../hooks/utils/optionsSelector'; import { GridRootContainerRef } from '../../models/gridRootContainerRef'; import { useStyles } from './GridRootStyles'; import { visibleGridColumnsLengthSelector } from '../../hooks/features/columns/gridColumnsSelector'; @@ -24,7 +23,6 @@ export const GridRoot = React.forwardRef(function const { children, className: classNameProp, ...other } = props; const visibleColumnsLength = useGridSelector(apiRef, visibleGridColumnsLengthSelector); const [gridState] = useGridState(apiRef!); - const options = useGridSelector(apiRef, optionsSelector); const rootContainerRef: GridRootContainerRef = React.useRef(null); const handleRef = useForkRef(rootContainerRef, ref); apiRef.current.rootElementRef = rootContainerRef; @@ -33,7 +31,7 @@ export const GridRoot = React.forwardRef(function
0 && preferencePanelState.open} - {...apiRef?.current.componentsProps?.panel} + {...rootProps.componentsProps?.panel} {...props} > {!rootProps.disableColumnSelector && isColumnsTabOpen && ( - + )} {!rootProps.disableColumnFilter && isFiltersTabOpen && ( - + )} - + ); }); diff --git a/packages/grid/_modules_/grid/components/toolbar/GridToolbarColumnsButton.tsx b/packages/grid/_modules_/grid/components/toolbar/GridToolbarColumnsButton.tsx index f21b56da54aff..e1fef258c0358 100644 --- a/packages/grid/_modules_/grid/components/toolbar/GridToolbarColumnsButton.tsx +++ b/packages/grid/_modules_/grid/components/toolbar/GridToolbarColumnsButton.tsx @@ -11,7 +11,6 @@ export const GridToolbarColumnsButton = React.forwardRef { @@ -35,7 +34,7 @@ export const GridToolbarColumnsButton = React.forwardRef} + startIcon={} {...other} onClick={showColumns} > diff --git a/packages/grid/_modules_/grid/components/toolbar/GridToolbarDensitySelector.tsx b/packages/grid/_modules_/grid/components/toolbar/GridToolbarDensitySelector.tsx index 06f8189bd897d..cc5771b64a5c3 100644 --- a/packages/grid/_modules_/grid/components/toolbar/GridToolbarDensitySelector.tsx +++ b/packages/grid/_modules_/grid/components/toolbar/GridToolbarDensitySelector.tsx @@ -24,9 +24,9 @@ export const GridToolbarDensitySelector = React.forwardRef = [ { diff --git a/packages/grid/_modules_/grid/components/toolbar/GridToolbarExport.tsx b/packages/grid/_modules_/grid/components/toolbar/GridToolbarExport.tsx index eaa8e3fd503a1..3522bcb195253 100644 --- a/packages/grid/_modules_/grid/components/toolbar/GridToolbarExport.tsx +++ b/packages/grid/_modules_/grid/components/toolbar/GridToolbarExport.tsx @@ -8,6 +8,7 @@ import { isHideMenuKey, isTabKey } from '../../utils/keyboardUtils'; import { useGridApiContext } from '../../hooks/root/useGridApiContext'; import { GridMenu } from '../menu/GridMenu'; import { GridExportCsvOptions } from '../../models/gridExport'; +import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; interface GridExportFormatCsv { format: 'csv'; @@ -28,10 +29,10 @@ export const GridToolbarExport = React.forwardRef = []; exportOptions.push({ @@ -68,7 +69,7 @@ export const GridToolbarExport = React.forwardRef} + startIcon={} aria-expanded={anchorEl ? 'true' : undefined} aria-label={apiRef!.current.getLocaleText('toolbarExportLabel')} aria-haspopup="menu" diff --git a/packages/grid/_modules_/grid/components/toolbar/GridToolbarFilterButton.tsx b/packages/grid/_modules_/grid/components/toolbar/GridToolbarFilterButton.tsx index ff5439e79275a..8c5ea781d384f 100644 --- a/packages/grid/_modules_/grid/components/toolbar/GridToolbarFilterButton.tsx +++ b/packages/grid/_modules_/grid/components/toolbar/GridToolbarFilterButton.tsx @@ -105,7 +105,6 @@ export const GridToolbarFilterButton = React.forwardRef< return null; } - const OpenFilterButtonIcon = apiRef!.current.components!.OpenFilterButtonIcon!; return (
- +
); diff --git a/packages/storybook/src/components/pricing-grid.tsx b/packages/storybook/src/components/pricing-grid.tsx index 529fee609b78b..879ee395c9551 100644 --- a/packages/storybook/src/components/pricing-grid.tsx +++ b/packages/storybook/src/components/pricing-grid.tsx @@ -10,11 +10,11 @@ import { import { currencyPairs } from '../data/currency-pairs'; export interface PricingGridProps extends Omit { - min?: number; - max?: number; + min: number; + max: number; } export const PricingGrid = (props: PricingGridProps) => { - const { min, max } = props; + const { min, max, ...other } = props; const [columns] = React.useState(pricingColumns); const [rows] = React.useState([]); @@ -54,6 +54,7 @@ export const PricingGrid = (props: PricingGridProps) => { subscribeToStream(); } }; + const getRowId = React.useCallback((row) => row.idfield, []); return ( @@ -68,7 +69,7 @@ export const PricingGrid = (props: PricingGridProps) => {
- +
); diff --git a/packages/storybook/src/stories/grid-streaming.stories.tsx b/packages/storybook/src/stories/grid-streaming.stories.tsx index 76df0dd8a77a5..59ae8a2df7f20 100644 --- a/packages/storybook/src/stories/grid-streaming.stories.tsx +++ b/packages/storybook/src/stories/grid-streaming.stories.tsx @@ -27,11 +27,13 @@ export const SlowUpdateGrid = () => {

action('onSelectionChange', { depth: 1 })(params)} + throttleRowsMs={100} {...rate} /> ); }; + export const FastUpdateGrid = () => { const rate = { min: 100, max: 500 }; @@ -42,13 +44,30 @@ export const FastUpdateGrid = () => {

action('onSelectionChange', { depth: 1 })(params)} + throttleRowsMs={100} {...rate} /> ); }; + export const SingleSubscriptionFast = () => { - const rate = { min: 100, max: 500 }; + const rate = { min: 50, max: 500 }; + return ( + +

+ One Subscription for the whole feed! Update rate between {rate.min} - {rate.max} ms! +

+ action('onSelectionChange', { depth: 1 })(params)} + {...rate} + /> +
+ ); +} + +export const SingleSubscriptionFastWithThrottle = () => { + const rate = { min: 50, max: 500 }; return (

@@ -56,6 +75,7 @@ export const SingleSubscriptionFast = () => {

action('onSelectionChange', { depth: 1 })(params)} + throttleRowsMs={500} {...rate} />
From 84afae2b3316f51b7b59c7cd895f3aa5c3464423 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 7 Sep 2021 17:15:10 +0200 Subject: [PATCH 033/390] Remove unused event --- packages/grid/_modules_/grid/constants/eventsConstants.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/grid/_modules_/grid/constants/eventsConstants.ts b/packages/grid/_modules_/grid/constants/eventsConstants.ts index d89a2c6988806..2eaaa928d9f0d 100644 --- a/packages/grid/_modules_/grid/constants/eventsConstants.ts +++ b/packages/grid/_modules_/grid/constants/eventsConstants.ts @@ -286,13 +286,6 @@ export enum GridEvents { * @ignore - do not document. */ rowsSet = 'rowsSet', - /** - * Implementation detail. - * Fired to reset the sortedRow when the set of rows changes. - * It's important as the rendered rows are coming from the sortedRow - * @ignore - do not document. - */ - rowsClear = 'rowsClear', /** * Fired when the columns state is changed. * Called with an array of strings corresponding to the field names. From 8a37bef9b719bd9a0b1cc23adeb3bf6e7fdd5611 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 7 Sep 2021 17:15:32 +0200 Subject: [PATCH 034/390] Fix --- .../grid/hooks/features/rows/gridRowsState.ts | 6 +- .../grid/hooks/features/rows/useGridRows.ts | 92 +++++++++++-------- .../_modules_/grid/models/gridOptions.tsx | 2 +- .../src/stories/grid-streaming.stories.tsx | 2 +- 4 files changed, 58 insertions(+), 44 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts index fbd0cc2d28939..14d85c25bcbca 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts @@ -7,9 +7,9 @@ export interface GridRowsState { } export interface GridRowsInternalCache { - state: GridRowsState, - timeout: NodeJS.Timeout | null, - lastUpdateMs: number, + state: GridRowsState; + timeout: NodeJS.Timeout | null; + lastUpdateMs: number; } export const getInitialGridRowState: () => GridRowsState = () => ({ diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 17286ccfe6a11..84d172caec300 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -14,7 +14,7 @@ import { import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; import { useGridState } from '../core/useGridState'; -import {getInitialGridRowState, GridRowsInternalCache, GridRowsState} from './gridRowsState'; +import { getInitialGridRowState, GridRowsInternalCache, GridRowsState } from './gridRowsState'; import { gridRowCountSelector, gridRowsLookupSelector, @@ -68,7 +68,7 @@ export const useGridRows = ( state: getInitialGridRowState(), timeout: null, lastUpdateMs: 0, - } + }; } const getRowIndex = React.useCallback( @@ -96,36 +96,39 @@ export const useGridRows = ( [apiRef], ); - const throttledRowsChange = React.useCallback((newState: GridRowsState, throttle: boolean) => { - const run = () => { - rowsCache.current.timeout = null; - rowsCache.current.lastUpdateMs = Date.now() - setGridState((state) => ({ ...state, rows: rowsCache.current.state })); - apiRef.current.publishEvent(GridEvents.rowsSet) - forceUpdate(); - } - - if (rowsCache.current.timeout) { - clearTimeout(rowsCache.current.timeout) - } - - rowsCache.current.state = newState - rowsCache.current.timeout = null + const throttledRowsChange = React.useCallback( + (newState: GridRowsState, throttle: boolean) => { + const run = () => { + rowsCache.current.timeout = null; + rowsCache.current.lastUpdateMs = Date.now(); + setGridState((state) => ({ ...state, rows: rowsCache.current.state })); + apiRef.current.publishEvent(GridEvents.rowsSet); + forceUpdate(); + }; + + if (rowsCache.current.timeout) { + clearTimeout(rowsCache.current.timeout); + } - const throttleRemainingTimeMs = props.throttleRowsMs - (Date.now() - rowsCache.current.lastUpdateMs) + rowsCache.current.state = newState; + rowsCache.current.timeout = null; - if (throttle && throttleRemainingTimeMs > 0) { - rowsCache.current.timeout = setTimeout(run, throttleRemainingTimeMs) - } else { - run() - } + const throttleRemainingTimeMs = + props.throttleRowsMs - (Date.now() - rowsCache.current.lastUpdateMs); - }, [apiRef, forceUpdate, setGridState, rowsCache, props.throttleRowsMs]) + if (throttle && throttleRemainingTimeMs > 0) { + rowsCache.current.timeout = setTimeout(run, throttleRemainingTimeMs); + } else { + run(); + } + }, + [apiRef, forceUpdate, setGridState, rowsCache, props.throttleRowsMs], + ); const setRows = React.useCallback( (rows) => { logger.debug(`Updating all rows, new length ${rows.length}`); - throttledRowsChange(convertGridRowsPropToState(rows, props.rowCount, props.getRowId), true) + throttledRowsChange(convertGridRowsPropToState(rows, props.rowCount, props.getRowId), true); }, [logger, throttledRowsChange, props.rowCount, props.getRowId], ); @@ -144,7 +147,7 @@ export const useGridRows = ( }, {}); const addedRows: [GridRowId, GridRowModel][] = []; - const modifiedRows: [GridRowId, GridRowModel][] = [] + const modifiedRows: [GridRowId, GridRowModel][] = []; const deletedRowIds: GridRowId[] = []; Object.entries(uniqUpdates).forEach(([id, partialRow]) => { @@ -160,35 +163,43 @@ export const useGridRows = ( return; } - modifiedRows.push([id, partialRow]) + modifiedRows.push([id, partialRow]); }); if (deletedRowIds.length > 0) { - deletedRowIds.forEach(id => { - delete rowsCache.current.state.idRowsLookup[id] - }) + deletedRowIds.forEach((id) => { + delete rowsCache.current.state.idRowsLookup[id]; + }); } - const state = { ...rowsCache.current.state } + const state = { ...rowsCache.current.state }; if (addedRows.length > 0) { state.idRowsLookup = { ...state.idRowsLookup, - ...Object.fromEntries(addedRows) - } + ...Object.fromEntries(addedRows), + }; } if (modifiedRows.length > 0) { state.idRowsLookup = { ...state.idRowsLookup, - ...Object.fromEntries(modifiedRows.map(([id, partialRow]) => [id, { ...apiRef.current.getRow(id), ...partialRow}])) - } + ...Object.fromEntries( + modifiedRows.map(([id, partialRow]) => [ + id, + { ...apiRef.current.getRow(id), ...partialRow }, + ]), + ), + }; } - state.allRows = Object.keys(rowsCache.current.state.idRowsLookup) - state.totalRowCount = props.rowCount && props.rowCount > state.allRows.length ? props.rowCount : state.allRows.length + state.allRows = Object.keys(rowsCache.current.state.idRowsLookup); + state.totalRowCount = + props.rowCount && props.rowCount > state.allRows.length + ? props.rowCount + : state.allRows.length; - throttledRowsChange(state, true) + throttledRowsChange(state, true); }, [apiRef, props.getRowId, props.rowCount, throttledRowsChange], ); @@ -220,7 +231,10 @@ export const useGridRows = ( React.useEffect(() => { logger.debug(`Updating all rows, new length ${props.rows.length}`); - throttledRowsChange(convertGridRowsPropToState(props.rows, props.rowCount, props.getRowId), false) + throttledRowsChange( + convertGridRowsPropToState(props.rows, props.rowCount, props.getRowId), + false, + ); }, [props.rows, props.rowCount, props.getRowId, logger, throttledRowsChange]); const rowApi: GridRowApi = { diff --git a/packages/grid/_modules_/grid/models/gridOptions.tsx b/packages/grid/_modules_/grid/models/gridOptions.tsx index c100feeff5e72..6c0fe52aa0065 100644 --- a/packages/grid/_modules_/grid/models/gridOptions.tsx +++ b/packages/grid/_modules_/grid/models/gridOptions.tsx @@ -234,7 +234,7 @@ export interface GridSimpleOptions { * It can be useful if you have a high update rate * @default 0 */ - throttleRowsMs: number + throttleRowsMs: number; } /** diff --git a/packages/storybook/src/stories/grid-streaming.stories.tsx b/packages/storybook/src/stories/grid-streaming.stories.tsx index 59ae8a2df7f20..4de77bfa6c0c5 100644 --- a/packages/storybook/src/stories/grid-streaming.stories.tsx +++ b/packages/storybook/src/stories/grid-streaming.stories.tsx @@ -64,7 +64,7 @@ export const SingleSubscriptionFast = () => { /> ); -} +}; export const SingleSubscriptionFastWithThrottle = () => { const rate = { min: 50, max: 500 }; From e330b7b9d081d70934733c4d3565941057f84255 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 7 Sep 2021 17:54:04 +0200 Subject: [PATCH 035/390] =?UTF-8?q?Test=20should=20pass=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../keyboard/useGridKeyboardNavigation.ts | 11 +++-- .../grid/hooks/features/rows/useGridRows.ts | 48 ++++++++++++------- .../src/tests/rows.DataGridPro.test.tsx | 20 +------- 3 files changed, 39 insertions(+), 40 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts index 5687f8ed34471..9df9678102321 100644 --- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts +++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts @@ -76,8 +76,6 @@ export const useGridKeyboardNavigation = ( ): void => { const logger = useGridLogger(apiRef, 'useGridKeyboardNavigation'); const paginationState = useGridSelector(apiRef, gridPaginationSelector); - const totalRowCount = useGridSelector(apiRef, gridRowCountSelector); - const colCount = useGridSelector(apiRef, visibleGridColumnsLengthSelector); const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); const visibleSortedRowsAsArray = useGridSelector(apiRef, visibleSortedGridRowsAsArraySelector); @@ -94,6 +92,9 @@ export const useGridKeyboardNavigation = ( const navigateCells = React.useCallback( (params: GridCellParams, event: React.KeyboardEvent) => { event.preventDefault(); + const colCount = visibleGridColumnsLengthSelector(apiRef.current.state); + const totalRowCount = gridRowCountSelector(apiRef.current.state); + const colIndex = apiRef.current.getColumnIndex(params.field); const rowIndex = visibleSortedRowsAsArray.findIndex(([id]) => id === params.id); @@ -162,11 +163,9 @@ export const useGridKeyboardNavigation = ( [ apiRef, visibleSortedRowsAsArray, - totalRowCount, props.pagination, paginationState.pageSize, paginationState.page, - colCount, logger, containerSizes, ], @@ -174,6 +173,8 @@ export const useGridKeyboardNavigation = ( const navigateColumnHeaders = React.useCallback( (params: GridColumnHeaderParams, event: React.KeyboardEvent) => { + const colCount = visibleGridColumnsLengthSelector(apiRef.current.state); + event.preventDefault(); let nextColumnHeaderIndexes: GridColumnHeaderIndexCoordinates | null; const colIndex = apiRef.current.getColumnIndex(params.field); @@ -218,7 +219,7 @@ export const useGridKeyboardNavigation = ( const field = apiRef.current.getVisibleColumns()[nextColumnHeaderIndexes.colIndex].field; apiRef.current.setColumnHeaderFocus(field, event); }, - [apiRef, colCount, containerSizes, logger, visibleSortedRowsAsArray], + [apiRef, containerSizes, logger, visibleSortedRowsAsArray], ); useGridApiEventHandler(apiRef, GridEvents.cellNavigationKeyDown, navigateCells); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 84d172caec300..a8c2f5c2285e5 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -136,21 +136,27 @@ export const useGridRows = ( const updateRows = React.useCallback( (updates) => { // we removes duplicate updates. A server can batch updates, and send several updates for the same row in one fn call. - const uniqUpdates = updates.reduce<{ [id: string]: GridRowModel }>((acc, update) => { + const uniqUpdates = new Map(); + + updates.forEach((update) => { const id = getGridRowId( update, props.getRowId, 'A row was provided without id when calling updateRows():', ); - acc[id] = acc[id] != null ? { ...acc[id!], ...update } : update; - return acc; - }, {}); + + if (uniqUpdates.has(id)) { + uniqUpdates.set(id, { ...uniqUpdates.get(id), ...update }); + } else { + uniqUpdates.set(id, update); + } + }); const addedRows: [GridRowId, GridRowModel][] = []; const modifiedRows: [GridRowId, GridRowModel][] = []; const deletedRowIds: GridRowId[] = []; - Object.entries(uniqUpdates).forEach(([id, partialRow]) => { + uniqUpdates.forEach((partialRow, id) => { // eslint-disable-next-line no-underscore-dangle if (partialRow._action === 'delete') { deletedRowIds.push(id); @@ -166,24 +172,29 @@ export const useGridRows = ( modifiedRows.push([id, partialRow]); }); + let idRowsLookup = { ...rowsCache.current.state.idRowsLookup }; + let allRows = [...rowsCache.current.state.allRows]; + if (deletedRowIds.length > 0) { deletedRowIds.forEach((id) => { - delete rowsCache.current.state.idRowsLookup[id]; + delete idRowsLookup[id]; }); - } - const state = { ...rowsCache.current.state }; + allRows = allRows.filter((id) => !deletedRowIds.includes(id)); + } if (addedRows.length > 0) { - state.idRowsLookup = { - ...state.idRowsLookup, + idRowsLookup = { + ...idRowsLookup, ...Object.fromEntries(addedRows), }; + + allRows = [...allRows, ...addedRows.map(([id]) => id)]; } if (modifiedRows.length > 0) { - state.idRowsLookup = { - ...state.idRowsLookup, + idRowsLookup = { + ...idRowsLookup, ...Object.fromEntries( modifiedRows.map(([id, partialRow]) => [ id, @@ -193,11 +204,14 @@ export const useGridRows = ( }; } - state.allRows = Object.keys(rowsCache.current.state.idRowsLookup); - state.totalRowCount = - props.rowCount && props.rowCount > state.allRows.length - ? props.rowCount - : state.allRows.length; + const totalRowCount = + props.rowCount && props.rowCount > allRows.length ? props.rowCount : allRows.length; + + const state: GridRowsState = { + idRowsLookup, + allRows, + totalRowCount, + }; throttledRowsChange(state, true); }, diff --git a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx index 4e7785d41e75d..47e50dcbb5499 100644 --- a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx @@ -86,8 +86,6 @@ describe(' - Rows', () => { { clientId: 'c2', age: 30 }, { clientId: 'c3', age: 31 }, ]); - clock.tick(100); - expect(getColumnValues(2)).to.deep.equal(['11', '30', '31']); }); }); @@ -175,8 +173,8 @@ describe(' - Rows', () => { ); }; - it('should allow to reset rows with setRows and render after 100ms', () => { - render(); + it('should allow to throttle row update', () => { + render(); expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); const newRows = [ { @@ -190,14 +188,6 @@ describe(' - Rows', () => { expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); clock.tick(50); expect(getColumnValues()).to.deep.equal(['Asics']); - - apiRef.current.setRows(baselineProps.rows); - // Force an update before the 100ms - apiRef.current.forceUpdate(() => apiRef.current.state); - // Tradeoff, the value is YOLO - expect(getColumnValues()).to.deep.equal(['Nike']); - clock.tick(100); - expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); }); it('should allow to update row data', () => { @@ -205,7 +195,6 @@ describe(' - Rows', () => { apiRef.current.updateRows([{ id: 1, brand: 'Fila' }]); apiRef.current.updateRows([{ id: 0, brand: 'Pata' }]); apiRef.current.updateRows([{ id: 2, brand: 'Pum' }]); - clock.tick(100); expect(getColumnValues()).to.deep.equal(['Pata', 'Fila', 'Pum']); }); @@ -215,7 +204,6 @@ describe(' - Rows', () => { apiRef.current.updateRows([{ id: 0, brand: 'Pata' }]); apiRef.current.updateRows([{ id: 2, brand: 'Pum' }]); apiRef.current.updateRows([{ id: 3, brand: 'Jordan' }]); - clock.tick(100); expect(getColumnValues()).to.deep.equal(['Pata', 'Fila', 'Pum', 'Jordan']); }); @@ -227,7 +215,6 @@ describe(' - Rows', () => { { id: 2, brand: 'Pum' }, { id: 3, brand: 'Jordan' }, ]); - clock.tick(100); expect(getColumnValues()).to.deep.equal(['Pata', 'Fila', 'Pum', 'Jordan']); }); @@ -237,7 +224,6 @@ describe(' - Rows', () => { apiRef.current.updateRows([{ id: 0, brand: 'Apple' }]); apiRef.current.updateRows([{ id: 2, _action: 'delete' }]); apiRef.current.updateRows([{ id: 5, brand: 'Atari' }]); - clock.tick(100); expect(getColumnValues()).to.deep.equal(['Apple', 'Atari']); }); @@ -249,7 +235,6 @@ describe(' - Rows', () => { { id: 2, _action: 'delete' }, { id: 5, brand: 'Atari' }, ]); - clock.tick(100); expect(getColumnValues()).to.deep.equal(['Apple', 'Atari']); }); @@ -277,7 +262,6 @@ describe(' - Rows', () => { { idField: 2, _action: 'delete' }, { idField: 5, brand: 'Atari' }, ]); - clock.tick(100); expect(getColumnValues()).to.deep.equal(['Apple', 'Atari']); }); }); From e62ddc9ec66e713715973c414a24280d93e6d6c3 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 7 Sep 2021 18:01:21 +0200 Subject: [PATCH 036/390] Fix doc --- packages/grid/_modules_/grid/models/gridOptions.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/grid/_modules_/grid/models/gridOptions.tsx b/packages/grid/_modules_/grid/models/gridOptions.tsx index 6c0fe52aa0065..3d1230cc6c7f7 100644 --- a/packages/grid/_modules_/grid/models/gridOptions.tsx +++ b/packages/grid/_modules_/grid/models/gridOptions.tsx @@ -230,8 +230,8 @@ export interface GridSimpleOptions { */ sortingMode: GridFeatureMode; /** - * If the Grid receives an updates through `apiRef.current.updateRows`, `apiRef.current.setRows` or `props.rows`, it will wait this amount of time before applying the update - * It can be useful if you have a high update rate + * If positive, the Grid will throttle updates coming from `apiRef.current.updateRows` or `apiRef.current.setRows`. + * It can be useful if you have a high update rate but do not want to do heavy work like filtering / sorting or rendering on each individual update. * @default 0 */ throttleRowsMs: number; From 200851cba4ea6ee443661934e05a78c499cd60f4 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 7 Sep 2021 18:12:38 +0200 Subject: [PATCH 037/390] Fix tests --- .../src/tests/filtering.DataGridPro.test.tsx | 2 -- .../src/tests/sorting.DataGridPro.test.tsx | 17 +---------------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx index 97fec65e27143..ca2a06b1f9922 100644 --- a/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx @@ -112,7 +112,6 @@ describe(' - Filter', () => { }, ]; apiRef.current.setRows(newRows); - clock.tick(100); expect(getColumnValues()).to.deep.equal(['Asics']); }); @@ -120,7 +119,6 @@ describe(' - Filter', () => { render(); apiRef.current.updateRows([{ id: 1, brand: 'Fila' }]); apiRef.current.updateRows([{ id: 0, brand: 'Patagonia' }]); - clock.tick(100); expect(getColumnValues()).to.deep.equal(['Patagonia', 'Fila', 'Puma']); }); diff --git a/packages/grid/x-grid/src/tests/sorting.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/sorting.DataGridPro.test.tsx index 3c57d4d0935da..fc9099134a612 100644 --- a/packages/grid/x-grid/src/tests/sorting.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/sorting.DataGridPro.test.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { GridApiRef, GridComponentProps, GridSortModel, useGridApiRef } from '@mui/x-data-grid'; import { DataGridPro } from '@mui/x-data-grid-pro'; import { expect } from 'chai'; -import { spy, useFakeTimers } from 'sinon'; +import { spy } from 'sinon'; import { getColumnValues, getCell, getColumnHeaderCell } from 'test/utils/helperFn'; import { createClientRenderStrictMode, @@ -18,7 +18,6 @@ import { useData } from 'packages/storybook/src/hooks/useData'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); describe(' - Sorting', () => { - let clock; const baselineProps = { autoHeight: isJSDOM, rows: [ @@ -41,14 +40,6 @@ describe(' - Sorting', () => { columns: [{ field: 'brand' }, { field: 'year', type: 'number' }], }; - beforeEach(() => { - clock = useFakeTimers(); - }); - - afterEach(() => { - clock.restore(); - }); - // TODO v5: replace with createClientRender const render = createClientRenderStrictMode(); @@ -97,7 +88,6 @@ describe(' - Sorting', () => { }, ]; apiRef.current.setRows(newRows); - clock.tick(100); expect(getColumnValues()).to.deep.equal(['Asics', 'Hugo', 'RedBull']); }); @@ -105,7 +95,6 @@ describe(' - Sorting', () => { renderBrandSortedAsc(); apiRef.current.updateRows([{ id: 1, brand: 'Fila' }]); apiRef.current.updateRows([{ id: 0, brand: 'Patagonia' }]); - clock.tick(100); expect(getColumnValues()).to.deep.equal(['Fila', 'Patagonia', 'Puma']); }); @@ -186,10 +175,6 @@ describe(' - Sorting', () => { }); describe('performance', () => { - beforeEach(() => { - clock.restore(); - }); - it('should sort 5,000 rows in less than 200 ms', async function test() { // It's simpler to only run the performance test in a single controlled environment. if (!/HeadlessChrome/.test(window.navigator.userAgent)) { From 86d88cd605f89117c8ad57b20ead1b7328ed04c6 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 8 Sep 2021 14:33:11 +0200 Subject: [PATCH 038/390] Fix --- .../grid/components/columnHeaders/GridColumnHeaders.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx index 6440bbfec9931..6d4bfdf1d7d11 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx @@ -21,7 +21,7 @@ export const gridScrollbarStateSelector = (state: GridState) => state.scrollBar; type OwnerState = { classes?: GridComponentProps['classes']; - dragCol: string; + dragCol: string | undefined; }; const useUtilityClasses = (ownerState: OwnerState) => { From 9275adda08f50d9e02c503c7d6c7d236541fce76 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 8 Sep 2021 15:16:12 +0200 Subject: [PATCH 039/390] Add tests and docs --- .../data-grid/rows/ThrottledRowsGrid.js | 53 ++++++++++++++++++ .../data-grid/rows/ThrottledRowsGrid.tsx | 53 ++++++++++++++++++ .../pages/components/data-grid/rows/rows.md | 9 +++ .../grid/hooks/features/rows/gridRowsState.ts | 2 +- .../grid/hooks/features/rows/useGridRows.ts | 19 +++---- .../_modules_/grid/models/gridOptions.tsx | 2 +- .../src/tests/rows.DataGrid.test.tsx | 56 ++++++++++++++++++- 7 files changed, 179 insertions(+), 15 deletions(-) create mode 100644 docs/src/pages/components/data-grid/rows/ThrottledRowsGrid.js create mode 100644 docs/src/pages/components/data-grid/rows/ThrottledRowsGrid.tsx diff --git a/docs/src/pages/components/data-grid/rows/ThrottledRowsGrid.js b/docs/src/pages/components/data-grid/rows/ThrottledRowsGrid.js new file mode 100644 index 0000000000000..54f91b47427d9 --- /dev/null +++ b/docs/src/pages/components/data-grid/rows/ThrottledRowsGrid.js @@ -0,0 +1,53 @@ +import * as React from 'react'; +import { DataGridPro, useGridApiRef } from '@mui/x-data-grid-pro'; +import { interval } from 'rxjs'; +import { randomInt, randomUserName } from '@mui/x-data-grid-generator'; + +const columns = [ + { field: 'id' }, + { field: 'username', width: 150 }, + { field: 'age', width: 80, type: 'number' }, +]; + +const rows = [ + { id: 1, username: randomUserName(), age: randomInt(10, 80) }, + { id: 2, username: randomUserName(), age: randomInt(10, 80) }, + { id: 3, username: randomUserName(), age: randomInt(10, 80) }, + { id: 4, username: randomUserName(), age: randomInt(10, 80) }, +]; + +export default function ApiRefRowsGrid() { + const apiRef = useGridApiRef(); + + React.useEffect(() => { + const subscription = interval(10).subscribe(() => { + apiRef.current.updateRows([ + { + id: randomInt(1, 4), + username: randomUserName(), + age: randomInt(10, 80), + }, + { + id: randomInt(1, 4), + username: randomUserName(), + age: randomInt(10, 80), + }, + ]); + }); + + return () => { + subscription.unsubscribe(); + }; + }, [apiRef]); + + return ( +
+ +
+ ); +} diff --git a/docs/src/pages/components/data-grid/rows/ThrottledRowsGrid.tsx b/docs/src/pages/components/data-grid/rows/ThrottledRowsGrid.tsx new file mode 100644 index 0000000000000..54f91b47427d9 --- /dev/null +++ b/docs/src/pages/components/data-grid/rows/ThrottledRowsGrid.tsx @@ -0,0 +1,53 @@ +import * as React from 'react'; +import { DataGridPro, useGridApiRef } from '@mui/x-data-grid-pro'; +import { interval } from 'rxjs'; +import { randomInt, randomUserName } from '@mui/x-data-grid-generator'; + +const columns = [ + { field: 'id' }, + { field: 'username', width: 150 }, + { field: 'age', width: 80, type: 'number' }, +]; + +const rows = [ + { id: 1, username: randomUserName(), age: randomInt(10, 80) }, + { id: 2, username: randomUserName(), age: randomInt(10, 80) }, + { id: 3, username: randomUserName(), age: randomInt(10, 80) }, + { id: 4, username: randomUserName(), age: randomInt(10, 80) }, +]; + +export default function ApiRefRowsGrid() { + const apiRef = useGridApiRef(); + + React.useEffect(() => { + const subscription = interval(10).subscribe(() => { + apiRef.current.updateRows([ + { + id: randomInt(1, 4), + username: randomUserName(), + age: randomInt(10, 80), + }, + { + id: randomInt(1, 4), + username: randomUserName(), + age: randomInt(10, 80), + }, + ]); + }); + + return () => { + subscription.unsubscribe(); + }; + }, [apiRef]); + + return ( +
+ +
+ ); +} diff --git a/docs/src/pages/components/data-grid/rows/rows.md b/docs/src/pages/components/data-grid/rows/rows.md index 9a6e4266cd7b3..b46b67901be9c 100644 --- a/docs/src/pages/components/data-grid/rows/rows.md +++ b/docs/src/pages/components/data-grid/rows/rows.md @@ -53,6 +53,15 @@ Alternatively, if you would like to delete a row, you would need to pass an extr apiRef.current.updateRows([{ id: 1, _action: 'delete' }]); ``` +### Throttling grid update + +If you have high frequency updates, but you want to keep good performances, you can pass set the `throttleRowsMs` prop to define the maximum update frequency. +When receiving updates more frequent than this threshold, the Grid will wait before applying the new values to avoid triggering heavy work too many times. + +The following demo updates the rows every 10ms but only apply them to the Grid every 500ms. + +{{"demo": "pages/components/data-grid/rows/ThrottledRowsGrid.js", "bg": "inline"}} + ## Row height By default, the rows have a height of 52 pixels. diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts index 14d85c25bcbca..f3aeb4d3b518f 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts @@ -9,7 +9,7 @@ export interface GridRowsState { export interface GridRowsInternalCache { state: GridRowsState; timeout: NodeJS.Timeout | null; - lastUpdateMs: number; + lastUpdateMs: number | null; } export const getInitialGridRowState: () => GridRowsState = () => ({ diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index a8c2f5c2285e5..2414e93c95be1 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -67,7 +67,7 @@ export const useGridRows = ( rowsCache.current = { state: getInitialGridRowState(), timeout: null, - lastUpdateMs: 0, + lastUpdateMs: null, }; } @@ -97,7 +97,7 @@ export const useGridRows = ( ); const throttledRowsChange = React.useCallback( - (newState: GridRowsState, throttle: boolean) => { + (newState: GridRowsState) => { const run = () => { rowsCache.current.timeout = null; rowsCache.current.lastUpdateMs = Date.now(); @@ -114,9 +114,11 @@ export const useGridRows = ( rowsCache.current.timeout = null; const throttleRemainingTimeMs = - props.throttleRowsMs - (Date.now() - rowsCache.current.lastUpdateMs); + rowsCache.current.lastUpdateMs === null + ? 0 + : props.throttleRowsMs - (Date.now() - rowsCache.current.lastUpdateMs); - if (throttle && throttleRemainingTimeMs > 0) { + if (throttleRemainingTimeMs > 0) { rowsCache.current.timeout = setTimeout(run, throttleRemainingTimeMs); } else { run(); @@ -128,7 +130,7 @@ export const useGridRows = ( const setRows = React.useCallback( (rows) => { logger.debug(`Updating all rows, new length ${rows.length}`); - throttledRowsChange(convertGridRowsPropToState(rows, props.rowCount, props.getRowId), true); + throttledRowsChange(convertGridRowsPropToState(rows, props.rowCount, props.getRowId)); }, [logger, throttledRowsChange, props.rowCount, props.getRowId], ); @@ -213,7 +215,7 @@ export const useGridRows = ( totalRowCount, }; - throttledRowsChange(state, true); + throttledRowsChange(state); }, [apiRef, props.getRowId, props.rowCount, throttledRowsChange], ); @@ -245,10 +247,7 @@ export const useGridRows = ( React.useEffect(() => { logger.debug(`Updating all rows, new length ${props.rows.length}`); - throttledRowsChange( - convertGridRowsPropToState(props.rows, props.rowCount, props.getRowId), - false, - ); + throttledRowsChange(convertGridRowsPropToState(props.rows, props.rowCount, props.getRowId)); }, [props.rows, props.rowCount, props.getRowId, logger, throttledRowsChange]); const rowApi: GridRowApi = { diff --git a/packages/grid/_modules_/grid/models/gridOptions.tsx b/packages/grid/_modules_/grid/models/gridOptions.tsx index 3d1230cc6c7f7..981ec010a7d51 100644 --- a/packages/grid/_modules_/grid/models/gridOptions.tsx +++ b/packages/grid/_modules_/grid/models/gridOptions.tsx @@ -230,7 +230,7 @@ export interface GridSimpleOptions { */ sortingMode: GridFeatureMode; /** - * If positive, the Grid will throttle updates coming from `apiRef.current.updateRows` or `apiRef.current.setRows`. + * If positive, the Grid will throttle updates coming from `props.rows`, `apiRef.current.updateRows` or `apiRef.current.setRows`. * It can be useful if you have a high update rate but do not want to do heavy work like filtering / sorting or rendering on each individual update. * @default 0 */ diff --git a/packages/grid/data-grid/src/tests/rows.DataGrid.test.tsx b/packages/grid/data-grid/src/tests/rows.DataGrid.test.tsx index 05c5142032528..b623d6df97496 100644 --- a/packages/grid/data-grid/src/tests/rows.DataGrid.test.tsx +++ b/packages/grid/data-grid/src/tests/rows.DataGrid.test.tsx @@ -5,10 +5,11 @@ import { fireEvent, } from 'test/utils'; import { expect } from 'chai'; -import { spy } from 'sinon'; +import { SinonFakeTimers, spy, useFakeTimers } from 'sinon'; import Portal from '@material-ui/core/Portal'; -import { DataGrid } from '@mui/x-data-grid'; +import { DataGrid, DataGridProps } from '@mui/x-data-grid'; import { getColumnValues, getRow } from 'test/utils/helperFn'; +import { getData } from 'storybook/src/data/data-service'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); @@ -38,7 +39,7 @@ describe(' - Rows', () => { columns: [{ field: 'clientId' }, { field: 'first' }, { field: 'age' }], }; - describe('getRowId', () => { + describe('props: getRowId', () => { it('should allow to select a field as id', () => { const getRowId = (row) => `${row.clientId}`; render( @@ -50,6 +51,55 @@ describe(' - Rows', () => { }); }); + describe('props: rows', () => { + let clock: SinonFakeTimers; + + beforeEach(() => { + clock = useFakeTimers(); + }); + + afterEach(() => { + clock.restore(); + }); + + it('should support new dataset', () => { + const { rows, columns } = getData(5, 2); + + const Test = (props: Pick) => ( +
+ +
+ ); + + const { setProps } = render(); + expect(getColumnValues(0)).to.deep.equal(['0', '1']); + setProps({ rows }); + expect(getColumnValues(0)).to.deep.equal(['0', '1', '2', '3', '4']); + }); + + it('should support new dataset throttle', () => { + const { rows, columns } = getData(5, 2); + + const Test = (props: Pick) => ( +
+ +
+ ); + + const { setProps } = render(); + + expect(getColumnValues(0)).to.deep.equal(['0', '1']); + clock.tick(150); + setProps({ rows: rows.slice(0, 3) }); + expect(getColumnValues(0)).to.deep.equal(['0', '1', '2']); + clock.tick(50); + setProps({ rows }); + expect(getColumnValues(0)).to.deep.equal(['0', '1', '2']); + clock.tick(50); + expect(getColumnValues(0)).to.deep.equal(['0', '1', '2', '3', '4']); + }); + }); + it('should ignore events coming from a portal in the cell', () => { const handleRowClick = spy(); const InputCell = () => ; From 5bfdc4253e502ae3037cb5340d48f008c1c139dc Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 8 Sep 2021 16:27:55 +0200 Subject: [PATCH 040/390] Undo useGKN modif --- .../features/keyboard/useGridKeyboardNavigation.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts index 9df9678102321..5687f8ed34471 100644 --- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts +++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts @@ -76,6 +76,8 @@ export const useGridKeyboardNavigation = ( ): void => { const logger = useGridLogger(apiRef, 'useGridKeyboardNavigation'); const paginationState = useGridSelector(apiRef, gridPaginationSelector); + const totalRowCount = useGridSelector(apiRef, gridRowCountSelector); + const colCount = useGridSelector(apiRef, visibleGridColumnsLengthSelector); const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); const visibleSortedRowsAsArray = useGridSelector(apiRef, visibleSortedGridRowsAsArraySelector); @@ -92,9 +94,6 @@ export const useGridKeyboardNavigation = ( const navigateCells = React.useCallback( (params: GridCellParams, event: React.KeyboardEvent) => { event.preventDefault(); - const colCount = visibleGridColumnsLengthSelector(apiRef.current.state); - const totalRowCount = gridRowCountSelector(apiRef.current.state); - const colIndex = apiRef.current.getColumnIndex(params.field); const rowIndex = visibleSortedRowsAsArray.findIndex(([id]) => id === params.id); @@ -163,9 +162,11 @@ export const useGridKeyboardNavigation = ( [ apiRef, visibleSortedRowsAsArray, + totalRowCount, props.pagination, paginationState.pageSize, paginationState.page, + colCount, logger, containerSizes, ], @@ -173,8 +174,6 @@ export const useGridKeyboardNavigation = ( const navigateColumnHeaders = React.useCallback( (params: GridColumnHeaderParams, event: React.KeyboardEvent) => { - const colCount = visibleGridColumnsLengthSelector(apiRef.current.state); - event.preventDefault(); let nextColumnHeaderIndexes: GridColumnHeaderIndexCoordinates | null; const colIndex = apiRef.current.getColumnIndex(params.field); @@ -219,7 +218,7 @@ export const useGridKeyboardNavigation = ( const field = apiRef.current.getVisibleColumns()[nextColumnHeaderIndexes.colIndex].field; apiRef.current.setColumnHeaderFocus(field, event); }, - [apiRef, containerSizes, logger, visibleSortedRowsAsArray], + [apiRef, colCount, containerSizes, logger, visibleSortedRowsAsArray], ); useGridApiEventHandler(apiRef, GridEvents.cellNavigationKeyDown, navigateCells); From 4d5a399b48ac4d571d256c9a8518441cc4fb6a06 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 9 Sep 2021 11:34:48 +0200 Subject: [PATCH 041/390] Finish --- .../hooks/features/columns/useGridColumns.ts | 60 ++++++++++--------- .../grid/hooks/features/core/gridState.ts | 30 +--------- .../grid/hooks/features/core/useGridApi.ts | 4 +- .../features/virtualization/renderingState.ts | 12 +--- .../virtualization/useGridVirtualization.ts | 17 +++++- .../grid/hooks/root/useGridContainerProps.ts | 10 +++- 6 files changed, 62 insertions(+), 71 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts index 4b5f53852e91e..1cc889d66365c 100644 --- a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts +++ b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts @@ -16,12 +16,13 @@ import { GridColumnOrderChangeParams } from '../../../models/params/gridColumnOr import { mergeGridColTypes } from '../../../utils/mergeUtils'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; -import { useGridSelector } from '../core/useGridSelector'; import { GridLocaleText, GridTranslationKeys } from '../../../models/api/gridLocaleTextApi'; import { useGridState } from '../core/useGridState'; import { + allGridColumnsFieldsSelector, allGridColumnsSelector, gridColumnsMetaSelector, + gridColumnsSelector, visibleGridColumnsSelector, } from './gridColumnsSelector'; import { useGridApiOptionHandler } from '../../root/useGridApiEventHandler'; @@ -146,7 +147,9 @@ const useUtilityClasses = (ownerState: OwnerState) => { /** * @requires useGridParamsApi (method) + * @requires useGridContainerProps (state) * TODO: Impossible priority - useGridParamsApi also needs to be after useGridColumns + * TODO: Impossible priority - useGridContainerProps also needs to be after useGridColumns */ export function useGridColumns( apiRef: GridApiRef, @@ -186,10 +189,10 @@ export function useGridColumns( columns: columnState, }; }); - const [gridState, setGridState, forceUpdate] = useGridState(apiRef); - const columnsMeta = useGridSelector(apiRef, gridColumnsMetaSelector); - const allColumns = useGridSelector(apiRef, allGridColumnsSelector); - const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); + const [, setGridState, forceUpdate] = useGridState(apiRef); + + // It should be fixed by removing `viewportSizes` from the state + const viewportSizes = apiRef.current.state.viewportSizes?.width ?? 0; const setGridColumnsState = React.useCallback( (columnsState: GridColumnsState, emit = true) => { @@ -211,32 +214,35 @@ export function useGridColumns( ); const getAllColumns = React.useCallback( - () => allColumns, - [allColumns], + () => allGridColumnsSelector(apiRef.current.state), + [apiRef], ); const getVisibleColumns = React.useCallback( - () => visibleColumns, - [visibleColumns], + () => visibleGridColumnsSelector(apiRef.current.state), + [apiRef], ); const getColumnsMeta = React.useCallback( - () => columnsMeta, - [columnsMeta], + () => gridColumnsMetaSelector(apiRef.current.state), + [apiRef], ); const getColumnIndex = React.useCallback( - (field: string, useVisibleColumns: boolean = true): number => - useVisibleColumns - ? visibleColumns.findIndex((col) => col.field === field) - : allColumns.findIndex((col) => col.field === field), - [allColumns, visibleColumns], + (field: string, useVisibleColumns: boolean = true): number => { + const columns = useVisibleColumns + ? visibleGridColumnsSelector(apiRef.current.state) + : allGridColumnsSelector(apiRef.current.state); + + return columns.findIndex((col) => col.field === field); + }, + [apiRef], ); const getColumnPosition: (field: string) => number = React.useCallback( (field) => { const index = getColumnIndex(field); - return columnsMeta.positions[index]; + return gridColumnsMetaSelector(apiRef.current.state).positions[index]; }, - [columnsMeta.positions, getColumnIndex], + [apiRef, getColumnIndex], ); const setColumnsState = React.useCallback( @@ -293,16 +299,17 @@ export function useGridColumns( const setColumnIndex = React.useCallback( (field: string, targetIndexPosition: number) => { - const oldIndexPosition = gridState.columns.all.findIndex((col) => col === field); + const allColumns = allGridColumnsFieldsSelector(apiRef.current.state); + const oldIndexPosition = allColumns.findIndex((col) => col === field); if (oldIndexPosition === targetIndexPosition) { return; } logger.debug(`Moving column ${field} to index ${targetIndexPosition}`); - const updatedColumns = [...gridState.columns.all]; + const updatedColumns = [...allColumns]; updatedColumns.splice(targetIndexPosition, 0, updatedColumns.splice(oldIndexPosition, 1)[0]); - setGridColumnsState({ ...gridState.columns, all: updatedColumns }); + setGridColumnsState({ ...gridColumnsSelector(apiRef.current.state), all: updatedColumns }); const params: GridColumnOrderChangeParams = { field, @@ -313,7 +320,7 @@ export function useGridColumns( }; apiRef.current.publishEvent(GridEvents.columnOrderChange, params); }, - [apiRef, gridState.columns, logger, setGridColumnsState], + [apiRef, logger, setGridColumnsState], ); const setColumnWidth = React.useCallback( @@ -372,20 +379,19 @@ export function useGridColumns( classes, ]); + // TODO: We need optional chaining due to impossible hook order (see description of this hook) React.useEffect(() => { - logger.debug( - `GridColumns gridState.viewportSizes.width, changed ${gridState.viewportSizes.width}`, - ); + logger.debug(`GridColumns gridState.viewportSizes.width, changed ${viewportSizes}`); // This hook is meant to update the column's width when the viewport changes // We can skip the whole block if the width is missing - if (gridState.viewportSizes.width === 0) { + if (viewportSizes === 0) { return; } // Avoid dependency on gridState as I only want to update cols when viewport size changed. setColumnsState(apiRef.current.state.columns); - }, [apiRef, setColumnsState, gridState.viewportSizes.width, logger]); + }, [apiRef, setColumnsState, viewportSizes, logger]); // Grid Option Handlers useGridApiOptionHandler( diff --git a/packages/grid/_modules_/grid/hooks/features/core/gridState.ts b/packages/grid/_modules_/grid/hooks/features/core/gridState.ts index fc009d8691886..6c7488f0e511a 100644 --- a/packages/grid/_modules_/grid/hooks/features/core/gridState.ts +++ b/packages/grid/_modules_/grid/hooks/features/core/gridState.ts @@ -16,10 +16,7 @@ import { GridPreferencePanelState } from '../preferencesPanel/gridPreferencePane import { InternalGridRowsState } from '../rows/gridRowsState'; import { GridSelectionModel } from '../../../models/gridSelectionModel'; import { GridSortingState } from '../sorting/gridSortingState'; -import { - getInitialGridRenderingState, - InternalRenderingState, -} from '../virtualization/renderingState'; +import { GridRenderingState } from '../virtualization/renderingState'; import { GridPaginationState } from '../pagination/gridPaginationState'; export interface GridState { @@ -30,7 +27,7 @@ export interface GridState { columnReorder: GridColumnReorderState; columnResize: GridColumnResizeState; columnMenu: GridColumnMenuState; - rendering: InternalRenderingState; + rendering: GridRenderingState; containerSizes: GridContainerProps | null; viewportSizes: GridViewportSizeState; scrollBar: GridScrollBarState; @@ -44,26 +41,3 @@ export interface GridState { density: GridDensityState; error?: any; } - -export const getInitialGridState = (): GridState => - ({ - // rows: getInitialGridRowState(), - // editRows: {}, - // pagination: getInitialPaginationState(), - // columns: getInitialGridColumnsState(), - // columnReorder: getInitialGridColumnReorderState(), - // columnResize: getInitialGridColumnResizeState(), - rendering: getInitialGridRenderingState(), - containerSizes: null, - scrollBar: { hasScrollX: false, hasScrollY: false, sizes: { x: 0, y: 0 } }, - viewportSizes: { width: 0, height: 1 }, - // sorting: getInitialGridSortingState(), - // focus: { cell: null, columnHeader: null }, - // tabIndex: { cell: null, columnHeader: null }, - // selection: [], - // filter: getInitialGridFilterState(), - // columnMenu: { open: false }, - // preferencePanel: { open: false }, - // visibleRows: getInitialVisibleGridRowsState(), - // density: getInitialGridDensityState(), - } as any as GridState); diff --git a/packages/grid/_modules_/grid/hooks/features/core/useGridApi.ts b/packages/grid/_modules_/grid/hooks/features/core/useGridApi.ts index 0f1b98cd73e08..03a1362c8ed51 100644 --- a/packages/grid/_modules_/grid/hooks/features/core/useGridApi.ts +++ b/packages/grid/_modules_/grid/hooks/features/core/useGridApi.ts @@ -5,7 +5,7 @@ import { GridApi } from '../../../models/api/gridApi'; import { isFunction } from '../../../utils/utils'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; -import { getInitialGridState, GridState } from './gridState'; +import { GridState } from './gridState'; export const useGridApi = (apiRef: GridApiRef): GridApi => { const logger = useGridLogger(apiRef, 'useGridApi'); @@ -13,7 +13,7 @@ export const useGridApi = (apiRef: GridApiRef): GridApi => { if (!apiRef.current.state) { logger.info('Initialising state.'); - apiRef.current.state = getInitialGridState(); + apiRef.current.state = {} as GridState; apiRef.current.forceUpdate = forceUpdate; } diff --git a/packages/grid/_modules_/grid/hooks/features/virtualization/renderingState.ts b/packages/grid/_modules_/grid/hooks/features/virtualization/renderingState.ts index 1e570b4f30afe..bcf7a7da7b338 100644 --- a/packages/grid/_modules_/grid/hooks/features/virtualization/renderingState.ts +++ b/packages/grid/_modules_/grid/hooks/features/virtualization/renderingState.ts @@ -1,20 +1,10 @@ import { GridScrollParams } from '../../../models/params/gridScrollParams'; import { GridRenderContextProps } from '../../../models/gridRenderContextProps'; -export interface InternalRenderingState { +export interface GridRenderingState { virtualPage: number; virtualRowsCount: number; renderContext: Partial | null; realScroll: GridScrollParams; renderingZoneScroll: GridScrollParams; } - -export const getInitialGridRenderingState = (): InternalRenderingState => { - return { - realScroll: { left: 0, top: 0 }, - renderContext: null, - renderingZoneScroll: { left: 0, top: 0 }, - virtualPage: 0, - virtualRowsCount: 0, - }; -}; diff --git a/packages/grid/_modules_/grid/hooks/features/virtualization/useGridVirtualization.ts b/packages/grid/_modules_/grid/hooks/features/virtualization/useGridVirtualization.ts index c24a083fb1e60..29acaebd674da 100644 --- a/packages/grid/_modules_/grid/hooks/features/virtualization/useGridVirtualization.ts +++ b/packages/grid/_modules_/grid/hooks/features/virtualization/useGridVirtualization.ts @@ -22,9 +22,10 @@ import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useNativeEventListener } from '../../root/useNativeEventListener'; import { useGridLogger } from '../../utils/useGridLogger'; import { useGridScrollFn } from '../../utils/useGridScrollFn'; -import { InternalRenderingState } from './renderingState'; +import { GridRenderingState } from './renderingState'; import { GridComponentProps } from '../../../GridComponentProps'; import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; +import { useGridStateInit } from '../../utils/useGridStateInit'; // Uses binary search to avoid looping through all possible positions function getIdxFromScroll( @@ -66,6 +67,18 @@ export const useGridVirtualization = ( >, ): void => { const logger = useGridLogger(apiRef, 'useGridVirtualization'); + + useGridStateInit(apiRef, (state) => ({ + ...state, + rendering: { + realScroll: { left: 0, top: 0 }, + renderContext: null, + renderingZoneScroll: { left: 0, top: 0 }, + virtualPage: 0, + virtualRowsCount: 0, + }, + })); + const colRef = apiRef.current.columnHeadersElementRef!; const windowRef = apiRef.current.windowRef!; const renderingZoneRef = apiRef.current.renderingZoneRef!; @@ -82,7 +95,7 @@ export const useGridVirtualization = ( const [scrollTo] = useGridScrollFn(apiRef, renderingZoneRef, colRef); const setRenderingState = React.useCallback( - (newState: Partial) => { + (newState: Partial) => { let stateChanged = false; setGridState((state) => { const currentRenderingState = { ...state.rendering, ...newState }; diff --git a/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts b/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts index c8d89e55beb9a..ca1f55c2b2968 100644 --- a/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts +++ b/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts @@ -19,6 +19,7 @@ import { gridPaginationSelector } from '../features/pagination/gridPaginationSel import { useGridLogger } from '../utils/useGridLogger'; import { useGridApiEventHandler } from './useGridApiEventHandler'; import { GridComponentProps } from '../../GridComponentProps'; +import { useGridStateInit } from '../utils/useGridStateInit'; function getScrollbarSize(doc: Document, element: HTMLElement): number { const scrollDiv = doc.createElement('div'); @@ -35,7 +36,6 @@ function getScrollbarSize(doc: Document, element: HTMLElement): number { } /** - * @requires useOptionsProp (state) * @requires useGridDensity (state) * @requires useGridColumns (state) * @requires useGridFilter (state) @@ -57,6 +57,14 @@ export const useGridContainerProps = ( >, ) => { const logger = useGridLogger(apiRef, 'useGridContainerProps'); + + useGridStateInit(apiRef, (state) => ({ + ...state, + containerSizes: null, + viewportSizes: { width: 0, height: 1 }, + scrollBar: { hasScrollX: false, hasScrollY: false, sizes: { x: 0, y: 0 } }, + })); + const [gridState, setGridState, forceUpdate] = useGridState(apiRef); const windowSizesRef = React.useRef({ width: 0, height: 0 }); const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector); From 41d427f48cd77b7454c0cb747d6605785fb86ecc Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 9 Sep 2021 12:06:30 +0200 Subject: [PATCH 042/390] PropTypes --- packages/grid/data-grid/src/DataGrid.tsx | 1 + packages/grid/x-grid/src/DataGridPro.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/grid/data-grid/src/DataGrid.tsx b/packages/grid/data-grid/src/DataGrid.tsx index 9fee5e62ccb62..5d2196be73dd6 100644 --- a/packages/grid/data-grid/src/DataGrid.tsx +++ b/packages/grid/data-grid/src/DataGrid.tsx @@ -437,6 +437,7 @@ DataGridRaw.propTypes = { /** * Callback fired when an exception is thrown in the grid. * @param {any} args The arguments passed to the `showError` call. + * @param {MuiEvent<{}>} event The event object. * @param {GridCallbackDetails} details Additional details for this callback. */ onError: PropTypes.func, diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index d703eb35c097d..5acbc121e5593 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -483,6 +483,7 @@ DataGridProRaw.propTypes = { /** * Callback fired when an exception is thrown in the grid. * @param {any} args The arguments passed to the `showError` call. + * @param {MuiEvent<{}>} event The event object. * @param {GridCallbackDetails} details Additional details for this callback. */ onError: PropTypes.func, From e1f774900a7dffe39cb5a0d2a4d1a61f90934da9 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 10 Sep 2021 12:12:39 +0200 Subject: [PATCH 043/390] Update proptypes --- packages/grid/data-grid/src/DataGrid.tsx | 6 ++++++ packages/grid/x-grid/src/DataGridPro.tsx | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/packages/grid/data-grid/src/DataGrid.tsx b/packages/grid/data-grid/src/DataGrid.tsx index 404583aefc659..c741ed3972e4b 100644 --- a/packages/grid/data-grid/src/DataGrid.tsx +++ b/packages/grid/data-grid/src/DataGrid.tsx @@ -668,4 +668,10 @@ DataGridRaw.propTypes = { * @ignore */ style: PropTypes.object, + /** + * If positive, the Grid will throttle updates coming from `props.rows`, `apiRef.current.updateRows` or `apiRef.current.setRows`. + * It can be useful if you have a high update rate but do not want to do heavy work like filtering / sorting or rendering on each individual update. + * @default 0 + */ + throttleRowsMs: PropTypes.number, } as any; diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index 35f95f91d14ae..d5437229eaeda 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -704,4 +704,10 @@ DataGridProRaw.propTypes = { * @ignore */ style: PropTypes.object, + /** + * If positive, the Grid will throttle updates coming from `props.rows`, `apiRef.current.updateRows` or `apiRef.current.setRows`. + * It can be useful if you have a high update rate but do not want to do heavy work like filtering / sorting or rendering on each individual update. + * @default 0 + */ + throttleRowsMs: PropTypes.number, } as any; From 942cfc8d4483830dbceaf6d3ff00c7b8537c74c3 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 10 Sep 2021 12:16:05 +0200 Subject: [PATCH 044/390] Make the throttling pro --- .../pages/components/data-grid/rows/rows.md | 2 +- packages/grid/data-grid/src/DataGridProps.ts | 1 + .../src/tests/rows.DataGrid.test.tsx | 22 ---------------- .../grid/data-grid/src/useDataGridProps.ts | 1 + .../src/tests/rows.DataGridPro.test.tsx | 25 +++++++++++++++++++ 5 files changed, 28 insertions(+), 23 deletions(-) diff --git a/docs/src/pages/components/data-grid/rows/rows.md b/docs/src/pages/components/data-grid/rows/rows.md index b46b67901be9c..1b7526da440c8 100644 --- a/docs/src/pages/components/data-grid/rows/rows.md +++ b/docs/src/pages/components/data-grid/rows/rows.md @@ -53,7 +53,7 @@ Alternatively, if you would like to delete a row, you would need to pass an extr apiRef.current.updateRows([{ id: 1, _action: 'delete' }]); ``` -### Throttling grid update +### Throttling grid update [](https://material-ui.com/store/items/material-ui-pro/) If you have high frequency updates, but you want to keep good performances, you can pass set the `throttleRowsMs` prop to define the maximum update frequency. When receiving updates more frequent than this threshold, the Grid will wait before applying the new values to avoid triggering heavy work too many times. diff --git a/packages/grid/data-grid/src/DataGridProps.ts b/packages/grid/data-grid/src/DataGridProps.ts index d147c8ed206b9..dac80ef5b90e9 100644 --- a/packages/grid/data-grid/src/DataGridProps.ts +++ b/packages/grid/data-grid/src/DataGridProps.ts @@ -14,6 +14,7 @@ export type DataGridProps = Omit< | 'disableMultipleColumnsFiltering' | 'disableMultipleColumnsSorting' | 'disableMultipleSelection' + | 'throttleRowsMs' | 'hideFooterRowCount' | 'options' | 'onRowsScrollEnd' diff --git a/packages/grid/data-grid/src/tests/rows.DataGrid.test.tsx b/packages/grid/data-grid/src/tests/rows.DataGrid.test.tsx index f8d1410497953..18454573afb67 100644 --- a/packages/grid/data-grid/src/tests/rows.DataGrid.test.tsx +++ b/packages/grid/data-grid/src/tests/rows.DataGrid.test.tsx @@ -80,28 +80,6 @@ describe(' - Rows', () => { setProps({ rows }); expect(getColumnValues(0)).to.deep.equal(['0', '1', '2', '3', '4']); }); - - it('should support new dataset throttle', () => { - const { rows, columns } = getData(5, 2); - - const Test = (props: Pick) => ( -
- -
- ); - - const { setProps } = render(); - - expect(getColumnValues(0)).to.deep.equal(['0', '1']); - clock.tick(150); - setProps({ rows: rows.slice(0, 3) }); - expect(getColumnValues(0)).to.deep.equal(['0', '1', '2']); - clock.tick(50); - setProps({ rows }); - expect(getColumnValues(0)).to.deep.equal(['0', '1', '2']); - clock.tick(50); - expect(getColumnValues(0)).to.deep.equal(['0', '1', '2', '3', '4']); - }); }); it('should ignore events coming from a portal in the cell', () => { diff --git a/packages/grid/data-grid/src/useDataGridProps.ts b/packages/grid/data-grid/src/useDataGridProps.ts index 2ccee83158201..e69059943515b 100644 --- a/packages/grid/data-grid/src/useDataGridProps.ts +++ b/packages/grid/data-grid/src/useDataGridProps.ts @@ -16,6 +16,7 @@ const FORCED_PROPS: { [key in ForcedPropsKey]-?: GridInputComponentProps[key] } disableMultipleColumnsFiltering: true, disableMultipleColumnsSorting: true, disableMultipleSelection: true, + throttleRowsMs: undefined, hideFooterRowCount: false, pagination: true, onRowsScrollEnd: undefined, diff --git a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx index 47e50dcbb5499..b780ac365ed1a 100644 --- a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx @@ -16,6 +16,7 @@ import { DataGridProProps, } from '@mui/x-data-grid-pro'; import { useData } from 'packages/storybook/src/hooks/useData'; +import { DataGridProps} from "@mui/x-data-grid"; const isJSDOM = /jsdom/.test(window.navigator.userAgent); @@ -134,6 +135,30 @@ describe(' - Rows', () => { }); }); + describe('props: rows', () => { + it('should support new dataset throttle', () => { + const { rows, columns } = getData(5, 2); + + const Test = (props: Pick) => ( +
+ +
+ ); + + const { setProps } = render(); + + expect(getColumnValues(0)).to.deep.equal(['0', '1']); + clock.tick(150); + setProps({ rows: rows.slice(0, 3) }); + expect(getColumnValues(0)).to.deep.equal(['0', '1', '2']); + clock.tick(50); + setProps({ rows }); + expect(getColumnValues(0)).to.deep.equal(['0', '1', '2']); + clock.tick(50); + expect(getColumnValues(0)).to.deep.equal(['0', '1', '2', '3', '4']); + }); + }) + describe('updateRows', () => { beforeEach(() => { clock = useFakeTimers(); From e45684a453e98393b17eb16dec448f626fb164db Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 10 Sep 2021 12:17:33 +0200 Subject: [PATCH 045/390] Fix --- .../grid/x-grid/src/tests/rows.DataGridPro.test.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx index b780ac365ed1a..604123521f01e 100644 --- a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx @@ -16,7 +16,8 @@ import { DataGridProProps, } from '@mui/x-data-grid-pro'; import { useData } from 'packages/storybook/src/hooks/useData'; -import { DataGridProps} from "@mui/x-data-grid"; +import { DataGridProps } from '@mui/x-data-grid'; +import { getData } from 'storybook/src/data/data-service'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); @@ -140,9 +141,9 @@ describe(' - Rows', () => { const { rows, columns } = getData(5, 2); const Test = (props: Pick) => ( -
- -
+
+ +
); const { setProps } = render(); @@ -157,7 +158,7 @@ describe(' - Rows', () => { clock.tick(50); expect(getColumnValues(0)).to.deep.equal(['0', '1', '2', '3', '4']); }); - }) + }); describe('updateRows', () => { beforeEach(() => { From 3f5e7cc3c7cc963640c5fd13a117b015a75d4160 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 10 Sep 2021 12:36:05 +0200 Subject: [PATCH 046/390] Fix --- packages/grid/data-grid/src/DataGrid.tsx | 6 ------ packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx | 8 ++++++++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/grid/data-grid/src/DataGrid.tsx b/packages/grid/data-grid/src/DataGrid.tsx index c741ed3972e4b..404583aefc659 100644 --- a/packages/grid/data-grid/src/DataGrid.tsx +++ b/packages/grid/data-grid/src/DataGrid.tsx @@ -668,10 +668,4 @@ DataGridRaw.propTypes = { * @ignore */ style: PropTypes.object, - /** - * If positive, the Grid will throttle updates coming from `props.rows`, `apiRef.current.updateRows` or `apiRef.current.setRows`. - * It can be useful if you have a high update rate but do not want to do heavy work like filtering / sorting or rendering on each individual update. - * @default 0 - */ - throttleRowsMs: PropTypes.number, } as any; diff --git a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx index 604123521f01e..599e078eddcd1 100644 --- a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx @@ -137,6 +137,14 @@ describe(' - Rows', () => { }); describe('props: rows', () => { + beforeEach(() => { + clock = useFakeTimers(); + }); + + afterEach(() => { + clock.restore(); + }); + it('should support new dataset throttle', () => { const { rows, columns } = getData(5, 2); From e369acebc26429541463ea561f40eb37f31b998f Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 10 Sep 2021 12:41:09 +0200 Subject: [PATCH 047/390] Fix --- docs/pages/api-docs/data-grid/grid-api.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index a11b33e9a09eb..a11c08beb4d13 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -55,13 +55,13 @@ import { GridApi } from '@mui/x-data-grid-pro'; | hideFilterPanel | () => void | Hides the filter panel. | | hidePreferences | () => void | Hides the preferences panel. | | isCellEditable | (params: GridCellParams) => boolean | Controls if a cell is editable. | -| isRowSelected | (id: GridRowId) => boolean | Determines if a row is selected or not. | -| publishEvent | (name: string, params?: any, event?: MuiEvent<MouseEvent \| UIEvent \| Event \| SyntheticEvent<Element, Event> \| TouchEvent \| ProgressEvent<EventTarget> \| ClipboardEvent \| ErrorEvent \| AnimationEvent \| InputEvent \| FocusEvent \| CompositionEvent \| DragEvent \| FormDataEvent \| PointerEvent \| KeyboardEvent \| SecurityPolicyViolationEvent \| TransitionEvent \| WheelEvent>) => void | Emits an event. | +| isRowSelected | (id: GridRowId) => boolean | Determines if a row is selected or not. | +| publishEvent | (name: string, params?: any, event?: MuiEvent<MouseEvent \| UIEvent \| Event \| SyntheticEvent<Element, Event> \| TouchEvent \| ProgressEvent<EventTarget> \| ClipboardEvent \| ErrorEvent \| AnimationEvent \| InputEvent \| FocusEvent \| CompositionEvent \| DragEvent \| FormDataEvent \| PointerEvent \| KeyboardEvent \| SecurityPolicyViolationEvent \| TransitionEvent \| WheelEvent>) => void | Emits an event. | | resize | () => void | Triggers a resize of the component and recalculation of width and height. | | scroll | (params: Partial<GridScrollParams>) => void | Triggers the viewport to scroll to the given positions (in pixels). | | scrollToIndexes | (params: Partial<GridCellIndexCoordinates>) => boolean | Triggers the viewport to scroll to the cell at indexes given by `params`.
Returns `true` if the grid had to scroll to reach the target. | -| selectRow | (id: GridRowId, isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of a row. | -| selectRows | (ids: GridRowId[], isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of multiple rows. | +| selectRow | (id: GridRowId, isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of a row. | +| selectRows | (ids: GridRowId[], isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of multiple rows. | | setCellFocus | (id: GridRowId, field: string) => void | Sets the focus to the cell at the given `id` and `field`. | | setCellMode | (id: GridRowId, field: string, mode: GridCellMode) => void | Sets the mode of a cell. | | setColumnHeaderFocus | (field: string, event?: SyntheticEvent<Element, Event>) => void | Sets the focus to the column header at the given `field`. | From ee6600a44b8957cbc770790c6958fb4d129bf4fd Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 10 Sep 2021 14:15:20 +0200 Subject: [PATCH 048/390] Replace useGridProSelector with a componentProps injection by the feature hooks --- .../grid/components/base/GridBody.tsx | 2 +- .../columnHeaders/GridColumnHeaders.tsx | 82 +++++++++---------- .../GridColumnHeadersItemCollection.tsx | 6 +- .../columnReorder/useGridColumnReorder.tsx | 24 +++++- .../grid/models/gridSlotsComponentsProps.ts | 3 + packages/grid/x-grid/src/DataGridPro.tsx | 4 +- .../x-grid/src/useDataGridProComponent.tsx | 4 +- 7 files changed, 74 insertions(+), 51 deletions(-) diff --git a/packages/grid/_modules_/grid/components/base/GridBody.tsx b/packages/grid/_modules_/grid/components/base/GridBody.tsx index 7ef169b343383..7039e71fa1423 100644 --- a/packages/grid/_modules_/grid/components/base/GridBody.tsx +++ b/packages/grid/_modules_/grid/components/base/GridBody.tsx @@ -40,7 +40,7 @@ function GridBody(props: GridBodyProps) { - + state.scrollBar; type OwnerState = { classes?: GridComponentProps['classes']; - dragCol: string | undefined; + dragCol?: string; }; const useUtilityClasses = (ownerState: OwnerState) => { @@ -34,44 +32,46 @@ const useUtilityClasses = (ownerState: OwnerState) => { return composeClasses(slots, getDataGridUtilityClass, classes); }; -export const GridColumnsHeader = React.forwardRef(function GridColumnsHeader( - props, - ref, -) { - const apiRef = useGridApiContext(); - const columns = useGridSelector(apiRef, visibleGridColumnsSelector); - const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); - const headerHeight = useGridSelector(apiRef, gridDensityHeaderHeightSelector); - const renderCtx = useGridSelector(apiRef, gridRenderingSelector).renderContext; - const { hasScrollX } = useGridSelector(apiRef, gridScrollbarStateSelector); - const dragCol = useGridProSelector(apiRef, gridColumnReorderDragColSelector); - const rootProps = useGridRootProps(); +export interface GridColumnsHeaderProps { + dragCol?: string; +} - const ownerState = { ...props, dragCol, classes: rootProps.classes }; - const classes = useUtilityClasses(ownerState); +export const GridColumnsHeader = React.forwardRef( + function GridColumnsHeader(props, ref) { + const apiRef = useGridApiContext(); + const columns = useGridSelector(apiRef, visibleGridColumnsSelector); + const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); + const headerHeight = useGridSelector(apiRef, gridDensityHeaderHeightSelector); + const renderCtx = useGridSelector(apiRef, gridRenderingSelector).renderContext; + const { hasScrollX } = useGridSelector(apiRef, gridScrollbarStateSelector); + const rootProps = useGridRootProps(); - const renderedCols = React.useMemo(() => { - if (renderCtx == null) { - return []; - } - return columns.slice(renderCtx.firstColIdx, renderCtx.lastColIdx! + 1); - }, [columns, renderCtx]); + const ownerState = { ...props, classes: rootProps.classes }; + const classes = useUtilityClasses(ownerState); - return ( - - -
- - - -
- -
- ); -}); + const renderedCols = React.useMemo(() => { + if (renderCtx == null) { + return []; + } + return columns.slice(renderCtx.firstColIdx, renderCtx.lastColIdx! + 1); + }, [columns, renderCtx]); + + return ( + + +
+ + + +
+ +
+ ); + }, +); diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx index be1734038ced1..7d5fa14073ee4 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { gridColumnReorderDragColSelector } from '../../hooks/features/columnReorder/columnReorderSelector'; import { gridResizingColumnFieldSelector } from '../../hooks/features/columnResize/columnResizeSelector'; import { useGridSelector } from '../../hooks/features/core/useGridSelector'; import { filterGridColumnLookupSelector } from '../../hooks/features/filter/gridFilterSelector'; @@ -18,10 +17,10 @@ import { useGridApiContext } from '../../hooks/root/useGridApiContext'; import { GridColumnHeaderItem } from './GridColumnHeaderItem'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { gridScrollBarSizeSelector } from '../../hooks/root/gridContainerSizesSelector'; -import { useGridProSelector } from '../../hooks/features/core/useGridProSelector'; export interface GridColumnHeadersItemCollectionProps { columns: GridStateColDef[]; + dragCol?: string; } function GridColumnHeadersItemCollection(props: GridColumnHeadersItemCollectionProps) { @@ -29,7 +28,6 @@ function GridColumnHeadersItemCollection(props: GridColumnHeadersItemCollectionP const apiRef = useGridApiContext(); const sortColumnLookup = useGridSelector(apiRef, gridSortColumnLookupSelector); const filterColumnLookup = useGridSelector(apiRef, filterGridColumnLookupSelector); - const dragCol = useGridProSelector(apiRef, gridColumnReorderDragColSelector); const resizingColumnField = useGridSelector(apiRef, gridResizingColumnFieldSelector); const columnHeaderFocus = useGridSelector(apiRef, gridFocusColumnHeaderSelector); const renderCtx = useGridSelector(apiRef, gridRenderingSelector).renderContext; @@ -67,7 +65,7 @@ function GridColumnHeadersItemCollection(props: GridColumnHeadersItemCollectionP columnMenuOpen={open} filterItemsCounter={filterColumnLookup[col.field] && filterColumnLookup[col.field].length} headerHeight={headerHeight} - isDragging={col.field === dragCol} + isDragging={col.field === props.dragCol} column={col} colIndex={colIndex} isResizing={resizingColumnField === col.field} diff --git a/packages/grid/_modules_/grid/hooks/features/columnReorder/useGridColumnReorder.tsx b/packages/grid/_modules_/grid/hooks/features/columnReorder/useGridColumnReorder.tsx index beac18e79b12c..da21b4923e1a2 100644 --- a/packages/grid/_modules_/grid/hooks/features/columnReorder/useGridColumnReorder.tsx +++ b/packages/grid/_modules_/grid/hooks/features/columnReorder/useGridColumnReorder.tsx @@ -13,6 +13,7 @@ import { gridColumnReorderDragColSelector } from './columnReorderSelector'; import { GridComponentProps } from '../../../GridComponentProps'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { composeClasses } from '../../../utils/material-ui-utils'; +import { GridSlotsComponentsProps } from '../../../models'; const CURSOR_MOVE_DIRECTION_LEFT = 'left'; const CURSOR_MOVE_DIRECTION_RIGHT = 'right'; @@ -50,8 +51,8 @@ const useUtilityClasses = (ownerState: OwnerState) => { */ export const useGridColumnReorder = ( apiRef: GridApiRef, - props: Pick, -): void => { + props: GridComponentProps, +): GridComponentProps => { const logger = useGridLogger(apiRef, 'useGridColumnReorder'); useGridStateInit(apiRef, (state) => ({ @@ -192,4 +193,23 @@ export const useGridColumnReorder = ( useGridApiEventHandler(apiRef, GridEvents.cellDragEnter, handleDragEnter); useGridApiEventHandler(apiRef, GridEvents.cellDragOver, handleDragOver); useGridApiEventHandler(apiRef, GridEvents.cellDragEnd, handleDragEnd); + + const componentsProps = React.useMemo( + () => ({ + ...props.componentsProps, + columnsHeader: { + ...props.componentsProps?.columnsHeader, + dragCol: dragColField, + }, + }), + [props.componentsProps, dragColField], + ); + + return React.useMemo( + () => ({ + ...props, + componentsProps, + }), + [props, componentsProps], + ); }; diff --git a/packages/grid/_modules_/grid/models/gridSlotsComponentsProps.ts b/packages/grid/_modules_/grid/models/gridSlotsComponentsProps.ts index 8ed8b4fac6e05..07c945cdf5fc5 100644 --- a/packages/grid/_modules_/grid/models/gridSlotsComponentsProps.ts +++ b/packages/grid/_modules_/grid/models/gridSlotsComponentsProps.ts @@ -1,3 +1,5 @@ +import type { GridColumnsHeaderProps } from '../components/columnHeaders/GridColumnHeaders'; + /** * Overrideable components props dynamically passed to the component at rendering. */ @@ -5,6 +7,7 @@ export interface GridSlotsComponentsProps { checkbox?: any; columnMenu?: any; columnsPanel?: any; + columnsHeader?: GridColumnsHeaderProps; errorOverlay?: any; filterPanel?: any; footer?: any; diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index 35f95f91d14ae..f93cc47f8cb31 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -33,8 +33,8 @@ const DataGridProRaw = React.forwardRef(functi ref, ) { const apiRef = useGridApiRef(inProps.apiRef); - const props = useDataGridProProps(inProps); - useDataGridProComponent(apiRef, props); + let props = useDataGridProProps(inProps); + props = useDataGridProComponent(apiRef, props); return ( diff --git a/packages/grid/x-grid/src/useDataGridProComponent.tsx b/packages/grid/x-grid/src/useDataGridProComponent.tsx index ac9c6f1c0d09e..307edbea38239 100644 --- a/packages/grid/x-grid/src/useDataGridProComponent.tsx +++ b/packages/grid/x-grid/src/useDataGridProComponent.tsx @@ -53,7 +53,7 @@ export const useDataGridProComponent = (apiRef: GridApiRef, props: GridComponent useGridPreferencesPanel(apiRef); useGridFilter(apiRef, props); useGridDensity(apiRef, props); - useGridColumnReorder(apiRef, props); + props = useGridColumnReorder(apiRef, props); useGridColumnResize(apiRef, props); useGridPageSize(apiRef, props); useGridPage(apiRef, props); @@ -70,4 +70,6 @@ export const useDataGridProComponent = (apiRef: GridApiRef, props: GridComponent useGridEvents(apiRef, props); useStateProp(apiRef, props); useRenderInfoLog(apiRef); + + return props; }; From 3b895404ad4fb1527d4729ee2ab57b46e8e948f5 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 10 Sep 2021 15:06:34 +0200 Subject: [PATCH 049/390] Rework control model upstert --- .../hooks/features/columns/useGridColumns.ts | 10 +- .../hooks/features/filter/useGridFilter.ts | 143 +++++++++--------- .../hooks/features/pagination/useGridPage.ts | 1 - .../features/pagination/useGridPageSize.ts | 11 +- .../hooks/features/rows/useGridEditRows.ts | 21 +-- .../grid/hooks/features/rows/useGridRows.ts | 8 + .../features/selection/useGridSelection.ts | 11 +- .../hooks/features/sorting/useGridSorting.ts | 31 ++-- 8 files changed, 126 insertions(+), 110 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts index 1cc889d66365c..25553af1218c5 100644 --- a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts +++ b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts @@ -356,7 +356,15 @@ export function useGridColumns( useGridApiMethod(apiRef, colApi, 'ColApi'); + // The effect do not track any value defined synchronously during the 1st render by hooks called after `useGridSorting` + // As a consequence, the state generated by the 1st run of this useEffect will always be equal to the initialization one + const isFirstRender = React.useRef(true); React.useEffect(() => { + if (isFirstRender.current) { + isFirstRender.current = false; + return; + } + logger.info(`GridColumns have changed, new length ${props.columns.length}`); const hydratedColumns = hydrateColumnsType( @@ -379,7 +387,7 @@ export function useGridColumns( classes, ]); - // TODO: We need optional chaining due to impossible hook order (see description of this hook) + // We need optional chaining due to impossible hook order (see description of this hook) React.useEffect(() => { logger.debug(`GridColumns gridState.viewportSizes.width, changed ${viewportSizes}`); diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 314c4c9c60851..fce2370fb9ade 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -11,7 +11,6 @@ import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; import { filterableGridColumnsIdsSelector } from '../columns/gridColumnsSelector'; -import { useGridSelector } from '../core/useGridSelector'; import { useGridState } from '../core/useGridState'; import { GridPreferencePanelsValue } from '../preferencesPanel/gridPreferencePanelsValue'; import { sortedGridRowsSelector } from '../sorting/gridSortingSelector'; @@ -22,6 +21,17 @@ import { useGridRegisterControlState } from '../../utils/useGridRegisterControlS import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; +const checkFilterModelValidity = (model: GridFilterModel) => { + if (model.items.length > 1) { + const hasItemsWithoutIds = model.items.find((item) => item.id == null); + if (hasItemsWithoutIds) { + throw new Error( + "The 'id' field is required on filterModel.items when you use multiple filters.", + ); + } + } +}; + /** * @requires useGridColumns (state, method, event) * @requires useGridParamsApi (method) @@ -37,16 +47,21 @@ export const useGridFilter = ( ): void => { const logger = useGridLogger(apiRef, 'useGridFilter'); - useGridStateInit(apiRef, (state) => ({ - ...state, - filter: props.filterModel ?? getInitialGridFilterState(), - visibleRows: { - visibleRowsLookup: {}, - }, - })); + useGridStateInit(apiRef, (state) => { + if (props.filterModel) { + checkFilterModelValidity(props.filterModel); + } + + return { + ...state, + filter: props.filterModel ?? getInitialGridFilterState(), + visibleRows: { + visibleRowsLookup: {}, + }, + }; + }); const [, setGridState, forceUpdate] = useGridState(apiRef); - const filterableColumnsIds = useGridSelector(apiRef, filterableGridColumnsIdsSelector); useGridRegisterControlState(apiRef, { stateId: 'filter', @@ -56,16 +71,6 @@ export const useGridFilter = ( changeEvent: GridEvents.filterModelChange, }); - const clearFilteredRows = React.useCallback(() => { - logger.debug('clearing filtered rows'); - setGridState((state) => ({ - ...state, - visibleRows: { - visibleRowsLookup: {}, - }, - })); - }, [logger, setGridState]); - const applyFilter = React.useCallback( (filterItem: GridFilterItem, linkOperator: GridLinkOperator = GridLinkOperator.And) => { if (!filterItem.columnField || !filterItem.operatorValue) { @@ -143,26 +148,34 @@ export const useGridFilter = ( [apiRef, forceUpdate, logger, setGridState], ); - const applyFilters = React.useCallback(() => { + const applyFilters = React.useCallback(() => { if (props.filterMode === GridFeatureModeConstant.server) { forceUpdate(); return; } - clearFilteredRows(); + // Clearing filtered rows + setGridState((state) => ({ + ...state, + visibleRows: { + visibleRowsLookup: {}, + }, + })); + const { items, linkOperator } = apiRef.current.state.filter; items.forEach((filterItem) => { apiRef.current.applyFilter(filterItem, linkOperator); }); forceUpdate(); - }, [apiRef, clearFilteredRows, forceUpdate, props.filterMode]); + }, [apiRef, setGridState, forceUpdate, props.filterMode]); - const upsertFilter = React.useCallback( - (item: GridFilterItem) => { + const upsertFilter = React.useCallback( + (item) => { logger.debug('Upserting filter'); setGridState((state) => { + const filterableColumnsIds = filterableGridColumnsIdsSelector(state); const items = [...state.filter.items]; const newItem = { ...item }; const itemIndex = items.findIndex((filterItem) => filterItem.id === newItem.id); @@ -197,20 +210,13 @@ export const useGridFilter = ( }; return newState; }); - applyFilters(); + apiRef.current.applyFilters(); }, - [ - logger, - setGridState, - apiRef, - applyFilters, - props.disableMultipleColumnsFiltering, - filterableColumnsIds, - ], + [apiRef, logger, setGridState, props.disableMultipleColumnsFiltering], ); - const deleteFilter = React.useCallback( - (item: GridFilterItem) => { + const deleteFilter = React.useCallback( + (item) => { logger.debug(`Deleting filter on column ${item.columnField} with value ${item.value}`); setGridState((state) => { const items = [...state.filter.items.filter((filterItem) => filterItem.id !== item.id)]; @@ -223,13 +229,13 @@ export const useGridFilter = ( if (apiRef.current.state.filter.items.length === 0) { apiRef.current.upsertFilter({}); } - applyFilters(); + apiRef.current.applyFilters(); }, - [apiRef, applyFilters, logger, setGridState], + [apiRef, logger, setGridState], ); - const showFilterPanel = React.useCallback( - (targetColumnField?: string) => { + const showFilterPanel = React.useCallback( + (targetColumnField) => { logger.debug('Displaying filter panel'); if (targetColumnField) { const filterState = filterGridStateSelector(apiRef.current.state); @@ -244,40 +250,43 @@ export const useGridFilter = ( }, [apiRef, logger], ); - const hideFilterPanel = React.useCallback(() => { + + const hideFilterPanel = React.useCallback(() => { logger.debug('Hiding filter panel'); apiRef.current.hidePreferences(); }, [apiRef, logger]); - const applyFilterLinkOperator = React.useCallback( - (linkOperator: GridLinkOperator = GridLinkOperator.And) => { + const applyFilterLinkOperator = React.useCallback( + (linkOperator) => { logger.debug('Applying filter link operator'); setGridState((state) => ({ ...state, filter: { ...state.filter, linkOperator }, })); - applyFilters(); + apiRef.current.applyFilters(); }, - [applyFilters, logger, setGridState], + [apiRef, logger, setGridState], ); - const clearFilterModel = React.useCallback(() => { - clearFilteredRows(); - logger.debug('Clearing filter model'); - setGridState((state) => ({ ...state, filter: getInitialGridFilterState() })); - }, [clearFilteredRows, logger, setGridState]); - - const setFilterModel = React.useCallback( - (model: GridFilterModel) => { - clearFilterModel(); - logger.debug('Setting filter model'); - applyFilterLinkOperator(model.linkOperator); - model.items.forEach((item) => upsertFilter(item)); + const setFilterModel = React.useCallback( + (model) => { + const currentModel = filterGridStateSelector(apiRef.current.state); + + if (currentModel !== model) { + checkFilterModelValidity(model); + + logger.debug('Setting filter model'); + setGridState((state) => ({ + ...state, + filter: model, + })); + apiRef.current.applyFilters(); + } }, - [applyFilterLinkOperator, clearFilterModel, logger, upsertFilter], + [apiRef, logger, setGridState], ); - const getVisibleRowModels = React.useCallback( + const getVisibleRowModels = React.useCallback( () => visibleSortedGridRowsSelector(apiRef.current.state), [apiRef], ); @@ -313,22 +322,8 @@ export const useGridFilter = ( }, [apiRef, logger]); React.useEffect(() => { - if (props.filterModel !== undefined && props.filterModel.items.length > 1) { - const hasItemsWithoutIds = props.filterModel.items.find((item) => item.id == null); - if (hasItemsWithoutIds) { - throw new Error( - "The 'id' field is required on filterModel.items when you use multiple filters.", - ); - } - } - const oldFilterModel = apiRef.current.state.filter; - if (props.filterModel && props.filterModel !== oldFilterModel) { - logger.debug('filterModel prop changed, applying filters'); - setGridState((state) => ({ - ...state, - filter: props.filterModel!, - })); - apiRef.current.applyFilters(); + if (props.filterModel !== undefined) { + apiRef.current.setFilterModel(props.filterModel); } }, [apiRef, logger, props.filterModel, setGridState]); diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts index baed980793728..03be1a1ad1a3b 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts @@ -83,7 +83,6 @@ export const useGridPage = ( setGridState((state) => { const rowCount = props.rowCount !== undefined ? props.rowCount : visibleRowCount; const pageCount = getPageCount(rowCount, state.pagination.pageSize); - const page = props.page == null ? state.pagination.page : props.page; return { diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts index 285eac68c1e65..883b358a2b1c6 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts @@ -6,7 +6,6 @@ import { useGridApiMethod } from '../../root'; import { GridEvents } from '../../../constants/eventsConstants'; import { useGridLogger } from '../../utils'; import { useGridSelector, useGridState } from '../core'; -import { visibleGridRowCountSelector } from '../filter'; import { gridContainerSizesSelector } from '../../root/gridContainerSizesSelector'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useGridRegisterControlState } from '../../utils/useGridRegisterControlState'; @@ -28,7 +27,6 @@ export const useGridPageSize = ( })); const [, setGridState, forceUpdate] = useGridState(apiRef); - const visibleRowCount = useGridSelector(apiRef, visibleGridRowCountSelector); const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); useGridRegisterControlState(apiRef, { @@ -55,7 +53,15 @@ export const useGridPageSize = ( [setGridState, forceUpdate, logger], ); + // The effect do not track any value defined synchronously during the 1st render by hooks called after `useGridSorting` + // As a consequence, the state generated by the 1st run of this useEffect will always be equal to the initialization one + const isFirstRender = React.useRef(true); React.useEffect(() => { + if (isFirstRender.current) { + isFirstRender.current = false; + return; + } + const autoPageSize = containerSizes?.viewportPageSize; const prevPageSize = apiRef.current.state.pagination.pageSize; @@ -85,7 +91,6 @@ export const useGridPageSize = ( apiRef, setGridState, forceUpdate, - visibleRowCount, props.autoPageSize, props.pageSize, containerSizes?.viewportPageSize, diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts index e0552916d77a4..95d967aeb1d8f 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts @@ -36,6 +36,7 @@ import { useGridState } from '../core/useGridState'; import { useGridSelector } from '../core/useGridSelector'; import { useGridRegisterControlState } from '../../utils/useGridRegisterControlState'; import { useGridStateInit } from '../../utils/useGridStateInit'; +import { gridEditRowsStateSelector } from './gridEditRowsSelector'; /** * @requires useGridFocus - can be after, async only @@ -247,13 +248,15 @@ export function useGridEditRows( ); const setEditRowsModel = React.useCallback( - (editRows: GridEditRowsModel): void => { - logger.debug(`Setting row model`); - - setGridState((state) => ({ ...state, editRows })); - forceUpdate(); + (model) => { + const currentModel = gridEditRowsStateSelector(apiRef.current.state); + if (currentModel !== model) { + logger.debug(`Setting editRows model`); + setGridState((state) => ({ ...state, editRows: model })); + forceUpdate(); + } }, - [forceUpdate, logger, setGridState], + [apiRef, forceUpdate, logger, setGridState], ); const getEditRowsModel = React.useCallback( @@ -515,10 +518,8 @@ export function useGridEditRows( ); React.useEffect(() => { - const currentEditRowsModel = apiRef.current.state.editRows; - - if (props.editRowsModel !== undefined && props.editRowsModel !== currentEditRowsModel) { - apiRef.current.setEditRowsModel(props.editRowsModel || {}); + if (props.editRowsModel !== undefined) { + apiRef.current.setEditRowsModel(props.editRowsModel); } }, [apiRef, props.editRowsModel]); } diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index ac713dd279455..f95918afccf72 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -219,7 +219,15 @@ export const useGridRows = ( return () => clearTimeout(updateTimeout!.current); }, []); + // The effect do not track any value defined synchronously during the 1st render by hooks called after `useGridSorting` + // As a consequence, the state generated by the 1st run of this useEffect will always be equal to the initialization one + const isFirstRender = React.useRef(true); React.useEffect(() => { + if (isFirstRender.current) { + isFirstRender.current = false; + return; + } + setRowsState(props.rows, props.rowCount, props.getRowId, false); }, [setRowsState, props.rows, props.rowCount, props.getRowId]); diff --git a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts index 67c40284528ea..452bc54c2592d 100644 --- a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts +++ b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts @@ -127,13 +127,14 @@ export const useGridSelection = (apiRef: GridApiRef, props: GridComponentProps): const setSelectionModel = React.useCallback( (model) => { - const currentModel = apiRef.current.state.selection; + const currentModel = gridSelectionStateSelector(apiRef.current.state); if (currentModel !== model) { + logger.debug(`Setting selection model`); setGridState((state) => ({ ...state, selection: model })); forceUpdate(); } }, - [setGridState, apiRef, forceUpdate], + [apiRef, setGridState, forceUpdate, logger], ); const isRowSelected = React.useCallback( @@ -199,11 +200,9 @@ export const useGridSelection = (apiRef: GridApiRef, props: GridComponentProps): }, [rowsLookup, apiRef]); React.useEffect(() => { - if (propSelectionModel === undefined) { - return; + if (propSelectionModel !== undefined) { + apiRef.current.setSelectionModel(propSelectionModel); } - - apiRef.current.setSelectionModel(propSelectionModel); }, [apiRef, propSelectionModel]); React.useEffect(() => { diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 11d0168a9458c..cf991f7baf758 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -169,7 +169,7 @@ export const useGridSorting = ( [apiRef], ); - const applySorting = React.useCallback(() => { + const applySorting = React.useCallback(() => { let sortedRows = apiRef.current.getAllRowIds(); if (props.sortingMode === GridFeatureModeConstant.server) { @@ -212,11 +212,12 @@ export const useGridSorting = ( props.sortingMode, ]); - const setSortModel = React.useCallback( - (sortModel: GridSortModel) => { + const setSortModel = React.useCallback( + (model) => { const currentModel = gridSortModelSelector(apiRef.current.state); - if (sortModel !== currentModel) { - setGridState((state) => ({ ...state, sorting: { ...state.sorting, sortModel } })); + if (currentModel !== model) { + logger.debug(`Setting sort model`); + setGridState((state) => ({ ...state, sorting: { ...state.sorting, sortModel: model } })); forceUpdate(); apiRef.current.applySorting(); } @@ -224,8 +225,8 @@ export const useGridSorting = ( [setGridState, forceUpdate, apiRef], ); - const sortColumn = React.useCallback( - (column: GridColDef, direction?: GridSortDirection, allowMultipleSorting?: boolean) => { + const sortColumn = React.useCallback( + (column, direction, allowMultipleSorting) => { if (!column.sortable) { return; } @@ -236,23 +237,23 @@ export const useGridSorting = ( } else { sortModel = upsertSortModel(column.field, sortItem); } - setSortModel(sortModel); + apiRef.current.setSortModel(sortModel); }, - [upsertSortModel, setSortModel, createSortItem, props.disableMultipleColumnsSorting], + [apiRef, upsertSortModel, createSortItem, props.disableMultipleColumnsSorting], ); - const getSortModel = React.useCallback( + const getSortModel = React.useCallback( () => gridSortModelSelector(apiRef.current.state), [apiRef], ); - const getSortedRows = React.useCallback( - (): GridRowModel[] => Object.values(sortedGridRowsSelector(apiRef.current.state)), + const getSortedRows = React.useCallback( + () => Object.values(sortedGridRowsSelector(apiRef.current.state)), [apiRef], ); - const getSortedRowIds = React.useCallback( - (): GridRowId[] => sortedGridRowIdsSelector(apiRef.current.state), + const getSortedRowIds = React.useCallback( + () => sortedGridRowIdsSelector(apiRef.current.state), [apiRef], ); @@ -270,7 +271,7 @@ export const useGridSorting = ( if (props.sortModel !== undefined) { apiRef.current.setSortModel(props.sortModel); } - }, [props.sortModel, apiRef]); + }, [apiRef, props.sortModel]); useFirstRender(() => apiRef.current.applySorting()); From 3147cec055030f1dc5eda06d917337b8c7f784c8 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 10 Sep 2021 15:08:18 +0200 Subject: [PATCH 050/390] Fix --- .../grid/_modules_/grid/hooks/features/filter/useGridFilter.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index fce2370fb9ade..badffc7e3a3ee 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -271,7 +271,6 @@ export const useGridFilter = ( const setFilterModel = React.useCallback( (model) => { const currentModel = filterGridStateSelector(apiRef.current.state); - if (currentModel !== model) { checkFilterModelValidity(model); From 1be930288fc7914547f92eafbe89ab7333cd8165 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 10 Sep 2021 15:45:05 +0200 Subject: [PATCH 051/390] Fix --- .../_modules_/grid/hooks/features/sorting/useGridSorting.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index cf991f7baf758..ac68550a3ec85 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -7,7 +7,7 @@ import { GridCellValue } from '../../../models/gridCell'; import { GridColDef } from '../../../models/colDef/gridColDef'; import { GridFeatureModeConstant } from '../../../models/gridFeatureMode'; import { GridColumnHeaderParams } from '../../../models/params/gridColumnHeaderParams'; -import { GridRowId, GridRowModel } from '../../../models/gridRows'; +import { GridRowId } from '../../../models/gridRows'; import { GridFieldComparatorList, GridSortItem, @@ -222,7 +222,7 @@ export const useGridSorting = ( apiRef.current.applySorting(); } }, - [setGridState, forceUpdate, apiRef], + [apiRef, setGridState, forceUpdate, logger], ); const sortColumn = React.useCallback( From e266e69be1d4dff521f6b588cbf8db2b3bbc0f52 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 10 Sep 2021 15:56:29 +0200 Subject: [PATCH 052/390] Fix --- .../grid/hooks/features/pagination/useGridPageSize.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts index 883b358a2b1c6..c03bbe04c3ba9 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts @@ -53,15 +53,7 @@ export const useGridPageSize = ( [setGridState, forceUpdate, logger], ); - // The effect do not track any value defined synchronously during the 1st render by hooks called after `useGridSorting` - // As a consequence, the state generated by the 1st run of this useEffect will always be equal to the initialization one - const isFirstRender = React.useRef(true); React.useEffect(() => { - if (isFirstRender.current) { - isFirstRender.current = false; - return; - } - const autoPageSize = containerSizes?.viewportPageSize; const prevPageSize = apiRef.current.state.pagination.pageSize; From 433c81f265d28548b283da56fd0479d8fe30d76b Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 10 Sep 2021 16:41:37 +0200 Subject: [PATCH 053/390] Code review --- .../data-grid/rows/ThrottledRowsGrid.js | 4 ++-- .../data-grid/rows/ThrottledRowsGrid.tsx | 4 ++-- .../pages/components/data-grid/rows/rows.md | 9 ++++---- .../grid/hooks/features/rows/gridRowsState.ts | 6 ----- .../grid/hooks/features/rows/useGridRows.ts | 22 ++++++++++--------- 5 files changed, 21 insertions(+), 24 deletions(-) diff --git a/docs/src/pages/components/data-grid/rows/ThrottledRowsGrid.js b/docs/src/pages/components/data-grid/rows/ThrottledRowsGrid.js index 54f91b47427d9..2cd18fcba8974 100644 --- a/docs/src/pages/components/data-grid/rows/ThrottledRowsGrid.js +++ b/docs/src/pages/components/data-grid/rows/ThrottledRowsGrid.js @@ -16,7 +16,7 @@ const rows = [ { id: 4, username: randomUserName(), age: randomInt(10, 80) }, ]; -export default function ApiRefRowsGrid() { +export default function ThrottledRowsGrid() { const apiRef = useGridApiRef(); React.useEffect(() => { @@ -46,7 +46,7 @@ export default function ApiRefRowsGrid() { rows={rows} columns={columns} apiRef={apiRef} - throttleRowsMs={500} + throttleRowsMs={2000} /> ); diff --git a/docs/src/pages/components/data-grid/rows/ThrottledRowsGrid.tsx b/docs/src/pages/components/data-grid/rows/ThrottledRowsGrid.tsx index 54f91b47427d9..2cd18fcba8974 100644 --- a/docs/src/pages/components/data-grid/rows/ThrottledRowsGrid.tsx +++ b/docs/src/pages/components/data-grid/rows/ThrottledRowsGrid.tsx @@ -16,7 +16,7 @@ const rows = [ { id: 4, username: randomUserName(), age: randomInt(10, 80) }, ]; -export default function ApiRefRowsGrid() { +export default function ThrottledRowsGrid() { const apiRef = useGridApiRef(); React.useEffect(() => { @@ -46,7 +46,7 @@ export default function ApiRefRowsGrid() { rows={rows} columns={columns} apiRef={apiRef} - throttleRowsMs={500} + throttleRowsMs={2000} /> ); diff --git a/docs/src/pages/components/data-grid/rows/rows.md b/docs/src/pages/components/data-grid/rows/rows.md index 1b7526da440c8..4de004b911308 100644 --- a/docs/src/pages/components/data-grid/rows/rows.md +++ b/docs/src/pages/components/data-grid/rows/rows.md @@ -53,12 +53,13 @@ Alternatively, if you would like to delete a row, you would need to pass an extr apiRef.current.updateRows([{ id: 1, _action: 'delete' }]); ``` -### Throttling grid update [](https://material-ui.com/store/items/material-ui-pro/) +### High frequency [](https://material-ui.com/store/items/material-ui-pro/) -If you have high frequency updates, but you want to keep good performances, you can pass set the `throttleRowsMs` prop to define the maximum update frequency. -When receiving updates more frequent than this threshold, the Grid will wait before applying the new values to avoid triggering heavy work too many times. +Whenever the rows are updated, the grid has to apply the sorting and filters. This can be a problem if you have high frequency updates. To maintain good performances, the grid allows to batch the updates and only apply them after a period of time. The `throttleRowsMs` prop can be used to define the frequency (in milliseconds) at which rows updates are applied. -The following demo updates the rows every 10ms but only apply them to the Grid every 500ms. +When receiving updates more frequently than this threshold, the grid will wait before updating the rows. + +The following demo updates the rows every 10ms, but they are only applied every 2 seconds. {{"demo": "pages/components/data-grid/rows/ThrottledRowsGrid.js", "bg": "inline"}} diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts index f3aeb4d3b518f..f86d234f2f046 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts @@ -6,12 +6,6 @@ export interface GridRowsState { totalRowCount: number; } -export interface GridRowsInternalCache { - state: GridRowsState; - timeout: NodeJS.Timeout | null; - lastUpdateMs: number | null; -} - export const getInitialGridRowState: () => GridRowsState = () => ({ idRowsLookup: {}, allRows: [], diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 2414e93c95be1..404e2af05c7fd 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -14,13 +14,19 @@ import { import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; import { useGridState } from '../core/useGridState'; -import { getInitialGridRowState, GridRowsInternalCache, GridRowsState } from './gridRowsState'; +import { getInitialGridRowState, GridRowsState } from './gridRowsState'; import { gridRowCountSelector, gridRowsLookupSelector, unorderedGridRowIdsSelector, } from './gridRowsSelector'; +export interface GridRowsInternalCache { + state: GridRowsState; + timeout: NodeJS.Timeout | null; + lastUpdateMs: number | null; +} + function getGridRowId( rowData: GridRowData, getRowId?: GridRowIdGetter, @@ -61,15 +67,11 @@ export const useGridRows = ( const logger = useGridLogger(apiRef, 'useGridRows'); const [, setGridState, forceUpdate] = useGridState(apiRef); - const rowsCache = React.useRef() as React.MutableRefObject; - - if (!rowsCache.current) { - rowsCache.current = { - state: getInitialGridRowState(), - timeout: null, - lastUpdateMs: null, - }; - } + const rowsCache = React.useRef({ + state: getInitialGridRowState(), + timeout: null, + lastUpdateMs: null, + }); const getRowIndex = React.useCallback( (id) => { From 38fbe1bb962bf4699ba10c834f3163f9c06d6ad0 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 10 Sep 2021 16:42:31 +0200 Subject: [PATCH 054/390] yarn prop types --- .../components/columnHeaders/GridColumnHeadersItemCollection.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx index 7d5fa14073ee4..1e310f5f81ec9 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx @@ -88,6 +88,7 @@ GridColumnHeadersItemCollection.propTypes = { // | To update them edit the TypeScript types and run "yarn proptypes" | // ---------------------------------------------------------------------- columns: PropTypes.arrayOf(PropTypes.object).isRequired, + dragCol: PropTypes.string, } as any; export { GridColumnHeadersItemCollection }; From dea38f6658ae55b2f5263b78f7c92510d697d1f4 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 13 Sep 2021 08:36:24 +0200 Subject: [PATCH 055/390] Code review --- docs/pages/api-docs/data-grid/grid-api.md | 2 +- packages/grid/_modules_/grid/hooks/root/useApi.ts | 11 ++++------- .../grid/hooks/root/useGridApiEventHandler.ts | 11 ++++------- .../grid/_modules_/grid/models/api/gridCoreApi.ts | 3 +-- packages/grid/_modules_/grid/models/muiEvent.ts | 6 +++++- .../grid/utils/eventEmitter/GridEventEmitter.ts | 10 +++------- 6 files changed, 18 insertions(+), 25 deletions(-) diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index a11c08beb4d13..3cdcd30c59280 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -85,7 +85,7 @@ import { GridApi } from '@mui/x-data-grid-pro'; | showPreferences | (newValue: GridPreferencePanelsValue) => void | Displays the preferences panel. The `newValue` argument controls the content of the panel. | | sortColumn | (column: GridColDef, direction?: GridSortDirection, allowMultipleSorting?: boolean) => void | Sorts a column. | | state | GridState | Property that contains the whole state of the grid. | -| subscribeEvent | <Params, Event extends GridValidEvent>(event: string, handler: GridListener<Params, Event>, options?: GridSubscribeEventOptions) => () => void | Registers a handler for an event. | +| subscribeEvent | <Params, Event extends MuiEvent<BaseEvent>>(event: string, handler: GridListener<Params, Event>, options?: GridSubscribeEventOptions) => () => void | Registers a handler for an event. | | toggleColumnMenu | (field: string) => void | Toggles the column menu under the `field` column. | | updateColumn | (col: GridColDef) => void | Updates the definition of a column. | | updateColumns | (cols: GridColDef[]) => void | Updates the definition of multiple columns at the same time. | diff --git a/packages/grid/_modules_/grid/hooks/root/useApi.ts b/packages/grid/_modules_/grid/hooks/root/useApi.ts index 69b3037ad5e01..1c9663f3205ac 100644 --- a/packages/grid/_modules_/grid/hooks/root/useApi.ts +++ b/packages/grid/_modules_/grid/hooks/root/useApi.ts @@ -1,15 +1,12 @@ import * as React from 'react'; import { GridApiRef } from '../../models/api/gridApiRef'; -import { - GridListener, - GridSubscribeEventOptions, - GridValidEvent, -} from '../../utils/eventEmitter/GridEventEmitter'; +import { GridListener, GridSubscribeEventOptions } from '../../utils/eventEmitter/GridEventEmitter'; import { useGridLogger } from '../utils/useGridLogger'; import { GridEvents } from '../../constants/eventsConstants'; import { useGridApiMethod } from './useGridApiMethod'; import { GridSignature } from './useGridApiEventHandler'; import { GridComponentProps } from '../../GridComponentProps'; +import { MuiEvent } from '../../models/muiEvent'; const isSyntheticEvent = (event: any): event is React.SyntheticEvent => { return event.isPropagationStopped !== undefined; @@ -19,7 +16,7 @@ export function useApi(apiRef: GridApiRef, props: Pick { + (name: string, params: any, event: MuiEvent = {}) => { event.defaultMuiPrevented = false; if (event && isSyntheticEvent(event) && event.isPropagationStopped()) { return; @@ -31,7 +28,7 @@ export function useApi(apiRef: GridApiRef, props: Pick( + ( event: string, handler: GridListener, options?: GridSubscribeEventOptions, diff --git a/packages/grid/_modules_/grid/hooks/root/useGridApiEventHandler.ts b/packages/grid/_modules_/grid/hooks/root/useGridApiEventHandler.ts index 880ca08d3a1d3..115516648a383 100644 --- a/packages/grid/_modules_/grid/hooks/root/useGridApiEventHandler.ts +++ b/packages/grid/_modules_/grid/hooks/root/useGridApiEventHandler.ts @@ -1,10 +1,7 @@ import * as React from 'react'; import { GridApiRef } from '../../models/api/gridApiRef'; -import { - GridListener, - GridSubscribeEventOptions, - GridValidEvent, -} from '../../utils/eventEmitter/GridEventEmitter'; +import { GridListener, GridSubscribeEventOptions } from '../../utils/eventEmitter/GridEventEmitter'; +import { MuiEvent } from '../../models/muiEvent'; /** * Signal to the underlying logic what version of the public component API @@ -15,7 +12,7 @@ export enum GridSignature { DataGridPro = 'DataGridPro', } -export function useGridApiEventHandler( +export function useGridApiEventHandler( apiRef: GridApiRef, eventName: string, handler?: GridListener, @@ -51,7 +48,7 @@ export function useGridApiEventHandler( const optionsSubscriberOptions: GridSubscribeEventOptions = { isFirst: true }; -export function useGridApiOptionHandler( +export function useGridApiOptionHandler( apiRef: GridApiRef, eventName: string, handler?: GridListener, diff --git a/packages/grid/_modules_/grid/models/api/gridCoreApi.ts b/packages/grid/_modules_/grid/models/api/gridCoreApi.ts index bd99ca958b5e0..bbf1668c522c5 100644 --- a/packages/grid/_modules_/grid/models/api/gridCoreApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridCoreApi.ts @@ -4,7 +4,6 @@ import { GridEventEmitter, GridListener, GridSubscribeEventOptions, - GridValidEvent, } from '../../utils/eventEmitter/GridEventEmitter'; /** @@ -53,7 +52,7 @@ export interface GridCoreApi extends GridEventEmitter { * @param {object} options Additional options for this listener. * @returns {function} A function to unsubscribe from this event. */ - subscribeEvent: ( + subscribeEvent: ( event: string, handler: GridListener, options?: GridSubscribeEventOptions, diff --git a/packages/grid/_modules_/grid/models/muiEvent.ts b/packages/grid/_modules_/grid/models/muiEvent.ts index ace0533014a31..82df620595768 100644 --- a/packages/grid/_modules_/grid/models/muiEvent.ts +++ b/packages/grid/_modules_/grid/models/muiEvent.ts @@ -1,3 +1,7 @@ -export type MuiEvent = E & { +import * as React from 'react'; + +type BaseEvent = React.SyntheticEvent | DocumentEventMap[keyof DocumentEventMap] | {}; + +export type MuiEvent = E & { defaultMuiPrevented?: boolean; }; diff --git a/packages/grid/_modules_/grid/utils/eventEmitter/GridEventEmitter.ts b/packages/grid/_modules_/grid/utils/eventEmitter/GridEventEmitter.ts index b57a58a9df61c..c342e39cd3544 100644 --- a/packages/grid/_modules_/grid/utils/eventEmitter/GridEventEmitter.ts +++ b/packages/grid/_modules_/grid/utils/eventEmitter/GridEventEmitter.ts @@ -1,12 +1,8 @@ -import * as React from 'react'; import { EventEmitter } from './EventEmitter'; -import { MuiEvent } from '../../models/muiEvent'; import { GridCallbackDetails } from '../../models/api/gridCallbackDetails'; +import { MuiEvent } from '../../models/muiEvent'; -export type GridValidEvent = MuiEvent< - React.SyntheticEvent | DocumentEventMap[keyof DocumentEventMap] | {} ->; -export type GridListener = ( +export type GridListener = ( params: Params, event: Event, details: GridCallbackDetails, @@ -17,7 +13,7 @@ export class GridEventEmitter extends EventEmitter { /** * @ignore - do not document. */ - on( + on( eventName: string, listener: GridListener, options?: GridSubscribeEventOptions, From bcbde903b2901cc0baca0cda131fc30fcacba5f8 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 10 Sep 2021 12:02:15 +0200 Subject: [PATCH 056/390] Tree data --- packages/grid/_modules_/grid/models/gridOptions.tsx | 5 +++++ .../grid/x-grid/src/features/treeData/useGridTreeData.ts | 9 +++++++++ 2 files changed, 14 insertions(+) create mode 100644 packages/grid/x-grid/src/features/treeData/useGridTreeData.ts diff --git a/packages/grid/_modules_/grid/models/gridOptions.tsx b/packages/grid/_modules_/grid/models/gridOptions.tsx index 01e97354d5e02..c282bbc7e96a9 100644 --- a/packages/grid/_modules_/grid/models/gridOptions.tsx +++ b/packages/grid/_modules_/grid/models/gridOptions.tsx @@ -192,6 +192,11 @@ export interface GridSimpleOptions { * @default [25, 50, 100] */ rowsPerPageOptions: number[]; + /** + * If `true`, the rows will be gathered in a tree structure, following the `getDataPath` prop + * @default false + */ + treeData: boolean /** * Set the area at the bottom of the grid viewport where onRowsScrollEnd is called. */ diff --git a/packages/grid/x-grid/src/features/treeData/useGridTreeData.ts b/packages/grid/x-grid/src/features/treeData/useGridTreeData.ts new file mode 100644 index 0000000000000..d6dd8e45401f6 --- /dev/null +++ b/packages/grid/x-grid/src/features/treeData/useGridTreeData.ts @@ -0,0 +1,9 @@ +/** + * Only available in DataGridPro + */ +import {useGridApiEventHandler} from "../../../../_modules_/grid/hooks/root/useGridApiEventHandler"; +import {GridApiRef, GridComponentProps, GridEvents } from "../../../../_modules_/grid"; + +export const useGridTreeData = (apiRef: GridApiRef, props: Pick) => { + useGridApiEventHandler(apiRef, GridEvents.rowsSet, console.log) +} \ No newline at end of file From 91bd62bc9c382460177413df8ba8813e842ae6bb Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 10 Sep 2021 13:32:00 +0200 Subject: [PATCH 057/390] Temp --- .../grid/_modules_/grid/hooks/features/rows/useGridRows.ts | 1 + packages/grid/_modules_/grid/models/gridRows.ts | 6 ++++++ .../grid/x-grid/src/features/treeData/GridTreeDataApi.ts | 5 +++++ 3 files changed, 12 insertions(+) create mode 100644 packages/grid/x-grid/src/features/treeData/GridTreeDataApi.ts diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 404e2af05c7fd..889035af7e888 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -20,6 +20,7 @@ import { gridRowsLookupSelector, unorderedGridRowIdsSelector, } from './gridRowsSelector'; +import {useGridApiEventHandler} from "../../root/useGridApiEventHandler"; export interface GridRowsInternalCache { state: GridRowsState; diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index bee406ae8bf00..71594bae6655a 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -12,6 +12,12 @@ export interface GridRowModelUpdate extends GridRowData { _action?: GridUpdateAction; } +export interface GridRowTreeNode { + value: GridRowModel + collapsed: boolean + children: [], +} + /** * The type of Id supported by the grid. */ diff --git a/packages/grid/x-grid/src/features/treeData/GridTreeDataApi.ts b/packages/grid/x-grid/src/features/treeData/GridTreeDataApi.ts new file mode 100644 index 0000000000000..28bb88ce7908b --- /dev/null +++ b/packages/grid/x-grid/src/features/treeData/GridTreeDataApi.ts @@ -0,0 +1,5 @@ +import {GridRowModel, GridRowTreeNode} from "../../../../_modules_"; + +export interface GridTreeDataApi { + groupRows: (flatRows: GridRowModel[]) => GridRowTreeNode[], +} \ No newline at end of file From 1e1fc79c2d1b3e3ef60c00816e577c24b031b2ab Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 10 Sep 2021 16:30:48 +0200 Subject: [PATCH 058/390] Work --- .../_modules_/grid/models/gridOptions.tsx | 1 + .../x-grid/src/features/treeData/index.ts | 2 ++ .../src/features/treeData/useGridTreeData.ts | 22 +++++++++++++++---- .../x-grid/src/useDataGridProComponent.tsx | 2 ++ .../src/stories/grid-tree-data.stories.tsx | 0 5 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 packages/grid/x-grid/src/features/treeData/index.ts create mode 100644 packages/storybook/src/stories/grid-tree-data.stories.tsx diff --git a/packages/grid/_modules_/grid/models/gridOptions.tsx b/packages/grid/_modules_/grid/models/gridOptions.tsx index c282bbc7e96a9..fe1118967b01e 100644 --- a/packages/grid/_modules_/grid/models/gridOptions.tsx +++ b/packages/grid/_modules_/grid/models/gridOptions.tsx @@ -266,6 +266,7 @@ export const GRID_DEFAULT_SIMPLE_OPTIONS: GridSimpleOptions = { paginationMode: GridFeatureModeConstant.client, rowHeight: 52, rowsPerPageOptions: [25, 50, 100], + treeData: false, scrollEndThreshold: 80, showCellRightBorder: false, showColumnRightBorder: false, diff --git a/packages/grid/x-grid/src/features/treeData/index.ts b/packages/grid/x-grid/src/features/treeData/index.ts new file mode 100644 index 0000000000000..4d0253a7e61c1 --- /dev/null +++ b/packages/grid/x-grid/src/features/treeData/index.ts @@ -0,0 +1,2 @@ +export * from './useGridTreeData' +export * from './GridTreeDataApi' \ No newline at end of file diff --git a/packages/grid/x-grid/src/features/treeData/useGridTreeData.ts b/packages/grid/x-grid/src/features/treeData/useGridTreeData.ts index d6dd8e45401f6..e884783dd924b 100644 --- a/packages/grid/x-grid/src/features/treeData/useGridTreeData.ts +++ b/packages/grid/x-grid/src/features/treeData/useGridTreeData.ts @@ -1,9 +1,23 @@ +import * as React from 'react' +import {useGridApiEventHandler} from "../../../../_modules_/grid/hooks/root/useGridApiEventHandler"; +import { + GridApiRef, + GridComponentProps, + GridEvents, + gridRowsLookupSelector, +} from "../../../../_modules_/grid"; + /** * Only available in DataGridPro */ -import {useGridApiEventHandler} from "../../../../_modules_/grid/hooks/root/useGridApiEventHandler"; -import {GridApiRef, GridComponentProps, GridEvents } from "../../../../_modules_/grid"; - export const useGridTreeData = (apiRef: GridApiRef, props: Pick) => { - useGridApiEventHandler(apiRef, GridEvents.rowsSet, console.log) + const getDataPath = React.useCallback((data) => [], []) + + const handleRowsSet = React.useCallback(() => { + const rowsLookup = gridRowsLookupSelector(apiRef.current.state) + + console.log(rowsLookup) + }, [apiRef]) + + useGridApiEventHandler(apiRef, GridEvents.rowsSet, handleRowsSet) } \ No newline at end of file diff --git a/packages/grid/x-grid/src/useDataGridProComponent.tsx b/packages/grid/x-grid/src/useDataGridProComponent.tsx index 8cc3da82d39db..a5a24826a60f1 100644 --- a/packages/grid/x-grid/src/useDataGridProComponent.tsx +++ b/packages/grid/x-grid/src/useDataGridProComponent.tsx @@ -34,6 +34,7 @@ import { useRenderInfoLog } from '../../_modules_/grid/hooks/utils/useRenderInfo import { useGridResizeContainer } from '../../_modules_/grid/hooks/utils/useGridResizeContainer'; import { useStateProp } from '../../_modules_/grid/hooks/utils/useStateProp'; import { GridApiRef } from '../../_modules_/grid/models/api/gridApiRef'; +import {useGridTreeData} from "./features/treeData"; export const useDataGridProComponent = (apiRef: GridApiRef, props: GridComponentProps) => { useGridLoggerFactory(apiRef, props); @@ -45,6 +46,7 @@ export const useDataGridProComponent = (apiRef: GridApiRef, props: GridComponent useGridFreezeRows(apiRef, props); useGridColumns(apiRef, props); useGridRows(apiRef, props); + useGridTreeData(apiRef, props) useGridParamsApi(apiRef); useGridEditRows(apiRef, props); useGridFocus(apiRef, props); diff --git a/packages/storybook/src/stories/grid-tree-data.stories.tsx b/packages/storybook/src/stories/grid-tree-data.stories.tsx new file mode 100644 index 0000000000000..e69de29bb2d1d From b3611a45356022387e03e5dfd98f15b0463e7400 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 13 Sep 2021 10:19:42 +0200 Subject: [PATCH 059/390] Create tree --- .../grid/_modules_/grid/GridComponentProps.ts | 6 ++ .../grid/hooks/features/rows/useGridRows.ts | 1 - .../_modules_/grid/models/gridOptions.tsx | 2 +- .../grid/_modules_/grid/models/gridRows.ts | 6 +- .../src/features/treeData/GridTreeDataApi.ts | 6 +- .../x-grid/src/features/treeData/index.ts | 4 +- .../src/features/treeData/useGridTreeData.ts | 90 +++++++++++++++---- .../x-grid/src/useDataGridProComponent.tsx | 4 +- .../src/stories/grid-tree-data.stories.tsx | 41 +++++++++ 9 files changed, 133 insertions(+), 27 deletions(-) diff --git a/packages/grid/_modules_/grid/GridComponentProps.ts b/packages/grid/_modules_/grid/GridComponentProps.ts index 1ace030b00e76..2a3d44f00f7ef 100644 --- a/packages/grid/_modules_/grid/GridComponentProps.ts +++ b/packages/grid/_modules_/grid/GridComponentProps.ts @@ -99,6 +99,12 @@ interface GridComponentOtherProps { * @returns {boolean} A boolean indicating if the cell is selectable. */ isRowSelectable?: (params: GridRowParams) => boolean; + /** + * Determines the path of a row in the tree data + * @param {GridRowParams} params With all properties from [[GridRowParams]]. + * @returns {string[]} the path to the row + */ + getTreeDataPath?: (params: GridRowParams) => string[]; /** * Callback fired when the edit cell value changes. * @param {GridEditCellPropsParams} params With all properties from [[GridEditCellPropsParams]]. diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 889035af7e888..404e2af05c7fd 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -20,7 +20,6 @@ import { gridRowsLookupSelector, unorderedGridRowIdsSelector, } from './gridRowsSelector'; -import {useGridApiEventHandler} from "../../root/useGridApiEventHandler"; export interface GridRowsInternalCache { state: GridRowsState; diff --git a/packages/grid/_modules_/grid/models/gridOptions.tsx b/packages/grid/_modules_/grid/models/gridOptions.tsx index fe1118967b01e..5c15bab04be9f 100644 --- a/packages/grid/_modules_/grid/models/gridOptions.tsx +++ b/packages/grid/_modules_/grid/models/gridOptions.tsx @@ -196,7 +196,7 @@ export interface GridSimpleOptions { * If `true`, the rows will be gathered in a tree structure, following the `getDataPath` prop * @default false */ - treeData: boolean + treeData: boolean; /** * Set the area at the bottom of the grid viewport where onRowsScrollEnd is called. */ diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index 71594bae6655a..6837fd20c41b4 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -13,9 +13,9 @@ export interface GridRowModelUpdate extends GridRowData { } export interface GridRowTreeNode { - value: GridRowModel - collapsed: boolean - children: [], + value: GridRowModel; + collapsed: boolean; + children: []; } /** diff --git a/packages/grid/x-grid/src/features/treeData/GridTreeDataApi.ts b/packages/grid/x-grid/src/features/treeData/GridTreeDataApi.ts index 28bb88ce7908b..d35ee807487d8 100644 --- a/packages/grid/x-grid/src/features/treeData/GridTreeDataApi.ts +++ b/packages/grid/x-grid/src/features/treeData/GridTreeDataApi.ts @@ -1,5 +1,5 @@ -import {GridRowModel, GridRowTreeNode} from "../../../../_modules_"; +import { GridRowModel, GridRowTreeNode } from '../../../../_modules_'; export interface GridTreeDataApi { - groupRows: (flatRows: GridRowModel[]) => GridRowTreeNode[], -} \ No newline at end of file + groupRows: (flatRows: GridRowModel[]) => GridRowTreeNode[]; +} diff --git a/packages/grid/x-grid/src/features/treeData/index.ts b/packages/grid/x-grid/src/features/treeData/index.ts index 4d0253a7e61c1..faa592795ad68 100644 --- a/packages/grid/x-grid/src/features/treeData/index.ts +++ b/packages/grid/x-grid/src/features/treeData/index.ts @@ -1,2 +1,2 @@ -export * from './useGridTreeData' -export * from './GridTreeDataApi' \ No newline at end of file +export * from './useGridTreeData'; +export * from './GridTreeDataApi'; diff --git a/packages/grid/x-grid/src/features/treeData/useGridTreeData.ts b/packages/grid/x-grid/src/features/treeData/useGridTreeData.ts index e884783dd924b..9068ef3135c67 100644 --- a/packages/grid/x-grid/src/features/treeData/useGridTreeData.ts +++ b/packages/grid/x-grid/src/features/treeData/useGridTreeData.ts @@ -1,23 +1,83 @@ -import * as React from 'react' -import {useGridApiEventHandler} from "../../../../_modules_/grid/hooks/root/useGridApiEventHandler"; +import * as React from 'react'; +import { useGridApiEventHandler } from '../../../../_modules_/grid/hooks/root/useGridApiEventHandler'; import { - GridApiRef, - GridComponentProps, - GridEvents, - gridRowsLookupSelector, -} from "../../../../_modules_/grid"; + GridApiRef, + GridComponentProps, + GridEvents, + GridRowId, + GridRowModel, + gridRowsLookupSelector, +} from '../../../../_modules_/grid'; + +interface TreeNode { + node: GridRowModel; + children: Tree; +} + +type Tree = { [nodeName: string]: TreeNode }; + +interface RowWithPath { + node: GridRowModel; + id: GridRowId; + path: string[]; +} + +const insertRowInTree = (tree: Tree, id: GridRowId, row: GridRowModel, path: string[]): Tree => { + if (path.length === 0) { + throw new Error(`Material-UI: Could not insert row #${id} in the tree structure.`); + } + + if (path.length === 1) { + tree[path[0]] = { + node: row, + children: {}, + }; + } else { + const [nodeName, ...restPath] = path; + + if (!tree.hasOwnProperty(nodeName)) { + throw new Error(`Material-UI: Could not insert row #${id} in the tree structure.`); + } + + tree[nodeName].children = insertRowInTree(tree[nodeName].children, id, row, restPath); + } + + return tree; +}; /** * Only available in DataGridPro */ -export const useGridTreeData = (apiRef: GridApiRef, props: Pick) => { - const getDataPath = React.useCallback((data) => [], []) +export const useGridTreeData = ( + apiRef: GridApiRef, + props: Pick, +) => { + const handleRowsSet = React.useCallback(() => { + if (!props.treeData) { + return; + } + + if (!props.getTreeDataPath) { + throw new Error( + 'Material-UI: getTreeDataPath should always be defined when treeData is activated.', + ); + } + + const rows: RowWithPath[] = Object.entries(gridRowsLookupSelector(apiRef.current.state)) + .map(([rowId, row]) => ({ + node: row, + id: rowId, + path: props.getTreeDataPath!(apiRef.current.getRowParams(rowId)), + })) + .sort((a, b) => a.path.length - b.path.length); - const handleRowsSet = React.useCallback(() => { - const rowsLookup = gridRowsLookupSelector(apiRef.current.state) + const tree = {}; + rows.forEach((row) => { + insertRowInTree(tree, row.id, row.node, row.path); + }); - console.log(rowsLookup) - }, [apiRef]) + // console.log(tree); + }, [apiRef, props.treeData, props.getTreeDataPath]); - useGridApiEventHandler(apiRef, GridEvents.rowsSet, handleRowsSet) -} \ No newline at end of file + useGridApiEventHandler(apiRef, GridEvents.rowsSet, handleRowsSet); +}; diff --git a/packages/grid/x-grid/src/useDataGridProComponent.tsx b/packages/grid/x-grid/src/useDataGridProComponent.tsx index a5a24826a60f1..f052c5cb2f9d2 100644 --- a/packages/grid/x-grid/src/useDataGridProComponent.tsx +++ b/packages/grid/x-grid/src/useDataGridProComponent.tsx @@ -34,7 +34,7 @@ import { useRenderInfoLog } from '../../_modules_/grid/hooks/utils/useRenderInfo import { useGridResizeContainer } from '../../_modules_/grid/hooks/utils/useGridResizeContainer'; import { useStateProp } from '../../_modules_/grid/hooks/utils/useStateProp'; import { GridApiRef } from '../../_modules_/grid/models/api/gridApiRef'; -import {useGridTreeData} from "./features/treeData"; +import { useGridTreeData } from './features/treeData'; export const useDataGridProComponent = (apiRef: GridApiRef, props: GridComponentProps) => { useGridLoggerFactory(apiRef, props); @@ -45,8 +45,8 @@ export const useDataGridProComponent = (apiRef: GridApiRef, props: GridComponent useGridResizeContainer(apiRef, props); useGridFreezeRows(apiRef, props); useGridColumns(apiRef, props); + useGridTreeData(apiRef, props); useGridRows(apiRef, props); - useGridTreeData(apiRef, props) useGridParamsApi(apiRef); useGridEditRows(apiRef, props); useGridFocus(apiRef, props); diff --git a/packages/storybook/src/stories/grid-tree-data.stories.tsx b/packages/storybook/src/stories/grid-tree-data.stories.tsx index e69de29bb2d1d..25cc38ec69147 100644 --- a/packages/storybook/src/stories/grid-tree-data.stories.tsx +++ b/packages/storybook/src/stories/grid-tree-data.stories.tsx @@ -0,0 +1,41 @@ +import * as React from 'react'; +import { DataGridPro, GridColumns } from '@mui/x-data-grid-pro'; +import { Meta } from '@storybook/react'; + +export default { + title: 'X-Grid Tests/Tree Data', + component: DataGridPro, + parameters: { + options: { selectedPanel: 'storybook/storysource/panel' }, + }, +} as Meta; + +const rows = [ + { id: 0, name: 'A' }, + { id: 1, name: 'A.A' }, + { id: 2, name: 'B' }, + { id: 3, name: 'B.A' }, + { id: 4, name: 'B.B' }, + { id: 5, name: 'B.B.A' }, + { id: 6, name: 'C' }, +]; + +const columns: GridColumns = [ + { + field: 'id', + }, + { + field: 'name', + }, +]; + +export function TreeData() { + return ( + params.row.name.split('.')} + /> + ); +} From 876c377b14d50124bed3ff462359183fc08c5818 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 13 Sep 2021 10:39:46 +0200 Subject: [PATCH 060/390] Work --- .../features/treeData/GridTreeDataApi.ts | 11 +++ .../grid/hooks}/features/treeData/index.ts | 0 .../features/treeData/useGridTreeData.ts | 68 +++++++++++++++ .../grid/_modules_/grid/models/api/gridApi.ts | 4 +- .../grid/_modules_/grid/models/gridRows.ts | 7 +- .../src/features/treeData/GridTreeDataApi.ts | 5 -- .../src/features/treeData/useGridTreeData.ts | 83 ------------------- .../x-grid/src/useDataGridProComponent.tsx | 2 +- 8 files changed, 87 insertions(+), 93 deletions(-) create mode 100644 packages/grid/_modules_/grid/hooks/features/treeData/GridTreeDataApi.ts rename packages/grid/{x-grid/src => _modules_/grid/hooks}/features/treeData/index.ts (100%) create mode 100644 packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts delete mode 100644 packages/grid/x-grid/src/features/treeData/GridTreeDataApi.ts delete mode 100644 packages/grid/x-grid/src/features/treeData/useGridTreeData.ts diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/GridTreeDataApi.ts b/packages/grid/_modules_/grid/hooks/features/treeData/GridTreeDataApi.ts new file mode 100644 index 0000000000000..8075a1d0ba71b --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/features/treeData/GridTreeDataApi.ts @@ -0,0 +1,11 @@ +import { GridRowsLookup } from '../rows/gridRowsSelector'; +import { GridRowTree } from '../../../models/gridRows'; + +export interface GridTreeDataApi { + /** + * Create the tree structure for a given set of rows + * @param {GridRowsLookup} rowsLookup the rows to process + * @returns {GridRowTree} tree the tree structure containing all the rows + */ + groupRows: (rowsLookup: GridRowsLookup) => GridRowTree; +} diff --git a/packages/grid/x-grid/src/features/treeData/index.ts b/packages/grid/_modules_/grid/hooks/features/treeData/index.ts similarity index 100% rename from packages/grid/x-grid/src/features/treeData/index.ts rename to packages/grid/_modules_/grid/hooks/features/treeData/index.ts diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts new file mode 100644 index 0000000000000..0becd868eef8b --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -0,0 +1,68 @@ +import * as React from 'react'; +import { GridTreeDataApi } from './GridTreeDataApi'; +import { GridRowId, GridRowModel, GridRowTree } from '../../../models/gridRows'; +import { GridApiRef } from '../../../models/api/gridApiRef'; +import { GridComponentProps } from '../../../GridComponentProps'; +import { GridRowsLookup } from '../rows/gridRowsSelector'; +import { useGridApiMethod } from '../../root/useGridApiMethod'; + +const insertRowInTree = (tree: GridRowTree, id: GridRowId, row: GridRowModel, path: string[]) => { + if (path.length === 0) { + throw new Error(`Material-UI: Could not insert row #${id} in the tree structure.`); + } + + if (path.length === 1) { + tree[path[0]] = { + node: row, + children: {}, + }; + } else { + const [nodeName, ...restPath] = path; + + if (!tree.hasOwnProperty(nodeName)) { + throw new Error(`Material-UI: Could not insert row #${id} in the tree structure.`); + } + + insertRowInTree(tree[nodeName].children, id, row, restPath); + } +}; + +/** + * Only available in DataGridPro + */ +export const useGridTreeData = ( + apiRef: GridApiRef, + props: Pick, +) => { + const groupRows = React.useCallback( + (rowsLookup: GridRowsLookup) => { + if (!props.getTreeDataPath) { + throw new Error( + 'Material-UI: getTreeDataPath should always be defined when calling apiRef.current.groupRows.', + ); + } + + const rows = Object.entries(rowsLookup) + .map(([rowId, row]) => ({ + node: row, + id: rowId, + path: props.getTreeDataPath!(apiRef.current.getRowParams(rowId)), + })) + .sort((a, b) => a.path.length - b.path.length); + + const tree = {}; + rows.forEach((row) => { + insertRowInTree(tree, row.id, row.node, row.path); + }); + + return tree; + }, + [apiRef, props.getTreeDataPath], + ); + + const treeDataApi: GridTreeDataApi = { + groupRows, + }; + + useGridApiMethod(apiRef, treeDataApi, 'GridTreeDataApi'); +}; diff --git a/packages/grid/_modules_/grid/models/api/gridApi.ts b/packages/grid/_modules_/grid/models/api/gridApi.ts index 2007409b6e2c5..3a46ce2c868a8 100644 --- a/packages/grid/_modules_/grid/models/api/gridApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridApi.ts @@ -21,6 +21,7 @@ import { GridStateApi } from './gridStateApi'; import { GridVirtualizationApi } from './gridVirtualizationApi'; import { GridLoggerApi } from './gridLoggerApi'; import { GridScrollApi } from './gridScrollApi'; +import type { GridTreeDataApi } from '../../hooks/features/treeData'; /** * The full grid API. @@ -48,4 +49,5 @@ export interface GridApi GridLocaleTextApi, GridControlStateApi, GridClipboardApi, - GridScrollApi {} + GridScrollApi, + Partial {} diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index 6837fd20c41b4..a1a2e2526a95c 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -13,11 +13,12 @@ export interface GridRowModelUpdate extends GridRowData { } export interface GridRowTreeNode { - value: GridRowModel; - collapsed: boolean; - children: []; + node: GridRowModel; + children: GridRowTree; } +export type GridRowTree = { [nodeName: string]: GridRowTreeNode }; + /** * The type of Id supported by the grid. */ diff --git a/packages/grid/x-grid/src/features/treeData/GridTreeDataApi.ts b/packages/grid/x-grid/src/features/treeData/GridTreeDataApi.ts deleted file mode 100644 index d35ee807487d8..0000000000000 --- a/packages/grid/x-grid/src/features/treeData/GridTreeDataApi.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { GridRowModel, GridRowTreeNode } from '../../../../_modules_'; - -export interface GridTreeDataApi { - groupRows: (flatRows: GridRowModel[]) => GridRowTreeNode[]; -} diff --git a/packages/grid/x-grid/src/features/treeData/useGridTreeData.ts b/packages/grid/x-grid/src/features/treeData/useGridTreeData.ts deleted file mode 100644 index 9068ef3135c67..0000000000000 --- a/packages/grid/x-grid/src/features/treeData/useGridTreeData.ts +++ /dev/null @@ -1,83 +0,0 @@ -import * as React from 'react'; -import { useGridApiEventHandler } from '../../../../_modules_/grid/hooks/root/useGridApiEventHandler'; -import { - GridApiRef, - GridComponentProps, - GridEvents, - GridRowId, - GridRowModel, - gridRowsLookupSelector, -} from '../../../../_modules_/grid'; - -interface TreeNode { - node: GridRowModel; - children: Tree; -} - -type Tree = { [nodeName: string]: TreeNode }; - -interface RowWithPath { - node: GridRowModel; - id: GridRowId; - path: string[]; -} - -const insertRowInTree = (tree: Tree, id: GridRowId, row: GridRowModel, path: string[]): Tree => { - if (path.length === 0) { - throw new Error(`Material-UI: Could not insert row #${id} in the tree structure.`); - } - - if (path.length === 1) { - tree[path[0]] = { - node: row, - children: {}, - }; - } else { - const [nodeName, ...restPath] = path; - - if (!tree.hasOwnProperty(nodeName)) { - throw new Error(`Material-UI: Could not insert row #${id} in the tree structure.`); - } - - tree[nodeName].children = insertRowInTree(tree[nodeName].children, id, row, restPath); - } - - return tree; -}; - -/** - * Only available in DataGridPro - */ -export const useGridTreeData = ( - apiRef: GridApiRef, - props: Pick, -) => { - const handleRowsSet = React.useCallback(() => { - if (!props.treeData) { - return; - } - - if (!props.getTreeDataPath) { - throw new Error( - 'Material-UI: getTreeDataPath should always be defined when treeData is activated.', - ); - } - - const rows: RowWithPath[] = Object.entries(gridRowsLookupSelector(apiRef.current.state)) - .map(([rowId, row]) => ({ - node: row, - id: rowId, - path: props.getTreeDataPath!(apiRef.current.getRowParams(rowId)), - })) - .sort((a, b) => a.path.length - b.path.length); - - const tree = {}; - rows.forEach((row) => { - insertRowInTree(tree, row.id, row.node, row.path); - }); - - // console.log(tree); - }, [apiRef, props.treeData, props.getTreeDataPath]); - - useGridApiEventHandler(apiRef, GridEvents.rowsSet, handleRowsSet); -}; diff --git a/packages/grid/x-grid/src/useDataGridProComponent.tsx b/packages/grid/x-grid/src/useDataGridProComponent.tsx index f052c5cb2f9d2..8e982ec29268b 100644 --- a/packages/grid/x-grid/src/useDataGridProComponent.tsx +++ b/packages/grid/x-grid/src/useDataGridProComponent.tsx @@ -34,7 +34,7 @@ import { useRenderInfoLog } from '../../_modules_/grid/hooks/utils/useRenderInfo import { useGridResizeContainer } from '../../_modules_/grid/hooks/utils/useGridResizeContainer'; import { useStateProp } from '../../_modules_/grid/hooks/utils/useStateProp'; import { GridApiRef } from '../../_modules_/grid/models/api/gridApiRef'; -import { useGridTreeData } from './features/treeData'; +import { useGridTreeData } from '../../_modules_/grid/hooks/features/treeData'; export const useDataGridProComponent = (apiRef: GridApiRef, props: GridComponentProps) => { useGridLoggerFactory(apiRef, props); From 8b7d651c9056b3e93bcb21664c6cce0fd0b4ddb7 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 13 Sep 2021 10:40:50 +0200 Subject: [PATCH 061/390] Fix --- packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 404e2af05c7fd..c0ba913114ec6 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -242,6 +242,7 @@ export const useGridRows = ( React.useEffect(() => { return () => { if (rowsCache.current.timeout !== null) { + // eslint-disable-next-line react-hooks/exhaustive-deps clearTimeout(rowsCache.current.timeout); } }; From e38f9e6691e88266071b9e3e4e5d8f19c60922b3 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 13 Sep 2021 11:15:48 +0200 Subject: [PATCH 062/390] Do not throttle the prop updates --- .../grid/hooks/features/rows/useGridRows.ts | 60 +++---- .../_modules_/grid/models/gridOptions.tsx | 4 +- .../src/tests/editRows.DataGridPro.test.tsx | 3 + .../src/tests/export.DataGridPro.test.tsx | 1 + .../src/tests/filtering.DataGridPro.test.tsx | 4 +- .../src/tests/rows.DataGridPro.test.tsx | 153 ++++++++++++++---- .../src/tests/sorting.DataGridPro.test.tsx | 2 +- 7 files changed, 152 insertions(+), 75 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index c0ba913114ec6..25eaa74ee39df 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -99,7 +99,7 @@ export const useGridRows = ( ); const throttledRowsChange = React.useCallback( - (newState: GridRowsState) => { + (newState: GridRowsState, throttle: boolean) => { const run = () => { rowsCache.current.timeout = null; rowsCache.current.lastUpdateMs = Date.now(); @@ -115,6 +115,11 @@ export const useGridRows = ( rowsCache.current.state = newState; rowsCache.current.timeout = null; + if (!throttle) { + run(); + return; + } + const throttleRemainingTimeMs = rowsCache.current.lastUpdateMs === null ? 0 @@ -122,9 +127,10 @@ export const useGridRows = ( if (throttleRemainingTimeMs > 0) { rowsCache.current.timeout = setTimeout(run, throttleRemainingTimeMs); - } else { - run(); + return; } + + run(); }, [apiRef, forceUpdate, setGridState, rowsCache, props.throttleRowsMs], ); @@ -132,7 +138,7 @@ export const useGridRows = ( const setRows = React.useCallback( (rows) => { logger.debug(`Updating all rows, new length ${rows.length}`); - throttledRowsChange(convertGridRowsPropToState(rows, props.rowCount, props.getRowId)); + throttledRowsChange(convertGridRowsPropToState(rows, props.rowCount, props.getRowId), true); }, [logger, throttledRowsChange, props.rowCount, props.getRowId], ); @@ -156,58 +162,33 @@ export const useGridRows = ( } }); - const addedRows: [GridRowId, GridRowModel][] = []; - const modifiedRows: [GridRowId, GridRowModel][] = []; const deletedRowIds: GridRowId[] = []; + const idRowsLookup = { ...rowsCache.current.state.idRowsLookup }; + let allRows = [...rowsCache.current.state.allRows]; + uniqUpdates.forEach((partialRow, id) => { // eslint-disable-next-line no-underscore-dangle if (partialRow._action === 'delete') { + delete idRowsLookup[id]; deletedRowIds.push(id); return; } const oldRow = apiRef.current.getRow(id); if (!oldRow) { - addedRows.push([id, partialRow]); + idRowsLookup[id] = partialRow; + allRows.push(id); return; } - modifiedRows.push([id, partialRow]); + idRowsLookup[id] = { ...apiRef.current.getRow(id), ...partialRow }; }); - let idRowsLookup = { ...rowsCache.current.state.idRowsLookup }; - let allRows = [...rowsCache.current.state.allRows]; - if (deletedRowIds.length > 0) { - deletedRowIds.forEach((id) => { - delete idRowsLookup[id]; - }); - allRows = allRows.filter((id) => !deletedRowIds.includes(id)); } - if (addedRows.length > 0) { - idRowsLookup = { - ...idRowsLookup, - ...Object.fromEntries(addedRows), - }; - - allRows = [...allRows, ...addedRows.map(([id]) => id)]; - } - - if (modifiedRows.length > 0) { - idRowsLookup = { - ...idRowsLookup, - ...Object.fromEntries( - modifiedRows.map(([id, partialRow]) => [ - id, - { ...apiRef.current.getRow(id), ...partialRow }, - ]), - ), - }; - } - const totalRowCount = props.rowCount && props.rowCount > allRows.length ? props.rowCount : allRows.length; @@ -217,7 +198,7 @@ export const useGridRows = ( totalRowCount, }; - throttledRowsChange(state); + throttledRowsChange(state, true); }, [apiRef, props.getRowId, props.rowCount, throttledRowsChange], ); @@ -250,7 +231,10 @@ export const useGridRows = ( React.useEffect(() => { logger.debug(`Updating all rows, new length ${props.rows.length}`); - throttledRowsChange(convertGridRowsPropToState(props.rows, props.rowCount, props.getRowId)); + throttledRowsChange( + convertGridRowsPropToState(props.rows, props.rowCount, props.getRowId), + false, + ); }, [props.rows, props.rowCount, props.getRowId, logger, throttledRowsChange]); const rowApi: GridRowApi = { diff --git a/packages/grid/_modules_/grid/models/gridOptions.tsx b/packages/grid/_modules_/grid/models/gridOptions.tsx index 01e97354d5e02..382020d858ec5 100644 --- a/packages/grid/_modules_/grid/models/gridOptions.tsx +++ b/packages/grid/_modules_/grid/models/gridOptions.tsx @@ -221,7 +221,7 @@ export interface GridSimpleOptions { /** * If positive, the Grid will throttle updates coming from `props.rows`, `apiRef.current.updateRows` or `apiRef.current.setRows`. * It can be useful if you have a high update rate but do not want to do heavy work like filtering / sorting or rendering on each individual update. - * @default 0 + * @default 50 */ throttleRowsMs: number; } @@ -266,5 +266,5 @@ export const GRID_DEFAULT_SIMPLE_OPTIONS: GridSimpleOptions = { showColumnRightBorder: false, sortingOrder: ['asc' as const, 'desc' as const, null], sortingMode: GridFeatureModeConstant.client, - throttleRowsMs: 0, + throttleRowsMs: 50, }; diff --git a/packages/grid/x-grid/src/tests/editRows.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/editRows.DataGridPro.test.tsx index 45a483d6921c5..6559296a0cd4d 100644 --- a/packages/grid/x-grid/src/tests/editRows.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/editRows.DataGridPro.test.tsx @@ -51,6 +51,7 @@ describe(' - Edit Rows', () => { { field: 'brand', editable: true }, { field: 'year', editable: true }, ], + throttleRowsMs: 0, }; }); @@ -543,6 +544,7 @@ describe(' - Edit Rows', () => { brand: 'Nike', }, ]} + throttleRowsMs={0} /> , ); @@ -588,6 +590,7 @@ describe(' - Edit Rows', () => { country: 'fr', }, ]} + throttleRowsMs={0} /> , ); diff --git a/packages/grid/x-grid/src/tests/export.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/export.DataGridPro.test.tsx index cf64cf1d0f448..ff7c844abb84b 100644 --- a/packages/grid/x-grid/src/tests/export.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/export.DataGridPro.test.tsx @@ -26,6 +26,7 @@ describe(' - Export', () => { - Filter', () => { }); it('should apply the filterModel prop correctly on GridApiRef setRows', () => { - render(); + render(); const newRows = [ { @@ -116,7 +116,7 @@ describe(' - Filter', () => { }); it('should apply the filterModel prop correctly on GridApiRef update row data', () => { - render(); + render(); apiRef.current.updateRows([{ id: 1, brand: 'Fila' }]); apiRef.current.updateRows([{ id: 0, brand: 'Patagonia' }]); expect(getColumnValues()).to.deep.equal(['Patagonia', 'Fila', 'Puma']); diff --git a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx index 599e078eddcd1..cac6f3c39f227 100644 --- a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx @@ -78,7 +78,12 @@ describe(' - Rows', () => { apiRef = useGridApiRef(); return (
- +
); }; @@ -137,15 +142,7 @@ describe(' - Rows', () => { }); describe('props: rows', () => { - beforeEach(() => { - clock = useFakeTimers(); - }); - - afterEach(() => { - clock.restore(); - }); - - it('should support new dataset throttle', () => { + it('should not throttle even when props.throttleRowsMs is defined', () => { const { rows, columns } = getData(5, 2); const Test = (props: Pick) => ( @@ -157,18 +154,12 @@ describe(' - Rows', () => { const { setProps } = render(); expect(getColumnValues(0)).to.deep.equal(['0', '1']); - clock.tick(150); - setProps({ rows: rows.slice(0, 3) }); - expect(getColumnValues(0)).to.deep.equal(['0', '1', '2']); - clock.tick(50); setProps({ rows }); - expect(getColumnValues(0)).to.deep.equal(['0', '1', '2']); - clock.tick(50); expect(getColumnValues(0)).to.deep.equal(['0', '1', '2', '3', '4']); }); }); - describe('updateRows', () => { + describe('apiRef: updateRows', () => { beforeEach(() => { clock = useFakeTimers(); @@ -207,25 +198,35 @@ describe(' - Rows', () => { ); }; - it('should allow to throttle row update', () => { - render(); + it('should throttle by default', () => { + render(); expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); - const newRows = [ - { - id: 3, - brand: 'Asics', - }, - ]; - apiRef.current.setRows(newRows); + apiRef.current.updateRows([{ id: 1, brand: 'Fila' }]); + clock.tick(25); + expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); + clock.tick(25); + expect(getColumnValues()).to.deep.equal(['Nike', 'Fila', 'Puma']); + }); + it('should allow to set the throttle duration', () => { + render(); + expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); + apiRef.current.updateRows([{ id: 1, brand: 'Fila' }]); clock.tick(50); expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); clock.tick(50); - expect(getColumnValues()).to.deep.equal(['Asics']); + expect(getColumnValues()).to.deep.equal(['Nike', 'Fila', 'Puma']); + }); + + it('should allow to disable throttle', () => { + render(); + expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); + apiRef.current.updateRows([{ id: 1, brand: 'Fila' }]); + expect(getColumnValues()).to.deep.equal(['Nike', 'Fila', 'Puma']); }); it('should allow to update row data', () => { - render(); + render(); apiRef.current.updateRows([{ id: 1, brand: 'Fila' }]); apiRef.current.updateRows([{ id: 0, brand: 'Pata' }]); apiRef.current.updateRows([{ id: 2, brand: 'Pum' }]); @@ -233,7 +234,7 @@ describe(' - Rows', () => { }); it('update row data can also add rows', () => { - render(); + render(); apiRef.current.updateRows([{ id: 1, brand: 'Fila' }]); apiRef.current.updateRows([{ id: 0, brand: 'Pata' }]); apiRef.current.updateRows([{ id: 2, brand: 'Pum' }]); @@ -242,7 +243,7 @@ describe(' - Rows', () => { }); it('update row data can also add rows in bulk', () => { - render(); + render(); apiRef.current.updateRows([ { id: 1, brand: 'Fila' }, { id: 0, brand: 'Pata' }, @@ -253,7 +254,7 @@ describe(' - Rows', () => { }); it('update row data can also delete rows', () => { - render(); + render(); apiRef.current.updateRows([{ id: 1, _action: 'delete' }]); apiRef.current.updateRows([{ id: 0, brand: 'Apple' }]); apiRef.current.updateRows([{ id: 2, _action: 'delete' }]); @@ -262,7 +263,7 @@ describe(' - Rows', () => { }); it('update row data can also delete rows in bulk', () => { - render(); + render(); apiRef.current.updateRows([ { id: 1, _action: 'delete' }, { id: 0, brand: 'Apple' }, @@ -283,6 +284,7 @@ describe(' - Rows', () => { apiRef={apiRef} rows={baselineProps.rows.map((row) => ({ idField: row.id, brand: row.brand }))} getRowId={getRowId} + throttleRowsMs={0} /> ); @@ -300,6 +302,93 @@ describe(' - Rows', () => { }); }); + describe('apiRef: setRows', () => { + beforeEach(() => { + clock = useFakeTimers(); + + baselineProps = { + autoHeight: isJSDOM, + rows: [ + { + id: 0, + brand: 'Nike', + }, + { + id: 1, + brand: 'Adidas', + }, + { + id: 2, + brand: 'Puma', + }, + ], + columns: [{ field: 'brand', headerName: 'Brand' }], + }; + }); + + afterEach(() => { + clock.restore(); + }); + + let apiRef: GridApiRef; + + const TestCase = (props: Partial) => { + apiRef = useGridApiRef(); + return ( +
+ +
+ ); + }; + + it('should throttle by default', () => { + render(); + expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); + const newRows = [ + { + id: 3, + brand: 'Asics', + }, + ]; + apiRef.current.setRows(newRows); + + clock.tick(25); + expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); + clock.tick(25); + expect(getColumnValues()).to.deep.equal(['Asics']); + }); + + it('should allow to set the throttle duration', () => { + render(); + expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); + const newRows = [ + { + id: 3, + brand: 'Asics', + }, + ]; + apiRef.current.setRows(newRows); + + clock.tick(50); + expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); + clock.tick(50); + expect(getColumnValues()).to.deep.equal(['Asics']); + }); + + it('should allow to disable throttle', () => { + render(); + expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); + const newRows = [ + { + id: 3, + brand: 'Asics', + }, + ]; + apiRef.current.setRows(newRows); + expect(getColumnValues()).to.deep.equal(['Asics']); + }); + }); + describe('virtualization', () => { before(function beforeHook() { if (isJSDOM) { diff --git a/packages/grid/x-grid/src/tests/sorting.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/sorting.DataGridPro.test.tsx index 3fb6d3823962e..b8c61979e52c0 100644 --- a/packages/grid/x-grid/src/tests/sorting.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/sorting.DataGridPro.test.tsx @@ -68,7 +68,7 @@ describe(' - Sorting', () => { const renderBrandSortedAsc = () => { const sortModel: GridSortModel = [{ field: 'brand', sort: 'asc' }]; - render(); + render(); }; it('should apply the sortModel prop correctly', () => { From 943879f3db91bfc4e1e73305642aa08cb387fb4d Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 13 Sep 2021 11:26:26 +0200 Subject: [PATCH 063/390] yarn docs:api:build --- packages/grid/_modules_/grid/models/gridOptions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/models/gridOptions.tsx b/packages/grid/_modules_/grid/models/gridOptions.tsx index 382020d858ec5..4a36c0b8b5263 100644 --- a/packages/grid/_modules_/grid/models/gridOptions.tsx +++ b/packages/grid/_modules_/grid/models/gridOptions.tsx @@ -219,7 +219,7 @@ export interface GridSimpleOptions { */ sortingMode: GridFeatureMode; /** - * If positive, the Grid will throttle updates coming from `props.rows`, `apiRef.current.updateRows` or `apiRef.current.setRows`. + * If positive, the Grid will throttle updates coming from `apiRef.current.updateRows` and `apiRef.current.setRows`. * It can be useful if you have a high update rate but do not want to do heavy work like filtering / sorting or rendering on each individual update. * @default 50 */ From f98f8789c4a1696b59244da12be98a38e2953706 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 13 Sep 2021 11:36:13 +0200 Subject: [PATCH 064/390] proptypes --- packages/grid/x-grid/src/DataGridPro.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index d5437229eaeda..e4e46cd49e371 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -705,9 +705,9 @@ DataGridProRaw.propTypes = { */ style: PropTypes.object, /** - * If positive, the Grid will throttle updates coming from `props.rows`, `apiRef.current.updateRows` or `apiRef.current.setRows`. + * If positive, the Grid will throttle updates coming from `apiRef.current.updateRows` and `apiRef.current.setRows`. * It can be useful if you have a high update rate but do not want to do heavy work like filtering / sorting or rendering on each individual update. - * @default 0 + * @default 50 */ throttleRowsMs: PropTypes.number, } as any; From 23bd5e99a0311ac6ae0465dcb24d1f14ec919957 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 13 Sep 2021 12:17:10 +0200 Subject: [PATCH 065/390] Store tree in rows state --- .../grid/hooks/features/rows/gridRowsState.ts | 4 +++- .../grid/hooks/features/rows/useGridRows.ts | 21 ++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts index f86d234f2f046..0ee070fc2b26a 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts @@ -1,13 +1,15 @@ -import { GridRowId, GridRowModel } from '../../../models/gridRows'; +import { GridRowId, GridRowModel, GridRowTree } from '../../../models/gridRows'; export interface GridRowsState { idRowsLookup: Record; allRows: GridRowId[]; + tree: GridRowTree; totalRowCount: number; } export const getInitialGridRowState: () => GridRowsState = () => ({ idRowsLookup: {}, + tree: {}, allRows: [], totalRowCount: 0, }); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 25eaa74ee39df..87b73f44ddf67 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -10,6 +10,7 @@ import { GridRowsProp, GridRowIdGetter, GridRowData, + GridRowTree, } from '../../../models/gridRows'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; @@ -17,12 +18,15 @@ import { useGridState } from '../core/useGridState'; import { getInitialGridRowState, GridRowsState } from './gridRowsState'; import { gridRowCountSelector, + GridRowsLookup, gridRowsLookupSelector, unorderedGridRowIdsSelector, } from './gridRowsSelector'; +export type GridRowsInternalCacheState = Omit; + export interface GridRowsInternalCache { - state: GridRowsState; + state: GridRowsInternalCacheState; timeout: NodeJS.Timeout | null; lastUpdateMs: number | null; } @@ -41,7 +45,7 @@ export function convertGridRowsPropToState( rows: GridRowsProp, propRowCount?: number, rowIdGetter?: GridRowIdGetter, -): GridRowsState { +): GridRowsInternalCacheState { const state: GridRowsState = { ...getInitialGridRowState(), totalRowCount: propRowCount && propRowCount > rows.length ? propRowCount : rows.length, @@ -56,6 +60,9 @@ export function convertGridRowsPropToState( return state; } +const getFlatRowTree = (lookup: GridRowsLookup): GridRowTree => + Object.fromEntries(Object.entries(lookup).map(([id, row]) => [id, { node: row, children: {} }])); + /** * @requires useGridSorting (method) * TODO: Impossible priority - useGridSorting also needs to be after useGridRows (which causes all the existence check for apiRef.current.apiRef.current.getSortedRowIds) @@ -99,11 +106,15 @@ export const useGridRows = ( ); const throttledRowsChange = React.useCallback( - (newState: GridRowsState, throttle: boolean) => { + (newState: GridRowsInternalCacheState, throttle: boolean) => { const run = () => { rowsCache.current.timeout = null; rowsCache.current.lastUpdateMs = Date.now(); - setGridState((state) => ({ ...state, rows: rowsCache.current.state })); + const rowState = rowsCache.current.state; + const tree = apiRef.current.groupRows + ? apiRef.current.groupRows(rowState.idRowsLookup) + : getFlatRowTree(rowState.idRowsLookup); + setGridState((state) => ({ ...state, rows: { ...rowState, tree } })); apiRef.current.publishEvent(GridEvents.rowsSet); forceUpdate(); }; @@ -192,7 +203,7 @@ export const useGridRows = ( const totalRowCount = props.rowCount && props.rowCount > allRows.length ? props.rowCount : allRows.length; - const state: GridRowsState = { + const state: GridRowsInternalCacheState = { idRowsLookup, allRows, totalRowCount, From 42229925c867f7384a7556d2d93666c7ea45e5f2 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 13 Sep 2021 13:35:59 +0200 Subject: [PATCH 066/390] Sort using tree --- .../grid/_modules_/grid/GridComponentProps.ts | 4 +- .../hooks/features/rows/gridRowsSelector.ts | 2 + .../grid/hooks/features/rows/useGridRows.ts | 7 ++- .../features/sorting/gridSortingState.ts | 8 +++- .../hooks/features/sorting/useGridSorting.ts | 47 ++++++++++++++----- .../features/treeData/GridTreeDataApi.ts | 2 +- .../features/treeData/useGridTreeData.ts | 26 ++++------ .../grid/_modules_/grid/models/gridRows.ts | 2 +- .../src/stories/grid-state.stories.tsx | 15 +++++- .../src/stories/grid-tree-data.stories.tsx | 11 ++--- 10 files changed, 76 insertions(+), 48 deletions(-) diff --git a/packages/grid/_modules_/grid/GridComponentProps.ts b/packages/grid/_modules_/grid/GridComponentProps.ts index 3cc7ecd0e6711..ad1e4406ba369 100644 --- a/packages/grid/_modules_/grid/GridComponentProps.ts +++ b/packages/grid/_modules_/grid/GridComponentProps.ts @@ -8,7 +8,7 @@ import { GridMergedOptions, } from './models/gridOptions'; import { MuiEvent } from './models/muiEvent'; -import { GridRowId, GridRowIdGetter, GridRowsProp } from './models/gridRows'; +import { GridRowId, GridRowIdGetter, GridRowModel, GridRowsProp } from './models/gridRows'; import { ElementSize } from './models/elementSize'; import { GridColumnTypesRecord } from './models/colDef/gridColTypeDef'; import { GridSortModel } from './models/gridSortModel'; @@ -104,7 +104,7 @@ interface GridComponentOtherProps { * @param {GridRowParams} params With all properties from [[GridRowParams]]. * @returns {string[]} the path to the row */ - getTreeDataPath?: (params: GridRowParams) => string[]; + getTreeDataPath?: (row: GridRowModel) => string[]; /** * Callback fired when the edit cell value changes. * @param {GridEditCellPropsParams} params With all properties from [[GridEditCellPropsParams]]. diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts index 6f8ca6dc17bd4..a10cc73308bf1 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts @@ -22,6 +22,8 @@ export const unorderedGridRowIdsSelector = createSelector( (rows: GridRowsState) => rows.allRows, ); +export const gridRowTreeSelector = createSelector(gridRowsStateSelector, (rows) => rows.tree); + export const unorderedGridRowModelsSelector = createSelector( gridRowsStateSelector, (rows: GridRowsState) => rows.allRows.map((id) => rows.idRowsLookup[id]), diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 87b73f44ddf67..2c75d26c51b4d 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -18,7 +18,6 @@ import { useGridState } from '../core/useGridState'; import { getInitialGridRowState, GridRowsState } from './gridRowsState'; import { gridRowCountSelector, - GridRowsLookup, gridRowsLookupSelector, unorderedGridRowIdsSelector, } from './gridRowsSelector'; @@ -60,8 +59,8 @@ export function convertGridRowsPropToState( return state; } -const getFlatRowTree = (lookup: GridRowsLookup): GridRowTree => - Object.fromEntries(Object.entries(lookup).map(([id, row]) => [id, { node: row, children: {} }])); +const getFlatRowTree = (rowIds: GridRowId[]): GridRowTree => + Object.fromEntries(rowIds.map((id) => [id, { id, children: {} }])); /** * @requires useGridSorting (method) @@ -113,7 +112,7 @@ export const useGridRows = ( const rowState = rowsCache.current.state; const tree = apiRef.current.groupRows ? apiRef.current.groupRows(rowState.idRowsLookup) - : getFlatRowTree(rowState.idRowsLookup); + : getFlatRowTree(rowState.allRows); setGridState((state) => ({ ...state, rows: { ...rowState, tree } })); apiRef.current.publishEvent(GridEvents.rowsSet); forceUpdate(); diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts index 405ff460515d3..16703d4c8d7d9 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts @@ -1,11 +1,17 @@ import { GridRowId } from '../../../models/gridRows'; import { GridSortModel } from '../../../models/gridSortModel'; +export interface GridSortedRowTreeNode { + id: GridRowId; + children: GridSortedRowTreeNode[]; +} + export interface GridSortingState { sortedRows: GridRowId[]; + sortedRowTree: GridSortedRowTreeNode[]; sortModel: GridSortModel; } export function getInitialGridSortingState(): GridSortingState { - return { sortedRows: [], sortModel: [] }; + return { sortedRows: [], sortedRowTree: [], sortModel: [] }; } diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index ee8bec88e0bae..51a9a67a7a813 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -7,7 +7,7 @@ import { GridCellValue } from '../../../models/gridCell'; import { GridColDef } from '../../../models/colDef/gridColDef'; import { GridFeatureModeConstant } from '../../../models/gridFeatureMode'; import { GridColumnHeaderParams } from '../../../models/params/gridColumnHeaderParams'; -import { GridRowId, GridRowModel } from '../../../models/gridRows'; +import { GridRowId, GridRowModel, GridRowTree } from '../../../models/gridRows'; import { GridFieldComparatorList, GridSortItem, @@ -23,6 +23,8 @@ import { useGridLogger } from '../../utils/useGridLogger'; import { allGridColumnsSelector } from '../columns/gridColumnsSelector'; import { useGridState } from '../core/useGridState'; import { sortedGridRowIdsSelector, sortedGridRowsSelector } from './gridSortingSelector'; +import { gridRowTreeSelector } from '../rows'; +import { GridSortedRowTreeNode } from './gridSortingState'; /** * @requires useGridRows (state, event) @@ -145,25 +147,22 @@ export const useGridSorting = ( ); const applySorting = React.useCallback(() => { - let sortedRows = apiRef.current.getAllRowIds(); + const unsortedRows = apiRef.current.getAllRowIds(); + const unsortedRowTree = gridRowTreeSelector(apiRef.current.state); if (props.sortingMode === GridFeatureModeConstant.server) { logger.debug('Skipping sorting rows as sortingMode = server'); - setGridState((state) => { - return { - ...state, - sorting: { ...state.sorting, sortedRows }, - }; - }); - return; } const sortModel = apiRef.current.state.sorting.sortModel; + const comparatorList = buildComparatorList(sortModel); - if (sortModel.length > 0) { - const comparatorList = buildComparatorList(sortModel); + // List sorting + // TODO: Remove + let sortedRows: GridRowId[]; + if (sortModel.length > 0 && props.sortingMode === GridFeatureModeConstant.client) { logger.debug('Sorting rows with ', sortModel); - sortedRows = sortedRows + sortedRows = unsortedRows .map((id) => { return comparatorList.map((colComparator) => { return getSortCellParams(id, colComparator.field); @@ -171,12 +170,34 @@ export const useGridSorting = ( }) .sort(comparatorListAggregate(comparatorList)) .map((field) => field[0].id); + } else { + sortedRows = unsortedRows; } + // Tree sorting + const aggregatedComparator = comparatorListAggregate(comparatorList); + + const sortRowTree = (tree: GridRowTree): GridSortedRowTreeNode[] => { + return Object.entries(tree) + .map(([name, value]) => { + const params = comparatorList.map((colComparator) => + getSortCellParams(value.id, colComparator.field), + ); + + return { value, name, params }; + }) + .sort((a, b) => aggregatedComparator(a.params, b.params)) + .map((node) => ({ + id: node.value.id, + children: sortRowTree(node.value.children), + })); + }; + const sortedRowTree = sortRowTree(unsortedRowTree); + setGridState((state) => { return { ...state, - sorting: { ...state.sorting, sortedRows }, + sorting: { ...state.sorting, sortedRows, sortedRowTree }, }; }); forceUpdate(); diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/GridTreeDataApi.ts b/packages/grid/_modules_/grid/hooks/features/treeData/GridTreeDataApi.ts index 8075a1d0ba71b..26299d53f42d7 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/GridTreeDataApi.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/GridTreeDataApi.ts @@ -1,5 +1,5 @@ -import { GridRowsLookup } from '../rows/gridRowsSelector'; import { GridRowTree } from '../../../models/gridRows'; +import { GridRowsLookup } from '../rows'; export interface GridTreeDataApi { /** diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 0becd868eef8b..908168897a245 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -1,19 +1,18 @@ import * as React from 'react'; import { GridTreeDataApi } from './GridTreeDataApi'; -import { GridRowId, GridRowModel, GridRowTree } from '../../../models/gridRows'; +import { GridRowId, GridRowTree } from '../../../models/gridRows'; import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridComponentProps } from '../../../GridComponentProps'; -import { GridRowsLookup } from '../rows/gridRowsSelector'; import { useGridApiMethod } from '../../root/useGridApiMethod'; -const insertRowInTree = (tree: GridRowTree, id: GridRowId, row: GridRowModel, path: string[]) => { +const insertRowInTree = (tree: GridRowTree, id: GridRowId, path: string[]) => { if (path.length === 0) { throw new Error(`Material-UI: Could not insert row #${id} in the tree structure.`); } if (path.length === 1) { tree[path[0]] = { - node: row, + id, children: {}, }; } else { @@ -23,7 +22,7 @@ const insertRowInTree = (tree: GridRowTree, id: GridRowId, row: GridRowModel, pa throw new Error(`Material-UI: Could not insert row #${id} in the tree structure.`); } - insertRowInTree(tree[nodeName].children, id, row, restPath); + insertRowInTree(tree[nodeName].children, id, restPath); } }; @@ -34,30 +33,25 @@ export const useGridTreeData = ( apiRef: GridApiRef, props: Pick, ) => { - const groupRows = React.useCallback( - (rowsLookup: GridRowsLookup) => { - if (!props.getTreeDataPath) { - throw new Error( - 'Material-UI: getTreeDataPath should always be defined when calling apiRef.current.groupRows.', - ); - } + const groupRows = React.useCallback( + (rowsLookup) => { + const getTreeDataPath = props.getTreeDataPath ?? ((row) => [row.id.toString()]); const rows = Object.entries(rowsLookup) .map(([rowId, row]) => ({ - node: row, id: rowId, - path: props.getTreeDataPath!(apiRef.current.getRowParams(rowId)), + path: getTreeDataPath(row), })) .sort((a, b) => a.path.length - b.path.length); const tree = {}; rows.forEach((row) => { - insertRowInTree(tree, row.id, row.node, row.path); + insertRowInTree(tree, row.id, row.path); }); return tree; }, - [apiRef, props.getTreeDataPath], + [props.getTreeDataPath], ); const treeDataApi: GridTreeDataApi = { diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index a1a2e2526a95c..ff18448ba674d 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -13,7 +13,7 @@ export interface GridRowModelUpdate extends GridRowData { } export interface GridRowTreeNode { - node: GridRowModel; + id: GridRowId; children: GridRowTree; } diff --git a/packages/storybook/src/stories/grid-state.stories.tsx b/packages/storybook/src/stories/grid-state.stories.tsx index fe171028157e2..c645064e75ef2 100644 --- a/packages/storybook/src/stories/grid-state.stories.tsx +++ b/packages/storybook/src/stories/grid-state.stories.tsx @@ -109,16 +109,27 @@ const defaultProps = { export function InitialState() { const [gridState, setGridState] = React.useState>({ - sorting: { sortModel: [{ field: 'brand', sort: 'desc' }], sortedRows: [2, 0, 1] }, + sorting: { + sortModel: [{ field: 'brand', sort: 'desc' }], + sortedRows: [2, 0, 1], + sortedRowTree: [ + { id: 2, children: [] }, + { id: 0, children: [] }, + { id: 1, children: [] }, + ], + }, }); const updateDirection = React.useCallback(() => { setGridState((p) => { const newDirection = p.sorting!.sortModel[0]?.sort === 'desc' ? 'asc' : 'desc'; + const sortedRows = [...p.sorting!.sortedRows!].reverse(); + return { ...p, sorting: { - sortedRows: [...p.sorting!.sortedRows!].reverse(), + sortedRows, + sortedRowTree: sortedRows.map((id) => ({ id, children: [] })), sortModel: [{ field: 'brand', sort: newDirection }], }, }; diff --git a/packages/storybook/src/stories/grid-tree-data.stories.tsx b/packages/storybook/src/stories/grid-tree-data.stories.tsx index 25cc38ec69147..b2c2334cd8f77 100644 --- a/packages/storybook/src/stories/grid-tree-data.stories.tsx +++ b/packages/storybook/src/stories/grid-tree-data.stories.tsx @@ -29,13 +29,8 @@ const columns: GridColumns = [ }, ]; +const getTreeDataPath = (row) => row.name.split('.'); + export function TreeData() { - return ( - params.row.name.split('.')} - /> - ); + return ; } From 695a13332137bb5a36cd68a176179d4c90294acb Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 13 Sep 2021 14:45:00 +0200 Subject: [PATCH 067/390] Code review --- .../_modules_/grid/models/gridOptions.tsx | 4 +- packages/grid/x-grid/src/DataGridPro.tsx | 2 +- .../src/tests/editRows.DataGridPro.test.tsx | 2 - .../src/tests/export.DataGridPro.test.tsx | 1 - .../src/tests/filtering.DataGridPro.test.tsx | 4 +- .../src/tests/rows.DataGridPro.test.tsx | 52 ++++--------------- .../src/tests/sorting.DataGridPro.test.tsx | 2 +- 7 files changed, 16 insertions(+), 51 deletions(-) diff --git a/packages/grid/_modules_/grid/models/gridOptions.tsx b/packages/grid/_modules_/grid/models/gridOptions.tsx index 4a36c0b8b5263..2711e3cbfa3a9 100644 --- a/packages/grid/_modules_/grid/models/gridOptions.tsx +++ b/packages/grid/_modules_/grid/models/gridOptions.tsx @@ -221,7 +221,7 @@ export interface GridSimpleOptions { /** * If positive, the Grid will throttle updates coming from `apiRef.current.updateRows` and `apiRef.current.setRows`. * It can be useful if you have a high update rate but do not want to do heavy work like filtering / sorting or rendering on each individual update. - * @default 50 + * @default 0 */ throttleRowsMs: number; } @@ -266,5 +266,5 @@ export const GRID_DEFAULT_SIMPLE_OPTIONS: GridSimpleOptions = { showColumnRightBorder: false, sortingOrder: ['asc' as const, 'desc' as const, null], sortingMode: GridFeatureModeConstant.client, - throttleRowsMs: 50, + throttleRowsMs: 0, }; diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index 688bb9dcca996..cf0f156d2f6b4 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -708,7 +708,7 @@ DataGridProRaw.propTypes = { /** * If positive, the Grid will throttle updates coming from `apiRef.current.updateRows` and `apiRef.current.setRows`. * It can be useful if you have a high update rate but do not want to do heavy work like filtering / sorting or rendering on each individual update. - * @default 50 + * @default 0 */ throttleRowsMs: PropTypes.number, } as any; diff --git a/packages/grid/x-grid/src/tests/editRows.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/editRows.DataGridPro.test.tsx index 6559296a0cd4d..d298368c260ff 100644 --- a/packages/grid/x-grid/src/tests/editRows.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/editRows.DataGridPro.test.tsx @@ -544,7 +544,6 @@ describe(' - Edit Rows', () => { brand: 'Nike', }, ]} - throttleRowsMs={0} /> , ); @@ -590,7 +589,6 @@ describe(' - Edit Rows', () => { country: 'fr', }, ]} - throttleRowsMs={0} /> , ); diff --git a/packages/grid/x-grid/src/tests/export.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/export.DataGridPro.test.tsx index ff7c844abb84b..cf64cf1d0f448 100644 --- a/packages/grid/x-grid/src/tests/export.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/export.DataGridPro.test.tsx @@ -26,7 +26,6 @@ describe(' - Export', () => { - Filter', () => { }); it('should apply the filterModel prop correctly on GridApiRef setRows', () => { - render(); + render(); const newRows = [ { @@ -116,7 +116,7 @@ describe(' - Filter', () => { }); it('should apply the filterModel prop correctly on GridApiRef update row data', () => { - render(); + render(); apiRef.current.updateRows([{ id: 1, brand: 'Fila' }]); apiRef.current.updateRows([{ id: 0, brand: 'Patagonia' }]); expect(getColumnValues()).to.deep.equal(['Patagonia', 'Fila', 'Puma']); diff --git a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx index cac6f3c39f227..dc398913343e9 100644 --- a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx @@ -78,12 +78,7 @@ describe(' - Rows', () => { apiRef = useGridApiRef(); return (
- +
); }; @@ -198,17 +193,14 @@ describe(' - Rows', () => { ); }; - it('should throttle by default', () => { + it('should not throttle by default', () => { render(); expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); apiRef.current.updateRows([{ id: 1, brand: 'Fila' }]); - clock.tick(25); - expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); - clock.tick(25); expect(getColumnValues()).to.deep.equal(['Nike', 'Fila', 'Puma']); }); - it('should allow to set the throttle duration', () => { + it('should allow to enable throttle', () => { render(); expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); apiRef.current.updateRows([{ id: 1, brand: 'Fila' }]); @@ -218,15 +210,8 @@ describe(' - Rows', () => { expect(getColumnValues()).to.deep.equal(['Nike', 'Fila', 'Puma']); }); - it('should allow to disable throttle', () => { - render(); - expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); - apiRef.current.updateRows([{ id: 1, brand: 'Fila' }]); - expect(getColumnValues()).to.deep.equal(['Nike', 'Fila', 'Puma']); - }); - it('should allow to update row data', () => { - render(); + render(); apiRef.current.updateRows([{ id: 1, brand: 'Fila' }]); apiRef.current.updateRows([{ id: 0, brand: 'Pata' }]); apiRef.current.updateRows([{ id: 2, brand: 'Pum' }]); @@ -234,7 +219,7 @@ describe(' - Rows', () => { }); it('update row data can also add rows', () => { - render(); + render(); apiRef.current.updateRows([{ id: 1, brand: 'Fila' }]); apiRef.current.updateRows([{ id: 0, brand: 'Pata' }]); apiRef.current.updateRows([{ id: 2, brand: 'Pum' }]); @@ -243,7 +228,7 @@ describe(' - Rows', () => { }); it('update row data can also add rows in bulk', () => { - render(); + render(); apiRef.current.updateRows([ { id: 1, brand: 'Fila' }, { id: 0, brand: 'Pata' }, @@ -254,7 +239,7 @@ describe(' - Rows', () => { }); it('update row data can also delete rows', () => { - render(); + render(); apiRef.current.updateRows([{ id: 1, _action: 'delete' }]); apiRef.current.updateRows([{ id: 0, brand: 'Apple' }]); apiRef.current.updateRows([{ id: 2, _action: 'delete' }]); @@ -263,7 +248,7 @@ describe(' - Rows', () => { }); it('update row data can also delete rows in bulk', () => { - render(); + render(); apiRef.current.updateRows([ { id: 1, _action: 'delete' }, { id: 0, brand: 'Apple' }, @@ -284,7 +269,6 @@ describe(' - Rows', () => { apiRef={apiRef} rows={baselineProps.rows.map((row) => ({ idField: row.id, brand: row.brand }))} getRowId={getRowId} - throttleRowsMs={0} /> ); @@ -341,7 +325,7 @@ describe(' - Rows', () => { ); }; - it('should throttle by default', () => { + it('should not throttle by default', () => { render(); expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); const newRows = [ @@ -352,13 +336,10 @@ describe(' - Rows', () => { ]; apiRef.current.setRows(newRows); - clock.tick(25); - expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); - clock.tick(25); expect(getColumnValues()).to.deep.equal(['Asics']); }); - it('should allow to set the throttle duration', () => { + it('should allow to enable throttle', () => { render(); expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); const newRows = [ @@ -374,19 +355,6 @@ describe(' - Rows', () => { clock.tick(50); expect(getColumnValues()).to.deep.equal(['Asics']); }); - - it('should allow to disable throttle', () => { - render(); - expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']); - const newRows = [ - { - id: 3, - brand: 'Asics', - }, - ]; - apiRef.current.setRows(newRows); - expect(getColumnValues()).to.deep.equal(['Asics']); - }); }); describe('virtualization', () => { diff --git a/packages/grid/x-grid/src/tests/sorting.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/sorting.DataGridPro.test.tsx index b8c61979e52c0..3fb6d3823962e 100644 --- a/packages/grid/x-grid/src/tests/sorting.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/sorting.DataGridPro.test.tsx @@ -68,7 +68,7 @@ describe(' - Sorting', () => { const renderBrandSortedAsc = () => { const sortModel: GridSortModel = [{ field: 'brand', sort: 'asc' }]; - render(); + render(); }; it('should apply the sortModel prop correctly', () => { From 2972dcee2636a4bb82df8e999e5ac9c27ea9fa8e Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 13 Sep 2021 14:50:59 +0200 Subject: [PATCH 068/390] Remove delay docs --- .../pages/components/data-grid/editing/FullFeaturedCrudGrid.js | 2 +- .../pages/components/data-grid/editing/FullFeaturedCrudGrid.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/pages/components/data-grid/editing/FullFeaturedCrudGrid.js b/docs/src/pages/components/data-grid/editing/FullFeaturedCrudGrid.js index 97d5c8bd2874b..3bf8ba4ac256e 100644 --- a/docs/src/pages/components/data-grid/editing/FullFeaturedCrudGrid.js +++ b/docs/src/pages/components/data-grid/editing/FullFeaturedCrudGrid.js @@ -87,7 +87,7 @@ function EditToolbar(props) { }); apiRef.current.setCellFocus(id, 'name'); - }, 150); + }); }; return ( diff --git a/docs/src/pages/components/data-grid/editing/FullFeaturedCrudGrid.tsx b/docs/src/pages/components/data-grid/editing/FullFeaturedCrudGrid.tsx index 6feb8df98217e..2e065c6ff00f9 100644 --- a/docs/src/pages/components/data-grid/editing/FullFeaturedCrudGrid.tsx +++ b/docs/src/pages/components/data-grid/editing/FullFeaturedCrudGrid.tsx @@ -94,7 +94,7 @@ function EditToolbar(props: EditToolbarProps) { rowIndex: apiRef.current.getRowsCount() - 1, }); apiRef.current.setCellFocus(id, 'name'); - }, 150); + }); }; return ( From 4430e37bc0e080fb7ac0e4ff4b22facffc60a80b Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 14 Sep 2021 10:02:42 +0200 Subject: [PATCH 069/390] Merge filter states --- .../panel/filterPanel/GridFilterPanel.tsx | 20 +++--- .../grid/hooks/features/core/gridState.ts | 6 +- .../features/filter/gridFilterModelState.ts | 7 -- .../features/filter/gridFilterSelector.ts | 30 +++++---- .../hooks/features/filter/gridFilterState.ts | 14 ++++ .../grid/hooks/features/filter/index.ts | 3 +- .../hooks/features/filter/useGridFilter.ts | 66 +++++++++++-------- .../features/filter/visibleGridRowsState.ts | 6 -- .../src/tests/filtering.DataGridPro.test.tsx | 4 +- .../src/stories/grid-filter.stories.tsx | 16 ++--- 10 files changed, 92 insertions(+), 80 deletions(-) delete mode 100644 packages/grid/_modules_/grid/hooks/features/filter/gridFilterModelState.ts create mode 100644 packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts delete mode 100644 packages/grid/_modules_/grid/hooks/features/filter/visibleGridRowsState.ts diff --git a/packages/grid/_modules_/grid/components/panel/filterPanel/GridFilterPanel.tsx b/packages/grid/_modules_/grid/components/panel/filterPanel/GridFilterPanel.tsx index f431712533926..aabdb7a352be9 100644 --- a/packages/grid/_modules_/grid/components/panel/filterPanel/GridFilterPanel.tsx +++ b/packages/grid/_modules_/grid/components/panel/filterPanel/GridFilterPanel.tsx @@ -1,24 +1,22 @@ import * as React from 'react'; import Button from '@mui/material/Button'; -import { useGridState } from '../../../hooks/features/core/useGridState'; import { GridFilterItem, GridLinkOperator } from '../../../models/gridFilterItem'; import { useGridApiContext } from '../../../hooks/root/useGridApiContext'; -import { GridAddIcon } from '../../icons/index'; +import { GridAddIcon } from '../../icons'; import { GridPanelContent } from '../GridPanelContent'; import { GridPanelFooter } from '../GridPanelFooter'; import { GridPanelWrapper } from '../GridPanelWrapper'; import { GridFilterForm } from './GridFilterForm'; import { useGridRootProps } from '../../../hooks/utils/useGridRootProps'; +import { useGridSelector } from '../../../hooks/features/core/useGridSelector'; +import { gridFilterModelSelector } from '../../../hooks/features/filter/gridFilterSelector'; export function GridFilterPanel() { const apiRef = useGridApiContext(); - const [gridState] = useGridState(apiRef); const rootProps = useGridRootProps(); + const filterModel = useGridSelector(apiRef, gridFilterModelSelector); - const hasMultipleFilters = React.useMemo( - () => gridState.filter.items.length > 1, - [gridState.filter.items.length], - ); + const hasMultipleFilters = filterModel.items.length > 1; const applyFilter = React.useCallback( (item: GridFilterItem) => { @@ -46,15 +44,15 @@ export function GridFilterPanel() { ); React.useEffect(() => { - if (gridState.filter.items.length === 0) { + if (filterModel.items.length === 0) { addNewFilter(); } - }, [addNewFilter, gridState.filter.items.length]); + }, [addNewFilter, filterModel.items.length]); return ( - {gridState.filter.items.map((item, index) => ( + {filterModel.items.map((item, index) => ( 0} - multiFilterOperator={gridState.filter.linkOperator} + multiFilterOperator={filterModel.linkOperator} disableMultiFilterOperator={index !== 1} applyMultiFilterOperatorChanges={applyFilterLinkOperator} /> diff --git a/packages/grid/_modules_/grid/hooks/features/core/gridState.ts b/packages/grid/_modules_/grid/hooks/features/core/gridState.ts index 6c7488f0e511a..fbb7e7bc7a18a 100644 --- a/packages/grid/_modules_/grid/hooks/features/core/gridState.ts +++ b/packages/grid/_modules_/grid/hooks/features/core/gridState.ts @@ -4,13 +4,12 @@ import { GridScrollBarState, GridViewportSizeState, } from '../../../models/gridContainerProps'; -import { GridFilterModel } from '../../../models/gridFilterModel'; import { GridEditRowsModel } from '../../../models/gridEditRowModel'; import { GridColumnMenuState } from '../columnMenu/columnMenuState'; import { GridColumnReorderState } from '../columnReorder/columnReorderState'; import { GridColumnResizeState } from '../columnResize/columnResizeState'; import { GridDensityState } from '../density/densityState'; -import { VisibleGridRowsState } from '../filter/visibleGridRowsState'; +import { GridFilterState } from '../filter/gridFilterState'; import { GridFocusState, GridTabIndexState } from '../focus/gridFocusState'; import { GridPreferencePanelState } from '../preferencesPanel/gridPreferencePanelState'; import { InternalGridRowsState } from '../rows/gridRowsState'; @@ -35,8 +34,7 @@ export interface GridState { focus: GridFocusState; tabIndex: GridTabIndexState; selection: GridSelectionModel; - filter: GridFilterModel; - visibleRows: VisibleGridRowsState; + filter: GridFilterState; preferencePanel: GridPreferencePanelState; density: GridDensityState; error?: any; diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterModelState.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterModelState.ts deleted file mode 100644 index 5e66692972252..0000000000000 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterModelState.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { GridLinkOperator } from '../../../models/gridFilterItem'; -import { GridFilterModel } from '../../../models/gridFilterModel'; - -export const getInitialGridFilterState: () => GridFilterModel = () => ({ - items: [], - linkOperator: GridLinkOperator.And, -}); diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index 529a1e4d2480a..5ac86cc14e484 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -6,15 +6,25 @@ import { gridRowCountSelector } from '../rows/gridRowsSelector'; import { sortedGridRowsSelector } from '../sorting/gridSortingSelector'; import { gridColumnLookupSelector } from '../columns/gridColumnsSelector'; -export const visibleGridRowsStateSelector = (state: GridState) => state.visibleRows; +export const gridFilterStateSelector = (state: GridState) => state.filter; + +export const gridFilterModelSelector = createSelector( + gridFilterStateSelector, + (filterState) => filterState.filterModel, +); + +export const gridVisibleRowsLookupSelector = createSelector( + gridFilterStateSelector, + (filterState) => filterState.visibleRowsLookup, +); export const visibleSortedGridRowsSelector = createSelector( - visibleGridRowsStateSelector, + gridVisibleRowsLookupSelector, sortedGridRowsSelector, - (visibleRowsState, sortedRows) => { + (visibleRowsLookup, sortedRows) => { const map = new Map(); sortedRows.forEach((row, id) => { - if (visibleRowsState.visibleRowsLookup[id] !== false) { + if (visibleRowsLookup[id] !== false) { map.set(id, row); } }); @@ -33,20 +43,18 @@ export const visibleSortedGridRowIdsSelector = createSelector( ); export const visibleGridRowCountSelector = createSelector( - visibleGridRowsStateSelector, + gridFilterStateSelector, gridRowCountSelector, - (visibleRowsState, totalRowsCount) => { - if (visibleRowsState.visibleRows == null) { + (filterState, totalRowsCount) => { + if (filterState.visibleRows == null) { return totalRowsCount; } - return visibleRowsState.visibleRows.length; + return filterState.visibleRows.length; }, ); -export const filterGridStateSelector = (state: GridState) => state.filter; - export const activeGridFilterItemsSelector = createSelector( - filterGridStateSelector, + gridFilterModelSelector, gridColumnLookupSelector, (filterModel, columnLookup) => filterModel.items?.filter((item) => { diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts new file mode 100644 index 0000000000000..b00629996e4dc --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts @@ -0,0 +1,14 @@ +import { GridLinkOperator } from '../../../models/gridFilterItem'; +import { GridFilterModel } from '../../../models/gridFilterModel'; +import { GridRowId } from '../../../models/gridRows'; + +export const getDefaultGridFilterModel: () => GridFilterModel = () => ({ + items: [], + linkOperator: GridLinkOperator.And, +}); + +export interface GridFilterState { + filterModel: GridFilterModel; + visibleRowsLookup: Record; + visibleRows: GridRowId[] | null; +} diff --git a/packages/grid/_modules_/grid/hooks/features/filter/index.ts b/packages/grid/_modules_/grid/hooks/features/filter/index.ts index 281e8b334d82f..e1a35c1029cd2 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/index.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/index.ts @@ -1,4 +1,3 @@ -export * from './gridFilterModelState'; +export * from './gridFilterState'; export * from './gridFilterSelector'; export * from './useGridFilter'; -export * from './visibleGridRowsState'; diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index badffc7e3a3ee..bacdf01febf0f 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -14,9 +14,13 @@ import { filterableGridColumnsIdsSelector } from '../columns/gridColumnsSelector import { useGridState } from '../core/useGridState'; import { GridPreferencePanelsValue } from '../preferencesPanel/gridPreferencePanelsValue'; import { sortedGridRowsSelector } from '../sorting/gridSortingSelector'; -import { getInitialGridFilterState } from './gridFilterModelState'; +import { getDefaultGridFilterModel } from './gridFilterState'; import { GridFilterModel } from '../../../models/gridFilterModel'; -import { filterGridStateSelector, visibleSortedGridRowsSelector } from './gridFilterSelector'; +import { + gridVisibleRowsLookupSelector, + visibleSortedGridRowsSelector, + gridFilterModelSelector, +} from './gridFilterSelector'; import { useGridRegisterControlState } from '../../utils/useGridRegisterControlState'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; @@ -54,9 +58,10 @@ export const useGridFilter = ( return { ...state, - filter: props.filterModel ?? getInitialGridFilterState(), - visibleRows: { + filter: { + filterModel: props.filterModel ?? getDefaultGridFilterModel(), visibleRowsLookup: {}, + visibleRows: null, }, }; }); @@ -67,7 +72,7 @@ export const useGridFilter = ( stateId: 'filter', propModel: props.filterModel, propOnChange: props.onFilterModelChange, - stateSelector: (state) => state.filter, + stateSelector: (state) => state.filter.filterModel, changeEvent: GridEvents.filterModelChange, }); @@ -112,7 +117,7 @@ export const useGridFilter = ( } setGridState((state) => { - const visibleRowsLookup = { ...state.visibleRows.visibleRowsLookup }; + const visibleRowsLookup = { ...gridVisibleRowsLookupSelector(state) }; // We run the selector on the state here to avoid rendering the rows and then filtering again. // This way we have latest rows on the first rendering @@ -134,8 +139,8 @@ export const useGridFilter = ( return { ...state, - visibleRows: { - ...state.visibleRows, + filter: { + ...state.filter, visibleRowsLookup, visibleRows: Object.entries(visibleRowsLookup) .filter(([, isVisible]) => isVisible) @@ -157,12 +162,14 @@ export const useGridFilter = ( // Clearing filtered rows setGridState((state) => ({ ...state, - visibleRows: { + filter: { + ...state.filter, visibleRowsLookup: {}, + visibleRows: null, }, })); - const { items, linkOperator } = apiRef.current.state.filter; + const { items, linkOperator } = gridFilterModelSelector(apiRef.current.state); items.forEach((filterItem) => { apiRef.current.applyFilter(filterItem, linkOperator); @@ -176,7 +183,7 @@ export const useGridFilter = ( setGridState((state) => { const filterableColumnsIds = filterableGridColumnsIdsSelector(state); - const items = [...state.filter.items]; + const items = [...gridFilterModelSelector(state).items]; const newItem = { ...item }; const itemIndex = items.findIndex((filterItem) => filterItem.id === newItem.id); @@ -204,11 +211,11 @@ export const useGridFilter = ( if (props.disableMultipleColumnsFiltering && items.length > 1) { items.length = 1; } - const newState = { + + return { ...state, - filter: { ...state.filter, items }, + filter: { ...state.filter, filterModel: { ...state.filter.filterModel, items } }, }; - return newState; }); apiRef.current.applyFilters(); }, @@ -219,14 +226,16 @@ export const useGridFilter = ( (item) => { logger.debug(`Deleting filter on column ${item.columnField} with value ${item.value}`); setGridState((state) => { - const items = [...state.filter.items.filter((filterItem) => filterItem.id !== item.id)]; - const newState = { + const items = [ + ...gridFilterModelSelector(state).items.filter((filterItem) => filterItem.id !== item.id), + ]; + + return { ...state, - filter: { ...state.filter, items }, + filter: { ...state.filter, filterModel: { ...state.filter.filterModel, items } }, }; - return newState; }); - if (apiRef.current.state.filter.items.length === 0) { + if (gridFilterModelSelector(apiRef.current.state).items.length === 0) { apiRef.current.upsertFilter({}); } apiRef.current.applyFilters(); @@ -238,10 +247,10 @@ export const useGridFilter = ( (targetColumnField) => { logger.debug('Displaying filter panel'); if (targetColumnField) { - const filterState = filterGridStateSelector(apiRef.current.state); + const filterModel = gridFilterModelSelector(apiRef.current.state); const lastFilter = - filterState.items.length > 0 ? filterState.items[filterState.items.length - 1] : null; + filterModel.items.length > 0 ? filterModel.items[filterModel.items.length - 1] : null; if (!lastFilter || lastFilter.columnField !== targetColumnField) { apiRef.current.upsertFilter({ columnField: targetColumnField }); } @@ -261,7 +270,7 @@ export const useGridFilter = ( logger.debug('Applying filter link operator'); setGridState((state) => ({ ...state, - filter: { ...state.filter, linkOperator }, + filter: { ...state.filter, filterModel: { ...state.filter.filterModel, linkOperator } }, })); apiRef.current.applyFilters(); }, @@ -270,14 +279,17 @@ export const useGridFilter = ( const setFilterModel = React.useCallback( (model) => { - const currentModel = filterGridStateSelector(apiRef.current.state); + const currentModel = gridFilterModelSelector(apiRef.current.state); if (currentModel !== model) { checkFilterModelValidity(model); logger.debug('Setting filter model'); setGridState((state) => ({ ...state, - filter: model, + filter: { + ...state.filter, + filterModel: model, + }, })); apiRef.current.applyFilters(); } @@ -308,11 +320,11 @@ export const useGridFilter = ( const onColUpdated = React.useCallback(() => { logger.debug('onColUpdated - GridColumns changed, applying filters'); - const filterState = apiRef.current.state.filter; + const filterModel = gridFilterModelSelector(apiRef.current.state); const columnsIds = filterableGridColumnsIdsSelector(apiRef.current.state); logger.debug('GridColumns changed, applying filters'); - filterState.items.forEach((filter) => { + filterModel.items.forEach((filter) => { if (!columnsIds.find((field) => field === filter.columnField)) { apiRef.current.deleteFilter(filter); } @@ -324,7 +336,7 @@ export const useGridFilter = ( if (props.filterModel !== undefined) { apiRef.current.setFilterModel(props.filterModel); } - }, [apiRef, logger, props.filterModel, setGridState]); + }, [apiRef, logger, props.filterModel]); useFirstRender(() => apiRef.current.applyFilters()); diff --git a/packages/grid/_modules_/grid/hooks/features/filter/visibleGridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/filter/visibleGridRowsState.ts deleted file mode 100644 index d1297ad1d4c82..0000000000000 --- a/packages/grid/_modules_/grid/hooks/features/filter/visibleGridRowsState.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { GridRowId } from '../../../models/gridRows'; - -export interface VisibleGridRowsState { - visibleRowsLookup: Record; - visibleRows?: GridRowId[]; -} diff --git a/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx index 97fec65e27143..c8a54864de3aa 100644 --- a/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx @@ -1,5 +1,5 @@ import { - getInitialGridFilterState, + getDefaultGridFilterModel, GridApiRef, GridComponentProps, GridFilterModel, @@ -484,7 +484,7 @@ describe(' - Filter', () => { it('should control filter state when the model and the onChange are set', () => { const ControlCase = (props: Partial) => { const { rows, columns, ...others } = props; - const [caseFilterModel, setFilterModel] = React.useState(getInitialGridFilterState()); + const [caseFilterModel, setFilterModel] = React.useState(getDefaultGridFilterModel); const handleFilterChange = (newModel) => { setFilterModel(newModel); }; diff --git a/packages/storybook/src/stories/grid-filter.stories.tsx b/packages/storybook/src/stories/grid-filter.stories.tsx index 1feeaa85a2433..1b4160793360b 100644 --- a/packages/storybook/src/stories/grid-filter.stories.tsx +++ b/packages/storybook/src/stories/grid-filter.stories.tsx @@ -15,7 +15,7 @@ import { GridRowModel, useGridApiRef, DataGridPro, - getInitialGridFilterState, + getDefaultGridFilterModel, } from '@mui/x-data-grid-pro'; import { useDemoData, randomArrayItem } from '@mui/x-data-grid-generator'; import { action } from '@storybook/addon-actions'; @@ -61,17 +61,15 @@ export function CommodityWithOpenFiltersAndState() { @@ -802,9 +800,7 @@ export function SimpleModelWithOnChangeControlFilter() { }, ]); - const [simpleFilterModel, setFilterModel] = React.useState( - getInitialGridFilterState(), - ); + const [simpleFilterModel, setFilterModel] = React.useState(getDefaultGridFilterModel); const handleFilterChange = React.useCallback((model) => { setFilterModel(model); }, []); From 923743489a7be3bf3d21218be791eac734ca52b6 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 14 Sep 2021 10:05:50 +0200 Subject: [PATCH 070/390] Small fixes --- .../grid/_modules_/grid/components/containers/GridRoot.tsx | 6 +++--- .../_modules_/grid/hooks/features/filter/useGridFilter.ts | 2 +- .../_modules_/grid/hooks/features/sorting/useGridSorting.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/grid/_modules_/grid/components/containers/GridRoot.tsx b/packages/grid/_modules_/grid/components/containers/GridRoot.tsx index 92b691083fe8e..db79e62fda49b 100644 --- a/packages/grid/_modules_/grid/components/containers/GridRoot.tsx +++ b/packages/grid/_modules_/grid/components/containers/GridRoot.tsx @@ -6,10 +6,10 @@ import { GridRootContainerRef } from '../../models/gridRootContainerRef'; import { useStyles } from './GridRootStyles'; import { visibleGridColumnsLengthSelector } from '../../hooks/features/columns/gridColumnsSelector'; import { useGridSelector } from '../../hooks/features/core/useGridSelector'; -import { useGridState } from '../../hooks/features/core/useGridState'; import { useGridApiContext } from '../../hooks/root/useGridApiContext'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { gridClasses } from '../../gridClasses'; +import { gridRowCountSelector } from '../../hooks/features/rows/gridRowsSelector'; export type GridRootProps = React.HTMLAttributes; @@ -22,7 +22,7 @@ export const GridRoot = React.forwardRef(function const rootProps = useGridRootProps(); const { children, className: classNameProp, ...other } = props; const visibleColumnsLength = useGridSelector(apiRef, visibleGridColumnsLengthSelector); - const [gridState] = useGridState(apiRef); + const totalRowCount = useGridSelector(apiRef, gridRowCountSelector); const rootContainerRef: GridRootContainerRef = React.useRef(null); const handleRef = useForkRef(rootContainerRef, ref); @@ -44,7 +44,7 @@ export const GridRoot = React.forwardRef(function )} role="grid" aria-colcount={visibleColumnsLength} - aria-rowcount={gridState.rows.totalRowCount} + aria-rowcount={totalRowCount} aria-multiselectable={!rootProps.disableMultipleSelection} aria-label={rootProps['aria-label']} aria-labelledby={rootProps['aria-labelledby']} diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index bacdf01febf0f..45b37ce85c07c 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -72,7 +72,7 @@ export const useGridFilter = ( stateId: 'filter', propModel: props.filterModel, propOnChange: props.onFilterModelChange, - stateSelector: (state) => state.filter.filterModel, + stateSelector: gridFilterModelSelector, changeEvent: GridEvents.filterModelChange, }); diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index ac68550a3ec85..cb0f763284201 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -63,7 +63,7 @@ export const useGridSorting = ( stateId: 'sortModel', propModel: props.sortModel, propOnChange: props.onSortModelChange, - stateSelector: (state) => state.sorting.sortModel, + stateSelector: gridSortModelSelector, changeEvent: GridEvents.sortModelChange, }); From f263bcefca69c05c7dc848ba9b92d288e16e9091 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 14 Sep 2021 10:25:28 +0200 Subject: [PATCH 071/390] Work --- .../hooks/features/columns/useGridColumns.ts | 7 +++--- .../core/useGridControlStateManager.ts | 4 ++-- .../hooks/features/core/useGridProSelector.ts | 23 ------------------- .../hooks/features/core/useGridSelector.ts | 5 +--- .../hooks/features/density/densitySelector.ts | 11 +++++---- .../hooks/features/density/useGridDensity.tsx | 19 +++++++++++---- .../pagination/gridPaginationSelector.ts | 10 ++++++++ .../hooks/features/pagination/useGridPage.ts | 3 ++- .../features/pagination/useGridPageSize.ts | 3 ++- .../hooks/features/rows/useGridEditRows.ts | 2 +- .../grid/hooks/features/rows/useGridRows.ts | 2 +- .../grid/hooks/root/useGridContainerProps.ts | 2 ++ 12 files changed, 47 insertions(+), 44 deletions(-) delete mode 100644 packages/grid/_modules_/grid/hooks/features/core/useGridProSelector.ts diff --git a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts index 25553af1218c5..ea1c5f5d8b89f 100644 --- a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts +++ b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts @@ -191,7 +191,9 @@ export function useGridColumns( }); const [, setGridState, forceUpdate] = useGridState(apiRef); - // It should be fixed by removing `viewportSizes` from the state + // On the first render, `useGridContainerProps` has not yet initialized its state because it is called after `useGridColumns` + // But it viewport width would always be 0 on the 1st render since the DOM Node is not mounted yet, so we can provide a safe fallback here + // TODO: Fix when removing `viewportSizes` from the state const viewportSizes = apiRef.current.state.viewportSizes?.width ?? 0; const setGridColumnsState = React.useCallback( @@ -356,7 +358,7 @@ export function useGridColumns( useGridApiMethod(apiRef, colApi, 'ColApi'); - // The effect do not track any value defined synchronously during the 1st render by hooks called after `useGridSorting` + // The effect do not track any value defined synchronously during the 1st render by hooks called after `useGridColumns` // As a consequence, the state generated by the 1st run of this useEffect will always be equal to the initialization one const isFirstRender = React.useRef(true); React.useEffect(() => { @@ -387,7 +389,6 @@ export function useGridColumns( classes, ]); - // We need optional chaining due to impossible hook order (see description of this hook) React.useEffect(() => { logger.debug(`GridColumns gridState.viewportSizes.width, changed ${viewportSizes}`); diff --git a/packages/grid/_modules_/grid/hooks/features/core/useGridControlStateManager.ts b/packages/grid/_modules_/grid/hooks/features/core/useGridControlStateManager.ts index 07895d77e5ccd..fe31f3f9bd0ca 100644 --- a/packages/grid/_modules_/grid/hooks/features/core/useGridControlStateManager.ts +++ b/packages/grid/_modules_/grid/hooks/features/core/useGridControlStateManager.ts @@ -31,10 +31,10 @@ export function useGridControlStateManager(apiRef: GridApiRef, props: GridCompon Object.keys(controlStateMap).forEach((stateId) => { const controlState = controlStateMap[stateId]; - const oldState = controlState.stateSelector(apiRef.current.state); + const oldSubState = controlState.stateSelector(apiRef.current.state); const newSubState = controlState.stateSelector(newState); - if (newSubState === oldState) { + if (newSubState === oldSubState) { return; } diff --git a/packages/grid/_modules_/grid/hooks/features/core/useGridProSelector.ts b/packages/grid/_modules_/grid/hooks/features/core/useGridProSelector.ts deleted file mode 100644 index 383e8d6c0c371..0000000000000 --- a/packages/grid/_modules_/grid/hooks/features/core/useGridProSelector.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { GridApiRef } from '../../../models'; -import { GridState } from './gridState'; -import { useGridState } from './useGridState'; -import { useGridRootProps } from '../../utils/useGridRootProps'; -import { GridSignature } from '../../root/useGridApiEventHandler'; - -/** - * Temporary hack - * TODO: Find a correct way to pass pro-only data to the components without importing their selectors in the component - */ -export const useGridProSelector = ( - apiRef: GridApiRef, - selector: (state: GridState) => State, -) => { - const [state] = useGridState(apiRef); - const rootProps = useGridRootProps(); - - if (rootProps.signature !== GridSignature.DataGridPro) { - return undefined; - } - - return selector(state); -}; diff --git a/packages/grid/_modules_/grid/hooks/features/core/useGridSelector.ts b/packages/grid/_modules_/grid/hooks/features/core/useGridSelector.ts index e550a2a938763..4bb6e414cd1ea 100644 --- a/packages/grid/_modules_/grid/hooks/features/core/useGridSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/core/useGridSelector.ts @@ -2,10 +2,7 @@ import { GridApiRef } from '../../../models/api/gridApiRef'; import { useGridState } from './useGridState'; import { GridState } from './gridState'; -export const useGridSelector = ( - apiRef: GridApiRef, - selector: (state: GridState) => State, -) => { +export const useGridSelector = (apiRef: GridApiRef, selector: (state: GridState) => T) => { const [state] = useGridState(apiRef); return selector(state); }; diff --git a/packages/grid/_modules_/grid/hooks/features/density/densitySelector.ts b/packages/grid/_modules_/grid/hooks/features/density/densitySelector.ts index b18495283c8a6..0e7581a2da143 100644 --- a/packages/grid/_modules_/grid/hooks/features/density/densitySelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/density/densitySelector.ts @@ -1,16 +1,19 @@ import { createSelector } from 'reselect'; import { GridState } from '../core/gridState'; -export const densitySelector = (state: GridState) => state.density; +export const gridDensitySelector = (state: GridState) => state.density; -export const gridDensityValueSelector = createSelector(densitySelector, (density) => density.value); +export const gridDensityValueSelector = createSelector( + gridDensitySelector, + (density) => density.value, +); export const gridDensityRowHeightSelector = createSelector( - densitySelector, + gridDensitySelector, (density) => density.rowHeight, ); export const gridDensityHeaderHeightSelector = createSelector( - densitySelector, + gridDensitySelector, (density) => density.headerHeight, ); diff --git a/packages/grid/_modules_/grid/hooks/features/density/useGridDensity.tsx b/packages/grid/_modules_/grid/hooks/features/density/useGridDensity.tsx index 61230bad4051a..3dbd209b5cf6b 100644 --- a/packages/grid/_modules_/grid/hooks/features/density/useGridDensity.tsx +++ b/packages/grid/_modules_/grid/hooks/features/density/useGridDensity.tsx @@ -8,6 +8,8 @@ import { GridDensityApi } from '../../../models/api/gridDensityApi'; import { GridDensityState } from './densityState'; import { GridComponentProps } from '../../../GridComponentProps'; import { useGridStateInit } from '../../utils/useGridStateInit'; +import { gridDensitySelector } from './densitySelector'; +import { isDeepEqual } from '../../../utils/utils'; export const COMPACT_DENSITY_FACTOR = 0.7; export const COMFORTABLE_DENSITY_FACTOR = 1.3; @@ -59,10 +61,19 @@ export const useGridDensity = ( newRowHeight = props.rowHeight, ): void => { logger.debug(`Set grid density to ${newDensity}`); - setGridState((state) => ({ - ...state, - density: getUpdatedDensityState(newDensity, newHeaderHeight, newRowHeight), - })); + setGridState((state) => { + const currentDensityState = gridDensitySelector(state); + const newDensityState = getUpdatedDensityState(newDensity, newHeaderHeight, newRowHeight); + + if (isDeepEqual(currentDensityState, newDensityState)) { + return state; + } + + return { + ...state, + density: newDensityState, + }; + }); forceUpdate(); }, [logger, setGridState, forceUpdate, props.headerHeight, props.rowHeight], diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts index e0a8a7b2bb197..ec0531c7bf9fe 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts @@ -5,6 +5,16 @@ import { GridPaginationState } from './gridPaginationState'; export const gridPaginationSelector = (state: GridState): GridPaginationState => state.pagination; +export const gridPageSelector = createSelector( + gridPaginationSelector, + (pagination) => pagination.page, +); + +export const gridPageSizeSelector = createSelector( + gridPaginationSelector, + (pagination) => pagination.pageSize, +); + export const gridPaginatedVisibleSortedGridRowIdsSelector = createSelector( gridPaginationSelector, visibleSortedGridRowIdsSelector, diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts index 03be1a1ad1a3b..59327d3c64b51 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts @@ -11,6 +11,7 @@ import { GridPaginationState } from './gridPaginationState'; import { visibleGridRowCountSelector } from '../filter'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useGridRegisterControlState } from '../../utils/useGridRegisterControlState'; +import { gridPageSelector } from './gridPaginationSelector'; const getPageCount = (rowCount: number, pageSize: number): number => { if (pageSize > 0 && rowCount > 0) { @@ -59,7 +60,7 @@ export const useGridPage = ( stateId: 'page', propModel: props.page, propOnChange: props.onPageChange, - stateSelector: (state) => state.pagination.page, + stateSelector: gridPageSelector, changeEvent: GridEvents.pageChange, }); diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts index c03bbe04c3ba9..e39b0b83ecbd0 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts @@ -9,6 +9,7 @@ import { useGridSelector, useGridState } from '../core'; import { gridContainerSizesSelector } from '../../root/gridContainerSizesSelector'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useGridRegisterControlState } from '../../utils/useGridRegisterControlState'; +import { gridPageSizeSelector } from './gridPaginationSelector'; /** * @requires useGridControlStateManager (method) @@ -33,7 +34,7 @@ export const useGridPageSize = ( stateId: 'pageSize', propModel: props.pageSize, propOnChange: props.onPageSizeChange, - stateSelector: (state) => state.pagination.pageSize, + stateSelector: gridPageSizeSelector, changeEvent: GridEvents.pageSizeChange, }); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts index 60d75f2df8f54..cc24c723215c5 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts @@ -73,7 +73,7 @@ export function useGridEditRows( stateId: 'editRows', propModel: props.editRowsModel, propOnChange: props.onEditRowsModelChange, - stateSelector: (state) => state.editRows, + stateSelector: gridEditRowsStateSelector, changeEvent: GridEvents.editRowsModelChange, }); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index f95918afccf72..9f9ca939fe9d6 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -219,7 +219,7 @@ export const useGridRows = ( return () => clearTimeout(updateTimeout!.current); }, []); - // The effect do not track any value defined synchronously during the 1st render by hooks called after `useGridSorting` + // The effect do not track any value defined synchronously during the 1st render by hooks called after `useGridRows` // As a consequence, the state generated by the 1st run of this useEffect will always be equal to the initialization one const isFirstRender = React.useRef(true); React.useEffect(() => { diff --git a/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts b/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts index 2d8d85c0e32d7..45227df81f948 100644 --- a/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts +++ b/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts @@ -58,6 +58,8 @@ export const useGridContainerProps = ( ) => { const logger = useGridLogger(apiRef, 'useGridContainerProps'); + // TODO: Remove from the state an add direct computation method + // See https://github.com/mui-org/material-ui-x/issues/820#issuecomment-897906608 useGridStateInit(apiRef, (state) => ({ ...state, containerSizes: null, From c80ea578be749a204e715eab0da3e6c681f41caa Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 15 Sep 2021 11:19:30 +0200 Subject: [PATCH 072/390] Temp work with filtered tree --- .../grid/components/GridViewport.tsx | 3 +- .../features/export/useGridCsvExport.tsx | 4 +- .../features/filter/gridFilterSelector.ts | 49 ++++----- .../hooks/features/filter/useGridFilter.ts | 87 ++++++++++++++-- .../features/filter/visibleGridRowsState.ts | 20 ++++ .../grid/hooks/features/rows/gridRowsState.ts | 4 +- .../grid/hooks/features/rows/useGridRows.ts | 4 +- .../features/sorting/gridSortingSelector.ts | 31 +++++- .../features/sorting/gridSortingState.ts | 13 ++- .../hooks/features/sorting/useGridSorting.ts | 10 +- .../features/treeData/GridTreeDataApi.ts | 6 +- .../features/treeData/useGridTreeData.ts | 4 +- .../grid/_modules_/grid/models/gridRows.ts | 6 +- .../src/tests/state.DataGrid.test.tsx | 99 ++++++++++--------- 14 files changed, 235 insertions(+), 105 deletions(-) diff --git a/packages/grid/_modules_/grid/components/GridViewport.tsx b/packages/grid/_modules_/grid/components/GridViewport.tsx index 2e6918cb7a00c..43f7362bc760d 100644 --- a/packages/grid/_modules_/grid/components/GridViewport.tsx +++ b/packages/grid/_modules_/grid/components/GridViewport.tsx @@ -69,6 +69,7 @@ export const GridViewport: ViewportType = React.forwardRef( renderState.renderContext.lastRowIdx!, ); + // TODO: Add tree children return renderedRows.map(([id, row], idx) => ( ( { const logger = useGridLogger(apiRef, 'useGridCsvExport'); const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); const columns = useGridSelector(apiRef, allGridColumnsSelector); - const visibleSortedRows = useGridSelector(apiRef, visibleSortedGridRowsSelector); + const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowsSelector); const selection = useGridSelector(apiRef, gridSelectionStateSelector); const getDataAsCsv = React.useCallback( diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index 529a1e4d2480a..fb233acd2d404 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -1,46 +1,51 @@ import { createSelector } from 'reselect'; import { GridFilterItem } from '../../../models/gridFilterItem'; -import { GridRowId, GridRowModel } from '../../../models/gridRows'; +import { GridRowId } from '../../../models/gridRows'; import { GridState } from '../core/gridState'; import { gridRowCountSelector } from '../rows/gridRowsSelector'; -import { sortedGridRowsSelector } from '../sorting/gridSortingSelector'; +import { gridSortedRowsTreeSelector} from '../sorting/gridSortingSelector'; import { gridColumnLookupSelector } from '../columns/gridColumnsSelector'; +import {GridSortedRowsTreeNode} from "../sorting"; -export const visibleGridRowsStateSelector = (state: GridState) => state.visibleRows; +export const gridVisibleRowStateSelector = (state: GridState) => state.visibleRows; -export const visibleSortedGridRowsSelector = createSelector( - visibleGridRowsStateSelector, - sortedGridRowsSelector, - (visibleRowsState, sortedRows) => { - const map = new Map(); - sortedRows.forEach((row, id) => { - if (visibleRowsState.visibleRowsLookup[id] !== false) { - map.set(id, row); +export const gridSortedVisibleRowsSelector = createSelector( + gridVisibleRowStateSelector, + gridSortedRowsTreeSelector, + (visibleRowsState, sortedRowsTree) => { + const removeHiddenRows = (nodes: Map) => { + const filteredRows = new Map() + + nodes.forEach((row, id) => { + if (visibleRowsState.visibleRowsLookup[id] !== false) { + filteredRows.set(id, { + node: row.node, + children: removeHiddenRows(row.children) + }) + } + }) + + return filteredRows } - }); - return map; + + return removeHiddenRows(sortedRowsTree) }, ); export const visibleSortedGridRowsAsArraySelector = createSelector( - visibleSortedGridRowsSelector, + gridSortedVisibleRowsSelector, (visibleSortedRows) => [...visibleSortedRows.entries()], ); export const visibleSortedGridRowIdsSelector = createSelector( - visibleSortedGridRowsSelector, + gridSortedVisibleRowsSelector, (visibleSortedRows) => [...visibleSortedRows.keys()], ); export const visibleGridRowCountSelector = createSelector( - visibleGridRowsStateSelector, + gridVisibleRowStateSelector, gridRowCountSelector, - (visibleRowsState, totalRowsCount) => { - if (visibleRowsState.visibleRows == null) { - return totalRowsCount; - } - return visibleRowsState.visibleRows.length; - }, + (visibleRowsState, totalRowsCount) => visibleRowsState.visibleRowsCount ?? totalRowsCount, ); export const filterGridStateSelector = (state: GridState) => state.filter; diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 9d128695f15e8..77f535e3a11f9 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -14,11 +14,16 @@ import { filterableGridColumnsIdsSelector } from '../columns/gridColumnsSelector import { useGridSelector } from '../core/useGridSelector'; import { useGridState } from '../core/useGridState'; import { GridPreferencePanelsValue } from '../preferencesPanel/gridPreferencePanelsValue'; -import { sortedGridRowsSelector } from '../sorting/gridSortingSelector'; +import { gridSortedRowsTreeSelector, gridSortedRowsSelector } from '../sorting/gridSortingSelector'; import { getInitialGridFilterState } from './gridFilterModelState'; import { GridFilterModel } from '../../../models/gridFilterModel'; -import { visibleSortedGridRowsSelector } from './gridFilterSelector'; -import { getInitialVisibleGridRowsState } from './visibleGridRowsState'; +import { gridSortedVisibleRowsSelector } from './gridFilterSelector'; +import { + getInitialVisibleGridRowsState, + GridVisibleRowsTreeLookup, + GridVisibleRowsTreeNode, +} from './visibleGridRowsState'; +import { GridSortedRowsTreeNode } from '../sorting'; /** * @requires useGridColumns (state, method, event) @@ -85,11 +90,13 @@ export const useGridFilter = ( } setGridState((state) => { + // List filtering + // TODO: Remove const visibleRowsLookup = { ...state.visibleRows.visibleRowsLookup }; // We run the selector on the state here to avoid rendering the rows and then filtering again. // This way we have latest rows on the first rendering - const rows = sortedGridRowsSelector(state); + const rows = gridSortedRowsSelector(state); rows.forEach((row: GridRowModel, id: GridRowId) => { const params = apiRef.current.getCellParams(id, newFilterItem.columnField!); @@ -105,14 +112,76 @@ export const useGridFilter = ( } }); + const visibleRows = Object.entries(visibleRowsLookup) + .filter(([, isVisible]) => isVisible) + .map(([id]) => id); + + // Tree filtering + // We run the selector on the state here to avoid rendering the rows and then filtering again. + // This way we have latest rows on the first rendering + const rowTree = gridSortedRowsTreeSelector(state); + + const filterRowTree = ( + tree: Map, + visibleLookup: GridVisibleRowsTreeLookup, + ) => { + if (tree.size === 0) { + return { lookup: {}, list: [], flatVisibleRows: [], count: 0, } + } + + const visibleList: GridVisibleRowsTreeNode[] = []; + const flatVisibleRows: GridRowId[] = []; + let visibleRowsCount = 0 + + tree.forEach((node, id) => { + const params = apiRef.current.getCellParams(id, newFilterItem.columnField!); + const hasCurrentFilter = applyFilterOnRow(params); + const lookupElementBeforeCurrentFilter = visibleLookup[id]; + + let isVisible: boolean; + + if (lookupElementBeforeCurrentFilter == null) { + isVisible = hasCurrentFilter; + } else if (linkOperator === GridLinkOperator.And) { + isVisible = lookupElementBeforeCurrentFilter.isVisible && hasCurrentFilter; + } else { + isVisible = lookupElementBeforeCurrentFilter.isVisible || hasCurrentFilter; + } + + const childrenBeforeFilter = { ...(lookupElementBeforeCurrentFilter?.children ?? {}) }; + const childrenAfterFilter = filterRowTree(node.children, childrenBeforeFilter) + + visibleLookup[id] = { + isVisible, + children: childrenAfterFilter.lookup, + }; + + if (isVisible) { + visibleList.push({ id, children: childrenAfterFilter.list }); + flatVisibleRows.push(id) + visibleRowsCount += 1 + } + + visibleRowsCount += childrenAfterFilter.count + flatVisibleRows.concat(childrenAfterFilter.flatVisibleRows) + }); + + return { lookup: visibleLookup, list: visibleList, flatVisibleRows, count: visibleRowsCount }; + }; + + const visibleRowsTree = filterRowTree(rowTree, { + ...state.visibleRows.visibleRowsTreeLookup, + }); + return { ...state, visibleRows: { ...state.visibleRows, visibleRowsLookup, - visibleRows: Object.entries(visibleRowsLookup) - .filter(([, isVisible]) => isVisible) - .map(([id]) => id), + visibleRows, + visibleRowsTreeLookup: visibleRowsTree.lookup, + visibleRowsTree: visibleRowsTree.list, + visibleRowsCount: visibleRowsTree.count, }, }; }); @@ -255,8 +324,8 @@ export const useGridFilter = ( [applyFilterLinkOperator, clearFilterModel, logger, upsertFilter], ); - const getVisibleRowModels = React.useCallback( - () => visibleSortedGridRowsSelector(apiRef.current.state), + const getVisibleRowModels = React.useCallback( + () => gridSortedVisibleRowsSelector(apiRef.current.state), [apiRef], ); diff --git a/packages/grid/_modules_/grid/hooks/features/filter/visibleGridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/filter/visibleGridRowsState.ts index 239dca8684437..f9eccea4204c7 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/visibleGridRowsState.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/visibleGridRowsState.ts @@ -1,10 +1,30 @@ import { GridRowId } from '../../../models/gridRows'; +export interface GridVisibleRowsTreeNode { + id: GridRowId; + children: GridVisibleRowsTreeNode[]; +} + +export interface GridVisibleRowsTreeLookupNode { + isVisible: boolean; + children: GridVisibleRowsTreeLookup; +} + +export type GridVisibleRowsTreeLookup = { [id: GridRowId]: GridVisibleRowsTreeLookupNode }; + export interface VisibleGridRowsState { visibleRowsLookup: Record; visibleRows?: GridRowId[]; + + // Tree + // visibleRowsTreeLookup: GridVisibleRowsTreeLookup; + // visibleRowsTree?: GridVisibleRowsTreeNode[]; + // visibleRowsCount: number | null, + // flatVisibleRows?: [], } export const getInitialVisibleGridRowsState: () => VisibleGridRowsState = () => ({ visibleRowsLookup: {}, + // visibleRowsTreeLookup: {}, + // visibleRowsCount: null, }); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts index 0ee070fc2b26a..fdad45c1d9fa7 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts @@ -1,9 +1,9 @@ -import { GridRowId, GridRowModel, GridRowTree } from '../../../models/gridRows'; +import { GridRowId, GridRowModel, GridRowIdTree } from '../../../models/gridRows'; export interface GridRowsState { idRowsLookup: Record; allRows: GridRowId[]; - tree: GridRowTree; + tree: GridRowIdTree; totalRowCount: number; } diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 2c75d26c51b4d..d0f20a5f2d61c 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -10,7 +10,7 @@ import { GridRowsProp, GridRowIdGetter, GridRowData, - GridRowTree, + GridRowIdTree, } from '../../../models/gridRows'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; @@ -59,7 +59,7 @@ export function convertGridRowsPropToState( return state; } -const getFlatRowTree = (rowIds: GridRowId[]): GridRowTree => +const getFlatRowTree = (rowIds: GridRowId[]): GridRowIdTree => Object.fromEntries(rowIds.map((id) => [id, { id, children: {} }])); /** diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts index 57123f02824f0..2c71f51496da7 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts @@ -7,7 +7,11 @@ import { gridRowsLookupSelector, unorderedGridRowIdsSelector, } from '../rows/gridRowsSelector'; -import { GridSortingState } from './gridSortingState'; +import { + GridSortedRowsIdTreeNode, + GridSortedRowsTreeNode, + GridSortingState, +} from './gridSortingState'; const sortingGridStateSelector = (state: GridState) => state.sorting; @@ -18,7 +22,7 @@ export const sortedGridRowIdsSelector = createSelector( sortingState.sortedRows.length ? sortingState.sortedRows : allRows, ); -export const sortedGridRowsSelector = createSelector( +export const gridSortedRowsSelector = createSelector( sortedGridRowIdsSelector, gridRowsLookupSelector, (sortedIds: GridRowId[], idRowsLookup: GridRowsLookup) => { @@ -30,6 +34,29 @@ export const sortedGridRowsSelector = createSelector( }, ); +export const gridSortedRowIdsTreeSelector = createSelector( + sortingGridStateSelector, + (sortingState) => sortingState.sortedRowTree, +); + +export const gridSortedRowsTreeSelector = createSelector( + gridSortedRowIdsTreeSelector, + gridRowsLookupSelector, + (sortedTree, idRowsLookup) => { + const buildMap = (nodes: GridSortedRowsIdTreeNode[]) => { + const map = new Map(); + + nodes.forEach((node) => { + map.set(node.id, { node: idRowsLookup[node.id], children: buildMap(node.children) }); + }); + + return map; + }; + + return buildMap(sortedTree); + }, +); + export const gridSortModelSelector = createSelector( sortingGridStateSelector, (sorting) => sorting.sortModel, diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts index 16703d4c8d7d9..1c1c06bd4e97d 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts @@ -1,14 +1,19 @@ -import { GridRowId } from '../../../models/gridRows'; +import { GridRowId, GridRowModel } from '../../../models/gridRows'; import { GridSortModel } from '../../../models/gridSortModel'; -export interface GridSortedRowTreeNode { +export interface GridSortedRowsTreeNode { + node: GridRowModel; + children: Map; +} + +export interface GridSortedRowsIdTreeNode { id: GridRowId; - children: GridSortedRowTreeNode[]; + children: GridSortedRowsIdTreeNode[]; } export interface GridSortingState { sortedRows: GridRowId[]; - sortedRowTree: GridSortedRowTreeNode[]; + sortedRowTree: GridSortedRowsIdTreeNode[]; sortModel: GridSortModel; } diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 51a9a67a7a813..5dea243b5f2ee 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -7,7 +7,7 @@ import { GridCellValue } from '../../../models/gridCell'; import { GridColDef } from '../../../models/colDef/gridColDef'; import { GridFeatureModeConstant } from '../../../models/gridFeatureMode'; import { GridColumnHeaderParams } from '../../../models/params/gridColumnHeaderParams'; -import { GridRowId, GridRowModel, GridRowTree } from '../../../models/gridRows'; +import { GridRowId, GridRowModel, GridRowIdTree } from '../../../models/gridRows'; import { GridFieldComparatorList, GridSortItem, @@ -22,9 +22,9 @@ import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; import { allGridColumnsSelector } from '../columns/gridColumnsSelector'; import { useGridState } from '../core/useGridState'; -import { sortedGridRowIdsSelector, sortedGridRowsSelector } from './gridSortingSelector'; +import { sortedGridRowIdsSelector, gridSortedRowsSelector } from './gridSortingSelector'; import { gridRowTreeSelector } from '../rows'; -import { GridSortedRowTreeNode } from './gridSortingState'; +import { GridSortedRowsIdTreeNode } from './gridSortingState'; /** * @requires useGridRows (state, event) @@ -177,7 +177,7 @@ export const useGridSorting = ( // Tree sorting const aggregatedComparator = comparatorListAggregate(comparatorList); - const sortRowTree = (tree: GridRowTree): GridSortedRowTreeNode[] => { + const sortRowTree = (tree: GridRowIdTree): GridSortedRowsIdTreeNode[] => { return Object.entries(tree) .map(([name, value]) => { const params = comparatorList.map((colComparator) => @@ -246,7 +246,7 @@ export const useGridSorting = ( ); const getSortedRows = React.useCallback( - (): GridRowModel[] => Object.values(sortedGridRowsSelector(apiRef.current.state)), + (): GridRowModel[] => Object.values(gridSortedRowsSelector(apiRef.current.state)), [apiRef], ); diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/GridTreeDataApi.ts b/packages/grid/_modules_/grid/hooks/features/treeData/GridTreeDataApi.ts index 26299d53f42d7..97436ded89482 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/GridTreeDataApi.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/GridTreeDataApi.ts @@ -1,11 +1,11 @@ -import { GridRowTree } from '../../../models/gridRows'; +import { GridRowIdTree } from '../../../models/gridRows'; import { GridRowsLookup } from '../rows'; export interface GridTreeDataApi { /** * Create the tree structure for a given set of rows * @param {GridRowsLookup} rowsLookup the rows to process - * @returns {GridRowTree} tree the tree structure containing all the rows + * @returns {GridRowIdTree} tree the tree structure containing all the rows */ - groupRows: (rowsLookup: GridRowsLookup) => GridRowTree; + groupRows: (rowsLookup: GridRowsLookup) => GridRowIdTree; } diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 908168897a245..739a7ed07dd9e 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -1,11 +1,11 @@ import * as React from 'react'; import { GridTreeDataApi } from './GridTreeDataApi'; -import { GridRowId, GridRowTree } from '../../../models/gridRows'; +import { GridRowId, GridRowIdTree } from '../../../models/gridRows'; import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridComponentProps } from '../../../GridComponentProps'; import { useGridApiMethod } from '../../root/useGridApiMethod'; -const insertRowInTree = (tree: GridRowTree, id: GridRowId, path: string[]) => { +const insertRowInTree = (tree: GridRowIdTree, id: GridRowId, path: string[]) => { if (path.length === 0) { throw new Error(`Material-UI: Could not insert row #${id} in the tree structure.`); } diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index f645245349a03..41f7eea1e3897 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -12,12 +12,12 @@ export interface GridRowModelUpdate extends GridRowData { _action?: GridUpdateAction; } -export interface GridRowTreeNode { +export interface GridRowIdTreeNode { id: GridRowId; - children: GridRowTree; + children: GridRowIdTree; } -export type GridRowTree = { [nodeName: string]: GridRowTreeNode }; +export type GridRowIdTree = { [nodeName: string]: GridRowIdTreeNode }; /** * The type of Id supported by the grid. diff --git a/packages/grid/data-grid/src/tests/state.DataGrid.test.tsx b/packages/grid/data-grid/src/tests/state.DataGrid.test.tsx index 8f2fb007fc941..f35a10b6e8c22 100644 --- a/packages/grid/data-grid/src/tests/state.DataGrid.test.tsx +++ b/packages/grid/data-grid/src/tests/state.DataGrid.test.tsx @@ -1,50 +1,53 @@ -import * as React from 'react'; -import { createClientRenderStrictMode } from 'test/utils'; -import { expect } from 'chai'; -import { DataGrid } from '@mui/x-data-grid'; -import { getColumnValues } from 'test/utils/helperFn'; +// import * as React from 'react'; +// import { createClientRenderStrictMode } from 'test/utils'; +// import { expect } from 'chai'; +// import { DataGrid } from '@mui/x-data-grid'; +// import { getColumnValues } from 'test/utils/helperFn'; +// +// const isJSDOM = /jsdom/.test(window.navigator.userAgent); +// +// describe(' - State', () => { +// // TODO v5: replace with createClientRender +// const render = createClientRenderStrictMode(); +// +// const baselineProps = { +// autoHeight: isJSDOM, +// rows: [ +// { +// id: 0, +// brand: 'Nike', +// }, +// { +// id: 1, +// brand: 'Adidas', +// }, +// { +// id: 2, +// brand: 'Puma', +// }, +// ], +// columns: [{ field: 'brand' }], +// }; +// +// it('should allow to control the state using useState', () => { +// function GridStateTest({ direction, sortedRows }) { +// const gridState = { +// sorting: { sortModel: [{ field: 'brand', sort: direction }], sortedRows }, +// }; +// +// return ( +//
+// +//
+// ); +// } +// +// const { setProps } = render(); +// expect(getColumnValues()).to.deep.equal(['Puma', 'Nike', 'Adidas']); +// setProps({ direction: 'asc', sortedRows: [1, 0, 2] }); +// expect(getColumnValues()).to.deep.equal(['Puma', 'Nike', 'Adidas'].reverse()); +// }); +// }); -const isJSDOM = /jsdom/.test(window.navigator.userAgent); -describe(' - State', () => { - // TODO v5: replace with createClientRender - const render = createClientRenderStrictMode(); - - const baselineProps = { - autoHeight: isJSDOM, - rows: [ - { - id: 0, - brand: 'Nike', - }, - { - id: 1, - brand: 'Adidas', - }, - { - id: 2, - brand: 'Puma', - }, - ], - columns: [{ field: 'brand' }], - }; - - it('should allow to control the state using useState', () => { - function GridStateTest({ direction, sortedRows }) { - const gridState = { - sorting: { sortModel: [{ field: 'brand', sort: direction }], sortedRows }, - }; - - return ( -
- -
- ); - } - - const { setProps } = render(); - expect(getColumnValues()).to.deep.equal(['Puma', 'Nike', 'Adidas']); - setProps({ direction: 'asc', sortedRows: [1, 0, 2] }); - expect(getColumnValues()).to.deep.equal(['Puma', 'Nike', 'Adidas'].reverse()); - }); -}); +export {} \ No newline at end of file From 930442575eb44d5b3362885f28359235e79aeabc Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 15 Sep 2021 12:18:50 +0200 Subject: [PATCH 073/390] Work --- .../_modules_/grid/components/GridFooter.tsx | 4 +- .../grid/components/base/GridOverlays.tsx | 4 +- .../features/filter/gridFilterSelector.ts | 47 +++++----- .../hooks/features/filter/useGridFilter.ts | 86 +++---------------- .../features/filter/visibleGridRowsState.ts | 20 ----- .../hooks/features/pagination/useGridPage.ts | 4 +- .../features/pagination/useGridPageSize.ts | 4 +- .../grid/hooks/features/rows/useGridRows.ts | 13 +-- .../features/sorting/gridSortingSelector.ts | 51 ++++------- .../features/sorting/gridSortingState.ts | 5 +- .../hooks/features/sorting/useGridSorting.ts | 77 ++++++++--------- .../features/treeData/useGridTreeData.ts | 42 ++++++--- .../grid/hooks/root/useGridContainerProps.ts | 4 +- .../_modules_/grid/models/api/gridSortApi.ts | 17 +++- .../grid/_modules_/grid/models/gridRows.ts | 2 +- .../src/tests/state.DataGrid.test.tsx | 3 +- .../src/tests/editRows.DataGridPro.test.tsx | 1 + .../src/tests/filtering.DataGridPro.test.tsx | 5 +- .../src/stories/grid-state.stories.tsx | 3 +- 19 files changed, 163 insertions(+), 229 deletions(-) diff --git a/packages/grid/_modules_/grid/components/GridFooter.tsx b/packages/grid/_modules_/grid/components/GridFooter.tsx index 4f35c2442dda3..f9b579818a051 100644 --- a/packages/grid/_modules_/grid/components/GridFooter.tsx +++ b/packages/grid/_modules_/grid/components/GridFooter.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { useGridSelector } from '../hooks/features/core/useGridSelector'; import { gridRowCountSelector } from '../hooks/features/rows/gridRowsSelector'; import { selectedGridRowsCountSelector } from '../hooks/features/selection/gridSelectionSelector'; -import { visibleGridRowCountSelector } from '../hooks/features/filter/gridFilterSelector'; +import { gridVisibleRowCountSelector } from '../hooks/features/filter/gridFilterSelector'; import { useGridApiContext } from '../hooks/root/useGridApiContext'; import { GridRowCount } from './GridRowCount'; import { GridSelectedRowCount } from './GridSelectedRowCount'; @@ -15,7 +15,7 @@ export const GridFooter = React.forwardRef 0 ? ( diff --git a/packages/grid/_modules_/grid/components/base/GridOverlays.tsx b/packages/grid/_modules_/grid/components/base/GridOverlays.tsx index e7aea44f017cb..850eae0e5143a 100644 --- a/packages/grid/_modules_/grid/components/base/GridOverlays.tsx +++ b/packages/grid/_modules_/grid/components/base/GridOverlays.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { useGridSelector } from '../../hooks/features/core/useGridSelector'; -import { visibleGridRowCountSelector } from '../../hooks/features/filter/gridFilterSelector'; +import { gridVisibleRowCountSelector } from '../../hooks/features/filter/gridFilterSelector'; import { gridRowCountSelector } from '../../hooks/features/rows/gridRowsSelector'; import { useGridApiContext } from '../../hooks/root/useGridApiContext'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; @@ -10,7 +10,7 @@ export function GridOverlays() { const rootProps = useGridRootProps(); const totalRowCount = useGridSelector(apiRef, gridRowCountSelector); - const visibleRowCount = useGridSelector(apiRef, visibleGridRowCountSelector); + const visibleRowCount = useGridSelector(apiRef, gridVisibleRowCountSelector); const showNoRowsOverlay = !rootProps.loading && totalRowCount === 0; const showNoResultsOverlay = !rootProps.loading && totalRowCount > 0 && visibleRowCount === 0; diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index fb233acd2d404..563466c0c31aa 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -1,51 +1,56 @@ import { createSelector } from 'reselect'; import { GridFilterItem } from '../../../models/gridFilterItem'; -import { GridRowId } from '../../../models/gridRows'; +import { GridRowId } from '../../../models/gridRows'; import { GridState } from '../core/gridState'; import { gridRowCountSelector } from '../rows/gridRowsSelector'; -import { gridSortedRowsTreeSelector} from '../sorting/gridSortingSelector'; +import { gridSortedRowsSelector } from '../sorting/gridSortingSelector'; import { gridColumnLookupSelector } from '../columns/gridColumnsSelector'; -import {GridSortedRowsTreeNode} from "../sorting"; +import { GridSortedRowsTreeNode } from '../sorting'; export const gridVisibleRowStateSelector = (state: GridState) => state.visibleRows; export const gridSortedVisibleRowsSelector = createSelector( gridVisibleRowStateSelector, - gridSortedRowsTreeSelector, + gridSortedRowsSelector, (visibleRowsState, sortedRowsTree) => { - const removeHiddenRows = (nodes: Map) => { - const filteredRows = new Map() + const removeHiddenRows = (nodes: Map) => { + const filteredRows = new Map(); - nodes.forEach((row, id) => { - if (visibleRowsState.visibleRowsLookup[id] !== false) { - filteredRows.set(id, { - node: row.node, - children: removeHiddenRows(row.children) - }) - } - }) + nodes.forEach((row, id) => { + if (visibleRowsState.visibleRowsLookup[id] !== false) { + filteredRows.set(id, { + node: row.node, + children: removeHiddenRows(row.children), + }); + } + }); - return filteredRows - } + return filteredRows; + }; - return removeHiddenRows(sortedRowsTree) + return removeHiddenRows(sortedRowsTree); }, ); export const visibleSortedGridRowsAsArraySelector = createSelector( - gridSortedVisibleRowsSelector, + gridSortedVisibleRowsSelector, (visibleSortedRows) => [...visibleSortedRows.entries()], ); export const visibleSortedGridRowIdsSelector = createSelector( - gridSortedVisibleRowsSelector, + gridSortedVisibleRowsSelector, (visibleSortedRows) => [...visibleSortedRows.keys()], ); -export const visibleGridRowCountSelector = createSelector( +export const gridVisibleRowCountSelector = createSelector( gridVisibleRowStateSelector, gridRowCountSelector, - (visibleRowsState, totalRowsCount) => visibleRowsState.visibleRowsCount ?? totalRowsCount, + (visibleRowsState, totalRowsCount) => { + if (visibleRowsState.visibleRows == null) { + return totalRowsCount; + } + return visibleRowsState.visibleRows.length; + }, ); export const filterGridStateSelector = (state: GridState) => state.filter; diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 77f535e3a11f9..4c8b13c9925c4 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -5,7 +5,7 @@ import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridFilterApi } from '../../../models/api/gridFilterApi'; import { GridFeatureModeConstant } from '../../../models/gridFeatureMode'; import { GridFilterItem, GridLinkOperator } from '../../../models/gridFilterItem'; -import { GridRowId, GridRowModel } from '../../../models/gridRows'; +import { GridRowId } from '../../../models/gridRows'; import { isDeepEqual } from '../../../utils/utils'; import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; import { useGridApiMethod } from '../../root/useGridApiMethod'; @@ -14,15 +14,11 @@ import { filterableGridColumnsIdsSelector } from '../columns/gridColumnsSelector import { useGridSelector } from '../core/useGridSelector'; import { useGridState } from '../core/useGridState'; import { GridPreferencePanelsValue } from '../preferencesPanel/gridPreferencePanelsValue'; -import { gridSortedRowsTreeSelector, gridSortedRowsSelector } from '../sorting/gridSortingSelector'; +import { gridSortedRowsSelector } from '../sorting/gridSortingSelector'; import { getInitialGridFilterState } from './gridFilterModelState'; import { GridFilterModel } from '../../../models/gridFilterModel'; import { gridSortedVisibleRowsSelector } from './gridFilterSelector'; -import { - getInitialVisibleGridRowsState, - GridVisibleRowsTreeLookup, - GridVisibleRowsTreeNode, -} from './visibleGridRowsState'; +import { getInitialVisibleGridRowsState } from './visibleGridRowsState'; import { GridSortedRowsTreeNode } from '../sorting'; /** @@ -90,98 +86,44 @@ export const useGridFilter = ( } setGridState((state) => { - // List filtering - // TODO: Remove const visibleRowsLookup = { ...state.visibleRows.visibleRowsLookup }; // We run the selector on the state here to avoid rendering the rows and then filtering again. // This way we have latest rows on the first rendering - const rows = gridSortedRowsSelector(state); - - rows.forEach((row: GridRowModel, id: GridRowId) => { - const params = apiRef.current.getCellParams(id, newFilterItem.columnField!); - - const isShown = applyFilterOnRow(params); - if (visibleRowsLookup[id] == null) { - visibleRowsLookup[id] = isShown; - } else { - visibleRowsLookup[id] = - linkOperator === GridLinkOperator.And - ? visibleRowsLookup[id] && isShown - : visibleRowsLookup[id] || isShown; - } - }); - - const visibleRows = Object.entries(visibleRowsLookup) - .filter(([, isVisible]) => isVisible) - .map(([id]) => id); - - // Tree filtering - // We run the selector on the state here to avoid rendering the rows and then filtering again. - // This way we have latest rows on the first rendering - const rowTree = gridSortedRowsTreeSelector(state); - - const filterRowTree = ( - tree: Map, - visibleLookup: GridVisibleRowsTreeLookup, - ) => { - if (tree.size === 0) { - return { lookup: {}, list: [], flatVisibleRows: [], count: 0, } - } - - const visibleList: GridVisibleRowsTreeNode[] = []; - const flatVisibleRows: GridRowId[] = []; - let visibleRowsCount = 0 + const rowTree = gridSortedRowsSelector(state); + const filterRowTree = (tree: Map) => { tree.forEach((node, id) => { const params = apiRef.current.getCellParams(id, newFilterItem.columnField!); const hasCurrentFilter = applyFilterOnRow(params); - const lookupElementBeforeCurrentFilter = visibleLookup[id]; + const lookupElementBeforeCurrentFilter = visibleRowsLookup[id]; let isVisible: boolean; if (lookupElementBeforeCurrentFilter == null) { isVisible = hasCurrentFilter; } else if (linkOperator === GridLinkOperator.And) { - isVisible = lookupElementBeforeCurrentFilter.isVisible && hasCurrentFilter; + isVisible = lookupElementBeforeCurrentFilter && hasCurrentFilter; } else { - isVisible = lookupElementBeforeCurrentFilter.isVisible || hasCurrentFilter; + isVisible = lookupElementBeforeCurrentFilter || hasCurrentFilter; } - const childrenBeforeFilter = { ...(lookupElementBeforeCurrentFilter?.children ?? {}) }; - const childrenAfterFilter = filterRowTree(node.children, childrenBeforeFilter) + visibleRowsLookup[id] = isVisible; - visibleLookup[id] = { - isVisible, - children: childrenAfterFilter.lookup, - }; - - if (isVisible) { - visibleList.push({ id, children: childrenAfterFilter.list }); - flatVisibleRows.push(id) - visibleRowsCount += 1 - } - - visibleRowsCount += childrenAfterFilter.count - flatVisibleRows.concat(childrenAfterFilter.flatVisibleRows) + filterRowTree(node.children); }); - - return { lookup: visibleLookup, list: visibleList, flatVisibleRows, count: visibleRowsCount }; }; - const visibleRowsTree = filterRowTree(rowTree, { - ...state.visibleRows.visibleRowsTreeLookup, - }); + filterRowTree(rowTree); return { ...state, visibleRows: { ...state.visibleRows, visibleRowsLookup, - visibleRows, - visibleRowsTreeLookup: visibleRowsTree.lookup, - visibleRowsTree: visibleRowsTree.list, - visibleRowsCount: visibleRowsTree.count, + visibleRows: Object.entries(visibleRowsLookup) + .filter(([, isVisible]) => isVisible) + .map(([id]) => id), }, }; }); diff --git a/packages/grid/_modules_/grid/hooks/features/filter/visibleGridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/filter/visibleGridRowsState.ts index f9eccea4204c7..239dca8684437 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/visibleGridRowsState.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/visibleGridRowsState.ts @@ -1,30 +1,10 @@ import { GridRowId } from '../../../models/gridRows'; -export interface GridVisibleRowsTreeNode { - id: GridRowId; - children: GridVisibleRowsTreeNode[]; -} - -export interface GridVisibleRowsTreeLookupNode { - isVisible: boolean; - children: GridVisibleRowsTreeLookup; -} - -export type GridVisibleRowsTreeLookup = { [id: GridRowId]: GridVisibleRowsTreeLookupNode }; - export interface VisibleGridRowsState { visibleRowsLookup: Record; visibleRows?: GridRowId[]; - - // Tree - // visibleRowsTreeLookup: GridVisibleRowsTreeLookup; - // visibleRowsTree?: GridVisibleRowsTreeNode[]; - // visibleRowsCount: number | null, - // flatVisibleRows?: [], } export const getInitialVisibleGridRowsState: () => VisibleGridRowsState = () => ({ visibleRowsLookup: {}, - // visibleRowsTreeLookup: {}, - // visibleRowsCount: null, }); diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts index 8ddfb6f6c9f8a..d4af64a77e7cd 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts @@ -8,7 +8,7 @@ import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { GridPageApi } from '../../../models/api/gridPageApi'; import { GridPaginationState } from './gridPaginationState'; -import { visibleGridRowCountSelector } from '../filter'; +import { gridVisibleRowCountSelector } from '../filter'; const getPageCount = (rowCount: number, pageSize: number): number => { if (pageSize > 0 && rowCount > 0) { @@ -40,7 +40,7 @@ export const useGridPage = ( ) => { const logger = useGridLogger(apiRef, 'useGridPage'); const [, setGridState, forceUpdate] = useGridState(apiRef); - const visibleRowCount = useGridSelector(apiRef, visibleGridRowCountSelector); + const visibleRowCount = useGridSelector(apiRef, gridVisibleRowCountSelector); const setPage = React.useCallback( (page: number) => { diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts index aa81509124f04..75fc415b5f096 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts @@ -6,7 +6,7 @@ import { useGridApiMethod } from '../../root'; import { GridEvents } from '../../../constants/eventsConstants'; import { useGridLogger } from '../../utils'; import { useGridSelector, useGridState } from '../core'; -import { visibleGridRowCountSelector } from '../filter'; +import { gridVisibleRowCountSelector } from '../filter'; import { gridContainerSizesSelector } from '../../root/gridContainerSizesSelector'; /** @@ -20,7 +20,7 @@ export const useGridPageSize = ( ) => { const logger = useGridLogger(apiRef, 'useGridPageSize'); const [, setGridState, forceUpdate] = useGridState(apiRef); - const visibleRowCount = useGridSelector(apiRef, visibleGridRowCountSelector); + const visibleRowCount = useGridSelector(apiRef, gridVisibleRowCountSelector); const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); const setPageSize = React.useCallback( diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index d0f20a5f2d61c..55739f24c5622 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -11,6 +11,7 @@ import { GridRowIdGetter, GridRowData, GridRowIdTree, + GridRowIdTreeNode, } from '../../../models/gridRows'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; @@ -60,7 +61,9 @@ export function convertGridRowsPropToState( } const getFlatRowTree = (rowIds: GridRowId[]): GridRowIdTree => - Object.fromEntries(rowIds.map((id) => [id, { id, children: {} }])); + new Map( + rowIds.map((id) => [id.toString(), { id, children: new Map() }]), + ); /** * @requires useGridSorting (method) @@ -81,8 +84,8 @@ export const useGridRows = ( const getRowIndex = React.useCallback( (id) => { - if (apiRef.current.getSortedRowIds) { - return apiRef.current.getSortedRowIds().indexOf(id); + if (apiRef.current.getFlatSortedRowIds) { + return apiRef.current.getFlatSortedRowIds().indexOf(id); } return apiRef.current.state.rows.allRows.indexOf(id); }, @@ -91,8 +94,8 @@ export const useGridRows = ( const getRowIdFromRowIndex = React.useCallback( (index) => { - if (apiRef.current.getSortedRowIds) { - return apiRef.current.getSortedRowIds()[index]; + if (apiRef.current.getFlatSortedRowIds) { + return apiRef.current.getFlatSortedRowIds()[index]; } return apiRef.current.state.rows.allRows[index]; }, diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts index 2c71f51496da7..0da551fd1e9ae 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts @@ -1,46 +1,29 @@ import { createSelector } from 'reselect'; -import { GridRowId, GridRowModel } from '../../../models/gridRows'; +import { GridRowId } from '../../../models/gridRows'; import { GridSortDirection, GridSortModel } from '../../../models/gridSortModel'; import { GridState } from '../core/gridState'; -import { - GridRowsLookup, - gridRowsLookupSelector, - unorderedGridRowIdsSelector, -} from '../rows/gridRowsSelector'; -import { - GridSortedRowsIdTreeNode, - GridSortedRowsTreeNode, - GridSortingState, -} from './gridSortingState'; +import { gridRowsLookupSelector } from '../rows/gridRowsSelector'; +import { GridSortedRowsIdTreeNode, GridSortedRowsTreeNode } from './gridSortingState'; -const sortingGridStateSelector = (state: GridState) => state.sorting; +const gridSortingStateSelector = (state: GridState) => state.sorting; -export const sortedGridRowIdsSelector = createSelector( - sortingGridStateSelector, - unorderedGridRowIdsSelector, - (sortingState: GridSortingState, allRows: GridRowId[]) => - sortingState.sortedRows.length ? sortingState.sortedRows : allRows, +export const gridSortedRowIdsSelector = createSelector( + gridSortingStateSelector, + (sortingState) => sortingState.sortedRows, ); -export const gridSortedRowsSelector = createSelector( - sortedGridRowIdsSelector, - gridRowsLookupSelector, - (sortedIds: GridRowId[], idRowsLookup: GridRowsLookup) => { - const map = new Map(); - sortedIds.forEach((id) => { - map.set(id, idRowsLookup[id]); - }); - return map; - }, -); +export const gridSortedRowIdsFlatSelector = createSelector( + gridSortedRowIdsSelector, + (sortedRowIds) => { + const flattenRowIds = (nodes: GridSortedRowsIdTreeNode[]): GridRowId[] => + nodes.flatMap((node) => [node.id, ...flattenRowIds(node.children)]); -export const gridSortedRowIdsTreeSelector = createSelector( - sortingGridStateSelector, - (sortingState) => sortingState.sortedRowTree, + return flattenRowIds(sortedRowIds); + }, ); -export const gridSortedRowsTreeSelector = createSelector( - gridSortedRowIdsTreeSelector, +export const gridSortedRowsSelector = createSelector( + gridSortedRowIdsSelector, gridRowsLookupSelector, (sortedTree, idRowsLookup) => { const buildMap = (nodes: GridSortedRowsIdTreeNode[]) => { @@ -58,7 +41,7 @@ export const gridSortedRowsTreeSelector = createSelector( ); export const gridSortModelSelector = createSelector( - sortingGridStateSelector, + gridSortingStateSelector, (sorting) => sorting.sortModel, ); diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts index 1c1c06bd4e97d..d2c75a49ada45 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts @@ -12,11 +12,10 @@ export interface GridSortedRowsIdTreeNode { } export interface GridSortingState { - sortedRows: GridRowId[]; - sortedRowTree: GridSortedRowsIdTreeNode[]; + sortedRows: GridSortedRowsIdTreeNode[]; sortModel: GridSortModel; } export function getInitialGridSortingState(): GridSortingState { - return { sortedRows: [], sortedRowTree: [], sortModel: [] }; + return { sortedRows: [], sortModel: [] }; } diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 5dea243b5f2ee..f360f43294b5f 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -7,7 +7,7 @@ import { GridCellValue } from '../../../models/gridCell'; import { GridColDef } from '../../../models/colDef/gridColDef'; import { GridFeatureModeConstant } from '../../../models/gridFeatureMode'; import { GridColumnHeaderParams } from '../../../models/params/gridColumnHeaderParams'; -import { GridRowId, GridRowModel, GridRowIdTree } from '../../../models/gridRows'; +import { GridRowId, GridRowIdTree } from '../../../models/gridRows'; import { GridFieldComparatorList, GridSortItem, @@ -22,7 +22,11 @@ import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; import { allGridColumnsSelector } from '../columns/gridColumnsSelector'; import { useGridState } from '../core/useGridState'; -import { sortedGridRowIdsSelector, gridSortedRowsSelector } from './gridSortingSelector'; +import { + gridSortedRowIdsSelector, + gridSortedRowIdsFlatSelector, + gridSortedRowsSelector, +} from './gridSortingSelector'; import { gridRowTreeSelector } from '../rows'; import { GridSortedRowsIdTreeNode } from './gridSortingState'; @@ -147,57 +151,42 @@ export const useGridSorting = ( ); const applySorting = React.useCallback(() => { - const unsortedRows = apiRef.current.getAllRowIds(); const unsortedRowTree = gridRowTreeSelector(apiRef.current.state); - if (props.sortingMode === GridFeatureModeConstant.server) { + const skipServerSorting = props.sortingMode === GridFeatureModeConstant.server; + + if (skipServerSorting) { logger.debug('Skipping sorting rows as sortingMode = server'); } const sortModel = apiRef.current.state.sorting.sortModel; const comparatorList = buildComparatorList(sortModel); - - // List sorting - // TODO: Remove - let sortedRows: GridRowId[]; - if (sortModel.length > 0 && props.sortingMode === GridFeatureModeConstant.client) { - logger.debug('Sorting rows with ', sortModel); - sortedRows = unsortedRows - .map((id) => { - return comparatorList.map((colComparator) => { - return getSortCellParams(id, colComparator.field); - }); - }) - .sort(comparatorListAggregate(comparatorList)) - .map((field) => field[0].id); - } else { - sortedRows = unsortedRows; - } - - // Tree sorting const aggregatedComparator = comparatorListAggregate(comparatorList); const sortRowTree = (tree: GridRowIdTree): GridSortedRowsIdTreeNode[] => { - return Object.entries(tree) - .map(([name, value]) => { - const params = comparatorList.map((colComparator) => - getSortCellParams(value.id, colComparator.field), - ); + const rowsWithParams = Array.from(tree.entries()).map(([name, value]) => { + const params = comparatorList.map((colComparator) => + getSortCellParams(value.id, colComparator.field), + ); - return { value, name, params }; - }) - .sort((a, b) => aggregatedComparator(a.params, b.params)) - .map((node) => ({ - id: node.value.id, - children: sortRowTree(node.value.children), - })); + return { value, name, params }; + }); + + const sortedRowsWithParams = skipServerSorting + ? rowsWithParams + : rowsWithParams.sort((a, b) => aggregatedComparator(a.params, b.params)); + + return sortedRowsWithParams.map((node) => ({ + id: node.value.id, + children: sortRowTree(node.value.children), + })); }; - const sortedRowTree = sortRowTree(unsortedRowTree); + const sortedRows = sortRowTree(unsortedRowTree); setGridState((state) => { return { ...state, - sorting: { ...state.sorting, sortedRows, sortedRowTree }, + sorting: { ...state.sorting, sortedRows }, }; }); forceUpdate(); @@ -245,13 +234,18 @@ export const useGridSorting = ( [gridState.sorting.sortModel], ); - const getSortedRows = React.useCallback( - (): GridRowModel[] => Object.values(gridSortedRowsSelector(apiRef.current.state)), + const getSortedRows = React.useCallback( + () => gridSortedRowsSelector(apiRef.current.state), + [apiRef], + ); + + const getSortedRowIds = React.useCallback( + () => gridSortedRowIdsSelector(apiRef.current.state), [apiRef], ); - const getSortedRowIds = React.useCallback( - (): GridRowId[] => sortedGridRowIdsSelector(apiRef.current.state), + const getFlatSortedRowIds = React.useCallback( + () => gridSortedRowIdsFlatSelector(apiRef.current.state), [apiRef], ); @@ -259,6 +253,7 @@ export const useGridSorting = ( getSortModel, getSortedRows, getSortedRowIds, + getFlatSortedRowIds, setSortModel, sortColumn, applySorting, diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 739a7ed07dd9e..133787044137f 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -1,6 +1,12 @@ import * as React from 'react'; import { GridTreeDataApi } from './GridTreeDataApi'; -import { GridRowId, GridRowIdTree } from '../../../models/gridRows'; +import { + GridRowData, + GridRowId, + GridRowIdGetter, + GridRowIdTree, + GridRowIdTreeNode, +} from '../../../models/gridRows'; import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridComponentProps } from '../../../GridComponentProps'; import { useGridApiMethod } from '../../root/useGridApiMethod'; @@ -11,14 +17,14 @@ const insertRowInTree = (tree: GridRowIdTree, id: GridRowId, path: string[]) => } if (path.length === 1) { - tree[path[0]] = { + tree.set(path[0], { id, - children: {}, - }; + children: new Map(), + }); } else { const [nodeName, ...restPath] = path; - if (!tree.hasOwnProperty(nodeName)) { + if (!tree.has(nodeName)) { throw new Error(`Material-UI: Could not insert row #${id} in the tree structure.`); } @@ -26,25 +32,35 @@ const insertRowInTree = (tree: GridRowIdTree, id: GridRowId, path: string[]) => } }; +// TODO: Remove, it is only here to avoid casting the ID when using the lookup key +function getGridRowId(rowData: GridRowData, getRowId?: GridRowIdGetter): GridRowId { + return getRowId ? getRowId(rowData) : rowData.id; +} + /** * Only available in DataGridPro */ export const useGridTreeData = ( apiRef: GridApiRef, - props: Pick, + props: Pick, ) => { const groupRows = React.useCallback( (rowsLookup) => { - const getTreeDataPath = props.getTreeDataPath ?? ((row) => [row.id.toString()]); + const getTreeDataPath = + props.getTreeDataPath ?? ((row) => [getGridRowId(row, props.getRowId).toString()]); + + const rows = Object.values(rowsLookup) + .map((row) => { + const id = getGridRowId(row, props.getRowId); - const rows = Object.entries(rowsLookup) - .map(([rowId, row]) => ({ - id: rowId, - path: getTreeDataPath(row), - })) + return { + id, + path: getTreeDataPath(row), + }; + }) .sort((a, b) => a.path.length - b.path.length); - const tree = {}; + const tree = new Map(); rows.forEach((row) => { insertRowInTree(tree, row.id, row.path); }); diff --git a/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts b/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts index 6a33113d9ccb7..6a4c3ab41f2e1 100644 --- a/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts +++ b/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts @@ -14,7 +14,7 @@ import { GridState } from '../features/core/gridState'; import { useGridSelector } from '../features/core/useGridSelector'; import { useGridState } from '../features/core/useGridState'; import { gridDensityRowHeightSelector } from '../features/density/densitySelector'; -import { visibleGridRowCountSelector } from '../features/filter/gridFilterSelector'; +import { gridVisibleRowCountSelector } from '../features/filter/gridFilterSelector'; import { gridPaginationSelector } from '../features/pagination/gridPaginationSelector'; import { useGridLogger } from '../utils/useGridLogger'; import { useGridApiEventHandler } from './useGridApiEventHandler'; @@ -61,7 +61,7 @@ export const useGridContainerProps = ( const windowSizesRef = React.useRef({ width: 0, height: 0 }); const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector); const columnsTotalWidth = useGridSelector(apiRef, gridColumnsTotalWidthSelector); - const visibleRowsCount = useGridSelector(apiRef, visibleGridRowCountSelector); + const visibleRowsCount = useGridSelector(apiRef, gridVisibleRowCountSelector); const paginationState = useGridSelector(apiRef, gridPaginationSelector); const windowRef = apiRef.current.windowRef; diff --git a/packages/grid/_modules_/grid/models/api/gridSortApi.ts b/packages/grid/_modules_/grid/models/api/gridSortApi.ts index 39d45f8126e43..7248bf1963116 100644 --- a/packages/grid/_modules_/grid/models/api/gridSortApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridSortApi.ts @@ -1,6 +1,10 @@ import { GridColDef } from '../colDef/gridColDef'; -import { GridRowId, GridRowModel } from '../gridRows'; +import { GridRowId } from '../gridRows'; import { GridSortDirection, GridSortModel } from '../gridSortModel'; +import { + GridSortedRowsIdTreeNode, + GridSortedRowsTreeNode, +} from '../../hooks/features/sorting/gridSortingState'; /** * The sort API interface that is available in the grid [[apiRef]]. @@ -33,12 +37,17 @@ export interface GridSortApi { ) => void; /** * Returns all rows sorted according to the active sort model. - * @returns {GridRowModel[]} The sorted [[GridRowModel]] objects. + * @returns {Map} The sorted [[GridRowModel]] objects. */ - getSortedRows: () => GridRowModel[]; + getSortedRows: () => Map; + /** + * Returns all row ids sorted according to the active sort model. + * @returns {GridSortedRowsIdTreeNode[]} The sorted [[GridRowId]] values. + */ + getSortedRowIds: () => GridSortedRowsIdTreeNode[]; /** * Returns all row ids sorted according to the active sort model. * @returns {GridRowId[]} The sorted [[GridRowId]] values. */ - getSortedRowIds: () => GridRowId[]; + getFlatSortedRowIds: () => GridRowId[]; } diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index 41f7eea1e3897..7640efa484d8a 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -17,7 +17,7 @@ export interface GridRowIdTreeNode { children: GridRowIdTree; } -export type GridRowIdTree = { [nodeName: string]: GridRowIdTreeNode }; +export type GridRowIdTree = Map; /** * The type of Id supported by the grid. diff --git a/packages/grid/data-grid/src/tests/state.DataGrid.test.tsx b/packages/grid/data-grid/src/tests/state.DataGrid.test.tsx index f35a10b6e8c22..03663f2f8934a 100644 --- a/packages/grid/data-grid/src/tests/state.DataGrid.test.tsx +++ b/packages/grid/data-grid/src/tests/state.DataGrid.test.tsx @@ -49,5 +49,4 @@ // }); // }); - -export {} \ No newline at end of file +export {}; diff --git a/packages/grid/x-grid/src/tests/editRows.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/editRows.DataGridPro.test.tsx index d971841cc414a..3fff1e0dcffda 100644 --- a/packages/grid/x-grid/src/tests/editRows.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/editRows.DataGridPro.test.tsx @@ -419,6 +419,7 @@ describe(' - Edit Rows', () => { fireEvent.keyDown(input, { key: 'Enter' }); expect(cell).not.to.have.class('MuiDataGrid-cell--editing'); expect(cell).to.have.text('1962'); + expect(valueParser.callCount).to.equal(1); expect(valueParser.args[0][0]).to.equal('62'); expect(valueParser.args[0][1]).to.deep.include({ diff --git a/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx index ca2a06b1f9922..7be47070a89fb 100644 --- a/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx @@ -288,7 +288,10 @@ describe(' - Filter', () => { expect(getColumnValues()).to.deep.equal(['Adidas']); expect(apiRef.current.getVisibleRowModels().size).to.equal(1); - expect(apiRef.current.getVisibleRowModels().get(1)).to.deep.equal({ id: 1, brand: 'Adidas' }); + expect(apiRef.current.getVisibleRowModels().get(1)!.node).to.deep.equal({ + id: 1, + brand: 'Adidas', + }); }); describe('performance', () => { diff --git a/packages/storybook/src/stories/grid-state.stories.tsx b/packages/storybook/src/stories/grid-state.stories.tsx index c645064e75ef2..fa646a1c36fb3 100644 --- a/packages/storybook/src/stories/grid-state.stories.tsx +++ b/packages/storybook/src/stories/grid-state.stories.tsx @@ -111,8 +111,7 @@ export function InitialState() { const [gridState, setGridState] = React.useState>({ sorting: { sortModel: [{ field: 'brand', sort: 'desc' }], - sortedRows: [2, 0, 1], - sortedRowTree: [ + sortedRows: [ { id: 2, children: [] }, { id: 0, children: [] }, { id: 1, children: [] }, From 61c6b1f524bf37580634b9938b944c8c91bbafda Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 15 Sep 2021 12:25:15 +0200 Subject: [PATCH 074/390] Fix --- .../grid/_modules_/grid/hooks/features/rows/gridRowsState.ts | 2 +- .../grid/hooks/features/treeData/useGridTreeData.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts index fdad45c1d9fa7..7b3bc097e3793 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts @@ -9,7 +9,7 @@ export interface GridRowsState { export const getInitialGridRowState: () => GridRowsState = () => ({ idRowsLookup: {}, - tree: {}, + tree: new Map(), allRows: [], totalRowCount: 0, }); diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 133787044137f..511904963e889 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -24,11 +24,12 @@ const insertRowInTree = (tree: GridRowIdTree, id: GridRowId, path: string[]) => } else { const [nodeName, ...restPath] = path; - if (!tree.has(nodeName)) { + const parent = tree.get(nodeName); + if (!parent) { throw new Error(`Material-UI: Could not insert row #${id} in the tree structure.`); } - insertRowInTree(tree[nodeName].children, id, restPath); + insertRowInTree(parent.children, id, restPath); } }; From 94fa8105b5aef633d8ce261157f4cf7cb7b5852b Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 15 Sep 2021 12:44:39 +0200 Subject: [PATCH 075/390] Fix --- .../columnHeaders/GridColumnHeaders.tsx | 7 +++- .../GridColumnHeadersItemCollection.tsx | 8 ++--- .../columnResize/useGridColumnResize.tsx | 34 ++++++++++++++++--- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx index 9e6155b984547..5f46acfe8f136 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx @@ -34,6 +34,7 @@ const useUtilityClasses = (ownerState: OwnerState) => { export interface GridColumnsHeaderProps { dragCol?: string; + resizeCol?: string; } export const GridColumnsHeader = React.forwardRef( @@ -67,7 +68,11 @@ export const GridColumnsHeader = React.forwardRef - + diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx index 1e310f5f81ec9..6a81f50e3076b 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx @@ -21,14 +21,14 @@ import { gridScrollBarSizeSelector } from '../../hooks/root/gridContainerSizesSe export interface GridColumnHeadersItemCollectionProps { columns: GridStateColDef[]; dragCol?: string; + resizeCol?: string; } function GridColumnHeadersItemCollection(props: GridColumnHeadersItemCollectionProps) { - const { columns } = props; + const { columns, dragCol, resizeCol } = props; const apiRef = useGridApiContext(); const sortColumnLookup = useGridSelector(apiRef, gridSortColumnLookupSelector); const filterColumnLookup = useGridSelector(apiRef, filterGridColumnLookupSelector); - const resizingColumnField = useGridSelector(apiRef, gridResizingColumnFieldSelector); const columnHeaderFocus = useGridSelector(apiRef, gridFocusColumnHeaderSelector); const renderCtx = useGridSelector(apiRef, gridRenderingSelector).renderContext; const tabIndexState = useGridSelector(apiRef, gridTabIndexColumnHeaderSelector); @@ -65,10 +65,10 @@ function GridColumnHeadersItemCollection(props: GridColumnHeadersItemCollectionP columnMenuOpen={open} filterItemsCounter={filterColumnLookup[col.field] && filterColumnLookup[col.field].length} headerHeight={headerHeight} - isDragging={col.field === props.dragCol} + isDragging={col.field === dragCol} column={col} colIndex={colIndex} - isResizing={resizingColumnField === col.field} + isResizing={resizeCol === col.field} isLastColumn={colIndex === columns.length - 1} extendRowFullWidth={!rootProps.disableExtendRowFullWidth} hasScrollX={scrollBarState.hasScrollX} diff --git a/packages/grid/_modules_/grid/hooks/features/columnResize/useGridColumnResize.tsx b/packages/grid/_modules_/grid/hooks/features/columnResize/useGridColumnResize.tsx index 72d11e1174ed4..e4b2e5c697528 100644 --- a/packages/grid/_modules_/grid/hooks/features/columnResize/useGridColumnResize.tsx +++ b/packages/grid/_modules_/grid/hooks/features/columnResize/useGridColumnResize.tsx @@ -11,12 +11,19 @@ import { getFieldFromHeaderElem, findHeaderElementFromField, } from '../../../utils/domUtils'; -import { GridApiRef, CursorCoordinates, GridColumnHeaderParams } from '../../../models'; +import { + GridApiRef, + CursorCoordinates, + GridColumnHeaderParams, + GridSlotsComponentsProps, +} from '../../../models'; import { useGridApiEventHandler, useGridApiOptionHandler } from '../../root/useGridApiEventHandler'; import { useGridState } from '../core/useGridState'; import { useNativeEventListener } from '../../root/useNativeEventListener'; import { GridComponentProps } from '../../../GridComponentProps'; import { useGridStateInit } from '../../utils/useGridStateInit'; +import { useGridSelector } from '../core'; +import { gridResizingColumnFieldSelector } from './columnResizeSelector'; // TODO: remove support for Safari < 13. // https://caniuse.com/#search=touch-action @@ -66,10 +73,7 @@ function trackFinger(event, currentTouchId): CursorCoordinates | boolean { * @requires useGridColumns (method, event) * TODO: improve experience for last column */ -export const useGridColumnResize = ( - apiRef: GridApiRef, - props: Pick, -) => { +export const useGridColumnResize = (apiRef: GridApiRef, props: GridComponentProps) => { const logger = useGridLogger(apiRef, 'useGridColumnResize'); useGridStateInit(apiRef, (state) => ({ @@ -83,6 +87,7 @@ export const useGridColumnResize = ( const initialOffset = React.useRef(); const stopResizeEventTimeout = React.useRef(); const touchId = React.useRef(); + const resizingColumnField = useGridSelector(apiRef, gridResizingColumnFieldSelector); const updateWidth = (newWidth: number) => { logger.debug(`Updating width to ${newWidth} for col ${colDefRef.current!.field}`); @@ -346,4 +351,23 @@ export const useGridColumnResize = ( useGridApiOptionHandler(apiRef, GridEvents.columnResize, props.onColumnResize); useGridApiOptionHandler(apiRef, GridEvents.columnWidthChange, props.onColumnWidthChange); + + const componentsProps = React.useMemo( + () => ({ + ...props.componentsProps, + columnsHeader: { + ...props.componentsProps?.columnsHeader, + resizeCol: resizingColumnField, + }, + }), + [props.componentsProps, resizingColumnField], + ); + + return React.useMemo( + () => ({ + ...props, + componentsProps, + }), + [props, componentsProps], + ); }; From b29874529efbff0a2fffa5b7456412fe00d70f35 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 15 Sep 2021 12:54:52 +0200 Subject: [PATCH 076/390] Fix --- .../components/columnHeaders/GridColumnHeadersItemCollection.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx index 6a81f50e3076b..e9cdd0c045e5b 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { gridResizingColumnFieldSelector } from '../../hooks/features/columnResize/columnResizeSelector'; import { useGridSelector } from '../../hooks/features/core/useGridSelector'; import { filterGridColumnLookupSelector } from '../../hooks/features/filter/gridFilterSelector'; import { From d9c0d9f5f3b45f4e7f0e84e177d7dd447cc413b1 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 15 Sep 2021 13:05:38 +0200 Subject: [PATCH 077/390] proptype --- .../components/columnHeaders/GridColumnHeadersItemCollection.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx index e9cdd0c045e5b..c0b5defa29982 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx @@ -88,6 +88,7 @@ GridColumnHeadersItemCollection.propTypes = { // ---------------------------------------------------------------------- columns: PropTypes.arrayOf(PropTypes.object).isRequired, dragCol: PropTypes.string, + resizeCol: PropTypes.string, } as any; export { GridColumnHeadersItemCollection }; From b5f6f1a394d1d79f429a1b2919a69e6bc1540479 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 15 Sep 2021 13:21:10 +0200 Subject: [PATCH 078/390] Fix --- docs/pages/api-docs/data-grid/grid-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index 0d3c952406571..3d6e440182b05 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -56,7 +56,7 @@ import { GridApi } from '@mui/x-data-grid-pro'; | hidePreferences | () => void | Hides the preferences panel. | | isCellEditable | (params: GridCellParams) => boolean | Controls if a cell is editable. | | isRowSelected | (id: GridRowId) => boolean | Determines if a row is selected or not. | -| publishEvent | (name: string, params?: any, event?: MuiEvent<MouseEvent \| UIEvent \| Event \| SyntheticEvent<Element, Event> \| TouchEvent \| ErrorEvent \| ProgressEvent<EventTarget> \| ClipboardEvent \| AnimationEvent \| InputEvent \| FocusEvent \| CompositionEvent \| DragEvent \| FormDataEvent \| PointerEvent \| KeyboardEvent \| SecurityPolicyViolationEvent \| TransitionEvent \| WheelEvent>) => void | Emits an event. | +| publishEvent | (name: string, params?: any, event?: MuiEvent<MouseEvent \| UIEvent \| Event \| SyntheticEvent<Element, Event> \| TouchEvent \| KeyboardEvent \| ErrorEvent \| ProgressEvent<EventTarget> \| ClipboardEvent \| AnimationEvent \| InputEvent \| FocusEvent \| CompositionEvent \| DragEvent \| FormDataEvent \| PointerEvent \| SecurityPolicyViolationEvent \| TransitionEvent \| WheelEvent>) => void | Emits an event. | | resize | () => void | Triggers a resize of the component and recalculation of width and height. | | scroll | (params: Partial<GridScrollParams>) => void | Triggers the viewport to scroll to the given positions (in pixels). | | scrollToIndexes | (params: Partial<GridCellIndexCoordinates>) => boolean | Triggers the viewport to scroll to the cell at indexes given by `params`.
Returns `true` if the grid had to scroll to reach the target. | From 21184ef020e444e1071e79443d6bd71ba4a8787a Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 15 Sep 2021 13:39:29 +0200 Subject: [PATCH 079/390] Remove allRows fro row state --- .../hooks/features/rows/gridRowsSelector.ts | 17 +++--- .../grid/hooks/features/rows/gridRowsState.ts | 2 - .../grid/hooks/features/rows/useGridRows.ts | 56 +++++++++++-------- .../features/useGridSlotComponentProps.tsx | 4 +- .../_modules_/grid/models/api/gridRowApi.ts | 8 +-- .../models/params/gridSlotComponentProps.ts | 4 +- 6 files changed, 48 insertions(+), 43 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts index a10cc73308bf1..a0d3b0114cfba 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts @@ -1,5 +1,5 @@ import { createSelector } from 'reselect'; -import { GridRowId, GridRowModel } from '../../../models/gridRows'; +import { GridRowId, GridRowIdTree, GridRowModel } from '../../../models/gridRows'; import { GridState } from '../core/gridState'; import { GridRowsState } from './gridRowsState'; @@ -17,14 +17,11 @@ export const gridRowsLookupSelector = createSelector( (rows: GridRowsState) => rows.idRowsLookup, ); -export const unorderedGridRowIdsSelector = createSelector( - gridRowsStateSelector, - (rows: GridRowsState) => rows.allRows, -); - export const gridRowTreeSelector = createSelector(gridRowsStateSelector, (rows) => rows.tree); -export const unorderedGridRowModelsSelector = createSelector( - gridRowsStateSelector, - (rows: GridRowsState) => rows.allRows.map((id) => rows.idRowsLookup[id]), -); +export const gridRowIdsFlatSelector = createSelector(gridRowTreeSelector, (tree) => { + const flattenRowIds = (nodes: GridRowIdTree): GridRowId[] => + Array.from(nodes.values()).flatMap((node) => [node.id, ...flattenRowIds(node.children)]); + + return flattenRowIds(tree); +}); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts index 7b3bc097e3793..be90a0025f55c 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts @@ -2,7 +2,6 @@ import { GridRowId, GridRowModel, GridRowIdTree } from '../../../models/gridRows export interface GridRowsState { idRowsLookup: Record; - allRows: GridRowId[]; tree: GridRowIdTree; totalRowCount: number; } @@ -10,6 +9,5 @@ export interface GridRowsState { export const getInitialGridRowState: () => GridRowsState = () => ({ idRowsLookup: {}, tree: new Map(), - allRows: [], totalRowCount: 0, }); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 55739f24c5622..39c2c9270056e 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -16,14 +16,17 @@ import { import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; import { useGridState } from '../core/useGridState'; -import { getInitialGridRowState, GridRowsState } from './gridRowsState'; +import { GridRowsState } from './gridRowsState'; import { gridRowCountSelector, gridRowsLookupSelector, - unorderedGridRowIdsSelector, + gridRowTreeSelector, + gridRowIdsFlatSelector, } from './gridRowsSelector'; -export type GridRowsInternalCacheState = Omit; +export type GridRowsInternalCacheState = Omit & { + rowIds: GridRowId[]; +}; export interface GridRowsInternalCache { state: GridRowsInternalCacheState; @@ -46,15 +49,16 @@ export function convertGridRowsPropToState( propRowCount?: number, rowIdGetter?: GridRowIdGetter, ): GridRowsInternalCacheState { - const state: GridRowsState = { - ...getInitialGridRowState(), + const state: GridRowsInternalCacheState = { + idRowsLookup: {}, + rowIds: [], totalRowCount: propRowCount && propRowCount > rows.length ? propRowCount : rows.length, }; rows.forEach((rowData) => { const id = getGridRowId(rowData, rowIdGetter); - state.allRows.push(id); state.idRowsLookup[id] = rowData; + state.rowIds.push(id); }); return state; @@ -77,7 +81,11 @@ export const useGridRows = ( const [, setGridState, forceUpdate] = useGridState(apiRef); const rowsCache = React.useRef({ - state: getInitialGridRowState(), + state: { + idRowsLookup: {}, + totalRowCount: 0, + rowIds: [], + }, timeout: null, lastUpdateMs: null, }); @@ -87,7 +95,9 @@ export const useGridRows = ( if (apiRef.current.getFlatSortedRowIds) { return apiRef.current.getFlatSortedRowIds().indexOf(id); } - return apiRef.current.state.rows.allRows.indexOf(id); + + // TODO: Remove, getFlatSortedRowIds should always be defined + return gridRowIdsFlatSelector(apiRef.current.state).indexOf(id); }, [apiRef], ); @@ -97,7 +107,9 @@ export const useGridRows = ( if (apiRef.current.getFlatSortedRowIds) { return apiRef.current.getFlatSortedRowIds()[index]; } - return apiRef.current.state.rows.allRows[index]; + + // TODO: Remove, getFlatSortedRowIds should always be defined + return gridRowIdsFlatSelector(apiRef.current.state)[index]; }, [apiRef], ); @@ -112,10 +124,10 @@ export const useGridRows = ( const run = () => { rowsCache.current.timeout = null; rowsCache.current.lastUpdateMs = Date.now(); - const rowState = rowsCache.current.state; + const { rowIds, ...rowState } = rowsCache.current.state; const tree = apiRef.current.groupRows ? apiRef.current.groupRows(rowState.idRowsLookup) - : getFlatRowTree(rowState.allRows); + : getFlatRowTree(rowIds); setGridState((state) => ({ ...state, rows: { ...rowState, tree } })); apiRef.current.publishEvent(GridEvents.rowsSet); forceUpdate(); @@ -178,7 +190,7 @@ export const useGridRows = ( const deletedRowIds: GridRowId[] = []; const idRowsLookup = { ...rowsCache.current.state.idRowsLookup }; - let allRows = [...rowsCache.current.state.allRows]; + let rowIds = [...rowsCache.current.state.rowIds]; uniqUpdates.forEach((partialRow, id) => { // eslint-disable-next-line no-underscore-dangle @@ -191,7 +203,7 @@ export const useGridRows = ( const oldRow = apiRef.current.getRow(id); if (!oldRow) { idRowsLookup[id] = partialRow; - allRows.push(id); + rowIds.push(id); return; } @@ -199,15 +211,15 @@ export const useGridRows = ( }); if (deletedRowIds.length > 0) { - allRows = allRows.filter((id) => !deletedRowIds.includes(id)); + rowIds = rowIds.filter((id) => !deletedRowIds.includes(id)); } const totalRowCount = - props.rowCount && props.rowCount > allRows.length ? props.rowCount : allRows.length; + props.rowCount && props.rowCount > rowIds.length ? props.rowCount : rowIds.length; const state: GridRowsInternalCacheState = { idRowsLookup, - allRows, + rowIds, totalRowCount, }; @@ -216,12 +228,10 @@ export const useGridRows = ( [apiRef, props.getRowId, props.rowCount, throttledRowsChange], ); - const getRowModels = React.useCallback(() => { - const allRows = unorderedGridRowIdsSelector(apiRef.current.state); - const idRowsLookup = gridRowsLookupSelector(apiRef.current.state); - - return new Map(allRows.map((id) => [id, idRowsLookup[id]])); - }, [apiRef]); + const getRowModels = React.useCallback( + () => gridRowTreeSelector(apiRef.current.state), + [apiRef], + ); const getRowsCount = React.useCallback( () => gridRowCountSelector(apiRef.current.state), @@ -229,7 +239,7 @@ export const useGridRows = ( ); const getAllRowIds = React.useCallback( - () => unorderedGridRowIdsSelector(apiRef.current.state), + () => gridRowIdsFlatSelector(apiRef.current.state), [apiRef], ); diff --git a/packages/grid/_modules_/grid/hooks/features/useGridSlotComponentProps.tsx b/packages/grid/_modules_/grid/hooks/features/useGridSlotComponentProps.tsx index 5e6cac49ca531..f5d0fe8bd4ef9 100644 --- a/packages/grid/_modules_/grid/hooks/features/useGridSlotComponentProps.tsx +++ b/packages/grid/_modules_/grid/hooks/features/useGridSlotComponentProps.tsx @@ -4,11 +4,11 @@ import { GridSlotComponentProps } from '../../models/params/gridSlotComponentPro import { visibleGridColumnsSelector } from './columns/gridColumnsSelector'; import { useGridSelector } from './core/useGridSelector'; import { useGridState } from './core/useGridState'; -import { unorderedGridRowModelsSelector } from './rows/gridRowsSelector'; +import { gridRowTreeSelector } from './rows/gridRowsSelector'; export const useGridSlotComponentProps = () => { const apiRef = useGridApiContext(); - const rows = useGridSelector(apiRef, unorderedGridRowModelsSelector); + const rows = useGridSelector(apiRef, gridRowTreeSelector); const columns = useGridSelector(apiRef, visibleGridColumnsSelector); const [state] = useGridState(apiRef); diff --git a/packages/grid/_modules_/grid/models/api/gridRowApi.ts b/packages/grid/_modules_/grid/models/api/gridRowApi.ts index 9c16e4126e32b..0dcac25088b2d 100644 --- a/packages/grid/_modules_/grid/models/api/gridRowApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridRowApi.ts @@ -1,14 +1,14 @@ -import { GridRowModel, GridRowId, GridRowModelUpdate } from '../gridRows'; +import { GridRowModel, GridRowId, GridRowModelUpdate, GridRowIdTree } from '../gridRows'; /** * The Row API interface that is available in the grid `apiRef`. */ export interface GridRowApi { /** - * Gets the full set of rows as [[Map]]. - * @returns {Map} The full set of rows. + * Gets the full set of rows ordered in a tree structure. + * @returns {GridRowIdTree} The full set of rows. */ - getRowModels: () => Map; + getRowModels: () => GridRowIdTree; /** * Gets the total number of rows in the grid. * @returns {number} The number of rows. diff --git a/packages/grid/_modules_/grid/models/params/gridSlotComponentProps.ts b/packages/grid/_modules_/grid/models/params/gridSlotComponentProps.ts index 183d30d37d5b3..6a1909bb57d36 100644 --- a/packages/grid/_modules_/grid/models/params/gridSlotComponentProps.ts +++ b/packages/grid/_modules_/grid/models/params/gridSlotComponentProps.ts @@ -2,7 +2,7 @@ import { GridState } from '../../hooks/features/core/gridState'; import { GridApiRef } from '../api/gridApiRef'; import { GridColumns } from '../colDef/gridColDef'; import { GridRootContainerRef } from '../gridRootContainerRef'; -import { GridRowModel } from '../gridRows'; +import { GridRowIdTree } from '../gridRows'; /** * Object passed as React prop in the component override. @@ -15,7 +15,7 @@ export interface GridSlotComponentProps { /** * The full set of rows. */ - rows: GridRowModel[]; + rows: GridRowIdTree; /** * The full set of columns. */ From 051dc79e677026b46242ac2a45183fa41592bfee Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 15 Sep 2021 13:40:31 +0200 Subject: [PATCH 080/390] Regen doc and proptypes --- docs/pages/api-docs/data-grid/grid-api.md | 164 +++++++++++----------- packages/grid/data-grid/src/DataGrid.tsx | 11 ++ packages/grid/x-grid/src/DataGridPro.tsx | 11 ++ 3 files changed, 105 insertions(+), 81 deletions(-) diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index 0d3c952406571..af456a9a38b37 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -10,84 +10,86 @@ import { GridApi } from '@mui/x-data-grid-pro'; ## Properties -| Name | Type | Description | -| :----------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| applyFilter | (item: GridFilterItem, linkOperator?: GridLinkOperator) => void | Applies a GridFilterItem on all rows. If no `linkOperator` is given, the "and" operator is used. | -| applyFilterLinkOperator | (operator: GridLinkOperator) => void | Changes the GridLinkOperator used to connect the filters. | -| applyFilters | () => void | Applies all filters on all rows. | -| applySorting | () => void | Applies the current sort model to the rows. | -| commitCellChange | (params: GridCommitCellChangeParams, event?: any) => boolean | Updates the field at the given id with the value stored in the edit row model. | -| commitRowChange | (id: GridRowId, event?: any) => boolean | Updates the row at the given id with the values stored in the edit row model. | -| deleteFilter | (item: GridFilterItem) => void | Deletes a GridFilterItem. | -| exportDataAsCsv | (options?: GridExportCsvOptions) => void | Downloads and exports a CSV of the grid's data. | -| forceUpdate | Dispatch<any> | Forces the grid to rerender. It's often used after a state update. | -| getAllColumns | () => GridStateColDef[] | Returns an array of [GridColDef](/api/data-grid/grid-col-def/) containing all the column definitions. | -| getAllRowIds | () => GridRowId[] | Gets the list of row ids. | -| getCellElement | (id: GridRowId, field: string) => null \| HTMLDivElement | Gets the underlying DOM element for a cell at the given `id` and `field`. | -| getCellMode | (id: GridRowId, field: string) => GridCellMode | Gets the mode of a cell. | -| getCellParams | (id: GridRowId, field: string) => GridCellParams | Gets the [GridCellParams](/api/data-grid/grid-cell-params/) object that is passed as argument in events. | -| getCellValue | (id: GridRowId, field: string) => GridCellValue | Gets the value of a cell at the given `id` and `field`. | -| getColumn | (field: string) => GridStateColDef | Returns the [GridColDef](/api/data-grid/grid-col-def/) for the given `field`. | -| getColumnHeaderElement | (field: string) => null \| HTMLDivElement | Gets the underlying DOM element for the column header with the given `field`. | -| getColumnHeaderParams | (field: string) => GridColumnHeaderParams | Gets the GridColumnHeaderParams object that is passed as argument in events. | -| getColumnIndex | (field: string, useVisibleColumns?: boolean) => number | Returns the index position of a column. By default, only the visible columns are considered.
Pass `false` to `useVisibleColumns` to consider all columns. | -| getColumnPosition | (field: string) => number | Returns the left-position of a column relative to the inner border of the grid. | -| getColumnsMeta | () => GridColumnsMeta | Returns the GridColumnsMeta for each column. | -| getDataAsCsv | (options?: GridExportCsvOptions) => string | Returns the grid data as a CSV string.
This method is used internally by `exportDataAsCsv`. | -| getEditRowsModel | () => GridEditRowsModel | Gets the edit rows model of the grid. | -| getLocaleText | <T extends keyof GridLocaleText>(key: T) => GridLocaleText[T] | Returns the translation for the `key`. | -| getRow | (id: GridRowId) => null \| GridRowData | Gets the row data with a given id. | -| getRowElement | (id: GridRowId) => null \| HTMLDivElement | Gets the underlying DOM element for a row at the given `id`. | -| getRowIdFromRowIndex | (index: number) => GridRowId | Gets the `GridRowId` of a row at a specific index. | -| getRowIndex | (id: GridRowId) => number | Gets the row index of a row with a given id. | -| getRowMode | (id: GridRowId) => GridRowMode | Gets the mode of a row. | -| getRowModels | () => Map<GridRowId, GridRowData> | Gets the full set of rows as Map<GridRowId, GridRowModel>. | -| getRowParams | (id: GridRowId) => GridRowParams | Gets the [GridRowParams](/api/data-grid/grid-row-params/) object that is passed as argument in events. | -| getRowsCount | () => number | Gets the total number of rows in the grid. | -| getScrollPosition | () => GridScrollParams | Returns the current scroll position. | -| getSelectedRows | () => Map<GridRowId, GridRowData> | Returns an array of the selected rows. | -| getSortedRowIds | () => GridRowId[] | Returns all row ids sorted according to the active sort model. | -| getSortedRows | () => GridRowData[] | Returns all rows sorted according to the active sort model. | -| getSortModel | () => GridSortModel | Returns the sort model currently applied to the grid. | -| getVisibleColumns | () => GridStateColDef[] | Returns the currently visible columns. | -| getVisibleRowModels | () => Map<GridRowId, GridRowData> | Returns a sorted `Map` containing only the visible rows. | -| hideColumnMenu | () => void | Hides the column menu that is open. | -| hideFilterPanel | () => void | Hides the filter panel. | -| hidePreferences | () => void | Hides the preferences panel. | -| isCellEditable | (params: GridCellParams) => boolean | Controls if a cell is editable. | -| isRowSelected | (id: GridRowId) => boolean | Determines if a row is selected or not. | -| publishEvent | (name: string, params?: any, event?: MuiEvent<MouseEvent \| UIEvent \| Event \| SyntheticEvent<Element, Event> \| TouchEvent \| ErrorEvent \| ProgressEvent<EventTarget> \| ClipboardEvent \| AnimationEvent \| InputEvent \| FocusEvent \| CompositionEvent \| DragEvent \| FormDataEvent \| PointerEvent \| KeyboardEvent \| SecurityPolicyViolationEvent \| TransitionEvent \| WheelEvent>) => void | Emits an event. | -| resize | () => void | Triggers a resize of the component and recalculation of width and height. | -| scroll | (params: Partial<GridScrollParams>) => void | Triggers the viewport to scroll to the given positions (in pixels). | -| scrollToIndexes | (params: Partial<GridCellIndexCoordinates>) => boolean | Triggers the viewport to scroll to the cell at indexes given by `params`.
Returns `true` if the grid had to scroll to reach the target. | -| selectRow | (id: GridRowId, isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of a row. | -| selectRows | (ids: GridRowId[], isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of multiple rows. | -| setCellFocus | (id: GridRowId, field: string) => void | Sets the focus to the cell at the given `id` and `field`. | -| setCellMode | (id: GridRowId, field: string, mode: GridCellMode) => void | Sets the mode of a cell. | -| setColumnHeaderFocus | (field: string, event?: SyntheticEvent<Element, Event>) => void | Sets the focus to the column header at the given `field`. | -| setColumnIndex | (field: string, targetIndexPosition: number) => void | Moves a column from its original position to the position given by `targetIndexPosition`. | -| setColumnVisibility | (field: string, isVisible: boolean) => void | Changes the visibility of the column referred by `field`. | -| setColumnWidth | (field: string, width: number) => void | Updates the width of a column. | -| setDensity | (density: GridDensity, headerHeight?: number, rowHeight?: number) => void | Sets the density of the grid. | -| setEditCellValue | (params: GridEditCellValueParams, event?: SyntheticEvent<Element, Event>) => void | Sets the value of the edit cell.
Commonly used inside the edit cell component. | -| setEditRowsModel | (model: GridEditRowsModel) => void | Set the edit rows model of the grid. | -| setFilterModel | (model: GridFilterModel) => void | Sets the filter model to the one given by `model`. | -| setPage | (page: number) => void | Sets the displayed page to the value given by `page`. | -| setPageSize | (pageSize: number) => void | Sets the number of displayed rows to the value given by `pageSize`. | -| setRowMode | (id: GridRowId, mode: GridRowMode) => void | Sets the mode of a row. | -| setRows | (rows: GridRowData[]) => void | Sets a new set of rows. | -| setSelectionModel | (rowIds: GridRowId[]) => void | Updates the selected rows to be those passed to the `rowIds` argument.
Any row already selected will be unselected. | -| setSortModel | (model: GridSortModel) => void | Updates the sort model and triggers the sorting of rows. | -| setState | (state: GridState \| ((previousState: GridState) => GridState)) => void | Sets the whole state of the grid. | -| showColumnMenu | (field: string) => void | Display the column menu under the `field` column. | -| showError | (props: any) => void | Displays the error overlay component. | -| showFilterPanel | (targetColumnField?: string) => void | Shows the filter panel. If `targetColumnField` is given, a filter for this field is also added. | -| showPreferences | (newValue: GridPreferencePanelsValue) => void | Displays the preferences panel. The `newValue` argument controls the content of the panel. | -| sortColumn | (column: GridColDef, direction?: GridSortDirection, allowMultipleSorting?: boolean) => void | Sorts a column. | -| state | GridState | Property that contains the whole state of the grid. | -| subscribeEvent | <Params, Event extends MuiEvent<BaseEvent>>(event: string, handler: GridListener<Params, Event>, options?: GridSubscribeEventOptions) => () => void | Registers a handler for an event. | -| toggleColumnMenu | (field: string) => void | Toggles the column menu under the `field` column. | -| updateColumn | (col: GridColDef) => void | Updates the definition of a column. | -| updateColumns | (cols: GridColDef[]) => void | Updates the definition of multiple columns at the same time. | -| updateRows | (updates: GridRowModelUpdate<>[]) => void | Allows to updates, insert and delete rows in a single call. | -| upsertFilter | (item: GridFilterItem) => void | Updates or inserts a GridFilterItem. | +| Name | Type | Description | +| :------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| applyFilter | (item: GridFilterItem, linkOperator?: GridLinkOperator) => void | Applies a GridFilterItem on all rows. If no `linkOperator` is given, the "and" operator is used. | +| applyFilterLinkOperator | (operator: GridLinkOperator) => void | Changes the GridLinkOperator used to connect the filters. | +| applyFilters | () => void | Applies all filters on all rows. | +| applySorting | () => void | Applies the current sort model to the rows. | +| commitCellChange | (params: GridCommitCellChangeParams, event?: any) => boolean | Updates the field at the given id with the value stored in the edit row model. | +| commitRowChange | (id: GridRowId, event?: any) => boolean | Updates the row at the given id with the values stored in the edit row model. | +| deleteFilter | (item: GridFilterItem) => void | Deletes a GridFilterItem. | +| exportDataAsCsv | (options?: GridExportCsvOptions) => void | Downloads and exports a CSV of the grid's data. | +| forceUpdate | Dispatch<any> | Forces the grid to rerender. It's often used after a state update. | +| getAllColumns | () => GridStateColDef[] | Returns an array of [GridColDef](/api/data-grid/grid-col-def/) containing all the column definitions. | +| getAllRowIds | () => GridRowId[] | Gets the list of row ids. | +| getCellElement | (id: GridRowId, field: string) => null \| HTMLDivElement | Gets the underlying DOM element for a cell at the given `id` and `field`. | +| getCellMode | (id: GridRowId, field: string) => GridCellMode | Gets the mode of a cell. | +| getCellParams | (id: GridRowId, field: string) => GridCellParams | Gets the [GridCellParams](/api/data-grid/grid-cell-params/) object that is passed as argument in events. | +| getCellValue | (id: GridRowId, field: string) => GridCellValue | Gets the value of a cell at the given `id` and `field`. | +| getColumn | (field: string) => GridStateColDef | Returns the [GridColDef](/api/data-grid/grid-col-def/) for the given `field`. | +| getColumnHeaderElement | (field: string) => null \| HTMLDivElement | Gets the underlying DOM element for the column header with the given `field`. | +| getColumnHeaderParams | (field: string) => GridColumnHeaderParams | Gets the GridColumnHeaderParams object that is passed as argument in events. | +| getColumnIndex | (field: string, useVisibleColumns?: boolean) => number | Returns the index position of a column. By default, only the visible columns are considered.
Pass `false` to `useVisibleColumns` to consider all columns. | +| getColumnPosition | (field: string) => number | Returns the left-position of a column relative to the inner border of the grid. | +| getColumnsMeta | () => GridColumnsMeta | Returns the GridColumnsMeta for each column. | +| getDataAsCsv | (options?: GridExportCsvOptions) => string | Returns the grid data as a CSV string.
This method is used internally by `exportDataAsCsv`. | +| getEditRowsModel | () => GridEditRowsModel | Gets the edit rows model of the grid. | +| getFlatSortedRowIds | () => GridRowId[] | Returns all row ids sorted according to the active sort model. | +| getLocaleText | <T extends keyof GridLocaleText>(key: T) => GridLocaleText[T] | Returns the translation for the `key`. | +| getRow | (id: GridRowId) => null \| GridRowData | Gets the row data with a given id. | +| getRowElement | (id: GridRowId) => null \| HTMLDivElement | Gets the underlying DOM element for a row at the given `id`. | +| getRowIdFromRowIndex | (index: number) => GridRowId | Gets the `GridRowId` of a row at a specific index. | +| getRowIndex | (id: GridRowId) => number | Gets the row index of a row with a given id. | +| getRowMode | (id: GridRowId) => GridRowMode | Gets the mode of a row. | +| getRowModels | () => GridRowIdTree | Gets the full set of rows ordered in a tree structure. | +| getRowParams | (id: GridRowId) => GridRowParams | Gets the [GridRowParams](/api/data-grid/grid-row-params/) object that is passed as argument in events. | +| getRowsCount | () => number | Gets the total number of rows in the grid. | +| getScrollPosition | () => GridScrollParams | Returns the current scroll position. | +| getSelectedRows | () => Map<GridRowId, GridRowData> | Returns an array of the selected rows. | +| getSortedRowIds | () => GridSortedRowsIdTreeNode[] | Returns all row ids sorted according to the active sort model. | +| getSortedRows | () => Map<GridRowId, GridSortedRowsTreeNode> | Returns all rows sorted according to the active sort model. | +| getSortModel | () => GridSortModel | Returns the sort model currently applied to the grid. | +| getVisibleColumns | () => GridStateColDef[] | Returns the currently visible columns. | +| getVisibleRowModels | () => Map<GridRowId, GridRowData> | Returns a sorted `Map` containing only the visible rows. | +| groupRows? | (rowsLookup: GridRowsLookup) => GridRowIdTree | Create the tree structure for a given set of rows | +| hideColumnMenu | () => void | Hides the column menu that is open. | +| hideFilterPanel | () => void | Hides the filter panel. | +| hidePreferences | () => void | Hides the preferences panel. | +| isCellEditable | (params: GridCellParams) => boolean | Controls if a cell is editable. | +| isRowSelected | (id: GridRowId) => boolean | Determines if a row is selected or not. | +| publishEvent | (name: string, params?: any, event?: MuiEvent<MouseEvent \| UIEvent \| Event \| SyntheticEvent<Element, Event> \| TouchEvent \| ErrorEvent \| ProgressEvent<EventTarget> \| ClipboardEvent \| AnimationEvent \| InputEvent \| FocusEvent \| CompositionEvent \| DragEvent \| FormDataEvent \| PointerEvent \| KeyboardEvent \| SecurityPolicyViolationEvent \| TransitionEvent \| WheelEvent>) => void | Emits an event. | +| resize | () => void | Triggers a resize of the component and recalculation of width and height. | +| scroll | (params: Partial<GridScrollParams>) => void | Triggers the viewport to scroll to the given positions (in pixels). | +| scrollToIndexes | (params: Partial<GridCellIndexCoordinates>) => boolean | Triggers the viewport to scroll to the cell at indexes given by `params`.
Returns `true` if the grid had to scroll to reach the target. | +| selectRow | (id: GridRowId, isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of a row. | +| selectRows | (ids: GridRowId[], isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of multiple rows. | +| setCellFocus | (id: GridRowId, field: string) => void | Sets the focus to the cell at the given `id` and `field`. | +| setCellMode | (id: GridRowId, field: string, mode: GridCellMode) => void | Sets the mode of a cell. | +| setColumnHeaderFocus | (field: string, event?: SyntheticEvent<Element, Event>) => void | Sets the focus to the column header at the given `field`. | +| setColumnIndex | (field: string, targetIndexPosition: number) => void | Moves a column from its original position to the position given by `targetIndexPosition`. | +| setColumnVisibility | (field: string, isVisible: boolean) => void | Changes the visibility of the column referred by `field`. | +| setColumnWidth | (field: string, width: number) => void | Updates the width of a column. | +| setDensity | (density: GridDensity, headerHeight?: number, rowHeight?: number) => void | Sets the density of the grid. | +| setEditCellValue | (params: GridEditCellValueParams, event?: SyntheticEvent<Element, Event>) => void | Sets the value of the edit cell.
Commonly used inside the edit cell component. | +| setEditRowsModel | (model: GridEditRowsModel) => void | Set the edit rows model of the grid. | +| setFilterModel | (model: GridFilterModel) => void | Sets the filter model to the one given by `model`. | +| setPage | (page: number) => void | Sets the displayed page to the value given by `page`. | +| setPageSize | (pageSize: number) => void | Sets the number of displayed rows to the value given by `pageSize`. | +| setRowMode | (id: GridRowId, mode: GridRowMode) => void | Sets the mode of a row. | +| setRows | (rows: GridRowData[]) => void | Sets a new set of rows. | +| setSelectionModel | (rowIds: GridRowId[]) => void | Updates the selected rows to be those passed to the `rowIds` argument.
Any row already selected will be unselected. | +| setSortModel | (model: GridSortModel) => void | Updates the sort model and triggers the sorting of rows. | +| setState | (state: GridState \| ((previousState: GridState) => GridState)) => void | Sets the whole state of the grid. | +| showColumnMenu | (field: string) => void | Display the column menu under the `field` column. | +| showError | (props: any) => void | Displays the error overlay component. | +| showFilterPanel | (targetColumnField?: string) => void | Shows the filter panel. If `targetColumnField` is given, a filter for this field is also added. | +| showPreferences | (newValue: GridPreferencePanelsValue) => void | Displays the preferences panel. The `newValue` argument controls the content of the panel. | +| sortColumn | (column: GridColDef, direction?: GridSortDirection, allowMultipleSorting?: boolean) => void | Sorts a column. | +| state | GridState | Property that contains the whole state of the grid. | +| subscribeEvent | <Params, Event extends MuiEvent<BaseEvent>>(event: string, handler: GridListener<Params, Event>, options?: GridSubscribeEventOptions) => () => void | Registers a handler for an event. | +| toggleColumnMenu | (field: string) => void | Toggles the column menu under the `field` column. | +| updateColumn | (col: GridColDef) => void | Updates the definition of a column. | +| updateColumns | (cols: GridColDef[]) => void | Updates the definition of multiple columns at the same time. | +| updateRows | (updates: GridRowModelUpdate<>[]) => void | Allows to updates, insert and delete rows in a single call. | +| upsertFilter | (item: GridFilterItem) => void | Updates or inserts a GridFilterItem. | diff --git a/packages/grid/data-grid/src/DataGrid.tsx b/packages/grid/data-grid/src/DataGrid.tsx index 95ada66b3d1dd..2de9e4920348b 100644 --- a/packages/grid/data-grid/src/DataGrid.tsx +++ b/packages/grid/data-grid/src/DataGrid.tsx @@ -197,6 +197,12 @@ DataGridRaw.propTypes = { * Return the id of a given [[GridRowData]]. */ getRowId: PropTypes.func, + /** + * Determines the path of a row in the tree data + * @param {GridRowParams} params With all properties from [[GridRowParams]]. + * @returns {string[]} the path to the row + */ + getTreeDataPath: PropTypes.func, /** * Set the height in pixel of the column headers in the grid. * @default 56 @@ -669,4 +675,9 @@ DataGridRaw.propTypes = { * @ignore */ style: PropTypes.object, + /** + * If `true`, the rows will be gathered in a tree structure, following the `getDataPath` prop + * @default false + */ + treeData: PropTypes.bool, } as any; diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index cf0f156d2f6b4..8b394c3507360 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -243,6 +243,12 @@ DataGridProRaw.propTypes = { * Return the id of a given [[GridRowData]]. */ getRowId: PropTypes.func, + /** + * Determines the path of a row in the tree data + * @param {GridRowParams} params With all properties from [[GridRowParams]]. + * @returns {string[]} the path to the row + */ + getTreeDataPath: PropTypes.func, /** * Set the height in pixel of the column headers in the grid. * @default 56 @@ -711,4 +717,9 @@ DataGridProRaw.propTypes = { * @default 0 */ throttleRowsMs: PropTypes.number, + /** + * If `true`, the rows will be gathered in a tree structure, following the `getDataPath` prop + * @default false + */ + treeData: PropTypes.bool, } as any; From 6d79cfa4f6591b19ad84ae3f94c705eb87159b64 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 15 Sep 2021 14:09:00 +0200 Subject: [PATCH 081/390] Work on columns preprocessing --- .../grid/constants/eventsConstants.ts | 4 +++ .../hooks/features/columns/useGridColumns.ts | 4 ++- .../grid/hooks/features/core/gridState.ts | 1 + .../treeData/gridTreeDataCollapseColDef.ts | 16 +++++++++ .../features/treeData/useGridTreeData.ts | 19 +++++++++- .../gridColumnsPreProcessingApi.ts | 9 +++++ .../hooks/root/columnsPreProcessing/index.ts | 2 ++ .../useGridColumnsPreProcessing.ts | 36 +++++++++++++++++++ .../grid/_modules_/grid/models/api/gridApi.ts | 2 ++ .../x-grid/src/useDataGridProComponent.tsx | 4 ++- 10 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataCollapseColDef.ts create mode 100644 packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/gridColumnsPreProcessingApi.ts create mode 100644 packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/index.ts create mode 100644 packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/useGridColumnsPreProcessing.ts diff --git a/packages/grid/_modules_/grid/constants/eventsConstants.ts b/packages/grid/_modules_/grid/constants/eventsConstants.ts index 2eaaa928d9f0d..62f5cbf284f12 100644 --- a/packages/grid/_modules_/grid/constants/eventsConstants.ts +++ b/packages/grid/_modules_/grid/constants/eventsConstants.ts @@ -291,6 +291,10 @@ export enum GridEvents { * Called with an array of strings corresponding to the field names. */ columnsChange = 'columnsChange', + /** + * Fired when a column pre-processing is changed + */ + columnsPreProcessingChange = 'columnsPreProcessingChange', /** * Fired when the sort model changes. * Called with a [[GridSortModelParams]] object. diff --git a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts index 13fc8e5c32cfe..627e6d39a21f9 100644 --- a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts +++ b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts @@ -331,7 +331,9 @@ export function useGridColumns( classes, ); - const columnState = upsertColumnsState(hydratedColumns); + const preProcessedColumns = apiRef.current.applyAllColumnPreProcessing(hydratedColumns) + + const columnState = upsertColumnsState(preProcessedColumns); setColumnsState(columnState); }, [ logger, diff --git a/packages/grid/_modules_/grid/hooks/features/core/gridState.ts b/packages/grid/_modules_/grid/hooks/features/core/gridState.ts index 66ed4a5daed58..6f6837ee8a9ae 100644 --- a/packages/grid/_modules_/grid/hooks/features/core/gridState.ts +++ b/packages/grid/_modules_/grid/hooks/features/core/gridState.ts @@ -31,6 +31,7 @@ import { InternalRenderingState, } from '../virtualization/renderingState'; import { getInitialPaginationState, GridPaginationState } from '../pagination/gridPaginationState'; +import {GridColumnsPreProcessing} from "../../../models/gridColumnsPreProcessing"; export interface GridState { rows: GridRowsState; diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataCollapseColDef.ts b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataCollapseColDef.ts new file mode 100644 index 0000000000000..a93a3315b0eaa --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataCollapseColDef.ts @@ -0,0 +1,16 @@ +import {GRID_BOOLEAN_COL_DEF} from "../../../models/colDef/gridBooleanColDef"; +import {GridColDef} from "../../../models/colDef/gridColDef"; + +export const GridTreeDataCollapseColDef: GridColDef = { + ...GRID_BOOLEAN_COL_DEF, + field: '__check__', + width: 50, + resizable: false, + sortable: false, + filterable: false, + disableColumnMenu: true, + disableReorder: true, + valueGetter: () => true, + renderHeader: () => 'Header', + renderCell: () => 'Content', +} \ No newline at end of file diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 511904963e889..212400c63f0f8 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -10,6 +10,8 @@ import { import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridComponentProps } from '../../../GridComponentProps'; import { useGridApiMethod } from '../../root/useGridApiMethod'; +import {GridColumnsPreProcessing} from "../../root/columnsPreProcessing"; +import {GridTreeDataCollapseColDef} from "./gridTreeDataCollapseColDef"; const insertRowInTree = (tree: GridRowIdTree, id: GridRowId, path: string[]) => { if (path.length === 0) { @@ -43,7 +45,7 @@ function getGridRowId(rowData: GridRowData, getRowId?: GridRowIdGetter): GridRow */ export const useGridTreeData = ( apiRef: GridApiRef, - props: Pick, + props: Pick, ) => { const groupRows = React.useCallback( (rowsLookup) => { @@ -71,6 +73,21 @@ export const useGridTreeData = ( [props.getTreeDataPath], ); + React.useEffect(() => { + if (!props.treeData) { + return + } + + const addCollapseColumn: GridColumnsPreProcessing = (columns ) => [ + { + ...GridTreeDataCollapseColDef, + }, + ...columns, + ] + + return apiRef.current.registerColumnPreProcessing(addCollapseColumn) + }, []) + const treeDataApi: GridTreeDataApi = { groupRows, }; diff --git a/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/gridColumnsPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/gridColumnsPreProcessingApi.ts new file mode 100644 index 0000000000000..1bdefa083eb89 --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/gridColumnsPreProcessingApi.ts @@ -0,0 +1,9 @@ +import { GridColumns} from "../../../models/colDef/gridColDef"; + +export type GridColumnsPreProcessing = (columns: GridColumns) => GridColumns; +export type GridColumnsPreProcessingCleanup = () => void + +export interface GridColumnsPreProcessingApi { + registerColumnPreProcessing: (columnsPreProcessing: GridColumnsPreProcessing) => GridColumnsPreProcessingCleanup, + applyAllColumnPreProcessing: GridColumnsPreProcessing +} \ No newline at end of file diff --git a/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/index.ts b/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/index.ts new file mode 100644 index 0000000000000..be6ec10bc3f7b --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/index.ts @@ -0,0 +1,2 @@ +export * from './useGridColumnsPreProcessing' +export * from './gridColumnsPreProcessingApi' \ No newline at end of file diff --git a/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/useGridColumnsPreProcessing.ts b/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/useGridColumnsPreProcessing.ts new file mode 100644 index 0000000000000..733d34b945462 --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/useGridColumnsPreProcessing.ts @@ -0,0 +1,36 @@ +import * as React from 'react' +import {GridApiRef} from "../../../models/api/gridApiRef"; +import {GridColumnsPreProcessing, GridColumnsPreProcessingApi} from './gridColumnsPreProcessingApi' +import {useGridApiMethod} from "../useGridApiMethod"; +import {GridEvents} from "../../../constants/eventsConstants"; + +export const useGridColumnsPreProcessing = (apiRef: GridApiRef) => { + const columnsPreProcessingRef = React.useRef(new Map()) + + const registerColumnPreProcessing = React.useCallback((columnsPreProcessing: GridColumnsPreProcessing) => { + const id = Math.random() + + columnsPreProcessingRef.current[id] = columnsPreProcessing + + apiRef.current.publishEvent(GridEvents.columnsPreProcessingChange) + + return () => columnsPreProcessingRef.current.delete(id) + }, []) + + const applyAllColumnPreProcessing = React.useCallback((columns) => { + let preProcessedColumns = columns + + columnsPreProcessingRef.current.forEach(columnsPreProcessing => { + preProcessedColumns = columnsPreProcessing(preProcessedColumns) + }) + + return preProcessedColumns + }, []) + + const columnsPreProcessingApi: GridColumnsPreProcessingApi = { + registerColumnPreProcessing, + applyAllColumnPreProcessing, + } + + useGridApiMethod(apiRef, columnsPreProcessingApi, 'GridColumnsPreProcessing'); +} \ No newline at end of file diff --git a/packages/grid/_modules_/grid/models/api/gridApi.ts b/packages/grid/_modules_/grid/models/api/gridApi.ts index 3a46ce2c868a8..f4999fb7ed266 100644 --- a/packages/grid/_modules_/grid/models/api/gridApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridApi.ts @@ -22,6 +22,7 @@ import { GridVirtualizationApi } from './gridVirtualizationApi'; import { GridLoggerApi } from './gridLoggerApi'; import { GridScrollApi } from './gridScrollApi'; import type { GridTreeDataApi } from '../../hooks/features/treeData'; +import type { GridColumnsPreProcessingApi } from "../../hooks/root/columnsPreProcessing"; /** * The full grid API. @@ -30,6 +31,7 @@ export interface GridApi extends GridCoreApi, GridStateApi, GridLoggerApi, + GridColumnsPreProcessingApi, GridDensityApi, GridEventsApi, GridRowApi, diff --git a/packages/grid/x-grid/src/useDataGridProComponent.tsx b/packages/grid/x-grid/src/useDataGridProComponent.tsx index 8e982ec29268b..f3603a5e4be15 100644 --- a/packages/grid/x-grid/src/useDataGridProComponent.tsx +++ b/packages/grid/x-grid/src/useDataGridProComponent.tsx @@ -35,17 +35,19 @@ import { useGridResizeContainer } from '../../_modules_/grid/hooks/utils/useGrid import { useStateProp } from '../../_modules_/grid/hooks/utils/useStateProp'; import { GridApiRef } from '../../_modules_/grid/models/api/gridApiRef'; import { useGridTreeData } from '../../_modules_/grid/hooks/features/treeData'; +import {useGridColumnsPreProcessing} from "../../_modules_/grid/hooks/root/columnsPreProcessing"; export const useDataGridProComponent = (apiRef: GridApiRef, props: GridComponentProps) => { useGridLoggerFactory(apiRef, props); useApi(apiRef, props); useErrorHandler(apiRef, props); useGridControlState(apiRef, props); + useGridColumnsPreProcessing(apiRef); useLocaleText(apiRef, props); useGridResizeContainer(apiRef, props); useGridFreezeRows(apiRef, props); - useGridColumns(apiRef, props); useGridTreeData(apiRef, props); + useGridColumns(apiRef, props); useGridRows(apiRef, props); useGridParamsApi(apiRef); useGridEditRows(apiRef, props); From d0656921c5cf5a2f6642fbc6c2a592ed287c0798 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 15 Sep 2021 14:56:26 +0200 Subject: [PATCH 082/390] Work --- .../hooks/features/columns/useGridColumns.ts | 3 +- .../grid/hooks/features/core/gridState.ts | 6 +- .../grid/hooks/features/rows/useGridRows.ts | 2 +- .../features/treeData/GridTreeDataApi.ts | 18 ++++- .../treeData/gridTreeDataCollapseColDef.ts | 16 ---- .../features/treeData/gridTreeDataState.ts | 5 ++ .../gridTreeDataToggleExpansionColDef.tsx | 37 ++++++++++ .../features/treeData/treeDataSelector.ts | 10 +++ .../features/treeData/useGridTreeData.ts | 73 ++++++++++++++----- .../gridColumnsPreProcessingApi.ts | 13 ++-- .../hooks/root/columnsPreProcessing/index.ts | 4 +- .../useGridColumnsPreProcessing.ts | 60 ++++++++------- .../grid/hooks/utils/useProcessedProps.ts | 2 + .../grid/_modules_/grid/models/api/gridApi.ts | 4 +- .../grid/models/gridIconSlotsComponent.ts | 8 ++ .../x-grid/src/useDataGridProComponent.tsx | 2 +- 16 files changed, 186 insertions(+), 77 deletions(-) delete mode 100644 packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataCollapseColDef.ts create mode 100644 packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataState.ts create mode 100644 packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataToggleExpansionColDef.tsx create mode 100644 packages/grid/_modules_/grid/hooks/features/treeData/treeDataSelector.ts diff --git a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts index 627e6d39a21f9..2a33cd6baafc7 100644 --- a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts +++ b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts @@ -331,8 +331,7 @@ export function useGridColumns( classes, ); - const preProcessedColumns = apiRef.current.applyAllColumnPreProcessing(hydratedColumns) - + const preProcessedColumns = apiRef.current.applyAllColumnPreProcessing(hydratedColumns); const columnState = upsertColumnsState(preProcessedColumns); setColumnsState(columnState); }, [ diff --git a/packages/grid/_modules_/grid/hooks/features/core/gridState.ts b/packages/grid/_modules_/grid/hooks/features/core/gridState.ts index 6f6837ee8a9ae..5f0dfee48b91b 100644 --- a/packages/grid/_modules_/grid/hooks/features/core/gridState.ts +++ b/packages/grid/_modules_/grid/hooks/features/core/gridState.ts @@ -31,7 +31,7 @@ import { InternalRenderingState, } from '../virtualization/renderingState'; import { getInitialPaginationState, GridPaginationState } from '../pagination/gridPaginationState'; -import {GridColumnsPreProcessing} from "../../../models/gridColumnsPreProcessing"; +import { GridTreeDataState } from '../treeData/gridTreeDataState'; export interface GridState { rows: GridRowsState; @@ -53,6 +53,7 @@ export interface GridState { visibleRows: VisibleGridRowsState; preferencePanel: GridPreferencePanelState; density: GridGridDensity; + treeData: GridTreeDataState; error?: any; } @@ -76,4 +77,7 @@ export const getInitialGridState = (): GridState => ({ preferencePanel: { open: false }, visibleRows: getInitialVisibleGridRowsState(), density: getInitialGridDensityState(), + treeData: { + expandedRows: {}, + }, }); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 39c2c9270056e..55b234d076edf 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -126,7 +126,7 @@ export const useGridRows = ( rowsCache.current.lastUpdateMs = Date.now(); const { rowIds, ...rowState } = rowsCache.current.state; const tree = apiRef.current.groupRows - ? apiRef.current.groupRows(rowState.idRowsLookup) + ? apiRef.current.groupRows(rowState.idRowsLookup, rowIds) : getFlatRowTree(rowIds); setGridState((state) => ({ ...state, rows: { ...rowState, tree } })); apiRef.current.publishEvent(GridEvents.rowsSet); diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/GridTreeDataApi.ts b/packages/grid/_modules_/grid/hooks/features/treeData/GridTreeDataApi.ts index 97436ded89482..a56c1e6f9ffed 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/GridTreeDataApi.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/GridTreeDataApi.ts @@ -1,11 +1,25 @@ -import { GridRowIdTree } from '../../../models/gridRows'; +import { GridRowId, GridRowIdTree } from '../../../models/gridRows'; import { GridRowsLookup } from '../rows'; export interface GridTreeDataApi { /** * Create the tree structure for a given set of rows * @param {GridRowsLookup} rowsLookup the rows to process + * @param {GridRowId[]} rowIds the id of each row * @returns {GridRowIdTree} tree the tree structure containing all the rows */ - groupRows: (rowsLookup: GridRowsLookup) => GridRowIdTree; + groupRows: (rowsLookup: GridRowsLookup, rowIds: GridRowId[]) => GridRowIdTree; + + /** + * Toggle the expansion of a tree data row + * @param {GridRowId} id the ID of the row to toggle + */ + toggleTreeDataRow: (id: GridRowId) => void; + + /** + * Determines if a row is expanded in the tree data or not. + * @param {GridRowId} id The id of the row. + * @returns {boolean} A boolean indicating if the row is expanded. + */ + isTreeDataRowExpanded: (id: GridRowId) => boolean; } diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataCollapseColDef.ts b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataCollapseColDef.ts deleted file mode 100644 index a93a3315b0eaa..0000000000000 --- a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataCollapseColDef.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {GRID_BOOLEAN_COL_DEF} from "../../../models/colDef/gridBooleanColDef"; -import {GridColDef} from "../../../models/colDef/gridColDef"; - -export const GridTreeDataCollapseColDef: GridColDef = { - ...GRID_BOOLEAN_COL_DEF, - field: '__check__', - width: 50, - resizable: false, - sortable: false, - filterable: false, - disableColumnMenu: true, - disableReorder: true, - valueGetter: () => true, - renderHeader: () => 'Header', - renderCell: () => 'Content', -} \ No newline at end of file diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataState.ts b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataState.ts new file mode 100644 index 0000000000000..f7191680d1f63 --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataState.ts @@ -0,0 +1,5 @@ +import { GridRowId } from '../../../models/gridRows'; + +export interface GridTreeDataState { + expandedRows: Record; +} diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataToggleExpansionColDef.tsx b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataToggleExpansionColDef.tsx new file mode 100644 index 0000000000000..b18b461341167 --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataToggleExpansionColDef.tsx @@ -0,0 +1,37 @@ +import * as React from 'react'; +import IconButton from '@mui/material/IconButton'; + +import { GRID_BOOLEAN_COL_DEF } from '../../../models/colDef/gridBooleanColDef'; +import { GridColDef } from '../../../models/colDef/gridColDef'; +import { useGridRootProps } from '../../utils/useGridRootProps'; +import { useGridApiContext } from '../../root/useGridApiContext'; +import { GridRowId } from '../../../models/gridRows'; + +const TreeDataToggleIcon = ({ id }: { id: GridRowId }) => { + const rootProps = useGridRootProps(); + const apiRef = useGridApiContext(); + + const Icon = apiRef.current.isTreeDataRowExpanded!(id) + ? rootProps.components.TreeDataCollapseIcon + : rootProps.components.TreeDataExpandIcon; + + return ( + apiRef.current.toggleTreeDataRow!(id)}> + + + ); +}; + +export const GridTreeDataToggleExpansionColDef: GridColDef = { + ...GRID_BOOLEAN_COL_DEF, + field: '__check__', + sortable: false, + filterable: false, + disableColumnMenu: true, + disableReorder: true, + valueGetter: () => true, + renderHeader: () => 'Header', + renderCell: (params) => { + return ; + }, +}; diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/treeDataSelector.ts b/packages/grid/_modules_/grid/hooks/features/treeData/treeDataSelector.ts new file mode 100644 index 0000000000000..b33d9717f3018 --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/features/treeData/treeDataSelector.ts @@ -0,0 +1,10 @@ +import { createSelector } from 'reselect'; + +import { GridState } from '../core/gridState'; + +const gridTreeDataStateSelector = (state: GridState) => state.treeData; + +export const gridTreeDataExpandedRowsSelector = createSelector( + gridTreeDataStateSelector, + (treeData) => treeData.expandedRows, +); diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 212400c63f0f8..1155a0ac7f43f 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -10,8 +10,11 @@ import { import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridComponentProps } from '../../../GridComponentProps'; import { useGridApiMethod } from '../../root/useGridApiMethod'; -import {GridColumnsPreProcessing} from "../../root/columnsPreProcessing"; -import {GridTreeDataCollapseColDef} from "./gridTreeDataCollapseColDef"; +import { GridColumnsPreProcessing } from '../../root/columnsPreProcessing'; +import { GridTreeDataToggleExpansionColDef } from './gridTreeDataToggleExpansionColDef'; +import { useGridState } from '../core'; +import { useGridLogger } from '../../utils'; +import { gridTreeDataExpandedRowsSelector } from './treeDataSelector'; const insertRowInTree = (tree: GridRowIdTree, id: GridRowId, path: string[]) => { if (path.length === 0) { @@ -47,10 +50,18 @@ export const useGridTreeData = ( apiRef: GridApiRef, props: Pick, ) => { + const logger = useGridLogger(apiRef, 'useGridTreeData'); + const [, setGridState, forceUpdate] = useGridState(apiRef); + const groupRows = React.useCallback( - (rowsLookup) => { - const getTreeDataPath = - props.getTreeDataPath ?? ((row) => [getGridRowId(row, props.getRowId).toString()]); + (rowsLookup, rowIds) => { + if (!props.treeData) { + return new Map(rowIds.map((id) => [id.toString(), { id, children: new Map() }]),) + } + + if (!props.getTreeDataPath ) { + throw new Error('Material-UI: No getTreeDataPath given to create the tree data.'); + } const rows = Object.values(rowsLookup) .map((row) => { @@ -58,7 +69,7 @@ export const useGridTreeData = ( return { id, - path: getTreeDataPath(row), + path: props.getTreeDataPath!(row), }; }) .sort((a, b) => a.path.length - b.path.length); @@ -70,27 +81,51 @@ export const useGridTreeData = ( return tree; }, - [props.getTreeDataPath], + [props.getTreeDataPath, props.getRowId], ); - React.useEffect(() => { - if (!props.treeData) { - return - } - - const addCollapseColumn: GridColumnsPreProcessing = (columns ) => [ - { - ...GridTreeDataCollapseColDef, + const toggleTreeDataRow = React.useCallback( + (id) => { + logger.debug(`Toggle tree data row #${id}`); + setGridState((state) => ({ + ...state, + treeData: { + expandedRows: { + ...state.treeData.expandedRows, + [id]: !state.treeData.expandedRows[id], }, - ...columns, - ] + }, + })); + forceUpdate(); + }, + [setGridState, forceUpdate, logger], + ); - return apiRef.current.registerColumnPreProcessing(addCollapseColumn) - }, []) + const isTreeDataRowExpanded = React.useCallback( + (id) => !!gridTreeDataExpandedRowsSelector(apiRef.current.state)[id], + [apiRef], + ); const treeDataApi: GridTreeDataApi = { groupRows, + toggleTreeDataRow, + isTreeDataRowExpanded, }; useGridApiMethod(apiRef, treeDataApi, 'GridTreeDataApi'); + + React.useEffect(() => { + if (!props.treeData) { + return () => {}; + } + + const addCollapseColumn: GridColumnsPreProcessing = (columns) => [ + { + ...GridTreeDataToggleExpansionColDef, + }, + ...columns, + ]; + + return apiRef.current.registerColumnPreProcessing('treeData', addCollapseColumn); + }, [apiRef, props.treeData]); }; diff --git a/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/gridColumnsPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/gridColumnsPreProcessingApi.ts index 1bdefa083eb89..9a6c3bc05396f 100644 --- a/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/gridColumnsPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/gridColumnsPreProcessingApi.ts @@ -1,9 +1,12 @@ -import { GridColumns} from "../../../models/colDef/gridColDef"; +import { GridColumns } from '../../../models/colDef/gridColDef'; export type GridColumnsPreProcessing = (columns: GridColumns) => GridColumns; -export type GridColumnsPreProcessingCleanup = () => void +export type GridColumnsPreProcessingCleanup = () => void; export interface GridColumnsPreProcessingApi { - registerColumnPreProcessing: (columnsPreProcessing: GridColumnsPreProcessing) => GridColumnsPreProcessingCleanup, - applyAllColumnPreProcessing: GridColumnsPreProcessing -} \ No newline at end of file + registerColumnPreProcessing: ( + processingName: string, + columnsPreProcessing: GridColumnsPreProcessing, + ) => GridColumnsPreProcessingCleanup; + applyAllColumnPreProcessing: GridColumnsPreProcessing; +} diff --git a/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/index.ts b/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/index.ts index be6ec10bc3f7b..06a2b57bada22 100644 --- a/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/index.ts +++ b/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/index.ts @@ -1,2 +1,2 @@ -export * from './useGridColumnsPreProcessing' -export * from './gridColumnsPreProcessingApi' \ No newline at end of file +export * from './useGridColumnsPreProcessing'; +export * from './gridColumnsPreProcessingApi'; diff --git a/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/useGridColumnsPreProcessing.ts b/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/useGridColumnsPreProcessing.ts index 733d34b945462..6c2b6311a60d0 100644 --- a/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/useGridColumnsPreProcessing.ts +++ b/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/useGridColumnsPreProcessing.ts @@ -1,36 +1,44 @@ -import * as React from 'react' -import {GridApiRef} from "../../../models/api/gridApiRef"; -import {GridColumnsPreProcessing, GridColumnsPreProcessingApi} from './gridColumnsPreProcessingApi' -import {useGridApiMethod} from "../useGridApiMethod"; -import {GridEvents} from "../../../constants/eventsConstants"; +import * as React from 'react'; +import { GridApiRef } from '../../../models/api/gridApiRef'; +import { + GridColumnsPreProcessing, + GridColumnsPreProcessingApi, +} from './gridColumnsPreProcessingApi'; +import { useGridApiMethod } from '../useGridApiMethod'; +import { GridEvents } from '../../../constants/eventsConstants'; export const useGridColumnsPreProcessing = (apiRef: GridApiRef) => { - const columnsPreProcessingRef = React.useRef(new Map()) + const columnsPreProcessingRef = React.useRef(new Map()); - const registerColumnPreProcessing = React.useCallback((columnsPreProcessing: GridColumnsPreProcessing) => { - const id = Math.random() + const registerColumnPreProcessing = React.useCallback< + GridColumnsPreProcessingApi['registerColumnPreProcessing'] + >( + (processingName, columnsPreProcessing: GridColumnsPreProcessing) => { + columnsPreProcessingRef.current.set(processingName, columnsPreProcessing); - columnsPreProcessingRef.current[id] = columnsPreProcessing + apiRef.current.publishEvent(GridEvents.columnsPreProcessingChange); - apiRef.current.publishEvent(GridEvents.columnsPreProcessingChange) + return () => columnsPreProcessingRef.current.delete(processingName); + }, + [apiRef], + ); - return () => columnsPreProcessingRef.current.delete(id) - }, []) + const applyAllColumnPreProcessing = React.useCallback< + GridColumnsPreProcessingApi['applyAllColumnPreProcessing'] + >((columns) => { + let preProcessedColumns = columns; - const applyAllColumnPreProcessing = React.useCallback((columns) => { - let preProcessedColumns = columns + columnsPreProcessingRef.current.forEach((columnsPreProcessing) => { + preProcessedColumns = columnsPreProcessing(preProcessedColumns); + }); - columnsPreProcessingRef.current.forEach(columnsPreProcessing => { - preProcessedColumns = columnsPreProcessing(preProcessedColumns) - }) + return preProcessedColumns; + }, []); - return preProcessedColumns - }, []) + const columnsPreProcessingApi: GridColumnsPreProcessingApi = { + registerColumnPreProcessing, + applyAllColumnPreProcessing, + }; - const columnsPreProcessingApi: GridColumnsPreProcessingApi = { - registerColumnPreProcessing, - applyAllColumnPreProcessing, - } - - useGridApiMethod(apiRef, columnsPreProcessingApi, 'GridColumnsPreProcessing'); -} \ No newline at end of file + useGridApiMethod(apiRef, columnsPreProcessingApi, 'GridColumnsPreProcessing'); +}; diff --git a/packages/grid/_modules_/grid/hooks/utils/useProcessedProps.ts b/packages/grid/_modules_/grid/hooks/utils/useProcessedProps.ts index 52ad632792750..4ff93d7e19551 100644 --- a/packages/grid/_modules_/grid/hooks/utils/useProcessedProps.ts +++ b/packages/grid/_modules_/grid/hooks/utils/useProcessedProps.ts @@ -50,6 +50,8 @@ const DEFAULT_GRID_ICON_SLOTS_COMPONENTS: GridIconSlotsComponent = { DensityComfortableIcon: GridViewStreamIcon, ExportIcon: GridSaveAltIcon, MoreActionsIcon: GridMoreVertIcon, + TreeDataCollapseIcon: GridArrowUpwardIcon, + TreeDataExpandIcon: GridArrowDownwardIcon, }; export const DEFAULT_GRID_SLOTS_COMPONENTS: GridSlotsComponent = { diff --git a/packages/grid/_modules_/grid/models/api/gridApi.ts b/packages/grid/_modules_/grid/models/api/gridApi.ts index f4999fb7ed266..d6d303c038bfb 100644 --- a/packages/grid/_modules_/grid/models/api/gridApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridApi.ts @@ -22,7 +22,7 @@ import { GridVirtualizationApi } from './gridVirtualizationApi'; import { GridLoggerApi } from './gridLoggerApi'; import { GridScrollApi } from './gridScrollApi'; import type { GridTreeDataApi } from '../../hooks/features/treeData'; -import type { GridColumnsPreProcessingApi } from "../../hooks/root/columnsPreProcessing"; +import type { GridColumnsPreProcessingApi } from '../../hooks/root/columnsPreProcessing'; /** * The full grid API. @@ -31,7 +31,7 @@ export interface GridApi extends GridCoreApi, GridStateApi, GridLoggerApi, - GridColumnsPreProcessingApi, + GridColumnsPreProcessingApi, GridDensityApi, GridEventsApi, GridRowApi, diff --git a/packages/grid/_modules_/grid/models/gridIconSlotsComponent.ts b/packages/grid/_modules_/grid/models/gridIconSlotsComponent.ts index c18e4cec255b1..4207344c8451d 100644 --- a/packages/grid/_modules_/grid/models/gridIconSlotsComponent.ts +++ b/packages/grid/_modules_/grid/models/gridIconSlotsComponent.ts @@ -64,4 +64,12 @@ export interface GridIconSlotsComponent { * Icon displayed on the `actions` column type to open the menu. */ MoreActionsIcon: React.JSXElementConstructor; + /** + * Icon displayed on the tree data toggling column when the children are collapsed + */ + TreeDataExpandIcon: React.JSXElementConstructor; + /** + * Icon displayed on the tree data toggling column when the children are expanded + */ + TreeDataCollapseIcon: React.JSXElementConstructor; } diff --git a/packages/grid/x-grid/src/useDataGridProComponent.tsx b/packages/grid/x-grid/src/useDataGridProComponent.tsx index f3603a5e4be15..c16356226da38 100644 --- a/packages/grid/x-grid/src/useDataGridProComponent.tsx +++ b/packages/grid/x-grid/src/useDataGridProComponent.tsx @@ -35,7 +35,7 @@ import { useGridResizeContainer } from '../../_modules_/grid/hooks/utils/useGrid import { useStateProp } from '../../_modules_/grid/hooks/utils/useStateProp'; import { GridApiRef } from '../../_modules_/grid/models/api/gridApiRef'; import { useGridTreeData } from '../../_modules_/grid/hooks/features/treeData'; -import {useGridColumnsPreProcessing} from "../../_modules_/grid/hooks/root/columnsPreProcessing"; +import { useGridColumnsPreProcessing } from '../../_modules_/grid/hooks/root/columnsPreProcessing'; export const useDataGridProComponent = (apiRef: GridApiRef, props: GridComponentProps) => { useGridLoggerFactory(apiRef, props); From 81b789ded5bc764178053f580a902a9034908942 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 15 Sep 2021 16:31:36 +0200 Subject: [PATCH 083/390] First working toggle --- docs/pages/api-docs/data-grid/grid-api.md | 6 +- .../components/data-grid/events/events.json | 4 + .../grid/components/GridViewport.tsx | 20 ++-- .../grid/hooks/features/core/gridState.ts | 5 - .../features/filter/gridFilterSelector.ts | 16 ++- .../keyboard/useGridKeyboardNavigation.ts | 18 +-- .../hooks/features/rows/gridRowsSelector.ts | 21 ++++ .../grid/hooks/features/rows/gridRowsState.ts | 2 + .../grid/hooks/features/rows/useGridRows.ts | 103 ++++++++++++++++-- .../hooks/features/sorting/useGridSorting.ts | 5 +- .../features/treeData/GridTreeDataApi.ts | 25 ----- .../features/treeData/gridTreeDataApi.ts | 12 ++ .../features/treeData/gridTreeDataState.ts | 5 - .../gridTreeDataToggleExpansionColDef.tsx | 9 +- .../grid/hooks/features/treeData/index.ts | 2 +- .../features/treeData/treeDataSelector.ts | 10 -- .../features/treeData/useGridTreeData.ts | 51 +++------ .../_modules_/grid/models/api/gridRowApi.ts | 11 ++ .../grid/_modules_/grid/models/gridRows.ts | 6 + 19 files changed, 211 insertions(+), 120 deletions(-) delete mode 100644 packages/grid/_modules_/grid/hooks/features/treeData/GridTreeDataApi.ts create mode 100644 packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataApi.ts delete mode 100644 packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataState.ts delete mode 100644 packages/grid/_modules_/grid/hooks/features/treeData/treeDataSelector.ts diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index af456a9a38b37..522ecbb2784bc 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -12,6 +12,7 @@ import { GridApi } from '@mui/x-data-grid-pro'; | Name | Type | Description | | :------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| applyAllColumnPreProcessing | GridColumnsPreProcessing | | | applyFilter | (item: GridFilterItem, linkOperator?: GridLinkOperator) => void | Applies a GridFilterItem on all rows. If no `linkOperator` is given, the "and" operator is used. | | applyFilterLinkOperator | (operator: GridLinkOperator) => void | Changes the GridLinkOperator used to connect the filters. | | applyFilters | () => void | Applies all filters on all rows. | @@ -52,13 +53,15 @@ import { GridApi } from '@mui/x-data-grid-pro'; | getSortModel | () => GridSortModel | Returns the sort model currently applied to the grid. | | getVisibleColumns | () => GridStateColDef[] | Returns the currently visible columns. | | getVisibleRowModels | () => Map<GridRowId, GridRowData> | Returns a sorted `Map` containing only the visible rows. | -| groupRows? | (rowsLookup: GridRowsLookup) => GridRowIdTree | Create the tree structure for a given set of rows | +| groupRows? | (rowsLookup: GridRowsLookup, rowIds: GridRowId[]) => GridRowGroupingResult | Create the tree structure for a given set of rows | | hideColumnMenu | () => void | Hides the column menu that is open. | | hideFilterPanel | () => void | Hides the filter panel. | | hidePreferences | () => void | Hides the preferences panel. | | isCellEditable | (params: GridCellParams) => boolean | Controls if a cell is editable. | +| isRowExpanded | (id: GridRowId) => boolean | Determines if a row is expanded or not. | | isRowSelected | (id: GridRowId) => boolean | Determines if a row is selected or not. | | publishEvent | (name: string, params?: any, event?: MuiEvent<MouseEvent \| UIEvent \| Event \| SyntheticEvent<Element, Event> \| TouchEvent \| ErrorEvent \| ProgressEvent<EventTarget> \| ClipboardEvent \| AnimationEvent \| InputEvent \| FocusEvent \| CompositionEvent \| DragEvent \| FormDataEvent \| PointerEvent \| KeyboardEvent \| SecurityPolicyViolationEvent \| TransitionEvent \| WheelEvent>) => void | Emits an event. | +| registerColumnPreProcessing | (processingName: string, columnsPreProcessing: GridColumnsPreProcessing) => GridColumnsPreProcessingCleanup | | | resize | () => void | Triggers a resize of the component and recalculation of width and height. | | scroll | (params: Partial<GridScrollParams>) => void | Triggers the viewport to scroll to the given positions (in pixels). | | scrollToIndexes | (params: Partial<GridCellIndexCoordinates>) => boolean | Triggers the viewport to scroll to the cell at indexes given by `params`.
Returns `true` if the grid had to scroll to reach the target. | @@ -76,6 +79,7 @@ import { GridApi } from '@mui/x-data-grid-pro'; | setFilterModel | (model: GridFilterModel) => void | Sets the filter model to the one given by `model`. | | setPage | (page: number) => void | Sets the displayed page to the value given by `page`. | | setPageSize | (pageSize: number) => void | Sets the number of displayed rows to the value given by `pageSize`. | +| setRowExpansion | (id: GridRowId, isExpanded: boolean) => void | | | setRowMode | (id: GridRowId, mode: GridRowMode) => void | Sets the mode of a row. | | setRows | (rows: GridRowData[]) => void | Sets a new set of rows. | | setSelectionModel | (rowIds: GridRowId[]) => void | Updates the selected rows to be those passed to the `rowIds` argument.
Any row already selected will be unselected. | diff --git a/docs/src/pages/components/data-grid/events/events.json b/docs/src/pages/components/data-grid/events/events.json index bc1df37f2a204..a0ee1041b0e59 100644 --- a/docs/src/pages/components/data-grid/events/events.json +++ b/docs/src/pages/components/data-grid/events/events.json @@ -92,6 +92,10 @@ "name": "columnsChange", "description": "Fired when the columns state is changed.\nCalled with an array of strings corresponding to the field names." }, + { + "name": "columnsPreProcessingChange", + "description": "Fired when a column pre-processing is changed" + }, { "name": "columnVisibilityChange", "description": "Fired when a column visibility changes. Called with a GridColumnVisibilityChangeParams object." diff --git a/packages/grid/_modules_/grid/components/GridViewport.tsx b/packages/grid/_modules_/grid/components/GridViewport.tsx index 43f7362bc760d..49866a0e6c39f 100644 --- a/packages/grid/_modules_/grid/components/GridViewport.tsx +++ b/packages/grid/_modules_/grid/components/GridViewport.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { visibleGridColumnsSelector } from '../hooks/features/columns/gridColumnsSelector'; import { useGridSelector } from '../hooks/features/core/useGridSelector'; import { gridDensityRowHeightSelector } from '../hooks/features/density/densitySelector'; -import { visibleSortedGridRowsAsArraySelector } from '../hooks/features/filter/gridFilterSelector'; +import { gridSortedVisibleRowsFlatSelector } from '../hooks/features/filter/gridFilterSelector'; import { gridFocusCellSelector, gridTabIndexCellSelector, @@ -38,7 +38,7 @@ export const GridViewport: ViewportType = React.forwardRef( const cellFocus = useGridSelector(apiRef, gridFocusCellSelector); const cellTabIndex = useGridSelector(apiRef, gridTabIndexCellSelector); const selection = useGridSelector(apiRef, gridSelectionStateSelector); - const visibleSortedRowsAsArray = useGridSelector(apiRef, visibleSortedGridRowsAsArraySelector); + const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowsFlatSelector); const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector); const editRowsState = useGridSelector(apiRef, gridEditRowsStateSelector); @@ -64,24 +64,24 @@ export const GridViewport: ViewportType = React.forwardRef( return null; } - const renderedRows = visibleSortedRowsAsArray.slice( + const renderedRows = visibleSortedRows.slice( renderState.renderContext.firstRowIdx, renderState.renderContext.lastRowIdx!, ); // TODO: Add tree children - return renderedRows.map(([id, row], idx) => ( + return renderedRows.map((row, idx) => ( ( rowIndex={renderState.renderContext!.firstRowIdx! + idx} cellFocus={cellFocus} cellTabIndex={cellTabIndex} - isSelected={selectionLookup[id] !== undefined} - editRowState={editRowsState[id]} + isSelected={selectionLookup[row.id] !== undefined} + editRowState={editRowsState[row.id]} getCellClassName={rootProps.getCellClassName} /> diff --git a/packages/grid/_modules_/grid/hooks/features/core/gridState.ts b/packages/grid/_modules_/grid/hooks/features/core/gridState.ts index 5f0dfee48b91b..66ed4a5daed58 100644 --- a/packages/grid/_modules_/grid/hooks/features/core/gridState.ts +++ b/packages/grid/_modules_/grid/hooks/features/core/gridState.ts @@ -31,7 +31,6 @@ import { InternalRenderingState, } from '../virtualization/renderingState'; import { getInitialPaginationState, GridPaginationState } from '../pagination/gridPaginationState'; -import { GridTreeDataState } from '../treeData/gridTreeDataState'; export interface GridState { rows: GridRowsState; @@ -53,7 +52,6 @@ export interface GridState { visibleRows: VisibleGridRowsState; preferencePanel: GridPreferencePanelState; density: GridGridDensity; - treeData: GridTreeDataState; error?: any; } @@ -77,7 +75,4 @@ export const getInitialGridState = (): GridState => ({ preferencePanel: { open: false }, visibleRows: getInitialVisibleGridRowsState(), density: getInitialGridDensityState(), - treeData: { - expandedRows: {}, - }, }); diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index 563466c0c31aa..cf21badab793c 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -1,6 +1,6 @@ import { createSelector } from 'reselect'; import { GridFilterItem } from '../../../models/gridFilterItem'; -import { GridRowId } from '../../../models/gridRows'; +import { GridRowId, GridRowModel } from '../../../models/gridRows'; import { GridState } from '../core/gridState'; import { gridRowCountSelector } from '../rows/gridRowsSelector'; import { gridSortedRowsSelector } from '../sorting/gridSortingSelector'; @@ -32,9 +32,19 @@ export const gridSortedVisibleRowsSelector = createSelector( }, ); -export const visibleSortedGridRowsAsArraySelector = createSelector( +export const gridSortedVisibleRowsFlatSelector = createSelector( gridSortedVisibleRowsSelector, - (visibleSortedRows) => [...visibleSortedRows.entries()], + (rows) => { + const flattenRowIds = ( + nodes: Map, + ): { id: GridRowId; node: GridRowModel }[] => + Array.from(nodes.entries()).flatMap(([id, row]) => [ + { id, node: row.node }, + ...flattenRowIds(row.children), + ]); + + return flattenRowIds(rows); + }, ); export const visibleSortedGridRowIdsSelector = createSelector( diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts index 5687f8ed34471..345864cf8b08f 100644 --- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts +++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts @@ -23,7 +23,7 @@ import { gridRowCountSelector } from '../rows/gridRowsSelector'; import { useGridLogger } from '../../utils/useGridLogger'; import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; import { GridComponentProps } from '../../../GridComponentProps'; -import { visibleSortedGridRowsAsArraySelector } from '../filter/gridFilterSelector'; +import { gridSortedVisibleRowsFlatSelector } from '../filter/gridFilterSelector'; const getNextCellIndexes = (key: string, indexes: GridCellIndexCoordinates) => { if (!isArrowKeys(key)) { @@ -79,7 +79,7 @@ export const useGridKeyboardNavigation = ( const totalRowCount = useGridSelector(apiRef, gridRowCountSelector); const colCount = useGridSelector(apiRef, visibleGridColumnsLengthSelector); const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); - const visibleSortedRowsAsArray = useGridSelector(apiRef, visibleSortedGridRowsAsArraySelector); + const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowsFlatSelector); const mapKey = (event: React.KeyboardEvent) => { if (isEnterKey(event.key)) { @@ -95,7 +95,7 @@ export const useGridKeyboardNavigation = ( (params: GridCellParams, event: React.KeyboardEvent) => { event.preventDefault(); const colIndex = apiRef.current.getColumnIndex(params.field); - const rowIndex = visibleSortedRowsAsArray.findIndex(([id]) => id === params.id); + const rowIndex = visibleSortedRows.findIndex((row) => row.id === params.id); const key = mapKey(event); const isCtrlPressed = event.ctrlKey || event.metaKey || event.shiftKey; @@ -156,12 +156,12 @@ export const useGridKeyboardNavigation = ( ); apiRef.current.scrollToIndexes(nextCellIndexes); const field = apiRef.current.getVisibleColumns()[nextCellIndexes.colIndex].field; - const [id] = visibleSortedRowsAsArray[nextCellIndexes.rowIndex]; - apiRef.current.setCellFocus(id, field); + const node = visibleSortedRows[nextCellIndexes.rowIndex]; + apiRef.current.setCellFocus(node.id, field); }, [ apiRef, - visibleSortedRowsAsArray, + visibleSortedRows, totalRowCount, props.pagination, paginationState.pageSize, @@ -202,8 +202,8 @@ export const useGridKeyboardNavigation = ( if (!nextColumnHeaderIndexes) { const field = apiRef.current.getVisibleColumns()[colIndex].field; - const [id] = visibleSortedRowsAsArray[0]; - apiRef.current.setCellFocus(id, field); + const node = visibleSortedRows[0]; + apiRef.current.setCellFocus(node.id, field); return; } @@ -218,7 +218,7 @@ export const useGridKeyboardNavigation = ( const field = apiRef.current.getVisibleColumns()[nextColumnHeaderIndexes.colIndex].field; apiRef.current.setColumnHeaderFocus(field, event); }, - [apiRef, colCount, containerSizes, logger, visibleSortedRowsAsArray], + [apiRef, colCount, containerSizes, logger, visibleSortedRows], ); useGridApiEventHandler(apiRef, GridEvents.cellNavigationKeyDown, navigateCells); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts index a0d3b0114cfba..a93a0db9f5e15 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts @@ -19,6 +19,27 @@ export const gridRowsLookupSelector = createSelector( export const gridRowTreeSelector = createSelector(gridRowsStateSelector, (rows) => rows.tree); +export const gridRowExpandedTreeSelector = createSelector(gridRowTreeSelector, (rowsTree) => { + const removeCollapsedNodes = (tree: GridRowIdTree) => { + const treeWithoutCollapsedChildren: GridRowIdTree = new Map(); + + tree.forEach((node, id) => { + const children: GridRowIdTree = node.expanded + ? removeCollapsedNodes(node.children) + : new Map(); + + treeWithoutCollapsedChildren.set(id, { + ...node, + children, + }); + }); + + return treeWithoutCollapsedChildren; + }; + + return removeCollapsedNodes(rowsTree); +}); + export const gridRowIdsFlatSelector = createSelector(gridRowTreeSelector, (tree) => { const flattenRowIds = (nodes: GridRowIdTree): GridRowId[] => Array.from(nodes.values()).flatMap((node) => [node.id, ...flattenRowIds(node.children)]); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts index be90a0025f55c..79e7bfa530dcf 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts @@ -2,12 +2,14 @@ import { GridRowId, GridRowModel, GridRowIdTree } from '../../../models/gridRows export interface GridRowsState { idRowsLookup: Record; + paths: Record; tree: GridRowIdTree; totalRowCount: number; } export const getInitialGridRowState: () => GridRowsState = () => ({ idRowsLookup: {}, + paths: {}, tree: new Map(), totalRowCount: 0, }); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 55b234d076edf..d2180680bd2a7 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -10,8 +10,9 @@ import { GridRowsProp, GridRowIdGetter, GridRowData, - GridRowIdTree, GridRowIdTreeNode, + GridRowGroupingResult, + GridRowIdTree, } from '../../../models/gridRows'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; @@ -24,7 +25,7 @@ import { gridRowIdsFlatSelector, } from './gridRowsSelector'; -export type GridRowsInternalCacheState = Omit & { +export type GridRowsInternalCacheState = Omit & { rowIds: GridRowId[]; }; @@ -64,10 +65,43 @@ export function convertGridRowsPropToState( return state; } -const getFlatRowTree = (rowIds: GridRowId[]): GridRowIdTree => - new Map( +const getFlatRowTree = (rowIds: GridRowId[]): GridRowGroupingResult => ({ + tree: new Map( rowIds.map((id) => [id.toString(), { id, children: new Map() }]), - ); + ), + paths: Object.fromEntries(rowIds.map((id) => [id, [id.toString()]])), +}); + +const setRowExpansionInTree = ( + id: GridRowId, + tree: GridRowIdTree, + path: string[], + isExpanded: boolean, +) => { + if (path.length === 0) { + throw new Error(`Material-UI: Invalid path for row #${id}.`); + } + + const clonedMap = new Map(tree.entries()); + const [nodeName, ...restPath] = path; + const nodeBefore = clonedMap.get(nodeName); + + if (!nodeBefore) { + throw new Error(`Material-UI: Invalid path for row #${id}.`); + } + + if (restPath.length === 0) { + clonedMap.set(nodeName, { ...nodeBefore, expanded: isExpanded }); + } else { + clonedMap.set(nodeName, { + ...nodeBefore, + expanded: nodeBefore.expanded || isExpanded, + children: setRowExpansionInTree(id, nodeBefore.children, restPath, isExpanded), + }); + } + + return clonedMap; +}; /** * @requires useGridSorting (method) @@ -125,10 +159,10 @@ export const useGridRows = ( rowsCache.current.timeout = null; rowsCache.current.lastUpdateMs = Date.now(); const { rowIds, ...rowState } = rowsCache.current.state; - const tree = apiRef.current.groupRows + const { tree, paths } = apiRef.current.groupRows ? apiRef.current.groupRows(rowState.idRowsLookup, rowIds) : getFlatRowTree(rowIds); - setGridState((state) => ({ ...state, rows: { ...rowState, tree } })); + setGridState((state) => ({ ...state, rows: { ...rowState, tree, paths } })); apiRef.current.publishEvent(GridEvents.rowsSet); forceUpdate(); }; @@ -243,6 +277,59 @@ export const useGridRows = ( [apiRef], ); + const setRowExpansion = React.useCallback( + (id, isExpanded) => { + setGridState((state) => { + const path = state.rows.paths[id]; + + return { + ...state, + rows: { + ...state.rows, + tree: setRowExpansionInTree(id, state.rows.tree, path, isExpanded), + }, + }; + }); + forceUpdate(); + apiRef.current.publishEvent(GridEvents.rowsSet); + }, + [apiRef, setGridState, forceUpdate], + ); + + const isRowExpanded = React.useCallback( + (id) => { + const getNodeFromTree = (tree: GridRowIdTree, path: string[]): GridRowIdTreeNode => { + if (path.length === 0) { + throw new Error(`Material-UI: No row with id #${id} found in row tree`); + } + + const [nodeName, ...restPath] = path; + const node = tree.get(nodeName); + + if (!node) { + throw new Error(`Material-UI: No row with id #${id} found in row tree`); + } + + if (restPath.length === 0) { + return node; + } + + return getNodeFromTree(node.children, restPath); + }; + + const path = apiRef.current.state.rows.paths[id]; + + if (!path) { + throw new Error(`Material-UI: No row with id #${id} found in row path list`); + } + + const node = getNodeFromTree(apiRef.current.getRowModels(), path); + + return !!node.expanded; + }, + [apiRef], + ); + React.useEffect(() => { return () => { if (rowsCache.current.timeout !== null) { @@ -269,6 +356,8 @@ export const useGridRows = ( getAllRowIds, setRows, updateRows, + setRowExpansion, + isRowExpanded, }; useGridApiMethod(apiRef, rowApi, 'GridRowApi'); diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index f360f43294b5f..706bfc29ca04e 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -27,7 +27,7 @@ import { gridSortedRowIdsFlatSelector, gridSortedRowsSelector, } from './gridSortingSelector'; -import { gridRowTreeSelector } from '../rows'; +import { gridRowExpandedTreeSelector } from '../rows'; import { GridSortedRowsIdTreeNode } from './gridSortingState'; /** @@ -151,8 +151,7 @@ export const useGridSorting = ( ); const applySorting = React.useCallback(() => { - const unsortedRowTree = gridRowTreeSelector(apiRef.current.state); - + const unsortedRowTree = gridRowExpandedTreeSelector(apiRef.current.state); const skipServerSorting = props.sortingMode === GridFeatureModeConstant.server; if (skipServerSorting) { diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/GridTreeDataApi.ts b/packages/grid/_modules_/grid/hooks/features/treeData/GridTreeDataApi.ts deleted file mode 100644 index a56c1e6f9ffed..0000000000000 --- a/packages/grid/_modules_/grid/hooks/features/treeData/GridTreeDataApi.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { GridRowId, GridRowIdTree } from '../../../models/gridRows'; -import { GridRowsLookup } from '../rows'; - -export interface GridTreeDataApi { - /** - * Create the tree structure for a given set of rows - * @param {GridRowsLookup} rowsLookup the rows to process - * @param {GridRowId[]} rowIds the id of each row - * @returns {GridRowIdTree} tree the tree structure containing all the rows - */ - groupRows: (rowsLookup: GridRowsLookup, rowIds: GridRowId[]) => GridRowIdTree; - - /** - * Toggle the expansion of a tree data row - * @param {GridRowId} id the ID of the row to toggle - */ - toggleTreeDataRow: (id: GridRowId) => void; - - /** - * Determines if a row is expanded in the tree data or not. - * @param {GridRowId} id The id of the row. - * @returns {boolean} A boolean indicating if the row is expanded. - */ - isTreeDataRowExpanded: (id: GridRowId) => boolean; -} diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataApi.ts b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataApi.ts new file mode 100644 index 0000000000000..d6eb379a34f9d --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataApi.ts @@ -0,0 +1,12 @@ +import { GridRowGroupingResult, GridRowId } from '../../../models/gridRows'; +import { GridRowsLookup } from '../rows'; + +export interface GridTreeDataApi { + /** + * Create the tree structure for a given set of rows + * @param {GridRowsLookup} rowsLookup the rows to process + * @param {GridRowId[]} rowIds the id of each row + * @returns {GridRowGroupingResult} tree the tree structure containing all the rows + */ + groupRows: (rowsLookup: GridRowsLookup, rowIds: GridRowId[]) => GridRowGroupingResult; +} diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataState.ts b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataState.ts deleted file mode 100644 index f7191680d1f63..0000000000000 --- a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataState.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { GridRowId } from '../../../models/gridRows'; - -export interface GridTreeDataState { - expandedRows: Record; -} diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataToggleExpansionColDef.tsx b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataToggleExpansionColDef.tsx index b18b461341167..9fa91d8b7c5e4 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataToggleExpansionColDef.tsx +++ b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataToggleExpansionColDef.tsx @@ -10,13 +10,14 @@ import { GridRowId } from '../../../models/gridRows'; const TreeDataToggleIcon = ({ id }: { id: GridRowId }) => { const rootProps = useGridRootProps(); const apiRef = useGridApiContext(); + const isExpanded = apiRef.current.isRowExpanded(id); - const Icon = apiRef.current.isTreeDataRowExpanded!(id) + const Icon = isExpanded ? rootProps.components.TreeDataCollapseIcon : rootProps.components.TreeDataExpandIcon; return ( - apiRef.current.toggleTreeDataRow!(id)}> + apiRef.current.setRowExpansion(id, !isExpanded)}> ); @@ -31,7 +32,5 @@ export const GridTreeDataToggleExpansionColDef: GridColDef = { disableReorder: true, valueGetter: () => true, renderHeader: () => 'Header', - renderCell: (params) => { - return ; - }, + renderCell: (params) => , }; diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/index.ts b/packages/grid/_modules_/grid/hooks/features/treeData/index.ts index faa592795ad68..a1436305a97dc 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/index.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/index.ts @@ -1,2 +1,2 @@ export * from './useGridTreeData'; -export * from './GridTreeDataApi'; +export * from './gridTreeDataApi'; diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/treeDataSelector.ts b/packages/grid/_modules_/grid/hooks/features/treeData/treeDataSelector.ts deleted file mode 100644 index b33d9717f3018..0000000000000 --- a/packages/grid/_modules_/grid/hooks/features/treeData/treeDataSelector.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { createSelector } from 'reselect'; - -import { GridState } from '../core/gridState'; - -const gridTreeDataStateSelector = (state: GridState) => state.treeData; - -export const gridTreeDataExpandedRowsSelector = createSelector( - gridTreeDataStateSelector, - (treeData) => treeData.expandedRows, -); diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 1155a0ac7f43f..7d8c837f65690 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -1,5 +1,5 @@ import * as React from 'react'; -import { GridTreeDataApi } from './GridTreeDataApi'; +import { GridTreeDataApi } from './gridTreeDataApi'; import { GridRowData, GridRowId, @@ -12,9 +12,6 @@ import { GridComponentProps } from '../../../GridComponentProps'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { GridColumnsPreProcessing } from '../../root/columnsPreProcessing'; import { GridTreeDataToggleExpansionColDef } from './gridTreeDataToggleExpansionColDef'; -import { useGridState } from '../core'; -import { useGridLogger } from '../../utils'; -import { gridTreeDataExpandedRowsSelector } from './treeDataSelector'; const insertRowInTree = (tree: GridRowIdTree, id: GridRowId, path: string[]) => { if (path.length === 0) { @@ -50,17 +47,19 @@ export const useGridTreeData = ( apiRef: GridApiRef, props: Pick, ) => { - const logger = useGridLogger(apiRef, 'useGridTreeData'); - const [, setGridState, forceUpdate] = useGridState(apiRef); - const groupRows = React.useCallback( (rowsLookup, rowIds) => { if (!props.treeData) { - return new Map(rowIds.map((id) => [id.toString(), { id, children: new Map() }]),) + return { + tree: new Map( + rowIds.map((id) => [id.toString(), { id, children: new Map() }]), + ), + paths: Object.fromEntries(rowIds.map((id) => [id, [id.toString()]])), + }; } - if (!props.getTreeDataPath ) { - throw new Error('Material-UI: No getTreeDataPath given to create the tree data.'); + if (!props.getTreeDataPath) { + throw new Error('Material-UI: No getTreeDataPath given.'); } const rows = Object.values(rowsLookup) @@ -74,42 +73,22 @@ export const useGridTreeData = ( }) .sort((a, b) => a.path.length - b.path.length); + const paths = Object.fromEntries(rows.map((row) => [row.id, row.path])); const tree = new Map(); rows.forEach((row) => { insertRowInTree(tree, row.id, row.path); }); - return tree; - }, - [props.getTreeDataPath, props.getRowId], - ); - - const toggleTreeDataRow = React.useCallback( - (id) => { - logger.debug(`Toggle tree data row #${id}`); - setGridState((state) => ({ - ...state, - treeData: { - expandedRows: { - ...state.treeData.expandedRows, - [id]: !state.treeData.expandedRows[id], - }, - }, - })); - forceUpdate(); + return { + tree, + paths, + }; }, - [setGridState, forceUpdate, logger], - ); - - const isTreeDataRowExpanded = React.useCallback( - (id) => !!gridTreeDataExpandedRowsSelector(apiRef.current.state)[id], - [apiRef], + [props.getTreeDataPath, props.getRowId, props.treeData], ); const treeDataApi: GridTreeDataApi = { groupRows, - toggleTreeDataRow, - isTreeDataRowExpanded, }; useGridApiMethod(apiRef, treeDataApi, 'GridTreeDataApi'); diff --git a/packages/grid/_modules_/grid/models/api/gridRowApi.ts b/packages/grid/_modules_/grid/models/api/gridRowApi.ts index 0dcac25088b2d..18cd8202d81a0 100644 --- a/packages/grid/_modules_/grid/models/api/gridRowApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridRowApi.ts @@ -47,4 +47,15 @@ export interface GridRowApi { * @returns {GridRowModel} The row data. */ getRow: (id: GridRowId) => GridRowModel | null; + /** + * @param {GridRowId} id the ID of the row to toggle + * @param {boolean} isExpanded A boolean indicating if the row must be expanded + */ + setRowExpansion: (id: GridRowId, isExpanded: boolean) => void; + /** + * Determines if a row is expanded or not. + * @param {GridRowId} id The id of the row. + * @returns {boolean} A boolean indicating if the row is expanded. + */ + isRowExpanded: (id: GridRowId) => boolean; } diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index 7640efa484d8a..4a6400b454638 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -15,10 +15,16 @@ export interface GridRowModelUpdate extends GridRowData { export interface GridRowIdTreeNode { id: GridRowId; children: GridRowIdTree; + expanded?: boolean; } export type GridRowIdTree = Map; +export interface GridRowGroupingResult { + tree: GridRowIdTree; + paths: Record; +} + /** * The type of Id supported by the grid. */ From c73e06fd370d65a3877638097e2d0c33220d69a8 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 15 Sep 2021 16:41:11 +0200 Subject: [PATCH 084/390] Improve Story --- .../src/stories/grid-tree-data.stories.tsx | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/storybook/src/stories/grid-tree-data.stories.tsx b/packages/storybook/src/stories/grid-tree-data.stories.tsx index b2c2334cd8f77..61dcb3fab95ef 100644 --- a/packages/storybook/src/stories/grid-tree-data.stories.tsx +++ b/packages/storybook/src/stories/grid-tree-data.stories.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { DataGridPro, GridColumns } from '@mui/x-data-grid-pro'; import { Meta } from '@storybook/react'; +import Button from '@mui/material/Button'; export default { title: 'X-Grid Tests/Tree Data', @@ -32,5 +33,25 @@ const columns: GridColumns = [ const getTreeDataPath = (row) => row.name.split('.'); export function TreeData() { - return ; + const [treeDataEnabled, setTreeDataEnabled] = React.useState(true); + + return ( + + + + + ); } From 1f6fe3c7512bfb059d436688f7519e34792e56af Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 15 Sep 2021 16:51:35 +0200 Subject: [PATCH 085/390] Allow to toggle tree data --- .../hooks/features/columns/useGridColumns.ts | 28 ++++++++++++++++++- .../features/treeData/useGridTreeData.ts | 20 ++++++------- .../gridColumnsPreProcessingApi.ts | 5 ++-- .../useGridColumnsPreProcessing.ts | 17 ++++++----- .../src/stories/grid-tree-data.stories.tsx | 1 - 5 files changed, 49 insertions(+), 22 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts index 2a33cd6baafc7..99045233bd493 100644 --- a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts +++ b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts @@ -24,7 +24,7 @@ import { gridColumnsMetaSelector, visibleGridColumnsSelector, } from './gridColumnsSelector'; -import { useGridApiOptionHandler } from '../../root/useGridApiEventHandler'; +import { useGridApiEventHandler, useGridApiOptionHandler } from '../../root/useGridApiEventHandler'; import { GRID_STRING_COL_DEF } from '../../../models/colDef/gridStringColDef'; import { GridComponentProps } from '../../../GridComponentProps'; import { getDataGridUtilityClass } from '../../../gridClasses'; @@ -359,6 +359,32 @@ export function useGridColumns( setColumnsState(apiRef.current.state.columns); }, [apiRef, setColumnsState, gridState.viewportSizes.width, logger]); + const handlePreProcessColumns = React.useCallback(() => { + logger.info(`Columns pre-processing have changed, regenerating the columns`); + + const hydratedColumns = hydrateColumnsType( + props.columns, + props.columnTypes, + apiRef.current.getLocaleText, + props.checkboxSelection, + classes, + ); + + const preProcessedColumns = apiRef.current.applyAllColumnPreProcessing(hydratedColumns); + const columnState = upsertColumnsState(preProcessedColumns); + setColumnsState(columnState); + }, [ + apiRef, + logger, + classes, + setColumnsState, + props.columns, + props.columnTypes, + props.checkboxSelection, + ]); + + useGridApiEventHandler(apiRef, GridEvents.columnsPreProcessingChange, handlePreProcessColumns); + // Grid Option Handlers useGridApiOptionHandler( apiRef, diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 7d8c837f65690..c02f68a210292 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -95,16 +95,16 @@ export const useGridTreeData = ( React.useEffect(() => { if (!props.treeData) { - return () => {}; + apiRef.current.registerColumnPreProcessing('treeData', null); + } else { + const addCollapseColumn: GridColumnsPreProcessing = (columns) => [ + { + ...GridTreeDataToggleExpansionColDef, + }, + ...columns, + ]; + + apiRef.current.registerColumnPreProcessing('treeData', addCollapseColumn); } - - const addCollapseColumn: GridColumnsPreProcessing = (columns) => [ - { - ...GridTreeDataToggleExpansionColDef, - }, - ...columns, - ]; - - return apiRef.current.registerColumnPreProcessing('treeData', addCollapseColumn); }, [apiRef, props.treeData]); }; diff --git a/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/gridColumnsPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/gridColumnsPreProcessingApi.ts index 9a6c3bc05396f..79b03265aae4c 100644 --- a/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/gridColumnsPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/gridColumnsPreProcessingApi.ts @@ -1,12 +1,11 @@ import { GridColumns } from '../../../models/colDef/gridColDef'; export type GridColumnsPreProcessing = (columns: GridColumns) => GridColumns; -export type GridColumnsPreProcessingCleanup = () => void; export interface GridColumnsPreProcessingApi { registerColumnPreProcessing: ( processingName: string, - columnsPreProcessing: GridColumnsPreProcessing, - ) => GridColumnsPreProcessingCleanup; + columnsPreProcessing: GridColumnsPreProcessing | null, + ) => void; applyAllColumnPreProcessing: GridColumnsPreProcessing; } diff --git a/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/useGridColumnsPreProcessing.ts b/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/useGridColumnsPreProcessing.ts index 6c2b6311a60d0..da280ef8adc0a 100644 --- a/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/useGridColumnsPreProcessing.ts +++ b/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/useGridColumnsPreProcessing.ts @@ -8,17 +8,18 @@ import { useGridApiMethod } from '../useGridApiMethod'; import { GridEvents } from '../../../constants/eventsConstants'; export const useGridColumnsPreProcessing = (apiRef: GridApiRef) => { - const columnsPreProcessingRef = React.useRef(new Map()); + const columnsPreProcessingRef = React.useRef(new Map()); const registerColumnPreProcessing = React.useCallback< GridColumnsPreProcessingApi['registerColumnPreProcessing'] >( - (processingName, columnsPreProcessing: GridColumnsPreProcessing) => { - columnsPreProcessingRef.current.set(processingName, columnsPreProcessing); + (processingName, columnsPreProcessing) => { + const columnPreProcessingBefore = columnsPreProcessingRef.current.get(processingName) ?? null; - apiRef.current.publishEvent(GridEvents.columnsPreProcessingChange); - - return () => columnsPreProcessingRef.current.delete(processingName); + if (columnPreProcessingBefore !== columnsPreProcessing) { + columnsPreProcessingRef.current.set(processingName, columnsPreProcessing); + apiRef.current.publishEvent(GridEvents.columnsPreProcessingChange); + } }, [apiRef], ); @@ -29,7 +30,9 @@ export const useGridColumnsPreProcessing = (apiRef: GridApiRef) => { let preProcessedColumns = columns; columnsPreProcessingRef.current.forEach((columnsPreProcessing) => { - preProcessedColumns = columnsPreProcessing(preProcessedColumns); + if (columnsPreProcessing) { + preProcessedColumns = columnsPreProcessing(preProcessedColumns); + } }); return preProcessedColumns; diff --git a/packages/storybook/src/stories/grid-tree-data.stories.tsx b/packages/storybook/src/stories/grid-tree-data.stories.tsx index 61dcb3fab95ef..31c2fe3bdbde6 100644 --- a/packages/storybook/src/stories/grid-tree-data.stories.tsx +++ b/packages/storybook/src/stories/grid-tree-data.stories.tsx @@ -46,7 +46,6 @@ export function TreeData() { {treeDataEnabled ? 'Disable tree data' : 'Enable tree data'} Date: Thu, 16 Sep 2021 12:00:53 +0200 Subject: [PATCH 086/390] Improve collapse UI --- docs/pages/api-docs/data-grid/grid-api.md | 7 +- .../cell/GridTreeDataGroupingCell.tsx | 129 ++++++++++++++++++ .../_modules_/grid/components/cell/index.ts | 1 + .../_modules_/grid/components/icons/index.tsx | 10 ++ .../features/filter/gridFilterSelector.ts | 4 +- .../hooks/features/filter/useGridFilter.ts | 4 +- .../hooks/features/rows/gridRowsSelector.ts | 25 ++-- .../grid/hooks/features/rows/gridRowsState.ts | 4 +- .../grid/hooks/features/rows/useGridRows.ts | 73 ++++++---- .../features/sorting/gridSortingSelector.ts | 7 +- .../features/sorting/gridSortingState.ts | 4 +- .../hooks/features/sorting/useGridSorting.ts | 6 +- .../treeData/gridTreeDataGroupColDef.tsx | 18 +++ .../gridTreeDataToggleExpansionColDef.tsx | 36 ----- .../features/treeData/useGridTreeData.ts | 33 +++-- .../grid/hooks/utils/useProcessedProps.ts | 6 +- .../_modules_/grid/models/api/gridRowApi.ts | 29 ++-- .../grid/_modules_/grid/models/gridRows.ts | 9 +- .../models/params/gridSlotComponentProps.ts | 4 +- .../src/stories/grid-tree-data.stories.tsx | 3 +- 20 files changed, 290 insertions(+), 122 deletions(-) create mode 100644 packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx create mode 100644 packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx delete mode 100644 packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataToggleExpansionColDef.tsx diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index 522ecbb2784bc..89a905156fcfa 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -43,8 +43,10 @@ import { GridApi } from '@mui/x-data-grid-pro'; | getRowIdFromRowIndex | (index: number) => GridRowId | Gets the `GridRowId` of a row at a specific index. | | getRowIndex | (id: GridRowId) => number | Gets the row index of a row with a given id. | | getRowMode | (id: GridRowId) => GridRowMode | Gets the mode of a row. | -| getRowModels | () => GridRowIdTree | Gets the full set of rows ordered in a tree structure. | +| getRowModels | () => GridRowConfigTree | Gets the full set of rows ordered in a tree structure. | +| getRowNode | (id: GridRowId) => null \| GridRowConfigTreeNode | Gets the row node from the internal tree structure. | | getRowParams | (id: GridRowId) => GridRowParams | Gets the [GridRowParams](/api/data-grid/grid-row-params/) object that is passed as argument in events. | +| getRowPath | (id: GridRowId) => null \| string[] | | | getRowsCount | () => number | Gets the total number of rows in the grid. | | getScrollPosition | () => GridScrollParams | Returns the current scroll position. | | getSelectedRows | () => Map<GridRowId, GridRowData> | Returns an array of the selected rows. | @@ -58,10 +60,9 @@ import { GridApi } from '@mui/x-data-grid-pro'; | hideFilterPanel | () => void | Hides the filter panel. | | hidePreferences | () => void | Hides the preferences panel. | | isCellEditable | (params: GridCellParams) => boolean | Controls if a cell is editable. | -| isRowExpanded | (id: GridRowId) => boolean | Determines if a row is expanded or not. | | isRowSelected | (id: GridRowId) => boolean | Determines if a row is selected or not. | | publishEvent | (name: string, params?: any, event?: MuiEvent<MouseEvent \| UIEvent \| Event \| SyntheticEvent<Element, Event> \| TouchEvent \| ErrorEvent \| ProgressEvent<EventTarget> \| ClipboardEvent \| AnimationEvent \| InputEvent \| FocusEvent \| CompositionEvent \| DragEvent \| FormDataEvent \| PointerEvent \| KeyboardEvent \| SecurityPolicyViolationEvent \| TransitionEvent \| WheelEvent>) => void | Emits an event. | -| registerColumnPreProcessing | (processingName: string, columnsPreProcessing: GridColumnsPreProcessing) => GridColumnsPreProcessingCleanup | | +| registerColumnPreProcessing | (processingName: string, columnsPreProcessing: null \| GridColumnsPreProcessing) => void | | | resize | () => void | Triggers a resize of the component and recalculation of width and height. | | scroll | (params: Partial<GridScrollParams>) => void | Triggers the viewport to scroll to the given positions (in pixels). | | scrollToIndexes | (params: Partial<GridCellIndexCoordinates>) => boolean | Triggers the viewport to scroll to the cell at indexes given by `params`.
Returns `true` if the grid had to scroll to reach the target. | diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx new file mode 100644 index 0000000000000..b6137c8df603c --- /dev/null +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -0,0 +1,129 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { makeStyles } from '@mui/styles'; +import IconButton from '@mui/material/IconButton'; +import Typography from '@mui/material/Typography'; +import Box from '@mui/material/Box'; +import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; +import { useGridApiContext } from '../../hooks/root/useGridApiContext'; +import { GridRenderCellParams } from '../../models/params/gridCellParams'; + +const useStyles = makeStyles({ + root: { + display: 'flex', + alignItems: 'center', + width: '100%', + }, + toggle: { + flex: '0 0 28px', + alignSelf: 'stretch', + marginRight: 16, + }, +}); + +const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { + const { id } = props; + + const rootProps = useGridRootProps(); + const apiRef = useGridApiContext(); + const classes = useStyles(); + const node = apiRef.current.getRowNode(id); + + const Icon = node?.expanded + ? rootProps.components.TreeDataCollapseIcon + : rootProps.components.TreeDataExpandIcon; + + const path = apiRef.current.getRowPath(id); + + if (!node || !path) { + throw new Error(`No row with id #${id} found`); + } + + return ( + +
+ {!!node.children?.size && ( + apiRef.current.setRowExpansion(id, !node?.expanded)} + > + + + )} +
+ {path[path.length - 1]} +
+ ); +}; + +GridTreeDataGroupingCell.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "yarn proptypes" | + // ---------------------------------------------------------------------- + /** + * GridApi that let you manipulate the grid. + */ + api: PropTypes.any.isRequired, + /** + * The mode of the cell. + */ + cellMode: PropTypes.oneOf(['edit', 'view']).isRequired, + /** + * The column of the row that the current cell belongs to. + */ + colDef: PropTypes.object.isRequired, + /** + * The column field of the cell that triggered the event + */ + field: PropTypes.string.isRequired, + /** + * The cell value formatted with the column valueFormatter. + */ + formattedValue: PropTypes.oneOfType([ + PropTypes.instanceOf(Date), + PropTypes.number, + PropTypes.object, + PropTypes.string, + PropTypes.bool, + ]), + /** + * Get the cell value of a row and field. + * @param {GridRowId} id The row id. + * @param {string} field The field. + * @returns {GridCellValue} The cell value. + */ + getValue: PropTypes.func.isRequired, + /** + * If true, the cell is the active element. + */ + hasFocus: PropTypes.bool.isRequired, + /** + * The grid row id. + */ + id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + /** + * If true, the cell is editable. + */ + isEditable: PropTypes.bool, + /** + * The row model of the row that the current cell belongs to. + */ + row: PropTypes.object.isRequired, + /** + * the tabIndex value. + */ + tabIndex: PropTypes.oneOf([-1, 0]).isRequired, + /** + * The cell value, but if the column has valueGetter, use getValue. + */ + value: PropTypes.oneOfType([ + PropTypes.instanceOf(Date), + PropTypes.number, + PropTypes.object, + PropTypes.string, + PropTypes.bool, + ]), +} as any; + +export { GridTreeDataGroupingCell }; diff --git a/packages/grid/_modules_/grid/components/cell/index.ts b/packages/grid/_modules_/grid/components/cell/index.ts index b5c4d88140fb1..8b24b4e5c5b10 100644 --- a/packages/grid/_modules_/grid/components/cell/index.ts +++ b/packages/grid/_modules_/grid/components/cell/index.ts @@ -5,3 +5,4 @@ export * from './GridEmptyCell'; export * from './GridRowCells'; export * from './GridActionsCell'; export * from './GridActionsCellItem'; +export * from './GridTreeDataGroupingCell'; diff --git a/packages/grid/_modules_/grid/components/icons/index.tsx b/packages/grid/_modules_/grid/components/icons/index.tsx index a38a887757af9..568a9ad28e985 100644 --- a/packages/grid/_modules_/grid/components/icons/index.tsx +++ b/packages/grid/_modules_/grid/components/icons/index.tsx @@ -11,6 +11,16 @@ export const GridArrowDownwardIcon = createSvgIcon( 'ArrowDownward', ); +export const GridExpandMoreIcon = createSvgIcon( + , + 'ExpandMore', +); + +export const GridExpandLessIcon = createSvgIcon( + , + 'ExpandMore', +); + export const GridFilterListIcon = createSvgIcon( , 'FilterList', diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index cf21badab793c..d3ae707bb6abf 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -20,7 +20,7 @@ export const gridSortedVisibleRowsSelector = createSelector( if (visibleRowsState.visibleRowsLookup[id] !== false) { filteredRows.set(id, { node: row.node, - children: removeHiddenRows(row.children), + children: row.children ? removeHiddenRows(row.children) : undefined, }); } }); @@ -40,7 +40,7 @@ export const gridSortedVisibleRowsFlatSelector = createSelector( ): { id: GridRowId; node: GridRowModel }[] => Array.from(nodes.entries()).flatMap(([id, row]) => [ { id, node: row.node }, - ...flattenRowIds(row.children), + ...(row.children ? flattenRowIds(row.children) : []), ]); return flattenRowIds(rows); diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 4c8b13c9925c4..4ef719aac8831 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -110,7 +110,9 @@ export const useGridFilter = ( visibleRowsLookup[id] = isVisible; - filterRowTree(node.children); + if (node.children) { + filterRowTree(node.children); + } }); }; diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts index a93a0db9f5e15..c9aa68eaf0c0a 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts @@ -1,7 +1,6 @@ import { createSelector } from 'reselect'; -import { GridRowId, GridRowIdTree, GridRowModel } from '../../../models/gridRows'; +import { GridRowId, GridRowConfigTree, GridRowModel } from '../../../models/gridRows'; import { GridState } from '../core/gridState'; -import { GridRowsState } from './gridRowsState'; export type GridRowsLookup = Record; @@ -9,24 +8,25 @@ export const gridRowsStateSelector = (state: GridState) => state.rows; export const gridRowCountSelector = createSelector( gridRowsStateSelector, - (rows: GridRowsState) => rows.totalRowCount, + (rows) => rows.totalRowCount, ); export const gridRowsLookupSelector = createSelector( gridRowsStateSelector, - (rows: GridRowsState) => rows.idRowsLookup, + (rows) => rows.idRowsLookup, ); +export const gridRowsPathSelector = createSelector(gridRowsStateSelector, (rows) => rows.paths); + export const gridRowTreeSelector = createSelector(gridRowsStateSelector, (rows) => rows.tree); export const gridRowExpandedTreeSelector = createSelector(gridRowTreeSelector, (rowsTree) => { - const removeCollapsedNodes = (tree: GridRowIdTree) => { - const treeWithoutCollapsedChildren: GridRowIdTree = new Map(); + const removeCollapsedNodes = (tree: GridRowConfigTree) => { + const treeWithoutCollapsedChildren: GridRowConfigTree = new Map(); tree.forEach((node, id) => { - const children: GridRowIdTree = node.expanded - ? removeCollapsedNodes(node.children) - : new Map(); + const children: GridRowConfigTree | undefined = + node.expanded && node.children ? removeCollapsedNodes(node.children) : undefined; treeWithoutCollapsedChildren.set(id, { ...node, @@ -41,8 +41,11 @@ export const gridRowExpandedTreeSelector = createSelector(gridRowTreeSelector, ( }); export const gridRowIdsFlatSelector = createSelector(gridRowTreeSelector, (tree) => { - const flattenRowIds = (nodes: GridRowIdTree): GridRowId[] => - Array.from(nodes.values()).flatMap((node) => [node.id, ...flattenRowIds(node.children)]); + const flattenRowIds = (nodes: GridRowConfigTree): GridRowId[] => + Array.from(nodes.values()).flatMap((node) => [ + node.id, + ...(node.children ? flattenRowIds(node.children) : []), + ]); return flattenRowIds(tree); }); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts index 79e7bfa530dcf..1f9b540bbbcd2 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts @@ -1,9 +1,9 @@ -import { GridRowId, GridRowModel, GridRowIdTree } from '../../../models/gridRows'; +import { GridRowId, GridRowModel, GridRowConfigTree } from '../../../models/gridRows'; export interface GridRowsState { idRowsLookup: Record; paths: Record; - tree: GridRowIdTree; + tree: GridRowConfigTree; totalRowCount: number; } diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index d2180680bd2a7..fa55d7f52a02e 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -10,9 +10,9 @@ import { GridRowsProp, GridRowIdGetter, GridRowData, - GridRowIdTreeNode, + GridRowConfigTreeNode, GridRowGroupingResult, - GridRowIdTree, + GridRowConfigTree, } from '../../../models/gridRows'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; @@ -23,6 +23,7 @@ import { gridRowsLookupSelector, gridRowTreeSelector, gridRowIdsFlatSelector, + gridRowsPathSelector, } from './gridRowsSelector'; export type GridRowsInternalCacheState = Omit & { @@ -66,15 +67,38 @@ export function convertGridRowsPropToState( } const getFlatRowTree = (rowIds: GridRowId[]): GridRowGroupingResult => ({ - tree: new Map( - rowIds.map((id) => [id.toString(), { id, children: new Map() }]), + tree: new Map( + rowIds.map((id) => [id.toString(), { id, depth: 0 }]), ), paths: Object.fromEntries(rowIds.map((id) => [id, [id.toString()]])), }); +const getNodeFromTree = (tree: GridRowConfigTree, path: string[]): GridRowConfigTreeNode | null => { + if (path.length === 0) { + return null; + } + + const [nodeName, ...restPath] = path; + const node = tree.get(nodeName); + + if (!node) { + return null; + } + + if (restPath.length === 0) { + return node; + } + + if (!node.children) { + return null; + } + + return getNodeFromTree(node.children, restPath); +}; + const setRowExpansionInTree = ( id: GridRowId, - tree: GridRowIdTree, + tree: GridRowConfigTree, path: string[], isExpanded: boolean, ) => { @@ -93,6 +117,10 @@ const setRowExpansionInTree = ( if (restPath.length === 0) { clonedMap.set(nodeName, { ...nodeBefore, expanded: isExpanded }); } else { + if (!nodeBefore.children) { + throw new Error(`Material-UI: Invalid path for row #${id}.`); + } + clonedMap.set(nodeName, { ...nodeBefore, expanded: nodeBefore.expanded || isExpanded, @@ -296,36 +324,20 @@ export const useGridRows = ( [apiRef, setGridState, forceUpdate], ); - const isRowExpanded = React.useCallback( - (id) => { - const getNodeFromTree = (tree: GridRowIdTree, path: string[]): GridRowIdTreeNode => { - if (path.length === 0) { - throw new Error(`Material-UI: No row with id #${id} found in row tree`); - } - - const [nodeName, ...restPath] = path; - const node = tree.get(nodeName); - - if (!node) { - throw new Error(`Material-UI: No row with id #${id} found in row tree`); - } - - if (restPath.length === 0) { - return node; - } - - return getNodeFromTree(node.children, restPath); - }; + const getRowPath = React.useCallback( + (id) => gridRowsPathSelector(apiRef.current.state)[id] ?? null, + [apiRef], + ); - const path = apiRef.current.state.rows.paths[id]; + const getRowNode = React.useCallback( + (id) => { + const path = apiRef.current.getRowPath(id); if (!path) { throw new Error(`Material-UI: No row with id #${id} found in row path list`); } - const node = getNodeFromTree(apiRef.current.getRowModels(), path); - - return !!node.expanded; + return getNodeFromTree(apiRef.current.getRowModels(), path); }, [apiRef], ); @@ -356,8 +368,9 @@ export const useGridRows = ( getAllRowIds, setRows, updateRows, + getRowPath, setRowExpansion, - isRowExpanded, + getRowNode, }; useGridApiMethod(apiRef, rowApi, 'GridRowApi'); diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts index 0da551fd1e9ae..96edec62c9d72 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts @@ -16,7 +16,7 @@ export const gridSortedRowIdsFlatSelector = createSelector( gridSortedRowIdsSelector, (sortedRowIds) => { const flattenRowIds = (nodes: GridSortedRowsIdTreeNode[]): GridRowId[] => - nodes.flatMap((node) => [node.id, ...flattenRowIds(node.children)]); + nodes.flatMap((node) => [node.id, ...(node.children ? flattenRowIds(node.children) : [])]); return flattenRowIds(sortedRowIds); }, @@ -30,7 +30,10 @@ export const gridSortedRowsSelector = createSelector( const map = new Map(); nodes.forEach((node) => { - map.set(node.id, { node: idRowsLookup[node.id], children: buildMap(node.children) }); + map.set(node.id, { + node: idRowsLookup[node.id], + children: node.children ? buildMap(node.children) : undefined, + }); }); return map; diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts index d2c75a49ada45..b3810e1060127 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts @@ -3,12 +3,12 @@ import { GridSortModel } from '../../../models/gridSortModel'; export interface GridSortedRowsTreeNode { node: GridRowModel; - children: Map; + children?: Map; } export interface GridSortedRowsIdTreeNode { id: GridRowId; - children: GridSortedRowsIdTreeNode[]; + children?: GridSortedRowsIdTreeNode[]; } export interface GridSortingState { diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 706bfc29ca04e..9ab51420b8ab5 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -7,7 +7,7 @@ import { GridCellValue } from '../../../models/gridCell'; import { GridColDef } from '../../../models/colDef/gridColDef'; import { GridFeatureModeConstant } from '../../../models/gridFeatureMode'; import { GridColumnHeaderParams } from '../../../models/params/gridColumnHeaderParams'; -import { GridRowId, GridRowIdTree } from '../../../models/gridRows'; +import { GridRowId, GridRowConfigTree } from '../../../models/gridRows'; import { GridFieldComparatorList, GridSortItem, @@ -162,7 +162,7 @@ export const useGridSorting = ( const comparatorList = buildComparatorList(sortModel); const aggregatedComparator = comparatorListAggregate(comparatorList); - const sortRowTree = (tree: GridRowIdTree): GridSortedRowsIdTreeNode[] => { + const sortRowTree = (tree: GridRowConfigTree): GridSortedRowsIdTreeNode[] => { const rowsWithParams = Array.from(tree.entries()).map(([name, value]) => { const params = comparatorList.map((colComparator) => getSortCellParams(value.id, colComparator.field), @@ -177,7 +177,7 @@ export const useGridSorting = ( return sortedRowsWithParams.map((node) => ({ id: node.value.id, - children: sortRowTree(node.value.children), + children: node.value.children ? sortRowTree(node.value.children) : undefined, })); }; const sortedRows = sortRowTree(unsortedRowTree); diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx new file mode 100644 index 0000000000000..f2355013fe7de --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import { GridColDef } from '../../../models/colDef/gridColDef'; +import { GridTreeDataGroupingCell } from '../../../components/cell/GridTreeDataGroupingCell'; +import { GRID_STRING_COL_DEF } from '../../../models/colDef/gridStringColDef'; + +export const GridTreeDataGroupColDef: GridColDef = { + ...GRID_STRING_COL_DEF, + field: '__tree_data_group__', + sortable: false, + filterable: false, + disableColumnMenu: true, + disableReorder: true, + align: 'left', + width: 200, + valueGetter: () => true, + renderHeader: () => 'Header', + renderCell: (params) => , +}; diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataToggleExpansionColDef.tsx b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataToggleExpansionColDef.tsx deleted file mode 100644 index 9fa91d8b7c5e4..0000000000000 --- a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataToggleExpansionColDef.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import * as React from 'react'; -import IconButton from '@mui/material/IconButton'; - -import { GRID_BOOLEAN_COL_DEF } from '../../../models/colDef/gridBooleanColDef'; -import { GridColDef } from '../../../models/colDef/gridColDef'; -import { useGridRootProps } from '../../utils/useGridRootProps'; -import { useGridApiContext } from '../../root/useGridApiContext'; -import { GridRowId } from '../../../models/gridRows'; - -const TreeDataToggleIcon = ({ id }: { id: GridRowId }) => { - const rootProps = useGridRootProps(); - const apiRef = useGridApiContext(); - const isExpanded = apiRef.current.isRowExpanded(id); - - const Icon = isExpanded - ? rootProps.components.TreeDataCollapseIcon - : rootProps.components.TreeDataExpandIcon; - - return ( - apiRef.current.setRowExpansion(id, !isExpanded)}> - - - ); -}; - -export const GridTreeDataToggleExpansionColDef: GridColDef = { - ...GRID_BOOLEAN_COL_DEF, - field: '__check__', - sortable: false, - filterable: false, - disableColumnMenu: true, - disableReorder: true, - valueGetter: () => true, - renderHeader: () => 'Header', - renderCell: (params) => , -}; diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index c02f68a210292..97d5d9b726539 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -4,16 +4,21 @@ import { GridRowData, GridRowId, GridRowIdGetter, - GridRowIdTree, - GridRowIdTreeNode, + GridRowConfigTree, + GridRowConfigTreeNode, } from '../../../models/gridRows'; import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridComponentProps } from '../../../GridComponentProps'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { GridColumnsPreProcessing } from '../../root/columnsPreProcessing'; -import { GridTreeDataToggleExpansionColDef } from './gridTreeDataToggleExpansionColDef'; +import { GridTreeDataGroupColDef } from './gridTreeDataGroupColDef'; -const insertRowInTree = (tree: GridRowIdTree, id: GridRowId, path: string[]) => { +const insertRowInTree = ( + tree: GridRowConfigTree, + id: GridRowId, + path: string[], + depth: number = 0, +) => { if (path.length === 0) { throw new Error(`Material-UI: Could not insert row #${id} in the tree structure.`); } @@ -21,7 +26,7 @@ const insertRowInTree = (tree: GridRowIdTree, id: GridRowId, path: string[]) => if (path.length === 1) { tree.set(path[0], { id, - children: new Map(), + depth, }); } else { const [nodeName, ...restPath] = path; @@ -31,7 +36,11 @@ const insertRowInTree = (tree: GridRowIdTree, id: GridRowId, path: string[]) => throw new Error(`Material-UI: Could not insert row #${id} in the tree structure.`); } - insertRowInTree(parent.children, id, restPath); + if (!parent.children) { + parent.children = new Map(); + } + + insertRowInTree(parent.children, id, restPath, depth + 1); } }; @@ -51,8 +60,8 @@ export const useGridTreeData = ( (rowsLookup, rowIds) => { if (!props.treeData) { return { - tree: new Map( - rowIds.map((id) => [id.toString(), { id, children: new Map() }]), + tree: new Map( + rowIds.map((id) => [id.toString(), { id, depth: 0 }]), ), paths: Object.fromEntries(rowIds.map((id) => [id, [id.toString()]])), }; @@ -74,7 +83,7 @@ export const useGridTreeData = ( .sort((a, b) => a.path.length - b.path.length); const paths = Object.fromEntries(rows.map((row) => [row.id, row.path])); - const tree = new Map(); + const tree = new Map(); rows.forEach((row) => { insertRowInTree(tree, row.id, row.path); }); @@ -97,14 +106,14 @@ export const useGridTreeData = ( if (!props.treeData) { apiRef.current.registerColumnPreProcessing('treeData', null); } else { - const addCollapseColumn: GridColumnsPreProcessing = (columns) => [ + const addGroupingColumn: GridColumnsPreProcessing = (columns) => [ { - ...GridTreeDataToggleExpansionColDef, + ...GridTreeDataGroupColDef, }, ...columns, ]; - apiRef.current.registerColumnPreProcessing('treeData', addCollapseColumn); + apiRef.current.registerColumnPreProcessing('treeData', addGroupingColumn); } }, [apiRef, props.treeData]); }; diff --git a/packages/grid/_modules_/grid/hooks/utils/useProcessedProps.ts b/packages/grid/_modules_/grid/hooks/utils/useProcessedProps.ts index 4ff93d7e19551..55f8c9efc4a7d 100644 --- a/packages/grid/_modules_/grid/hooks/utils/useProcessedProps.ts +++ b/packages/grid/_modules_/grid/hooks/utils/useProcessedProps.ts @@ -29,6 +29,8 @@ import { GridViewHeadlineIcon, GridViewStreamIcon, GridMoreVertIcon, + GridExpandMoreIcon, + GridExpandLessIcon, } from '../../components'; import { GridColumnUnsortedIcon } from '../../components/columnHeaders/GridColumnUnsortedIcon'; import { ErrorOverlay } from '../../components/ErrorOverlay'; @@ -50,8 +52,8 @@ const DEFAULT_GRID_ICON_SLOTS_COMPONENTS: GridIconSlotsComponent = { DensityComfortableIcon: GridViewStreamIcon, ExportIcon: GridSaveAltIcon, MoreActionsIcon: GridMoreVertIcon, - TreeDataCollapseIcon: GridArrowUpwardIcon, - TreeDataExpandIcon: GridArrowDownwardIcon, + TreeDataCollapseIcon: GridExpandLessIcon, + TreeDataExpandIcon: GridExpandMoreIcon, }; export const DEFAULT_GRID_SLOTS_COMPONENTS: GridSlotsComponent = { diff --git a/packages/grid/_modules_/grid/models/api/gridRowApi.ts b/packages/grid/_modules_/grid/models/api/gridRowApi.ts index 18cd8202d81a0..2dc41eca6f8b0 100644 --- a/packages/grid/_modules_/grid/models/api/gridRowApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridRowApi.ts @@ -1,4 +1,10 @@ -import { GridRowModel, GridRowId, GridRowModelUpdate, GridRowIdTree } from '../gridRows'; +import { + GridRowModel, + GridRowId, + GridRowModelUpdate, + GridRowConfigTree, + GridRowConfigTreeNode, +} from '../gridRows'; /** * The Row API interface that is available in the grid `apiRef`. @@ -6,9 +12,9 @@ import { GridRowModel, GridRowId, GridRowModelUpdate, GridRowIdTree } from '../g export interface GridRowApi { /** * Gets the full set of rows ordered in a tree structure. - * @returns {GridRowIdTree} The full set of rows. + * @returns {GridRowConfigTree} The full set of rows. */ - getRowModels: () => GridRowIdTree; + getRowModels: () => GridRowConfigTree; /** * Gets the total number of rows in the grid. * @returns {number} The number of rows. @@ -48,14 +54,19 @@ export interface GridRowApi { */ getRow: (id: GridRowId) => GridRowModel | null; /** - * @param {GridRowId} id the ID of the row to toggle - * @param {boolean} isExpanded A boolean indicating if the row must be expanded + * Gets the row node from the internal tree structure. + * @param {GridRowId} id The id of the row. + * @returns {GridRowConfigTreeNode} The row data. + */ + getRowNode: (id: GridRowId) => GridRowConfigTreeNode | null; + /** + * @param {GridRowId} id the ID of the row to toggle. + * @param {boolean} isExpanded A boolean indicating if the row must be expanded. */ setRowExpansion: (id: GridRowId, isExpanded: boolean) => void; /** - * Determines if a row is expanded or not. - * @param {GridRowId} id The id of the row. - * @returns {boolean} A boolean indicating if the row is expanded. + * @param {GridRowId} id the ID of the row to toggle. + * @returns {string[] | null} path The path of the row. */ - isRowExpanded: (id: GridRowId) => boolean; + getRowPath: (id: GridRowId) => string[] | null; } diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index 4a6400b454638..b54e6a530f0b7 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -12,16 +12,17 @@ export interface GridRowModelUpdate extends GridRowData { _action?: GridUpdateAction; } -export interface GridRowIdTreeNode { +export interface GridRowConfigTreeNode { id: GridRowId; - children: GridRowIdTree; + children?: GridRowConfigTree; expanded?: boolean; + depth: number; } -export type GridRowIdTree = Map; +export type GridRowConfigTree = Map; export interface GridRowGroupingResult { - tree: GridRowIdTree; + tree: GridRowConfigTree; paths: Record; } diff --git a/packages/grid/_modules_/grid/models/params/gridSlotComponentProps.ts b/packages/grid/_modules_/grid/models/params/gridSlotComponentProps.ts index 6a1909bb57d36..6ea93f941ff56 100644 --- a/packages/grid/_modules_/grid/models/params/gridSlotComponentProps.ts +++ b/packages/grid/_modules_/grid/models/params/gridSlotComponentProps.ts @@ -2,7 +2,7 @@ import { GridState } from '../../hooks/features/core/gridState'; import { GridApiRef } from '../api/gridApiRef'; import { GridColumns } from '../colDef/gridColDef'; import { GridRootContainerRef } from '../gridRootContainerRef'; -import { GridRowIdTree } from '../gridRows'; +import { GridRowConfigTree } from '../gridRows'; /** * Object passed as React prop in the component override. @@ -15,7 +15,7 @@ export interface GridSlotComponentProps { /** * The full set of rows. */ - rows: GridRowIdTree; + rows: GridRowConfigTree; /** * The full set of columns. */ diff --git a/packages/storybook/src/stories/grid-tree-data.stories.tsx b/packages/storybook/src/stories/grid-tree-data.stories.tsx index 31c2fe3bdbde6..91fc490f3ebb5 100644 --- a/packages/storybook/src/stories/grid-tree-data.stories.tsx +++ b/packages/storybook/src/stories/grid-tree-data.stories.tsx @@ -27,12 +27,13 @@ const columns: GridColumns = [ }, { field: 'name', + width: 200, }, ]; const getTreeDataPath = (row) => row.name.split('.'); -export function TreeData() { +export function BasicTreeData() { const [treeDataEnabled, setTreeDataEnabled] = React.useState(true); return ( From 15fcafdb5ab9f26b190a9fbeaca9296e4dd629a6 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 16 Sep 2021 12:22:35 +0200 Subject: [PATCH 087/390] Work on group col --- .../grid/_modules_/grid/GridComponentProps.ts | 6 +++- .../cell/GridTreeDataGroupingCell.tsx | 1 + .../treeData/gridTreeDataGroupColDef.tsx | 2 +- .../features/treeData/useGridTreeData.ts | 21 ++++++++----- .../src/stories/grid-tree-data.stories.tsx | 31 ++++++++++++++++++- 5 files changed, 51 insertions(+), 10 deletions(-) diff --git a/packages/grid/_modules_/grid/GridComponentProps.ts b/packages/grid/_modules_/grid/GridComponentProps.ts index ad1e4406ba369..0a4e29f3501f3 100644 --- a/packages/grid/_modules_/grid/GridComponentProps.ts +++ b/packages/grid/_modules_/grid/GridComponentProps.ts @@ -1,7 +1,7 @@ import * as React from 'react'; import { GridState } from './hooks/features/core/gridState'; import { GridApiRef } from './models/api/gridApiRef'; -import { GridColumns } from './models/colDef/gridColDef'; +import {GridColDef, GridColumns} from './models/colDef/gridColDef'; import { GridSimpleOptions, GridProcessedMergedOptions, @@ -606,4 +606,8 @@ interface GridComponentOtherProps { * Overrideable components props dynamically passed to the component at rendering. */ componentsProps?: GridSlotsComponentsProps; + /** + * The grouping column used by the tree data + */ + groupingColDef?: Partial } diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index b6137c8df603c..453f69a139a3e 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -46,6 +46,7 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { apiRef.current.setRowExpansion(id, !node?.expanded)} + tabIndex={-1} > diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx index f2355013fe7de..2227283d4bd5a 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx +++ b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx @@ -6,6 +6,7 @@ import { GRID_STRING_COL_DEF } from '../../../models/colDef/gridStringColDef'; export const GridTreeDataGroupColDef: GridColDef = { ...GRID_STRING_COL_DEF, field: '__tree_data_group__', + headerName: 'Grouping', sortable: false, filterable: false, disableColumnMenu: true, @@ -13,6 +14,5 @@ export const GridTreeDataGroupColDef: GridColDef = { align: 'left', width: 200, valueGetter: () => true, - renderHeader: () => 'Header', renderCell: (params) => , }; diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 97d5d9b726539..74e4f2cd2aa99 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -54,7 +54,7 @@ function getGridRowId(rowData: GridRowData, getRowId?: GridRowIdGetter): GridRow */ export const useGridTreeData = ( apiRef: GridApiRef, - props: Pick, + props: Pick, ) => { const groupRows = React.useCallback( (rowsLookup, rowIds) => { @@ -106,14 +106,21 @@ export const useGridTreeData = ( if (!props.treeData) { apiRef.current.registerColumnPreProcessing('treeData', null); } else { - const addGroupingColumn: GridColumnsPreProcessing = (columns) => [ - { + const addGroupingColumn: GridColumnsPreProcessing = (columns) => { + const index = columns[0].type === 'checkboxSelection' ? 1 : 0 + const groupingColumn = { ...GridTreeDataGroupColDef, - }, - ...columns, - ]; + ...props.groupingColDef, + } + + return [ + ...columns.slice(0, index), + groupingColumn, + ...columns.slice(index) + ]; + }; apiRef.current.registerColumnPreProcessing('treeData', addGroupingColumn); } - }, [apiRef, props.treeData]); + }, [apiRef, props.treeData, props.groupingColDef]); }; diff --git a/packages/storybook/src/stories/grid-tree-data.stories.tsx b/packages/storybook/src/stories/grid-tree-data.stories.tsx index 91fc490f3ebb5..ec97c87539ce8 100644 --- a/packages/storybook/src/stories/grid-tree-data.stories.tsx +++ b/packages/storybook/src/stories/grid-tree-data.stories.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { DataGridPro, GridColumns } from '@mui/x-data-grid-pro'; +import { DataGridPro, GridColumns, DataGridProProps } from '@mui/x-data-grid-pro'; import { Meta } from '@storybook/react'; import Button from '@mui/material/Button'; @@ -55,3 +55,32 @@ export function BasicTreeData() { ); } + +export function CustomGroupingColumn() { + const groupingColDef = React.useMemo(() => ({ + headerName: 'Custom header' + }), []) + + return ( + + ) +} + +export function TreeDataWithCheckboxSelection() { + return ( + + ) +} \ No newline at end of file From 9c32189e65a22d20eff3e4c161a4a4ebf977aa38 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 16 Sep 2021 12:22:43 +0200 Subject: [PATCH 088/390] Prettier --- .../grid/_modules_/grid/GridComponentProps.ts | 4 +- .../features/treeData/useGridTreeData.ts | 10 ++-- .../src/stories/grid-tree-data.stories.tsx | 49 ++++++++++--------- 3 files changed, 31 insertions(+), 32 deletions(-) diff --git a/packages/grid/_modules_/grid/GridComponentProps.ts b/packages/grid/_modules_/grid/GridComponentProps.ts index 0a4e29f3501f3..358d167d9876b 100644 --- a/packages/grid/_modules_/grid/GridComponentProps.ts +++ b/packages/grid/_modules_/grid/GridComponentProps.ts @@ -1,7 +1,7 @@ import * as React from 'react'; import { GridState } from './hooks/features/core/gridState'; import { GridApiRef } from './models/api/gridApiRef'; -import {GridColDef, GridColumns} from './models/colDef/gridColDef'; +import { GridColDef, GridColumns } from './models/colDef/gridColDef'; import { GridSimpleOptions, GridProcessedMergedOptions, @@ -609,5 +609,5 @@ interface GridComponentOtherProps { /** * The grouping column used by the tree data */ - groupingColDef?: Partial + groupingColDef?: Partial; } diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 74e4f2cd2aa99..aa2cba4beb16d 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -107,17 +107,13 @@ export const useGridTreeData = ( apiRef.current.registerColumnPreProcessing('treeData', null); } else { const addGroupingColumn: GridColumnsPreProcessing = (columns) => { - const index = columns[0].type === 'checkboxSelection' ? 1 : 0 + const index = columns[0].type === 'checkboxSelection' ? 1 : 0; const groupingColumn = { ...GridTreeDataGroupColDef, ...props.groupingColDef, - } + }; - return [ - ...columns.slice(0, index), - groupingColumn, - ...columns.slice(index) - ]; + return [...columns.slice(0, index), groupingColumn, ...columns.slice(index)]; }; apiRef.current.registerColumnPreProcessing('treeData', addGroupingColumn); diff --git a/packages/storybook/src/stories/grid-tree-data.stories.tsx b/packages/storybook/src/stories/grid-tree-data.stories.tsx index ec97c87539ce8..5fe70845a75e5 100644 --- a/packages/storybook/src/stories/grid-tree-data.stories.tsx +++ b/packages/storybook/src/stories/grid-tree-data.stories.tsx @@ -57,30 +57,33 @@ export function BasicTreeData() { } export function CustomGroupingColumn() { - const groupingColDef = React.useMemo(() => ({ - headerName: 'Custom header' - }), []) + const groupingColDef = React.useMemo( + () => ({ + headerName: 'Custom header', + }), + [], + ); - return ( - - ) + return ( + + ); } export function TreeDataWithCheckboxSelection() { - return ( - - ) -} \ No newline at end of file + return ( + + ); +} From 227c7c2bb33e5a8ddeb89ff98684fd6bbf03ecc3 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 16 Sep 2021 12:23:05 +0200 Subject: [PATCH 089/390] Fix --- packages/grid/data-grid/src/DataGrid.tsx | 51 ++++++++++++++++++++++++ packages/grid/x-grid/src/DataGridPro.tsx | 51 ++++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/packages/grid/data-grid/src/DataGrid.tsx b/packages/grid/data-grid/src/DataGrid.tsx index 2de9e4920348b..a78336a80dd9b 100644 --- a/packages/grid/data-grid/src/DataGrid.tsx +++ b/packages/grid/data-grid/src/DataGrid.tsx @@ -203,6 +203,57 @@ DataGridRaw.propTypes = { * @returns {string[]} the path to the row */ getTreeDataPath: PropTypes.func, + /** + * The grouping column used by the tree data + */ + groupingColDef: PropTypes.shape({ + align: PropTypes.oneOf(['center', 'left', 'right']), + cellClassName: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + description: PropTypes.string, + disableColumnMenu: PropTypes.bool, + disableExport: PropTypes.bool, + disableReorder: PropTypes.bool, + editable: PropTypes.bool, + field: PropTypes.string, + filterable: PropTypes.bool, + filterOperators: PropTypes.arrayOf( + PropTypes.shape({ + getApplyFilterFn: PropTypes.func.isRequired, + InputComponent: PropTypes.elementType, + InputComponentProps: PropTypes.object, + label: PropTypes.string, + value: PropTypes.string.isRequired, + }), + ), + flex: PropTypes.number, + headerAlign: PropTypes.oneOf(['center', 'left', 'right']), + headerClassName: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + headerName: PropTypes.string, + hide: PropTypes.bool, + hideSortIcons: PropTypes.bool, + minWidth: PropTypes.number, + renderCell: PropTypes.func, + renderEditCell: PropTypes.func, + renderHeader: PropTypes.func, + resizable: PropTypes.bool, + sortable: PropTypes.bool, + sortComparator: PropTypes.func, + type: PropTypes.string, + valueFormatter: PropTypes.func, + valueGetter: PropTypes.func, + valueOptions: PropTypes.arrayOf( + PropTypes.oneOfType([ + PropTypes.number, + PropTypes.shape({ + label: PropTypes.string.isRequired, + value: PropTypes.any.isRequired, + }), + PropTypes.string, + ]).isRequired, + ), + valueParser: PropTypes.func, + width: PropTypes.number, + }), /** * Set the height in pixel of the column headers in the grid. * @default 56 diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index 8b394c3507360..63afbeaeb980c 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -249,6 +249,57 @@ DataGridProRaw.propTypes = { * @returns {string[]} the path to the row */ getTreeDataPath: PropTypes.func, + /** + * The grouping column used by the tree data + */ + groupingColDef: PropTypes.shape({ + align: PropTypes.oneOf(['center', 'left', 'right']), + cellClassName: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + description: PropTypes.string, + disableColumnMenu: PropTypes.bool, + disableExport: PropTypes.bool, + disableReorder: PropTypes.bool, + editable: PropTypes.bool, + field: PropTypes.string, + filterable: PropTypes.bool, + filterOperators: PropTypes.arrayOf( + PropTypes.shape({ + getApplyFilterFn: PropTypes.func.isRequired, + InputComponent: PropTypes.elementType, + InputComponentProps: PropTypes.object, + label: PropTypes.string, + value: PropTypes.string.isRequired, + }), + ), + flex: PropTypes.number, + headerAlign: PropTypes.oneOf(['center', 'left', 'right']), + headerClassName: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + headerName: PropTypes.string, + hide: PropTypes.bool, + hideSortIcons: PropTypes.bool, + minWidth: PropTypes.number, + renderCell: PropTypes.func, + renderEditCell: PropTypes.func, + renderHeader: PropTypes.func, + resizable: PropTypes.bool, + sortable: PropTypes.bool, + sortComparator: PropTypes.func, + type: PropTypes.string, + valueFormatter: PropTypes.func, + valueGetter: PropTypes.func, + valueOptions: PropTypes.arrayOf( + PropTypes.oneOfType([ + PropTypes.number, + PropTypes.shape({ + label: PropTypes.string.isRequired, + value: PropTypes.any.isRequired, + }), + PropTypes.string, + ]).isRequired, + ), + valueParser: PropTypes.func, + width: PropTypes.number, + }), /** * Set the height in pixel of the column headers in the grid. * @default 56 From f8338eb73b0ba6742d15332f1b91aed910a8ce19 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 17 Sep 2021 08:44:26 +0200 Subject: [PATCH 090/390] Fix --- .../grid/hooks/features/columnResize/useGridColumnResize.tsx | 5 ++++- packages/grid/x-grid/src/useDataGridProComponent.tsx | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/columnResize/useGridColumnResize.tsx b/packages/grid/_modules_/grid/hooks/features/columnResize/useGridColumnResize.tsx index e4b2e5c697528..20da06d3a6ee5 100644 --- a/packages/grid/_modules_/grid/hooks/features/columnResize/useGridColumnResize.tsx +++ b/packages/grid/_modules_/grid/hooks/features/columnResize/useGridColumnResize.tsx @@ -73,7 +73,10 @@ function trackFinger(event, currentTouchId): CursorCoordinates | boolean { * @requires useGridColumns (method, event) * TODO: improve experience for last column */ -export const useGridColumnResize = (apiRef: GridApiRef, props: GridComponentProps) => { +export const useGridColumnResize = ( + apiRef: GridApiRef, + props: GridComponentProps, +): GridComponentProps => { const logger = useGridLogger(apiRef, 'useGridColumnResize'); useGridStateInit(apiRef, (state) => ({ diff --git a/packages/grid/x-grid/src/useDataGridProComponent.tsx b/packages/grid/x-grid/src/useDataGridProComponent.tsx index 307edbea38239..487e5901d92f7 100644 --- a/packages/grid/x-grid/src/useDataGridProComponent.tsx +++ b/packages/grid/x-grid/src/useDataGridProComponent.tsx @@ -54,7 +54,7 @@ export const useDataGridProComponent = (apiRef: GridApiRef, props: GridComponent useGridFilter(apiRef, props); useGridDensity(apiRef, props); props = useGridColumnReorder(apiRef, props); - useGridColumnResize(apiRef, props); + props = useGridColumnResize(apiRef, props); useGridPageSize(apiRef, props); useGridPage(apiRef, props); useGridContainerProps(apiRef, props); From a4bd0e8d29567cb3860d8291d00e23479ce61908 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 17 Sep 2021 10:01:46 +0200 Subject: [PATCH 091/390] Add keyboard group toggle expansion --- .../features/filter/gridFilterSelector.ts | 1 + .../hooks/features/filter/useGridFilter.ts | 38 +++++++++++---- .../keyboard/useGridKeyboardNavigation.ts | 10 ++-- .../hooks/features/rows/gridRowsSelector.ts | 21 ++++++++ .../treeData/gridTreeDataGroupColDef.tsx | 1 - .../features/treeData/useGridTreeData.ts | 20 ++++++++ .../src/tests/treeData.DataGridPro.test.tsx | 48 +++++++++++++++++++ 7 files changed, 123 insertions(+), 16 deletions(-) create mode 100644 packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index d3ae707bb6abf..6d2c18e3275ce 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -59,6 +59,7 @@ export const gridVisibleRowCountSelector = createSelector( if (visibleRowsState.visibleRows == null) { return totalRowsCount; } + return visibleRowsState.visibleRows.length; }, ); diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 4ef719aac8831..93f7ece6164c4 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -14,7 +14,10 @@ import { filterableGridColumnsIdsSelector } from '../columns/gridColumnsSelector import { useGridSelector } from '../core/useGridSelector'; import { useGridState } from '../core/useGridState'; import { GridPreferencePanelsValue } from '../preferencesPanel/gridPreferencePanelsValue'; -import { gridSortedRowsSelector } from '../sorting/gridSortingSelector'; +import { + gridSortedRowIdsFlatSelector, + gridSortedRowsSelector, +} from '../sorting/gridSortingSelector'; import { getInitialGridFilterState } from './gridFilterModelState'; import { GridFilterModel } from '../../../models/gridFilterModel'; import { gridSortedVisibleRowsSelector } from './gridFilterSelector'; @@ -135,17 +138,32 @@ export const useGridFilter = ( ); const applyFilters = React.useCallback(() => { - if (props.filterMode === GridFeatureModeConstant.server) { - forceUpdate(); - return; - } + const { items, linkOperator } = apiRef.current.state.filter; - clearFilteredRows(); + const hasFilterToApply = props.filterMode === GridFeatureModeConstant.client && !!items.length; - const { items, linkOperator } = apiRef.current.state.filter; - items.forEach((filterItem) => { - apiRef.current.applyFilter(filterItem, linkOperator); - }); + if (hasFilterToApply) { + clearFilteredRows(); + + items.forEach((filterItem) => { + apiRef.current.applyFilter(filterItem, linkOperator); + }); + } else { + setGridState((state) => { + const rowIds = gridSortedRowIdsFlatSelector(state); + const visibleRowsLookup = Object.fromEntries(rowIds.map((rowId) => [rowId, true])); + const visibleRows = [...rowIds]; + + return { + ...state, + visibleRows: { + ...state.visibleRows, + visibleRowsLookup, + visibleRows, + }, + }; + }); + } forceUpdate(); }, [apiRef, clearFilteredRows, forceUpdate, props.filterMode]); diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts index 345864cf8b08f..dfe2b18108921 100644 --- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts +++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts @@ -19,7 +19,7 @@ import { gridContainerSizesSelector } from '../../root/gridContainerSizesSelecto import { visibleGridColumnsLengthSelector } from '../columns/gridColumnsSelector'; import { useGridSelector } from '../core/useGridSelector'; import { gridPaginationSelector } from '../pagination/gridPaginationSelector'; -import { gridRowCountSelector } from '../rows/gridRowsSelector'; +import { gridRowExpandedCountSelector } from '../rows/gridRowsSelector'; import { useGridLogger } from '../../utils/useGridLogger'; import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; import { GridComponentProps } from '../../../GridComponentProps'; @@ -76,7 +76,7 @@ export const useGridKeyboardNavigation = ( ): void => { const logger = useGridLogger(apiRef, 'useGridKeyboardNavigation'); const paginationState = useGridSelector(apiRef, gridPaginationSelector); - const totalRowCount = useGridSelector(apiRef, gridRowCountSelector); + const expandedRowCount = useGridSelector(apiRef, gridRowExpandedCountSelector); const colCount = useGridSelector(apiRef, visibleGridColumnsLengthSelector); const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowsFlatSelector); @@ -99,9 +99,9 @@ export const useGridKeyboardNavigation = ( const key = mapKey(event); const isCtrlPressed = event.ctrlKey || event.metaKey || event.shiftKey; - let rowCount = totalRowCount; + let rowCount = expandedRowCount; - if (props.pagination && totalRowCount > paginationState.pageSize) { + if (props.pagination && expandedRowCount > paginationState.pageSize) { rowCount = paginationState.pageSize * (paginationState.page + 1); } @@ -162,7 +162,7 @@ export const useGridKeyboardNavigation = ( [ apiRef, visibleSortedRows, - totalRowCount, + expandedRowCount, props.pagination, paginationState.pageSize, paginationState.page, diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts index c9aa68eaf0c0a..d5e9f8963002d 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts @@ -40,6 +40,27 @@ export const gridRowExpandedTreeSelector = createSelector(gridRowTreeSelector, ( return removeCollapsedNodes(rowsTree); }); +export const gridRowExpandedCountSelector = createSelector( + gridRowExpandedTreeSelector, + (expandedRows) => { + const countNodes = (tree: GridRowConfigTree) => { + let count: number = 0; + + tree.forEach((node) => { + count += 1; + + if (node.children) { + count += countNodes(node.children); + } + }); + + return count; + }; + + return countNodes(expandedRows); + }, +); + export const gridRowIdsFlatSelector = createSelector(gridRowTreeSelector, (tree) => { const flattenRowIds = (nodes: GridRowConfigTree): GridRowId[] => Array.from(nodes.values()).flatMap((node) => [ diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx index 2227283d4bd5a..188fd63e90761 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx +++ b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx @@ -13,6 +13,5 @@ export const GridTreeDataGroupColDef: GridColDef = { disableReorder: true, align: 'left', width: 200, - valueGetter: () => true, renderCell: (params) => , }; diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index aa2cba4beb16d..9e145371e6cd8 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -12,6 +12,11 @@ import { GridComponentProps } from '../../../GridComponentProps'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { GridColumnsPreProcessing } from '../../root/columnsPreProcessing'; import { GridTreeDataGroupColDef } from './gridTreeDataGroupColDef'; +import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; +import { GridEvents } from '../../../constants'; +import { GridCellModes, GridCellParams, MuiEvent } from '../../../models'; +import { isGridCellRoot } from '../../../utils/domUtils'; +import { isNavigationKey, isSpaceKey } from '../../../utils/keyboardUtils'; const insertRowInTree = ( tree: GridRowConfigTree, @@ -119,4 +124,19 @@ export const useGridTreeData = ( apiRef.current.registerColumnPreProcessing('treeData', addGroupingColumn); } }, [apiRef, props.treeData, props.groupingColDef]); + + const handleCellKeyDown = React.useCallback( + (params: GridCellParams, event: MuiEvent) => { + // Get the most recent params because the cell mode may have changed by another listener + const cellParams = apiRef.current.getCellParams(params.id, params.field); + + if (cellParams.field === '__tree_data_group__' && isSpaceKey(event.key)) { + event.stopPropagation(); + apiRef.current.setRowExpansion(params.id, !apiRef.current.getRowNode(params.id)?.expanded); + } + }, + [apiRef], + ); + + useGridApiEventHandler(apiRef, GridEvents.cellKeyDown, handleCellKeyDown); }; diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx new file mode 100644 index 0000000000000..79bd368323296 --- /dev/null +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -0,0 +1,48 @@ +import { createClientRenderStrictMode } from 'test/utils'; +import { getColumnHeadersTextContent } from 'test/utils/helperFn'; +import * as React from 'react'; +import { expect } from 'chai'; +import { DataGridPro, DataGridProProps } from '@mui/x-data-grid-pro'; + +const baselineProps: DataGridProProps = { + rows: [ + { id: 0, name: 'A' }, + { id: 1, name: 'A.A' }, + { id: 2, name: 'B' }, + { id: 3, name: 'B.A' }, + { id: 4, name: 'B.B' }, + { id: 5, name: 'B.B.A' }, + { id: 6, name: 'C' }, + ], + columns: [ + { + field: 'id', + }, + { + field: 'name', + width: 200, + }, + ], + treeData: true, + getTreeDataPath: (row) => row.name.split('.'), +}; + +describe.only(' - Pagination', () => { + // TODO v5: replace with createClientRender + const render = createClientRenderStrictMode(); + + describe('grouping column', () => { + it('should add a grouping column', () => { + render( +
+ +
, + ); + + const columnsHeader = getColumnHeadersTextContent(); + + expect(columnsHeader).to.have.length(3); + expect(columnsHeader[0]).to.equal('Grouping'); + }); + }); +}); From 5f2846a30abb5d7cb4011c84873cdfdaeb0761d9 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 17 Sep 2021 10:03:05 +0200 Subject: [PATCH 092/390] Regen doc --- docs/pages/api-docs/data-grid/grid-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index 3d6e440182b05..0d3c952406571 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -56,7 +56,7 @@ import { GridApi } from '@mui/x-data-grid-pro'; | hidePreferences | () => void | Hides the preferences panel. | | isCellEditable | (params: GridCellParams) => boolean | Controls if a cell is editable. | | isRowSelected | (id: GridRowId) => boolean | Determines if a row is selected or not. | -| publishEvent | (name: string, params?: any, event?: MuiEvent<MouseEvent \| UIEvent \| Event \| SyntheticEvent<Element, Event> \| TouchEvent \| KeyboardEvent \| ErrorEvent \| ProgressEvent<EventTarget> \| ClipboardEvent \| AnimationEvent \| InputEvent \| FocusEvent \| CompositionEvent \| DragEvent \| FormDataEvent \| PointerEvent \| SecurityPolicyViolationEvent \| TransitionEvent \| WheelEvent>) => void | Emits an event. | +| publishEvent | (name: string, params?: any, event?: MuiEvent<MouseEvent \| UIEvent \| Event \| SyntheticEvent<Element, Event> \| TouchEvent \| ErrorEvent \| ProgressEvent<EventTarget> \| ClipboardEvent \| AnimationEvent \| InputEvent \| FocusEvent \| CompositionEvent \| DragEvent \| FormDataEvent \| PointerEvent \| KeyboardEvent \| SecurityPolicyViolationEvent \| TransitionEvent \| WheelEvent>) => void | Emits an event. | | resize | () => void | Triggers a resize of the component and recalculation of width and height. | | scroll | (params: Partial<GridScrollParams>) => void | Triggers the viewport to scroll to the given positions (in pixels). | | scrollToIndexes | (params: Partial<GridCellIndexCoordinates>) => boolean | Triggers the viewport to scroll to the cell at indexes given by `params`.
Returns `true` if the grid had to scroll to reach the target. | From 33bd3341768baf10bca4b7aca26532ef212abec3 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 17 Sep 2021 10:37:00 +0200 Subject: [PATCH 093/390] Add some tests --- .../cell/GridTreeDataGroupingCell.tsx | 5 +++ .../grid/constants/localeTextConstants.ts | 5 +++ .../treeData/gridTreeDataGroupColDef.tsx | 1 - .../features/treeData/useGridTreeData.ts | 7 ++-- packages/grid/_modules_/grid/locales/frFR.ts | 3 ++ .../grid/models/api/gridLocaleTextApi.ts | 5 +++ .../src/tests/treeData.DataGridPro.test.tsx | 41 +++++++++++++++---- 7 files changed, 53 insertions(+), 14 deletions(-) diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index 453f69a139a3e..c6b9d8a662e27 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -47,6 +47,11 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { size="small" onClick={() => apiRef.current.setRowExpansion(id, !node?.expanded)} tabIndex={-1} + aria-label={ + node.expanded + ? apiRef.current.getLocaleText('treeDataCollapse') + : apiRef.current.getLocaleText('treeDataExpand') + } >
diff --git a/packages/grid/_modules_/grid/constants/localeTextConstants.ts b/packages/grid/_modules_/grid/constants/localeTextConstants.ts index b2449d9ec144f..798241e5761d9 100644 --- a/packages/grid/_modules_/grid/constants/localeTextConstants.ts +++ b/packages/grid/_modules_/grid/constants/localeTextConstants.ts @@ -104,6 +104,11 @@ export const GRID_DEFAULT_LOCALE_TEXT: GridLocaleText = { // Actions cell more text actionsCellMore: 'more', + // Tree data + treeDataGroupingHeaderName: 'Group', + treeDataExpand: 'see children', + treeDataCollapse: 'hide children', + // Used core components translation keys MuiTablePagination: {}, }; diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx index 188fd63e90761..c3475599463e9 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx +++ b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx @@ -6,7 +6,6 @@ import { GRID_STRING_COL_DEF } from '../../../models/colDef/gridStringColDef'; export const GridTreeDataGroupColDef: GridColDef = { ...GRID_STRING_COL_DEF, field: '__tree_data_group__', - headerName: 'Grouping', sortable: false, filterable: false, disableColumnMenu: true, diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 9e145371e6cd8..f1cc5a163aa15 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -14,7 +14,7 @@ import { GridColumnsPreProcessing } from '../../root/columnsPreProcessing'; import { GridTreeDataGroupColDef } from './gridTreeDataGroupColDef'; import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; import { GridEvents } from '../../../constants'; -import { GridCellModes, GridCellParams, MuiEvent } from '../../../models'; +import { GridCellModes, GridCellParams, GridColDef, MuiEvent } from '../../../models'; import { isGridCellRoot } from '../../../utils/domUtils'; import { isNavigationKey, isSpaceKey } from '../../../utils/keyboardUtils'; @@ -113,8 +113,9 @@ export const useGridTreeData = ( } else { const addGroupingColumn: GridColumnsPreProcessing = (columns) => { const index = columns[0].type === 'checkboxSelection' ? 1 : 0; - const groupingColumn = { + const groupingColumn: GridColDef = { ...GridTreeDataGroupColDef, + headerName: apiRef.current.getLocaleText('treeDataGroupingHeaderName'), ...props.groupingColDef, }; @@ -127,9 +128,7 @@ export const useGridTreeData = ( const handleCellKeyDown = React.useCallback( (params: GridCellParams, event: MuiEvent) => { - // Get the most recent params because the cell mode may have changed by another listener const cellParams = apiRef.current.getCellParams(params.id, params.field); - if (cellParams.field === '__tree_data_group__' && isSpaceKey(event.key)) { event.stopPropagation(); apiRef.current.setRowExpansion(params.id, !apiRef.current.getRowNode(params.id)?.expanded); diff --git a/packages/grid/_modules_/grid/locales/frFR.ts b/packages/grid/_modules_/grid/locales/frFR.ts index acff50d71e1a5..005352eeeec0b 100644 --- a/packages/grid/_modules_/grid/locales/frFR.ts +++ b/packages/grid/_modules_/grid/locales/frFR.ts @@ -96,6 +96,9 @@ const frFRGrid: Partial = { // Actions cell more text actionsCellMore: 'Plus', + + // Tree data + treeDataGroupingHeaderName: 'Groupe', }; export const frFR: Localization = getGridLocalization(frFRGrid, frFRCore); diff --git a/packages/grid/_modules_/grid/models/api/gridLocaleTextApi.ts b/packages/grid/_modules_/grid/models/api/gridLocaleTextApi.ts index 631b05a16cc9e..59b5b04d92ac2 100644 --- a/packages/grid/_modules_/grid/models/api/gridLocaleTextApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridLocaleTextApi.ts @@ -101,6 +101,11 @@ export interface GridLocaleText { // Actions cell more text actionsCellMore: string; + // Tree data + treeDataGroupingHeaderName: string; + treeDataExpand: string; + treeDataCollapse: string; + // Used core components translation keys MuiTablePagination: Omit< ComponentsPropsList['MuiTablePagination'], diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index 79bd368323296..49caafb0219d5 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -1,5 +1,5 @@ -import { createClientRenderStrictMode } from 'test/utils'; -import { getColumnHeadersTextContent } from 'test/utils/helperFn'; +import { createClientRenderStrictMode, fireEvent, screen } from 'test/utils'; +import { getCell, getColumnHeadersTextContent, getColumnValues } from 'test/utils/helperFn'; import * as React from 'react'; import { expect } from 'chai'; import { DataGridPro, DataGridProProps } from '@mui/x-data-grid-pro'; @@ -31,18 +31,41 @@ describe.only(' - Pagination', () => { // TODO v5: replace with createClientRender const render = createClientRenderStrictMode(); + const Test = () => ( +
+ +
+ ); + describe('grouping column', () => { it('should add a grouping column', () => { - render( -
- -
, - ); + render(); const columnsHeader = getColumnHeadersTextContent(); - expect(columnsHeader).to.have.length(3); - expect(columnsHeader[0]).to.equal('Grouping'); + expect(columnsHeader[0]).to.equal('Group'); + }); + + it('should toggle expansion when clicking on grouping column icon', () => { + render(); + + expect(getColumnValues(1)).to.deep.equal(['0', '2', '6']); + fireEvent.click(getCell(0, 0).querySelector('button')); + expect(getColumnValues(1)).to.deep.equal(['0', '1', '2', '6']); + fireEvent.click(getCell(0, 0).querySelector('button')); + expect(getColumnValues(1)).to.deep.equal(['0', '2', '6']); + }); + + it('should toggle expansion when pressing Space while focusing grouping column', () => { + render(); + + expect(getColumnValues(1)).to.deep.equal(['0', '2', '6']); + getCell(0, 0).focus(); + expect(getColumnValues(1)).to.deep.equal(['0', '2', '6']); + fireEvent.keyDown(getCell(0, 0), { key: ' ' }); + expect(getColumnValues(1)).to.deep.equal(['0', '1', '2', '6']); + fireEvent.keyDown(getCell(0, 0), { key: ' ' }); + expect(getColumnValues(1)).to.deep.equal(['0', '2', '6']); }); }); }); From 23ce90e41905ff7add1be4e7a2cdcb78a70674f4 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 17 Sep 2021 10:51:31 +0200 Subject: [PATCH 094/390] Add tree data pagination story --- .../grid/hooks/root/useGridContainerProps.ts | 1 + .../src/stories/grid-tree-data.stories.tsx | 101 ++++++++++-------- 2 files changed, 58 insertions(+), 44 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts b/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts index 6a4c3ab41f2e1..e1197e834f3f3 100644 --- a/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts +++ b/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts @@ -19,6 +19,7 @@ import { gridPaginationSelector } from '../features/pagination/gridPaginationSel import { useGridLogger } from '../utils/useGridLogger'; import { useGridApiEventHandler } from './useGridApiEventHandler'; import { GridComponentProps } from '../../GridComponentProps'; +import { height } from '@material-ui/monorepo/packages/mui-system'; function getScrollbarSize(doc: Document, element: HTMLElement): number { const scrollDiv = doc.createElement('div'); diff --git a/packages/storybook/src/stories/grid-tree-data.stories.tsx b/packages/storybook/src/stories/grid-tree-data.stories.tsx index 5fe70845a75e5..2a851c8b02ab6 100644 --- a/packages/storybook/src/stories/grid-tree-data.stories.tsx +++ b/packages/storybook/src/stories/grid-tree-data.stories.tsx @@ -11,27 +11,54 @@ export default { }, } as Meta; -const rows = [ - { id: 0, name: 'A' }, - { id: 1, name: 'A.A' }, - { id: 2, name: 'B' }, - { id: 3, name: 'B.A' }, - { id: 4, name: 'B.B' }, - { id: 5, name: 'B.B.A' }, - { id: 6, name: 'C' }, -]; - -const columns: GridColumns = [ - { - field: 'id', - }, - { - field: 'name', - width: 200, - }, -]; - -const getTreeDataPath = (row) => row.name.split('.'); +const baselineProps: DataGridProProps = { + rows: [ + { name: 'A' }, + { name: 'A.A' }, + { name: 'B' }, + { name: 'B.A' }, + { name: 'B.B' }, + { name: 'B.B.A' }, + { name: 'C' }, + { name: 'D' }, + { name: 'D.A' }, + { name: 'D.B' }, + { name: 'D.C' }, + { name: 'D.D' }, + { name: 'D.E' }, + { name: 'D.F' }, + { name: 'D.G' }, + { name: 'D.H' }, + { name: 'D.I' }, + { name: 'D.J' }, + { name: 'D.K' }, + { name: 'E' }, + { name: 'F' }, + { name: 'F.A' }, + { name: 'F.A.A' }, + { name: 'F.A.B' }, + { name: 'F.A.C' }, + { name: 'F.A.D' }, + { name: 'F.A.E' }, + { name: 'G' }, + { name: 'H' }, + { name: 'I' }, + { name: 'J' }, + ], + columns: [ + { + field: 'id', + }, + { + field: 'name', + width: 200, + }, + ], + treeData: true, + disableSelectionOnClick: true, + getRowId: (row) => row.name, + getTreeDataPath: (row) => row.name.split('.'), +}; export function BasicTreeData() { const [treeDataEnabled, setTreeDataEnabled] = React.useState(true); @@ -46,12 +73,7 @@ export function BasicTreeData() { > {treeDataEnabled ? 'Disable tree data' : 'Enable tree data'} - + ); } @@ -64,26 +86,17 @@ export function CustomGroupingColumn() { [], ); - return ( - - ); + return ; } export function TreeDataWithCheckboxSelection() { + return ; +} + +export function TreeDataPagination() { return ( - +
+ +
); } From c7bf76e4c1e5cfd2824a520607cb307f4daaf901 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 17 Sep 2021 13:08:41 +0200 Subject: [PATCH 095/390] Add pagination treeData test --- .../src/tests/treeData.DataGridPro.test.tsx | 56 ++++++++++++------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index 49caafb0219d5..b65be7402b403 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -1,4 +1,9 @@ -import { createClientRenderStrictMode, fireEvent, screen } from 'test/utils'; +import { + createClientRenderStrictMode, + // @ts-expect-error need to migrate helpers to TypeScript + fireEvent, + screen, +} from 'test/utils'; import { getCell, getColumnHeadersTextContent, getColumnValues } from 'test/utils/helperFn'; import * as React from 'react'; import { expect } from 'chai'; @@ -6,18 +11,15 @@ import { DataGridPro, DataGridProProps } from '@mui/x-data-grid-pro'; const baselineProps: DataGridProProps = { rows: [ - { id: 0, name: 'A' }, - { id: 1, name: 'A.A' }, - { id: 2, name: 'B' }, - { id: 3, name: 'B.A' }, - { id: 4, name: 'B.B' }, - { id: 5, name: 'B.B.A' }, - { id: 6, name: 'C' }, + { name: 'A' }, + { name: 'A.A' }, + { name: 'B' }, + { name: 'B.A' }, + { name: 'B.B' }, + { name: 'B.B.A' }, + { name: 'C' }, ], columns: [ - { - field: 'id', - }, { field: 'name', width: 200, @@ -25,15 +27,16 @@ const baselineProps: DataGridProProps = { ], treeData: true, getTreeDataPath: (row) => row.name.split('.'), + getRowId: (row) => row.name, }; describe.only(' - Pagination', () => { // TODO v5: replace with createClientRender const render = createClientRenderStrictMode(); - const Test = () => ( + const Test = (props: Partial) => (
- +
); @@ -42,30 +45,41 @@ describe.only(' - Pagination', () => { render(); const columnsHeader = getColumnHeadersTextContent(); - expect(columnsHeader).to.have.length(3); + expect(columnsHeader).to.have.length(baselineProps.columns.length + 1); expect(columnsHeader[0]).to.equal('Group'); }); it('should toggle expansion when clicking on grouping column icon', () => { render(); - expect(getColumnValues(1)).to.deep.equal(['0', '2', '6']); + expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); fireEvent.click(getCell(0, 0).querySelector('button')); - expect(getColumnValues(1)).to.deep.equal(['0', '1', '2', '6']); + expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'B', 'C']); fireEvent.click(getCell(0, 0).querySelector('button')); - expect(getColumnValues(1)).to.deep.equal(['0', '2', '6']); + expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); }); it('should toggle expansion when pressing Space while focusing grouping column', () => { render(); - expect(getColumnValues(1)).to.deep.equal(['0', '2', '6']); + expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); getCell(0, 0).focus(); - expect(getColumnValues(1)).to.deep.equal(['0', '2', '6']); + expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); fireEvent.keyDown(getCell(0, 0), { key: ' ' }); - expect(getColumnValues(1)).to.deep.equal(['0', '1', '2', '6']); + expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'B', 'C']); fireEvent.keyDown(getCell(0, 0), { key: ' ' }); - expect(getColumnValues(1)).to.deep.equal(['0', '2', '6']); + expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); + }); + }); + + describe('pagination', () => { + it('should respect the pageSize when toggling children expansion', () => { + render(); + expect(getColumnValues(1)).to.deep.equal(['A', 'B']); + fireEvent.click(getCell(0, 0).querySelector('button')); + expect(getColumnValues(1)).to.deep.equal(['A', 'A.A']); + fireEvent.click(screen.getByRole('button', { name: /next page/i })); + expect(getColumnValues(1)).to.deep.equal(['B', 'C']); }); }); }); From 3fcf09c8877858cf9aa85ff33af23c4c82f51b0f Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 17 Sep 2021 15:55:37 +0200 Subject: [PATCH 096/390] Use col preprocessing for checkbox selection --- .../hooks/features/columns/useGridColumns.ts | 72 +--------------- .../features/selection/useGridSelection.ts | 85 ++++++++++++++----- .../src/tests/pagination.DataGrid.test.tsx | 2 +- .../data-grid/src/useDataGridComponent.tsx | 6 +- .../src/tests/treeData.DataGridPro.test.tsx | 3 +- .../x-grid/src/useDataGridProComponent.tsx | 4 +- 6 files changed, 76 insertions(+), 96 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts index 99045233bd493..00d3708daad47 100644 --- a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts +++ b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts @@ -2,7 +2,6 @@ import * as React from 'react'; import { GridEvents } from '../../../constants/eventsConstants'; import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridColumnApi } from '../../../models/api/gridColumnApi'; -import { gridCheckboxSelectionColDef } from '../../../models/colDef/gridCheckboxSelection'; import { GridColDef, GridColumns, @@ -17,7 +16,6 @@ import { mergeGridColTypes } from '../../../utils/mergeUtils'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; import { useGridSelector } from '../core/useGridSelector'; -import { GridLocaleText, GridTranslationKeys } from '../../../models/api/gridLocaleTextApi'; import { useGridState } from '../core/useGridState'; import { allGridColumnsSelector, @@ -27,8 +25,6 @@ import { import { useGridApiEventHandler, useGridApiOptionHandler } from '../../root/useGridApiEventHandler'; import { GRID_STRING_COL_DEF } from '../../../models/colDef/gridStringColDef'; import { GridComponentProps } from '../../../GridComponentProps'; -import { getDataGridUtilityClass } from '../../../gridClasses'; -import { composeClasses } from '../../../utils/material-ui-utils'; type RawGridColumnsState = Omit & { lookup: { [field: string]: GridColDef | GridStateColDef }; @@ -84,9 +80,6 @@ function hydrateColumnsWidth( function hydrateColumnsType( columns: GridColumns, columnTypes: GridColumnTypesRecord = {}, - getLocaleText: (key: T) => GridLocaleText[T], - checkboxSelection: boolean, - classes: Record<'cellCheckbox' | 'columnHeaderCheckbox', string>, ): GridColumns { const mergedColTypes = mergeGridColTypes(getGridDefaultColumnTypes(), columnTypes); const extendedColumns = columns.map((column) => ({ @@ -94,18 +87,6 @@ function hydrateColumnsType( ...column, })); - if (checkboxSelection) { - return [ - { - ...gridCheckboxSelectionColDef, - cellClassName: classes.cellCheckbox, - columnHeaderCheckbox: classes.columnHeaderCheckbox, - headerName: getLocaleText('checkboxSelectionHeaderName'), - }, - ...extendedColumns, - ]; - } - return extendedColumns; } @@ -128,21 +109,6 @@ const upsertColumnsState = (columnUpdates: GridColDef[], prevColumnsState?: Grid return newState; }; -type OwnerState = { classes: GridComponentProps['classes'] }; - -const useUtilityClasses = (ownerState: OwnerState) => { - const { classes } = ownerState; - - return React.useMemo(() => { - const slots = { - cellCheckbox: ['cellCheckbox'], - columnHeaderCheckbox: ['columnHeaderCheckbox'], - }; - - return composeClasses(slots, getDataGridUtilityClass, classes); - }, [classes]); -}; - /** * @requires useGridParamsApi (method) * TODO: Impossible priority - useGridParamsApi also needs to be after useGridColumns @@ -159,8 +125,6 @@ export function useGridColumns( const columnsMeta = useGridSelector(apiRef, gridColumnsMetaSelector); const allColumns = useGridSelector(apiRef, allGridColumnsSelector); const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); - const ownerState = { classes: props.classes }; - const classes = useUtilityClasses(ownerState); const setGridColumnsState = React.useCallback( (columnsState: GridColumnsState, emit = true) => { @@ -323,26 +287,12 @@ export function useGridColumns( React.useEffect(() => { logger.info(`GridColumns have changed, new length ${props.columns.length}`); - const hydratedColumns = hydrateColumnsType( - props.columns, - props.columnTypes, - apiRef.current.getLocaleText, - props.checkboxSelection, - classes, - ); + const hydratedColumns = hydrateColumnsType(props.columns, props.columnTypes); const preProcessedColumns = apiRef.current.applyAllColumnPreProcessing(hydratedColumns); const columnState = upsertColumnsState(preProcessedColumns); setColumnsState(columnState); - }, [ - logger, - apiRef, - setColumnsState, - props.columns, - props.columnTypes, - props.checkboxSelection, - classes, - ]); + }, [logger, apiRef, setColumnsState, props.columns, props.columnTypes]); React.useEffect(() => { logger.debug( @@ -362,26 +312,12 @@ export function useGridColumns( const handlePreProcessColumns = React.useCallback(() => { logger.info(`Columns pre-processing have changed, regenerating the columns`); - const hydratedColumns = hydrateColumnsType( - props.columns, - props.columnTypes, - apiRef.current.getLocaleText, - props.checkboxSelection, - classes, - ); + const hydratedColumns = hydrateColumnsType(props.columns, props.columnTypes); const preProcessedColumns = apiRef.current.applyAllColumnPreProcessing(hydratedColumns); const columnState = upsertColumnsState(preProcessedColumns); setColumnsState(columnState); - }, [ - apiRef, - logger, - classes, - setColumnsState, - props.columns, - props.columnTypes, - props.checkboxSelection, - ]); + }, [apiRef, logger, setColumnsState, props.columns, props.columnTypes]); useGridApiEventHandler(apiRef, GridEvents.columnsPreProcessingChange, handlePreProcessColumns); diff --git a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts index dfd9d13830ac0..238366cf403a0 100644 --- a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts +++ b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts @@ -8,7 +8,6 @@ import { GridRowId } from '../../../models/gridRows'; import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; -import { useGridSelector } from '../core/useGridSelector'; import { useGridState } from '../core/useGridState'; import { gridRowsLookupSelector } from '../rows/gridRowsSelector'; import { @@ -16,6 +15,25 @@ import { selectedGridRowsSelector, selectedIdsLookupSelector, } from './gridSelectionSelector'; +import { GridColumnsPreProcessing } from '../../root/columnsPreProcessing'; +import { gridCheckboxSelectionColDef, GridColDef } from '../../../models'; +import { composeClasses } from '../../../utils/material-ui-utils'; +import { getDataGridUtilityClass } from '../../../gridClasses'; + +type OwnerState = { classes: GridComponentProps['classes'] }; + +const useUtilityClasses = (ownerState: OwnerState) => { + const { classes } = ownerState; + + return React.useMemo(() => { + const slots = { + cellCheckbox: ['cellCheckbox'], + columnHeaderCheckbox: ['columnHeaderCheckbox'], + }; + + return composeClasses(slots, getDataGridUtilityClass, classes); + }, [classes]); +}; /** * @requires useGridRows (state, method) @@ -25,7 +43,9 @@ import { export const useGridSelection = (apiRef: GridApiRef, props: GridComponentProps): void => { const logger = useGridLogger(apiRef, 'useGridSelection'); const [, setGridState, forceUpdate] = useGridState(apiRef); - const rowsLookup = useGridSelector(apiRef, gridRowsLookupSelector); + + const ownerState = { classes: props.classes }; + const classes = useUtilityClasses(ownerState); const propSelectionModel = React.useMemo(() => { if (props.selectionModel == null) { @@ -175,26 +195,6 @@ export const useGridSelection = (apiRef: GridApiRef, props: GridComponentProps): }); }, [apiRef, props.onSelectionModelChange, propSelectionModel]); - React.useEffect(() => { - // Rows changed - const currentSelection = gridSelectionStateSelector(apiRef.current.state); - - // We clone the existing object to avoid mutating the same object returned by the selector to others part of the project - const selectionLookup = { ...selectedIdsLookupSelector(apiRef.current.state) }; - - let hasChanged = false; - currentSelection.forEach((id: GridRowId) => { - if (!rowsLookup[id]) { - delete selectionLookup[id]; - hasChanged = true; - } - }); - - if (hasChanged) { - apiRef.current.setSelectionModel(Object.values(selectionLookup)); - } - }, [rowsLookup, apiRef]); - React.useEffect(() => { if (propSelectionModel === undefined) { return; @@ -217,4 +217,45 @@ export const useGridSelection = (apiRef: GridApiRef, props: GridComponentProps): } } }, [apiRef, isRowSelectable]); + + React.useEffect(() => { + if (!props.checkboxSelection) { + apiRef.current.registerColumnPreProcessing('selection', null); + } else { + const addCheckboxColumn: GridColumnsPreProcessing = (columns) => { + const groupingColumn: GridColDef = { + ...gridCheckboxSelectionColDef, + cellClassName: classes.cellCheckbox, + headerClassName: classes.columnHeaderCheckbox, + headerName: apiRef.current.getLocaleText('checkboxSelectionHeaderName'), + }; + + return [groupingColumn, ...columns]; + }; + + apiRef.current.registerColumnPreProcessing('selection', addCheckboxColumn); + } + }, [apiRef, props.checkboxSelection, classes]); + + const removeOutdatedSelection = React.useCallback(() => { + const currentSelection = gridSelectionStateSelector(apiRef.current.state); + const rowsLookup = gridRowsLookupSelector(apiRef.current.state); + + // We clone the existing object to avoid mutating the same object returned by the selector to others part of the project + const selectionLookup = { ...selectedIdsLookupSelector(apiRef.current.state) }; + + let hasChanged = false; + currentSelection.forEach((id: GridRowId) => { + if (!rowsLookup[id]) { + delete selectionLookup[id]; + hasChanged = true; + } + }); + + if (hasChanged) { + apiRef.current.setSelectionModel(Object.values(selectionLookup)); + } + }, [apiRef]); + + useGridApiEventHandler(apiRef, GridEvents.rowsSet, removeOutdatedSelection); }; diff --git a/packages/grid/data-grid/src/tests/pagination.DataGrid.test.tsx b/packages/grid/data-grid/src/tests/pagination.DataGrid.test.tsx index ab2aafbe33f00..b9a8898a8578a 100644 --- a/packages/grid/data-grid/src/tests/pagination.DataGrid.test.tsx +++ b/packages/grid/data-grid/src/tests/pagination.DataGrid.test.tsx @@ -495,7 +495,7 @@ describe(' - Pagination', () => { expect(document.querySelector('.MuiTablePagination-root')).to.have.text('1-1 of 21'); }); - it('should support server side pagination', () => { + it.only('should support server side pagination', () => { const ServerPaginationGrid = () => { const [rows, setRows] = React.useState([]); const [page, setPage] = React.useState(0); diff --git a/packages/grid/data-grid/src/useDataGridComponent.tsx b/packages/grid/data-grid/src/useDataGridComponent.tsx index a600bd60d0fd1..bfb87edb11110 100644 --- a/packages/grid/data-grid/src/useDataGridComponent.tsx +++ b/packages/grid/data-grid/src/useDataGridComponent.tsx @@ -31,21 +31,23 @@ import { useRenderInfoLog } from '../../_modules_/grid/hooks/utils/useRenderInfo import { useGridResizeContainer } from '../../_modules_/grid/hooks/utils/useGridResizeContainer'; import { useStateProp } from '../../_modules_/grid/hooks/utils/useStateProp'; import { GridApiRef } from '../../_modules_/grid/models/api/gridApiRef'; +import { useGridColumnsPreProcessing } from '../../_modules_/grid/hooks/root/columnsPreProcessing'; export const useDataGridComponent = (apiRef: GridApiRef, props: GridComponentProps) => { useGridLoggerFactory(apiRef, props); useApi(apiRef, props); useErrorHandler(apiRef, props); useGridControlState(apiRef, props); + useGridColumnsPreProcessing(apiRef); useLocaleText(apiRef, props); useGridResizeContainer(apiRef, props); - useGridFreezeRows(apiRef, props); + useGridSelection(apiRef, props); useGridColumns(apiRef, props); + useGridFreezeRows(apiRef, props); useGridRows(apiRef, props); useGridParamsApi(apiRef); useGridEditRows(apiRef, props); useGridFocus(apiRef, props); - useGridSelection(apiRef, props); useGridSorting(apiRef, props); useGridPreferencesPanel(apiRef); useGridFilter(apiRef, props); diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index b65be7402b403..f8a928d3a8871 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -2,6 +2,7 @@ import { createClientRenderStrictMode, // @ts-expect-error need to migrate helpers to TypeScript fireEvent, + // @ts-expect-error need to migrate helpers to TypeScript screen, } from 'test/utils'; import { getCell, getColumnHeadersTextContent, getColumnValues } from 'test/utils/helperFn'; @@ -30,7 +31,7 @@ const baselineProps: DataGridProProps = { getRowId: (row) => row.name, }; -describe.only(' - Pagination', () => { +describe(' - Pagination', () => { // TODO v5: replace with createClientRender const render = createClientRenderStrictMode(); diff --git a/packages/grid/x-grid/src/useDataGridProComponent.tsx b/packages/grid/x-grid/src/useDataGridProComponent.tsx index c16356226da38..7add758995785 100644 --- a/packages/grid/x-grid/src/useDataGridProComponent.tsx +++ b/packages/grid/x-grid/src/useDataGridProComponent.tsx @@ -45,14 +45,14 @@ export const useDataGridProComponent = (apiRef: GridApiRef, props: GridComponent useGridColumnsPreProcessing(apiRef); useLocaleText(apiRef, props); useGridResizeContainer(apiRef, props); - useGridFreezeRows(apiRef, props); useGridTreeData(apiRef, props); + useGridSelection(apiRef, props); useGridColumns(apiRef, props); + useGridFreezeRows(apiRef, props); useGridRows(apiRef, props); useGridParamsApi(apiRef); useGridEditRows(apiRef, props); useGridFocus(apiRef, props); - useGridSelection(apiRef, props); useGridSorting(apiRef, props); useGridPreferencesPanel(apiRef); useGridFilter(apiRef, props); From 52ef880df8d17a410062a7c86b68c32b4b259d9c Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 17 Sep 2021 15:57:25 +0200 Subject: [PATCH 097/390] Clean --- .../grid/hooks/features/treeData/useGridTreeData.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index f1cc5a163aa15..d98448b42253b 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -14,9 +14,8 @@ import { GridColumnsPreProcessing } from '../../root/columnsPreProcessing'; import { GridTreeDataGroupColDef } from './gridTreeDataGroupColDef'; import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; import { GridEvents } from '../../../constants'; -import { GridCellModes, GridCellParams, GridColDef, MuiEvent } from '../../../models'; -import { isGridCellRoot } from '../../../utils/domUtils'; -import { isNavigationKey, isSpaceKey } from '../../../utils/keyboardUtils'; +import { GridCellParams, GridColDef, MuiEvent } from '../../../models'; +import { isSpaceKey } from '../../../utils/keyboardUtils'; const insertRowInTree = ( tree: GridRowConfigTree, From df930cc84d524bf846514b4ea5f2947a3e5d5c29 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 27 Sep 2021 13:56:18 +0200 Subject: [PATCH 098/390] Replace selector in components with events for pro features --- .../grid/components/base/GridBody.tsx | 2 +- .../columnHeaders/GridColumnHeaders.tsx | 105 ++++++++++-------- .../GridColumnHeadersItemCollection.tsx | 8 +- .../grid/constants/eventsConstants.ts | 9 +- .../columnReorder/useGridColumnReorder.tsx | 28 +---- .../columnResize/useGridColumnResize.tsx | 33 +----- .../grid/models/gridSlotsComponentsProps.ts | 1 - packages/grid/x-grid/src/DataGridPro.tsx | 4 +- .../x-grid/src/useDataGridProComponent.tsx | 6 +- 9 files changed, 87 insertions(+), 109 deletions(-) diff --git a/packages/grid/_modules_/grid/components/base/GridBody.tsx b/packages/grid/_modules_/grid/components/base/GridBody.tsx index 7039e71fa1423..7ef169b343383 100644 --- a/packages/grid/_modules_/grid/components/base/GridBody.tsx +++ b/packages/grid/_modules_/grid/components/base/GridBody.tsx @@ -40,7 +40,7 @@ function GridBody(props: GridBodyProps) { - + state.scrollBar; @@ -32,51 +34,66 @@ const useUtilityClasses = (ownerState: OwnerState) => { return composeClasses(slots, getDataGridUtilityClass, classes); }; -export interface GridColumnsHeaderProps { - dragCol?: string; - resizeCol?: string; -} +export const GridColumnsHeader = React.forwardRef(function GridColumnsHeader( + props, + ref, +) { + const [dragCol, setDragCol] = React.useState(''); + const [resizeCol, setResizeCol] = React.useState(''); + + const apiRef = useGridApiContext(); + const columns = useGridSelector(apiRef, visibleGridColumnsSelector); + const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); + const headerHeight = useGridSelector(apiRef, gridDensityHeaderHeightSelector); + const renderCtx = useGridSelector(apiRef, gridRenderingSelector).renderContext; + const { hasScrollX } = useGridSelector(apiRef, gridScrollbarStateSelector); + const rootProps = useGridRootProps(); + + const ownerState = { ...props, classes: rootProps.classes }; + const classes = useUtilityClasses(ownerState); -export const GridColumnsHeader = React.forwardRef( - function GridColumnsHeader(props, ref) { - const apiRef = useGridApiContext(); - const columns = useGridSelector(apiRef, visibleGridColumnsSelector); - const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); - const headerHeight = useGridSelector(apiRef, gridDensityHeaderHeightSelector); - const renderCtx = useGridSelector(apiRef, gridRenderingSelector).renderContext; - const { hasScrollX } = useGridSelector(apiRef, gridScrollbarStateSelector); - const rootProps = useGridRootProps(); + const renderedCols = React.useMemo(() => { + if (renderCtx == null) { + return []; + } + return columns.slice(renderCtx.firstColIdx, renderCtx.lastColIdx! + 1); + }, [columns, renderCtx]); - const ownerState = { ...props, classes: rootProps.classes }; - const classes = useUtilityClasses(ownerState); + const handleColumnResizeStart = React.useCallback( + (params: { field: string }) => setResizeCol(params.field), + [], + ); + const handleColumnResizeStop = React.useCallback(() => setResizeCol(''), []); + const handleColumnReorderStart = React.useCallback( + (params: { field: string }) => setDragCol(params.field), + [], + ); + const handleColumnReorderStop = React.useCallback(() => setDragCol(''), []); - const renderedCols = React.useMemo(() => { - if (renderCtx == null) { - return []; - } - return columns.slice(renderCtx.firstColIdx, renderCtx.lastColIdx! + 1); - }, [columns, renderCtx]); + useGridApiEventHandler(apiRef, GridEvents.columnResizeStart, handleColumnResizeStart); + useGridApiEventHandler(apiRef, GridEvents.columnResizeStop, handleColumnResizeStop); + useGridApiEventHandler(apiRef, GridEvents.columnReorderStart, handleColumnReorderStart); + useGridApiEventHandler(apiRef, GridEvents.columnReorderStop, handleColumnReorderStop); - return ( - - -
- - - -
- -
- ); - }, -); + return ( + + +
+ + + +
+ +
+ ); +}); diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx index c0b5defa29982..d2b16a8c658e8 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx @@ -19,8 +19,8 @@ import { gridScrollBarSizeSelector } from '../../hooks/root/gridContainerSizesSe export interface GridColumnHeadersItemCollectionProps { columns: GridStateColDef[]; - dragCol?: string; - resizeCol?: string; + dragCol: string; + resizeCol: string; } function GridColumnHeadersItemCollection(props: GridColumnHeadersItemCollectionProps) { @@ -87,8 +87,8 @@ GridColumnHeadersItemCollection.propTypes = { // | To update them edit the TypeScript types and run "yarn proptypes" | // ---------------------------------------------------------------------- columns: PropTypes.arrayOf(PropTypes.object).isRequired, - dragCol: PropTypes.string, - resizeCol: PropTypes.string, + dragCol: PropTypes.string.isRequired, + resizeCol: PropTypes.string.isRequired, } as any; export { GridColumnHeadersItemCollection }; diff --git a/packages/grid/_modules_/grid/constants/eventsConstants.ts b/packages/grid/_modules_/grid/constants/eventsConstants.ts index 2eaaa928d9f0d..b55e902a2a275 100644 --- a/packages/grid/_modules_/grid/constants/eventsConstants.ts +++ b/packages/grid/_modules_/grid/constants/eventsConstants.ts @@ -272,11 +272,18 @@ export enum GridEvents { * Fired when the user starts resizing a column. Called with an object `{ field: string }`. */ columnResizeStart = 'columnResizeStart', - /** * Fired when the user stops resizing a column. Called with an object `{ field: string }`. */ columnResizeStop = 'columnResizeStop', + /** + * Fired when the user starts reordering a column. Called with an object `{ field: string }`. + */ + columnReorderStart = 'columnResizeStart', + /** + * Fired when the user stops reordering a column. Called with an object `{ field: string }`. + */ + columnReorderStop = 'columnResizeStop', /** * Fired when the user ends reordering a column. */ diff --git a/packages/grid/_modules_/grid/hooks/features/columnReorder/useGridColumnReorder.tsx b/packages/grid/_modules_/grid/hooks/features/columnReorder/useGridColumnReorder.tsx index da21b4923e1a2..ce89f8c814302 100644 --- a/packages/grid/_modules_/grid/hooks/features/columnReorder/useGridColumnReorder.tsx +++ b/packages/grid/_modules_/grid/hooks/features/columnReorder/useGridColumnReorder.tsx @@ -13,7 +13,6 @@ import { gridColumnReorderDragColSelector } from './columnReorderSelector'; import { GridComponentProps } from '../../../GridComponentProps'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { composeClasses } from '../../../utils/material-ui-utils'; -import { GridSlotsComponentsProps } from '../../../models'; const CURSOR_MOVE_DIRECTION_LEFT = 'left'; const CURSOR_MOVE_DIRECTION_RIGHT = 'right'; @@ -51,8 +50,8 @@ const useUtilityClasses = (ownerState: OwnerState) => { */ export const useGridColumnReorder = ( apiRef: GridApiRef, - props: GridComponentProps, -): GridComponentProps => { + props: Pick, +) => { const logger = useGridLogger(apiRef, 'useGridColumnReorder'); useGridStateInit(apiRef, (state) => ({ @@ -89,6 +88,8 @@ export const useGridColumnReorder = ( dragColNode.current = event.currentTarget; dragColNode.current.classList.add(classes.columnHeaderDragging); + apiRef.current.publishEvent(GridEvents.columnReorderStart, { field: params.field }); + setGridState((state) => ({ ...state, columnReorder: { ...state.columnReorder, dragCol: params.field }, @@ -171,6 +172,8 @@ export const useGridColumnReorder = ( clearTimeout(removeDnDStylesTimeout.current); dragColNode.current = null; + apiRef.current.publishEvent(GridEvents.columnReorderStop, { field: params.field }); + // Check if the column was dropped outside the grid. if (event.dataTransfer.dropEffect === 'none') { apiRef.current.setColumnIndex(params.field, originColumnIndex.current!); @@ -193,23 +196,4 @@ export const useGridColumnReorder = ( useGridApiEventHandler(apiRef, GridEvents.cellDragEnter, handleDragEnter); useGridApiEventHandler(apiRef, GridEvents.cellDragOver, handleDragOver); useGridApiEventHandler(apiRef, GridEvents.cellDragEnd, handleDragEnd); - - const componentsProps = React.useMemo( - () => ({ - ...props.componentsProps, - columnsHeader: { - ...props.componentsProps?.columnsHeader, - dragCol: dragColField, - }, - }), - [props.componentsProps, dragColField], - ); - - return React.useMemo( - () => ({ - ...props, - componentsProps, - }), - [props, componentsProps], - ); }; diff --git a/packages/grid/_modules_/grid/hooks/features/columnResize/useGridColumnResize.tsx b/packages/grid/_modules_/grid/hooks/features/columnResize/useGridColumnResize.tsx index 20da06d3a6ee5..72d11e1174ed4 100644 --- a/packages/grid/_modules_/grid/hooks/features/columnResize/useGridColumnResize.tsx +++ b/packages/grid/_modules_/grid/hooks/features/columnResize/useGridColumnResize.tsx @@ -11,19 +11,12 @@ import { getFieldFromHeaderElem, findHeaderElementFromField, } from '../../../utils/domUtils'; -import { - GridApiRef, - CursorCoordinates, - GridColumnHeaderParams, - GridSlotsComponentsProps, -} from '../../../models'; +import { GridApiRef, CursorCoordinates, GridColumnHeaderParams } from '../../../models'; import { useGridApiEventHandler, useGridApiOptionHandler } from '../../root/useGridApiEventHandler'; import { useGridState } from '../core/useGridState'; import { useNativeEventListener } from '../../root/useNativeEventListener'; import { GridComponentProps } from '../../../GridComponentProps'; import { useGridStateInit } from '../../utils/useGridStateInit'; -import { useGridSelector } from '../core'; -import { gridResizingColumnFieldSelector } from './columnResizeSelector'; // TODO: remove support for Safari < 13. // https://caniuse.com/#search=touch-action @@ -75,8 +68,8 @@ function trackFinger(event, currentTouchId): CursorCoordinates | boolean { */ export const useGridColumnResize = ( apiRef: GridApiRef, - props: GridComponentProps, -): GridComponentProps => { + props: Pick, +) => { const logger = useGridLogger(apiRef, 'useGridColumnResize'); useGridStateInit(apiRef, (state) => ({ @@ -90,7 +83,6 @@ export const useGridColumnResize = ( const initialOffset = React.useRef(); const stopResizeEventTimeout = React.useRef(); const touchId = React.useRef(); - const resizingColumnField = useGridSelector(apiRef, gridResizingColumnFieldSelector); const updateWidth = (newWidth: number) => { logger.debug(`Updating width to ${newWidth} for col ${colDefRef.current!.field}`); @@ -354,23 +346,4 @@ export const useGridColumnResize = ( useGridApiOptionHandler(apiRef, GridEvents.columnResize, props.onColumnResize); useGridApiOptionHandler(apiRef, GridEvents.columnWidthChange, props.onColumnWidthChange); - - const componentsProps = React.useMemo( - () => ({ - ...props.componentsProps, - columnsHeader: { - ...props.componentsProps?.columnsHeader, - resizeCol: resizingColumnField, - }, - }), - [props.componentsProps, resizingColumnField], - ); - - return React.useMemo( - () => ({ - ...props, - componentsProps, - }), - [props, componentsProps], - ); }; diff --git a/packages/grid/_modules_/grid/models/gridSlotsComponentsProps.ts b/packages/grid/_modules_/grid/models/gridSlotsComponentsProps.ts index 07c945cdf5fc5..9cbdc999c26e0 100644 --- a/packages/grid/_modules_/grid/models/gridSlotsComponentsProps.ts +++ b/packages/grid/_modules_/grid/models/gridSlotsComponentsProps.ts @@ -7,7 +7,6 @@ export interface GridSlotsComponentsProps { checkbox?: any; columnMenu?: any; columnsPanel?: any; - columnsHeader?: GridColumnsHeaderProps; errorOverlay?: any; filterPanel?: any; footer?: any; diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index 81a55bc4d342f..cf0f156d2f6b4 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -33,8 +33,8 @@ const DataGridProRaw = React.forwardRef(functi ref, ) { const apiRef = useGridApiRef(inProps.apiRef); - let props = useDataGridProProps(inProps); - props = useDataGridProComponent(apiRef, props); + const props = useDataGridProProps(inProps); + useDataGridProComponent(apiRef, props); return ( diff --git a/packages/grid/x-grid/src/useDataGridProComponent.tsx b/packages/grid/x-grid/src/useDataGridProComponent.tsx index 487e5901d92f7..ac9c6f1c0d09e 100644 --- a/packages/grid/x-grid/src/useDataGridProComponent.tsx +++ b/packages/grid/x-grid/src/useDataGridProComponent.tsx @@ -53,8 +53,8 @@ export const useDataGridProComponent = (apiRef: GridApiRef, props: GridComponent useGridPreferencesPanel(apiRef); useGridFilter(apiRef, props); useGridDensity(apiRef, props); - props = useGridColumnReorder(apiRef, props); - props = useGridColumnResize(apiRef, props); + useGridColumnReorder(apiRef, props); + useGridColumnResize(apiRef, props); useGridPageSize(apiRef, props); useGridPage(apiRef, props); useGridContainerProps(apiRef, props); @@ -70,6 +70,4 @@ export const useDataGridProComponent = (apiRef: GridApiRef, props: GridComponent useGridEvents(apiRef, props); useStateProp(apiRef, props); useRenderInfoLog(apiRef); - - return props; }; From 5a68d276c841a706de8766b32fd25d2a8820be15 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 27 Sep 2021 14:13:15 +0200 Subject: [PATCH 099/390] Fix --- .../grid/components/columnHeaders/GridColumnHeaders.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx index f3438db858b34..ad2bc3c980f93 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx @@ -21,7 +21,7 @@ export const gridScrollbarStateSelector = (state: GridState) => state.scrollBar; type OwnerState = { classes?: GridComponentProps['classes']; - dragCol?: string; + dragCol: string; }; const useUtilityClasses = (ownerState: OwnerState) => { From 20ae7c7d216edd0c5e4c3cca44819c3d2dd91718 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 27 Sep 2021 14:14:08 +0200 Subject: [PATCH 100/390] Fix --- .../grid/components/columnHeaders/GridColumnHeaders.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx index ad2bc3c980f93..ee3ae59f88e24 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx @@ -49,7 +49,7 @@ export const GridColumnsHeader = React.forwardRef(function G const { hasScrollX } = useGridSelector(apiRef, gridScrollbarStateSelector); const rootProps = useGridRootProps(); - const ownerState = { ...props, classes: rootProps.classes }; + const ownerState = { dragCol, classes: rootProps.classes }; const classes = useUtilityClasses(ownerState); const renderedCols = React.useMemo(() => { From 4031d762cb34f1f3eaf567ae5dba53ade5954fa0 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 27 Sep 2021 14:18:25 +0200 Subject: [PATCH 101/390] [core] Stop using selector for pro features on React components --- .../columnHeaders/GridColumnHeaders.tsx | 31 ++++++++++++++++--- .../GridColumnHeadersItemCollection.tsx | 10 +++--- .../grid/constants/eventsConstants.ts | 9 +++++- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx index d3fbe044c3e6e..e6ed88015cefd 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx @@ -9,12 +9,13 @@ import { GridEmptyCell } from '../cell/GridEmptyCell'; import { GridScrollArea } from '../GridScrollArea'; import { GridColumnHeadersItemCollection } from './GridColumnHeadersItemCollection'; import { gridDensityHeaderHeightSelector } from '../../hooks/features/density/densitySelector'; -import { gridColumnReorderDragColSelector } from '../../hooks/features/columnReorder/columnReorderSelector'; import { gridContainerSizesSelector } from '../../hooks/root/gridContainerSizesSelector'; import { getDataGridUtilityClass } from '../../gridClasses'; import { composeClasses } from '../../utils/material-ui-utils'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { GridComponentProps } from '../../GridComponentProps'; +import {useGridApiEventHandler} from "../../hooks/root/useGridApiEventHandler"; +import {GridEvents} from "../../constants/eventsConstants"; export const gridScrollbarStateSelector = (state: GridState) => state.scrollBar; @@ -37,16 +38,18 @@ export const GridColumnsHeader = React.forwardRef(function G props, ref, ) { + const [dragCol, setDragCol] = React.useState(''); + const [resizeCol, setResizeCol] = React.useState(''); + const apiRef = useGridApiContext(); const columns = useGridSelector(apiRef, visibleGridColumnsSelector); const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); const headerHeight = useGridSelector(apiRef, gridDensityHeaderHeightSelector); const renderCtx = useGridSelector(apiRef, gridRenderingSelector).renderContext; const { hasScrollX } = useGridSelector(apiRef, gridScrollbarStateSelector); - const dragCol = useGridSelector(apiRef, gridColumnReorderDragColSelector); const rootProps = useGridRootProps(); - const ownerState = { ...props, dragCol, classes: rootProps.classes }; + const ownerState = { dragCol, classes: rootProps.classes }; const classes = useUtilityClasses(ownerState); const renderedCols = React.useMemo(() => { @@ -56,6 +59,22 @@ export const GridColumnsHeader = React.forwardRef(function G return columns.slice(renderCtx.firstColIdx, renderCtx.lastColIdx! + 1); }, [columns, renderCtx]); + const handleColumnResizeStart = React.useCallback( + (params: { field: string }) => setResizeCol(params.field), + [], + ); + const handleColumnResizeStop = React.useCallback(() => setResizeCol(''), []); + const handleColumnReorderStart = React.useCallback( + (params: { field: string }) => setDragCol(params.field), + [], + ); + const handleColumnReorderStop = React.useCallback(() => setDragCol(''), []); + + useGridApiEventHandler(apiRef, GridEvents.columnResizeStart, handleColumnResizeStart); + useGridApiEventHandler(apiRef, GridEvents.columnResizeStop, handleColumnResizeStop); + useGridApiEventHandler(apiRef, GridEvents.columnReorderStart, handleColumnReorderStart); + useGridApiEventHandler(apiRef, GridEvents.columnReorderStop, handleColumnReorderStop); + return ( @@ -67,7 +86,11 @@ export const GridColumnsHeader = React.forwardRef(function G style={{ minWidth: containerSizes?.totalSizes?.width }} > - + diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx index b2ae1bd88eb43..f5007c548038e 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx @@ -1,7 +1,5 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { gridColumnReorderDragColSelector } from '../../hooks/features/columnReorder/columnReorderSelector'; -import { gridResizingColumnFieldSelector } from '../../hooks/features/columnResize/columnResizeSelector'; import { useGridSelector } from '../../hooks/features/core/useGridSelector'; import { filterGridColumnLookupSelector } from '../../hooks/features/filter/gridFilterSelector'; import { @@ -21,15 +19,15 @@ import { gridScrollBarSizeSelector } from '../../hooks/root/gridContainerSizesSe export interface GridColumnHeadersItemCollectionProps { columns: GridStateColDef[]; + dragCol: string; + resizeCol: string; } function GridColumnHeadersItemCollection(props: GridColumnHeadersItemCollectionProps) { - const { columns } = props; + const { columns, dragCol, resizeCol } = props; const apiRef = useGridApiContext(); const sortColumnLookup = useGridSelector(apiRef, gridSortColumnLookupSelector); const filterColumnLookup = useGridSelector(apiRef, filterGridColumnLookupSelector); - const dragCol = useGridSelector(apiRef, gridColumnReorderDragColSelector); - const resizingColumnField = useGridSelector(apiRef, gridResizingColumnFieldSelector); const columnHeaderFocus = useGridSelector(apiRef, gridFocusColumnHeaderSelector); const renderCtx = useGridSelector(apiRef, gridRenderingSelector).renderContext; const tabIndexState = useGridSelector(apiRef, gridTabIndexColumnHeaderSelector); @@ -69,7 +67,7 @@ function GridColumnHeadersItemCollection(props: GridColumnHeadersItemCollectionP isDragging={col.field === dragCol} column={col} colIndex={colIndex} - isResizing={resizingColumnField === col.field} + isResizing={resizeCol === col.field} isLastColumn={colIndex === columns.length - 1} extendRowFullWidth={!rootProps.disableExtendRowFullWidth} hasScrollX={scrollBarState.hasScrollX} diff --git a/packages/grid/_modules_/grid/constants/eventsConstants.ts b/packages/grid/_modules_/grid/constants/eventsConstants.ts index 2eaaa928d9f0d..b55e902a2a275 100644 --- a/packages/grid/_modules_/grid/constants/eventsConstants.ts +++ b/packages/grid/_modules_/grid/constants/eventsConstants.ts @@ -272,11 +272,18 @@ export enum GridEvents { * Fired when the user starts resizing a column. Called with an object `{ field: string }`. */ columnResizeStart = 'columnResizeStart', - /** * Fired when the user stops resizing a column. Called with an object `{ field: string }`. */ columnResizeStop = 'columnResizeStop', + /** + * Fired when the user starts reordering a column. Called with an object `{ field: string }`. + */ + columnReorderStart = 'columnResizeStart', + /** + * Fired when the user stops reordering a column. Called with an object `{ field: string }`. + */ + columnReorderStop = 'columnResizeStop', /** * Fired when the user ends reordering a column. */ From 5c890119c40ff2196f94b7a0f3cb2a3c32dec601 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 27 Sep 2021 14:19:00 +0200 Subject: [PATCH 102/390] Format --- .../columnHeaders/GridColumnHeaders.tsx | 18 +++++++++--------- .../GridColumnHeadersItemCollection.tsx | 2 ++ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx index e6ed88015cefd..ee3ae59f88e24 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx @@ -14,8 +14,8 @@ import { getDataGridUtilityClass } from '../../gridClasses'; import { composeClasses } from '../../utils/material-ui-utils'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { GridComponentProps } from '../../GridComponentProps'; -import {useGridApiEventHandler} from "../../hooks/root/useGridApiEventHandler"; -import {GridEvents} from "../../constants/eventsConstants"; +import { useGridApiEventHandler } from '../../hooks/root/useGridApiEventHandler'; +import { GridEvents } from '../../constants/eventsConstants'; export const gridScrollbarStateSelector = (state: GridState) => state.scrollBar; @@ -60,13 +60,13 @@ export const GridColumnsHeader = React.forwardRef(function G }, [columns, renderCtx]); const handleColumnResizeStart = React.useCallback( - (params: { field: string }) => setResizeCol(params.field), - [], + (params: { field: string }) => setResizeCol(params.field), + [], ); const handleColumnResizeStop = React.useCallback(() => setResizeCol(''), []); const handleColumnReorderStart = React.useCallback( - (params: { field: string }) => setDragCol(params.field), - [], + (params: { field: string }) => setDragCol(params.field), + [], ); const handleColumnReorderStop = React.useCallback(() => setDragCol(''), []); @@ -87,9 +87,9 @@ export const GridColumnsHeader = React.forwardRef(function G > diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx index f5007c548038e..d2b16a8c658e8 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx @@ -87,6 +87,8 @@ GridColumnHeadersItemCollection.propTypes = { // | To update them edit the TypeScript types and run "yarn proptypes" | // ---------------------------------------------------------------------- columns: PropTypes.arrayOf(PropTypes.object).isRequired, + dragCol: PropTypes.string.isRequired, + resizeCol: PropTypes.string.isRequired, } as any; export { GridColumnHeadersItemCollection }; From 047aee63151fcfc29da343e463956b706257e139 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 27 Sep 2021 14:24:01 +0200 Subject: [PATCH 103/390] Use existing events --- .../grid/components/columnHeaders/GridColumnHeaders.tsx | 7 ++++--- packages/grid/_modules_/grid/constants/eventsConstants.ts | 8 -------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx index ee3ae59f88e24..3fd87023f4723 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx @@ -16,6 +16,7 @@ import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { GridComponentProps } from '../../GridComponentProps'; import { useGridApiEventHandler } from '../../hooks/root/useGridApiEventHandler'; import { GridEvents } from '../../constants/eventsConstants'; +import { GridColumnHeaderParams } from '../../models/params/gridColumnHeaderParams'; export const gridScrollbarStateSelector = (state: GridState) => state.scrollBar; @@ -65,15 +66,15 @@ export const GridColumnsHeader = React.forwardRef(function G ); const handleColumnResizeStop = React.useCallback(() => setResizeCol(''), []); const handleColumnReorderStart = React.useCallback( - (params: { field: string }) => setDragCol(params.field), + (params: GridColumnHeaderParams) => setDragCol(params.field), [], ); const handleColumnReorderStop = React.useCallback(() => setDragCol(''), []); useGridApiEventHandler(apiRef, GridEvents.columnResizeStart, handleColumnResizeStart); useGridApiEventHandler(apiRef, GridEvents.columnResizeStop, handleColumnResizeStop); - useGridApiEventHandler(apiRef, GridEvents.columnReorderStart, handleColumnReorderStart); - useGridApiEventHandler(apiRef, GridEvents.columnReorderStop, handleColumnReorderStop); + useGridApiEventHandler(apiRef, GridEvents.columnHeaderDragStart, handleColumnReorderStart); + useGridApiEventHandler(apiRef, GridEvents.columnHeaderDragEnd, handleColumnReorderStop); return ( diff --git a/packages/grid/_modules_/grid/constants/eventsConstants.ts b/packages/grid/_modules_/grid/constants/eventsConstants.ts index b55e902a2a275..ff906e2709950 100644 --- a/packages/grid/_modules_/grid/constants/eventsConstants.ts +++ b/packages/grid/_modules_/grid/constants/eventsConstants.ts @@ -276,14 +276,6 @@ export enum GridEvents { * Fired when the user stops resizing a column. Called with an object `{ field: string }`. */ columnResizeStop = 'columnResizeStop', - /** - * Fired when the user starts reordering a column. Called with an object `{ field: string }`. - */ - columnReorderStart = 'columnResizeStart', - /** - * Fired when the user stops reordering a column. Called with an object `{ field: string }`. - */ - columnReorderStop = 'columnResizeStop', /** * Fired when the user ends reordering a column. */ From bb48b96e709754f51319ff34c59c3a61d5bcfa2b Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 27 Sep 2021 14:31:15 +0200 Subject: [PATCH 104/390] Code review --- .../grid/_modules_/grid/hooks/features/filter/useGridFilter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index c9f0e890d0950..c8a8c012fdb6b 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -30,7 +30,7 @@ const checkFilterModelValidity = (model: GridFilterModel) => { const hasItemsWithoutIds = model.items.find((item) => item.id == null); if (hasItemsWithoutIds) { throw new Error( - "The 'id' field is required on filterModel.items when you use multiple filters.", + "MUI: The 'id' field is required on `filterModel.items` when you use multiple filters.", ); } } From 2dfdc77461c05de965cd86c8a2c20e4e0124634b Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 27 Sep 2021 15:26:01 +0200 Subject: [PATCH 105/390] Fix lint --- packages/grid/_modules_/grid/models/gridSlotsComponentsProps.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/grid/_modules_/grid/models/gridSlotsComponentsProps.ts b/packages/grid/_modules_/grid/models/gridSlotsComponentsProps.ts index 9cbdc999c26e0..8ed8b4fac6e05 100644 --- a/packages/grid/_modules_/grid/models/gridSlotsComponentsProps.ts +++ b/packages/grid/_modules_/grid/models/gridSlotsComponentsProps.ts @@ -1,5 +1,3 @@ -import type { GridColumnsHeaderProps } from '../components/columnHeaders/GridColumnHeaders'; - /** * Overrideable components props dynamically passed to the component at rendering. */ From 6b430e95cf03ede4e403a26afa643aac4de23273 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 27 Sep 2021 15:28:21 +0200 Subject: [PATCH 106/390] Fix --- .../hooks/features/columnReorder/useGridColumnReorder.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/columnReorder/useGridColumnReorder.tsx b/packages/grid/_modules_/grid/hooks/features/columnReorder/useGridColumnReorder.tsx index ce89f8c814302..d46882771fe9f 100644 --- a/packages/grid/_modules_/grid/hooks/features/columnReorder/useGridColumnReorder.tsx +++ b/packages/grid/_modules_/grid/hooks/features/columnReorder/useGridColumnReorder.tsx @@ -88,8 +88,6 @@ export const useGridColumnReorder = ( dragColNode.current = event.currentTarget; dragColNode.current.classList.add(classes.columnHeaderDragging); - apiRef.current.publishEvent(GridEvents.columnReorderStart, { field: params.field }); - setGridState((state) => ({ ...state, columnReorder: { ...state.columnReorder, dragCol: params.field }, @@ -172,8 +170,6 @@ export const useGridColumnReorder = ( clearTimeout(removeDnDStylesTimeout.current); dragColNode.current = null; - apiRef.current.publishEvent(GridEvents.columnReorderStop, { field: params.field }); - // Check if the column was dropped outside the grid. if (event.dataTransfer.dropEffect === 'none') { apiRef.current.setColumnIndex(params.field, originColumnIndex.current!); From 082941252d6cf300e78c3004246cacd8cb257168 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 28 Sep 2021 10:22:47 +0200 Subject: [PATCH 107/390] Fix filter --- .../grid/components/GridViewport.tsx | 1 - .../features/filter/gridFilterSelector.ts | 18 +--- .../hooks/features/filter/gridFilterState.ts | 3 +- .../hooks/features/filter/useGridFilter.ts | 92 +++++++++++-------- .../virtualization/useGridVirtualization.ts | 1 + .../grid/hooks/root/useGridContainerProps.ts | 8 +- .../grid/models/api/gridFilterApi.ts | 3 +- .../src/tests/rows.DataGridPro.test.tsx | 6 +- 8 files changed, 67 insertions(+), 65 deletions(-) diff --git a/packages/grid/_modules_/grid/components/GridViewport.tsx b/packages/grid/_modules_/grid/components/GridViewport.tsx index 49866a0e6c39f..90a95dad0fd0d 100644 --- a/packages/grid/_modules_/grid/components/GridViewport.tsx +++ b/packages/grid/_modules_/grid/components/GridViewport.tsx @@ -69,7 +69,6 @@ export const GridViewport: ViewportType = React.forwardRef( renderState.renderContext.lastRowIdx!, ); - // TODO: Add tree children return renderedRows.map((row, idx) => ( filterState.visibleRowsLookup, ); +export const gridVisibleRowCountSelector = createSelector( + gridFilterStateSelector, + (filterState) => filterState.visibleRowCount, +); + export const gridSortedVisibleRowsSelector = createSelector( gridVisibleRowsLookupSelector, gridSortedRowsSelector, @@ -67,18 +71,6 @@ export const visibleSortedGridRowIdsSelector = createSelector( (visibleSortedRows) => [...visibleSortedRows.keys()], ); -export const gridVisibleRowCountSelector = createSelector( - gridVisibleRowsSelector, - gridRowCountSelector, - (visibleRows, totalRowsCount) => { - if (visibleRows == null) { - return totalRowsCount; - } - - return visibleRows.length; - }, -); - export const activeGridFilterItemsSelector = createSelector( gridFilterModelSelector, gridColumnLookupSelector, diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts index b00629996e4dc..52d81af6189dd 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts @@ -10,5 +10,6 @@ export const getDefaultGridFilterModel: () => GridFilterModel = () => ({ export interface GridFilterState { filterModel: GridFilterModel; visibleRowsLookup: Record; - visibleRows: GridRowId[] | null; + visibleRows: GridRowId[]; + visibleRowCount: number; } diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 9da21dd610682..e8943561d1ad8 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -28,6 +28,7 @@ import { GridSortedRowsTreeNode } from '../sorting'; import { useGridRegisterControlState } from '../../utils/useGridRegisterControlState'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; +import { gridRowCountSelector } from '../rows'; const checkFilterModelValidity = (model: GridFilterModel) => { if (model.items.length > 1) { @@ -65,7 +66,8 @@ export const useGridFilter = ( filter: { filterModel: props.filterModel ?? getDefaultGridFilterModel(), visibleRowsLookup: {}, - visibleRows: null, + visibleRows: [], + visibleRowCount: 0, }, }; }); @@ -80,16 +82,16 @@ export const useGridFilter = ( changeEvent: GridEvents.filterModelChange, }); - const applyFilter = React.useCallback( + const applyFilter = React.useCallback( (filterItem: GridFilterItem, linkOperator: GridLinkOperator = GridLinkOperator.And) => { if (!filterItem.columnField || !filterItem.operatorValue) { - return; + return false; } const column = apiRef.current.getColumn(filterItem.columnField); if (!column) { - return; + return false; } const parsedValue = column.valueParser @@ -117,7 +119,7 @@ export const useGridFilter = ( const applyFilterOnRow = filterOperator.getApplyFilterFn(newFilterItem, column)!; if (typeof applyFilterOnRow !== 'function') { - return; + return false; } setGridState((state) => { @@ -153,18 +155,23 @@ export const useGridFilter = ( filterRowTree(rowTree); + const visibleRows = Object.entries(visibleRowsLookup) + .filter(([, isVisible]) => isVisible) + .map(([id]) => id); + return { ...state, filter: { ...state.filter, visibleRowsLookup, - visibleRows: Object.entries(visibleRowsLookup) - .filter(([, isVisible]) => isVisible) - .map(([id]) => id), + visibleRows, + visibleRowCount: visibleRows.length, }, }; }); forceUpdate(); + + return true }, [apiRef, forceUpdate, logger, setGridState], ); @@ -172,39 +179,46 @@ export const useGridFilter = ( const applyFilters = React.useCallback(() => { const { items, linkOperator } = gridFilterModelSelector(apiRef.current.state); - const hasFilterToApply = props.filterMode === GridFeatureModeConstant.client && !!items.length; - - if (hasFilterToApply) { - // Clearing filtered rows - setGridState((state) => ({ - ...state, - filter: { - ...state.filter, - visibleRowsLookup: {}, - visibleRows: null, - }, - })); + let hasAppliedAtLeastOneFilter = false; + + if (props.filterMode === GridFeatureModeConstant.client) { + // Clearing filtered rows + setGridState(state => ({ + ...state, + filter: { + ...state.filter, + visibleRowsLookup: {}, + visibleRows: [], + visibleRowCount: 0, + } + })) + + items.forEach((filterItem) => { + const hasAppliedFilter = apiRef.current.applyFilter(filterItem, linkOperator) + hasAppliedAtLeastOneFilter = hasAppliedAtLeastOneFilter || hasAppliedFilter; + }); + } - items.forEach((filterItem) => { - apiRef.current.applyFilter(filterItem, linkOperator); - }); - } else { - setGridState((state) => { - const rowIds = gridSortedRowIdsFlatSelector(state); - const visibleRowsLookup = Object.fromEntries(rowIds.map((rowId) => [rowId, true])); - const visibleRows = [...rowIds]; + // If no filter has been applied, we set all rows to be visible + if (!hasAppliedAtLeastOneFilter) { + setGridState((state) => { + const rowIds = gridSortedRowIdsFlatSelector(state); + const visibleRowsLookup = Object.fromEntries(rowIds.map((rowId) => [rowId, true])); + const visibleRows = [...rowIds]; + + return { + ...state, + filter: { + ...state.filter, + visibleRowsLookup, + visibleRows, + visibleRowCount: gridRowCountSelector(state), + }, + }; + }); + } - return { - ...state, - filter: { - ...state.filter, - visibleRowsLookup, - visibleRows, - }, - }; - }); - } - forceUpdate(); + forceUpdate(); }, [apiRef, setGridState, forceUpdate, props.filterMode]); const upsertFilter = React.useCallback( diff --git a/packages/grid/_modules_/grid/hooks/features/virtualization/useGridVirtualization.ts b/packages/grid/_modules_/grid/hooks/features/virtualization/useGridVirtualization.ts index d4c48d7c546f3..a226da44e4501 100644 --- a/packages/grid/_modules_/grid/hooks/features/virtualization/useGridVirtualization.ts +++ b/packages/grid/_modules_/grid/hooks/features/virtualization/useGridVirtualization.ts @@ -101,6 +101,7 @@ export const useGridVirtualization = ( const currentRenderingState = { ...state.rendering, ...newState }; if (!isDeepEqual(state.rendering, currentRenderingState)) { stateChanged = true; + return { ...state, rendering: currentRenderingState }; } return state; diff --git a/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts b/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts index 837c495327dc2..eaf77971abd54 100644 --- a/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts +++ b/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts @@ -50,7 +50,6 @@ export const useGridContainerProps = ( | 'pagination' | 'autoPageSize' | 'pageSize' - | 'paginationMode' | 'autoHeight' | 'hideFooter' | 'scrollbarSize' @@ -97,11 +96,7 @@ export const useGridContainerProps = ( const getVirtualRowCount = React.useCallback(() => { logger.debug('Calculating virtual row count.'); - if ( - props.pagination && - (!props.autoPageSize || props.pageSize) && - props.paginationMode === 'client' - ) { + if (props.pagination && (!props.autoPageSize || props.pageSize)) { const rowsLeft = visibleRowsCount - paginationState.page * paginationState.pageSize; return rowsLeft > paginationState.pageSize ? paginationState.pageSize : rowsLeft; } @@ -110,7 +105,6 @@ export const useGridContainerProps = ( logger, props.autoPageSize, props.pagination, - props.paginationMode, props.pageSize, paginationState.page, paginationState.pageSize, diff --git a/packages/grid/_modules_/grid/models/api/gridFilterApi.ts b/packages/grid/_modules_/grid/models/api/gridFilterApi.ts index e3e8a7b047d26..0863f376afd92 100644 --- a/packages/grid/_modules_/grid/models/api/gridFilterApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridFilterApi.ts @@ -24,8 +24,9 @@ export interface GridFilterApi { * Applies a [[GridFilterItem]] on all rows. If no `linkOperator` is given, the "and" operator is used. * @param {GridFilterItem} item The filter to be applied. * @param {GridLinkOperator} linkOperator The link operator to use. + * @return {boolean} Returns `true` if the filter has been applied */ - applyFilter: (item: GridFilterItem, linkOperator?: GridLinkOperator) => void; + applyFilter: (item: GridFilterItem, linkOperator?: GridLinkOperator) => boolean; /** * Applies all filters on all rows. */ diff --git a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx index 6084774bb564a..9115e5d7d0b2c 100644 --- a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx @@ -6,7 +6,7 @@ import { } from 'test/utils'; import { useFakeTimers, spy } from 'sinon'; import { expect } from 'chai'; -import { getCell, getColumnValues } from 'test/utils/helperFn'; +import {getCell, getColumnValues, raf, sleep} from 'test/utils/helperFn'; import { GridApiRef, GridComponentProps, @@ -218,7 +218,7 @@ describe(' - Rows', () => { expect(getColumnValues()).to.deep.equal(['Pata', 'Fila', 'Pum']); }); - it('update row data can also add rows', () => { + it.only('update row data can also add rows', () => { render(); apiRef.current.updateRows([{ id: 1, brand: 'Fila' }]); apiRef.current.updateRows([{ id: 0, brand: 'Pata' }]); @@ -805,7 +805,7 @@ describe(' - Rows', () => { expect(handleCellFocusOut.args[0][0].field).to.equal(baselineProps.columns[0].field); }); - it.only('should not crash when the row is removed during the click', () => { + it('should not crash when the row is removed during the click', () => { expect(() => { render(); const cell = getCell(0, 0); From 6d0caa10742cab680019421e7ee729b6804bc75e Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 28 Sep 2021 10:56:47 +0200 Subject: [PATCH 108/390] Clean --- docs/pages/api-docs/data-grid/grid-api.md | 4 +- .../api-docs/data-grid/grid-filter-api.json | 2 +- .../components/data-grid/events/events.json | 4 - .../grid/constants/eventsConstants.ts | 1 + .../hooks/features/filter/useGridFilter.ts | 74 +++++++++---------- .../gridColumnsPreProcessingApi.ts | 12 ++- .../src/tests/rows.DataGridPro.test.tsx | 2 +- 7 files changed, 52 insertions(+), 47 deletions(-) diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index 89a905156fcfa..e29aec7559cc6 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -12,8 +12,7 @@ import { GridApi } from '@mui/x-data-grid-pro'; | Name | Type | Description | | :------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| applyAllColumnPreProcessing | GridColumnsPreProcessing | | -| applyFilter | (item: GridFilterItem, linkOperator?: GridLinkOperator) => void | Applies a GridFilterItem on all rows. If no `linkOperator` is given, the "and" operator is used. | +| applyFilter | (item: GridFilterItem, linkOperator?: GridLinkOperator) => boolean | Applies a GridFilterItem on all rows. If no `linkOperator` is given, the "and" operator is used. | | applyFilterLinkOperator | (operator: GridLinkOperator) => void | Changes the GridLinkOperator used to connect the filters. | | applyFilters | () => void | Applies all filters on all rows. | | applySorting | () => void | Applies the current sort model to the rows. | @@ -62,7 +61,6 @@ import { GridApi } from '@mui/x-data-grid-pro'; | isCellEditable | (params: GridCellParams) => boolean | Controls if a cell is editable. | | isRowSelected | (id: GridRowId) => boolean | Determines if a row is selected or not. | | publishEvent | (name: string, params?: any, event?: MuiEvent<MouseEvent \| UIEvent \| Event \| SyntheticEvent<Element, Event> \| TouchEvent \| ErrorEvent \| ProgressEvent<EventTarget> \| ClipboardEvent \| AnimationEvent \| InputEvent \| FocusEvent \| CompositionEvent \| DragEvent \| FormDataEvent \| PointerEvent \| KeyboardEvent \| SecurityPolicyViolationEvent \| TransitionEvent \| WheelEvent>) => void | Emits an event. | -| registerColumnPreProcessing | (processingName: string, columnsPreProcessing: null \| GridColumnsPreProcessing) => void | | | resize | () => void | Triggers a resize of the component and recalculation of width and height. | | scroll | (params: Partial<GridScrollParams>) => void | Triggers the viewport to scroll to the given positions (in pixels). | | scrollToIndexes | (params: Partial<GridCellIndexCoordinates>) => boolean | Triggers the viewport to scroll to the cell at indexes given by `params`.
Returns `true` if the grid had to scroll to reach the target. | diff --git a/docs/pages/api-docs/data-grid/grid-filter-api.json b/docs/pages/api-docs/data-grid/grid-filter-api.json index 0bfb1c8f6c8bd..ce5acad0b1ac7 100644 --- a/docs/pages/api-docs/data-grid/grid-filter-api.json +++ b/docs/pages/api-docs/data-grid/grid-filter-api.json @@ -5,7 +5,7 @@ { "name": "applyFilter", "description": "Applies a GridFilterItem on all rows. If no linkOperator is given, the "and" operator is used.", - "type": "(item: GridFilterItem, linkOperator?: GridLinkOperator) => void" + "type": "(item: GridFilterItem, linkOperator?: GridLinkOperator) => boolean" }, { "name": "applyFilterLinkOperator", diff --git a/docs/src/pages/components/data-grid/events/events.json b/docs/src/pages/components/data-grid/events/events.json index a0ee1041b0e59..bc1df37f2a204 100644 --- a/docs/src/pages/components/data-grid/events/events.json +++ b/docs/src/pages/components/data-grid/events/events.json @@ -92,10 +92,6 @@ "name": "columnsChange", "description": "Fired when the columns state is changed.\nCalled with an array of strings corresponding to the field names." }, - { - "name": "columnsPreProcessingChange", - "description": "Fired when a column pre-processing is changed" - }, { "name": "columnVisibilityChange", "description": "Fired when a column visibility changes. Called with a GridColumnVisibilityChangeParams object." diff --git a/packages/grid/_modules_/grid/constants/eventsConstants.ts b/packages/grid/_modules_/grid/constants/eventsConstants.ts index 1f0643e82f29e..7750928115691 100644 --- a/packages/grid/_modules_/grid/constants/eventsConstants.ts +++ b/packages/grid/_modules_/grid/constants/eventsConstants.ts @@ -292,6 +292,7 @@ export enum GridEvents { columnsChange = 'columnsChange', /** * Fired when a column pre-processing is changed + * @ignore - do not document */ columnsPreProcessingChange = 'columnsPreProcessingChange', /** diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index e8943561d1ad8..a3bf50d3b9bca 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -171,7 +171,7 @@ export const useGridFilter = ( }); forceUpdate(); - return true + return true; }, [apiRef, forceUpdate, logger, setGridState], ); @@ -181,44 +181,44 @@ export const useGridFilter = ( let hasAppliedAtLeastOneFilter = false; - if (props.filterMode === GridFeatureModeConstant.client) { - // Clearing filtered rows - setGridState(state => ({ - ...state, - filter: { - ...state.filter, - visibleRowsLookup: {}, - visibleRows: [], - visibleRowCount: 0, - } - })) - - items.forEach((filterItem) => { - const hasAppliedFilter = apiRef.current.applyFilter(filterItem, linkOperator) - hasAppliedAtLeastOneFilter = hasAppliedAtLeastOneFilter || hasAppliedFilter; - }); - } + if (props.filterMode === GridFeatureModeConstant.client) { + // Clearing filtered rows + setGridState((state) => ({ + ...state, + filter: { + ...state.filter, + visibleRowsLookup: {}, + visibleRows: [], + visibleRowCount: 0, + }, + })); - // If no filter has been applied, we set all rows to be visible - if (!hasAppliedAtLeastOneFilter) { - setGridState((state) => { - const rowIds = gridSortedRowIdsFlatSelector(state); - const visibleRowsLookup = Object.fromEntries(rowIds.map((rowId) => [rowId, true])); - const visibleRows = [...rowIds]; - - return { - ...state, - filter: { - ...state.filter, - visibleRowsLookup, - visibleRows, - visibleRowCount: gridRowCountSelector(state), - }, - }; - }); - } + items.forEach((filterItem) => { + const hasAppliedFilter = apiRef.current.applyFilter(filterItem, linkOperator); + hasAppliedAtLeastOneFilter = hasAppliedAtLeastOneFilter || hasAppliedFilter; + }); + } - forceUpdate(); + // If no filter has been applied, we set all rows to be visible + if (!hasAppliedAtLeastOneFilter) { + setGridState((state) => { + const rowIds = gridSortedRowIdsFlatSelector(state); + const visibleRowsLookup = Object.fromEntries(rowIds.map((rowId) => [rowId, true])); + const visibleRows = [...rowIds]; + + return { + ...state, + filter: { + ...state.filter, + visibleRowsLookup, + visibleRows, + visibleRowCount: gridRowCountSelector(state), + }, + }; + }); + } + + forceUpdate(); }, [apiRef, setGridState, forceUpdate, props.filterMode]); const upsertFilter = React.useCallback( diff --git a/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/gridColumnsPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/gridColumnsPreProcessingApi.ts index 79b03265aae4c..f053c3c76d5e0 100644 --- a/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/gridColumnsPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/gridColumnsPreProcessingApi.ts @@ -3,9 +3,19 @@ import { GridColumns } from '../../../models/colDef/gridColDef'; export type GridColumnsPreProcessing = (columns: GridColumns) => GridColumns; export interface GridColumnsPreProcessingApi { + /** + * @param {string} processingName Name of the pre-processing. Used to clean the previous version of the pre-processing. + * @param {GridColumnsPreProcessing | null } columnsPreProcessing Pre-processing to register. + * @ignore - do not document + */ registerColumnPreProcessing: ( processingName: string, columnsPreProcessing: GridColumnsPreProcessing | null, ) => void; - applyAllColumnPreProcessing: GridColumnsPreProcessing; + /** + * @param {GridColumns} columns. Columns to pre-process + * @returns {GridColumns} The pre-processed columns + * @ignore - do not document + */ + applyAllColumnPreProcessing: (columns: GridColumns) => GridColumns; } diff --git a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx index 9115e5d7d0b2c..9b53433b10714 100644 --- a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx @@ -6,7 +6,7 @@ import { } from 'test/utils'; import { useFakeTimers, spy } from 'sinon'; import { expect } from 'chai'; -import {getCell, getColumnValues, raf, sleep} from 'test/utils/helperFn'; +import { getCell, getColumnValues, raf, sleep } from 'test/utils/helperFn'; import { GridApiRef, GridComponentProps, From 54ffd97cd5057215838a299b14d717f3776cc15f Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 28 Sep 2021 11:57:37 +0200 Subject: [PATCH 109/390] Clean --- packages/grid/x-grid/src/tests/editRows.DataGridPro.test.tsx | 1 - packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/grid/x-grid/src/tests/editRows.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/editRows.DataGridPro.test.tsx index a340223076543..685b3c87d585e 100644 --- a/packages/grid/x-grid/src/tests/editRows.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/editRows.DataGridPro.test.tsx @@ -419,7 +419,6 @@ describe(' - Edit Rows', () => { fireEvent.keyDown(input, { key: 'Enter' }); expect(cell).not.to.have.class('MuiDataGrid-cell--editing'); expect(cell).to.have.text('1962'); - expect(valueParser.callCount).to.equal(1); expect(valueParser.args[0][0]).to.equal('62'); expect(valueParser.args[0][1]).to.deep.include({ diff --git a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx index 9b53433b10714..dc398913343e9 100644 --- a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx @@ -6,7 +6,7 @@ import { } from 'test/utils'; import { useFakeTimers, spy } from 'sinon'; import { expect } from 'chai'; -import { getCell, getColumnValues, raf, sleep } from 'test/utils/helperFn'; +import { getCell, getColumnValues } from 'test/utils/helperFn'; import { GridApiRef, GridComponentProps, @@ -218,7 +218,7 @@ describe(' - Rows', () => { expect(getColumnValues()).to.deep.equal(['Pata', 'Fila', 'Pum']); }); - it.only('update row data can also add rows', () => { + it('update row data can also add rows', () => { render(); apiRef.current.updateRows([{ id: 1, brand: 'Fila' }]); apiRef.current.updateRows([{ id: 0, brand: 'Pata' }]); From a774bc3bd93f360cf87aa382c23906ac9d966e3e Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 28 Sep 2021 12:19:48 +0200 Subject: [PATCH 110/390] [core] Use selectors instead of direct state access in feature hooks and components --- .../grid/components/containers/GridRoot.tsx | 6 +- .../panel/filterPanel/GridFilterPanel.tsx | 20 +++---- .../hooks/features/columns/useGridColumns.ts | 44 +++++++------- .../grid/hooks/features/core/gridState.ts | 11 ++-- .../hooks/features/density/densitySelector.ts | 11 ++-- .../hooks/features/density/densityState.ts | 4 +- .../hooks/features/density/useGridDensity.tsx | 55 ++++++++--------- .../features/filter/gridFilterSelector.ts | 4 +- .../hooks/features/filter/useGridFilter.ts | 6 +- .../features/selection/useGridSelection.ts | 2 +- .../hooks/features/sorting/useGridSorting.ts | 60 ++++++++++--------- .../features/virtualization/renderingState.ts | 4 +- .../virtualization/useGridVirtualization.ts | 4 +- 13 files changed, 117 insertions(+), 114 deletions(-) diff --git a/packages/grid/_modules_/grid/components/containers/GridRoot.tsx b/packages/grid/_modules_/grid/components/containers/GridRoot.tsx index 92b691083fe8e..db79e62fda49b 100644 --- a/packages/grid/_modules_/grid/components/containers/GridRoot.tsx +++ b/packages/grid/_modules_/grid/components/containers/GridRoot.tsx @@ -6,10 +6,10 @@ import { GridRootContainerRef } from '../../models/gridRootContainerRef'; import { useStyles } from './GridRootStyles'; import { visibleGridColumnsLengthSelector } from '../../hooks/features/columns/gridColumnsSelector'; import { useGridSelector } from '../../hooks/features/core/useGridSelector'; -import { useGridState } from '../../hooks/features/core/useGridState'; import { useGridApiContext } from '../../hooks/root/useGridApiContext'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { gridClasses } from '../../gridClasses'; +import { gridRowCountSelector } from '../../hooks/features/rows/gridRowsSelector'; export type GridRootProps = React.HTMLAttributes; @@ -22,7 +22,7 @@ export const GridRoot = React.forwardRef(function const rootProps = useGridRootProps(); const { children, className: classNameProp, ...other } = props; const visibleColumnsLength = useGridSelector(apiRef, visibleGridColumnsLengthSelector); - const [gridState] = useGridState(apiRef); + const totalRowCount = useGridSelector(apiRef, gridRowCountSelector); const rootContainerRef: GridRootContainerRef = React.useRef(null); const handleRef = useForkRef(rootContainerRef, ref); @@ -44,7 +44,7 @@ export const GridRoot = React.forwardRef(function )} role="grid" aria-colcount={visibleColumnsLength} - aria-rowcount={gridState.rows.totalRowCount} + aria-rowcount={totalRowCount} aria-multiselectable={!rootProps.disableMultipleSelection} aria-label={rootProps['aria-label']} aria-labelledby={rootProps['aria-labelledby']} diff --git a/packages/grid/_modules_/grid/components/panel/filterPanel/GridFilterPanel.tsx b/packages/grid/_modules_/grid/components/panel/filterPanel/GridFilterPanel.tsx index f431712533926..aabdb7a352be9 100644 --- a/packages/grid/_modules_/grid/components/panel/filterPanel/GridFilterPanel.tsx +++ b/packages/grid/_modules_/grid/components/panel/filterPanel/GridFilterPanel.tsx @@ -1,24 +1,22 @@ import * as React from 'react'; import Button from '@mui/material/Button'; -import { useGridState } from '../../../hooks/features/core/useGridState'; import { GridFilterItem, GridLinkOperator } from '../../../models/gridFilterItem'; import { useGridApiContext } from '../../../hooks/root/useGridApiContext'; -import { GridAddIcon } from '../../icons/index'; +import { GridAddIcon } from '../../icons'; import { GridPanelContent } from '../GridPanelContent'; import { GridPanelFooter } from '../GridPanelFooter'; import { GridPanelWrapper } from '../GridPanelWrapper'; import { GridFilterForm } from './GridFilterForm'; import { useGridRootProps } from '../../../hooks/utils/useGridRootProps'; +import { useGridSelector } from '../../../hooks/features/core/useGridSelector'; +import { gridFilterModelSelector } from '../../../hooks/features/filter/gridFilterSelector'; export function GridFilterPanel() { const apiRef = useGridApiContext(); - const [gridState] = useGridState(apiRef); const rootProps = useGridRootProps(); + const filterModel = useGridSelector(apiRef, gridFilterModelSelector); - const hasMultipleFilters = React.useMemo( - () => gridState.filter.items.length > 1, - [gridState.filter.items.length], - ); + const hasMultipleFilters = filterModel.items.length > 1; const applyFilter = React.useCallback( (item: GridFilterItem) => { @@ -46,15 +44,15 @@ export function GridFilterPanel() { ); React.useEffect(() => { - if (gridState.filter.items.length === 0) { + if (filterModel.items.length === 0) { addNewFilter(); } - }, [addNewFilter, gridState.filter.items.length]); + }, [addNewFilter, filterModel.items.length]); return ( - {gridState.filter.items.map((item, index) => ( + {filterModel.items.map((item, index) => ( 0} - multiFilterOperator={gridState.filter.linkOperator} + multiFilterOperator={filterModel.linkOperator} disableMultiFilterOperator={index !== 1} applyMultiFilterOperatorChanges={applyFilterLinkOperator} /> diff --git a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts index 13fc8e5c32cfe..363fcd6590a49 100644 --- a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts +++ b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts @@ -16,12 +16,13 @@ import { GridColumnOrderChangeParams } from '../../../models/params/gridColumnOr import { mergeGridColTypes } from '../../../utils/mergeUtils'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; -import { useGridSelector } from '../core/useGridSelector'; import { GridLocaleText, GridTranslationKeys } from '../../../models/api/gridLocaleTextApi'; import { useGridState } from '../core/useGridState'; import { + allGridColumnsFieldsSelector, allGridColumnsSelector, gridColumnsMetaSelector, + gridColumnsSelector, visibleGridColumnsSelector, } from './gridColumnsSelector'; import { useGridApiOptionHandler } from '../../root/useGridApiEventHandler'; @@ -156,9 +157,6 @@ export function useGridColumns( ): void { const logger = useGridLogger(apiRef, 'useGridColumns'); const [gridState, setGridState, forceUpdate] = useGridState(apiRef); - const columnsMeta = useGridSelector(apiRef, gridColumnsMetaSelector); - const allColumns = useGridSelector(apiRef, allGridColumnsSelector); - const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); const ownerState = { classes: props.classes }; const classes = useUtilityClasses(ownerState); @@ -182,32 +180,35 @@ export function useGridColumns( ); const getAllColumns = React.useCallback( - () => allColumns, - [allColumns], + () => allGridColumnsSelector(apiRef.current.state), + [apiRef], ); const getVisibleColumns = React.useCallback( - () => visibleColumns, - [visibleColumns], + () => visibleGridColumnsSelector(apiRef.current.state), + [apiRef], ); const getColumnsMeta = React.useCallback( - () => columnsMeta, - [columnsMeta], + () => gridColumnsMetaSelector(apiRef.current.state), + [apiRef], ); const getColumnIndex = React.useCallback( - (field: string, useVisibleColumns: boolean = true): number => - useVisibleColumns - ? visibleColumns.findIndex((col) => col.field === field) - : allColumns.findIndex((col) => col.field === field), - [allColumns, visibleColumns], + (field: string, useVisibleColumns: boolean = true): number => { + const columns = useVisibleColumns + ? visibleGridColumnsSelector(apiRef.current.state) + : allGridColumnsSelector(apiRef.current.state); + + return columns.findIndex((col) => col.field === field); + }, + [apiRef], ); const getColumnPosition: (field: string) => number = React.useCallback( (field) => { const index = getColumnIndex(field); - return columnsMeta.positions[index]; + return gridColumnsMetaSelector(apiRef.current.state).positions[index]; }, - [columnsMeta.positions, getColumnIndex], + [apiRef, getColumnIndex], ); const setColumnsState = React.useCallback( @@ -264,16 +265,17 @@ export function useGridColumns( const setColumnIndex = React.useCallback( (field: string, targetIndexPosition: number) => { - const oldIndexPosition = gridState.columns.all.findIndex((col) => col === field); + const allColumns = allGridColumnsFieldsSelector(apiRef.current.state); + const oldIndexPosition = allColumns.findIndex((col) => col === field); if (oldIndexPosition === targetIndexPosition) { return; } logger.debug(`Moving column ${field} to index ${targetIndexPosition}`); - const updatedColumns = [...gridState.columns.all]; + const updatedColumns = [...allColumns]; updatedColumns.splice(targetIndexPosition, 0, updatedColumns.splice(oldIndexPosition, 1)[0]); - setGridColumnsState({ ...gridState.columns, all: updatedColumns }); + setGridColumnsState({ ...gridColumnsSelector(apiRef.current.state), all: updatedColumns }); const params: GridColumnOrderChangeParams = { field, @@ -284,7 +286,7 @@ export function useGridColumns( }; apiRef.current.publishEvent(GridEvents.columnOrderChange, params); }, - [apiRef, gridState.columns, logger, setGridColumnsState], + [apiRef, logger, setGridColumnsState], ); const setColumnWidth = React.useCallback( diff --git a/packages/grid/_modules_/grid/hooks/features/core/gridState.ts b/packages/grid/_modules_/grid/hooks/features/core/gridState.ts index 66ed4a5daed58..7360aa0213dae 100644 --- a/packages/grid/_modules_/grid/hooks/features/core/gridState.ts +++ b/packages/grid/_modules_/grid/hooks/features/core/gridState.ts @@ -15,7 +15,7 @@ import { getInitialGridColumnResizeState, GridColumnResizeState, } from '../columnResize/columnResizeState'; -import { GridGridDensity, getInitialGridDensityState } from '../density/densityState'; +import { GridDensityState, getInitialGridDensityState } from '../density/densityState'; import { getInitialGridFilterState } from '../filter/gridFilterModelState'; import { getInitialVisibleGridRowsState, @@ -26,10 +26,7 @@ import { GridPreferencePanelState } from '../preferencesPanel/gridPreferencePane import { getInitialGridRowState, GridRowsState } from '../rows/gridRowsState'; import { GridSelectionModel } from '../../../models/gridSelectionModel'; import { getInitialGridSortingState, GridSortingState } from '../sorting/gridSortingState'; -import { - getInitialGridRenderingState, - InternalRenderingState, -} from '../virtualization/renderingState'; +import { getInitialGridRenderingState, GridRenderingState } from '../virtualization/renderingState'; import { getInitialPaginationState, GridPaginationState } from '../pagination/gridPaginationState'; export interface GridState { @@ -40,7 +37,7 @@ export interface GridState { columnReorder: GridColumnReorderState; columnResize: GridColumnResizeState; columnMenu: GridColumnMenuState; - rendering: InternalRenderingState; + rendering: GridRenderingState; containerSizes: GridContainerProps | null; viewportSizes: GridViewportSizeState; scrollBar: GridScrollBarState; @@ -51,7 +48,7 @@ export interface GridState { filter: GridFilterModel; visibleRows: VisibleGridRowsState; preferencePanel: GridPreferencePanelState; - density: GridGridDensity; + density: GridDensityState; error?: any; } diff --git a/packages/grid/_modules_/grid/hooks/features/density/densitySelector.ts b/packages/grid/_modules_/grid/hooks/features/density/densitySelector.ts index b18495283c8a6..0e7581a2da143 100644 --- a/packages/grid/_modules_/grid/hooks/features/density/densitySelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/density/densitySelector.ts @@ -1,16 +1,19 @@ import { createSelector } from 'reselect'; import { GridState } from '../core/gridState'; -export const densitySelector = (state: GridState) => state.density; +export const gridDensitySelector = (state: GridState) => state.density; -export const gridDensityValueSelector = createSelector(densitySelector, (density) => density.value); +export const gridDensityValueSelector = createSelector( + gridDensitySelector, + (density) => density.value, +); export const gridDensityRowHeightSelector = createSelector( - densitySelector, + gridDensitySelector, (density) => density.rowHeight, ); export const gridDensityHeaderHeightSelector = createSelector( - densitySelector, + gridDensitySelector, (density) => density.headerHeight, ); diff --git a/packages/grid/_modules_/grid/hooks/features/density/densityState.ts b/packages/grid/_modules_/grid/hooks/features/density/densityState.ts index 5cca5b290ce1d..f550802ac121f 100644 --- a/packages/grid/_modules_/grid/hooks/features/density/densityState.ts +++ b/packages/grid/_modules_/grid/hooks/features/density/densityState.ts @@ -1,12 +1,12 @@ import { GridDensity, GridDensityTypes } from '../../../models/gridDensity'; -export interface GridGridDensity { +export interface GridDensityState { value: GridDensity; rowHeight: number; headerHeight: number; } -export function getInitialGridDensityState(): GridGridDensity { +export function getInitialGridDensityState(): GridDensityState { return { value: GridDensityTypes.Standard, rowHeight: 52, diff --git a/packages/grid/_modules_/grid/hooks/features/density/useGridDensity.tsx b/packages/grid/_modules_/grid/hooks/features/density/useGridDensity.tsx index 1ceab984afced..17f88b8664a36 100644 --- a/packages/grid/_modules_/grid/hooks/features/density/useGridDensity.tsx +++ b/packages/grid/_modules_/grid/hooks/features/density/useGridDensity.tsx @@ -5,12 +5,39 @@ import { GridApiRef } from '../../../models/api/gridApiRef'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridState } from '../core/useGridState'; import { GridDensityApi } from '../../../models/api/gridDensityApi'; -import { GridGridDensity } from './densityState'; +import { GridDensityState } from './densityState'; import { GridComponentProps } from '../../../GridComponentProps'; export const COMPACT_DENSITY_FACTOR = 0.7; export const COMFORTABLE_DENSITY_FACTOR = 1.3; +const getUpdatedDensityState = ( + newDensity: GridDensity, + newHeaderHeight: number, + newRowHeight: number, +): GridDensityState => { + switch (newDensity) { + case GridDensityTypes.Compact: + return { + value: newDensity, + headerHeight: Math.floor(newHeaderHeight * COMPACT_DENSITY_FACTOR), + rowHeight: Math.floor(newRowHeight * COMPACT_DENSITY_FACTOR), + }; + case GridDensityTypes.Comfortable: + return { + value: newDensity, + headerHeight: Math.floor(newHeaderHeight * COMFORTABLE_DENSITY_FACTOR), + rowHeight: Math.floor(newRowHeight * COMFORTABLE_DENSITY_FACTOR), + }; + default: + return { + value: newDensity, + headerHeight: newHeaderHeight, + rowHeight: newRowHeight, + }; + } +}; + export const useGridDensity = ( apiRef: GridApiRef, props: Pick, @@ -18,32 +45,6 @@ export const useGridDensity = ( const logger = useGridLogger(apiRef, 'useDensity'); const [, setGridState, forceUpdate] = useGridState(apiRef); - const getUpdatedDensityState = React.useCallback( - (newDensity: GridDensity, newHeaderHeight: number, newRowHeight: number): GridGridDensity => { - switch (newDensity) { - case GridDensityTypes.Compact: - return { - value: newDensity, - headerHeight: Math.floor(newHeaderHeight * COMPACT_DENSITY_FACTOR), - rowHeight: Math.floor(newRowHeight * COMPACT_DENSITY_FACTOR), - }; - case GridDensityTypes.Comfortable: - return { - value: newDensity, - headerHeight: Math.floor(newHeaderHeight * COMFORTABLE_DENSITY_FACTOR), - rowHeight: Math.floor(newRowHeight * COMFORTABLE_DENSITY_FACTOR), - }; - default: - return { - value: newDensity, - headerHeight: newHeaderHeight, - rowHeight: newRowHeight, - }; - } - }, - [], - ); - const setDensity = React.useCallback( ( newDensity: GridDensity, diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index 529a1e4d2480a..b924c8d644b3c 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -43,10 +43,10 @@ export const visibleGridRowCountSelector = createSelector( }, ); -export const filterGridStateSelector = (state: GridState) => state.filter; +export const gridFilterModelSelector = (state: GridState) => state.filter; export const activeGridFilterItemsSelector = createSelector( - filterGridStateSelector, + gridFilterModelSelector, gridColumnLookupSelector, (filterModel, columnLookup) => filterModel.items?.filter((item) => { diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 9d128695f15e8..16442507baf63 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -17,7 +17,7 @@ import { GridPreferencePanelsValue } from '../preferencesPanel/gridPreferencePan import { sortedGridRowsSelector } from '../sorting/gridSortingSelector'; import { getInitialGridFilterState } from './gridFilterModelState'; import { GridFilterModel } from '../../../models/gridFilterModel'; -import { visibleSortedGridRowsSelector } from './gridFilterSelector'; +import { gridFilterModelSelector, visibleSortedGridRowsSelector } from './gridFilterSelector'; import { getInitialVisibleGridRowsState } from './visibleGridRowsState'; /** @@ -278,11 +278,11 @@ export const useGridFilter = ( const onColUpdated = React.useCallback(() => { logger.debug('onColUpdated - GridColumns changed, applying filters'); - const filterState = apiRef.current.state.filter; + const filterModel = gridFilterModelSelector(apiRef.current.state); const columnsIds = filterableGridColumnsIdsSelector(apiRef.current.state); logger.debug('GridColumns changed, applying filters'); - filterState.items.forEach((filter) => { + filterModel.items.forEach((filter) => { if (!columnsIds.find((field) => field === filter.columnField)) { apiRef.current.deleteFilter(filter); } diff --git a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts index dfd9d13830ac0..697e8dea029b2 100644 --- a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts +++ b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts @@ -114,7 +114,7 @@ export const useGridSelection = (apiRef: GridApiRef, props: GridComponentProps): const setSelectionModel = React.useCallback( (model) => { - const currentModel = apiRef.current.state.selection; + const currentModel = gridSelectionStateSelector(apiRef.current.state); if (currentModel !== model) { setGridState((state) => ({ ...state, selection: model })); forceUpdate(); diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index ee8bec88e0bae..ab45e0d06ed36 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -7,7 +7,7 @@ import { GridCellValue } from '../../../models/gridCell'; import { GridColDef } from '../../../models/colDef/gridColDef'; import { GridFeatureModeConstant } from '../../../models/gridFeatureMode'; import { GridColumnHeaderParams } from '../../../models/params/gridColumnHeaderParams'; -import { GridRowId, GridRowModel } from '../../../models/gridRows'; +import { GridRowId } from '../../../models/gridRows'; import { GridFieldComparatorList, GridSortItem, @@ -22,7 +22,11 @@ import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; import { allGridColumnsSelector } from '../columns/gridColumnsSelector'; import { useGridState } from '../core/useGridState'; -import { sortedGridRowIdsSelector, sortedGridRowsSelector } from './gridSortingSelector'; +import { + gridSortModelSelector, + sortedGridRowIdsSelector, + sortedGridRowsSelector, +} from './gridSortingSelector'; /** * @requires useGridRows (state, event) @@ -42,12 +46,13 @@ export const useGridSorting = ( ) => { const logger = useGridLogger(apiRef, 'useGridSorting'); - const [gridState, setGridState, forceUpdate] = useGridState(apiRef); + const [, setGridState, forceUpdate] = useGridState(apiRef); const upsertSortModel = React.useCallback( (field: string, sortItem?: GridSortItem): GridSortModel => { - const existingIdx = gridState.sorting.sortModel.findIndex((c) => c.field === field); - let newSortModel = [...gridState.sorting.sortModel]; + const sortModel = gridSortModelSelector(apiRef.current.state); + const existingIdx = sortModel.findIndex((c) => c.field === field); + let newSortModel = [...sortModel]; if (existingIdx > -1) { if (!sortItem) { newSortModel.splice(existingIdx, 1); @@ -55,16 +60,17 @@ export const useGridSorting = ( newSortModel.splice(existingIdx, 1, sortItem); } } else { - newSortModel = [...gridState.sorting.sortModel, sortItem!]; + newSortModel = [...sortModel, sortItem!]; } return newSortModel; }, - [gridState.sorting.sortModel], + [apiRef], ); const createSortItem = React.useCallback( (col: GridColDef, directionOverride?: GridSortDirection): GridSortItem | undefined => { - const existing = gridState.sorting.sortModel.find((c) => c.field === col.field); + const sortModel = gridSortModelSelector(apiRef.current.state); + const existing = sortModel.find((c) => c.field === col.field); if (existing) { const nextSort = @@ -82,7 +88,7 @@ export const useGridSorting = ( : directionOverride, }; }, - [gridState.sorting.sortModel, props.sortingOrder], + [apiRef, props.sortingOrder], ); const getSortCellParams = React.useCallback( @@ -149,16 +155,14 @@ export const useGridSorting = ( if (props.sortingMode === GridFeatureModeConstant.server) { logger.debug('Skipping sorting rows as sortingMode = server'); - setGridState((state) => { - return { - ...state, - sorting: { ...state.sorting, sortedRows }, - }; - }); + setGridState((state) => ({ + ...state, + sorting: { ...state.sorting, sortedRows }, + })); return; } - const sortModel = apiRef.current.state.sorting.sortModel; + const sortModel = gridSortModelSelector(apiRef.current.state); if (sortModel.length > 0) { const comparatorList = buildComparatorList(sortModel); @@ -173,12 +177,10 @@ export const useGridSorting = ( .map((field) => field[0].id); } - setGridState((state) => { - return { - ...state, - sorting: { ...state.sorting, sortedRows }, - }; - }); + setGridState((state) => ({ + ...state, + sorting: { ...state.sorting, sortedRows }, + })); forceUpdate(); }, [ apiRef, @@ -219,18 +221,18 @@ export const useGridSorting = ( [upsertSortModel, setSortModel, createSortItem, props.disableMultipleColumnsSorting], ); - const getSortModel = React.useCallback( - () => gridState.sorting.sortModel, - [gridState.sorting.sortModel], + const getSortModel = React.useCallback( + () => gridSortModelSelector(apiRef.current.state), + [apiRef], ); - const getSortedRows = React.useCallback( - (): GridRowModel[] => Object.values(sortedGridRowsSelector(apiRef.current.state)), + const getSortedRows = React.useCallback( + () => Object.values(sortedGridRowsSelector(apiRef.current.state)), [apiRef], ); - const getSortedRowIds = React.useCallback( - (): GridRowId[] => sortedGridRowIdsSelector(apiRef.current.state), + const getSortedRowIds = React.useCallback( + () => sortedGridRowIdsSelector(apiRef.current.state), [apiRef], ); diff --git a/packages/grid/_modules_/grid/hooks/features/virtualization/renderingState.ts b/packages/grid/_modules_/grid/hooks/features/virtualization/renderingState.ts index 1e570b4f30afe..2a82436e43af5 100644 --- a/packages/grid/_modules_/grid/hooks/features/virtualization/renderingState.ts +++ b/packages/grid/_modules_/grid/hooks/features/virtualization/renderingState.ts @@ -1,7 +1,7 @@ import { GridScrollParams } from '../../../models/params/gridScrollParams'; import { GridRenderContextProps } from '../../../models/gridRenderContextProps'; -export interface InternalRenderingState { +export interface GridRenderingState { virtualPage: number; virtualRowsCount: number; renderContext: Partial | null; @@ -9,7 +9,7 @@ export interface InternalRenderingState { renderingZoneScroll: GridScrollParams; } -export const getInitialGridRenderingState = (): InternalRenderingState => { +export const getInitialGridRenderingState = (): GridRenderingState => { return { realScroll: { left: 0, top: 0 }, renderContext: null, diff --git a/packages/grid/_modules_/grid/hooks/features/virtualization/useGridVirtualization.ts b/packages/grid/_modules_/grid/hooks/features/virtualization/useGridVirtualization.ts index 06c591501303f..f8b5b48d71e6b 100644 --- a/packages/grid/_modules_/grid/hooks/features/virtualization/useGridVirtualization.ts +++ b/packages/grid/_modules_/grid/hooks/features/virtualization/useGridVirtualization.ts @@ -22,7 +22,7 @@ import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useNativeEventListener } from '../../root/useNativeEventListener'; import { useGridLogger } from '../../utils/useGridLogger'; import { useGridScrollFn } from '../../utils/useGridScrollFn'; -import { InternalRenderingState } from './renderingState'; +import { GridRenderingState } from './renderingState'; import { GridComponentProps } from '../../../GridComponentProps'; import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; @@ -82,7 +82,7 @@ export const useGridVirtualization = ( const [scrollTo] = useGridScrollFn(apiRef, renderingZoneRef, colRef); const setRenderingState = React.useCallback( - (newState: Partial) => { + (newState: Partial) => { let stateChanged = false; setGridState((state) => { const currentRenderingState = { ...state.rendering, ...newState }; From 53a7f64d3f9a97e0e59eb3cfb8e0f355536be7ee Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 28 Sep 2021 12:28:18 +0200 Subject: [PATCH 111/390] Remove useGridRegisterControlState --- .../grid/hooks/features/filter/useGridFilter.ts | 3 +-- .../grid/hooks/features/pagination/useGridPage.ts | 3 +-- .../grid/hooks/features/pagination/useGridPageSize.ts | 3 +-- .../grid/hooks/features/rows/useGridEditRows.ts | 3 +-- .../grid/hooks/features/selection/useGridSelection.ts | 4 ++-- .../grid/hooks/features/sorting/useGridSorting.ts | 2 +- .../grid/hooks/utils/useGridRegisterControlState.ts | 11 ----------- 7 files changed, 7 insertions(+), 22 deletions(-) delete mode 100644 packages/grid/_modules_/grid/hooks/utils/useGridRegisterControlState.ts diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index c8a8c012fdb6b..de8b280c6981a 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -21,7 +21,6 @@ import { visibleSortedGridRowsSelector, gridFilterModelSelector, } from './gridFilterSelector'; -import { useGridRegisterControlState } from '../../utils/useGridRegisterControlState'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; @@ -68,7 +67,7 @@ export const useGridFilter = ( const [, setGridState, forceUpdate] = useGridState(apiRef); - useGridRegisterControlState(apiRef, { + apiRef.current.updateControlState({ stateId: 'filter', propModel: props.filterModel, propOnChange: props.onFilterModelChange, diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts index 59327d3c64b51..8da1d1b795545 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts @@ -10,7 +10,6 @@ import { GridPageApi } from '../../../models/api/gridPageApi'; import { GridPaginationState } from './gridPaginationState'; import { visibleGridRowCountSelector } from '../filter'; import { useGridStateInit } from '../../utils/useGridStateInit'; -import { useGridRegisterControlState } from '../../utils/useGridRegisterControlState'; import { gridPageSelector } from './gridPaginationSelector'; const getPageCount = (rowCount: number, pageSize: number): number => { @@ -56,7 +55,7 @@ export const useGridPage = ( const visibleRowCount = useGridSelector(apiRef, visibleGridRowCountSelector); - useGridRegisterControlState(apiRef, { + apiRef.current.updateControlState({ stateId: 'page', propModel: props.page, propOnChange: props.onPageChange, diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts index e39b0b83ecbd0..f463a5117e57b 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts @@ -8,7 +8,6 @@ import { useGridLogger } from '../../utils'; import { useGridSelector, useGridState } from '../core'; import { gridContainerSizesSelector } from '../../root/gridContainerSizesSelector'; import { useGridStateInit } from '../../utils/useGridStateInit'; -import { useGridRegisterControlState } from '../../utils/useGridRegisterControlState'; import { gridPageSizeSelector } from './gridPaginationSelector'; /** @@ -30,7 +29,7 @@ export const useGridPageSize = ( const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); - useGridRegisterControlState(apiRef, { + apiRef.current.updateControlState({ stateId: 'pageSize', propModel: props.pageSize, propOnChange: props.onPageSizeChange, diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts index 452144833be6f..d9b2b36a3ba66 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts @@ -34,7 +34,6 @@ import { useEventCallback } from '../../../utils/material-ui-utils'; import { useGridLogger } from '../../utils/useGridLogger'; import { useGridState } from '../core/useGridState'; import { useGridSelector } from '../core/useGridSelector'; -import { useGridRegisterControlState } from '../../utils/useGridRegisterControlState'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { gridEditRowsStateSelector } from './gridEditRowsSelector'; @@ -69,7 +68,7 @@ export function useGridEditRows( const nextFocusedCell = React.useRef(null); const columns = useGridSelector(apiRef, allGridColumnsSelector); - useGridRegisterControlState(apiRef, { + apiRef.current.updateControlState({ stateId: 'editRows', propModel: props.editRowsModel, propOnChange: props.onEditRowsModelChange, diff --git a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts index 452bc54c2592d..c33f76728d19e 100644 --- a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts +++ b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts @@ -16,7 +16,6 @@ import { selectedGridRowsSelector, selectedIdsLookupSelector, } from './gridSelectionSelector'; -import { useGridRegisterControlState } from '../../utils/useGridRegisterControlState'; import { useGridStateInit } from '../../utils/useGridStateInit'; /** @@ -44,7 +43,7 @@ export const useGridSelection = (apiRef: GridApiRef, props: GridComponentProps): const [, setGridState, forceUpdate] = useGridState(apiRef); const rowsLookup = useGridSelector(apiRef, gridRowsLookupSelector); - const isStateControlled = useGridRegisterControlState(apiRef, { + apiRef.current.updateControlState({ stateId: 'selection', propModel: propSelectionModel, propOnChange: props.onSelectionModelChange, @@ -205,6 +204,7 @@ export const useGridSelection = (apiRef: GridApiRef, props: GridComponentProps): } }, [apiRef, propSelectionModel]); + const isStateControlled = propSelectionModel != null; React.useEffect(() => { if (isStateControlled) { return; diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 6425518437fe6..7d582237c7d8a 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -59,7 +59,7 @@ export const useGridSorting = ( const [, setGridState, forceUpdate] = useGridState(apiRef); - useGridRegisterControlState(apiRef, { + apiRef.current.updateControlState({ stateId: 'sortModel', propModel: props.sortModel, propOnChange: props.onSortModelChange, diff --git a/packages/grid/_modules_/grid/hooks/utils/useGridRegisterControlState.ts b/packages/grid/_modules_/grid/hooks/utils/useGridRegisterControlState.ts deleted file mode 100644 index 26808b9fb0f6a..0000000000000 --- a/packages/grid/_modules_/grid/hooks/utils/useGridRegisterControlState.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { GridApiRef } from '../../models/api/gridApiRef'; -import { GridControlStateItem } from '../../models/controlStateItem'; - -export const useGridRegisterControlState = ( - apiRef: GridApiRef, - controlStateItem: GridControlStateItem, -) => { - apiRef.current.updateControlState(controlStateItem); - - return controlStateItem.propModel !== undefined; -}; From f8652293a72775e39e2b1e4cb3dd1c8cc9e0b09e Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 28 Sep 2021 12:29:02 +0200 Subject: [PATCH 112/390] Lint --- .../grid/hooks/features/density/useGridDensity.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/density/useGridDensity.tsx b/packages/grid/_modules_/grid/hooks/features/density/useGridDensity.tsx index 17f88b8664a36..e88e7cf0567e8 100644 --- a/packages/grid/_modules_/grid/hooks/features/density/useGridDensity.tsx +++ b/packages/grid/_modules_/grid/hooks/features/density/useGridDensity.tsx @@ -61,14 +61,7 @@ export const useGridDensity = ( })); forceUpdate(); }, - [ - logger, - setGridState, - forceUpdate, - getUpdatedDensityState, - props.headerHeight, - props.rowHeight, - ], + [logger, setGridState, forceUpdate, props.headerHeight, props.rowHeight], ); React.useEffect(() => { From 17d14c91410504f15ec369fa64de28e9892736d0 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 28 Sep 2021 12:37:08 +0200 Subject: [PATCH 113/390] Fix --- .../grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 7d582237c7d8a..9f85caf79096c 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -28,7 +28,6 @@ import { sortedGridRowsSelector, } from './gridSortingSelector'; import { useGridStateInit } from '../../utils/useGridStateInit'; -import { useGridRegisterControlState } from '../../utils/useGridRegisterControlState'; import { useFirstRender } from '../../utils/useFirstRender'; /** From 02aad4a455e072c5833c7dc507bdc710e8657f5a Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 28 Sep 2021 13:29:02 +0200 Subject: [PATCH 114/390] Remove useless sort model update --- .../hooks/features/sorting/useGridSorting.ts | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 9f85caf79096c..370a68e5b9ace 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -294,23 +294,19 @@ export const useGridSorting = ( const onColUpdated = React.useCallback(() => { // When the columns change we check that the sorted columns are still part of the dataset - setGridState((state) => { - const sortModel = state.sorting.sortModel; - const latestColumns = allGridColumnsSelector(state); - let newModel = sortModel; - if (sortModel.length > 0) { - newModel = sortModel.reduce((model, sortedCol) => { - const exist = latestColumns.find((col) => col.field === sortedCol.field); - if (exist) { - model.push(sortedCol); - } - return model; - }, [] as GridSortModel); - } + const sortModel = gridSortModelSelector(apiRef.current.state); + const latestColumns = allGridColumnsSelector(apiRef.current.state); - return { ...state, sorting: { ...state.sorting, sortModel: newModel } }; - }); - }, [setGridState]); + if (sortModel.length > 0) { + const newModel = sortModel.filter((sortItem) => + latestColumns.find((col) => col.field === sortItem.field), + ); + + if (newModel.length < sortModel.length) { + apiRef.current.setSortModel(newModel); + } + } + }, [apiRef]); useGridApiEventHandler(apiRef, GridEvents.columnHeaderClick, handleColumnHeaderClick); useGridApiEventHandler(apiRef, GridEvents.columnHeaderKeyDown, handleColumnHeaderKeyDown); From cd367bc1ba4b1f0340e6d73cee1afb4821597ee4 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 28 Sep 2021 13:37:33 +0200 Subject: [PATCH 115/390] [bug] onSortModelChange should not been called on initialization and on prop model change --- .../hooks/features/sorting/useGridSorting.ts | 34 +++++++++--------- .../src/tests/sorting.DataGridPro.test.tsx | 36 +++++++++++++++++++ 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index ee8bec88e0bae..e4516c7538d49 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -22,7 +22,11 @@ import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; import { allGridColumnsSelector } from '../columns/gridColumnsSelector'; import { useGridState } from '../core/useGridState'; -import { sortedGridRowIdsSelector, sortedGridRowsSelector } from './gridSortingSelector'; +import { + gridSortModelSelector, + sortedGridRowIdsSelector, + sortedGridRowsSelector, +} from './gridSortingSelector'; /** * @requires useGridRows (state, event) @@ -281,23 +285,19 @@ export const useGridSorting = ( const onColUpdated = React.useCallback(() => { // When the columns change we check that the sorted columns are still part of the dataset - setGridState((state) => { - const sortModel = state.sorting.sortModel; - const latestColumns = allGridColumnsSelector(state); - let newModel = sortModel; - if (sortModel.length > 0) { - newModel = sortModel.reduce((model, sortedCol) => { - const exist = latestColumns.find((col) => col.field === sortedCol.field); - if (exist) { - model.push(sortedCol); - } - return model; - }, [] as GridSortModel); - } + const sortModel = gridSortModelSelector(apiRef.current.state); + const latestColumns = allGridColumnsSelector(apiRef.current.state); - return { ...state, sorting: { ...state.sorting, sortModel: newModel } }; - }); - }, [setGridState]); + if (sortModel.length > 0) { + const newModel = sortModel.filter((sortItem) => + latestColumns.find((col) => col.field === sortItem.field), + ); + + if (newModel.length < sortModel.length) { + apiRef.current.setSortModel(newModel); + } + } + }, [apiRef]); useGridApiEventHandler(apiRef, GridEvents.columnHeaderClick, handleColumnHeaderClick); useGridApiEventHandler(apiRef, GridEvents.columnHeaderKeyDown, handleColumnHeaderKeyDown); diff --git a/packages/grid/x-grid/src/tests/sorting.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/sorting.DataGridPro.test.tsx index 3fb6d3823962e..b293d70463b72 100644 --- a/packages/grid/x-grid/src/tests/sorting.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/sorting.DataGridPro.test.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { DataGridPro, + DataGridProProps, GridApiRef, GridComponentProps, GridSortModel, @@ -308,5 +309,40 @@ describe(' - Sorting', () => { expect(getColumnValues()).to.deep.equal(['Adidas', 'Nike', 'Puma']); expect(expectedModel).to.deep.equal([{ field: 'brand', sort: 'asc' }]); }); + + it('should not call onSortModelChange on initialisation or on sortModel prop change', () => { + const onSortModelChange = spy(); + + const Test = (props: Partial) => ( +
+ +
+ ); + + const { setProps } = render( + , + ); + + expect(onSortModelChange.callCount).to.equal(0); + setProps({ + sortModel: [ + { field: 'year', sort: 'asc' }, + { field: 'brand', sort: 'asc' }, + ], + }); + + expect(onSortModelChange.callCount).to.equal(0); + }); }); }); From fc45eb9bd3263587bf6129dd83b179f100b01f99 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 28 Sep 2021 14:08:58 +0200 Subject: [PATCH 116/390] Fix lint --- packages/grid/_modules_/grid/GridComponentProps.ts | 2 +- packages/grid/_modules_/grid/models/api/gridFilterApi.ts | 2 +- packages/storybook/src/stories/grid-tree-data.stories.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/grid/_modules_/grid/GridComponentProps.ts b/packages/grid/_modules_/grid/GridComponentProps.ts index 358d167d9876b..732f0710fccdd 100644 --- a/packages/grid/_modules_/grid/GridComponentProps.ts +++ b/packages/grid/_modules_/grid/GridComponentProps.ts @@ -101,7 +101,7 @@ interface GridComponentOtherProps { isRowSelectable?: (params: GridRowParams) => boolean; /** * Determines the path of a row in the tree data - * @param {GridRowParams} params With all properties from [[GridRowParams]]. + * @param {GridRowModel} row The row from which we want the path. * @returns {string[]} the path to the row */ getTreeDataPath?: (row: GridRowModel) => string[]; diff --git a/packages/grid/_modules_/grid/models/api/gridFilterApi.ts b/packages/grid/_modules_/grid/models/api/gridFilterApi.ts index 0863f376afd92..5cc3d259fbaa9 100644 --- a/packages/grid/_modules_/grid/models/api/gridFilterApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridFilterApi.ts @@ -24,7 +24,7 @@ export interface GridFilterApi { * Applies a [[GridFilterItem]] on all rows. If no `linkOperator` is given, the "and" operator is used. * @param {GridFilterItem} item The filter to be applied. * @param {GridLinkOperator} linkOperator The link operator to use. - * @return {boolean} Returns `true` if the filter has been applied + * @returns {boolean} Returns `true` if the filter has been applied */ applyFilter: (item: GridFilterItem, linkOperator?: GridLinkOperator) => boolean; /** diff --git a/packages/storybook/src/stories/grid-tree-data.stories.tsx b/packages/storybook/src/stories/grid-tree-data.stories.tsx index 2a851c8b02ab6..13715d5af8eac 100644 --- a/packages/storybook/src/stories/grid-tree-data.stories.tsx +++ b/packages/storybook/src/stories/grid-tree-data.stories.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { DataGridPro, GridColumns, DataGridProProps } from '@mui/x-data-grid-pro'; +import { DataGridPro, DataGridProProps } from '@mui/x-data-grid-pro'; import { Meta } from '@storybook/react'; import Button from '@mui/material/Button'; From bde994c652bd6bee0cf85c0b9a9349f2a6b4df5d Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 28 Sep 2021 14:16:16 +0200 Subject: [PATCH 117/390] Regen proptypes --- packages/grid/data-grid/src/DataGrid.tsx | 2 +- packages/grid/x-grid/src/DataGridPro.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/grid/data-grid/src/DataGrid.tsx b/packages/grid/data-grid/src/DataGrid.tsx index a78336a80dd9b..436fc124e1ab4 100644 --- a/packages/grid/data-grid/src/DataGrid.tsx +++ b/packages/grid/data-grid/src/DataGrid.tsx @@ -199,7 +199,7 @@ DataGridRaw.propTypes = { getRowId: PropTypes.func, /** * Determines the path of a row in the tree data - * @param {GridRowParams} params With all properties from [[GridRowParams]]. + * @param {GridRowModel} row The row from which we want the path. * @returns {string[]} the path to the row */ getTreeDataPath: PropTypes.func, diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index 63afbeaeb980c..15df626df6566 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -245,7 +245,7 @@ DataGridProRaw.propTypes = { getRowId: PropTypes.func, /** * Determines the path of a row in the tree data - * @param {GridRowParams} params With all properties from [[GridRowParams]]. + * @param {GridRowModel} row The row from which we want the path. * @returns {string[]} the path to the row */ getTreeDataPath: PropTypes.func, From 1205369abfcdddb4efc6fc11734a4db88ff711bd Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 28 Sep 2021 18:16:13 +0200 Subject: [PATCH 118/390] Rework top level rows pagination --- .../_modules_/grid/components/GridFooter.tsx | 13 ++++--- .../grid/components/GridViewport.tsx | 21 ++++++++-- .../grid/components/base/GridOverlays.tsx | 7 ++-- .../features/filter/gridFilterSelector.ts | 23 ++++++++++- .../hooks/features/filter/gridFilterState.ts | 28 +++++++++++++ .../hooks/features/filter/useGridFilter.ts | 39 +++++++++++-------- .../keyboard/useGridKeyboardNavigation.ts | 8 ++-- .../hooks/features/pagination/useGridPage.ts | 8 ++-- .../hooks/features/rows/gridRowsSelector.ts | 7 +++- .../grid/hooks/features/rows/gridRowsState.ts | 16 ++++++++ .../grid/hooks/features/rows/useGridRows.ts | 32 ++++++++++----- 11 files changed, 154 insertions(+), 48 deletions(-) diff --git a/packages/grid/_modules_/grid/components/GridFooter.tsx b/packages/grid/_modules_/grid/components/GridFooter.tsx index f9b579818a051..b6f32e2932739 100644 --- a/packages/grid/_modules_/grid/components/GridFooter.tsx +++ b/packages/grid/_modules_/grid/components/GridFooter.tsx @@ -1,8 +1,11 @@ import * as React from 'react'; import { useGridSelector } from '../hooks/features/core/useGridSelector'; -import { gridRowCountSelector } from '../hooks/features/rows/gridRowsSelector'; +import { + gridRowCountSelector, + gridTopLevelRowCountSelector, +} from '../hooks/features/rows/gridRowsSelector'; import { selectedGridRowsCountSelector } from '../hooks/features/selection/gridSelectionSelector'; -import { gridVisibleRowCountSelector } from '../hooks/features/filter/gridFilterSelector'; +import { gridVisibleTopLevelRowCountSelector } from '../hooks/features/filter/gridFilterSelector'; import { useGridApiContext } from '../hooks/root/useGridApiContext'; import { GridRowCount } from './GridRowCount'; import { GridSelectedRowCount } from './GridSelectedRowCount'; @@ -13,9 +16,9 @@ export const GridFooter = React.forwardRef 0 ? ( @@ -26,7 +29,7 @@ export const GridFooter = React.forwardRef + ) : null; const paginationElement = rootProps.pagination && diff --git a/packages/grid/_modules_/grid/components/GridViewport.tsx b/packages/grid/_modules_/grid/components/GridViewport.tsx index 90a95dad0fd0d..84eb4d92b530b 100644 --- a/packages/grid/_modules_/grid/components/GridViewport.tsx +++ b/packages/grid/_modules_/grid/components/GridViewport.tsx @@ -2,7 +2,10 @@ import * as React from 'react'; import { visibleGridColumnsSelector } from '../hooks/features/columns/gridColumnsSelector'; import { useGridSelector } from '../hooks/features/core/useGridSelector'; import { gridDensityRowHeightSelector } from '../hooks/features/density/densitySelector'; -import { gridSortedVisibleRowsFlatSelector } from '../hooks/features/filter/gridFilterSelector'; +import { + gridSortedVisibleRowsAsArraySelector, + VisibleRow, +} from '../hooks/features/filter/gridFilterSelector'; import { gridFocusCellSelector, gridTabIndexCellSelector, @@ -24,6 +27,15 @@ import { } from '../hooks/root/gridContainerSizesSelector'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; +const getRowsSlice = (rows: VisibleRow[], startIndex: number, endIndex: number) => { + const topLevelRows = rows.slice(startIndex, endIndex); + + const flattenRows = (nodes: VisibleRow[]) => + nodes.flatMap((node) => [node, ...(node.children ? flattenRows(node.children) : [])]); + + return flattenRows(topLevelRows); +}; + type ViewportType = React.ForwardRefExoticComponent>; export const GridViewport: ViewportType = React.forwardRef( @@ -38,7 +50,7 @@ export const GridViewport: ViewportType = React.forwardRef( const cellFocus = useGridSelector(apiRef, gridFocusCellSelector); const cellTabIndex = useGridSelector(apiRef, gridTabIndexCellSelector); const selection = useGridSelector(apiRef, gridSelectionStateSelector); - const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowsFlatSelector); + const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowsAsArraySelector); const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector); const editRowsState = useGridSelector(apiRef, gridEditRowsStateSelector); @@ -64,8 +76,9 @@ export const GridViewport: ViewportType = React.forwardRef( return null; } - const renderedRows = visibleSortedRows.slice( - renderState.renderContext.firstRowIdx, + const renderedRows = getRowsSlice( + visibleSortedRows, + renderState.renderContext.firstRowIdx!, renderState.renderContext.lastRowIdx!, ); diff --git a/packages/grid/_modules_/grid/components/base/GridOverlays.tsx b/packages/grid/_modules_/grid/components/base/GridOverlays.tsx index 850eae0e5143a..ed1607458dd0a 100644 --- a/packages/grid/_modules_/grid/components/base/GridOverlays.tsx +++ b/packages/grid/_modules_/grid/components/base/GridOverlays.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { useGridSelector } from '../../hooks/features/core/useGridSelector'; -import { gridVisibleRowCountSelector } from '../../hooks/features/filter/gridFilterSelector'; +import { gridVisibleTopLevelRowCountSelector } from '../../hooks/features/filter/gridFilterSelector'; import { gridRowCountSelector } from '../../hooks/features/rows/gridRowsSelector'; import { useGridApiContext } from '../../hooks/root/useGridApiContext'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; @@ -10,10 +10,11 @@ export function GridOverlays() { const rootProps = useGridRootProps(); const totalRowCount = useGridSelector(apiRef, gridRowCountSelector); - const visibleRowCount = useGridSelector(apiRef, gridVisibleRowCountSelector); + const visibleTopLevelRowCount = useGridSelector(apiRef, gridVisibleTopLevelRowCountSelector); const showNoRowsOverlay = !rootProps.loading && totalRowCount === 0; - const showNoResultsOverlay = !rootProps.loading && totalRowCount > 0 && visibleRowCount === 0; + const showNoResultsOverlay = + !rootProps.loading && totalRowCount > 0 && visibleTopLevelRowCount === 0; if (showNoRowsOverlay) { return ; diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index 5803308ba6130..16bd6f5d38ae1 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -28,6 +28,11 @@ export const gridVisibleRowCountSelector = createSelector( (filterState) => filterState.visibleRowCount, ); +export const gridVisibleTopLevelRowCountSelector = createSelector( + gridFilterStateSelector, + (filterState) => filterState.visibleTopLevelRowCount, +); + export const gridSortedVisibleRowsSelector = createSelector( gridVisibleRowsLookupSelector, gridSortedRowsSelector, @@ -51,7 +56,23 @@ export const gridSortedVisibleRowsSelector = createSelector( }, ); -export const gridSortedVisibleRowsFlatSelector = createSelector( +export type VisibleRow = { id: GridRowId; node: GridRowModel; children?: VisibleRow[] }; + +export const gridSortedVisibleRowsAsArraySelector = createSelector( + gridSortedVisibleRowsSelector, + (rows) => { + const flattenRowIds = (nodes: Map): VisibleRow[] => + Array.from(nodes.entries()).map(([id, row]) => ({ + id, + node: row.node, + children: row.children ? flattenRowIds(row.children) : undefined, + })); + + return flattenRowIds(rows); + }, +); + +export const gridSortedVisibleRowsAsArrayFlatSelector = createSelector( gridSortedVisibleRowsSelector, (rows) => { const flattenRowIds = ( diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts index 52d81af6189dd..d7b7b0e0668dd 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts @@ -7,9 +7,37 @@ export const getDefaultGridFilterModel: () => GridFilterModel = () => ({ linkOperator: GridLinkOperator.And, }); +export const getEmptyVisibleRows = (): Omit => ({ + visibleRowsLookup: {}, + visibleRows: [], + visibleRowCount: 0, + visibleTopLevelRowCount: 0, +}); + export interface GridFilterState { filterModel: GridFilterModel; + + /** + * Visibility status of each row after applying the filtering + * The expanded children rows are also set to `true` + */ visibleRowsLookup: Record; + + /** + * Rows visible after applying the filtering + * It also contains the expanded children rows + */ visibleRows: GridRowId[]; + + /** + * Amount of rows after applying the filtering + * It also count the expanded children rows + */ visibleRowCount: number; + + /** + * Amount of rows after applying the filtering + * It does not count the expanded children rows + */ + visibleTopLevelRowCount: number; } diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 8ed1eb6cc3152..022447cb2d70b 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -17,7 +17,7 @@ import { gridSortedRowIdsFlatSelector, gridSortedRowsSelector, } from '../sorting/gridSortingSelector'; -import { getDefaultGridFilterModel } from './gridFilterState'; +import { getDefaultGridFilterModel, getEmptyVisibleRows } from './gridFilterState'; import { GridFilterModel } from '../../../models/gridFilterModel'; import { gridVisibleRowsLookupSelector, @@ -27,7 +27,7 @@ import { import { GridSortedRowsTreeNode } from '../sorting'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; -import { gridRowCountSelector } from '../rows'; +import { gridExpandedRowCountSelector, gridTopLevelRowCountSelector } from '../rows'; const checkFilterModelValidity = (model: GridFilterModel) => { if (model.items.length > 1) { @@ -64,9 +64,7 @@ export const useGridFilter = ( ...state, filter: { filterModel: props.filterModel ?? getDefaultGridFilterModel(), - visibleRowsLookup: {}, - visibleRows: [], - visibleRowCount: 0, + ...getEmptyVisibleRows(), }, }; }); @@ -123,12 +121,15 @@ export const useGridFilter = ( setGridState((state) => { const visibleRowsLookup = { ...gridVisibleRowsLookupSelector(state) }; + let visibleRowCount = 0; + let visibleTopLevelRowCount = 0; + const visibleRows: GridRowId[] = []; // We run the selector on the state here to avoid rendering the rows and then filtering again. // This way we have latest rows on the first rendering const rowTree = gridSortedRowsSelector(state); - const filterRowTree = (tree: Map) => { + const filterRowTree = (tree: Map, depth = 0) => { tree.forEach((node, id) => { const params = apiRef.current.getCellParams(id, newFilterItem.columnField!); const hasCurrentFilter = applyFilterOnRow(params); @@ -146,25 +147,30 @@ export const useGridFilter = ( visibleRowsLookup[id] = isVisible; + if (isVisible) { + visibleRows.push(id); + visibleRowCount += 1; + if (depth === 0) { + visibleTopLevelRowCount += 1; + } + } + if (node.children) { - filterRowTree(node.children); + filterRowTree(node.children, depth + 1); } }); }; filterRowTree(rowTree); - const visibleRows = Object.entries(visibleRowsLookup) - .filter(([, isVisible]) => isVisible) - .map(([id]) => id); - return { ...state, filter: { ...state.filter, visibleRowsLookup, visibleRows, - visibleRowCount: visibleRows.length, + visibleRowCount, + visibleTopLevelRowCount, }, }; }); @@ -186,9 +192,7 @@ export const useGridFilter = ( ...state, filter: { ...state.filter, - visibleRowsLookup: {}, - visibleRows: [], - visibleRowCount: 0, + ...getEmptyVisibleRows(), }, })); @@ -211,12 +215,15 @@ export const useGridFilter = ( ...state.filter, visibleRowsLookup, visibleRows, - visibleRowCount: gridRowCountSelector(state), + visibleRowCount: gridExpandedRowCountSelector(state), + visibleTopLevelRowCount: gridTopLevelRowCountSelector(state), }, }; }); } + console.log(apiRef.current.state.filter.visibleRowCount); + forceUpdate(); }, [apiRef, setGridState, forceUpdate, props.filterMode]); diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts index dfe2b18108921..4df5d978650d7 100644 --- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts +++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts @@ -19,11 +19,11 @@ import { gridContainerSizesSelector } from '../../root/gridContainerSizesSelecto import { visibleGridColumnsLengthSelector } from '../columns/gridColumnsSelector'; import { useGridSelector } from '../core/useGridSelector'; import { gridPaginationSelector } from '../pagination/gridPaginationSelector'; -import { gridRowExpandedCountSelector } from '../rows/gridRowsSelector'; +import { gridExpandedRowCountSelector } from '../rows/gridRowsSelector'; import { useGridLogger } from '../../utils/useGridLogger'; import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; import { GridComponentProps } from '../../../GridComponentProps'; -import { gridSortedVisibleRowsFlatSelector } from '../filter/gridFilterSelector'; +import { gridSortedVisibleRowsAsArrayFlatSelector } from '../filter/gridFilterSelector'; const getNextCellIndexes = (key: string, indexes: GridCellIndexCoordinates) => { if (!isArrowKeys(key)) { @@ -76,10 +76,10 @@ export const useGridKeyboardNavigation = ( ): void => { const logger = useGridLogger(apiRef, 'useGridKeyboardNavigation'); const paginationState = useGridSelector(apiRef, gridPaginationSelector); - const expandedRowCount = useGridSelector(apiRef, gridRowExpandedCountSelector); + const expandedRowCount = useGridSelector(apiRef, gridExpandedRowCountSelector); const colCount = useGridSelector(apiRef, visibleGridColumnsLengthSelector); const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); - const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowsFlatSelector); + const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowsAsArrayFlatSelector); const mapKey = (event: React.KeyboardEvent) => { if (isEnterKey(event.key)) { diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts index 3f2e60c62b8f1..d1e56a9e3b43f 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts @@ -8,7 +8,7 @@ import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { GridPageApi } from '../../../models/api/gridPageApi'; import { GridPaginationState } from './gridPaginationState'; -import { gridVisibleRowCountSelector } from '../filter'; +import { gridVisibleTopLevelRowCountSelector } from '../filter'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { gridPageSelector } from './gridPaginationSelector'; @@ -53,7 +53,7 @@ export const useGridPage = ( })); const [, setGridState, forceUpdate] = useGridState(apiRef); - const visibleRowCount = useGridSelector(apiRef, gridVisibleRowCountSelector); + const visibleTopLevelRowCount = useGridSelector(apiRef, gridVisibleTopLevelRowCountSelector); apiRef.current.updateControlState({ stateId: 'page', @@ -81,7 +81,7 @@ export const useGridPage = ( React.useEffect(() => { setGridState((state) => { - const rowCount = props.rowCount !== undefined ? props.rowCount : visibleRowCount; + const rowCount = props.rowCount !== undefined ? props.rowCount : visibleTopLevelRowCount; const pageCount = getPageCount(rowCount, state.pagination.pageSize); const page = props.page == null ? state.pagination.page : props.page; @@ -96,7 +96,7 @@ export const useGridPage = ( }; }); forceUpdate(); - }, [setGridState, forceUpdate, visibleRowCount, props.rowCount, props.page, apiRef]); + }, [setGridState, forceUpdate, visibleTopLevelRowCount, props.rowCount, props.page, apiRef]); const handlePageSizeChange = React.useCallback( (pageSize: number) => { diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts index d5e9f8963002d..8acc5e0adfcd6 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts @@ -11,6 +11,11 @@ export const gridRowCountSelector = createSelector( (rows) => rows.totalRowCount, ); +export const gridTopLevelRowCountSelector = createSelector( + gridRowsStateSelector, + (rows) => rows.totalTopLevelRowCount, +); + export const gridRowsLookupSelector = createSelector( gridRowsStateSelector, (rows) => rows.idRowsLookup, @@ -40,7 +45,7 @@ export const gridRowExpandedTreeSelector = createSelector(gridRowTreeSelector, ( return removeCollapsedNodes(rowsTree); }); -export const gridRowExpandedCountSelector = createSelector( +export const gridExpandedRowCountSelector = createSelector( gridRowExpandedTreeSelector, (expandedRows) => { const countNodes = (tree: GridRowConfigTree) => { diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts index d083d21024284..60066d96e9be9 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts @@ -2,7 +2,23 @@ import { GridRowId, GridRowModel, GridRowConfigTree } from '../../../models/grid export interface GridRowsState { idRowsLookup: Record; + + /** + * Path in the tree to access to a given row + */ paths: Record; + tree: GridRowConfigTree; + + /** + * Amount of rows before applying the filtering + * It also count the expanded and collapsed children rows + */ totalRowCount: number; + + /** + * Amount of rows before applying the filtering + * It does not count the expanded children rows + */ + totalTopLevelRowCount: number; } diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 7df96d3ff4f46..182911c7ae853 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -27,8 +27,17 @@ import { gridRowsPathSelector, } from './gridRowsSelector'; -export type GridRowsInternalCacheState = Omit & { +export type GridRowsInternalCacheState = Omit< + GridRowsState, + 'tree' | 'paths' | 'totalRowCount' | 'totalTopLevelRowCount' +> & { rowIds: GridRowId[]; + + /** + * The row count property when the internal cache was created + * We are storing it instead of accessing it directly when storing the cache to avoid synchronization issues + */ + propRowCount?: number; }; export interface GridRowsInternalCache { @@ -55,7 +64,7 @@ export function convertGridRowsPropToState( const state: GridRowsInternalCacheState = { idRowsLookup: {}, rowIds: [], - totalRowCount: propRowCount && propRowCount > rows.length ? propRowCount : rows.length, + propRowCount, }; rows.forEach((rowData) => { @@ -132,13 +141,19 @@ const setRowExpansionInTree = ( return clonedMap; }; -const getRowsStateFromCache = (rowsCache: GridRowsInternalCache, apiRef: GridApiRef) => { - const { rowIds, ...rowState } = rowsCache.state; +const getRowsStateFromCache = ( + rowsCache: GridRowsInternalCache, + apiRef: GridApiRef, +): GridRowsState => { + const { rowIds, propRowCount = 0, ...rowState } = rowsCache.state; const { tree, paths } = apiRef.current.groupRows ? apiRef.current.groupRows(rowState.idRowsLookup, rowIds) : getFlatRowTree(rowIds); - return { ...rowState, tree, paths }; + const totalRowCount = propRowCount > rowIds.length ? propRowCount : rowIds.length; + const totalTopLevelRowCount = propRowCount > tree.size ? propRowCount : tree.size; + + return { ...rowState, tree, paths, totalRowCount, totalTopLevelRowCount }; }; /** @@ -154,7 +169,7 @@ export const useGridRows = ( const rowsCache = React.useRef({ state: { idRowsLookup: {}, - totalRowCount: 0, + propRowCount: undefined, rowIds: [], }, timeout: null, @@ -296,13 +311,10 @@ export const useGridRows = ( rowIds = rowIds.filter((id) => !deletedRowIds.includes(id)); } - const totalRowCount = - props.rowCount && props.rowCount > rowIds.length ? props.rowCount : rowIds.length; - const state: GridRowsInternalCacheState = { idRowsLookup, rowIds, - totalRowCount, + propRowCount: props.rowCount, }; throttledRowsChange(state, true); From 4b6635b74f65142e964dc2d6638319abcfeb01fd Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 29 Sep 2021 16:45:13 +0200 Subject: [PATCH 119/390] Add tests --- .../cell/GridTreeDataGroupingCell.tsx | 2 +- .../hooks/features/filter/useGridFilter.ts | 2 - .../grid/hooks/features/rows/useGridRows.ts | 8 +- .../features/treeData/useGridTreeData.ts | 25 ++++- .../_modules_/grid/models/gridOptions.tsx | 2 +- packages/grid/data-grid/src/DataGrid.tsx | 2 +- packages/grid/x-grid/src/DataGridPro.tsx | 2 +- .../src/tests/treeData.DataGridPro.test.tsx | 99 ++++++++++++++++--- 8 files changed, 116 insertions(+), 26 deletions(-) diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index c6b9d8a662e27..112fa8352ead6 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -36,7 +36,7 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { const path = apiRef.current.getRowPath(id); if (!node || !path) { - throw new Error(`No row with id #${id} found`); + throw new Error(`MUI: No row with id #${id} found`); } return ( diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 022447cb2d70b..6c64cbd4fe3f2 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -222,8 +222,6 @@ export const useGridFilter = ( }); } - console.log(apiRef.current.state.filter.visibleRowCount); - forceUpdate(); }, [apiRef, setGridState, forceUpdate, props.filterMode]); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 182911c7ae853..8852f193d161c 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -113,7 +113,7 @@ const setRowExpansionInTree = ( isExpanded: boolean, ) => { if (path.length === 0) { - throw new Error(`Material-UI: Invalid path for row #${id}.`); + throw new Error(`MUI: Invalid path for row #${id}.`); } const clonedMap = new Map(tree.entries()); @@ -121,14 +121,14 @@ const setRowExpansionInTree = ( const nodeBefore = clonedMap.get(nodeName); if (!nodeBefore) { - throw new Error(`Material-UI: Invalid path for row #${id}.`); + throw new Error(`MUI: Invalid path for row #${id}.`); } if (restPath.length === 0) { clonedMap.set(nodeName, { ...nodeBefore, expanded: isExpanded }); } else { if (!nodeBefore.children) { - throw new Error(`Material-UI: Invalid path for row #${id}.`); + throw new Error(`MUI: Invalid path for row #${id}.`); } clonedMap.set(nodeName, { @@ -366,7 +366,7 @@ export const useGridRows = ( const path = apiRef.current.getRowPath(id); if (!path) { - throw new Error(`Material-UI: No row with id #${id} found in row path list`); + throw new Error(`MUI: No row with id #${id} found in row path list`); } return getNodeFromTree(apiRef.current.getRowModels(), path); diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index d98448b42253b..6aceb91c58658 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -24,7 +24,7 @@ const insertRowInTree = ( depth: number = 0, ) => { if (path.length === 0) { - throw new Error(`Material-UI: Could not insert row #${id} in the tree structure.`); + throw new Error(`MUI: Could not insert row #${id} in the tree structure.`); } if (path.length === 1) { @@ -37,7 +37,7 @@ const insertRowInTree = ( const parent = tree.get(nodeName); if (!parent) { - throw new Error(`Material-UI: Could not insert row #${id} in the tree structure.`); + throw new Error(`MUI: Could not insert row #${id} in the tree structure.`); } if (!parent.children) { @@ -58,7 +58,10 @@ function getGridRowId(rowData: GridRowData, getRowId?: GridRowIdGetter): GridRow */ export const useGridTreeData = ( apiRef: GridApiRef, - props: Pick, + props: Pick< + GridComponentProps, + 'treeData' | 'getTreeDataPath' | 'getRowId' | 'groupingColDef' | 'rows' + >, ) => { const groupRows = React.useCallback( (rowsLookup, rowIds) => { @@ -72,7 +75,8 @@ export const useGridTreeData = ( } if (!props.getTreeDataPath) { - throw new Error('Material-UI: No getTreeDataPath given.'); + // return { tree: new Map(), paths: {} } + throw new Error('MUI: No getTreeDataPath given.'); } const rows = Object.values(rowsLookup) @@ -137,4 +141,17 @@ export const useGridTreeData = ( ); useGridApiEventHandler(apiRef, GridEvents.cellKeyDown, handleCellKeyDown); + + // The treeData options have changed, we want to regenerate the tree + // We do not want to run that update on the first render + // TODO: This update is throttle is throttleRowsMs is defined, it should not + const isFirstRender = React.useRef(true); + React.useEffect(() => { + if (isFirstRender.current) { + isFirstRender.current = false; + return; + } + + apiRef.current.setRows([...props.rows]); + }, [apiRef, props.treeData, props.getTreeDataPath]); // eslint-disable-line react-hooks/exhaustive-deps }; diff --git a/packages/grid/_modules_/grid/models/gridOptions.tsx b/packages/grid/_modules_/grid/models/gridOptions.tsx index cb6c66acb75b9..857460c151c9a 100644 --- a/packages/grid/_modules_/grid/models/gridOptions.tsx +++ b/packages/grid/_modules_/grid/models/gridOptions.tsx @@ -193,7 +193,7 @@ export interface GridSimpleOptions { */ rowsPerPageOptions: number[]; /** - * If `true`, the rows will be gathered in a tree structure, following the `getDataPath` prop + * If `true`, the rows will be gathered in a tree structure, following the `getTreeDataPath` prop * @default false */ treeData: boolean; diff --git a/packages/grid/data-grid/src/DataGrid.tsx b/packages/grid/data-grid/src/DataGrid.tsx index 436fc124e1ab4..c4d98c17a58d9 100644 --- a/packages/grid/data-grid/src/DataGrid.tsx +++ b/packages/grid/data-grid/src/DataGrid.tsx @@ -727,7 +727,7 @@ DataGridRaw.propTypes = { */ style: PropTypes.object, /** - * If `true`, the rows will be gathered in a tree structure, following the `getDataPath` prop + * If `true`, the rows will be gathered in a tree structure, following the `getTreeDataPath` prop * @default false */ treeData: PropTypes.bool, diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index 15df626df6566..5ba0c1a310277 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -769,7 +769,7 @@ DataGridProRaw.propTypes = { */ throttleRowsMs: PropTypes.number, /** - * If `true`, the rows will be gathered in a tree structure, following the `getDataPath` prop + * If `true`, the rows will be gathered in a tree structure, following the `getTreeDataPath` prop * @default false */ treeData: PropTypes.bool, diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index f8a928d3a8871..fcc25f6296124 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -8,12 +8,13 @@ import { import { getCell, getColumnHeadersTextContent, getColumnValues } from 'test/utils/helperFn'; import * as React from 'react'; import { expect } from 'chai'; -import { DataGridPro, DataGridProProps } from '@mui/x-data-grid-pro'; +import { DataGridPro, DataGridProProps, GridApiRef, useGridApiRef } from '@mui/x-data-grid-pro'; const baselineProps: DataGridProProps = { rows: [ { name: 'A' }, { name: 'A.A' }, + { name: 'A.B' }, { name: 'B' }, { name: 'B.A' }, { name: 'B.B' }, @@ -31,15 +32,51 @@ const baselineProps: DataGridProProps = { getRowId: (row) => row.name, }; -describe(' - Pagination', () => { +describe.only(' - Pagination', () => { // TODO v5: replace with createClientRender const render = createClientRenderStrictMode(); - const Test = (props: Partial) => ( -
- -
- ); + let apiRef: GridApiRef; + + const Test = (props: Partial) => { + apiRef = useGridApiRef(); + + return ( +
+ +
+ ); + }; + + it('should support tree data toggling', () => { + const { setProps } = render(); + expect(getColumnHeadersTextContent()).to.deep.equal(['name']); + expect(getColumnValues(0)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'B.A', 'B.B', 'B.B.A', 'C']); + setProps({ treeData: true }); + expect(getColumnHeadersTextContent()).to.deep.equal(['Group', 'name']); + expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); + setProps({ treeData: false }); + expect(getColumnHeadersTextContent()).to.deep.equal(['name']); + expect(getColumnValues(0)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'B.A', 'B.B', 'B.B.A', 'C']); + }); + + describe('prop: getTreeDataPath', () => { + it('should allow to transform path', () => { + render( [...row.name.split('.').reverse()]} />); + expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); + fireEvent.click(getCell(0, 0).querySelector('button')); + expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'B.A', 'B', 'C']); + }); + + // it('should support new getTreeDataPath', () => { + // const { setProps } = render(); + // expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); + // fireEvent.click(getCell(0, 0).querySelector('button')); + // expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'C']); + // setProps({ getTreeDataPath: (row) => [...row.name.split('.').reverse()] }); + // expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'B.A', 'B', 'C']); + // }); + }); describe('grouping column', () => { it('should add a grouping column', () => { @@ -55,7 +92,7 @@ describe(' - Pagination', () => { expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); fireEvent.click(getCell(0, 0).querySelector('button')); - expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'B', 'C']); + expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'C']); fireEvent.click(getCell(0, 0).querySelector('button')); expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); }); @@ -67,20 +104,58 @@ describe(' - Pagination', () => { getCell(0, 0).focus(); expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); fireEvent.keyDown(getCell(0, 0), { key: ' ' }); - expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'B', 'C']); + expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'C']); fireEvent.keyDown(getCell(0, 0), { key: ' ' }); expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); }); }); describe('pagination', () => { - it('should respect the pageSize when toggling children expansion', () => { + it('should respect the pageSize for the top level rows when toggling children expansion', () => { render(); expect(getColumnValues(1)).to.deep.equal(['A', 'B']); fireEvent.click(getCell(0, 0).querySelector('button')); - expect(getColumnValues(1)).to.deep.equal(['A', 'A.A']); + expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B', 'B']); fireEvent.click(screen.getByRole('button', { name: /next page/i })); - expect(getColumnValues(1)).to.deep.equal(['B', 'C']); + expect(getColumnValues(1)).to.deep.equal(['C']); + }); + }); + + describe('filter', () => { + it('should filter every depth of the tree', () => { + const { setProps } = render(); + fireEvent.click(getCell(0, 0).querySelector('button')); + expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'C']); + setProps({ + filterModel: { items: [{ columnField: 'name', value: 'A', operatorValue: 'endsWith' }] }, + }); + expect(getColumnValues(1)).to.deep.equal(['A', 'A.A']); + }); + + it('should not show children when its parent does not match the filters', () => { + const { setProps } = render(); + fireEvent.click(getCell(0, 0).querySelector('button')); + expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'C']); + setProps({ + filterModel: { items: [{ columnField: 'name', value: 'A.B', operatorValue: 'equals' }] }, + }); + expect(getColumnValues(1)).to.deep.equal([]); + }); + }); + + describe('sorting', () => { + it('should respect the prop order for a given depth when no sortModel provided', () => { + render(); + expect(getColumnValues(1)).to.deep.equal(['D', 'A']); + fireEvent.click(getCell(1, 0).querySelector('button')); + expect(getColumnValues(1)).to.deep.equal(['D', 'A', 'A.B', 'A.A']); + }); + + it('should apply the sortModel on every depth of the tree', () => { + render(); + expect(getColumnValues(1)).to.deep.equal(['C', 'B', 'A']); + fireEvent.click(getCell(2, 0).querySelector('button')); + expect(getColumnValues(1)).to.deep.equal(['C', 'B', 'A', 'A.B', 'A.A']); }); }); }); From 095c06dd2452c5ba941a4824896ebbb6d1e6d381 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 29 Sep 2021 17:02:18 +0200 Subject: [PATCH 120/390] Work on tests --- .../hooks/features/columns/useGridColumns.ts | 5 +-- .../features/selection/useGridSelection.ts | 21 ++++++++-- .../features/treeData/useGridTreeData.ts | 42 ++++++++++++------- .../src/tests/selection.DataGrid.test.tsx | 17 +++++++- .../src/tests/treeData.DataGridPro.test.tsx | 19 +++++---- 5 files changed, 74 insertions(+), 30 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts index 4e33ac39677a6..be39de79ef67a 100644 --- a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts +++ b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts @@ -128,8 +128,8 @@ export function useGridColumns( useGridStateInit(apiRef, (state) => { const hydratedColumns = hydrateColumnsType(props.columns, props.columnTypes); - - const columns = upsertColumnsState(hydratedColumns); + const preProcessedColumns = apiRef.current.applyAllColumnPreProcessing(hydratedColumns); + const columns = upsertColumnsState(preProcessedColumns); let newColumns: GridColumns = columns.all.map((field) => columns.lookup[field]); newColumns = hydrateColumnsWidth(newColumns, 0); @@ -350,7 +350,6 @@ export function useGridColumns( logger.info(`Columns pre-processing have changed, regenerating the columns`); const hydratedColumns = hydrateColumnsType(props.columns, props.columnTypes); - const preProcessedColumns = apiRef.current.applyAllColumnPreProcessing(hydratedColumns); const columnState = upsertColumnsState(preProcessedColumns); setColumnsState(columnState); diff --git a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts index 2150f5ba30b65..9a09b4863dbc0 100644 --- a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts +++ b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts @@ -20,6 +20,7 @@ import { gridCheckboxSelectionColDef, GridColDef } from '../../../models'; import { composeClasses } from '../../../utils/material-ui-utils'; import { getDataGridUtilityClass } from '../../../gridClasses'; import { useGridStateInit } from '../../utils/useGridStateInit'; +import {useFirstRender} from "../../utils/useFirstRender"; type OwnerState = { classes: GridComponentProps['classes'] }; @@ -41,7 +42,7 @@ const useUtilityClasses = (ownerState: OwnerState) => { * @requires useGridParamsApi (method) * @requires useGridControlStateManager (method) */ -export const useGridSelection = (apiRef: GridApiRef, props: GridComponentProps): void => { +export const useGridSelection = (apiRef: GridApiRef, props: Pick): void => { const logger = useGridLogger(apiRef, 'useGridSelection'); const propSelectionModel = React.useMemo(() => { @@ -224,7 +225,7 @@ export const useGridSelection = (apiRef: GridApiRef, props: GridComponentProps): } }, [apiRef, isRowSelectable, isStateControlled]); - React.useEffect(() => { + const updateColumnsPreProcessing = React.useCallback(() => { if (!props.checkboxSelection) { apiRef.current.registerColumnPreProcessing('selection', null); } else { @@ -241,7 +242,21 @@ export const useGridSelection = (apiRef: GridApiRef, props: GridComponentProps): apiRef.current.registerColumnPreProcessing('selection', addCheckboxColumn); } - }, [apiRef, props.checkboxSelection, classes]); + }, [apiRef, props.checkboxSelection, classes]) + + useFirstRender(() => { + updateColumnsPreProcessing() + }) + + const isFirstRender = React.useRef(true); + React.useEffect(() => { + if (isFirstRender.current) { + isFirstRender.current = false + return; + } + + updateColumnsPreProcessing() + }, [updateColumnsPreProcessing]); const removeOutdatedSelection = React.useCallback(() => { const currentSelection = gridSelectionStateSelector(apiRef.current.state); diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 6aceb91c58658..1dad556d3500f 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -16,6 +16,7 @@ import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; import { GridEvents } from '../../../constants'; import { GridCellParams, GridColDef, MuiEvent } from '../../../models'; import { isSpaceKey } from '../../../utils/keyboardUtils'; +import {useFirstRender} from "../../utils/useFirstRender"; const insertRowInTree = ( tree: GridRowConfigTree, @@ -110,7 +111,8 @@ export const useGridTreeData = ( useGridApiMethod(apiRef, treeDataApi, 'GridTreeDataApi'); - React.useEffect(() => { + + const updateColumnsPreProcessing = React.useCallback(() => { if (!props.treeData) { apiRef.current.registerColumnPreProcessing('treeData', null); } else { @@ -127,25 +129,24 @@ export const useGridTreeData = ( apiRef.current.registerColumnPreProcessing('treeData', addGroupingColumn); } - }, [apiRef, props.treeData, props.groupingColDef]); + }, [apiRef, props.treeData, props.groupingColDef]) - const handleCellKeyDown = React.useCallback( - (params: GridCellParams, event: MuiEvent) => { - const cellParams = apiRef.current.getCellParams(params.id, params.field); - if (cellParams.field === '__tree_data_group__' && isSpaceKey(event.key)) { - event.stopPropagation(); - apiRef.current.setRowExpansion(params.id, !apiRef.current.getRowNode(params.id)?.expanded); - } - }, - [apiRef], - ); + useFirstRender(() => { + updateColumnsPreProcessing() + }) - useGridApiEventHandler(apiRef, GridEvents.cellKeyDown, handleCellKeyDown); + const isFirstRender = React.useRef(true); + React.useEffect(() => { + if (isFirstRender.current) { + return; + } + + updateColumnsPreProcessing() + }, [updateColumnsPreProcessing]); // The treeData options have changed, we want to regenerate the tree // We do not want to run that update on the first render // TODO: This update is throttle is throttleRowsMs is defined, it should not - const isFirstRender = React.useRef(true); React.useEffect(() => { if (isFirstRender.current) { isFirstRender.current = false; @@ -154,4 +155,17 @@ export const useGridTreeData = ( apiRef.current.setRows([...props.rows]); }, [apiRef, props.treeData, props.getTreeDataPath]); // eslint-disable-line react-hooks/exhaustive-deps + + const handleCellKeyDown = React.useCallback( + (params: GridCellParams, event: MuiEvent) => { + const cellParams = apiRef.current.getCellParams(params.id, params.field); + if (cellParams.field === '__tree_data_group__' && isSpaceKey(event.key)) { + event.stopPropagation(); + apiRef.current.setRowExpansion(params.id, !apiRef.current.getRowNode(params.id)?.expanded); + } + }, + [apiRef], + ); + + useGridApiEventHandler(apiRef, GridEvents.cellKeyDown, handleCellKeyDown); }; diff --git a/packages/grid/data-grid/src/tests/selection.DataGrid.test.tsx b/packages/grid/data-grid/src/tests/selection.DataGrid.test.tsx index d568041e9af7c..9ad837dbbb297 100644 --- a/packages/grid/data-grid/src/tests/selection.DataGrid.test.tsx +++ b/packages/grid/data-grid/src/tests/selection.DataGrid.test.tsx @@ -3,7 +3,13 @@ import * as React from 'react'; import { fireEvent, screen, createClientRenderStrictMode } from 'test/utils'; import { expect } from 'chai'; import { DataGrid, DataGridProps, GridInputSelectionModel } from '@mui/x-data-grid'; -import { getCell, getRow, getSelectedRowIndexes, getColumnHeaderCell } from 'test/utils/helperFn'; +import { + getCell, + getRow, + getSelectedRowIndexes, + getColumnHeaderCell, + getColumnHeadersTextContent +} from 'test/utils/helperFn'; import { getData } from 'storybook/src/data/data-service'; import { spy } from 'sinon'; @@ -61,6 +67,15 @@ describe(' - Selection', () => { }); describe('prop: checkboxSelection = true (multi selection)', () => { + it('should allow to toggle checkboxSelection', () => { + const { setProps } = render(); + expect(getColumnHeadersTextContent()).to.deep.equal(['id', 'Currency Pair']); + expect(getColumnHeaderCell(0).querySelectorAll('input')).to.have.length(0) + setProps({ checkboxSelection: true }) + expect(getColumnHeadersTextContent()).to.deep.equal(['', 'id', 'Currency Pair']); + expect(getColumnHeaderCell(0).querySelectorAll('input')).to.have.length(1) + }) + it('should check and uncheck when double clicking the row', () => { render(); expect(getSelectedRowIndexes()).to.deep.equal([]); diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index fcc25f6296124..5275857f069b0 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -32,7 +32,7 @@ const baselineProps: DataGridProProps = { getRowId: (row) => row.name, }; -describe.only(' - Pagination', () => { +describe.only(' - Tree Data', () => { // TODO v5: replace with createClientRender const render = createClientRenderStrictMode(); @@ -68,14 +68,15 @@ describe.only(' - Pagination', () => { expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'B.A', 'B', 'C']); }); - // it('should support new getTreeDataPath', () => { - // const { setProps } = render(); - // expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); - // fireEvent.click(getCell(0, 0).querySelector('button')); - // expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'C']); - // setProps({ getTreeDataPath: (row) => [...row.name.split('.').reverse()] }); - // expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'B.A', 'B', 'C']); - // }); + it('should support new getTreeDataPath', () => { + const { setProps } = render(); + expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); + fireEvent.click(getCell(0, 0).querySelector('button')); + expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'C']); + setProps({ getTreeDataPath: (row) => [...row.name.split('.').reverse()] }); + fireEvent.click(getCell(0, 0).querySelector('button')); + expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'B.A', 'B', 'C']); + }); }); describe('grouping column', () => { From f243befd1495aea669765376e6ce0ccdbc54d27f Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 29 Sep 2021 17:22:18 +0200 Subject: [PATCH 121/390] Prettier --- .../features/selection/useGridSelection.ts | 26 ++++++++++++++----- .../features/treeData/useGridTreeData.ts | 11 ++++---- .../src/tests/selection.DataGrid.test.tsx | 10 +++---- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts index 9a09b4863dbc0..5079d98cce63c 100644 --- a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts +++ b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts @@ -20,7 +20,7 @@ import { gridCheckboxSelectionColDef, GridColDef } from '../../../models'; import { composeClasses } from '../../../utils/material-ui-utils'; import { getDataGridUtilityClass } from '../../../gridClasses'; import { useGridStateInit } from '../../utils/useGridStateInit'; -import {useFirstRender} from "../../utils/useFirstRender"; +import { useFirstRender } from '../../utils/useFirstRender'; type OwnerState = { classes: GridComponentProps['classes'] }; @@ -42,7 +42,19 @@ const useUtilityClasses = (ownerState: OwnerState) => { * @requires useGridParamsApi (method) * @requires useGridControlStateManager (method) */ -export const useGridSelection = (apiRef: GridApiRef, props: Pick): void => { +export const useGridSelection = ( + apiRef: GridApiRef, + props: Pick< + GridComponentProps, + | 'checkboxSelection' + | 'selectionModel' + | 'onSelectionModelChange' + | 'classes' + | 'disableMultipleSelection' + | 'disableSelectionOnClick' + | 'isRowSelectable' + >, +): void => { const logger = useGridLogger(apiRef, 'useGridSelection'); const propSelectionModel = React.useMemo(() => { @@ -242,20 +254,20 @@ export const useGridSelection = (apiRef: GridApiRef, props: Pick { - updateColumnsPreProcessing() - }) + updateColumnsPreProcessing(); + }); const isFirstRender = React.useRef(true); React.useEffect(() => { if (isFirstRender.current) { - isFirstRender.current = false + isFirstRender.current = false; return; } - updateColumnsPreProcessing() + updateColumnsPreProcessing(); }, [updateColumnsPreProcessing]); const removeOutdatedSelection = React.useCallback(() => { diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 1dad556d3500f..f484ca632615f 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -16,7 +16,7 @@ import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; import { GridEvents } from '../../../constants'; import { GridCellParams, GridColDef, MuiEvent } from '../../../models'; import { isSpaceKey } from '../../../utils/keyboardUtils'; -import {useFirstRender} from "../../utils/useFirstRender"; +import { useFirstRender } from '../../utils/useFirstRender'; const insertRowInTree = ( tree: GridRowConfigTree, @@ -111,7 +111,6 @@ export const useGridTreeData = ( useGridApiMethod(apiRef, treeDataApi, 'GridTreeDataApi'); - const updateColumnsPreProcessing = React.useCallback(() => { if (!props.treeData) { apiRef.current.registerColumnPreProcessing('treeData', null); @@ -129,11 +128,11 @@ export const useGridTreeData = ( apiRef.current.registerColumnPreProcessing('treeData', addGroupingColumn); } - }, [apiRef, props.treeData, props.groupingColDef]) + }, [apiRef, props.treeData, props.groupingColDef]); useFirstRender(() => { - updateColumnsPreProcessing() - }) + updateColumnsPreProcessing(); + }); const isFirstRender = React.useRef(true); React.useEffect(() => { @@ -141,7 +140,7 @@ export const useGridTreeData = ( return; } - updateColumnsPreProcessing() + updateColumnsPreProcessing(); }, [updateColumnsPreProcessing]); // The treeData options have changed, we want to regenerate the tree diff --git a/packages/grid/data-grid/src/tests/selection.DataGrid.test.tsx b/packages/grid/data-grid/src/tests/selection.DataGrid.test.tsx index 9ad837dbbb297..b235391c30c44 100644 --- a/packages/grid/data-grid/src/tests/selection.DataGrid.test.tsx +++ b/packages/grid/data-grid/src/tests/selection.DataGrid.test.tsx @@ -8,7 +8,7 @@ import { getRow, getSelectedRowIndexes, getColumnHeaderCell, - getColumnHeadersTextContent + getColumnHeadersTextContent, } from 'test/utils/helperFn'; import { getData } from 'storybook/src/data/data-service'; import { spy } from 'sinon'; @@ -70,11 +70,11 @@ describe(' - Selection', () => { it('should allow to toggle checkboxSelection', () => { const { setProps } = render(); expect(getColumnHeadersTextContent()).to.deep.equal(['id', 'Currency Pair']); - expect(getColumnHeaderCell(0).querySelectorAll('input')).to.have.length(0) - setProps({ checkboxSelection: true }) + expect(getColumnHeaderCell(0).querySelectorAll('input')).to.have.length(0); + setProps({ checkboxSelection: true }); expect(getColumnHeadersTextContent()).to.deep.equal(['', 'id', 'Currency Pair']); - expect(getColumnHeaderCell(0).querySelectorAll('input')).to.have.length(1) - }) + expect(getColumnHeaderCell(0).querySelectorAll('input')).to.have.length(1); + }); it('should check and uncheck when double clicking the row', () => { render(); From 43ad19312aa90b5c2ed125d028429c252f90a6e3 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 30 Sep 2021 10:21:16 +0200 Subject: [PATCH 122/390] Remove groupingColDef from proptypes --- docs/scripts/generateProptypes.ts | 1 + packages/grid/data-grid/src/DataGrid.tsx | 49 +----------------------- packages/grid/x-grid/src/DataGridPro.tsx | 49 +----------------------- 3 files changed, 3 insertions(+), 96 deletions(-) diff --git a/docs/scripts/generateProptypes.ts b/docs/scripts/generateProptypes.ts index 00327a226273d..4341c006dc65a 100644 --- a/docs/scripts/generateProptypes.ts +++ b/docs/scripts/generateProptypes.ts @@ -22,6 +22,7 @@ async function generateProptypes(program: ttp.ts.Program, sourceFile: string) { 'columns', 'currentColumn', 'colDef', + 'groupingColDef', ]; if (propsToNotResolve.includes(name)) { return false; diff --git a/packages/grid/data-grid/src/DataGrid.tsx b/packages/grid/data-grid/src/DataGrid.tsx index c4d98c17a58d9..22d3bc8911cc7 100644 --- a/packages/grid/data-grid/src/DataGrid.tsx +++ b/packages/grid/data-grid/src/DataGrid.tsx @@ -206,54 +206,7 @@ DataGridRaw.propTypes = { /** * The grouping column used by the tree data */ - groupingColDef: PropTypes.shape({ - align: PropTypes.oneOf(['center', 'left', 'right']), - cellClassName: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), - description: PropTypes.string, - disableColumnMenu: PropTypes.bool, - disableExport: PropTypes.bool, - disableReorder: PropTypes.bool, - editable: PropTypes.bool, - field: PropTypes.string, - filterable: PropTypes.bool, - filterOperators: PropTypes.arrayOf( - PropTypes.shape({ - getApplyFilterFn: PropTypes.func.isRequired, - InputComponent: PropTypes.elementType, - InputComponentProps: PropTypes.object, - label: PropTypes.string, - value: PropTypes.string.isRequired, - }), - ), - flex: PropTypes.number, - headerAlign: PropTypes.oneOf(['center', 'left', 'right']), - headerClassName: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), - headerName: PropTypes.string, - hide: PropTypes.bool, - hideSortIcons: PropTypes.bool, - minWidth: PropTypes.number, - renderCell: PropTypes.func, - renderEditCell: PropTypes.func, - renderHeader: PropTypes.func, - resizable: PropTypes.bool, - sortable: PropTypes.bool, - sortComparator: PropTypes.func, - type: PropTypes.string, - valueFormatter: PropTypes.func, - valueGetter: PropTypes.func, - valueOptions: PropTypes.arrayOf( - PropTypes.oneOfType([ - PropTypes.number, - PropTypes.shape({ - label: PropTypes.string.isRequired, - value: PropTypes.any.isRequired, - }), - PropTypes.string, - ]).isRequired, - ), - valueParser: PropTypes.func, - width: PropTypes.number, - }), + groupingColDef: PropTypes.object, /** * Set the height in pixel of the column headers in the grid. * @default 56 diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index 5ba0c1a310277..57f9a8383bdd6 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -252,54 +252,7 @@ DataGridProRaw.propTypes = { /** * The grouping column used by the tree data */ - groupingColDef: PropTypes.shape({ - align: PropTypes.oneOf(['center', 'left', 'right']), - cellClassName: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), - description: PropTypes.string, - disableColumnMenu: PropTypes.bool, - disableExport: PropTypes.bool, - disableReorder: PropTypes.bool, - editable: PropTypes.bool, - field: PropTypes.string, - filterable: PropTypes.bool, - filterOperators: PropTypes.arrayOf( - PropTypes.shape({ - getApplyFilterFn: PropTypes.func.isRequired, - InputComponent: PropTypes.elementType, - InputComponentProps: PropTypes.object, - label: PropTypes.string, - value: PropTypes.string.isRequired, - }), - ), - flex: PropTypes.number, - headerAlign: PropTypes.oneOf(['center', 'left', 'right']), - headerClassName: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), - headerName: PropTypes.string, - hide: PropTypes.bool, - hideSortIcons: PropTypes.bool, - minWidth: PropTypes.number, - renderCell: PropTypes.func, - renderEditCell: PropTypes.func, - renderHeader: PropTypes.func, - resizable: PropTypes.bool, - sortable: PropTypes.bool, - sortComparator: PropTypes.func, - type: PropTypes.string, - valueFormatter: PropTypes.func, - valueGetter: PropTypes.func, - valueOptions: PropTypes.arrayOf( - PropTypes.oneOfType([ - PropTypes.number, - PropTypes.shape({ - label: PropTypes.string.isRequired, - value: PropTypes.any.isRequired, - }), - PropTypes.string, - ]).isRequired, - ), - valueParser: PropTypes.func, - width: PropTypes.number, - }), + groupingColDef: PropTypes.object, /** * Set the height in pixel of the column headers in the grid. * @default 56 From f1483b07560ecaa851a0b5aa334535f10e4ad06d Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 30 Sep 2021 11:09:10 +0200 Subject: [PATCH 123/390] Add row grouping preprocessing --- docs/pages/api-docs/data-grid/grid-api.md | 171 +++++++++--------- .../grid/constants/eventsConstants.ts | 5 + .../hooks/features/rows/gridRowsSelector.ts | 2 - .../grid/hooks/features/rows/useGridRows.ts | 55 ++++-- .../features/treeData/gridTreeDataApi.ts | 12 -- .../grid/hooks/features/treeData/index.ts | 1 - .../features/treeData/useGridTreeData.ts | 96 ++++------ .../gridRowGroupsPreProcessingApi.ts | 30 +++ .../root/rowGroupsPerProcessing/index.ts | 2 + .../useGridRowGroupsPreProcessing.ts | 50 +++++ .../grid/_modules_/grid/models/api/gridApi.ts | 6 +- .../grid/_modules_/grid/models/gridRows.ts | 2 + .../data-grid/src/useDataGridComponent.tsx | 2 + .../src/tests/treeData.DataGridPro.test.tsx | 8 +- .../x-grid/src/useDataGridProComponent.tsx | 2 + 15 files changed, 257 insertions(+), 187 deletions(-) delete mode 100644 packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataApi.ts create mode 100644 packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts create mode 100644 packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/index.ts create mode 100644 packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index e29aec7559cc6..5faf211289fbd 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -10,89 +10,88 @@ import { GridApi } from '@mui/x-data-grid-pro'; ## Properties -| Name | Type | Description | -| :------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| applyFilter | (item: GridFilterItem, linkOperator?: GridLinkOperator) => boolean | Applies a GridFilterItem on all rows. If no `linkOperator` is given, the "and" operator is used. | -| applyFilterLinkOperator | (operator: GridLinkOperator) => void | Changes the GridLinkOperator used to connect the filters. | -| applyFilters | () => void | Applies all filters on all rows. | -| applySorting | () => void | Applies the current sort model to the rows. | -| commitCellChange | (params: GridCommitCellChangeParams, event?: any) => boolean | Updates the field at the given id with the value stored in the edit row model. | -| commitRowChange | (id: GridRowId, event?: any) => boolean | Updates the row at the given id with the values stored in the edit row model. | -| deleteFilter | (item: GridFilterItem) => void | Deletes a GridFilterItem. | -| exportDataAsCsv | (options?: GridExportCsvOptions) => void | Downloads and exports a CSV of the grid's data. | -| forceUpdate | Dispatch<any> | Forces the grid to rerender. It's often used after a state update. | -| getAllColumns | () => GridStateColDef[] | Returns an array of [GridColDef](/api/data-grid/grid-col-def/) containing all the column definitions. | -| getAllRowIds | () => GridRowId[] | Gets the list of row ids. | -| getCellElement | (id: GridRowId, field: string) => null \| HTMLDivElement | Gets the underlying DOM element for a cell at the given `id` and `field`. | -| getCellMode | (id: GridRowId, field: string) => GridCellMode | Gets the mode of a cell. | -| getCellParams | (id: GridRowId, field: string) => GridCellParams | Gets the [GridCellParams](/api/data-grid/grid-cell-params/) object that is passed as argument in events. | -| getCellValue | (id: GridRowId, field: string) => GridCellValue | Gets the value of a cell at the given `id` and `field`. | -| getColumn | (field: string) => GridStateColDef | Returns the [GridColDef](/api/data-grid/grid-col-def/) for the given `field`. | -| getColumnHeaderElement | (field: string) => null \| HTMLDivElement | Gets the underlying DOM element for the column header with the given `field`. | -| getColumnHeaderParams | (field: string) => GridColumnHeaderParams | Gets the GridColumnHeaderParams object that is passed as argument in events. | -| getColumnIndex | (field: string, useVisibleColumns?: boolean) => number | Returns the index position of a column. By default, only the visible columns are considered.
Pass `false` to `useVisibleColumns` to consider all columns. | -| getColumnPosition | (field: string) => number | Returns the left-position of a column relative to the inner border of the grid. | -| getColumnsMeta | () => GridColumnsMeta | Returns the GridColumnsMeta for each column. | -| getDataAsCsv | (options?: GridExportCsvOptions) => string | Returns the grid data as a CSV string.
This method is used internally by `exportDataAsCsv`. | -| getEditRowsModel | () => GridEditRowsModel | Gets the edit rows model of the grid. | -| getFlatSortedRowIds | () => GridRowId[] | Returns all row ids sorted according to the active sort model. | -| getLocaleText | <T extends keyof GridLocaleText>(key: T) => GridLocaleText[T] | Returns the translation for the `key`. | -| getRow | (id: GridRowId) => null \| GridRowData | Gets the row data with a given id. | -| getRowElement | (id: GridRowId) => null \| HTMLDivElement | Gets the underlying DOM element for a row at the given `id`. | -| getRowIdFromRowIndex | (index: number) => GridRowId | Gets the `GridRowId` of a row at a specific index. | -| getRowIndex | (id: GridRowId) => number | Gets the row index of a row with a given id. | -| getRowMode | (id: GridRowId) => GridRowMode | Gets the mode of a row. | -| getRowModels | () => GridRowConfigTree | Gets the full set of rows ordered in a tree structure. | -| getRowNode | (id: GridRowId) => null \| GridRowConfigTreeNode | Gets the row node from the internal tree structure. | -| getRowParams | (id: GridRowId) => GridRowParams | Gets the [GridRowParams](/api/data-grid/grid-row-params/) object that is passed as argument in events. | -| getRowPath | (id: GridRowId) => null \| string[] | | -| getRowsCount | () => number | Gets the total number of rows in the grid. | -| getScrollPosition | () => GridScrollParams | Returns the current scroll position. | -| getSelectedRows | () => Map<GridRowId, GridRowData> | Returns an array of the selected rows. | -| getSortedRowIds | () => GridSortedRowsIdTreeNode[] | Returns all row ids sorted according to the active sort model. | -| getSortedRows | () => Map<GridRowId, GridSortedRowsTreeNode> | Returns all rows sorted according to the active sort model. | -| getSortModel | () => GridSortModel | Returns the sort model currently applied to the grid. | -| getVisibleColumns | () => GridStateColDef[] | Returns the currently visible columns. | -| getVisibleRowModels | () => Map<GridRowId, GridRowData> | Returns a sorted `Map` containing only the visible rows. | -| groupRows? | (rowsLookup: GridRowsLookup, rowIds: GridRowId[]) => GridRowGroupingResult | Create the tree structure for a given set of rows | -| hideColumnMenu | () => void | Hides the column menu that is open. | -| hideFilterPanel | () => void | Hides the filter panel. | -| hidePreferences | () => void | Hides the preferences panel. | -| isCellEditable | (params: GridCellParams) => boolean | Controls if a cell is editable. | -| isRowSelected | (id: GridRowId) => boolean | Determines if a row is selected or not. | -| publishEvent | (name: string, params?: any, event?: MuiEvent<MouseEvent \| UIEvent \| Event \| SyntheticEvent<Element, Event> \| TouchEvent \| ErrorEvent \| ProgressEvent<EventTarget> \| ClipboardEvent \| AnimationEvent \| InputEvent \| FocusEvent \| CompositionEvent \| DragEvent \| FormDataEvent \| PointerEvent \| KeyboardEvent \| SecurityPolicyViolationEvent \| TransitionEvent \| WheelEvent>) => void | Emits an event. | -| resize | () => void | Triggers a resize of the component and recalculation of width and height. | -| scroll | (params: Partial<GridScrollParams>) => void | Triggers the viewport to scroll to the given positions (in pixels). | -| scrollToIndexes | (params: Partial<GridCellIndexCoordinates>) => boolean | Triggers the viewport to scroll to the cell at indexes given by `params`.
Returns `true` if the grid had to scroll to reach the target. | -| selectRow | (id: GridRowId, isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of a row. | -| selectRows | (ids: GridRowId[], isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of multiple rows. | -| setCellFocus | (id: GridRowId, field: string) => void | Sets the focus to the cell at the given `id` and `field`. | -| setCellMode | (id: GridRowId, field: string, mode: GridCellMode) => void | Sets the mode of a cell. | -| setColumnHeaderFocus | (field: string, event?: SyntheticEvent<Element, Event>) => void | Sets the focus to the column header at the given `field`. | -| setColumnIndex | (field: string, targetIndexPosition: number) => void | Moves a column from its original position to the position given by `targetIndexPosition`. | -| setColumnVisibility | (field: string, isVisible: boolean) => void | Changes the visibility of the column referred by `field`. | -| setColumnWidth | (field: string, width: number) => void | Updates the width of a column. | -| setDensity | (density: GridDensity, headerHeight?: number, rowHeight?: number) => void | Sets the density of the grid. | -| setEditCellValue | (params: GridEditCellValueParams, event?: SyntheticEvent<Element, Event>) => void | Sets the value of the edit cell.
Commonly used inside the edit cell component. | -| setEditRowsModel | (model: GridEditRowsModel) => void | Set the edit rows model of the grid. | -| setFilterModel | (model: GridFilterModel) => void | Sets the filter model to the one given by `model`. | -| setPage | (page: number) => void | Sets the displayed page to the value given by `page`. | -| setPageSize | (pageSize: number) => void | Sets the number of displayed rows to the value given by `pageSize`. | -| setRowExpansion | (id: GridRowId, isExpanded: boolean) => void | | -| setRowMode | (id: GridRowId, mode: GridRowMode) => void | Sets the mode of a row. | -| setRows | (rows: GridRowData[]) => void | Sets a new set of rows. | -| setSelectionModel | (rowIds: GridRowId[]) => void | Updates the selected rows to be those passed to the `rowIds` argument.
Any row already selected will be unselected. | -| setSortModel | (model: GridSortModel) => void | Updates the sort model and triggers the sorting of rows. | -| setState | (state: GridState \| ((previousState: GridState) => GridState)) => void | Sets the whole state of the grid. | -| showColumnMenu | (field: string) => void | Display the column menu under the `field` column. | -| showError | (props: any) => void | Displays the error overlay component. | -| showFilterPanel | (targetColumnField?: string) => void | Shows the filter panel. If `targetColumnField` is given, a filter for this field is also added. | -| showPreferences | (newValue: GridPreferencePanelsValue) => void | Displays the preferences panel. The `newValue` argument controls the content of the panel. | -| sortColumn | (column: GridColDef, direction?: GridSortDirection, allowMultipleSorting?: boolean) => void | Sorts a column. | -| state | GridState | Property that contains the whole state of the grid. | -| subscribeEvent | <Params, Event extends MuiEvent<BaseEvent>>(event: string, handler: GridListener<Params, Event>, options?: GridSubscribeEventOptions) => () => void | Registers a handler for an event. | -| toggleColumnMenu | (field: string) => void | Toggles the column menu under the `field` column. | -| updateColumn | (col: GridColDef) => void | Updates the definition of a column. | -| updateColumns | (cols: GridColDef[]) => void | Updates the definition of multiple columns at the same time. | -| updateRows | (updates: GridRowModelUpdate<>[]) => void | Allows to updates, insert and delete rows in a single call. | -| upsertFilter | (item: GridFilterItem) => void | Updates or inserts a GridFilterItem. | +| Name | Type | Description | +| :----------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| applyFilter | (item: GridFilterItem, linkOperator?: GridLinkOperator) => boolean | Applies a GridFilterItem on all rows. If no `linkOperator` is given, the "and" operator is used. | +| applyFilterLinkOperator | (operator: GridLinkOperator) => void | Changes the GridLinkOperator used to connect the filters. | +| applyFilters | () => void | Applies all filters on all rows. | +| applySorting | () => void | Applies the current sort model to the rows. | +| commitCellChange | (params: GridCommitCellChangeParams, event?: any) => boolean | Updates the field at the given id with the value stored in the edit row model. | +| commitRowChange | (id: GridRowId, event?: any) => boolean | Updates the row at the given id with the values stored in the edit row model. | +| deleteFilter | (item: GridFilterItem) => void | Deletes a GridFilterItem. | +| exportDataAsCsv | (options?: GridExportCsvOptions) => void | Downloads and exports a CSV of the grid's data. | +| forceUpdate | Dispatch<any> | Forces the grid to rerender. It's often used after a state update. | +| getAllColumns | () => GridStateColDef[] | Returns an array of [GridColDef](/api/data-grid/grid-col-def/) containing all the column definitions. | +| getAllRowIds | () => GridRowId[] | Gets the list of row ids. | +| getCellElement | (id: GridRowId, field: string) => null \| HTMLDivElement | Gets the underlying DOM element for a cell at the given `id` and `field`. | +| getCellMode | (id: GridRowId, field: string) => GridCellMode | Gets the mode of a cell. | +| getCellParams | (id: GridRowId, field: string) => GridCellParams | Gets the [GridCellParams](/api/data-grid/grid-cell-params/) object that is passed as argument in events. | +| getCellValue | (id: GridRowId, field: string) => GridCellValue | Gets the value of a cell at the given `id` and `field`. | +| getColumn | (field: string) => GridStateColDef | Returns the [GridColDef](/api/data-grid/grid-col-def/) for the given `field`. | +| getColumnHeaderElement | (field: string) => null \| HTMLDivElement | Gets the underlying DOM element for the column header with the given `field`. | +| getColumnHeaderParams | (field: string) => GridColumnHeaderParams | Gets the GridColumnHeaderParams object that is passed as argument in events. | +| getColumnIndex | (field: string, useVisibleColumns?: boolean) => number | Returns the index position of a column. By default, only the visible columns are considered.
Pass `false` to `useVisibleColumns` to consider all columns. | +| getColumnPosition | (field: string) => number | Returns the left-position of a column relative to the inner border of the grid. | +| getColumnsMeta | () => GridColumnsMeta | Returns the GridColumnsMeta for each column. | +| getDataAsCsv | (options?: GridExportCsvOptions) => string | Returns the grid data as a CSV string.
This method is used internally by `exportDataAsCsv`. | +| getEditRowsModel | () => GridEditRowsModel | Gets the edit rows model of the grid. | +| getFlatSortedRowIds | () => GridRowId[] | Returns all row ids sorted according to the active sort model. | +| getLocaleText | <T extends keyof GridLocaleText>(key: T) => GridLocaleText[T] | Returns the translation for the `key`. | +| getRow | (id: GridRowId) => null \| GridRowData | Gets the row data with a given id. | +| getRowElement | (id: GridRowId) => null \| HTMLDivElement | Gets the underlying DOM element for a row at the given `id`. | +| getRowIdFromRowIndex | (index: number) => GridRowId | Gets the `GridRowId` of a row at a specific index. | +| getRowIndex | (id: GridRowId) => number | Gets the row index of a row with a given id. | +| getRowMode | (id: GridRowId) => GridRowMode | Gets the mode of a row. | +| getRowModels | () => GridRowConfigTree | Gets the full set of rows ordered in a tree structure. | +| getRowNode | (id: GridRowId) => null \| GridRowConfigTreeNode | Gets the row node from the internal tree structure. | +| getRowParams | (id: GridRowId) => GridRowParams | Gets the [GridRowParams](/api/data-grid/grid-row-params/) object that is passed as argument in events. | +| getRowPath | (id: GridRowId) => null \| string[] | | +| getRowsCount | () => number | Gets the total number of rows in the grid. | +| getScrollPosition | () => GridScrollParams | Returns the current scroll position. | +| getSelectedRows | () => Map<GridRowId, GridRowData> | Returns an array of the selected rows. | +| getSortedRowIds | () => GridSortedRowsIdTreeNode[] | Returns all row ids sorted according to the active sort model. | +| getSortedRows | () => Map<GridRowId, GridSortedRowsTreeNode> | Returns all rows sorted according to the active sort model. | +| getSortModel | () => GridSortModel | Returns the sort model currently applied to the grid. | +| getVisibleColumns | () => GridStateColDef[] | Returns the currently visible columns. | +| getVisibleRowModels | () => Map<GridRowId, GridRowData> | Returns a sorted `Map` containing only the visible rows. | +| hideColumnMenu | () => void | Hides the column menu that is open. | +| hideFilterPanel | () => void | Hides the filter panel. | +| hidePreferences | () => void | Hides the preferences panel. | +| isCellEditable | (params: GridCellParams) => boolean | Controls if a cell is editable. | +| isRowSelected | (id: GridRowId) => boolean | Determines if a row is selected or not. | +| publishEvent | (name: string, params?: any, event?: MuiEvent<MouseEvent \| UIEvent \| Event \| SyntheticEvent<Element, Event> \| TouchEvent \| ErrorEvent \| ProgressEvent<EventTarget> \| ClipboardEvent \| AnimationEvent \| InputEvent \| FocusEvent \| CompositionEvent \| DragEvent \| FormDataEvent \| PointerEvent \| KeyboardEvent \| SecurityPolicyViolationEvent \| TransitionEvent \| WheelEvent>) => void | Emits an event. | +| resize | () => void | Triggers a resize of the component and recalculation of width and height. | +| scroll | (params: Partial<GridScrollParams>) => void | Triggers the viewport to scroll to the given positions (in pixels). | +| scrollToIndexes | (params: Partial<GridCellIndexCoordinates>) => boolean | Triggers the viewport to scroll to the cell at indexes given by `params`.
Returns `true` if the grid had to scroll to reach the target. | +| selectRow | (id: GridRowId, isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of a row. | +| selectRows | (ids: GridRowId[], isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of multiple rows. | +| setCellFocus | (id: GridRowId, field: string) => void | Sets the focus to the cell at the given `id` and `field`. | +| setCellMode | (id: GridRowId, field: string, mode: GridCellMode) => void | Sets the mode of a cell. | +| setColumnHeaderFocus | (field: string, event?: SyntheticEvent<Element, Event>) => void | Sets the focus to the column header at the given `field`. | +| setColumnIndex | (field: string, targetIndexPosition: number) => void | Moves a column from its original position to the position given by `targetIndexPosition`. | +| setColumnVisibility | (field: string, isVisible: boolean) => void | Changes the visibility of the column referred by `field`. | +| setColumnWidth | (field: string, width: number) => void | Updates the width of a column. | +| setDensity | (density: GridDensity, headerHeight?: number, rowHeight?: number) => void | Sets the density of the grid. | +| setEditCellValue | (params: GridEditCellValueParams, event?: SyntheticEvent<Element, Event>) => void | Sets the value of the edit cell.
Commonly used inside the edit cell component. | +| setEditRowsModel | (model: GridEditRowsModel) => void | Set the edit rows model of the grid. | +| setFilterModel | (model: GridFilterModel) => void | Sets the filter model to the one given by `model`. | +| setPage | (page: number) => void | Sets the displayed page to the value given by `page`. | +| setPageSize | (pageSize: number) => void | Sets the number of displayed rows to the value given by `pageSize`. | +| setRowExpansion | (id: GridRowId, isExpanded: boolean) => void | | +| setRowMode | (id: GridRowId, mode: GridRowMode) => void | Sets the mode of a row. | +| setRows | (rows: GridRowData[]) => void | Sets a new set of rows. | +| setSelectionModel | (rowIds: GridRowId[]) => void | Updates the selected rows to be those passed to the `rowIds` argument.
Any row already selected will be unselected. | +| setSortModel | (model: GridSortModel) => void | Updates the sort model and triggers the sorting of rows. | +| setState | (state: GridState \| ((previousState: GridState) => GridState)) => void | Sets the whole state of the grid. | +| showColumnMenu | (field: string) => void | Display the column menu under the `field` column. | +| showError | (props: any) => void | Displays the error overlay component. | +| showFilterPanel | (targetColumnField?: string) => void | Shows the filter panel. If `targetColumnField` is given, a filter for this field is also added. | +| showPreferences | (newValue: GridPreferencePanelsValue) => void | Displays the preferences panel. The `newValue` argument controls the content of the panel. | +| sortColumn | (column: GridColDef, direction?: GridSortDirection, allowMultipleSorting?: boolean) => void | Sorts a column. | +| state | GridState | Property that contains the whole state of the grid. | +| subscribeEvent | <Params, Event extends MuiEvent<BaseEvent>>(event: string, handler: GridListener<Params, Event>, options?: GridSubscribeEventOptions) => () => void | Registers a handler for an event. | +| toggleColumnMenu | (field: string) => void | Toggles the column menu under the `field` column. | +| updateColumn | (col: GridColDef) => void | Updates the definition of a column. | +| updateColumns | (cols: GridColDef[]) => void | Updates the definition of multiple columns at the same time. | +| updateRows | (updates: GridRowModelUpdate<>[]) => void | Allows to updates, insert and delete rows in a single call. | +| upsertFilter | (item: GridFilterItem) => void | Updates or inserts a GridFilterItem. | diff --git a/packages/grid/_modules_/grid/constants/eventsConstants.ts b/packages/grid/_modules_/grid/constants/eventsConstants.ts index 7750928115691..10da85659d331 100644 --- a/packages/grid/_modules_/grid/constants/eventsConstants.ts +++ b/packages/grid/_modules_/grid/constants/eventsConstants.ts @@ -295,6 +295,11 @@ export enum GridEvents { * @ignore - do not document */ columnsPreProcessingChange = 'columnsPreProcessingChange', + /** + * Fired when the row grouping function is changed + * @ignore - do not document + */ + rowGroupsPreProcessingChange = 'rowGroupsPreProcessingChange', /** * Fired when the sort model changes. * Called with a [[GridSortModelParams]] object. diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts index 8acc5e0adfcd6..43f9c891417a2 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts @@ -2,8 +2,6 @@ import { createSelector } from 'reselect'; import { GridRowId, GridRowConfigTree, GridRowModel } from '../../../models/gridRows'; import { GridState } from '../core/gridState'; -export type GridRowsLookup = Record; - export const gridRowsStateSelector = (state: GridState) => state.rows; export const gridRowCountSelector = createSelector( diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 8852f193d161c..f728df05f8cd2 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -11,7 +11,6 @@ import { GridRowIdGetter, GridRowData, GridRowConfigTreeNode, - GridRowGroupingResult, GridRowConfigTree, } from '../../../models/gridRows'; import { useGridApiMethod } from '../../root/useGridApiMethod'; @@ -26,6 +25,7 @@ import { gridRowIdsFlatSelector, gridRowsPathSelector, } from './gridRowsSelector'; +import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; export type GridRowsInternalCacheState = Omit< GridRowsState, @@ -34,10 +34,18 @@ export type GridRowsInternalCacheState = Omit< rowIds: GridRowId[]; /** - * The row count property when the internal cache was created + * The rowCount property when the internal cache was created * We are storing it instead of accessing it directly when storing the cache to avoid synchronization issues */ propRowCount?: number; + + /** + * The getRowId property when the internal cache was created + * We are storing it instead of accessing it directly when storing the cache to avoid synchronization issues + */ + propGetRowId?: GridRowIdGetter; + + inputRows: GridRowsProp; }; export interface GridRowsInternalCache { @@ -57,18 +65,20 @@ function getGridRowId( } export function convertGridRowsPropToState( - rows: GridRowsProp, + inputRows: GridRowsProp, propRowCount?: number, - rowIdGetter?: GridRowIdGetter, + propGetRowId?: GridRowIdGetter, ): GridRowsInternalCacheState { const state: GridRowsInternalCacheState = { idRowsLookup: {}, rowIds: [], propRowCount, + propGetRowId, + inputRows, }; - rows.forEach((rowData) => { - const id = getGridRowId(rowData, rowIdGetter); + inputRows.forEach((rowData) => { + const id = getGridRowId(rowData, propGetRowId); state.idRowsLookup[id] = rowData; state.rowIds.push(id); }); @@ -76,13 +86,6 @@ export function convertGridRowsPropToState( return state; } -const getFlatRowTree = (rowIds: GridRowId[]): GridRowGroupingResult => ({ - tree: new Map( - rowIds.map((id) => [id.toString(), { id, depth: 0 }]), - ), - paths: Object.fromEntries(rowIds.map((id) => [id, [id.toString()]])), -}); - const getNodeFromTree = (tree: GridRowConfigTree, path: string[]): GridRowConfigTreeNode | null => { if (path.length === 0) { return null; @@ -145,10 +148,13 @@ const getRowsStateFromCache = ( rowsCache: GridRowsInternalCache, apiRef: GridApiRef, ): GridRowsState => { - const { rowIds, propRowCount = 0, ...rowState } = rowsCache.state; - const { tree, paths } = apiRef.current.groupRows - ? apiRef.current.groupRows(rowState.idRowsLookup, rowIds) - : getFlatRowTree(rowIds); + const { rowIds, propRowCount = 0, propGetRowId, ...rowState } = rowsCache.state; + + const { tree, paths } = apiRef.current.groupRows({ + lookup: rowState.idRowsLookup, + ids: rowIds, + gridRowId: (rowData: GridRowData) => getGridRowId(rowData, propGetRowId), + }); const totalRowCount = propRowCount > rowIds.length ? propRowCount : rowIds.length; const totalTopLevelRowCount = propRowCount > tree.size ? propRowCount : tree.size; @@ -170,7 +176,9 @@ export const useGridRows = ( state: { idRowsLookup: {}, propRowCount: undefined, + propGetRowId: undefined, rowIds: [], + inputRows: [], }, timeout: null, lastUpdateMs: Date.now(), @@ -315,6 +323,9 @@ export const useGridRows = ( idRowsLookup, rowIds, propRowCount: props.rowCount, + propGetRowId: props.getRowId, + // TODO: Test + inputRows: rowIds.map((rowId) => idRowsLookup[rowId]), }; throttledRowsChange(state, true); @@ -399,6 +410,16 @@ export const useGridRows = ( ); }, [props.rows, props.rowCount, props.getRowId, logger, throttledRowsChange]); + const handleGroupRows = React.useCallback(() => { + logger.info(`Row grouping pre-processing have changed, regenerating the row tree`); + throttledRowsChange( + convertGridRowsPropToState(rowsCache.current.state.inputRows, props.rowCount, props.getRowId), + false, + ); + }, [logger, throttledRowsChange, props.rowCount, props.getRowId]); + + useGridApiEventHandler(apiRef, GridEvents.rowGroupsPreProcessingChange, handleGroupRows); + const rowApi: GridRowApi = { getRowIndex, getRowIdFromRowIndex, diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataApi.ts b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataApi.ts deleted file mode 100644 index d6eb379a34f9d..0000000000000 --- a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataApi.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { GridRowGroupingResult, GridRowId } from '../../../models/gridRows'; -import { GridRowsLookup } from '../rows'; - -export interface GridTreeDataApi { - /** - * Create the tree structure for a given set of rows - * @param {GridRowsLookup} rowsLookup the rows to process - * @param {GridRowId[]} rowIds the id of each row - * @returns {GridRowGroupingResult} tree the tree structure containing all the rows - */ - groupRows: (rowsLookup: GridRowsLookup, rowIds: GridRowId[]) => GridRowGroupingResult; -} diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/index.ts b/packages/grid/_modules_/grid/hooks/features/treeData/index.ts index a1436305a97dc..fd93cf42729e0 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/index.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/index.ts @@ -1,2 +1 @@ export * from './useGridTreeData'; -export * from './gridTreeDataApi'; diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index f484ca632615f..d536219b5474d 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -1,15 +1,7 @@ import * as React from 'react'; -import { GridTreeDataApi } from './gridTreeDataApi'; -import { - GridRowData, - GridRowId, - GridRowIdGetter, - GridRowConfigTree, - GridRowConfigTreeNode, -} from '../../../models/gridRows'; +import { GridRowId, GridRowConfigTree, GridRowConfigTreeNode } from '../../../models/gridRows'; import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridComponentProps } from '../../../GridComponentProps'; -import { useGridApiMethod } from '../../root/useGridApiMethod'; import { GridColumnsPreProcessing } from '../../root/columnsPreProcessing'; import { GridTreeDataGroupColDef } from './gridTreeDataGroupColDef'; import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; @@ -17,6 +9,7 @@ import { GridEvents } from '../../../constants'; import { GridCellParams, GridColDef, MuiEvent } from '../../../models'; import { isSpaceKey } from '../../../utils/keyboardUtils'; import { useFirstRender } from '../../utils/useFirstRender'; +import { RowGroupingFunction } from '../../root/rowGroupsPerProcessing'; const insertRowInTree = ( tree: GridRowConfigTree, @@ -49,40 +42,45 @@ const insertRowInTree = ( } }; -// TODO: Remove, it is only here to avoid casting the ID when using the lookup key -function getGridRowId(rowData: GridRowData, getRowId?: GridRowIdGetter): GridRowId { - return getRowId ? getRowId(rowData) : rowData.id; -} - /** * Only available in DataGridPro */ export const useGridTreeData = ( apiRef: GridApiRef, - props: Pick< - GridComponentProps, - 'treeData' | 'getTreeDataPath' | 'getRowId' | 'groupingColDef' | 'rows' - >, + props: Pick, ) => { - const groupRows = React.useCallback( - (rowsLookup, rowIds) => { - if (!props.treeData) { - return { - tree: new Map( - rowIds.map((id) => [id.toString(), { id, depth: 0 }]), - ), - paths: Object.fromEntries(rowIds.map((id) => [id, [id.toString()]])), + const updateColumnsPreProcessing = React.useCallback(() => { + if (!props.treeData) { + apiRef.current.registerColumnPreProcessing('treeData', null); + } else { + const addGroupingColumn: GridColumnsPreProcessing = (columns) => { + const index = columns[0].type === 'checkboxSelection' ? 1 : 0; + const groupingColumn: GridColDef = { + ...GridTreeDataGroupColDef, + headerName: apiRef.current.getLocaleText('treeDataGroupingHeaderName'), + ...props.groupingColDef, }; - } + return [...columns.slice(0, index), groupingColumn, ...columns.slice(index)]; + }; + + apiRef.current.registerColumnPreProcessing('treeData', addGroupingColumn); + } + }, [apiRef, props.treeData, props.groupingColDef]); + + const updateRowGrouping = React.useCallback(() => { + if (!props.treeData) { + return apiRef.current.registerRowGroupsBuilder(null); + } + + const groupRows: RowGroupingFunction = (params) => { if (!props.getTreeDataPath) { - // return { tree: new Map(), paths: {} } throw new Error('MUI: No getTreeDataPath given.'); } - const rows = Object.values(rowsLookup) + const rows = Object.values(params.lookup) .map((row) => { - const id = getGridRowId(row, props.getRowId); + const id = params.gridRowId(row); return { id, @@ -101,37 +99,14 @@ export const useGridTreeData = ( tree, paths, }; - }, - [props.getTreeDataPath, props.getRowId, props.treeData], - ); - - const treeDataApi: GridTreeDataApi = { - groupRows, - }; - - useGridApiMethod(apiRef, treeDataApi, 'GridTreeDataApi'); - - const updateColumnsPreProcessing = React.useCallback(() => { - if (!props.treeData) { - apiRef.current.registerColumnPreProcessing('treeData', null); - } else { - const addGroupingColumn: GridColumnsPreProcessing = (columns) => { - const index = columns[0].type === 'checkboxSelection' ? 1 : 0; - const groupingColumn: GridColDef = { - ...GridTreeDataGroupColDef, - headerName: apiRef.current.getLocaleText('treeDataGroupingHeaderName'), - ...props.groupingColDef, - }; + }; - return [...columns.slice(0, index), groupingColumn, ...columns.slice(index)]; - }; - - apiRef.current.registerColumnPreProcessing('treeData', addGroupingColumn); - } - }, [apiRef, props.treeData, props.groupingColDef]); + return apiRef.current.registerRowGroupsBuilder(groupRows); + }, [apiRef, props.getTreeDataPath, props.treeData]); useFirstRender(() => { updateColumnsPreProcessing(); + updateRowGrouping(); }); const isFirstRender = React.useRef(true); @@ -143,17 +118,14 @@ export const useGridTreeData = ( updateColumnsPreProcessing(); }, [updateColumnsPreProcessing]); - // The treeData options have changed, we want to regenerate the tree - // We do not want to run that update on the first render - // TODO: This update is throttle is throttleRowsMs is defined, it should not React.useEffect(() => { if (isFirstRender.current) { isFirstRender.current = false; return; } - apiRef.current.setRows([...props.rows]); - }, [apiRef, props.treeData, props.getTreeDataPath]); // eslint-disable-line react-hooks/exhaustive-deps + updateRowGrouping(); + }, [updateRowGrouping]); const handleCellKeyDown = React.useCallback( (params: GridCellParams, event: MuiEvent) => { diff --git a/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts new file mode 100644 index 0000000000000..6e4345922d02c --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts @@ -0,0 +1,30 @@ +import { + GridRowData, + GridRowGroupingResult, + GridRowId, + GridRowsLookup, +} from '../../../models/gridRows'; + +export type RwoGroupParams = { + lookup: GridRowsLookup; + ids: GridRowId[]; + gridRowId: (rowData: GridRowData) => GridRowId; +}; + +export type RowGroupingFunction = (params: RwoGroupParams) => GridRowGroupingResult; + +export interface GridRowGroupsPreProcessingApi { + /** + * @param {RowGroupingFunction} columnsPreProcessing Pre-processing to register. + * @ignore - do not document + */ + registerRowGroupsBuilder: (groupingFunction: RowGroupingFunction | null) => void; + + /** + * @param {GridRowsLookup} rowsLookup. Lookup of the rows to group + * @param {GridRowId[]} List of the rows IDs + * @returns {GridRowGroupingResult} The grouped rows + * @ignore - do not document + */ + groupRows: (params: RwoGroupParams) => GridRowGroupingResult; +} diff --git a/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/index.ts b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/index.ts new file mode 100644 index 0000000000000..0d3699582464a --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/index.ts @@ -0,0 +1,2 @@ +export * from './useGridRowGroupsPreProcessing'; +export * from './gridRowGroupsPreProcessingApi'; diff --git a/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts new file mode 100644 index 0000000000000..3d40467c2eb52 --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts @@ -0,0 +1,50 @@ +import * as React from 'react'; +import { GridApiRef } from '../../../models/api/gridApiRef'; +import { + GridRowGroupsPreProcessingApi, + RowGroupingFunction, +} from './gridRowGroupsPreProcessingApi'; +import { GridEvents } from '../../../constants/eventsConstants'; +import { useGridApiMethod } from '../useGridApiMethod'; +import { GridRowConfigTreeNode } from '../../../models/gridRows'; + +const getFlatRowTree: RowGroupingFunction = (params) => ({ + tree: new Map( + params.ids.map((id) => [id.toString(), { id, depth: 0 }]), + ), + paths: Object.fromEntries(params.ids.map((id) => [id, [id.toString()]])), +}); + +/** + * TODO: Improve to be able to have several hooks registering and unregistering grouping method + * We should never have two hooks registering a grouping method, but we should be able to switch between grouping method on prop update + * For instance switch from treeData: true to a grouping by column. + */ +export const useGridRowGroupsPreProcessing = (apiRef: GridApiRef) => { + const rowGroupsPreProcessingRef = React.useRef(); + + const registerRowGroupsBuilder = React.useCallback< + GridRowGroupsPreProcessingApi['registerRowGroupsBuilder'] + >( + (rowGrouping) => { + rowGroupsPreProcessingRef.current = rowGrouping; + apiRef.current.publishEvent(GridEvents.rowGroupsPreProcessingChange); + }, + [apiRef], + ); + + const groupRows = React.useCallback((...params) => { + if (!rowGroupsPreProcessingRef.current) { + return getFlatRowTree(...params); + } + + return rowGroupsPreProcessingRef.current(...params); + }, []); + + const rowGroupsPreProcessingApi: GridRowGroupsPreProcessingApi = { + registerRowGroupsBuilder, + groupRows, + }; + + useGridApiMethod(apiRef, rowGroupsPreProcessingApi, 'GridRowGroupsPreProcessing'); +}; diff --git a/packages/grid/_modules_/grid/models/api/gridApi.ts b/packages/grid/_modules_/grid/models/api/gridApi.ts index d6d303c038bfb..f05634e7b27ad 100644 --- a/packages/grid/_modules_/grid/models/api/gridApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridApi.ts @@ -21,8 +21,8 @@ import { GridStateApi } from './gridStateApi'; import { GridVirtualizationApi } from './gridVirtualizationApi'; import { GridLoggerApi } from './gridLoggerApi'; import { GridScrollApi } from './gridScrollApi'; -import type { GridTreeDataApi } from '../../hooks/features/treeData'; import type { GridColumnsPreProcessingApi } from '../../hooks/root/columnsPreProcessing'; +import type { GridRowGroupsPreProcessingApi } from '../../hooks/root/rowGroupsPerProcessing'; /** * The full grid API. @@ -32,6 +32,7 @@ export interface GridApi GridStateApi, GridLoggerApi, GridColumnsPreProcessingApi, + GridRowGroupsPreProcessingApi, GridDensityApi, GridEventsApi, GridRowApi, @@ -51,5 +52,4 @@ export interface GridApi GridLocaleTextApi, GridControlStateApi, GridClipboardApi, - GridScrollApi, - Partial {} + GridScrollApi {} diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index b54e6a530f0b7..7ac612430ca9c 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -26,6 +26,8 @@ export interface GridRowGroupingResult { paths: Record; } +export type GridRowsLookup = Record; + /** * The type of Id supported by the grid. */ diff --git a/packages/grid/data-grid/src/useDataGridComponent.tsx b/packages/grid/data-grid/src/useDataGridComponent.tsx index 1847c36257050..3c022d966ca4d 100644 --- a/packages/grid/data-grid/src/useDataGridComponent.tsx +++ b/packages/grid/data-grid/src/useDataGridComponent.tsx @@ -32,6 +32,7 @@ import { useGridResizeContainer } from '../../_modules_/grid/hooks/utils/useGrid import { useStateProp } from '../../_modules_/grid/hooks/utils/useStateProp'; import { GridApiRef } from '../../_modules_/grid/models/api/gridApiRef'; import { useGridColumnsPreProcessing } from '../../_modules_/grid/hooks/root/columnsPreProcessing'; +import { useGridRowGroupsPreProcessing } from '../../_modules_/grid/hooks/root/rowGroupsPerProcessing'; export const useDataGridComponent = (apiRef: GridApiRef, props: GridComponentProps) => { useGridLoggerFactory(apiRef, props); @@ -39,6 +40,7 @@ export const useDataGridComponent = (apiRef: GridApiRef, props: GridComponentPro useErrorHandler(apiRef, props); useGridControlStateManager(apiRef, props); useGridColumnsPreProcessing(apiRef); + useGridRowGroupsPreProcessing(apiRef); useLocaleText(apiRef, props); useGridResizeContainer(apiRef, props); useGridSelection(apiRef, props); diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index 5275857f069b0..fea6d783f0e11 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -54,10 +54,10 @@ describe.only(' - Tree Data', () => { expect(getColumnValues(0)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'B.A', 'B.B', 'B.B.A', 'C']); setProps({ treeData: true }); expect(getColumnHeadersTextContent()).to.deep.equal(['Group', 'name']); - expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); - setProps({ treeData: false }); - expect(getColumnHeadersTextContent()).to.deep.equal(['name']); - expect(getColumnValues(0)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'B.A', 'B.B', 'B.B.A', 'C']); + // expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); + // setProps({ treeData: false }); + // expect(getColumnHeadersTextContent()).to.deep.equal(['name']); + // expect(getColumnValues(0)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'B.A', 'B.B', 'B.B.A', 'C']); }); describe('prop: getTreeDataPath', () => { diff --git a/packages/grid/x-grid/src/useDataGridProComponent.tsx b/packages/grid/x-grid/src/useDataGridProComponent.tsx index 511eeb3ab2236..1fd6edadbd85e 100644 --- a/packages/grid/x-grid/src/useDataGridProComponent.tsx +++ b/packages/grid/x-grid/src/useDataGridProComponent.tsx @@ -36,6 +36,7 @@ import { useStateProp } from '../../_modules_/grid/hooks/utils/useStateProp'; import { GridApiRef } from '../../_modules_/grid/models/api/gridApiRef'; import { useGridTreeData } from '../../_modules_/grid/hooks/features/treeData'; import { useGridColumnsPreProcessing } from '../../_modules_/grid/hooks/root/columnsPreProcessing'; +import { useGridRowGroupsPreProcessing } from '../../_modules_/grid/hooks/root/rowGroupsPerProcessing'; export const useDataGridProComponent = (apiRef: GridApiRef, props: GridComponentProps) => { useGridLoggerFactory(apiRef, props); @@ -43,6 +44,7 @@ export const useDataGridProComponent = (apiRef: GridApiRef, props: GridComponent useErrorHandler(apiRef, props); useGridControlStateManager(apiRef, props); useGridColumnsPreProcessing(apiRef); + useGridRowGroupsPreProcessing(apiRef); useLocaleText(apiRef, props); useGridResizeContainer(apiRef, props); useGridTreeData(apiRef, props); From 65e15091aaaef2fb99e44bd037372065e03f5345 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 30 Sep 2021 11:09:41 +0200 Subject: [PATCH 124/390] Fix --- .../_modules_/grid/hooks/features/treeData/useGridTreeData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index d536219b5474d..a32a8aa84a300 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -47,7 +47,7 @@ const insertRowInTree = ( */ export const useGridTreeData = ( apiRef: GridApiRef, - props: Pick, + props: Pick, ) => { const updateColumnsPreProcessing = React.useCallback(() => { if (!props.treeData) { From 918e6a18b94e316709f6acb33e28e824783a29be Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 30 Sep 2021 11:38:31 +0200 Subject: [PATCH 125/390] Add basic doc --- .../data-grid/group-pivot/BasicTreeData.tsx | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx diff --git a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx new file mode 100644 index 0000000000000..625e0361d631b --- /dev/null +++ b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx @@ -0,0 +1,72 @@ +import * as React from 'react'; +import { DataGrid, GridColumns, GridRowsProp } from '@mui/x-data-grid'; +import { + randomCreatedDate, + randomTraderName, + randomUpdatedDate, +} from '@mui/x-data-grid-generator'; + +export default function BasicEditingGrid() { + return ( +
+ +
+); +} + +const columns: GridColumns = [ + { field: 'name', headerName: 'Name', width: 180, editable: true }, + { field: 'age', headerName: 'Age', type: 'number', editable: true }, + { + field: 'dateCreated', + headerName: 'Date Created', + type: 'date', + width: 180, + editable: true, + }, + { + field: 'lastLogin', + headerName: 'Last Login', + type: 'dateTime', + width: 220, + editable: true, + }, +]; + +const rows: GridRowsProp = [ + { + id: 1, + name: randomTraderName(), + age: 25, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 2, + name: randomTraderName(), + age: 36, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 3, + name: randomTraderName(), + age: 19, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 4, + name: randomTraderName(), + age: 28, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, + { + id: 5, + name: randomTraderName(), + age: 23, + dateCreated: randomCreatedDate(), + lastLogin: randomUpdatedDate(), + }, +]; From 7ff527b773fb1ba4c825cee5ffff307af5491a79 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 30 Sep 2021 11:46:17 +0200 Subject: [PATCH 126/390] New option disableChildrenFiltering --- .../data-grid/group-pivot/BasicTreeData.js | 52 +++++++++ .../data-grid/group-pivot/BasicTreeData.tsx | 109 ++++++++---------- .../data-grid/group-pivot/group-pivot.md | 13 ++- .../hooks/features/filter/useGridFilter.ts | 24 +++- .../_modules_/grid/models/gridOptions.tsx | 6 + .../src/tests/treeData.DataGridPro.test.tsx | 78 ++++++++++--- .../src/stories/grid-tree-data.stories.tsx | 3 - 7 files changed, 196 insertions(+), 89 deletions(-) create mode 100644 docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js diff --git a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js new file mode 100644 index 0000000000000..f92436ff5e522 --- /dev/null +++ b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js @@ -0,0 +1,52 @@ +import * as React from 'react'; +import { DataGridPro } from '@mui/x-data-grid-pro'; + +export default function BasicTreeData() { + return ( +
+ row.name.split('.')} + getRowId={(row) => row.name} + /> +
+ ); +} + +const columns = [{ field: 'name', headerName: 'Name' }]; + +const rows = [ + { name: 'A' }, + { name: 'A.A' }, + { name: 'B' }, + { name: 'B.A' }, + { name: 'B.B' }, + { name: 'B.B.A' }, + { name: 'C' }, + { name: 'D' }, + { name: 'D.A' }, + { name: 'D.B' }, + { name: 'D.C' }, + { name: 'D.D' }, + { name: 'D.E' }, + { name: 'D.F' }, + { name: 'D.G' }, + { name: 'D.H' }, + { name: 'D.I' }, + { name: 'D.J' }, + { name: 'D.K' }, + { name: 'E' }, + { name: 'F' }, + { name: 'F.A' }, + { name: 'F.A.A' }, + { name: 'F.A.B' }, + { name: 'F.A.C' }, + { name: 'F.A.D' }, + { name: 'F.A.E' }, + { name: 'G' }, + { name: 'H' }, + { name: 'I' }, + { name: 'J' }, +]; diff --git a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx index 625e0361d631b..d79dfd3748ba9 100644 --- a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx @@ -1,72 +1,53 @@ import * as React from 'react'; -import { DataGrid, GridColumns, GridRowsProp } from '@mui/x-data-grid'; -import { - randomCreatedDate, - randomTraderName, - randomUpdatedDate, -} from '@mui/x-data-grid-generator'; +import { DataGridPro, GridColumns, GridRowsProp } from '@mui/x-data-grid-pro'; -export default function BasicEditingGrid() { - return ( -
- +export default function BasicTreeData() { + return ( +
+ row.name.split('.')} + getRowId={(row) => row.name} + disableSelectionOnClick + />
-); + ); } -const columns: GridColumns = [ - { field: 'name', headerName: 'Name', width: 180, editable: true }, - { field: 'age', headerName: 'Age', type: 'number', editable: true }, - { - field: 'dateCreated', - headerName: 'Date Created', - type: 'date', - width: 180, - editable: true, - }, - { - field: 'lastLogin', - headerName: 'Last Login', - type: 'dateTime', - width: 220, - editable: true, - }, -]; +const columns: GridColumns = [{ field: 'name', headerName: 'Name' }]; const rows: GridRowsProp = [ - { - id: 1, - name: randomTraderName(), - age: 25, - dateCreated: randomCreatedDate(), - lastLogin: randomUpdatedDate(), - }, - { - id: 2, - name: randomTraderName(), - age: 36, - dateCreated: randomCreatedDate(), - lastLogin: randomUpdatedDate(), - }, - { - id: 3, - name: randomTraderName(), - age: 19, - dateCreated: randomCreatedDate(), - lastLogin: randomUpdatedDate(), - }, - { - id: 4, - name: randomTraderName(), - age: 28, - dateCreated: randomCreatedDate(), - lastLogin: randomUpdatedDate(), - }, - { - id: 5, - name: randomTraderName(), - age: 23, - dateCreated: randomCreatedDate(), - lastLogin: randomUpdatedDate(), - }, + { name: 'A' }, + { name: 'A.A' }, + { name: 'B' }, + { name: 'B.A' }, + { name: 'B.B' }, + { name: 'B.B.A' }, + { name: 'C' }, + { name: 'D' }, + { name: 'D.A' }, + { name: 'D.B' }, + { name: 'D.C' }, + { name: 'D.D' }, + { name: 'D.E' }, + { name: 'D.F' }, + { name: 'D.G' }, + { name: 'D.H' }, + { name: 'D.I' }, + { name: 'D.J' }, + { name: 'D.K' }, + { name: 'E' }, + { name: 'F' }, + { name: 'F.A' }, + { name: 'F.A.A' }, + { name: 'F.A.B' }, + { name: 'F.A.C' }, + { name: 'F.A.D' }, + { name: 'F.A.E' }, + { name: 'G' }, + { name: 'H' }, + { name: 'I' }, + { name: 'J' }, ]; diff --git a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md index 657304b309fde..6dfd08566a56b 100644 --- a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md +++ b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md @@ -6,13 +6,16 @@ title: Data Grid - Group & Pivot

Use grouping, pivoting and more to analyse the data in depth.

-## 🚧 Tree data [](https://material-ui.com/store/items/material-ui-pro/) +## Tree data [](https://material-ui.com/store/items/material-ui-pro/) -> ⚠️ This feature isn't implemented yet. It's coming. -> -> 👍 Upvote [issue #210](https://github.com/mui-org/material-ui-x/issues/210) if you want to see it land faster. +Tree data allows to display data with parent / child relationships. +To enable the Tree Data, you must use the `treeData` prop as well as provide a `getTreeDataPath` prop. + +```jsx + row.path} /> +``` -Tree data allows to visualize self-referential hierarchical (tree-like structure) data. +{{"demo": "pages/components/data-grid/group-pivot/BasicTreeData.js", "bg": "inline", "defaultCodeOpen": false}} ## 🚧 Master detail [](https://material-ui.com/store/items/material-ui-pro/) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 6c64cbd4fe3f2..f257a6d428340 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -50,7 +50,11 @@ export const useGridFilter = ( apiRef: GridApiRef, props: Pick< GridComponentProps, - 'filterModel' | 'onFilterModelChange' | 'filterMode' | 'disableMultipleColumnsFiltering' + | 'filterModel' + | 'onFilterModelChange' + | 'filterMode' + | 'disableMultipleColumnsFiltering' + | 'disableChildrenFiltering' >, ): void => { const logger = useGridLogger(apiRef, 'useGridFilter'); @@ -131,8 +135,15 @@ export const useGridFilter = ( const filterRowTree = (tree: Map, depth = 0) => { tree.forEach((node, id) => { - const params = apiRef.current.getCellParams(id, newFilterItem.columnField!); - const hasCurrentFilter = applyFilterOnRow(params); + let hasCurrentFilter: boolean; + + if (depth > 0 && props.disableChildrenFiltering) { + hasCurrentFilter = true; + } else { + const params = apiRef.current.getCellParams(id, newFilterItem.columnField!); + hasCurrentFilter = applyFilterOnRow(params); + } + const lookupElementBeforeCurrentFilter = visibleRowsLookup[id]; let isVisible: boolean; @@ -178,7 +189,7 @@ export const useGridFilter = ( return true; }, - [apiRef, forceUpdate, logger, setGridState], + [apiRef, forceUpdate, logger, setGridState, props.disableChildrenFiltering], ); const applyFilters = React.useCallback(() => { @@ -386,6 +397,11 @@ export const useGridFilter = ( } }, [apiRef, logger, props.filterModel]); + // The filter options have changed + React.useEffect(() => { + apiRef.current.applyFilters(); + }, [apiRef, props.disableChildrenFiltering]); + useFirstRender(() => apiRef.current.applyFilters()); useGridApiEventHandler(apiRef, GridEvents.rowsSet, apiRef.current.applyFilters); diff --git a/packages/grid/_modules_/grid/models/gridOptions.tsx b/packages/grid/_modules_/grid/models/gridOptions.tsx index 857460c151c9a..1373a1f022356 100644 --- a/packages/grid/_modules_/grid/models/gridOptions.tsx +++ b/packages/grid/_modules_/grid/models/gridOptions.tsx @@ -103,6 +103,11 @@ export interface GridSimpleOptions { * @default false */ disableMultipleColumnsFiltering: boolean; + /** + * If `true`, the filtering will only be applied to the top level rows + * @default false + */ + disableChildrenFiltering: boolean; /** * If `true`, multiple selection using the CTRL or CMD key is disabled. * @default false @@ -249,6 +254,7 @@ export const GRID_DEFAULT_SIMPLE_OPTIONS: GridSimpleOptions = { disableColumnSelector: false, disableDensitySelector: false, disableMultipleColumnsFiltering: false, + disableChildrenFiltering: false, disableMultipleSelection: false, disableMultipleColumnsSorting: false, disableSelectionOnClick: false, diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index fea6d783f0e11..824b336a56505 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -5,7 +5,7 @@ import { // @ts-expect-error need to migrate helpers to TypeScript screen, } from 'test/utils'; -import { getCell, getColumnHeadersTextContent, getColumnValues } from 'test/utils/helperFn'; +import { getCell, getColumnHeadersTextContent, getColumnValues, sleep } from 'test/utils/helperFn'; import * as React from 'react'; import { expect } from 'chai'; import { DataGridPro, DataGridProProps, GridApiRef, useGridApiRef } from '@mui/x-data-grid-pro'; @@ -42,22 +42,64 @@ describe.only(' - Tree Data', () => { apiRef = useGridApiRef(); return ( -
+
); }; - it('should support tree data toggling', () => { - const { setProps } = render(); - expect(getColumnHeadersTextContent()).to.deep.equal(['name']); - expect(getColumnValues(0)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'B.A', 'B.B', 'B.B.A', 'C']); - setProps({ treeData: true }); - expect(getColumnHeadersTextContent()).to.deep.equal(['Group', 'name']); - // expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); - // setProps({ treeData: false }); - // expect(getColumnHeadersTextContent()).to.deep.equal(['name']); - // expect(getColumnValues(0)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'B.A', 'B.B', 'B.B.A', 'C']); + describe('prop: treeData', () => { + it('should support tree data toggling', () => { + const { setProps } = render(); + expect(getColumnHeadersTextContent()).to.deep.equal(['name']); + expect(getColumnValues(0)).to.deep.equal([ + 'A', + 'A.A', + 'A.B', + 'B', + 'B.A', + 'B.B', + 'B.B.A', + 'C', + ]); + setProps({ treeData: true }); + expect(getColumnHeadersTextContent()).to.deep.equal(['Group', 'name']); + expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); + setProps({ treeData: false }); + expect(getColumnHeadersTextContent()).to.deep.equal(['name']); + expect(getColumnValues(0)).to.deep.equal([ + 'A', + 'A.A', + 'A.B', + 'B', + 'B.A', + 'B.B', + 'B.B.A', + 'C', + ]); + }); + + it('should support enabling treeData after apiRef.current.updateRows has modified the rows', async () => { + const { setProps } = render(); + expect(getColumnHeadersTextContent()).to.deep.equal(['name']); + expect(getColumnValues(0)).to.deep.equal([ + 'A', + 'A.A', + 'A.B', + 'B', + 'B.A', + 'B.B', + 'B.B.A', + 'C', + ]); + apiRef.current.updateRows([{ name: 'A.A', _action: 'delete' }]); + expect(getColumnValues(0)).to.deep.equal(['A', 'A.B', 'B', 'B.A', 'B.B', 'B.B.A', 'C']); + setProps({ treeData: true }); + expect(getColumnHeadersTextContent()).to.deep.equal(['Group', 'name']); + expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); + fireEvent.click(getCell(0, 0).querySelector('button')); + expect(getColumnValues(1)).to.deep.equal(['A', 'A.B', 'B', 'C']); + }); }); describe('prop: getTreeDataPath', () => { @@ -123,7 +165,7 @@ describe.only(' - Tree Data', () => { }); describe('filter', () => { - it('should filter every depth of the tree', () => { + it('should filter every depth of the tree if props.disableChildrenFiltering = false', () => { const { setProps } = render(); fireEvent.click(getCell(0, 0).querySelector('button')); expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'C']); @@ -133,6 +175,16 @@ describe.only(' - Tree Data', () => { expect(getColumnValues(1)).to.deep.equal(['A', 'A.A']); }); + it('should only filter to top level rows if props.disableChildrenFiltering = true', () => { + const { setProps } = render(); + fireEvent.click(getCell(0, 0).querySelector('button')); + expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'C']); + setProps({ + filterModel: { items: [{ columnField: 'name', value: 'A', operatorValue: 'endsWith' }] }, + }); + expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B']); + }); + it('should not show children when its parent does not match the filters', () => { const { setProps } = render(); fireEvent.click(getCell(0, 0).querySelector('button')); diff --git a/packages/storybook/src/stories/grid-tree-data.stories.tsx b/packages/storybook/src/stories/grid-tree-data.stories.tsx index 13715d5af8eac..9655aa6bb8267 100644 --- a/packages/storybook/src/stories/grid-tree-data.stories.tsx +++ b/packages/storybook/src/stories/grid-tree-data.stories.tsx @@ -46,9 +46,6 @@ const baselineProps: DataGridProProps = { { name: 'J' }, ], columns: [ - { - field: 'id', - }, { field: 'name', width: 200, From 0e90cff6388cd0f7430a0dfb2c7c2c6863db1be6 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 30 Sep 2021 11:58:36 +0200 Subject: [PATCH 127/390] Fix --- .../pages/components/data-grid/group-pivot/BasicTreeData.js | 1 + packages/grid/_modules_/grid/components/GridFooter.tsx | 5 +---- .../_modules_/grid/hooks/features/rows/gridRowsSelector.ts | 2 +- packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js index f92436ff5e522..f8937937ba64d 100644 --- a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js @@ -10,6 +10,7 @@ export default function BasicTreeData() { treeData getTreeDataPath={(row) => row.name.split('.')} getRowId={(row) => row.name} + disableSelectionOnClick />
); diff --git a/packages/grid/_modules_/grid/components/GridFooter.tsx b/packages/grid/_modules_/grid/components/GridFooter.tsx index b6f32e2932739..70d3ed4c2a2cc 100644 --- a/packages/grid/_modules_/grid/components/GridFooter.tsx +++ b/packages/grid/_modules_/grid/components/GridFooter.tsx @@ -1,9 +1,6 @@ import * as React from 'react'; import { useGridSelector } from '../hooks/features/core/useGridSelector'; -import { - gridRowCountSelector, - gridTopLevelRowCountSelector, -} from '../hooks/features/rows/gridRowsSelector'; +import { gridTopLevelRowCountSelector } from '../hooks/features/rows/gridRowsSelector'; import { selectedGridRowsCountSelector } from '../hooks/features/selection/gridSelectionSelector'; import { gridVisibleTopLevelRowCountSelector } from '../hooks/features/filter/gridFilterSelector'; import { useGridApiContext } from '../hooks/root/useGridApiContext'; diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts index 43f9c891417a2..8c7df376e44c5 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts @@ -1,5 +1,5 @@ import { createSelector } from 'reselect'; -import { GridRowId, GridRowConfigTree, GridRowModel } from '../../../models/gridRows'; +import { GridRowId, GridRowConfigTree } from '../../../models/gridRows'; import { GridState } from '../core/gridState'; export const gridRowsStateSelector = (state: GridState) => state.rows; diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index 824b336a56505..bcecfc51a78a1 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -5,7 +5,7 @@ import { // @ts-expect-error need to migrate helpers to TypeScript screen, } from 'test/utils'; -import { getCell, getColumnHeadersTextContent, getColumnValues, sleep } from 'test/utils/helperFn'; +import { getCell, getColumnHeadersTextContent, getColumnValues } from 'test/utils/helperFn'; import * as React from 'react'; import { expect } from 'chai'; import { DataGridPro, DataGridProProps, GridApiRef, useGridApiRef } from '@mui/x-data-grid-pro'; From 39674dfa39b2cb2bbf702af9c534d008c3926a5e Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 30 Sep 2021 12:10:21 +0200 Subject: [PATCH 128/390] Clean --- docs/pages/api-docs/data-grid/grid-api.md | 2 +- .../features/filter/gridFilterSelector.ts | 18 ++++++++---------- .../hooks/features/filter/useGridFilter.ts | 4 ++-- .../features/sorting/gridSortingSelector.ts | 4 ++-- .../hooks/features/sorting/gridSortingState.ts | 4 +++- .../_modules_/grid/models/api/gridSortApi.ts | 6 +++--- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index 5faf211289fbd..26ba8bb4fa173 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -50,7 +50,7 @@ import { GridApi } from '@mui/x-data-grid-pro'; | getScrollPosition | () => GridScrollParams | Returns the current scroll position. | | getSelectedRows | () => Map<GridRowId, GridRowData> | Returns an array of the selected rows. | | getSortedRowIds | () => GridSortedRowsIdTreeNode[] | Returns all row ids sorted according to the active sort model. | -| getSortedRows | () => Map<GridRowId, GridSortedRowsTreeNode> | Returns all rows sorted according to the active sort model. | +| getSortedRows | () => GridSortedRowsTree | Returns all rows sorted according to the active sort model. | | getSortModel | () => GridSortModel | Returns the sort model currently applied to the grid. | | getVisibleColumns | () => GridStateColDef[] | Returns the currently visible columns. | | getVisibleRowModels | () => Map<GridRowId, GridRowData> | Returns a sorted `Map` containing only the visible rows. | diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index 16bd6f5d38ae1..d873a983c2099 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -4,7 +4,7 @@ import { GridRowId, GridRowModel } from '../../../models/gridRows'; import { GridState } from '../core/gridState'; import { gridSortedRowsSelector } from '../sorting/gridSortingSelector'; import { gridColumnLookupSelector } from '../columns/gridColumnsSelector'; -import { GridSortedRowsTreeNode } from '../sorting'; +import { GridSortedRowsTree } from '../sorting'; export const gridFilterStateSelector = (state: GridState) => state.filter; @@ -37,10 +37,10 @@ export const gridSortedVisibleRowsSelector = createSelector( gridVisibleRowsLookupSelector, gridSortedRowsSelector, (visibleRowsLookup, sortedRowsTree) => { - const removeHiddenRows = (nodes: Map) => { - const filteredRows = new Map(); + const removeHiddenRows = (tree: GridSortedRowsTree) => { + const filteredRows: GridSortedRowsTree = new Map(); - nodes.forEach((row, id) => { + tree.forEach((row, id) => { if (visibleRowsLookup[id] !== false) { filteredRows.set(id, { node: row.node, @@ -61,8 +61,8 @@ export type VisibleRow = { id: GridRowId; node: GridRowModel; children?: Visible export const gridSortedVisibleRowsAsArraySelector = createSelector( gridSortedVisibleRowsSelector, (rows) => { - const flattenRowIds = (nodes: Map): VisibleRow[] => - Array.from(nodes.entries()).map(([id, row]) => ({ + const flattenRowIds = (tree: GridSortedRowsTree): VisibleRow[] => + Array.from(tree.entries()).map(([id, row]) => ({ id, node: row.node, children: row.children ? flattenRowIds(row.children) : undefined, @@ -75,10 +75,8 @@ export const gridSortedVisibleRowsAsArraySelector = createSelector( export const gridSortedVisibleRowsAsArrayFlatSelector = createSelector( gridSortedVisibleRowsSelector, (rows) => { - const flattenRowIds = ( - nodes: Map, - ): { id: GridRowId; node: GridRowModel }[] => - Array.from(nodes.entries()).flatMap(([id, row]) => [ + const flattenRowIds = (tree: GridSortedRowsTree): { id: GridRowId; node: GridRowModel }[] => + Array.from(tree.entries()).flatMap(([id, row]) => [ { id, node: row.node }, ...(row.children ? flattenRowIds(row.children) : []), ]); diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index f257a6d428340..90bfc25db05af 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -24,7 +24,7 @@ import { gridSortedVisibleRowsSelector, gridFilterModelSelector, } from './gridFilterSelector'; -import { GridSortedRowsTreeNode } from '../sorting'; +import { GridSortedRowsTree } from '../sorting'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; import { gridExpandedRowCountSelector, gridTopLevelRowCountSelector } from '../rows'; @@ -133,7 +133,7 @@ export const useGridFilter = ( // This way we have latest rows on the first rendering const rowTree = gridSortedRowsSelector(state); - const filterRowTree = (tree: Map, depth = 0) => { + const filterRowTree = (tree: GridSortedRowsTree, depth = 0) => { tree.forEach((node, id) => { let hasCurrentFilter: boolean; diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts index 96edec62c9d72..d839ba0ec735f 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts @@ -3,7 +3,7 @@ import { GridRowId } from '../../../models/gridRows'; import { GridSortDirection, GridSortModel } from '../../../models/gridSortModel'; import { GridState } from '../core/gridState'; import { gridRowsLookupSelector } from '../rows/gridRowsSelector'; -import { GridSortedRowsIdTreeNode, GridSortedRowsTreeNode } from './gridSortingState'; +import { GridSortedRowsIdTreeNode, GridSortedRowsTree } from './gridSortingState'; const gridSortingStateSelector = (state: GridState) => state.sorting; @@ -27,7 +27,7 @@ export const gridSortedRowsSelector = createSelector( gridRowsLookupSelector, (sortedTree, idRowsLookup) => { const buildMap = (nodes: GridSortedRowsIdTreeNode[]) => { - const map = new Map(); + const map: GridSortedRowsTree = new Map(); nodes.forEach((node) => { map.set(node.id, { diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts index 5538416de526f..c117b999cb6c6 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts @@ -1,9 +1,11 @@ import { GridRowId, GridRowModel } from '../../../models/gridRows'; import { GridSortModel } from '../../../models/gridSortModel'; +export type GridSortedRowsTree = Map; + export interface GridSortedRowsTreeNode { node: GridRowModel; - children?: Map; + children?: GridSortedRowsTree; } export interface GridSortedRowsIdTreeNode { diff --git a/packages/grid/_modules_/grid/models/api/gridSortApi.ts b/packages/grid/_modules_/grid/models/api/gridSortApi.ts index 7248bf1963116..154ac1b07c529 100644 --- a/packages/grid/_modules_/grid/models/api/gridSortApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridSortApi.ts @@ -3,7 +3,7 @@ import { GridRowId } from '../gridRows'; import { GridSortDirection, GridSortModel } from '../gridSortModel'; import { GridSortedRowsIdTreeNode, - GridSortedRowsTreeNode, + GridSortedRowsTree, } from '../../hooks/features/sorting/gridSortingState'; /** @@ -37,9 +37,9 @@ export interface GridSortApi { ) => void; /** * Returns all rows sorted according to the active sort model. - * @returns {Map} The sorted [[GridRowModel]] objects. + * @returns {GridSortedRowsTree} The sorted [[GridRowModel]] objects. */ - getSortedRows: () => Map; + getSortedRows: () => GridSortedRowsTree; /** * Returns all row ids sorted according to the active sort model. * @returns {GridSortedRowsIdTreeNode[]} The sorted [[GridRowId]] values. From 65cd4d57f50e34d478910d466d0c0cdfeb4bd747 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 30 Sep 2021 12:30:27 +0200 Subject: [PATCH 129/390] Clean --- .../grid/_modules_/grid/components/GridViewport.tsx | 6 +++--- .../columnSelection/GridHeaderCheckbox.tsx | 4 ++-- .../grid/hooks/features/filter/gridFilterSelector.ts | 12 +++++++----- .../features/pagination/gridPaginationSelector.ts | 4 ++-- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/grid/_modules_/grid/components/GridViewport.tsx b/packages/grid/_modules_/grid/components/GridViewport.tsx index 84eb4d92b530b..c8d8af4d750fa 100644 --- a/packages/grid/_modules_/grid/components/GridViewport.tsx +++ b/packages/grid/_modules_/grid/components/GridViewport.tsx @@ -4,7 +4,7 @@ import { useGridSelector } from '../hooks/features/core/useGridSelector'; import { gridDensityRowHeightSelector } from '../hooks/features/density/densitySelector'; import { gridSortedVisibleRowsAsArraySelector, - VisibleRow, + TreeSortedVisibleRow, } from '../hooks/features/filter/gridFilterSelector'; import { gridFocusCellSelector, @@ -27,10 +27,10 @@ import { } from '../hooks/root/gridContainerSizesSelector'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; -const getRowsSlice = (rows: VisibleRow[], startIndex: number, endIndex: number) => { +const getRowsSlice = (rows: TreeSortedVisibleRow[], startIndex: number, endIndex: number) => { const topLevelRows = rows.slice(startIndex, endIndex); - const flattenRows = (nodes: VisibleRow[]) => + const flattenRows = (nodes: TreeSortedVisibleRow[]) => nodes.flatMap((node) => [node, ...(node.children ? flattenRows(node.children) : [])]); return flattenRows(topLevelRows); diff --git a/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx b/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx index 2b1ead130cfcf..dce9557150864 100644 --- a/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx +++ b/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { GridEvents } from '../../constants/eventsConstants'; import { useGridSelector } from '../../hooks/features/core/useGridSelector'; import { gridPaginatedVisibleSortedGridRowIdsSelector } from '../../hooks/features/pagination/gridPaginationSelector'; -import { visibleSortedGridRowIdsSelector } from '../../hooks/features/filter/gridFilterSelector'; +import { gridSortedVisibleRowIdsSelector } from '../../hooks/features/filter/gridFilterSelector'; import { gridTabIndexColumnHeaderSelector } from '../../hooks/features/focus/gridFocusStateSelector'; import { gridRowCountSelector } from '../../hooks/features/rows/gridRowsSelector'; import { gridSelectionStateSelector } from '../../hooks/features/selection/gridSelectionSelector'; @@ -59,7 +59,7 @@ const GridHeaderCheckbox = React.forwardRef { - const flattenRowIds = (tree: GridSortedRowsTree): VisibleRow[] => + const flattenRowIds = (tree: GridSortedRowsTree): TreeSortedVisibleRow[] => Array.from(tree.entries()).map(([id, row]) => ({ id, node: row.node, @@ -72,10 +72,12 @@ export const gridSortedVisibleRowsAsArraySelector = createSelector( }, ); +export type FlatSortedVisibleRow = { id: GridRowId; node: GridRowModel } + export const gridSortedVisibleRowsAsArrayFlatSelector = createSelector( gridSortedVisibleRowsSelector, (rows) => { - const flattenRowIds = (tree: GridSortedRowsTree): { id: GridRowId; node: GridRowModel }[] => + const flattenRowIds = (tree: GridSortedRowsTree): FlatSortedVisibleRow[] => Array.from(tree.entries()).flatMap(([id, row]) => [ { id, node: row.node }, ...(row.children ? flattenRowIds(row.children) : []), @@ -85,12 +87,12 @@ export const gridSortedVisibleRowsAsArrayFlatSelector = createSelector( }, ); -export const visibleSortedGridRowIdsSelector = createSelector( +export const gridSortedVisibleRowIdsSelector = createSelector( gridSortedVisibleRowsSelector, (visibleSortedRows) => [...visibleSortedRows.keys()], ); -export const activeGridFilterItemsSelector = createSelector( +export const activeGridFilterItemsSelector = createSelector( gridFilterModelSelector, gridColumnLookupSelector, (filterModel, columnLookup) => diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts index ec0531c7bf9fe..28e35f2bbc681 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts @@ -1,6 +1,6 @@ import { createSelector } from 'reselect'; import { GridState } from '../core/gridState'; -import { visibleSortedGridRowIdsSelector } from '../filter/gridFilterSelector'; +import { gridSortedVisibleRowIdsSelector } from '../filter/gridFilterSelector'; import { GridPaginationState } from './gridPaginationState'; export const gridPaginationSelector = (state: GridState): GridPaginationState => state.pagination; @@ -17,7 +17,7 @@ export const gridPageSizeSelector = createSelector( export const gridPaginatedVisibleSortedGridRowIdsSelector = createSelector( gridPaginationSelector, - visibleSortedGridRowIdsSelector, + gridSortedVisibleRowIdsSelector, (pagination, visibleSortedRows) => { const firstSelectedRowIndex = pagination.page * pagination.pageSize; const lastSelectedRowIndex = firstSelectedRowIndex + pagination.pageSize; From d3fbacad973c2801d8d77b1c0a0731cf6726889f Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 30 Sep 2021 12:42:21 +0200 Subject: [PATCH 130/390] Clean --- .../grid/hooks/features/filter/gridFilterSelector.ts | 10 +++++++--- .../grid/hooks/features/useGridSlotComponentProps.tsx | 5 +---- .../grid/models/params/gridSlotComponentProps.ts | 4 ---- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index 21365f86bd71b..e2812aed4ddb9 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -56,7 +56,11 @@ export const gridSortedVisibleRowsSelector = createSelector( }, ); -export type TreeSortedVisibleRow = { id: GridRowId; node: GridRowModel; children?: TreeSortedVisibleRow[] }; +export type TreeSortedVisibleRow = { + id: GridRowId; + node: GridRowModel; + children?: TreeSortedVisibleRow[]; +}; export const gridSortedVisibleRowsAsArraySelector = createSelector( gridSortedVisibleRowsSelector, @@ -72,7 +76,7 @@ export const gridSortedVisibleRowsAsArraySelector = createSelector( }, ); -export type FlatSortedVisibleRow = { id: GridRowId; node: GridRowModel } +export type FlatSortedVisibleRow = { id: GridRowId; node: GridRowModel }; export const gridSortedVisibleRowsAsArrayFlatSelector = createSelector( gridSortedVisibleRowsSelector, @@ -92,7 +96,7 @@ export const gridSortedVisibleRowIdsSelector = createSelector( (visibleSortedRows) => [...visibleSortedRows.keys()], ); -export const activeGridFilterItemsSelector = createSelector( +export const activeGridFilterItemsSelector = createSelector( gridFilterModelSelector, gridColumnLookupSelector, (filterModel, columnLookup) => diff --git a/packages/grid/_modules_/grid/hooks/features/useGridSlotComponentProps.tsx b/packages/grid/_modules_/grid/hooks/features/useGridSlotComponentProps.tsx index f5d0fe8bd4ef9..ea7b7d44fed71 100644 --- a/packages/grid/_modules_/grid/hooks/features/useGridSlotComponentProps.tsx +++ b/packages/grid/_modules_/grid/hooks/features/useGridSlotComponentProps.tsx @@ -4,22 +4,19 @@ import { GridSlotComponentProps } from '../../models/params/gridSlotComponentPro import { visibleGridColumnsSelector } from './columns/gridColumnsSelector'; import { useGridSelector } from './core/useGridSelector'; import { useGridState } from './core/useGridState'; -import { gridRowTreeSelector } from './rows/gridRowsSelector'; export const useGridSlotComponentProps = () => { const apiRef = useGridApiContext(); - const rows = useGridSelector(apiRef, gridRowTreeSelector); const columns = useGridSelector(apiRef, visibleGridColumnsSelector); const [state] = useGridState(apiRef); return React.useMemo( () => ({ state, - rows, columns, apiRef, rootElement: apiRef.current.rootElementRef!, }), - [state, rows, columns, apiRef], + [state, columns, apiRef], ); }; diff --git a/packages/grid/_modules_/grid/models/params/gridSlotComponentProps.ts b/packages/grid/_modules_/grid/models/params/gridSlotComponentProps.ts index 6ea93f941ff56..f9825f705f714 100644 --- a/packages/grid/_modules_/grid/models/params/gridSlotComponentProps.ts +++ b/packages/grid/_modules_/grid/models/params/gridSlotComponentProps.ts @@ -12,10 +12,6 @@ export interface GridSlotComponentProps { * The GridState object containing the current grid state. */ state: GridState; - /** - * The full set of rows. - */ - rows: GridRowConfigTree; /** * The full set of columns. */ From 8ad6f03116e821e8a55442143de1cb08190060ea Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 30 Sep 2021 13:20:20 +0200 Subject: [PATCH 131/390] Clean --- .../hooks/features/filter/useGridFilter.ts | 5 ++++ .../grid/hooks/features/rows/useGridRows.ts | 6 ++--- .../hooks/features/sorting/useGridSorting.ts | 23 +++++++++++++++++-- .../features/treeData/useGridTreeData.ts | 4 ++-- .../useGridRowGroupsPreProcessing.ts | 5 +--- .../_modules_/grid/models/gridOptions.tsx | 6 +++++ .../models/params/gridSlotComponentProps.ts | 1 - .../src/tests/treeData.DataGridPro.test.tsx | 15 +++++++----- 8 files changed, 46 insertions(+), 19 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 90bfc25db05af..788b44ae7fe73 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -398,7 +398,12 @@ export const useGridFilter = ( }, [apiRef, logger, props.filterModel]); // The filter options have changed + const isFirstRender = React.useRef(true); React.useEffect(() => { + if (isFirstRender.current) { + isFirstRender.current = false; + return; + } apiRef.current.applyFilters(); }, [apiRef, props.disableChildrenFiltering]); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index f728df05f8cd2..7769eb855b2d9 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -320,17 +320,15 @@ export const useGridRows = ( } const state: GridRowsInternalCacheState = { + ...rowsCache.current.state, idRowsLookup, rowIds, - propRowCount: props.rowCount, - propGetRowId: props.getRowId, - // TODO: Test inputRows: rowIds.map((rowId) => idRowsLookup[rowId]), }; throttledRowsChange(state, true); }, - [apiRef, props.getRowId, props.rowCount, throttledRowsChange], + [apiRef, props.getRowId, throttledRowsChange], ); const getRowModels = React.useCallback( diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index e8b85b6e89db7..b33ab399e14d4 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -47,6 +47,7 @@ export const useGridSorting = ( | 'sortingOrder' | 'sortingMode' | 'disableMultipleColumnsSorting' + | 'disableChildrenSorting' >, ) => { const logger = useGridLogger(apiRef, 'useGridSorting'); @@ -183,7 +184,14 @@ export const useGridSorting = ( const comparatorList = buildComparatorList(sortModel); const aggregatedComparator = comparatorListAggregate(comparatorList); - const sortRowTree = (tree: GridRowConfigTree): GridSortedRowsIdTreeNode[] => { + const sortRowTree = (tree: GridRowConfigTree, depth = 0): GridSortedRowsIdTreeNode[] => { + if (depth > 0 && props.disableChildrenSorting) { + return Array.from(tree.values()).map((value) => ({ + id: value.id, + children: value.children ? sortRowTree(value.children, depth + 1) : undefined, + })); + } + const rowsWithParams = Array.from(tree.entries()).map(([name, value]) => { const params = comparatorList.map((colComparator) => getSortCellParams(value.id, colComparator.field), @@ -198,7 +206,7 @@ export const useGridSorting = ( return sortedRowsWithParams.map((node) => ({ id: node.value.id, - children: node.value.children ? sortRowTree(node.value.children) : undefined, + children: node.value.children ? sortRowTree(node.value.children, depth + 1) : undefined, })); }; const sortedRows = sortRowTree(unsortedRowTree); @@ -217,6 +225,7 @@ export const useGridSorting = ( buildComparatorList, comparatorListAggregate, props.sortingMode, + props.disableChildrenSorting, ]); const setSortModel = React.useCallback( @@ -286,6 +295,16 @@ export const useGridSorting = ( } }, [apiRef, props.sortModel]); + // The filter options have changed + const isFirstRender = React.useRef(true); + React.useEffect(() => { + if (isFirstRender.current) { + isFirstRender.current = false; + return; + } + apiRef.current.applySorting(); + }, [apiRef, props.disableChildrenSorting]); + useFirstRender(() => apiRef.current.applySorting()); const handleColumnHeaderClick = React.useCallback( diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index a32a8aa84a300..7667c87082f7a 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -35,7 +35,7 @@ const insertRowInTree = ( } if (!parent.children) { - parent.children = new Map(); + parent.children = new Map(); } insertRowInTree(parent.children, id, restPath, depth + 1); @@ -90,7 +90,7 @@ export const useGridTreeData = ( .sort((a, b) => a.path.length - b.path.length); const paths = Object.fromEntries(rows.map((row) => [row.id, row.path])); - const tree = new Map(); + const tree = new Map(); rows.forEach((row) => { insertRowInTree(tree, row.id, row.path); }); diff --git a/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts index 3d40467c2eb52..0e08067b85f1d 100644 --- a/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts +++ b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts @@ -6,12 +6,9 @@ import { } from './gridRowGroupsPreProcessingApi'; import { GridEvents } from '../../../constants/eventsConstants'; import { useGridApiMethod } from '../useGridApiMethod'; -import { GridRowConfigTreeNode } from '../../../models/gridRows'; const getFlatRowTree: RowGroupingFunction = (params) => ({ - tree: new Map( - params.ids.map((id) => [id.toString(), { id, depth: 0 }]), - ), + tree: new Map(params.ids.map((id) => [id.toString(), { id, depth: 0 }])), paths: Object.fromEntries(params.ids.map((id) => [id, [id.toString()]])), }); diff --git a/packages/grid/_modules_/grid/models/gridOptions.tsx b/packages/grid/_modules_/grid/models/gridOptions.tsx index 1373a1f022356..a0fe29cee0049 100644 --- a/packages/grid/_modules_/grid/models/gridOptions.tsx +++ b/packages/grid/_modules_/grid/models/gridOptions.tsx @@ -118,6 +118,11 @@ export interface GridSimpleOptions { * @default false */ disableMultipleColumnsSorting: boolean; + /** + * If `true`, the sorting will only be applied to the top level rows + * @default false + */ + disableChildrenSorting: boolean; /** * If `true`, the selection on click on a row or cell is disabled. * @default false @@ -257,6 +262,7 @@ export const GRID_DEFAULT_SIMPLE_OPTIONS: GridSimpleOptions = { disableChildrenFiltering: false, disableMultipleSelection: false, disableMultipleColumnsSorting: false, + disableChildrenSorting: false, disableSelectionOnClick: false, disableVirtualization: false, editMode: GridEditModes.Cell, diff --git a/packages/grid/_modules_/grid/models/params/gridSlotComponentProps.ts b/packages/grid/_modules_/grid/models/params/gridSlotComponentProps.ts index f9825f705f714..7c10f7a40ae44 100644 --- a/packages/grid/_modules_/grid/models/params/gridSlotComponentProps.ts +++ b/packages/grid/_modules_/grid/models/params/gridSlotComponentProps.ts @@ -2,7 +2,6 @@ import { GridState } from '../../hooks/features/core/gridState'; import { GridApiRef } from '../api/gridApiRef'; import { GridColumns } from '../colDef/gridColDef'; import { GridRootContainerRef } from '../gridRootContainerRef'; -import { GridRowConfigTree } from '../gridRows'; /** * Object passed as React prop in the component override. diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index bcecfc51a78a1..15ea3a8dae7a2 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -124,15 +124,12 @@ describe.only(' - Tree Data', () => { describe('grouping column', () => { it('should add a grouping column', () => { render(); - const columnsHeader = getColumnHeadersTextContent(); - expect(columnsHeader).to.have.length(baselineProps.columns.length + 1); - expect(columnsHeader[0]).to.equal('Group'); + expect(columnsHeader).to.deep.equal(['Group', 'name']); }); it('should toggle expansion when clicking on grouping column icon', () => { render(); - expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); fireEvent.click(getCell(0, 0).querySelector('button')); expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'C']); @@ -142,7 +139,6 @@ describe.only(' - Tree Data', () => { it('should toggle expansion when pressing Space while focusing grouping column', () => { render(); - expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); getCell(0, 0).focus(); expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); @@ -204,11 +200,18 @@ describe.only(' - Tree Data', () => { expect(getColumnValues(1)).to.deep.equal(['D', 'A', 'A.B', 'A.A']); }); - it('should apply the sortModel on every depth of the tree', () => { + it('should apply the sortModel on every depth of the tree if props.disableChildrenSorting = false', () => { render(); expect(getColumnValues(1)).to.deep.equal(['C', 'B', 'A']); fireEvent.click(getCell(2, 0).querySelector('button')); expect(getColumnValues(1)).to.deep.equal(['C', 'B', 'A', 'A.B', 'A.A']); }); + + it('should only apply the sortModel on top level rows if props.disableChildrenSorting = true', () => { + render(); + expect(getColumnValues(1)).to.deep.equal(['C', 'B', 'A']); + fireEvent.click(getCell(2, 0).querySelector('button')); + expect(getColumnValues(1)).to.deep.equal(['C', 'B', 'A', 'A.A', 'A.B']); + }); }); }); From 84b47773e5a66b18044d06415a8e810df42e4929 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 30 Sep 2021 14:16:39 +0200 Subject: [PATCH 132/390] Export CSV : add expanded children in export --- .../hooks/features/export/serializers/csvSerializer.ts | 6 +++--- .../grid/hooks/features/export/useGridCsvExport.tsx | 4 ++-- packages/storybook/src/stories/grid-tree-data.stories.tsx | 7 ++++++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts b/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts index eb618551fa5b8..8c208023ae5c6 100644 --- a/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts +++ b/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts @@ -3,9 +3,9 @@ import { gridCheckboxSelectionColDef, GridStateColDef, GridRowId, - GridRowModel, } from '../../../../models'; import { GridExportCsvDelimiter } from '../../../../models/gridExport'; +import {FlatSortedVisibleRow} from "../../filter/gridFilterSelector"; const serialiseCellValue = (value: any, delimiterCharacter: GridExportCsvDelimiter) => { if (typeof value === 'string') { @@ -35,7 +35,7 @@ export function serialiseRow( interface BuildCSVOptions { columns: GridStateColDef[]; - rows: Map; + rows: FlatSortedVisibleRow[]; selectedRowIds: GridRowId[]; getCellParams: (id: GridRowId, field: string) => GridCellParams; delimiterCharacter: GridExportCsvDelimiter; @@ -51,7 +51,7 @@ export function buildCSV(options: BuildCSVOptions): string { delimiterCharacter, includeHeaders = true, } = options; - let rowIds = [...rows.keys()]; + let rowIds = rows.map(row => row.id) if (selectedRowIds.length) { rowIds = rowIds.filter((id) => selectedRowIds.includes(id)); diff --git a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx index 6140d6c77998c..26718c51fe20e 100644 --- a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx +++ b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx @@ -3,7 +3,7 @@ import { GridApiRef } from '../../../models/api/gridApiRef'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridSelector } from '../core/useGridSelector'; import { allGridColumnsSelector, visibleGridColumnsSelector } from '../columns'; -import { gridSortedVisibleRowsSelector } from '../filter'; +import {gridSortedVisibleRowsAsArrayFlatSelector, gridSortedVisibleRowsSelector} from '../filter'; import { gridSelectionStateSelector } from '../selection'; import { GridCsvExportApi } from '../../../models/api/gridCsvExportApi'; import { GridExportCsvOptions } from '../../../models/gridExport'; @@ -23,7 +23,7 @@ export const useGridCsvExport = (apiRef: GridApiRef): void => { const logger = useGridLogger(apiRef, 'useGridCsvExport'); const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); const columns = useGridSelector(apiRef, allGridColumnsSelector); - const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowsSelector); + const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowsAsArrayFlatSelector); const selection = useGridSelector(apiRef, gridSelectionStateSelector); const getDataAsCsv = React.useCallback( diff --git a/packages/storybook/src/stories/grid-tree-data.stories.tsx b/packages/storybook/src/stories/grid-tree-data.stories.tsx index 9655aa6bb8267..d4ab7cef48255 100644 --- a/packages/storybook/src/stories/grid-tree-data.stories.tsx +++ b/packages/storybook/src/stories/grid-tree-data.stories.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { DataGridPro, DataGridProProps } from '@mui/x-data-grid-pro'; +import { DataGridPro, GridToolbar, DataGridProProps } from '@mui/x-data-grid-pro'; import { Meta } from '@storybook/react'; import Button from '@mui/material/Button'; @@ -97,3 +97,8 @@ export function TreeDataPagination() {
); } + +export function TreeDataToolbar() { + return ; + +} \ No newline at end of file From 5c507253e546117cb8b8ed1da0190c5ca8dc0da1 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 30 Sep 2021 14:33:57 +0200 Subject: [PATCH 133/390] Clean --- .../columnSelection/GridHeaderCheckbox.tsx | 12 ++++++----- .../features/clipboard/useGridClipboard.ts | 9 +++++---- .../export/serializers/csvSerializer.ts | 20 +++---------------- .../features/export/useGridCsvExport.tsx | 11 +++++++--- .../features/filter/gridFilterSelector.ts | 5 ----- .../pagination/gridPaginationSelector.ts | 6 +++--- .../src/stories/grid-tree-data.stories.tsx | 3 +-- 7 files changed, 27 insertions(+), 39 deletions(-) diff --git a/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx b/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx index dce9557150864..46e3176788e93 100644 --- a/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx +++ b/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx @@ -2,8 +2,8 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { GridEvents } from '../../constants/eventsConstants'; import { useGridSelector } from '../../hooks/features/core/useGridSelector'; -import { gridPaginatedVisibleSortedGridRowIdsSelector } from '../../hooks/features/pagination/gridPaginationSelector'; -import { gridSortedVisibleRowIdsSelector } from '../../hooks/features/filter/gridFilterSelector'; +import { gridSortedVisiblePaginatedRowsAsArrayFlatSelector } from '../../hooks/features/pagination/gridPaginationSelector'; +import { gridSortedVisibleRowsAsArrayFlatSelector } from '../../hooks/features/filter/gridFilterSelector'; import { gridTabIndexColumnHeaderSelector } from '../../hooks/features/focus/gridFocusStateSelector'; import { gridRowCountSelector } from '../../hooks/features/rows/gridRowsSelector'; import { gridSelectionStateSelector } from '../../hooks/features/selection/gridSelectionSelector'; @@ -57,9 +57,11 @@ const GridHeaderCheckbox = React.forwardRef row.id); apiRef.current.selectRows(rowsToBeSelected, checked, !event.target.indeterminate); }; diff --git a/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts b/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts index a30648f5e4743..104bf43989db5 100644 --- a/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts +++ b/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts @@ -7,6 +7,8 @@ import { gridCheckboxSelectionColDef } from '../../../models/colDef'; import { GridClipboardApi } from '../../../models/api'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useNativeEventListener } from '../../root'; +import { FlatSortedVisibleRow } from '../filter'; +import { GridRowId } from '../../../models'; function writeToClipboardPolyfill(data: string) { const span = document.createElement('span'); @@ -40,19 +42,18 @@ export const useGridClipboard = (apiRef: GridApiRef): void => { const copySelectedRowsToClipboard = React.useCallback( (includeHeaders = false) => { - const selectedRows = apiRef.current.getSelectedRows(); + const selectedRows: GridRowId[] = Array.from(apiRef.current.getSelectedRows().keys()); const filteredColumns = visibleColumns.filter( (column) => column.field !== gridCheckboxSelectionColDef.field, ); - if (selectedRows.size === 0 || filteredColumns.length === 0) { + if (selectedRows.length === 0 || filteredColumns.length === 0) { return; } const data = buildCSV({ columns: visibleColumns, - rows: selectedRows, - selectedRowIds: [], + rowIds: selectedRows, includeHeaders, getCellParams: apiRef.current.getCellParams, delimiterCharacter: '\t', diff --git a/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts b/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts index 8c208023ae5c6..3930a7a50ea68 100644 --- a/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts +++ b/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts @@ -5,7 +5,7 @@ import { GridRowId, } from '../../../../models'; import { GridExportCsvDelimiter } from '../../../../models/gridExport'; -import {FlatSortedVisibleRow} from "../../filter/gridFilterSelector"; +import { FlatSortedVisibleRow } from '../../filter/gridFilterSelector'; const serialiseCellValue = (value: any, delimiterCharacter: GridExportCsvDelimiter) => { if (typeof value === 'string') { @@ -35,28 +35,14 @@ export function serialiseRow( interface BuildCSVOptions { columns: GridStateColDef[]; - rows: FlatSortedVisibleRow[]; - selectedRowIds: GridRowId[]; + rowIds: GridRowId[]; getCellParams: (id: GridRowId, field: string) => GridCellParams; delimiterCharacter: GridExportCsvDelimiter; includeHeaders?: boolean; } export function buildCSV(options: BuildCSVOptions): string { - const { - columns, - rows, - selectedRowIds, - getCellParams, - delimiterCharacter, - includeHeaders = true, - } = options; - let rowIds = rows.map(row => row.id) - - if (selectedRowIds.length) { - rowIds = rowIds.filter((id) => selectedRowIds.includes(id)); - } - + const { columns, rowIds, getCellParams, delimiterCharacter, includeHeaders = true } = options; const CSVBody = rowIds .reduce( (acc, id) => diff --git a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx index 26718c51fe20e..2c0a6a81220c0 100644 --- a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx +++ b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx @@ -3,7 +3,7 @@ import { GridApiRef } from '../../../models/api/gridApiRef'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridSelector } from '../core/useGridSelector'; import { allGridColumnsSelector, visibleGridColumnsSelector } from '../columns'; -import {gridSortedVisibleRowsAsArrayFlatSelector, gridSortedVisibleRowsSelector} from '../filter'; +import { gridSortedVisibleRowsAsArrayFlatSelector } from '../filter'; import { gridSelectionStateSelector } from '../selection'; import { GridCsvExportApi } from '../../../models/api/gridCsvExportApi'; import { GridExportCsvOptions } from '../../../models/gridExport'; @@ -42,10 +42,15 @@ export const useGridCsvExport = (apiRef: GridApiRef): void => { exportedColumns = validColumns.filter((column) => !column.disableExport); } + // TODO: Use a selector with only the ids + let exportedRowIds = visibleSortedRows.map((el) => el.id); + if (selection.length) { + exportedRowIds = exportedRowIds.filter((id) => selection.includes(id)); + } + return buildCSV({ columns: exportedColumns, - rows: visibleSortedRows, - selectedRowIds: selection, + rowIds: exportedRowIds, getCellParams: apiRef.current.getCellParams, delimiterCharacter: options?.delimiter || ',', }); diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index e2812aed4ddb9..26b50a67e4c26 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -91,11 +91,6 @@ export const gridSortedVisibleRowsAsArrayFlatSelector = createSelector( }, ); -export const gridSortedVisibleRowIdsSelector = createSelector( - gridSortedVisibleRowsSelector, - (visibleSortedRows) => [...visibleSortedRows.keys()], -); - export const activeGridFilterItemsSelector = createSelector( gridFilterModelSelector, gridColumnLookupSelector, diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts index 28e35f2bbc681..82596c82c9758 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts @@ -1,6 +1,6 @@ import { createSelector } from 'reselect'; import { GridState } from '../core/gridState'; -import { gridSortedVisibleRowIdsSelector } from '../filter/gridFilterSelector'; +import { gridSortedVisibleRowsAsArrayFlatSelector } from '../filter/gridFilterSelector'; import { GridPaginationState } from './gridPaginationState'; export const gridPaginationSelector = (state: GridState): GridPaginationState => state.pagination; @@ -15,9 +15,9 @@ export const gridPageSizeSelector = createSelector( (pagination) => pagination.pageSize, ); -export const gridPaginatedVisibleSortedGridRowIdsSelector = createSelector( +export const gridSortedVisiblePaginatedRowsAsArrayFlatSelector = createSelector( gridPaginationSelector, - gridSortedVisibleRowIdsSelector, + gridSortedVisibleRowsAsArrayFlatSelector, (pagination, visibleSortedRows) => { const firstSelectedRowIndex = pagination.page * pagination.pageSize; const lastSelectedRowIndex = firstSelectedRowIndex + pagination.pageSize; diff --git a/packages/storybook/src/stories/grid-tree-data.stories.tsx b/packages/storybook/src/stories/grid-tree-data.stories.tsx index d4ab7cef48255..a121015ec596d 100644 --- a/packages/storybook/src/stories/grid-tree-data.stories.tsx +++ b/packages/storybook/src/stories/grid-tree-data.stories.tsx @@ -100,5 +100,4 @@ export function TreeDataPagination() { export function TreeDataToolbar() { return ; - -} \ No newline at end of file +} From da33e188d8b74378820c8180b8281d61a6ccfdd9 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 30 Sep 2021 14:44:48 +0200 Subject: [PATCH 134/390] Clean --- .../grid/hooks/features/filter/useGridFilter.ts | 5 ++++- .../_modules_/grid/hooks/features/rows/gridRowsState.ts | 5 ++--- .../_modules_/grid/hooks/features/rows/useGridRows.ts | 9 +-------- .../_modules_/grid/hooks/root/useGridContainerProps.ts | 9 +++++++-- .../grid/x-grid/src/tests/treeData.DataGridPro.test.tsx | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 788b44ae7fe73..242fdb9f576f2 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -27,7 +27,10 @@ import { import { GridSortedRowsTree } from '../sorting'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; -import { gridExpandedRowCountSelector, gridTopLevelRowCountSelector } from '../rows'; +import { + gridExpandedRowCountSelector, + gridTopLevelRowCountSelector, +} from '../rows/gridRowsSelector'; const checkFilterModelValidity = (model: GridFilterModel) => { if (model.items.length > 1) { diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts index 60066d96e9be9..c58029b8db904 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts @@ -2,14 +2,13 @@ import { GridRowId, GridRowModel, GridRowConfigTree } from '../../../models/grid export interface GridRowsState { idRowsLookup: Record; + tree: GridRowConfigTree; /** - * Path in the tree to access to a given row + * Path in the tree to access a given row */ paths: Record; - tree: GridRowConfigTree; - /** * Amount of rows before applying the filtering * It also count the expanded and collapsed children rows diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 7769eb855b2d9..8beec37390e59 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -209,14 +209,7 @@ export const useGridRows = ( ); const getRowIdFromRowIndex = React.useCallback( - (index) => { - if (apiRef.current.getFlatSortedRowIds) { - return apiRef.current.getFlatSortedRowIds()[index]; - } - - // TODO: Remove, getFlatSortedRowIds should always be defined - return gridRowIdsFlatSelector(apiRef.current.state)[index]; - }, + (index) => apiRef.current.getFlatSortedRowIds()[index], [apiRef], ); diff --git a/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts b/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts index eaf77971abd54..29a03bb97695a 100644 --- a/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts +++ b/packages/grid/_modules_/grid/hooks/root/useGridContainerProps.ts @@ -50,6 +50,7 @@ export const useGridContainerProps = ( | 'pagination' | 'autoPageSize' | 'pageSize' + | 'paginationMode' | 'autoHeight' | 'hideFooter' | 'scrollbarSize' @@ -95,8 +96,11 @@ export const useGridContainerProps = ( const getVirtualRowCount = React.useCallback(() => { logger.debug('Calculating virtual row count.'); - - if (props.pagination && (!props.autoPageSize || props.pageSize)) { + if ( + props.pagination && + props.paginationMode === 'client' && + (!props.autoPageSize || props.pageSize) + ) { const rowsLeft = visibleRowsCount - paginationState.page * paginationState.pageSize; return rowsLeft > paginationState.pageSize ? paginationState.pageSize : rowsLeft; } @@ -106,6 +110,7 @@ export const useGridContainerProps = ( props.autoPageSize, props.pagination, props.pageSize, + props.paginationMode, paginationState.page, paginationState.pageSize, visibleRowsCount, diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index 15ea3a8dae7a2..1cea888b86d2d 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -32,7 +32,7 @@ const baselineProps: DataGridProProps = { getRowId: (row) => row.name, }; -describe.only(' - Tree Data', () => { +describe(' - Tree Data', () => { // TODO v5: replace with createClientRender const render = createClientRenderStrictMode(); From 82ab273ca0e877fa45e00ad1f9782ecead06d08f Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 30 Sep 2021 20:54:33 +0200 Subject: [PATCH 135/390] Fix --- .../features/clipboard/useGridClipboard.ts | 3 +- .../export/serializers/csvSerializer.ts | 1 - .../grid/hooks/features/rows/useGridRows.ts | 40 +++++++++---------- .../features/treeData/useGridTreeData.ts | 2 +- .../_modules_/grid/models/api/gridRowApi.ts | 10 +++-- 5 files changed, 26 insertions(+), 30 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts b/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts index 104bf43989db5..0adda5362d23f 100644 --- a/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts +++ b/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts @@ -7,8 +7,7 @@ import { gridCheckboxSelectionColDef } from '../../../models/colDef'; import { GridClipboardApi } from '../../../models/api'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useNativeEventListener } from '../../root'; -import { FlatSortedVisibleRow } from '../filter'; -import { GridRowId } from '../../../models'; +import { GridRowId } from '../../../models/gridRows'; function writeToClipboardPolyfill(data: string) { const span = document.createElement('span'); diff --git a/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts b/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts index 3930a7a50ea68..5a96276fbf3e7 100644 --- a/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts +++ b/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts @@ -5,7 +5,6 @@ import { GridRowId, } from '../../../../models'; import { GridExportCsvDelimiter } from '../../../../models/gridExport'; -import { FlatSortedVisibleRow } from '../../filter/gridFilterSelector'; const serialiseCellValue = (value: any, delimiterCharacter: GridExportCsvDelimiter) => { if (typeof value === 'string') { diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 8beec37390e59..63b8ddb0229ec 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -162,27 +162,28 @@ const getRowsStateFromCache = ( return { ...rowState, tree, paths, totalRowCount, totalTopLevelRowCount }; }; +// The cache is always redefined synchronously in `useGridStateInit` so this object don't need to be regenerated across DataGrid instances. +const INITIAL_GRID_ROWS_INTERNAL_CACHE: GridRowsInternalCache = { + state: { + idRowsLookup: {}, + propRowCount: undefined, + propGetRowId: undefined, + rowIds: [], + inputRows: [], + }, + timeout: null, + lastUpdateMs: Date.now(), +} + /** - * @requires useGridSorting (method) - * TODO: Impossible priority - useGridSorting also needs to be after useGridRows (which causes all the existence check for apiRef.current.apiRef.current.getSortedRowIds) + * @requires useGridSorting (method) - can be after, async only (TODO: Remove after moving the 2 methods to useGridSorting) */ export const useGridRows = ( apiRef: GridApiRef, props: Pick, ): void => { const logger = useGridLogger(apiRef, 'useGridRows'); - - const rowsCache = React.useRef({ - state: { - idRowsLookup: {}, - propRowCount: undefined, - propGetRowId: undefined, - rowIds: [], - inputRows: [], - }, - timeout: null, - lastUpdateMs: Date.now(), - }); + const rowsCache = React.useRef(INITIAL_GRID_ROWS_INTERNAL_CACHE); useGridStateInit(apiRef, (state) => { rowsCache.current.state = convertGridRowsPropToState( @@ -196,18 +197,13 @@ export const useGridRows = ( const [, setGridState, forceUpdate] = useGridState(apiRef); + // TODO: Move in useGridSorting const getRowIndex = React.useCallback( - (id) => { - if (apiRef.current.getFlatSortedRowIds) { - return apiRef.current.getFlatSortedRowIds().indexOf(id); - } - - // TODO: Remove, getFlatSortedRowIds should always be defined - return gridRowIdsFlatSelector(apiRef.current.state).indexOf(id); - }, + (id) => apiRef.current.getFlatSortedRowIds().indexOf(id), [apiRef], ); + // TODO: Move in useGridSorting const getRowIdFromRowIndex = React.useCallback( (index) => apiRef.current.getFlatSortedRowIds()[index], [apiRef], diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 7667c87082f7a..241397445d250 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -1,5 +1,5 @@ import * as React from 'react'; -import { GridRowId, GridRowConfigTree, GridRowConfigTreeNode } from '../../../models/gridRows'; +import { GridRowId, GridRowConfigTree } from '../../../models/gridRows'; import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridComponentProps } from '../../../GridComponentProps'; import { GridColumnsPreProcessing } from '../../root/columnsPreProcessing'; diff --git a/packages/grid/_modules_/grid/models/api/gridRowApi.ts b/packages/grid/_modules_/grid/models/api/gridRowApi.ts index 2dc41eca6f8b0..832821518de7d 100644 --- a/packages/grid/_modules_/grid/models/api/gridRowApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridRowApi.ts @@ -36,13 +36,13 @@ export interface GridRowApi { */ updateRows: (updates: GridRowModelUpdate[]) => void; /** - * Gets the `GridRowId` of a row at a specific index. + * Gets the id of a row for a given index in the list of the sorted unfiltered rows. * @param {number} index The index of the row * @returns {GridRowId} The `GridRowId` of the row. */ getRowIdFromRowIndex: (index: number) => GridRowId; /** - * Gets the row index of a row with a given id. + * Gets the index of a row for a given id in the list of the sorted unfiltered rows. * @param {GridRowId} id The `GridRowId` of the row. * @returns {number} The index of the row. */ @@ -60,11 +60,13 @@ export interface GridRowApi { */ getRowNode: (id: GridRowId) => GridRowConfigTreeNode | null; /** - * @param {GridRowId} id the ID of the row to toggle. - * @param {boolean} isExpanded A boolean indicating if the row must be expanded. + * Expand or collapse a row children. + * @param {GridRowId} id the ID of the row to expand or collapse. + * @param {boolean} isExpanded A boolean indicating if the row must be expanded or collapsed. */ setRowExpansion: (id: GridRowId, isExpanded: boolean) => void; /** + * Gets the row path in the tree with a given id. * @param {GridRowId} id the ID of the row to toggle. * @returns {string[] | null} path The path of the row. */ From 4fd733c31a97e78d1a593e3e2c1c48f557251071 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 30 Sep 2021 21:00:20 +0200 Subject: [PATCH 136/390] Clean --- docs/pages/api-docs/data-grid/grid-api.md | 8 ++++---- .../_modules_/grid/hooks/features/rows/useGridRows.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index 26ba8bb4fa173..d5fcd41d0c5fc 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -39,13 +39,13 @@ import { GridApi } from '@mui/x-data-grid-pro'; | getLocaleText | <T extends keyof GridLocaleText>(key: T) => GridLocaleText[T] | Returns the translation for the `key`. | | getRow | (id: GridRowId) => null \| GridRowData | Gets the row data with a given id. | | getRowElement | (id: GridRowId) => null \| HTMLDivElement | Gets the underlying DOM element for a row at the given `id`. | -| getRowIdFromRowIndex | (index: number) => GridRowId | Gets the `GridRowId` of a row at a specific index. | -| getRowIndex | (id: GridRowId) => number | Gets the row index of a row with a given id. | +| getRowIdFromRowIndex | (index: number) => GridRowId | Gets the id of a row for a given index in the list of the sorted unfiltered rows. | +| getRowIndex | (id: GridRowId) => number | Gets the index of a row for a given id in the list of the sorted unfiltered rows. | | getRowMode | (id: GridRowId) => GridRowMode | Gets the mode of a row. | | getRowModels | () => GridRowConfigTree | Gets the full set of rows ordered in a tree structure. | | getRowNode | (id: GridRowId) => null \| GridRowConfigTreeNode | Gets the row node from the internal tree structure. | | getRowParams | (id: GridRowId) => GridRowParams | Gets the [GridRowParams](/api/data-grid/grid-row-params/) object that is passed as argument in events. | -| getRowPath | (id: GridRowId) => null \| string[] | | +| getRowPath | (id: GridRowId) => null \| string[] | Gets the row path in the tree with a given id. | | getRowsCount | () => number | Gets the total number of rows in the grid. | | getScrollPosition | () => GridScrollParams | Returns the current scroll position. | | getSelectedRows | () => Map<GridRowId, GridRowData> | Returns an array of the selected rows. | @@ -77,7 +77,7 @@ import { GridApi } from '@mui/x-data-grid-pro'; | setFilterModel | (model: GridFilterModel) => void | Sets the filter model to the one given by `model`. | | setPage | (page: number) => void | Sets the displayed page to the value given by `page`. | | setPageSize | (pageSize: number) => void | Sets the number of displayed rows to the value given by `pageSize`. | -| setRowExpansion | (id: GridRowId, isExpanded: boolean) => void | | +| setRowExpansion | (id: GridRowId, isExpanded: boolean) => void | Expand or collapse a row children. | | setRowMode | (id: GridRowId, mode: GridRowMode) => void | Sets the mode of a row. | | setRows | (rows: GridRowData[]) => void | Sets a new set of rows. | | setSelectionModel | (rowIds: GridRowId[]) => void | Updates the selected rows to be those passed to the `rowIds` argument.
Any row already selected will be unselected. | diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 63b8ddb0229ec..1fd924698f729 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -173,7 +173,7 @@ const INITIAL_GRID_ROWS_INTERNAL_CACHE: GridRowsInternalCache = { }, timeout: null, lastUpdateMs: Date.now(), -} +}; /** * @requires useGridSorting (method) - can be after, async only (TODO: Remove after moving the 2 methods to useGridSorting) From e1c8bf45cf17f97dc995fc7676cf0e4d25505476 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 1 Oct 2021 11:23:45 +0200 Subject: [PATCH 137/390] New hook: useDemoTreeData --- .../data-grid/group-pivot/BasicTreeData.tsx | 50 ++--------- .../pagination/CursorPaginationGrid.tsx | 4 +- .../pagination/ServerPaginationGrid.tsx | 4 +- ...ontrolledSelectionServerPaginationGrid.tsx | 4 +- .../data-grid/sorting/ServerSortingGrid.tsx | 4 +- .../src/services/real-data-service.ts | 27 +++--- .../x-grid-data-generator/src/useDemoData.ts | 14 +-- .../src/useDemoTreeData.ts | 77 ++++++++++++++++ .../src/stories/grid-tree-data.stories.tsx | 88 ++++++++----------- 9 files changed, 147 insertions(+), 125 deletions(-) create mode 100644 packages/grid/x-grid-data-generator/src/useDemoTreeData.ts diff --git a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx index d79dfd3748ba9..813230d6a76ae 100644 --- a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx @@ -1,53 +1,13 @@ import * as React from 'react'; -import { DataGridPro, GridColumns, GridRowsProp } from '@mui/x-data-grid-pro'; +import { DataGridPro } from '@mui/x-data-grid-pro'; +import { useDemoTreeData } from '@mui/x-data-grid-generator'; export default function BasicTreeData() { + const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + return (
- row.name.split('.')} - getRowId={(row) => row.name} - disableSelectionOnClick - /> +
); } - -const columns: GridColumns = [{ field: 'name', headerName: 'Name' }]; - -const rows: GridRowsProp = [ - { name: 'A' }, - { name: 'A.A' }, - { name: 'B' }, - { name: 'B.A' }, - { name: 'B.B' }, - { name: 'B.B.A' }, - { name: 'C' }, - { name: 'D' }, - { name: 'D.A' }, - { name: 'D.B' }, - { name: 'D.C' }, - { name: 'D.D' }, - { name: 'D.E' }, - { name: 'D.F' }, - { name: 'D.G' }, - { name: 'D.H' }, - { name: 'D.I' }, - { name: 'D.J' }, - { name: 'D.K' }, - { name: 'E' }, - { name: 'F' }, - { name: 'F.A' }, - { name: 'F.A.A' }, - { name: 'F.A.B' }, - { name: 'F.A.C' }, - { name: 'F.A.D' }, - { name: 'F.A.E' }, - { name: 'G' }, - { name: 'H' }, - { name: 'I' }, - { name: 'J' }, -]; diff --git a/docs/src/pages/components/data-grid/pagination/CursorPaginationGrid.tsx b/docs/src/pages/components/data-grid/pagination/CursorPaginationGrid.tsx index 06c61f0abb346..ab4828d720cf8 100644 --- a/docs/src/pages/components/data-grid/pagination/CursorPaginationGrid.tsx +++ b/docs/src/pages/components/data-grid/pagination/CursorPaginationGrid.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { GridRowsProp, DataGrid, GridRowId } from '@mui/x-data-grid'; -import { useDemoData, GridData, DataRowModel } from '@mui/x-data-grid-generator'; +import { useDemoData, GridDemoData, DataRowModel } from '@mui/x-data-grid-generator'; interface ServerBasedGridResponse { rows: DataRowModel[]; @@ -11,7 +11,7 @@ const PAGE_SIZE = 5; function loadServerRows( cursor: GridRowId | null | undefined, - data: GridData, + data: GridDemoData, ): Promise { return new Promise((resolve) => { setTimeout(() => { diff --git a/docs/src/pages/components/data-grid/pagination/ServerPaginationGrid.tsx b/docs/src/pages/components/data-grid/pagination/ServerPaginationGrid.tsx index 5de91ef5a51f6..d212b2b523acb 100644 --- a/docs/src/pages/components/data-grid/pagination/ServerPaginationGrid.tsx +++ b/docs/src/pages/components/data-grid/pagination/ServerPaginationGrid.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import { GridRowsProp, DataGrid } from '@mui/x-data-grid'; -import { useDemoData, GridData } from '@mui/x-data-grid-generator'; +import { useDemoData, GridDemoData } from '@mui/x-data-grid-generator'; -function loadServerRows(page: number, data: GridData): Promise { +function loadServerRows(page: number, data: GridDemoData): Promise { return new Promise((resolve) => { setTimeout(() => { resolve(data.rows.slice(page * 5, (page + 1) * 5)); diff --git a/docs/src/pages/components/data-grid/selection/ControlledSelectionServerPaginationGrid.tsx b/docs/src/pages/components/data-grid/selection/ControlledSelectionServerPaginationGrid.tsx index c7e5964694e4d..101aad2adb78f 100644 --- a/docs/src/pages/components/data-grid/selection/ControlledSelectionServerPaginationGrid.tsx +++ b/docs/src/pages/components/data-grid/selection/ControlledSelectionServerPaginationGrid.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import { DataGrid, GridRowsProp, GridSelectionModel } from '@mui/x-data-grid'; -import { GridData, useDemoData } from '@mui/x-data-grid-generator'; +import { GridDemoData, useDemoData } from '@mui/x-data-grid-generator'; -function loadServerRows(page: number, data: GridData): Promise { +function loadServerRows(page: number, data: GridDemoData): Promise { return new Promise((resolve) => { setTimeout(() => { resolve(data.rows.slice(page * 5, (page + 1) * 5)); diff --git a/docs/src/pages/components/data-grid/sorting/ServerSortingGrid.tsx b/docs/src/pages/components/data-grid/sorting/ServerSortingGrid.tsx index 9775ee6ebd6fb..d5cfbef7a7fe3 100644 --- a/docs/src/pages/components/data-grid/sorting/ServerSortingGrid.tsx +++ b/docs/src/pages/components/data-grid/sorting/ServerSortingGrid.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import { GridRowsProp, DataGrid, GridSortModel } from '@mui/x-data-grid'; -import { useDemoData, GridData } from '@mui/x-data-grid-generator'; +import { useDemoData, GridDemoData } from '@mui/x-data-grid-generator'; -function loadServerRows(sortModel: GridSortModel, data: GridData): Promise { +function loadServerRows(sortModel: GridSortModel, data: GridDemoData): Promise { return new Promise((resolve) => { setTimeout(() => { if (sortModel.length === 0) { diff --git a/packages/grid/x-grid-data-generator/src/services/real-data-service.ts b/packages/grid/x-grid-data-generator/src/services/real-data-service.ts index 65724d862e4c6..420763e3253da 100644 --- a/packages/grid/x-grid-data-generator/src/services/real-data-service.ts +++ b/packages/grid/x-grid-data-generator/src/services/real-data-service.ts @@ -1,20 +1,19 @@ -import { GridColDef, GridRowId } from '@mui/x-data-grid-pro'; +import { GridRowData } from '@mui/x-data-grid-pro'; import asyncWorker from '../asyncWorker'; +import { GridColDefGenerator } from './gridColDefGenerator'; -export interface DataRowModel { - id: GridRowId; - [field: string]: any; +export interface GridDemoData { + rows: GridRowData[]; + columns: GridColDefGenerator[]; } -export interface GridData { - columns: GridColDef[]; - rows: DataRowModel[]; -} - -export function getRealData(rowLength: number, columns: any[]): Promise { - return new Promise((resolve) => { +export function getRealData( + rowLength: number, + columns: GridColDefGenerator[], +): Promise { + return new Promise((resolve) => { const tasks = { current: rowLength }; - const data: DataRowModel[] = []; + const rows: GridDemoData['rows'] = []; function work() { const row: any = {}; @@ -26,13 +25,13 @@ export function getRealData(rowLength: number, columns: any[]): Promise resolve({ columns, rows: data }), + done: () => resolve({ columns, rows }), tasks, }); }); diff --git a/packages/grid/x-grid-data-generator/src/useDemoData.ts b/packages/grid/x-grid-data-generator/src/useDemoData.ts index 51332ad749215..bf180390a1461 100644 --- a/packages/grid/x-grid-data-generator/src/useDemoData.ts +++ b/packages/grid/x-grid-data-generator/src/useDemoData.ts @@ -1,18 +1,18 @@ import * as React from 'react'; import LRUCache from 'lru-cache'; -import { GridData, getRealData } from './services/real-data-service'; +import { GridDemoData, getRealData } from './services/real-data-service'; import { getCommodityColumns } from './commodities.columns'; import { getEmployeeColumns } from './employees.columns'; import asyncWorker from './asyncWorker'; import { GridColDefGenerator } from './services/gridColDefGenerator'; -const dataCache = new LRUCache({ +const dataCache = new LRUCache({ max: 10, maxAge: 60 * 5 * 1e3, // 5 minutes }); export type DemoDataReturnType = { - data: GridData; + data: GridDemoData; loading: boolean; setRowLength: (count: number) => void; loadNewData: () => void; @@ -32,8 +32,8 @@ export interface DemoDataOptions { async function extrapolateSeed( rowLength: number, columns: GridColDefGenerator[], - data: GridData, -): Promise { + data: GridDemoData, +): Promise { return new Promise((resolve) => { const seed = data.rows; const rows = data.rows.slice(); @@ -101,7 +101,7 @@ export const useDemoData = (options: DemoDataOptions): DemoDataReturnType => { return columns; }, [options.dataSet, options.editable, options.maxColumns]); - const [data, setData] = React.useState({ columns: getColumns(), rows: [] }); + const [data, setData] = React.useState({ columns: getColumns(), rows: [] }); React.useEffect(() => { const cacheKey = `${options.dataSet}-${rowLength}-${index}-${options.maxColumns}`; @@ -120,7 +120,7 @@ export const useDemoData = (options: DemoDataOptions): DemoDataReturnType => { (async () => { setLoading(true); - let newData: GridData; + let newData: GridDemoData; const columns = getColumns(); if (rowLength > 1000) { newData = await getRealData(1000, columns); diff --git a/packages/grid/x-grid-data-generator/src/useDemoTreeData.ts b/packages/grid/x-grid-data-generator/src/useDemoTreeData.ts new file mode 100644 index 0000000000000..75196653986a6 --- /dev/null +++ b/packages/grid/x-grid-data-generator/src/useDemoTreeData.ts @@ -0,0 +1,77 @@ +import * as React from 'react'; +import { GridDemoData, randomInt } from '@mui/x-data-grid-generator/services'; +import { DataGridProProps, GridColumns, GridRowModel } from '@mui/x-data-grid-pro'; + +interface GridDemoTreeDataOptions { + rowLength: number[]; +} + +interface GridDemoTreeData extends GridDemoData, Pick {} + +interface GridDemoTreeDataResponse { + loading: boolean; + data: GridDemoTreeData; +} + +const TREE_DATA_COLUMNS: GridColumns = [ + { + field: 'name', + headerName: 'Name', + width: 150, + }, +]; + +const getTreeDataPath = (row: GridRowModel) => row.path.map((value) => `Element ${value}`); + +interface GetTreeDataRowsOptions { + rowLength: number[]; + parentPath?: string[]; + id?: number; +} + +const getTreeDataRows = ({ rowLength, parentPath = [], id = 0 }: GetTreeDataRowsOptions) => { + const rows: GridRowModel[] = []; + + const [currentDepthRowLength, ...restRowLength] = rowLength; + + // For top level rows we respect the exact length given, for deeper rows we add a random factor + const realCurrentDepthRowLength = + parentPath.length === 0 ? currentDepthRowLength : randomInt(0, currentDepthRowLength * 2); + + for (let index = 1; index < realCurrentDepthRowLength + 1; index += 1) { + const path = [...parentPath, index.toString()]; + const name = `Element n°${path.join('-')}`; + rows.push({ name, path, id }); + + id += 1; + + if (restRowLength.length) { + const childrenRows = getTreeDataRows({ rowLength: restRowLength, parentPath: path, id }); + + rows.push(...childrenRows.rows); + id = childrenRows.id; + } + } + + return { rows, id }; +}; + +export const useDemoTreeData = (options: GridDemoTreeDataOptions) => { + const [response, setResponse] = React.useState(() => ({ + loading: true, + data: { columns: TREE_DATA_COLUMNS, getTreeDataPath, rows: [] }, + })); + + const rowLengthStr = options.rowLength.join('-'); + + React.useEffect(() => { + setResponse((prev) => (prev.loading ? prev : { ...prev, loading: true })); + const { rows } = getTreeDataRows({ + rowLength: rowLengthStr.split('-').map((el) => Number(el)), + }); + + setResponse((prev) => ({ ...prev, loading: false, data: { ...prev.data, rows } })); + }, [rowLengthStr]); + + return response; +}; diff --git a/packages/storybook/src/stories/grid-tree-data.stories.tsx b/packages/storybook/src/stories/grid-tree-data.stories.tsx index a121015ec596d..7b3ab4933e48f 100644 --- a/packages/storybook/src/stories/grid-tree-data.stories.tsx +++ b/packages/storybook/src/stories/grid-tree-data.stories.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { DataGridPro, GridToolbar, DataGridProProps } from '@mui/x-data-grid-pro'; import { Meta } from '@storybook/react'; import Button from '@mui/material/Button'; +import { useDemoTreeData } from '@mui/x-data-grid-generator/useDemoTreeData'; export default { title: 'X-Grid Tests/Tree Data', @@ -11,53 +12,8 @@ export default { }, } as Meta; -const baselineProps: DataGridProProps = { - rows: [ - { name: 'A' }, - { name: 'A.A' }, - { name: 'B' }, - { name: 'B.A' }, - { name: 'B.B' }, - { name: 'B.B.A' }, - { name: 'C' }, - { name: 'D' }, - { name: 'D.A' }, - { name: 'D.B' }, - { name: 'D.C' }, - { name: 'D.D' }, - { name: 'D.E' }, - { name: 'D.F' }, - { name: 'D.G' }, - { name: 'D.H' }, - { name: 'D.I' }, - { name: 'D.J' }, - { name: 'D.K' }, - { name: 'E' }, - { name: 'F' }, - { name: 'F.A' }, - { name: 'F.A.A' }, - { name: 'F.A.B' }, - { name: 'F.A.C' }, - { name: 'F.A.D' }, - { name: 'F.A.E' }, - { name: 'G' }, - { name: 'H' }, - { name: 'I' }, - { name: 'J' }, - ], - columns: [ - { - field: 'name', - width: 200, - }, - ], - treeData: true, - disableSelectionOnClick: true, - getRowId: (row) => row.name, - getTreeDataPath: (row) => row.name.split('.'), -}; - export function BasicTreeData() { + const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); const [treeDataEnabled, setTreeDataEnabled] = React.useState(true); return ( @@ -70,12 +26,14 @@ export function BasicTreeData() { > {treeDataEnabled ? 'Disable tree data' : 'Enable tree data'} - +
); } export function CustomGroupingColumn() { + const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + const groupingColDef = React.useMemo( () => ({ headerName: 'Custom header', @@ -83,21 +41,49 @@ export function CustomGroupingColumn() { [], ); - return ; + return ( + + ); } export function TreeDataWithCheckboxSelection() { - return ; + const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + + return ; } export function TreeDataPagination() { + const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + return (
- +
); } export function TreeDataToolbar() { - return ; + const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + + return ( + + ); } From a607faa35a32a2b7422e443ee96601ef6b7d7991 Mon Sep 17 00:00:00 2001 From: flavien Date: Mon, 4 Oct 2021 15:02:46 +0200 Subject: [PATCH 138/390] Update Getting Started page --- .../components/data-grid/getting-started/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/pages/components/data-grid/getting-started/getting-started.md b/docs/src/pages/components/data-grid/getting-started/getting-started.md index 947a30ea28325..16e257a694b12 100644 --- a/docs/src/pages/components/data-grid/getting-started/getting-started.md +++ b/docs/src/pages/components/data-grid/getting-started/getting-started.md @@ -188,7 +188,7 @@ The enterprise components come in two plans: Pro and Premium. | [Column virtualization](/components/data-grid/virtualization/#column-virtualization) | ✅ | ✅ | ✅ | | [Row virtualization > 100 rows](/components/data-grid/virtualization/#row-virtualization) | ❌ | ✅ | ✅ | | **Group & Pivot** | | | | -| [Tree data](/components/data-grid/group-pivot/#tree-data) | ❌ | 🚧 | 🚧 | +| [Tree data](/components/data-grid/group-pivot/#tree-data) | ❌ | ✅ | ✅ | | [Master detail](/components/data-grid/group-pivot/#master-detail) | ❌ | 🚧 | 🚧 | | [Grouping](/components/data-grid/group-pivot/#grouping) | ❌ | ❌ | 🚧 | | [Aggregation](/components/data-grid/group-pivot/#aggregation) | ❌ | ❌ | 🚧 | From 6b8a707f16cc5dc1c367b297ea2cbb448b21582b Mon Sep 17 00:00:00 2001 From: flavien Date: Mon, 4 Oct 2021 15:03:52 +0200 Subject: [PATCH 139/390] Improve --- packages/grid/x-grid-data-generator/src/index.ts | 1 + packages/storybook/src/stories/grid-tree-data.stories.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/grid/x-grid-data-generator/src/index.ts b/packages/grid/x-grid-data-generator/src/index.ts index a5590f5ed67e6..05610610df288 100644 --- a/packages/grid/x-grid-data-generator/src/index.ts +++ b/packages/grid/x-grid-data-generator/src/index.ts @@ -3,3 +3,4 @@ export * from './services'; export * from './commodities.columns'; export * from './employees.columns'; export * from './useDemoData'; +export * from './useDemoTreeData' \ No newline at end of file diff --git a/packages/storybook/src/stories/grid-tree-data.stories.tsx b/packages/storybook/src/stories/grid-tree-data.stories.tsx index 7b3ab4933e48f..b863c208753b4 100644 --- a/packages/storybook/src/stories/grid-tree-data.stories.tsx +++ b/packages/storybook/src/stories/grid-tree-data.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { DataGridPro, GridToolbar, DataGridProProps } from '@mui/x-data-grid-pro'; import { Meta } from '@storybook/react'; import Button from '@mui/material/Button'; -import { useDemoTreeData } from '@mui/x-data-grid-generator/useDemoTreeData'; +import { useDemoTreeData } from '@mui/x-data-grid-generator'; export default { title: 'X-Grid Tests/Tree Data', From 58f0e080daaf7381bf01f76655a436f51154753c Mon Sep 17 00:00:00 2001 From: flavien Date: Mon, 4 Oct 2021 15:14:32 +0200 Subject: [PATCH 140/390] Prettier --- packages/grid/x-grid-data-generator/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/x-grid-data-generator/src/index.ts b/packages/grid/x-grid-data-generator/src/index.ts index 05610610df288..3b688cac86f5c 100644 --- a/packages/grid/x-grid-data-generator/src/index.ts +++ b/packages/grid/x-grid-data-generator/src/index.ts @@ -3,4 +3,4 @@ export * from './services'; export * from './commodities.columns'; export * from './employees.columns'; export * from './useDemoData'; -export * from './useDemoTreeData' \ No newline at end of file +export * from './useDemoTreeData'; From fdc51f80b1431d0ebc263e2f7974900b390a04a0 Mon Sep 17 00:00:00 2001 From: flavien Date: Mon, 4 Oct 2021 15:22:47 +0200 Subject: [PATCH 141/390] Doc --- .../data-grid/group-pivot/BasicTreeData.js | 48 ++----------------- 1 file changed, 4 insertions(+), 44 deletions(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js index f8937937ba64d..813230d6a76ae 100644 --- a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js @@ -1,53 +1,13 @@ import * as React from 'react'; import { DataGridPro } from '@mui/x-data-grid-pro'; +import { useDemoTreeData } from '@mui/x-data-grid-generator'; export default function BasicTreeData() { + const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + return (
- row.name.split('.')} - getRowId={(row) => row.name} - disableSelectionOnClick - /> +
); } - -const columns = [{ field: 'name', headerName: 'Name' }]; - -const rows = [ - { name: 'A' }, - { name: 'A.A' }, - { name: 'B' }, - { name: 'B.A' }, - { name: 'B.B' }, - { name: 'B.B.A' }, - { name: 'C' }, - { name: 'D' }, - { name: 'D.A' }, - { name: 'D.B' }, - { name: 'D.C' }, - { name: 'D.D' }, - { name: 'D.E' }, - { name: 'D.F' }, - { name: 'D.G' }, - { name: 'D.H' }, - { name: 'D.I' }, - { name: 'D.J' }, - { name: 'D.K' }, - { name: 'E' }, - { name: 'F' }, - { name: 'F.A' }, - { name: 'F.A.A' }, - { name: 'F.A.B' }, - { name: 'F.A.C' }, - { name: 'F.A.D' }, - { name: 'F.A.E' }, - { name: 'G' }, - { name: 'H' }, - { name: 'I' }, - { name: 'J' }, -]; From 0eb8328dad2b7cd8c10671f0e34bc338bb1b4e10 Mon Sep 17 00:00:00 2001 From: flavien Date: Mon, 4 Oct 2021 15:29:31 +0200 Subject: [PATCH 142/390] Fix --- .../data-grid/pagination/CursorPaginationGrid.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/pages/components/data-grid/pagination/CursorPaginationGrid.tsx b/docs/src/pages/components/data-grid/pagination/CursorPaginationGrid.tsx index ab4828d720cf8..fd45aeddbdd60 100644 --- a/docs/src/pages/components/data-grid/pagination/CursorPaginationGrid.tsx +++ b/docs/src/pages/components/data-grid/pagination/CursorPaginationGrid.tsx @@ -1,9 +1,9 @@ import * as React from 'react'; -import { GridRowsProp, DataGrid, GridRowId } from '@mui/x-data-grid'; -import { useDemoData, GridDemoData, DataRowModel } from '@mui/x-data-grid-generator'; +import { GridRowsProp, DataGrid, GridRowId, GridRowModel } from '@mui/x-data-grid'; +import { useDemoData, GridDemoData } from '@mui/x-data-grid-generator'; interface ServerBasedGridResponse { - rows: DataRowModel[]; + rows: GridRowModel[]; nextCursor: GridRowId | null | undefined; } From d720a047e91d4d3efe7529e1be337a362990a41a Mon Sep 17 00:00:00 2001 From: flavien Date: Mon, 4 Oct 2021 16:10:39 +0200 Subject: [PATCH 143/390] Proptypes --- packages/grid/data-grid/src/DataGrid.tsx | 10 ++++++++++ packages/grid/x-grid/src/DataGridPro.tsx | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/packages/grid/data-grid/src/DataGrid.tsx b/packages/grid/data-grid/src/DataGrid.tsx index 2dbe80539c6b5..c07c3b0d8692e 100644 --- a/packages/grid/data-grid/src/DataGrid.tsx +++ b/packages/grid/data-grid/src/DataGrid.tsx @@ -111,6 +111,16 @@ DataGridRaw.propTypes = { * @default "standard" */ density: PropTypes.oneOf(['comfortable', 'compact', 'standard']), + /** + * If `true`, the filtering will only be applied to the top level rows + * @default false + */ + disableChildrenFiltering: PropTypes.bool, + /** + * If `true`, the sorting will only be applied to the top level rows + * @default false + */ + disableChildrenSorting: PropTypes.bool, /** * If `true`, column filters are disabled. * @default false diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index baff5f8d28540..98764e361d8f7 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -134,6 +134,16 @@ DataGridProRaw.propTypes = { * @default "standard" */ density: PropTypes.oneOf(['comfortable', 'compact', 'standard']), + /** + * If `true`, the filtering will only be applied to the top level rows + * @default false + */ + disableChildrenFiltering: PropTypes.bool, + /** + * If `true`, the sorting will only be applied to the top level rows + * @default false + */ + disableChildrenSorting: PropTypes.bool, /** * If `true`, column filters are disabled. * @default false From f3d13c44f76f0ddf11056de56d09c4b53c7c4308 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 6 Oct 2021 09:33:14 +0200 Subject: [PATCH 144/390] Improve --- .../_modules_/grid/hooks/features/rows/useGridRows.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 1fd924698f729..2b0e4a357cd5d 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -51,7 +51,7 @@ export type GridRowsInternalCacheState = Omit< export interface GridRowsInternalCache { state: GridRowsInternalCacheState; timeout: NodeJS.Timeout | null; - lastUpdateMs: number | null; + lastUpdateMs: number; } function getGridRowId( @@ -172,7 +172,7 @@ const INITIAL_GRID_ROWS_INTERNAL_CACHE: GridRowsInternalCache = { inputRows: [], }, timeout: null, - lastUpdateMs: Date.now(), + lastUpdateMs: 0, }; /** @@ -191,6 +191,7 @@ export const useGridRows = ( props.rowCount, props.getRowId, ); + rowsCache.current.lastUpdateMs = Date.now(); return { ...state, rows: getRowsStateFromCache(rowsCache.current, apiRef) }; }); @@ -239,11 +240,7 @@ export const useGridRows = ( return; } - const throttleRemainingTimeMs = - rowsCache.current.lastUpdateMs === null - ? 0 - : props.throttleRowsMs - (Date.now() - rowsCache.current.lastUpdateMs); - + const throttleRemainingTimeMs = props.throttleRowsMs - (Date.now() - rowsCache.current.lastUpdateMs) if (throttleRemainingTimeMs > 0) { rowsCache.current.timeout = setTimeout(run, throttleRemainingTimeMs); return; From adedb03e13e21e031f5e18b4df763a10c0153648 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 7 Oct 2021 09:36:28 +0200 Subject: [PATCH 145/390] New prop defaultGroupingExpansionDepth --- .../features/keyboard/useGridKeyboard.ts | 5 ++- .../grid/hooks/features/rows/useGridRows.ts | 3 +- .../features/treeData/useGridTreeData.ts | 43 ++++++++++++++----- .../_modules_/grid/models/gridOptions.tsx | 6 +++ .../src/tests/treeData.DataGridPro.test.tsx | 40 +++++++++++++++++ .../src/stories/grid-tree-data.stories.tsx | 19 +++++++- 6 files changed, 102 insertions(+), 14 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts index 9174ce624997c..04fa264c25176 100644 --- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts +++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts @@ -11,7 +11,7 @@ import { import { isEnterKey, isNavigationKey, isSpaceKey } from '../../../utils/keyboardUtils'; import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; import { GridCellModes } from '../../../models/gridEditRowModel'; -import { visibleSortedGridRowIdsSelector } from '../filter'; +import { gridSortedVisibleRowsAsArrayFlatSelector } from '../filter'; /** * @requires useGridSelection (method) @@ -37,7 +37,8 @@ export const useGridKeyboard = (apiRef: GridApiRef): void => { )! as HTMLElement; const startRowIndex = Number(rowEl.getAttribute('data-rowindex')); - const startId = visibleSortedGridRowIdsSelector(apiRef.current.state)[startRowIndex]; + const startId = gridSortedVisibleRowsAsArrayFlatSelector(apiRef.current.state)[startRowIndex] + .id; if (startId === focusCell.id) { return; diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 2b0e4a357cd5d..2e2db1403d0b5 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -240,7 +240,8 @@ export const useGridRows = ( return; } - const throttleRemainingTimeMs = props.throttleRowsMs - (Date.now() - rowsCache.current.lastUpdateMs) + const throttleRemainingTimeMs = + props.throttleRowsMs - (Date.now() - rowsCache.current.lastUpdateMs); if (throttleRemainingTimeMs > 0) { rowsCache.current.timeout = setTimeout(run, throttleRemainingTimeMs); return; diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 241397445d250..785f9ed01b47a 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -11,12 +11,19 @@ import { isSpaceKey } from '../../../utils/keyboardUtils'; import { useFirstRender } from '../../utils/useFirstRender'; import { RowGroupingFunction } from '../../root/rowGroupsPerProcessing'; -const insertRowInTree = ( - tree: GridRowConfigTree, - id: GridRowId, - path: string[], - depth: number = 0, -) => { +const insertRowInTree = ({ + tree, + id, + path, + depth, + defaultGroupingExpansionDepth, +}: { + tree: GridRowConfigTree; + id: GridRowId; + path: string[]; + depth: number; + defaultGroupingExpansionDepth: number; +}) => { if (path.length === 0) { throw new Error(`MUI: Could not insert row #${id} in the tree structure.`); } @@ -25,6 +32,7 @@ const insertRowInTree = ( tree.set(path[0], { id, depth, + expanded: defaultGroupingExpansionDepth > depth, }); } else { const [nodeName, ...restPath] = path; @@ -38,7 +46,13 @@ const insertRowInTree = ( parent.children = new Map(); } - insertRowInTree(parent.children, id, restPath, depth + 1); + insertRowInTree({ + tree: parent.children, + id, + path: restPath, + depth: depth + 1, + defaultGroupingExpansionDepth, + }); } }; @@ -47,7 +61,10 @@ const insertRowInTree = ( */ export const useGridTreeData = ( apiRef: GridApiRef, - props: Pick, + props: Pick< + GridComponentProps, + 'treeData' | 'getTreeDataPath' | 'groupingColDef' | 'defaultGroupingExpansionDepth' + >, ) => { const updateColumnsPreProcessing = React.useCallback(() => { if (!props.treeData) { @@ -92,7 +109,13 @@ export const useGridTreeData = ( const paths = Object.fromEntries(rows.map((row) => [row.id, row.path])); const tree = new Map(); rows.forEach((row) => { - insertRowInTree(tree, row.id, row.path); + insertRowInTree({ + tree, + id: row.id, + path: row.path, + depth: 0, + defaultGroupingExpansionDepth: props.defaultGroupingExpansionDepth, + }); }); return { @@ -102,7 +125,7 @@ export const useGridTreeData = ( }; return apiRef.current.registerRowGroupsBuilder(groupRows); - }, [apiRef, props.getTreeDataPath, props.treeData]); + }, [apiRef, props.getTreeDataPath, props.treeData, props.defaultGroupingExpansionDepth]); useFirstRender(() => { updateColumnsPreProcessing(); diff --git a/packages/grid/_modules_/grid/models/gridOptions.tsx b/packages/grid/_modules_/grid/models/gridOptions.tsx index a0fe29cee0049..cb8e94c8f73c5 100644 --- a/packages/grid/_modules_/grid/models/gridOptions.tsx +++ b/packages/grid/_modules_/grid/models/gridOptions.tsx @@ -207,6 +207,11 @@ export interface GridSimpleOptions { * @default false */ treeData: boolean; + /** + * If defined, the row children will be automatically expanded up to this depth + * @default 0 + */ + defaultGroupingExpansionDepth: number; /** * Set the area at the bottom of the grid viewport where onRowsScrollEnd is called. */ @@ -279,6 +284,7 @@ export const GRID_DEFAULT_SIMPLE_OPTIONS: GridSimpleOptions = { rowHeight: 52, rowsPerPageOptions: [25, 50, 100], treeData: false, + defaultGroupingExpansionDepth: 0, scrollEndThreshold: 80, showCellRightBorder: false, showColumnRightBorder: false, diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index 1cea888b86d2d..12aaec106ed7f 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -121,6 +121,46 @@ describe(' - Tree Data', () => { }); }); + describe('prop: defaultGroupingExpansionDepth', () => { + it('should not expand any row if defaultGroupingExpansionDepth = 0', () => { + render(); + expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); + }); + + it('should expand all top level rows if defaultGroupingExpansionDepth = 1', () => { + render(); + expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'B.A', 'B.B', 'C']); + }); + + it('should expand all rows up to depth of 2 if defaultGroupingExpansionDepth = 2', () => { + render(); + expect(getColumnValues(1)).to.deep.equal([ + 'A', + 'A.A', + 'A.B', + 'B', + 'B.A', + 'B.B', + 'B.B.A', + 'C', + ]); + }); + + it('should not re-apply default expansion on rerender after expansion manually toggled', () => { + const { setProps } = render(); + expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'B.A', 'B.B', 'C']); + setProps({ sortModel: [{ field: 'name', sort: 'desc' }] }); + expect(getColumnValues(1)).to.deep.equal(['C', 'B', 'B.B', 'B.A', 'A', 'A.B', 'A.A']); + }); + }); + + describe('prop: groupingColDef', () => { + it('should set the custom headerName', () => { + render(); + expect(getColumnHeadersTextContent()).to.deep.equal(['Custom header name', 'name']); + }); + }); + describe('grouping column', () => { it('should add a grouping column', () => { render(); diff --git a/packages/storybook/src/stories/grid-tree-data.stories.tsx b/packages/storybook/src/stories/grid-tree-data.stories.tsx index b863c208753b4..fb5a3af19cf1a 100644 --- a/packages/storybook/src/stories/grid-tree-data.stories.tsx +++ b/packages/storybook/src/stories/grid-tree-data.stories.tsx @@ -44,6 +44,7 @@ export function CustomGroupingColumn() { return ( ; + return ; } export function TreeDataPagination() { @@ -64,6 +65,7 @@ export function TreeDataPagination() {
); } + +export function TreeDataAutoExpand() { + const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + + return ( + + ); +} From a63be94c5b0b19f0ee0d01226e7f9e1cf2c4f2be Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 7 Oct 2021 10:10:05 +0200 Subject: [PATCH 146/390] New demo for custom group col --- .../CustomGroupingColumnTreeData.js | 64 +++++++++++++++++++ .../CustomGroupingColumnTreeData.tsx | 61 ++++++++++++++++++ .../data-grid/group-pivot/group-pivot.md | 8 +++ .../cell/GridTreeDataGroupingCell.tsx | 3 +- 4 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js create mode 100644 docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js new file mode 100644 index 0000000000000..c805a5857e6ae --- /dev/null +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js @@ -0,0 +1,64 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { DataGridPro, useGridSlotComponentProps } from '@mui/x-data-grid-pro'; +import { useDemoTreeData } from '@mui/x-data-grid-generator'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; + +const CustomGridTreeDataGroupingCell = (props) => { + const { id } = props; + + const { apiRef } = useGridSlotComponentProps(); + const node = apiRef.current.getRowNode(id); + + const path = apiRef.current.getRowPath(id); + + if (!node || !path) { + throw new Error(`MUI: No row with id #${id} found`); + } + + return ( + +
+ {node.children?.size ? ( + + ) : ( + + )} +
+
+ ); +}; + +CustomGridTreeDataGroupingCell.propTypes = { + /** + * The grid row id. + */ + id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, +}; + +const groupingColDef = { + renderCell: (params) => , +}; + +export default function CustomGroupingColumnTreeData() { + const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + + return ( +
+ +
+ ); +} diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx new file mode 100644 index 0000000000000..9376092e60dc1 --- /dev/null +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx @@ -0,0 +1,61 @@ +import * as React from 'react'; +import { + DataGridPro, + DataGridProProps, + GridRenderCellParams, + useGridSlotComponentProps, +} from '@mui/x-data-grid-pro'; +import { useDemoTreeData } from '@mui/x-data-grid-generator'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; + +const CustomGridTreeDataGroupingCell = (props: GridRenderCellParams) => { + const { id } = props; + + const { apiRef } = useGridSlotComponentProps(); + const node = apiRef.current.getRowNode(id); + + const path = apiRef.current.getRowPath(id); + + if (!node || !path) { + throw new Error(`MUI: No row with id #${id} found`); + } + + return ( + +
+ {node.children?.size ? ( + + ) : ( + + )} +
+
+ ); +}; + +const groupingColDef: DataGridProProps['groupingColDef'] = { + renderCell: (params) => , +}; + +export default function CustomGroupingColumnTreeData() { + const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + + return ( +
+ +
+ ); +} diff --git a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md index 1692a6ae4b43a..b30724159aa27 100644 --- a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md +++ b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md @@ -11,12 +11,20 @@ title: Data Grid - Group & Pivot Tree data allows to display data with parent / child relationships. To enable the Tree Data, you must use the `treeData` prop as well as provide a `getTreeDataPath` prop. +### Basic example + ```jsx row.path} /> ``` {{"demo": "pages/components/data-grid/group-pivot/BasicTreeData.js", "bg": "inline", "defaultCodeOpen": false}} +### Custom grouping column + +Use the `groupingColDef` prop to customize the rendering of the grouping column. + +{{"demo": "pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js", "bg": "inline", "defaultCodeOpen": false}} + ## 🚧 Master detail [](https://mui.com/store/items/material-ui-pro/) > ⚠️ This feature isn't implemented yet. It's coming. diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index 112fa8352ead6..a8a12e5c7adf3 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { makeStyles } from '@mui/styles'; import IconButton from '@mui/material/IconButton'; -import Typography from '@mui/material/Typography'; import Box from '@mui/material/Box'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { useGridApiContext } from '../../hooks/root/useGridApiContext'; @@ -57,7 +56,7 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { )}
- {path[path.length - 1]} + {path[path.length - 1]} ); }; From 9dffc4eaf6bd3d563810adc91d399f136915061f Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 7 Oct 2021 11:02:34 +0200 Subject: [PATCH 147/390] Basic handling of filler rows --- .../grid/hooks/features/rows/gridRowsState.ts | 4 +- .../grid/hooks/features/rows/useGridRows.ts | 11 +- .../features/treeData/useGridTreeData.ts | 120 ++++++++++-------- .../gridRowGroupsPreProcessingApi.ts | 10 +- .../useGridRowGroupsPreProcessing.ts | 1 + .../grid/_modules_/grid/models/gridRows.ts | 10 +- .../src/tests/treeData.DataGridPro.test.tsx | 67 +++++++--- 7 files changed, 138 insertions(+), 85 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts index c58029b8db904..fc8bcafec3169 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts @@ -1,7 +1,7 @@ -import { GridRowId, GridRowModel, GridRowConfigTree } from '../../../models/gridRows'; +import { GridRowId, GridRowConfigTree, GridRowsLookup } from '../../../models/gridRows'; export interface GridRowsState { - idRowsLookup: Record; + idRowsLookup: GridRowsLookup; tree: GridRowConfigTree; /** diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 2e2db1403d0b5..8d7bad7104740 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -148,18 +148,19 @@ const getRowsStateFromCache = ( rowsCache: GridRowsInternalCache, apiRef: GridApiRef, ): GridRowsState => { - const { rowIds, propRowCount = 0, propGetRowId, ...rowState } = rowsCache.state; + const { rowIds, idRowsLookup, propRowCount = 0, propGetRowId, ...rowState } = rowsCache.state; - const { tree, paths } = apiRef.current.groupRows({ - lookup: rowState.idRowsLookup, + const groupingResponse = apiRef.current.groupRows({ + idRowsLookup, ids: rowIds, gridRowId: (rowData: GridRowData) => getGridRowId(rowData, propGetRowId), }); const totalRowCount = propRowCount > rowIds.length ? propRowCount : rowIds.length; - const totalTopLevelRowCount = propRowCount > tree.size ? propRowCount : tree.size; + const totalTopLevelRowCount = + propRowCount > groupingResponse.tree.size ? propRowCount : groupingResponse.tree.size; - return { ...rowState, tree, paths, totalRowCount, totalTopLevelRowCount }; + return { ...rowState, ...groupingResponse, totalRowCount, totalTopLevelRowCount }; }; // The cache is always redefined synchronously in `useGridStateInit` so this object don't need to be regenerated across DataGrid instances. diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 785f9ed01b47a..9f7b6fd0c18ab 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -1,5 +1,5 @@ import * as React from 'react'; -import { GridRowId, GridRowConfigTree } from '../../../models/gridRows'; +import { GridRowId, GridRowConfigTree, GridRowsLookup } from '../../../models/gridRows'; import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridComponentProps } from '../../../GridComponentProps'; import { GridColumnsPreProcessing } from '../../root/columnsPreProcessing'; @@ -11,51 +11,6 @@ import { isSpaceKey } from '../../../utils/keyboardUtils'; import { useFirstRender } from '../../utils/useFirstRender'; import { RowGroupingFunction } from '../../root/rowGroupsPerProcessing'; -const insertRowInTree = ({ - tree, - id, - path, - depth, - defaultGroupingExpansionDepth, -}: { - tree: GridRowConfigTree; - id: GridRowId; - path: string[]; - depth: number; - defaultGroupingExpansionDepth: number; -}) => { - if (path.length === 0) { - throw new Error(`MUI: Could not insert row #${id} in the tree structure.`); - } - - if (path.length === 1) { - tree.set(path[0], { - id, - depth, - expanded: defaultGroupingExpansionDepth > depth, - }); - } else { - const [nodeName, ...restPath] = path; - - const parent = tree.get(nodeName); - if (!parent) { - throw new Error(`MUI: Could not insert row #${id} in the tree structure.`); - } - - if (!parent.children) { - parent.children = new Map(); - } - - insertRowInTree({ - tree: parent.children, - id, - path: restPath, - depth: depth + 1, - defaultGroupingExpansionDepth, - }); - } -}; - /** * Only available in DataGridPro */ @@ -95,7 +50,7 @@ export const useGridTreeData = ( throw new Error('MUI: No getTreeDataPath given.'); } - const rows = Object.values(params.lookup) + const rows = Object.values(params.idRowsLookup) .map((row) => { const id = params.gridRowId(row); @@ -107,20 +62,81 @@ export const useGridTreeData = ( .sort((a, b) => a.path.length - b.path.length); const paths = Object.fromEntries(rows.map((row) => [row.id, row.path])); - const tree = new Map(); + const fullTree = new Map(); + const idRowsLookupFiller: GridRowsLookup = {}; + const fillerPaths: Record = {}; + + const insertRowInTree = ({ + tree, + id, + path, + originalPath, + depth, + }: { + tree: GridRowConfigTree; + id: GridRowId; + path: string[]; + originalPath: string[]; + depth: number; + }) => { + if (path.length === 0) { + throw new Error(`MUI: Could not insert row #${id} in the tree structure.`); + } + + if (path.length === 1) { + tree.set(path[0], { + id, + depth, + expanded: props.defaultGroupingExpansionDepth > depth, + }); + } else { + const [nodeName, ...restPath] = path; + + let parent = tree.get(nodeName); + if (!parent) { + const fillerPath = originalPath.slice(0, -1); + const fillerId = `filler-row-${fillerPath.join('-')}`; + parent = { + id: fillerId, + fillerNode: true, + depth, + expanded: props.defaultGroupingExpansionDepth > depth, + }; + + idRowsLookupFiller[fillerId] = {}; + fillerPaths[fillerId] = fillerPath; + + tree.set(nodeName, parent); + } + + if (!parent.children) { + parent.children = new Map(); + } + + insertRowInTree({ + tree: parent.children, + id, + originalPath, + path: restPath, + depth: depth + 1, + }); + } + }; + rows.forEach((row) => { insertRowInTree({ - tree, + tree: fullTree, id: row.id, path: row.path, + originalPath: row.path, depth: 0, - defaultGroupingExpansionDepth: props.defaultGroupingExpansionDepth, }); }); return { - tree, - paths, + tree: fullTree, + paths: { ...paths, ...fillerPaths }, + idRowsLookup: { ...params.idRowsLookup, ...idRowsLookupFiller }, }; }; diff --git a/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts index 6e4345922d02c..240ee8cf809aa 100644 --- a/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts @@ -1,16 +1,22 @@ import { + GridRowConfigTree, GridRowData, - GridRowGroupingResult, GridRowId, GridRowsLookup, } from '../../../models/gridRows'; export type RwoGroupParams = { - lookup: GridRowsLookup; ids: GridRowId[]; gridRowId: (rowData: GridRowData) => GridRowId; + idRowsLookup: GridRowsLookup; }; +export interface GridRowGroupingResult { + tree: GridRowConfigTree; + paths: Record; + idRowsLookup: GridRowsLookup; +} + export type RowGroupingFunction = (params: RwoGroupParams) => GridRowGroupingResult; export interface GridRowGroupsPreProcessingApi { diff --git a/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts index 0e08067b85f1d..3f9c09a337d78 100644 --- a/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts +++ b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts @@ -10,6 +10,7 @@ import { useGridApiMethod } from '../useGridApiMethod'; const getFlatRowTree: RowGroupingFunction = (params) => ({ tree: new Map(params.ids.map((id) => [id.toString(), { id, depth: 0 }])), paths: Object.fromEntries(params.ids.map((id) => [id, [id.toString()]])), + idRowsLookup: params.idRowsLookup, }); /** diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index 44f516076f9bf..c041d70bed1a8 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -17,15 +17,15 @@ export interface GridRowConfigTreeNode { children?: GridRowConfigTree; expanded?: boolean; depth: number; + + /** + * If `true`, this node has been automatically added to fill a gap in the tree structure + */ + fillerNode?: boolean; } export type GridRowConfigTree = Map; -export interface GridRowGroupingResult { - tree: GridRowConfigTree; - paths: Record; -} - export type GridRowsLookup = Record; /** diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index 12aaec106ed7f..3091cb3555953 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -8,24 +8,44 @@ import { import { getCell, getColumnHeadersTextContent, getColumnValues } from 'test/utils/helperFn'; import * as React from 'react'; import { expect } from 'chai'; -import { DataGridPro, DataGridProProps, GridApiRef, useGridApiRef } from '@mui/x-data-grid-pro'; +import { + DataGridPro, + DataGridProProps, + GridApiRef, + GridRowsProp, + useGridApiRef, +} from '@mui/x-data-grid-pro'; + +const rowsWithoutFiller: GridRowsProp = [ + { name: 'A', value: 10 }, + { name: 'A.A', value: 4 }, + { name: 'A.B', value: 6 }, + { name: 'B', value: 20 }, + { name: 'B.A', value: 12 }, + { name: 'B.B', value: 8 }, + { name: 'B.B.A', value: 8 }, + { name: 'C', value: 5 }, +]; + +const rowsWithFiller: GridRowsProp = [ + { name: 'A', value: 10 }, + { name: 'A.B', value: 6 }, + { name: 'A.A', value: 4 }, + { name: 'B.A', value: 3 }, + { name: 'B.B', value: 7 }, +]; const baselineProps: DataGridProProps = { - rows: [ - { name: 'A' }, - { name: 'A.A' }, - { name: 'A.B' }, - { name: 'B' }, - { name: 'B.A' }, - { name: 'B.B' }, - { name: 'B.B.A' }, - { name: 'C' }, - ], + rows: rowsWithoutFiller, columns: [ { field: 'name', width: 200, }, + { + field: 'value', + type: 'number', + }, ], treeData: true, getTreeDataPath: (row) => row.name.split('.'), @@ -51,7 +71,7 @@ describe(' - Tree Data', () => { describe('prop: treeData', () => { it('should support tree data toggling', () => { const { setProps } = render(); - expect(getColumnHeadersTextContent()).to.deep.equal(['name']); + expect(getColumnHeadersTextContent()).to.deep.equal(['name', 'value']); expect(getColumnValues(0)).to.deep.equal([ 'A', 'A.A', @@ -63,10 +83,10 @@ describe(' - Tree Data', () => { 'C', ]); setProps({ treeData: true }); - expect(getColumnHeadersTextContent()).to.deep.equal(['Group', 'name']); + expect(getColumnHeadersTextContent()).to.deep.equal(['Group', 'name', 'value']); expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); setProps({ treeData: false }); - expect(getColumnHeadersTextContent()).to.deep.equal(['name']); + expect(getColumnHeadersTextContent()).to.deep.equal(['name', 'value']); expect(getColumnValues(0)).to.deep.equal([ 'A', 'A.A', @@ -81,7 +101,7 @@ describe(' - Tree Data', () => { it('should support enabling treeData after apiRef.current.updateRows has modified the rows', async () => { const { setProps } = render(); - expect(getColumnHeadersTextContent()).to.deep.equal(['name']); + expect(getColumnHeadersTextContent()).to.deep.equal(['name', 'value']); expect(getColumnValues(0)).to.deep.equal([ 'A', 'A.A', @@ -95,7 +115,7 @@ describe(' - Tree Data', () => { apiRef.current.updateRows([{ name: 'A.A', _action: 'delete' }]); expect(getColumnValues(0)).to.deep.equal(['A', 'A.B', 'B', 'B.A', 'B.B', 'B.B.A', 'C']); setProps({ treeData: true }); - expect(getColumnHeadersTextContent()).to.deep.equal(['Group', 'name']); + expect(getColumnHeadersTextContent()).to.deep.equal(['Group', 'name', 'value']); expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); fireEvent.click(getCell(0, 0).querySelector('button')); expect(getColumnValues(1)).to.deep.equal(['A', 'A.B', 'B', 'C']); @@ -157,15 +177,15 @@ describe(' - Tree Data', () => { describe('prop: groupingColDef', () => { it('should set the custom headerName', () => { render(); - expect(getColumnHeadersTextContent()).to.deep.equal(['Custom header name', 'name']); + expect(getColumnHeadersTextContent()).to.deep.equal(['Custom header name', 'name', 'value']); }); }); - describe('grouping column', () => { + describe('row grouping', () => { it('should add a grouping column', () => { render(); const columnsHeader = getColumnHeadersTextContent(); - expect(columnsHeader).to.deep.equal(['Group', 'name']); + expect(columnsHeader).to.deep.equal(['Group', 'name', 'value']); }); it('should toggle expansion when clicking on grouping column icon', () => { @@ -187,6 +207,15 @@ describe(' - Tree Data', () => { fireEvent.keyDown(getCell(0, 0), { key: ' ' }); expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); }); + + it('should add filler rows if some parents do not exist', () => { + render(); + expect(getColumnValues(1)).to.deep.equal(['A', '']); + expect(getColumnValues(0)).to.deep.equal(['A', 'B']); + fireEvent.click(getCell(1, 0).querySelector('button')); + expect(getColumnValues(1)).to.deep.equal(['A', '', 'B.A', 'B.B']); + expect(getColumnValues(0)).to.deep.equal(['A', 'B', 'A', 'B']); + }); }); describe('pagination', () => { From 271e27e93e6fda99f481b9e6fcd032d9e11c2903 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 7 Oct 2021 11:05:23 +0200 Subject: [PATCH 148/390] Improve requirements --- packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts | 2 ++ .../_modules_/grid/hooks/features/treeData/useGridTreeData.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 8d7bad7104740..0d6d0e1a98a1a 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -177,6 +177,8 @@ const INITIAL_GRID_ROWS_INTERNAL_CACHE: GridRowsInternalCache = { }; /** + * @requires useGridColumnsPreProcessing (method) + * @requires useGridRowGroupsPreProcessing (method) * @requires useGridSorting (method) - can be after, async only (TODO: Remove after moving the 2 methods to useGridSorting) */ export const useGridRows = ( diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 9f7b6fd0c18ab..1dbadecefcba2 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -13,6 +13,8 @@ import { RowGroupingFunction } from '../../root/rowGroupsPerProcessing'; /** * Only available in DataGridPro + * @requires useGridColumnsPreProcessing (method) + * @requires useGridRowGroupsPreProcessing (method) */ export const useGridTreeData = ( apiRef: GridApiRef, From 92c016293b752b8f3f7ccfcce9ce33489da3e756 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 7 Oct 2021 11:31:16 +0200 Subject: [PATCH 149/390] Remove diff --- .../hooks/features/columnReorder/useGridColumnReorder.tsx | 2 +- packages/grid/_modules_/grid/hooks/features/core/index.ts | 2 +- .../{useGridControlStateManager.ts => useGridControlState.ts} | 2 +- .../_modules_/grid/hooks/features/filter/useGridFilter.ts | 2 +- .../_modules_/grid/hooks/features/pagination/useGridPage.ts | 2 +- .../grid/hooks/features/pagination/useGridPageSize.ts | 2 +- .../_modules_/grid/hooks/features/rows/useGridEditRows.ts | 2 +- .../grid/hooks/features/selection/useGridSelection.ts | 2 +- .../_modules_/grid/hooks/features/sorting/useGridSorting.ts | 2 +- packages/grid/data-grid/src/useDataGridComponent.tsx | 4 ++-- packages/grid/x-grid/src/useDataGridProComponent.tsx | 4 ++-- 11 files changed, 13 insertions(+), 13 deletions(-) rename packages/grid/_modules_/grid/hooks/features/core/{useGridControlStateManager.ts => useGridControlState.ts} (97%) diff --git a/packages/grid/_modules_/grid/hooks/features/columnReorder/useGridColumnReorder.tsx b/packages/grid/_modules_/grid/hooks/features/columnReorder/useGridColumnReorder.tsx index d46882771fe9f..beac18e79b12c 100644 --- a/packages/grid/_modules_/grid/hooks/features/columnReorder/useGridColumnReorder.tsx +++ b/packages/grid/_modules_/grid/hooks/features/columnReorder/useGridColumnReorder.tsx @@ -51,7 +51,7 @@ const useUtilityClasses = (ownerState: OwnerState) => { export const useGridColumnReorder = ( apiRef: GridApiRef, props: Pick, -) => { +): void => { const logger = useGridLogger(apiRef, 'useGridColumnReorder'); useGridStateInit(apiRef, (state) => ({ diff --git a/packages/grid/_modules_/grid/hooks/features/core/index.ts b/packages/grid/_modules_/grid/hooks/features/core/index.ts index 5eebc1ea7370e..c8da26260af26 100644 --- a/packages/grid/_modules_/grid/hooks/features/core/index.ts +++ b/packages/grid/_modules_/grid/hooks/features/core/index.ts @@ -1,5 +1,5 @@ export * from './gridState'; export * from './useGridApi'; -export * from './useGridControlStateManager'; +export * from './useGridControlState'; export * from './useGridSelector'; export * from './useGridState'; diff --git a/packages/grid/_modules_/grid/hooks/features/core/useGridControlStateManager.ts b/packages/grid/_modules_/grid/hooks/features/core/useGridControlState.ts similarity index 97% rename from packages/grid/_modules_/grid/hooks/features/core/useGridControlStateManager.ts rename to packages/grid/_modules_/grid/hooks/features/core/useGridControlState.ts index fe31f3f9bd0ca..61f534e64964b 100644 --- a/packages/grid/_modules_/grid/hooks/features/core/useGridControlStateManager.ts +++ b/packages/grid/_modules_/grid/hooks/features/core/useGridControlState.ts @@ -6,7 +6,7 @@ import { GridControlStateItem } from '../../../models/controlStateItem'; import { GridSignature } from '../../root/useGridApiEventHandler'; import { useGridApiMethod } from '../../root/useGridApiMethod'; -export function useGridControlStateManager(apiRef: GridApiRef, props: GridComponentProps) { +export function useGridControlState(apiRef: GridApiRef, props: GridComponentProps) { const controlStateMapRef = React.useRef>>({}); const updateControlState = React.useCallback( diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index a25a266c1931b..0ad80932ac378 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -39,7 +39,7 @@ const checkFilterModelValidity = (model: GridFilterModel) => { * @requires useGridColumns (state, method, event) * @requires useGridParamsApi (method) * @requires useGridRows (event) - * @requires useGridControlStateManager (method) + * @requires useGridControlState (method) */ export const useGridFilter = ( apiRef: GridApiRef, diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts index 8da1d1b795545..a52585dd6cda6 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts @@ -32,7 +32,7 @@ const applyValidPage = (paginationState: GridPaginationState): GridPaginationSta }; /** - * @requires useGridControlStateManager (method) + * @requires useGridControlState (method) * @requires useGridPageSize (state, event) * @requires useGridFilter (state) */ diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts index f463a5117e57b..f067cbd90e21d 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts @@ -11,7 +11,7 @@ import { useGridStateInit } from '../../utils/useGridStateInit'; import { gridPageSizeSelector } from './gridPaginationSelector'; /** - * @requires useGridControlStateManager (method) + * @requires useGridControlState (method) * @requires useGridContainerProps (state) * @requires useGridFilter (state) */ diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts index d5d805cd3ae49..a258dc8090ddc 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts @@ -41,7 +41,7 @@ import { gridEditRowsStateSelector } from './gridEditRowsSelector'; * @requires useGridFocus - can be after, async only * @requires useGridParamsApi (method) * @requires useGridColumns (state) - * @requires useGridControlStateManager (method) + * @requires useGridControlState (method) */ export function useGridEditRows( apiRef: GridApiRef, diff --git a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts index 7d72bd483110a..759e8fdf907dd 100644 --- a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts +++ b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts @@ -24,7 +24,7 @@ import { useGridStateInit } from '../../utils/useGridStateInit'; /** * @requires useGridRows (state, method) * @requires useGridParamsApi (method) - * @requires useGridControlStateManager (method) + * @requires useGridControlState (method) */ export const useGridSelection = ( apiRef: GridApiRef, diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 370a68e5b9ace..7e121417b7109 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -32,7 +32,7 @@ import { useFirstRender } from '../../utils/useFirstRender'; /** * @requires useGridRows (state, event) - * @requires useGridControlStateManager (method) + * @requires useGridControlState (method) * @requires useGridColumns (event) */ export const useGridSorting = ( diff --git a/packages/grid/data-grid/src/useDataGridComponent.tsx b/packages/grid/data-grid/src/useDataGridComponent.tsx index 3e4b67d953b16..27be7daf48699 100644 --- a/packages/grid/data-grid/src/useDataGridComponent.tsx +++ b/packages/grid/data-grid/src/useDataGridComponent.tsx @@ -2,7 +2,7 @@ import { GridComponentProps } from '../../_modules_/grid/GridComponentProps'; import { useGridClipboard } from '../../_modules_/grid/hooks/features/clipboard/useGridClipboard'; import { useGridColumnMenu } from '../../_modules_/grid/hooks/features/columnMenu/useGridColumnMenu'; import { useGridColumns } from '../../_modules_/grid/hooks/features/columns/useGridColumns'; -import { useGridControlStateManager } from '../../_modules_/grid/hooks/features/core/useGridControlStateManager'; +import { useGridControlState } from '../../_modules_/grid/hooks/features/core/useGridControlState'; import { useGridDensity } from '../../_modules_/grid/hooks/features/density/useGridDensity'; import { useGridCsvExport } from '../../_modules_/grid/hooks/features/export/useGridCsvExport'; import { useGridFilter } from '../../_modules_/grid/hooks/features/filter/useGridFilter'; @@ -36,7 +36,7 @@ export const useDataGridComponent = (apiRef: GridApiRef, props: GridComponentPro useGridLoggerFactory(apiRef, props); useApi(apiRef, props); useErrorHandler(apiRef, props); - useGridControlStateManager(apiRef, props); + useGridControlState(apiRef, props); useLocaleText(apiRef, props); useGridResizeContainer(apiRef, props); useGridFreezeRows(apiRef, props); diff --git a/packages/grid/x-grid/src/useDataGridProComponent.tsx b/packages/grid/x-grid/src/useDataGridProComponent.tsx index ac9c6f1c0d09e..45c41592f32ab 100644 --- a/packages/grid/x-grid/src/useDataGridProComponent.tsx +++ b/packages/grid/x-grid/src/useDataGridProComponent.tsx @@ -4,7 +4,7 @@ import { useGridColumnMenu } from '../../_modules_/grid/hooks/features/columnMen import { useGridColumnReorder } from '../../_modules_/grid/hooks/features/columnReorder/useGridColumnReorder'; import { useGridColumnResize } from '../../_modules_/grid/hooks/features/columnResize/useGridColumnResize'; import { useGridColumns } from '../../_modules_/grid/hooks/features/columns/useGridColumns'; -import { useGridControlStateManager } from '../../_modules_/grid/hooks/features/core/useGridControlStateManager'; +import { useGridControlState } from '../../_modules_/grid/hooks/features/core/useGridControlState'; import { useGridDensity } from '../../_modules_/grid/hooks/features/density/useGridDensity'; import { useGridCsvExport } from '../../_modules_/grid/hooks/features/export/useGridCsvExport'; import { useGridFilter } from '../../_modules_/grid/hooks/features/filter/useGridFilter'; @@ -39,7 +39,7 @@ export const useDataGridProComponent = (apiRef: GridApiRef, props: GridComponent useGridLoggerFactory(apiRef, props); useApi(apiRef, props); useErrorHandler(apiRef, props); - useGridControlStateManager(apiRef, props); + useGridControlState(apiRef, props); useLocaleText(apiRef, props); useGridResizeContainer(apiRef, props); useGridFreezeRows(apiRef, props); From 3562f2df9a91cda07794034a790a83986dbddc0f Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 7 Oct 2021 13:54:43 +0200 Subject: [PATCH 150/390] [core] Generate list of all exports --- docs/scripts/buildApi.ts | 12 +- scripts/exportsSnapshot.json | 439 +++++++++++++++++++++++++++++++++++ 2 files changed, 449 insertions(+), 2 deletions(-) create mode 100644 scripts/exportsSnapshot.json diff --git a/docs/scripts/buildApi.ts b/docs/scripts/buildApi.ts index c5cac6a177912..f76484add387d 100644 --- a/docs/scripts/buildApi.ts +++ b/docs/scripts/buildApi.ts @@ -255,7 +255,15 @@ function run(argv: { outputDirectory?: string }) { exclude: ['**/*.test.ts'], tsconfig: 'packages/grid/data-grid/tsconfig.json', }); - const project = app.convert(); + const project = app.convert()!; + + const exports = (project.children ?? []).map(child => ({ name: child.name , kind: child?.kindString})) + + writePrettifiedFile( + path.resolve(workspaceRoot, 'scripts/exportsSnapshot.json'), + JSON.stringify(exports), + prettierConfigPath, + ); const apisToGenerate = [ 'GridApi', @@ -272,7 +280,7 @@ function run(argv: { outputDirectory?: string }) { ]; apisToGenerate.forEach((apiName) => { - const reflection = project!.findReflectionByName(apiName) as TypeDoc.DeclarationReflection; + const reflection = project.findReflectionByName(apiName) as TypeDoc.DeclarationReflection; if (!reflection) { throw new Error(`Could not find reflection for "${apiName}".`); } diff --git a/scripts/exportsSnapshot.json b/scripts/exportsSnapshot.json new file mode 100644 index 0000000000000..e59713ca81a6e --- /dev/null +++ b/scripts/exportsSnapshot.json @@ -0,0 +1,439 @@ +[ + { "name": "GridActionsCell", "kind": "Namespace" }, + { "name": "GridActionsCellItem", "kind": "Namespace" }, + { "name": "GridBody", "kind": "Namespace" }, + { "name": "GridColumnHeaderItem", "kind": "Namespace" }, + { "name": "GridColumnHeaderMenu", "kind": "Namespace" }, + { "name": "GridColumnHeaderTitle", "kind": "Namespace" }, + { "name": "GridColumnHeadersItemCollection", "kind": "Namespace" }, + { "name": "GridColumnsMenuItem", "kind": "Namespace" }, + { "name": "GridEditInputCell", "kind": "Namespace" }, + { "name": "GridEditSingleSelectCell", "kind": "Namespace" }, + { "name": "GridFilterForm", "kind": "Namespace" }, + { "name": "GridFilterInputValue", "kind": "Namespace" }, + { "name": "GridFilterMenuItem", "kind": "Namespace" }, + { "name": "GridMenu", "kind": "Namespace" }, + { "name": "GridRow", "kind": "Namespace" }, + { "name": "GridRowCells", "kind": "Namespace" }, + { "name": "GridStickyContainer", "kind": "Namespace" }, + { "name": "HideGridColMenuItem", "kind": "Namespace" }, + { "name": "SortGridMenuItems", "kind": "Namespace" }, + { "name": "GridCellModes", "kind": "Enumeration" }, + { "name": "GridDensityTypes", "kind": "Enumeration" }, + { "name": "GridEditModes", "kind": "Enumeration" }, + { "name": "GridEvents", "kind": "Enumeration" }, + { "name": "GridLinkOperator", "kind": "Enumeration" }, + { "name": "GridPreferencePanelsValue", "kind": "Enumeration" }, + { "name": "GridRowModes", "kind": "Enumeration" }, + { "name": "AutoSizerProps", "kind": "Interface" }, + { "name": "AutoSizerSize", "kind": "Interface" }, + { "name": "CursorCoordinates", "kind": "Interface" }, + { "name": "ElementSize", "kind": "Interface" }, + { "name": "GridActionsColDef", "kind": "Interface" }, + { "name": "GridApi", "kind": "Interface" }, + { "name": "GridCallbackDetails", "kind": "Interface" }, + { "name": "GridCellEditCommitParams", "kind": "Interface" }, + { "name": "GridCellIndexCoordinates", "kind": "Interface" }, + { "name": "GridCellParams", "kind": "Interface" }, + { "name": "GridCellProps", "kind": "Interface" }, + { "name": "GridClasses", "kind": "Interface" }, + { "name": "GridClipboardApi", "kind": "Interface" }, + { "name": "GridColDef", "kind": "Interface" }, + { "name": "GridColumnApi", "kind": "Interface" }, + { "name": "GridColumnHeaderIndexCoordinates", "kind": "Interface" }, + { "name": "GridColumnHeaderMenuProps", "kind": "Interface" }, + { "name": "GridColumnHeaderParams", "kind": "Interface" }, + { "name": "GridColumnHeaderSeparatorProps", "kind": "Interface" }, + { "name": "GridColumnHeaderSortIconProps", "kind": "Interface" }, + { "name": "GridColumnHeaderTitleProps", "kind": "Interface" }, + { "name": "GridColumnHeadersItemCollectionProps", "kind": "Interface" }, + { "name": "GridColumnMenuApi", "kind": "Interface" }, + { "name": "GridColumnMenuProps", "kind": "Interface" }, + { "name": "GridColumnMenuState", "kind": "Interface" }, + { "name": "GridColumnOrderChangeParams", "kind": "Interface" }, + { "name": "GridColumnReorderState", "kind": "Interface" }, + { "name": "GridColumnResizeParams", "kind": "Interface" }, + { "name": "GridColumnResizeState", "kind": "Interface" }, + { "name": "GridColumnVisibilityChangeParams", "kind": "Interface" }, + { "name": "GridColumnsMeta", "kind": "Interface" }, + { "name": "GridColumnsState", "kind": "Interface" }, + { "name": "GridCommitCellChangeParams", "kind": "Interface" }, + { "name": "GridComponentProps", "kind": "Interface" }, + { "name": "GridContainerProps", "kind": "Interface" }, + { "name": "GridControlStateApi", "kind": "Interface" }, + { "name": "GridCoreApi", "kind": "Interface" }, + { "name": "GridCsvExportApi", "kind": "Interface" }, + { "name": "GridDensityApi", "kind": "Interface" }, + { "name": "GridDensityOption", "kind": "Interface" }, + { "name": "GridEditCellProps", "kind": "Interface" }, + { "name": "GridEditCellPropsParams", "kind": "Interface" }, + { "name": "GridEditCellValueParams", "kind": "Interface" }, + { "name": "GridEditRowApi", "kind": "Interface" }, + { "name": "GridEmptyCellProps", "kind": "Interface" }, + { "name": "GridEventsApi", "kind": "Interface" }, + { "name": "GridExportCsvOptions", "kind": "Interface" }, + { "name": "GridFilterApi", "kind": "Interface" }, + { "name": "GridFilterFormProps", "kind": "Interface" }, + { "name": "GridFilterInputValueProps", "kind": "Interface" }, + { "name": "GridFilterItem", "kind": "Interface" }, + { "name": "GridFilterItemProps", "kind": "Interface" }, + { "name": "GridFilterModel", "kind": "Interface" }, + { "name": "GridFilterOperator", "kind": "Interface" }, + { "name": "GridFocusApi", "kind": "Interface" }, + { "name": "GridFocusState", "kind": "Interface" }, + { "name": "GridIconSlotsComponent", "kind": "Interface" }, + { "name": "GridInputComponentProps", "kind": "Interface" }, + { "name": "GridLocaleText", "kind": "Interface" }, + { "name": "GridLocaleTextApi", "kind": "Interface" }, + { "name": "GridMenuProps", "kind": "Interface" }, + { "name": "GridPageApi", "kind": "Interface" }, + { "name": "GridPageSizeApi", "kind": "Interface" }, + { "name": "GridPanelClasses", "kind": "Interface" }, + { "name": "GridPanelProps", "kind": "Interface" }, + { "name": "GridParamsApi", "kind": "Interface" }, + { "name": "GridPreferencePanelState", "kind": "Interface" }, + { "name": "GridPreferencesPanelApi", "kind": "Interface" }, + { "name": "GridRenderCellParams", "kind": "Interface" }, + { "name": "GridRenderColumnsProps", "kind": "Interface" }, + { "name": "GridRenderEditCellParams", "kind": "Interface" }, + { "name": "GridRenderPaginationProps", "kind": "Interface" }, + { "name": "GridRenderRowProps", "kind": "Interface" }, + { "name": "GridRenderingState", "kind": "Interface" }, + { "name": "GridRowApi", "kind": "Interface" }, + { "name": "GridRowModelUpdate", "kind": "Interface" }, + { "name": "GridRowParams", "kind": "Interface" }, + { "name": "GridRowProps", "kind": "Interface" }, + { "name": "GridRowScrollEndParams", "kind": "Interface" }, + { "name": "GridRowSelectionCheckboxParams", "kind": "Interface" }, + { "name": "GridRowsInternalCache", "kind": "Interface" }, + { "name": "GridRowsState", "kind": "Interface" }, + { "name": "GridScrollApi", "kind": "Interface" }, + { "name": "GridScrollBarState", "kind": "Interface" }, + { "name": "GridScrollParams", "kind": "Interface" }, + { "name": "GridSelectionApi", "kind": "Interface" }, + { "name": "GridSlotComponentProps", "kind": "Interface" }, + { "name": "GridSlotsComponent", "kind": "Interface" }, + { "name": "GridSlotsComponentsProps", "kind": "Interface" }, + { "name": "GridSortApi", "kind": "Interface" }, + { "name": "GridSortCellParams", "kind": "Interface" }, + { "name": "GridSortItem", "kind": "Interface" }, + { "name": "GridSortModelParams", "kind": "Interface" }, + { "name": "GridSortingState", "kind": "Interface" }, + { "name": "GridState", "kind": "Interface" }, + { "name": "GridStateApi", "kind": "Interface" }, + { "name": "GridStateChangeParams", "kind": "Interface" }, + { "name": "GridTabIndexState", "kind": "Interface" }, + { "name": "GridToolbarExportProps", "kind": "Interface" }, + { "name": "GridToolbarFilterButtonProps", "kind": "Interface" }, + { "name": "GridTypeFilterInputValueProps", "kind": "Interface" }, + { "name": "GridValueFormatterParams", "kind": "Interface" }, + { "name": "GridViewportRowsChangeParams", "kind": "Interface" }, + { "name": "GridVirtualizationApi", "kind": "Interface" }, + { "name": "GridWindowProps", "kind": "Interface" }, + { "name": "Logger", "kind": "Interface" }, + { "name": "VisibleGridRowsState", "kind": "Interface" }, + { "name": "DataGridProps", "kind": "Type alias" }, + { "name": "FilterColumnLookup", "kind": "Type alias" }, + { "name": "GridActionsCellItemProps", "kind": "Type alias" }, + { "name": "GridAlignment", "kind": "Type alias" }, + { "name": "GridApiRef", "kind": "Type alias" }, + { "name": "GridCellClassFn", "kind": "Type alias" }, + { "name": "GridCellClassNamePropType", "kind": "Type alias" }, + { "name": "GridCellIdentifier", "kind": "Type alias" }, + { "name": "GridCellMode", "kind": "Type alias" }, + { "name": "GridCellValue", "kind": "Type alias" }, + { "name": "GridClassKey", "kind": "Type alias" }, + { "name": "GridColType", "kind": "Type alias" }, + { "name": "GridColTypeDef", "kind": "Type alias" }, + { "name": "GridColumnHeaderClassFn", "kind": "Type alias" }, + { "name": "GridColumnHeaderClassNamePropType", "kind": "Type alias" }, + { "name": "GridColumnIdentifier", "kind": "Type alias" }, + { "name": "GridColumnLookup", "kind": "Type alias" }, + { "name": "GridColumnTypesRecord", "kind": "Type alias" }, + { "name": "GridColumns", "kind": "Type alias" }, + { "name": "GridComparatorFn", "kind": "Type alias" }, + { "name": "GridDensity", "kind": "Type alias" }, + { "name": "GridEditMode", "kind": "Type alias" }, + { "name": "GridEditRowProps", "kind": "Type alias" }, + { "name": "GridEditRowsModel", "kind": "Type alias" }, + { "name": "GridEnrichedColDef", "kind": "Type alias" }, + { "name": "GridExportCsvDelimiter", "kind": "Type alias" }, + { "name": "GridExportFormat", "kind": "Type alias" }, + { "name": "GridFeatureMode", "kind": "Type alias" }, + { "name": "GridFieldComparatorList", "kind": "Type alias" }, + { "name": "GridFooterContainerProps", "kind": "Type alias" }, + { "name": "GridInputSelectionModel", "kind": "Type alias" }, + { "name": "GridNativeColTypes", "kind": "Type alias" }, + { "name": "GridOverlayProps", "kind": "Type alias" }, + { "name": "GridRenderContextProps", "kind": "Type alias" }, + { "name": "GridRootContainerRef", "kind": "Type alias" }, + { "name": "GridRootProps", "kind": "Type alias" }, + { "name": "GridRowData", "kind": "Type alias" }, + { "name": "GridRowId", "kind": "Type alias" }, + { "name": "GridRowIdGetter", "kind": "Type alias" }, + { "name": "GridRowMode", "kind": "Type alias" }, + { "name": "GridRowModel", "kind": "Type alias" }, + { "name": "GridRowsLookup", "kind": "Type alias" }, + { "name": "GridRowsProp", "kind": "Type alias" }, + { "name": "GridScrollFn", "kind": "Type alias" }, + { "name": "GridSelectionModel", "kind": "Type alias" }, + { "name": "GridSortColumnLookup", "kind": "Type alias" }, + { "name": "GridSortDirection", "kind": "Type alias" }, + { "name": "GridSortModel", "kind": "Type alias" }, + { "name": "GridStateColDef", "kind": "Type alias" }, + { "name": "GridToolbarContainerProps", "kind": "Type alias" }, + { "name": "GridTranslationKeys", "kind": "Type alias" }, + { "name": "GridUpdateAction", "kind": "Type alias" }, + { "name": "GridValueGetterParams", "kind": "Type alias" }, + { "name": "GridViewportSizeState", "kind": "Type alias" }, + { "name": "MuiEvent", "kind": "Type alias" }, + { "name": "DEFAULT_GRID_COL_TYPE_KEY", "kind": "Variable" }, + { "name": "DataGrid", "kind": "Variable" }, + { "name": "GRID_ACTIONS_COL_DEF", "kind": "Variable" }, + { "name": "GRID_BOOLEAN_COLUMN_TYPE", "kind": "Variable" }, + { "name": "GRID_CELL_CSS_CLASS", "kind": "Variable" }, + { "name": "GRID_CELL_CSS_CLASS_SUFFIX", "kind": "Variable" }, + { "name": "GRID_COLUMN_HEADER_CSS_CLASS", "kind": "Variable" }, + { "name": "GRID_COLUMN_HEADER_CSS_CLASS_SUFFIX", "kind": "Variable" }, + { "name": "GRID_COLUMN_HEADER_DRAGGING_CSS_CLASS", "kind": "Variable" }, + { "name": "GRID_COLUMN_HEADER_DROP_ZONE_CSS_CLASS", "kind": "Variable" }, + { "name": "GRID_COLUMN_HEADER_SEPARATOR_RESIZABLE_CSS_CLASS", "kind": "Variable" }, + { "name": "GRID_COLUMN_HEADER_TITLE_CSS_CLASS", "kind": "Variable" }, + { "name": "GRID_CSS_CLASS_PREFIX", "kind": "Variable" }, + { "name": "GRID_DATETIME_COLUMN_TYPE", "kind": "Variable" }, + { "name": "GRID_DATETIME_COL_DEF", "kind": "Variable" }, + { "name": "GRID_DATE_COLUMN_TYPE", "kind": "Variable" }, + { "name": "GRID_DATE_COL_DEF", "kind": "Variable" }, + { "name": "GRID_DEFAULT_LOCALE_TEXT", "kind": "Variable" }, + { "name": "GRID_EXPERIMENTAL_ENABLED", "kind": "Variable" }, + { "name": "GRID_NUMBER_COLUMN_TYPE", "kind": "Variable" }, + { "name": "GRID_NUMERIC_COL_DEF", "kind": "Variable" }, + { "name": "GRID_ROOT_CSS_CLASS_SUFFIX", "kind": "Variable" }, + { "name": "GRID_ROW_CSS_CLASS", "kind": "Variable" }, + { "name": "GRID_ROW_CSS_CLASS_SUFFIX", "kind": "Variable" }, + { "name": "GRID_SINGLE_SELECT_COL_DEF", "kind": "Variable" }, + { "name": "GRID_STRING_COLUMN_TYPE", "kind": "Variable" }, + { "name": "GRID_STRING_COL_DEF", "kind": "Variable" }, + { "name": "GridActionsCell", "kind": "Variable" }, + { "name": "GridActionsCellItem", "kind": "Variable" }, + { "name": "GridAddIcon", "kind": "Variable" }, + { "name": "GridApiContext", "kind": "Variable" }, + { "name": "GridArrowDownwardIcon", "kind": "Variable" }, + { "name": "GridArrowUpwardIcon", "kind": "Variable" }, + { "name": "GridAutoSizer", "kind": "Variable" }, + { "name": "GridCell", "kind": "Variable" }, + { "name": "GridCellCheckboxForwardRef", "kind": "Variable" }, + { "name": "GridCellCheckboxRenderer", "kind": "Variable" }, + { "name": "GridCheckCircleIcon", "kind": "Variable" }, + { "name": "GridCheckIcon", "kind": "Variable" }, + { "name": "GridCloseIcon", "kind": "Variable" }, + { "name": "GridColumnHeaderSeparator", "kind": "Variable" }, + { "name": "GridColumnHeaderSortIcon", "kind": "Variable" }, + { "name": "GridColumnIcon", "kind": "Variable" }, + { "name": "GridColumnMenu", "kind": "Variable" }, + { "name": "GridColumnMenuContainer", "kind": "Variable" }, + { "name": "GridColumnsContainer", "kind": "Variable" }, + { "name": "GridColumnsHeader", "kind": "Variable" }, + { "name": "GridColumnsMenuItem", "kind": "Variable" }, + { "name": "GridDragIcon", "kind": "Variable" }, + { "name": "GridEmptyCell", "kind": "Variable" }, + { "name": "GridFeatureModeConstant", "kind": "Variable" }, + { "name": "GridFilterAltIcon", "kind": "Variable" }, + { "name": "GridFilterListIcon", "kind": "Variable" }, + { "name": "GridFilterMenuItem", "kind": "Variable" }, + { "name": "GridFooter", "kind": "Variable" }, + { "name": "GridFooterContainer", "kind": "Variable" }, + { "name": "GridHeader", "kind": "Variable" }, + { "name": "GridHeaderCheckbox", "kind": "Variable" }, + { "name": "GridLoadIcon", "kind": "Variable" }, + { "name": "GridLoadingOverlay", "kind": "Variable" }, + { "name": "GridMenu", "kind": "Variable" }, + { "name": "GridMenuIcon", "kind": "Variable" }, + { "name": "GridMoreVertIcon", "kind": "Variable" }, + { "name": "GridNoRowsOverlay", "kind": "Variable" }, + { "name": "GridOverlay", "kind": "Variable" }, + { "name": "GridPagination", "kind": "Variable" }, + { "name": "GridPanel", "kind": "Variable" }, + { "name": "GridPreferencesPanel", "kind": "Variable" }, + { "name": "GridRenderingZone", "kind": "Variable" }, + { "name": "GridRoot", "kind": "Variable" }, + { "name": "GridRowCount", "kind": "Variable" }, + { "name": "GridSaveAltIcon", "kind": "Variable" }, + { "name": "GridScrollArea", "kind": "Variable" }, + { "name": "GridSearchIcon", "kind": "Variable" }, + { "name": "GridSelectedRowCount", "kind": "Variable" }, + { "name": "GridSeparatorIcon", "kind": "Variable" }, + { "name": "GridTableRowsIcon", "kind": "Variable" }, + { "name": "GridToolbarContainer", "kind": "Variable" }, + { "name": "GridToolbarExport", "kind": "Variable" }, + { "name": "GridToolbarFilterButton", "kind": "Variable" }, + { "name": "GridTripleDotsVerticalIcon", "kind": "Variable" }, + { "name": "GridViewHeadlineIcon", "kind": "Variable" }, + { "name": "GridViewStreamIcon", "kind": "Variable" }, + { "name": "GridViewport", "kind": "Variable" }, + { "name": "GridWindow", "kind": "Variable" }, + { "name": "HideGridColMenuItem", "kind": "Variable" }, + { "name": "MAX_PAGE_SIZE", "kind": "Variable" }, + { "name": "SUBMIT_FILTER_STROKE_TIME", "kind": "Variable" }, + { "name": "SortGridMenuItems", "kind": "Variable" }, + { "name": "activeGridFilterItemsSelector", "kind": "Variable" }, + { "name": "allGridColumnsSelector", "kind": "Variable" }, + { "name": "arSD", "kind": "Variable" }, + { "name": "bgBG", "kind": "Variable" }, + { "name": "csCZ", "kind": "Variable" }, + { "name": "deDE", "kind": "Variable" }, + { "name": "elGR", "kind": "Variable" }, + { "name": "enUS", "kind": "Variable" }, + { "name": "esES", "kind": "Variable" }, + { "name": "faIR", "kind": "Variable" }, + { "name": "filterGridColumnLookupSelector", "kind": "Variable" }, + { "name": "filterGridItemsCounterSelector", "kind": "Variable" }, + { "name": "filterableGridColumnsIdsSelector", "kind": "Variable" }, + { "name": "filterableGridColumnsSelector", "kind": "Variable" }, + { "name": "frFR", "kind": "Variable" }, + { "name": "gridCheckboxSelectionColDef", "kind": "Variable" }, + { "name": "gridClasses", "kind": "Variable" }, + { "name": "gridColumnReorderDragColSelector", "kind": "Variable" }, + { "name": "gridColumnsMetaSelector", "kind": "Variable" }, + { "name": "gridColumnsTotalWidthSelector", "kind": "Variable" }, + { "name": "gridFocusCellSelector", "kind": "Variable" }, + { "name": "gridFocusColumnHeaderSelector", "kind": "Variable" }, + { "name": "gridPaginatedVisibleSortedGridRowIdsSelector", "kind": "Variable" }, + { "name": "gridPanelClasses", "kind": "Variable" }, + { "name": "gridResizingColumnFieldSelector", "kind": "Variable" }, + { "name": "gridRowCountSelector", "kind": "Variable" }, + { "name": "gridRowsLookupSelector", "kind": "Variable" }, + { "name": "gridSortColumnLookupSelector", "kind": "Variable" }, + { "name": "gridSortModelSelector", "kind": "Variable" }, + { "name": "gridTabIndexCellSelector", "kind": "Variable" }, + { "name": "gridTabIndexColumnHeaderSelector", "kind": "Variable" }, + { "name": "itIT", "kind": "Variable" }, + { "name": "jaJP", "kind": "Variable" }, + { "name": "koKR", "kind": "Variable" }, + { "name": "nlNL", "kind": "Variable" }, + { "name": "plPL", "kind": "Variable" }, + { "name": "plPLGrid", "kind": "Variable" }, + { "name": "ptBR", "kind": "Variable" }, + { "name": "ruRU", "kind": "Variable" }, + { "name": "ruRUGrid", "kind": "Variable" }, + { "name": "selectedGridRowsCountSelector", "kind": "Variable" }, + { "name": "selectedGridRowsSelector", "kind": "Variable" }, + { "name": "selectedIdsLookupSelector", "kind": "Variable" }, + { "name": "skSK", "kind": "Variable" }, + { "name": "skSKGrid", "kind": "Variable" }, + { "name": "sortedGridRowIdsSelector", "kind": "Variable" }, + { "name": "sortedGridRowsSelector", "kind": "Variable" }, + { "name": "trTR", "kind": "Variable" }, + { "name": "ukUA", "kind": "Variable" }, + { "name": "ukUAGrid", "kind": "Variable" }, + { "name": "unorderedGridRowIdsSelector", "kind": "Variable" }, + { "name": "unorderedGridRowModelsSelector", "kind": "Variable" }, + { "name": "viVN", "kind": "Variable" }, + { "name": "visibleGridColumnsLengthSelector", "kind": "Variable" }, + { "name": "visibleGridColumnsSelector", "kind": "Variable" }, + { "name": "visibleGridRowCountSelector", "kind": "Variable" }, + { "name": "visibleSortedGridRowIdsSelector", "kind": "Variable" }, + { "name": "visibleSortedGridRowsAsArraySelector", "kind": "Variable" }, + { "name": "visibleSortedGridRowsSelector", "kind": "Variable" }, + { "name": "zhCN", "kind": "Variable" }, + { "name": "zhCNGrid", "kind": "Variable" }, + { "name": "GridBody", "kind": "Function" }, + { "name": "GridColumnHeaderItem", "kind": "Function" }, + { "name": "GridColumnHeaderMenu", "kind": "Function" }, + { "name": "GridColumnHeaderTitle", "kind": "Function" }, + { "name": "GridColumnHeadersItemCollection", "kind": "Function" }, + { "name": "GridColumnsPanel", "kind": "Function" }, + { "name": "GridDataContainer", "kind": "Function" }, + { "name": "GridEditInputCell", "kind": "Function" }, + { "name": "GridEditSingleSelectCell", "kind": "Function" }, + { "name": "GridErrorHandler", "kind": "Function" }, + { "name": "GridFilterForm", "kind": "Function" }, + { "name": "GridFilterInputValue", "kind": "Function" }, + { "name": "GridFilterPanel", "kind": "Function" }, + { "name": "GridFooterPlaceholder", "kind": "Function" }, + { "name": "GridHeaderPlaceholder", "kind": "Function" }, + { "name": "GridOverlays", "kind": "Function" }, + { "name": "GridPanelContent", "kind": "Function" }, + { "name": "GridPanelFooter", "kind": "Function" }, + { "name": "GridPanelHeader", "kind": "Function" }, + { "name": "GridPanelWrapper", "kind": "Function" }, + { "name": "GridRow", "kind": "Function" }, + { "name": "GridRowCells", "kind": "Function" }, + { "name": "GridStickyContainer", "kind": "Function" }, + { "name": "GridToolbar", "kind": "Function" }, + { "name": "GridToolbarColumnsButton", "kind": "Function" }, + { "name": "GridToolbarDensitySelector", "kind": "Function" }, + { "name": "allGridColumnsFieldsSelector", "kind": "Function" }, + { "name": "checkGridRowIdIsValid", "kind": "Function" }, + { "name": "convertGridRowsPropToState", "kind": "Function" }, + { "name": "getDataGridUtilityClass", "kind": "Function" }, + { "name": "getGridColDef", "kind": "Function" }, + { "name": "getGridDateOperators", "kind": "Function" }, + { "name": "getGridDefaultColumnTypes", "kind": "Function" }, + { "name": "getGridNumericColumnOperators", "kind": "Function" }, + { "name": "getGridSingleSelectOperators", "kind": "Function" }, + { "name": "getGridStringOperators", "kind": "Function" }, + { "name": "getInitialGridColumnReorderState", "kind": "Function" }, + { "name": "getInitialGridColumnResizeState", "kind": "Function" }, + { "name": "getInitialGridColumnsState", "kind": "Function" }, + { "name": "getInitialGridFilterState", "kind": "Function" }, + { "name": "getInitialGridRenderingState", "kind": "Function" }, + { "name": "getInitialGridRowState", "kind": "Function" }, + { "name": "getInitialGridSortingState", "kind": "Function" }, + { "name": "getInitialGridState", "kind": "Function" }, + { "name": "getInitialVisibleGridRowsState", "kind": "Function" }, + { "name": "gridColumnLookupSelector", "kind": "Function" }, + { "name": "gridColumnMenuSelector", "kind": "Function" }, + { "name": "gridColumnReorderSelector", "kind": "Function" }, + { "name": "gridColumnResizeSelector", "kind": "Function" }, + { "name": "gridColumnsSelector", "kind": "Function" }, + { "name": "gridDateFormatter", "kind": "Function" }, + { "name": "gridDateTimeFormatter", "kind": "Function" }, + { "name": "gridEditRowsStateSelector", "kind": "Function" }, + { "name": "gridFilterModelSelector", "kind": "Function" }, + { "name": "gridFocusStateSelector", "kind": "Function" }, + { "name": "gridPaginationSelector", "kind": "Function" }, + { "name": "gridPreferencePanelStateSelector", "kind": "Function" }, + { "name": "gridRowsStateSelector", "kind": "Function" }, + { "name": "gridScrollbarStateSelector", "kind": "Function" }, + { "name": "gridSelectionStateSelector", "kind": "Function" }, + { "name": "gridTabIndexStateSelector", "kind": "Function" }, + { "name": "gridViewportSizeStateSelector", "kind": "Function" }, + { "name": "renderActionsCell", "kind": "Function" }, + { "name": "renderEditInputCell", "kind": "Function" }, + { "name": "renderEditSingleSelectCell", "kind": "Function" }, + { "name": "useApi", "kind": "Function" }, + { "name": "useDataGridComponent", "kind": "Function" }, + { "name": "useGridApi", "kind": "Function" }, + { "name": "useGridApiMethod", "kind": "Function" }, + { "name": "useGridApiRef", "kind": "Function" }, + { "name": "useGridColumnMenu", "kind": "Function" }, + { "name": "useGridColumnReorder", "kind": "Function" }, + { "name": "useGridColumnResize", "kind": "Function" }, + { "name": "useGridColumns", "kind": "Function" }, + { "name": "useGridContainerProps", "kind": "Function" }, + { "name": "useGridControlState", "kind": "Function" }, + { "name": "useGridEditRows", "kind": "Function" }, + { "name": "useGridFilter", "kind": "Function" }, + { "name": "useGridFocus", "kind": "Function" }, + { "name": "useGridKeyboard", "kind": "Function" }, + { "name": "useGridKeyboardNavigation", "kind": "Function" }, + { "name": "useGridLogger", "kind": "Function" }, + { "name": "useGridLoggerFactory", "kind": "Function" }, + { "name": "useGridPage", "kind": "Function" }, + { "name": "useGridPageSize", "kind": "Function" }, + { "name": "useGridParamsApi", "kind": "Function" }, + { "name": "useGridPreferencesPanel", "kind": "Function" }, + { "name": "useGridRootProps", "kind": "Function" }, + { "name": "useGridRows", "kind": "Function" }, + { "name": "useGridScroll", "kind": "Function" }, + { "name": "useGridScrollFn", "kind": "Function" }, + { "name": "useGridSelection", "kind": "Function" }, + { "name": "useGridSelector", "kind": "Function" }, + { "name": "useGridSlotComponentProps", "kind": "Function" }, + { "name": "useGridSorting", "kind": "Function" }, + { "name": "useGridState", "kind": "Function" }, + { "name": "useGridVirtualization", "kind": "Function" }, + { "name": "useNativeEventListener", "kind": "Function" }, + { "name": "visibleGridRowsStateSelector", "kind": "Function" } +] From 93a30e9d03fca2df11f8a073d896fd0d577ce202 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 7 Oct 2021 14:14:51 +0200 Subject: [PATCH 151/390] Proptypes --- packages/grid/data-grid/src/DataGrid.tsx | 5 +++++ packages/grid/x-grid/src/DataGridPro.tsx | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/packages/grid/data-grid/src/DataGrid.tsx b/packages/grid/data-grid/src/DataGrid.tsx index c07c3b0d8692e..38cbb764ea5a7 100644 --- a/packages/grid/data-grid/src/DataGrid.tsx +++ b/packages/grid/data-grid/src/DataGrid.tsx @@ -106,6 +106,11 @@ DataGridRaw.propTypes = { * Overrideable components props dynamically passed to the component at rendering. */ componentsProps: PropTypes.object, + /** + * If defined, the row children will be automatically expanded up to this depth + * @default 0 + */ + defaultGroupingExpansionDepth: PropTypes.number, /** * Set the density of the grid. * @default "standard" diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index 98764e361d8f7..43ee47db02bdc 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -129,6 +129,11 @@ DataGridProRaw.propTypes = { * Overrideable components props dynamically passed to the component at rendering. */ componentsProps: PropTypes.object, + /** + * If defined, the row children will be automatically expanded up to this depth + * @default 0 + */ + defaultGroupingExpansionDepth: PropTypes.number, /** * Set the density of the grid. * @default "standard" From 44afeaa2cb9172eaf52de6f911e7c152df664702 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 7 Oct 2021 14:15:02 +0200 Subject: [PATCH 152/390] Prettier --- docs/scripts/buildApi.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/scripts/buildApi.ts b/docs/scripts/buildApi.ts index f76484add387d..3722370597bec 100644 --- a/docs/scripts/buildApi.ts +++ b/docs/scripts/buildApi.ts @@ -257,12 +257,15 @@ function run(argv: { outputDirectory?: string }) { }); const project = app.convert()!; - const exports = (project.children ?? []).map(child => ({ name: child.name , kind: child?.kindString})) + const exports = (project.children ?? []).map((child) => ({ + name: child.name, + kind: child?.kindString, + })); writePrettifiedFile( - path.resolve(workspaceRoot, 'scripts/exportsSnapshot.json'), - JSON.stringify(exports), - prettierConfigPath, + path.resolve(workspaceRoot, 'scripts/exportsSnapshot.json'), + JSON.stringify(exports), + prettierConfigPath, ); const apisToGenerate = [ From 59039afda668be7f12606efafbc92bd41c06c6ff Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 8 Oct 2021 11:50:51 +0200 Subject: [PATCH 153/390] Work before row grouping --- .../data-grid/scrolling/ScrollPlayground.tsx | 10 ++--- .../features/treeData/useGridTreeData.ts | 20 +++++----- .../gridColumnsPreProcessingApi.ts | 2 + .../gridRowGroupsPreProcessingApi.ts | 20 +++++----- .../useGridRowGroupsPreProcessing.ts | 38 ++++++++++++------- 5 files changed, 50 insertions(+), 40 deletions(-) diff --git a/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.tsx b/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.tsx index 82b90f94cca5d..5b9ecca30a018 100644 --- a/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.tsx +++ b/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.tsx @@ -7,10 +7,10 @@ import HomeIcon from '@mui/icons-material/Home'; import { DataGridPro, useGridApiRef, - visibleGridRowCountSelector, + gridVisibleRowCountSelector, visibleGridColumnsLengthSelector, visibleGridColumnsSelector, - visibleSortedGridRowIdsSelector, + gridSortedRowIdsFlatSelector, GridCellParams, } from '@mui/x-data-grid-pro'; import { useDemoData } from '@mui/x-data-grid-generator'; @@ -31,13 +31,13 @@ export default function ScrollPlayground() { React.useEffect(() => { const { rowIndex, colIndex } = coordinates; apiRef.current.scrollToIndexes(coordinates); - const id = visibleSortedGridRowIdsSelector(apiRef.current.state)[rowIndex]; + const id = gridSortedRowIdsFlatSelector(apiRef.current.state)[rowIndex]; const column = visibleGridColumnsSelector(apiRef.current.state)[colIndex]; apiRef.current.setCellFocus(id, column.field); }, [apiRef, coordinates]); const handleClick = (position: string) => () => { - const maxRowIndex = visibleGridRowCountSelector(apiRef.current.state) - 1; + const maxRowIndex = gridVisibleRowCountSelector(apiRef.current.state) - 1; const maxColIndex = visibleGridColumnsLengthSelector(apiRef.current.state) - 1; setCoordinates((coords) => { @@ -57,7 +57,7 @@ export default function ScrollPlayground() { }; const handleCellClick = (params: GridCellParams) => { - const rowIndex = visibleSortedGridRowIdsSelector(apiRef.current.state).findIndex( + const rowIndex = gridSortedRowIdsFlatSelector(apiRef.current.state).findIndex( (id) => id === params.id, ); const colIndex = visibleGridColumnsSelector(apiRef.current.state).findIndex( diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 1dbadecefcba2..a58f0ee753abb 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -9,7 +9,7 @@ import { GridEvents } from '../../../constants'; import { GridCellParams, GridColDef, MuiEvent } from '../../../models'; import { isSpaceKey } from '../../../utils/keyboardUtils'; import { useFirstRender } from '../../utils/useFirstRender'; -import { RowGroupingFunction } from '../../root/rowGroupsPerProcessing'; +import { GridRowGroupingPreProcessing } from '../../root/rowGroupsPerProcessing'; /** * Only available in DataGridPro @@ -44,27 +44,25 @@ export const useGridTreeData = ( const updateRowGrouping = React.useCallback(() => { if (!props.treeData) { - return apiRef.current.registerRowGroupsBuilder(null); + return apiRef.current.registerRowGroupsBuilder('treeData', null); } - const groupRows: RowGroupingFunction = (params) => { + const groupRows: GridRowGroupingPreProcessing = (params) => { if (!props.getTreeDataPath) { throw new Error('MUI: No getTreeDataPath given.'); } - const rows = Object.values(params.idRowsLookup) - .map((row) => { - const id = params.gridRowId(row); - + const rows = params.ids + .map((rowId) => { return { - id, - path: props.getTreeDataPath!(row), + id: rowId, + path: props.getTreeDataPath!(params.idRowsLookup[rowId]), }; }) .sort((a, b) => a.path.length - b.path.length); const paths = Object.fromEntries(rows.map((row) => [row.id, row.path])); - const fullTree = new Map(); + const fullTree: GridRowConfigTree = new Map(); const idRowsLookupFiller: GridRowsLookup = {}; const fillerPaths: Record = {}; @@ -142,7 +140,7 @@ export const useGridTreeData = ( }; }; - return apiRef.current.registerRowGroupsBuilder(groupRows); + return apiRef.current.registerRowGroupsBuilder('treeData', groupRows); }, [apiRef, props.getTreeDataPath, props.treeData, props.defaultGroupingExpansionDepth]); useFirstRender(() => { diff --git a/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/gridColumnsPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/gridColumnsPreProcessingApi.ts index f053c3c76d5e0..5bb8503864340 100644 --- a/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/gridColumnsPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/root/columnsPreProcessing/gridColumnsPreProcessingApi.ts @@ -4,6 +4,7 @@ export type GridColumnsPreProcessing = (columns: GridColumns) => GridColumns; export interface GridColumnsPreProcessingApi { /** + * Register a column pre-processing and emit an event to re-apply all the columns pre-processing * @param {string} processingName Name of the pre-processing. Used to clean the previous version of the pre-processing. * @param {GridColumnsPreProcessing | null } columnsPreProcessing Pre-processing to register. * @ignore - do not document @@ -13,6 +14,7 @@ export interface GridColumnsPreProcessingApi { columnsPreProcessing: GridColumnsPreProcessing | null, ) => void; /** + * Apply all the columns pre-processing * @param {GridColumns} columns. Columns to pre-process * @returns {GridColumns} The pre-processed columns * @ignore - do not document diff --git a/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts index 240ee8cf809aa..9042056bb08eb 100644 --- a/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts @@ -1,13 +1,7 @@ -import { - GridRowConfigTree, - GridRowData, - GridRowId, - GridRowsLookup, -} from '../../../models/gridRows'; +import { GridRowConfigTree, GridRowId, GridRowsLookup } from '../../../models/gridRows'; export type RwoGroupParams = { ids: GridRowId[]; - gridRowId: (rowData: GridRowData) => GridRowId; idRowsLookup: GridRowsLookup; }; @@ -17,16 +11,22 @@ export interface GridRowGroupingResult { idRowsLookup: GridRowsLookup; } -export type RowGroupingFunction = (params: RwoGroupParams) => GridRowGroupingResult; +export type GridRowGroupingPreProcessing = (params: RwoGroupParams) => GridRowGroupingResult | null; export interface GridRowGroupsPreProcessingApi { /** - * @param {RowGroupingFunction} columnsPreProcessing Pre-processing to register. + * Register a column pre-processing and emit an event to re-apply the row grouping pre-processing + * @param {string} processingName Name of the pre-processing. Used to clean the previous version of the pre-processing. + * @param {GridRowGroupingPreProcessing} columnsPreProcessing Pre-processing to register. * @ignore - do not document */ - registerRowGroupsBuilder: (groupingFunction: RowGroupingFunction | null) => void; + registerRowGroupsBuilder: ( + processingName: string, + groupingFunction: GridRowGroupingPreProcessing | null, + ) => void; /** + * Apply the first row grouping pre-processing that does not return null * @param {GridRowsLookup} rowsLookup. Lookup of the rows to group * @param {GridRowId[]} List of the rows IDs * @returns {GridRowGroupingResult} The grouped rows diff --git a/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts index 3f9c09a337d78..66a7a6f363c89 100644 --- a/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts +++ b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts @@ -2,41 +2,51 @@ import * as React from 'react'; import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridRowGroupsPreProcessingApi, - RowGroupingFunction, + GridRowGroupingPreProcessing, + GridRowGroupingResult, } from './gridRowGroupsPreProcessingApi'; import { GridEvents } from '../../../constants/eventsConstants'; import { useGridApiMethod } from '../useGridApiMethod'; -const getFlatRowTree: RowGroupingFunction = (params) => ({ +const getFlatRowTree: GridRowGroupingPreProcessing = (params) => ({ tree: new Map(params.ids.map((id) => [id.toString(), { id, depth: 0 }])), paths: Object.fromEntries(params.ids.map((id) => [id, [id.toString()]])), idRowsLookup: params.idRowsLookup, }); -/** - * TODO: Improve to be able to have several hooks registering and unregistering grouping method - * We should never have two hooks registering a grouping method, but we should be able to switch between grouping method on prop update - * For instance switch from treeData: true to a grouping by column. - */ export const useGridRowGroupsPreProcessing = (apiRef: GridApiRef) => { - const rowGroupsPreProcessingRef = React.useRef(); + const rowGroupsPreProcessingRef = React.useRef( + new Map(), + ); const registerRowGroupsBuilder = React.useCallback< GridRowGroupsPreProcessingApi['registerRowGroupsBuilder'] >( - (rowGrouping) => { - rowGroupsPreProcessingRef.current = rowGrouping; - apiRef.current.publishEvent(GridEvents.rowGroupsPreProcessingChange); + (processingName, rowGroupingPreProcessing) => { + const rowGroupingPreProcessingBefore = + rowGroupsPreProcessingRef.current.get(processingName) ?? null; + + if (rowGroupingPreProcessingBefore !== rowGroupingPreProcessing) { + rowGroupsPreProcessingRef.current.set(processingName, rowGroupingPreProcessing); + apiRef.current.publishEvent(GridEvents.rowGroupsPreProcessingChange); + } }, [apiRef], ); const groupRows = React.useCallback((...params) => { - if (!rowGroupsPreProcessingRef.current) { - return getFlatRowTree(...params); + let response: GridRowGroupingResult | null = null; + const preProcessingList = Array.from(rowGroupsPreProcessingRef.current.values()); + + while (!response && preProcessingList.length) { + response = preProcessingList.shift()!(...params); + } + + if (!response) { + return getFlatRowTree(...params)!; } - return rowGroupsPreProcessingRef.current(...params); + return response; }, []); const rowGroupsPreProcessingApi: GridRowGroupsPreProcessingApi = { From 8baf00e465ecc3d2c0d4b0385a541d98794c2935 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 8 Oct 2021 11:54:33 +0200 Subject: [PATCH 154/390] Update doc --- packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 0d6d0e1a98a1a..9b9fca2abcec6 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -153,7 +153,6 @@ const getRowsStateFromCache = ( const groupingResponse = apiRef.current.groupRows({ idRowsLookup, ids: rowIds, - gridRowId: (rowData: GridRowData) => getGridRowId(rowData, propGetRowId), }); const totalRowCount = propRowCount > rowIds.length ? propRowCount : rowIds.length; From def77d6526115a698cd40406257ac7221749188b Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 8 Oct 2021 13:08:08 +0200 Subject: [PATCH 155/390] Merge --- docs/pages/api-docs/data-grid/grid-col-def.md | 64 ++++++++++--------- .../grid/components/GridViewport.tsx | 6 +- .../grid/components/cell/GridRowCells.tsx | 11 ++-- .../features/filter/gridFilterSelector.ts | 2 +- .../treeData/gridTreeDataGroupColDef.tsx | 1 + .../grid/models/colDef/gridColDef.ts | 4 ++ 6 files changed, 51 insertions(+), 37 deletions(-) diff --git a/docs/pages/api-docs/data-grid/grid-col-def.md b/docs/pages/api-docs/data-grid/grid-col-def.md index da3ecaf4306e9..aa14356c89578 100644 --- a/docs/pages/api-docs/data-grid/grid-col-def.md +++ b/docs/pages/api-docs/data-grid/grid-col-def.md @@ -12,34 +12,36 @@ import { GridColDef } from '@mui/x-data-grid'; ## Properties -| Name | Type | Default | Description | -| :-------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------- | :----------------------------------------------- | :-------------------------------------------------------------------------------------------------------------- | -| align? | GridAlignment | | Allows to align the column values in cells. | -| cellClassName? | GridCellClassNamePropType | | Class name that will be added in cells for that column. | -| description? | string | | The description of the column rendered as tooltip if the column header name is not fully displayed. | -| disableColumnMenu? | boolean | false
| If `true`, the column menu is disabled for this column. | -| disableExport? | boolean | false
| If `true`, this column will not be included in exports. | -| disableReorder? | boolean | false
| If `true`, this column cannot be reordered. | -| editable? | boolean | false
| If `true`, the cells of the column are editable. | -| field | string | | The column identifier. It's used to map with GridRowData values. | -| filterable? | boolean | true
| If `true`, the column is filterable. | -| filterOperators? | GridFilterOperator[] | | Allows setting the filter operators for this column. | -| flex? | number | | If set, it indicates that a column has fluid width. Range [0, ∞). | -| headerAlign? | GridAlignment | | Header cell element alignment. | -| headerClassName? | GridColumnHeaderClassNamePropType | | Class name that will be added in the column header cell. | -| headerName? | string | | The title of the column rendered in the column header cell. | -| hide? | boolean | false
| If `true`, hide the column. | -| hideSortIcons? | boolean | false
| Toggle the visibility of the sort icons. | -| minWidth? | number | 50
| Sets the minimum width of a column. | -| renderCell? | (params: GridRenderCellParams) => ReactNode | | Allows to override the component rendered as cell for this column. | -| renderEditCell? | (params: GridRenderEditCellParams) => ReactNode | | Allows to override the component rendered in edit cell mode for this column. | -| renderHeader? | (params: GridColumnHeaderParams) => ReactNode | | Allows to render a component in the column header cell. | -| resizable? | boolean | true
| If `true`, the column is resizable. | -| sortable? | boolean | true
| If `true`, the column is sortable. | -| sortComparator? | GridComparatorFn | | A comparator function used to sort rows. | -| type? | string | 'string'
| Type allows to merge this object with a default definition [GridColDef](/api/data-grid/grid-col-def/). | -| valueFormatter? | (params: GridValueFormatterParams) => GridCellValue | | Function that allows to apply a formatter before rendering its value. | -| valueGetter? | (params: GridValueGetterParams) => GridCellValue | | Function that allows to get a specific data instead of field to render in the cell. | -| valueOptions? | (string \| number \| { label: string; value: any })[] | | To be used in combination with `type: 'singleSelect'`. This is an array of the possible cell values and labels. | -| valueParser? | (value: GridCellValue, params?: GridCellParams) => GridCellValue | | Function that takes the user-entered value and converts it to a value used internally. | -| width? | number | 100
| Set the width of the column. | +| Name | Type | Default | Description | +| :------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------- | :----------------------------------------------- | :-------------------------------------------------------------------------------------------------------------- | +| align? | GridAlignment | | Allows to align the column values in cells. | +| cellClassName? | GridCellClassNamePropType | | Class name that will be added in cells for that column. | +| description? | string | | The description of the column rendered as tooltip if the column header name is not fully displayed. | +| disableColumnMenu? | boolean | false
| If `true`, the column menu is disabled for this column. | +| disableExport? | boolean | false
| If `true`, this column will not be included in exports. | +| disableReorder? | boolean | false
| If `true`, this column cannot be reordered. | +| editable? | boolean | false
| If `true`, the cells of the column are editable. | +| field | string | | The column identifier. It's used to map with GridRowData values. | +| filterable? | boolean | true
| If `true`, the column is filterable. | +| filterOperators? | GridFilterOperator[] | | Allows setting the filter operators for this column. | +| flex? | number | | If set, it indicates that a column has fluid width. Range [0, ∞). | +| groupRows? | boolean | | If `true`, the rows will be grouped according to this column | +| headerAlign? | GridAlignment | | Header cell element alignment. | +| headerClassName? | GridColumnHeaderClassNamePropType | | Class name that will be added in the column header cell. | +| headerName? | string | | The title of the column rendered in the column header cell. | +| hide? | boolean | false
| If `true`, hide the column. | +| hideSortIcons? | boolean | false
| Toggle the visibility of the sort icons. | +| minWidth? | number | 50
| Sets the minimum width of a column. | +| renderCell? | (params: GridRenderCellParams) => ReactNode | | Allows to override the component rendered as cell for this column. | +| renderEditCell? | (params: GridRenderEditCellParams) => ReactNode | | Allows to override the component rendered in edit cell mode for this column. | +| renderHeader? | (params: GridColumnHeaderParams) => ReactNode | | Allows to render a component in the column header cell. | +| resizable? | boolean | true
| If `true`, the column is resizable. | +| shouldRenderFillerRows? | boolean | | If `true`, the `renderCell` / will be called for the filler rows | +| sortable? | boolean | true
| If `true`, the column is sortable. | +| sortComparator? | GridComparatorFn | | A comparator function used to sort rows. | +| type? | string | 'string'
| Type allows to merge this object with a default definition [GridColDef](/api/data-grid/grid-col-def/). | +| valueFormatter? | (params: GridValueFormatterParams) => GridCellValue | | Function that allows to apply a formatter before rendering its value. | +| valueGetter? | (params: GridValueGetterParams) => GridCellValue | | Function that allows to get a specific data instead of field to render in the cell. | +| valueOptions? | (string \| number \| { label: string; value: any })[] | | To be used in combination with `type: 'singleSelect'`. This is an array of the possible cell values and labels. | +| valueParser? | (value: GridCellValue, params?: GridCellParams) => GridCellValue | | Function that takes the user-entered value and converts it to a value used internally. | +| width? | number | 100
| Set the width of the column. | diff --git a/packages/grid/_modules_/grid/components/GridViewport.tsx b/packages/grid/_modules_/grid/components/GridViewport.tsx index c8d8af4d750fa..84a2a175dc5bd 100644 --- a/packages/grid/_modules_/grid/components/GridViewport.tsx +++ b/packages/grid/_modules_/grid/components/GridViewport.tsx @@ -27,7 +27,11 @@ import { } from '../hooks/root/gridContainerSizesSelector'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; -const getRowsSlice = (rows: TreeSortedVisibleRow[], startIndex: number, endIndex: number) => { +const getRowsSlice = ( + rows: TreeSortedVisibleRow[], + startIndex: number, + endIndex: number, +): TreeSortedVisibleRow[] => { const topLevelRows = rows.slice(startIndex, endIndex); const flattenRows = (nodes: TreeSortedVisibleRow[]) => diff --git a/packages/grid/_modules_/grid/components/cell/GridRowCells.tsx b/packages/grid/_modules_/grid/components/cell/GridRowCells.tsx index 47122a401b823..299c9ed0349da 100644 --- a/packages/grid/_modules_/grid/components/cell/GridRowCells.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridRowCells.tsx @@ -54,6 +54,7 @@ export function GridRowCells(props: RowCellsProps) { } = props; const apiRef = useGridApiContext(); const rootProps = useGridRootProps(); + const rowNode = apiRef.current.getRowNode(id); const cellsProps = columns.slice(firstColIdx, lastColIdx + 1).map((column, colIdx) => { const colIndex = firstColIdx + colIdx; @@ -80,7 +81,9 @@ export function GridRowCells(props: RowCellsProps) { const editCellState = editRowState && editRowState[column.field]; let cellComponent: React.ReactNode = null; - if (editCellState == null && column.renderCell) { + const skipRender = !column.shouldRenderFillerRows && rowNode?.fillerNode; + + if (editCellState == null && column.renderCell && !skipRender) { cellComponent = column.renderCell({ ...cellParams, api: apiRef.current }); // TODO move to GridCell classNames.push( @@ -88,7 +91,7 @@ export function GridRowCells(props: RowCellsProps) { ); } - if (editCellState != null && column.renderEditCell) { + if (editCellState != null && column.renderEditCell && !skipRender) { const params = { ...cellParams, ...editCellState, api: apiRef.current }; cellComponent = column.renderEditCell(params); // TODO move to GridCell @@ -101,13 +104,13 @@ export function GridRowCells(props: RowCellsProps) { } const cellProps: GridCellProps = { - value: cellParams.value, + value: skipRender ? null : cellParams.value, field: column.field, width: column.computedWidth, rowId: id, height, showRightBorder, - formattedValue: cellParams.formattedValue, + formattedValue: skipRender ? null : cellParams.formattedValue, align: column.align || 'left', rowIndex, cellMode: cellParams.cellMode, diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index 26b50a67e4c26..1eadfde754d33 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -43,7 +43,7 @@ export const gridSortedVisibleRowsSelector = createSelector( tree.forEach((row, id) => { if (visibleRowsLookup[id] !== false) { filteredRows.set(id, { - node: row.node, + ...row, children: row.children ? removeHiddenRows(row.children) : undefined, }); } diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx index c3475599463e9..b9779cecc238b 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx +++ b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx @@ -10,6 +10,7 @@ export const GridTreeDataGroupColDef: GridColDef = { filterable: false, disableColumnMenu: true, disableReorder: true, + shouldRenderFillerRows: true, align: 'left', width: 200, renderCell: (params) => , diff --git a/packages/grid/_modules_/grid/models/colDef/gridColDef.ts b/packages/grid/_modules_/grid/models/colDef/gridColDef.ts index 4723a72f41e3e..08f20efe20738 100644 --- a/packages/grid/_modules_/grid/models/colDef/gridColDef.ts +++ b/packages/grid/_modules_/grid/models/colDef/gridColDef.ts @@ -156,6 +156,10 @@ export interface GridColDef { * Allows setting the filter operators for this column. */ filterOperators?: GridFilterOperator[]; + /** + * If `true`, the `renderCell` / will be called for the filler rows + */ + shouldRenderFillerRows?: boolean; /** * If `true`, this column cannot be reordered. * @default false From c51d47d6a56a898afc8c317d7ad1e32f98c5aa1e Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 8 Oct 2021 13:12:33 +0200 Subject: [PATCH 156/390] Rename node => model --- packages/grid/_modules_/grid/components/GridViewport.tsx | 2 +- .../grid/hooks/features/filter/gridFilterSelector.ts | 8 ++++---- .../grid/hooks/features/sorting/gridSortingSelector.ts | 2 +- .../grid/hooks/features/sorting/gridSortingState.ts | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/grid/_modules_/grid/components/GridViewport.tsx b/packages/grid/_modules_/grid/components/GridViewport.tsx index 84a2a175dc5bd..0b0f8b56ecbe7 100644 --- a/packages/grid/_modules_/grid/components/GridViewport.tsx +++ b/packages/grid/_modules_/grid/components/GridViewport.tsx @@ -96,7 +96,7 @@ export const GridViewport: ViewportType = React.forwardRef( Array.from(tree.entries()).map(([id, row]) => ({ id, - node: row.node, + model: row.model, children: row.children ? flattenRowIds(row.children) : undefined, })); @@ -76,14 +76,14 @@ export const gridSortedVisibleRowsAsArraySelector = createSelector( }, ); -export type FlatSortedVisibleRow = { id: GridRowId; node: GridRowModel }; +export type FlatSortedVisibleRow = { id: GridRowId; model: GridRowModel }; export const gridSortedVisibleRowsAsArrayFlatSelector = createSelector( gridSortedVisibleRowsSelector, (rows) => { const flattenRowIds = (tree: GridSortedRowsTree): FlatSortedVisibleRow[] => Array.from(tree.entries()).flatMap(([id, row]) => [ - { id, node: row.node }, + { id, model: row.model }, ...(row.children ? flattenRowIds(row.children) : []), ]); diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts index d839ba0ec735f..84c1baab2ceaa 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts @@ -31,7 +31,7 @@ export const gridSortedRowsSelector = createSelector( nodes.forEach((node) => { map.set(node.id, { - node: idRowsLookup[node.id], + model: idRowsLookup[node.id], children: node.children ? buildMap(node.children) : undefined, }); }); diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts index c117b999cb6c6..239d638d8cf21 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts @@ -4,7 +4,7 @@ import { GridSortModel } from '../../../models/gridSortModel'; export type GridSortedRowsTree = Map; export interface GridSortedRowsTreeNode { - node: GridRowModel; + model: GridRowModel; children?: GridSortedRowsTree; } From fcf1f824da05f56a566dbc571fa55361ae4a65f2 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 8 Oct 2021 13:46:25 +0200 Subject: [PATCH 157/390] Remove node depth --- .../_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx | 2 +- .../_modules_/grid/hooks/features/treeData/useGridTreeData.ts | 2 -- packages/grid/_modules_/grid/models/gridRows.ts | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index a8a12e5c7adf3..d55a59a79184e 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -39,7 +39,7 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { } return ( - +
{!!node.children?.size && ( depth, }); } else { @@ -99,7 +98,6 @@ export const useGridTreeData = ( parent = { id: fillerId, fillerNode: true, - depth, expanded: props.defaultGroupingExpansionDepth > depth, }; diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index c041d70bed1a8..419cc9f506558 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -16,7 +16,6 @@ export interface GridRowConfigTreeNode { id: GridRowId; children?: GridRowConfigTree; expanded?: boolean; - depth: number; /** * If `true`, this node has been automatically added to fill a gap in the tree structure From 5563f93a857d892554be872e3425a701a0bd7ff2 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 8 Oct 2021 16:13:06 +0200 Subject: [PATCH 158/390] Remove recursivness --- .../features/treeData/useGridTreeData.ts | 105 +++++++----------- .../grid/_modules_/grid/models/gridRows.ts | 1 + 2 files changed, 40 insertions(+), 66 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index d334c01114b06..472cfc6af9d52 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -1,5 +1,5 @@ import * as React from 'react'; -import { GridRowId, GridRowConfigTree, GridRowsLookup } from '../../../models/gridRows'; +import { GridRowConfigTree, GridRowsLookup } from '../../../models/gridRows'; import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridComponentProps } from '../../../GridComponentProps'; import { GridColumnsPreProcessing } from '../../root/columnsPreProcessing'; @@ -62,78 +62,51 @@ export const useGridTreeData = ( .sort((a, b) => a.path.length - b.path.length); const paths = Object.fromEntries(rows.map((row) => [row.id, row.path])); - const fullTree: GridRowConfigTree = new Map(); + const tree: GridRowConfigTree = new Map(); const idRowsLookupFiller: GridRowsLookup = {}; - const fillerPaths: Record = {}; - - const insertRowInTree = ({ - tree, - id, - path, - originalPath, - depth, - }: { - tree: GridRowConfigTree; - id: GridRowId; - path: string[]; - originalPath: string[]; - depth: number; - }) => { - if (path.length === 0) { - throw new Error(`MUI: Could not insert row #${id} in the tree structure.`); - } - - if (path.length === 1) { - tree.set(path[0], { - id, - expanded: props.defaultGroupingExpansionDepth > depth, - }); - } else { - const [nodeName, ...restPath] = path; - - let parent = tree.get(nodeName); - if (!parent) { - const fillerPath = originalPath.slice(0, -1); - const fillerId = `filler-row-${fillerPath.join('-')}`; - parent = { - id: fillerId, - fillerNode: true, - expanded: props.defaultGroupingExpansionDepth > depth, - }; - - idRowsLookupFiller[fillerId] = {}; - fillerPaths[fillerId] = fillerPath; - - tree.set(nodeName, parent); - } - - if (!parent.children) { - parent.children = new Map(); - } - - insertRowInTree({ - tree: parent.children, - id, - originalPath, - path: restPath, - depth: depth + 1, - }); - } - }; rows.forEach((row) => { - insertRowInTree({ - tree: fullTree, - id: row.id, - path: row.path, - originalPath: row.path, - depth: 0, + let subTree = tree; + + row.path.forEach((nodeName, index) => { + if (index < row.path.length - 1) { + let parentNode = subTree.get(nodeName); + + if (!parentNode) { + const path = row.path.slice(0, index + 1); + const fillerId = `filler-row-${path.join('-')}`; + const childrenTree: GridRowConfigTree = new Map(); + + idRowsLookupFiller[fillerId] = {}; + paths[fillerId] = path; + + parentNode = { + id: fillerId, + fillerNode: true, + expanded: props.defaultGroupingExpansionDepth > index, + children: childrenTree, + }; + + subTree.set(nodeName, parentNode); + } + + if (!parentNode.children) { + parentNode.children = new Map(); + } + + subTree = parentNode!.children!; + } else { + subTree.set(nodeName, { + id: row.id, + expanded: props.defaultGroupingExpansionDepth > index, + }); + } }); }); return { - tree: fullTree, - paths: { ...paths, ...fillerPaths }, + tree, + paths, idRowsLookup: { ...params.idRowsLookup, ...idRowsLookupFiller }, }; }; diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index 419cc9f506558..caf783b9b37aa 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -15,6 +15,7 @@ export interface GridRowModelUpdate extends GridRowData { export interface GridRowConfigTreeNode { id: GridRowId; children?: GridRowConfigTree; + descendantsCount?: number; expanded?: boolean; /** From e8a05aa1f960664d893796f6938bfd5523b81400 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 8 Oct 2021 16:27:40 +0200 Subject: [PATCH 159/390] Refacto --- .../grid/hooks/features/rows/gridRowsUtils.ts | 54 ++++++++++++++++ .../features/treeData/useGridTreeData.ts | 61 +++++-------------- 2 files changed, 70 insertions(+), 45 deletions(-) create mode 100644 packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts new file mode 100644 index 0000000000000..0cb01b29fe50c --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts @@ -0,0 +1,54 @@ +import type { GridRowConfigTree, GridRowId, GridRowsLookup } from '../../../models'; + +export const insertLeafInTree = ({ + tree, + path, + id, + defaultGroupingExpansionDepth, + paths, + idRowsLookup, +}: { + tree: GridRowConfigTree; + path: string[]; + id: GridRowId; + defaultGroupingExpansionDepth: number; + paths: Record; + idRowsLookup: GridRowsLookup; +}) => { + let subTree = tree; + + path.forEach((nodeName, index) => { + if (index < path.length - 1) { + let parentNode = subTree.get(nodeName); + + if (!parentNode) { + const fillerPath = path.slice(0, index + 1); + const fillerId = `filler-row-${fillerPath.join('-')}`; + const childrenTree: GridRowConfigTree = new Map(); + + parentNode = { + id: fillerId, + fillerNode: true, + expanded: defaultGroupingExpansionDepth > index, + children: childrenTree, + }; + + subTree.set(nodeName, parentNode); + idRowsLookup[fillerId] = {}; + paths[fillerId] = fillerPath; + } + + if (!parentNode.children) { + parentNode.children = new Map(); + } + + subTree = parentNode!.children!; + } else { + subTree.set(nodeName, { + id, + expanded: defaultGroupingExpansionDepth > index, + }); + paths[id] = path; + } + }); +}; diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 472cfc6af9d52..2d353918a85a5 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -1,5 +1,5 @@ import * as React from 'react'; -import { GridRowConfigTree, GridRowsLookup } from '../../../models/gridRows'; +import { GridRowConfigTree, GridRowId, GridRowsLookup } from '../../../models/gridRows'; import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridComponentProps } from '../../../GridComponentProps'; import { GridColumnsPreProcessing } from '../../root/columnsPreProcessing'; @@ -10,6 +10,7 @@ import { GridCellParams, GridColDef, MuiEvent } from '../../../models'; import { isSpaceKey } from '../../../utils/keyboardUtils'; import { useFirstRender } from '../../utils/useFirstRender'; import { GridRowGroupingPreProcessing } from '../../root/rowGroupsPerProcessing'; +import { insertLeafInTree } from '../rows/gridRowsUtils'; /** * Only available in DataGridPro @@ -53,61 +54,31 @@ export const useGridTreeData = ( } const rows = params.ids - .map((rowId) => { - return { - id: rowId, - path: props.getTreeDataPath!(params.idRowsLookup[rowId]), - }; - }) + .map((rowId) => ({ + id: rowId, + path: props.getTreeDataPath!(params.idRowsLookup[rowId]), + })) .sort((a, b) => a.path.length - b.path.length); - const paths = Object.fromEntries(rows.map((row) => [row.id, row.path])); const tree: GridRowConfigTree = new Map(); - const idRowsLookupFiller: GridRowsLookup = {}; + const paths: Record = {}; + const idRowsLookup: GridRowsLookup = { ...params.idRowsLookup }; rows.forEach((row) => { - let subTree = tree; - - row.path.forEach((nodeName, index) => { - if (index < row.path.length - 1) { - let parentNode = subTree.get(nodeName); - - if (!parentNode) { - const path = row.path.slice(0, index + 1); - const fillerId = `filler-row-${path.join('-')}`; - const childrenTree: GridRowConfigTree = new Map(); - - idRowsLookupFiller[fillerId] = {}; - paths[fillerId] = path; - - parentNode = { - id: fillerId, - fillerNode: true, - expanded: props.defaultGroupingExpansionDepth > index, - children: childrenTree, - }; - - subTree.set(nodeName, parentNode); - } - - if (!parentNode.children) { - parentNode.children = new Map(); - } - - subTree = parentNode!.children!; - } else { - subTree.set(nodeName, { - id: row.id, - expanded: props.defaultGroupingExpansionDepth > index, - }); - } + insertLeafInTree({ + tree, + path: row.path, + id: row.id, + defaultGroupingExpansionDepth: props.defaultGroupingExpansionDepth, + paths, + idRowsLookup, }); }); return { tree, paths, - idRowsLookup: { ...params.idRowsLookup, ...idRowsLookupFiller }, + idRowsLookup, }; }; From ba7efd671cad6f22051f92c3a2b3950a202545b3 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 8 Oct 2021 16:33:17 +0200 Subject: [PATCH 160/390] Regen --- docs/pages/api-docs/data-grid/grid-col-def.md | 1 - .../grid/components/columnHeaders/GridColumnHeaderItem.tsx | 1 + .../grid/components/menu/columnMenu/GridColumnsMenuItem.tsx | 1 + .../grid/components/menu/columnMenu/GridFilterMenuItem.tsx | 1 + .../grid/components/menu/columnMenu/HideGridColMenuItem.tsx | 1 + .../grid/components/menu/columnMenu/SortGridMenuItems.tsx | 1 + .../rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts | 6 +++++- 7 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/pages/api-docs/data-grid/grid-col-def.md b/docs/pages/api-docs/data-grid/grid-col-def.md index aa14356c89578..8837d8e059012 100644 --- a/docs/pages/api-docs/data-grid/grid-col-def.md +++ b/docs/pages/api-docs/data-grid/grid-col-def.md @@ -25,7 +25,6 @@ import { GridColDef } from '@mui/x-data-grid'; | filterable? | boolean | true
| If `true`, the column is filterable. | | filterOperators? | GridFilterOperator[] | | Allows setting the filter operators for this column. | | flex? | number | | If set, it indicates that a column has fluid width. Range [0, ∞). | -| groupRows? | boolean | | If `true`, the rows will be grouped according to this column | | headerAlign? | GridAlignment | | Header cell element alignment. | | headerClassName? | GridColumnHeaderClassNamePropType | | Class name that will be added in the column header cell. | | headerName? | string | | The title of the column rendered in the column header cell. | diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderItem.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderItem.tsx index d8fc666db8a4f..44eb1e8a6d0d3 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderItem.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderItem.tsx @@ -286,6 +286,7 @@ GridColumnHeaderItem.propTypes = { renderEditCell: PropTypes.func, renderHeader: PropTypes.func, resizable: PropTypes.bool, + shouldRenderFillerRows: PropTypes.bool, sortable: PropTypes.bool, sortComparator: PropTypes.func, type: PropTypes.string, diff --git a/packages/grid/_modules_/grid/components/menu/columnMenu/GridColumnsMenuItem.tsx b/packages/grid/_modules_/grid/components/menu/columnMenu/GridColumnsMenuItem.tsx index 5c4e08ba081f4..b80ebe4bb57a1 100644 --- a/packages/grid/_modules_/grid/components/menu/columnMenu/GridColumnsMenuItem.tsx +++ b/packages/grid/_modules_/grid/components/menu/columnMenu/GridColumnsMenuItem.tsx @@ -65,6 +65,7 @@ GridColumnsMenuItem.propTypes = { renderEditCell: PropTypes.func, renderHeader: PropTypes.func, resizable: PropTypes.bool, + shouldRenderFillerRows: PropTypes.bool, sortable: PropTypes.bool, sortComparator: PropTypes.func, type: PropTypes.string, diff --git a/packages/grid/_modules_/grid/components/menu/columnMenu/GridFilterMenuItem.tsx b/packages/grid/_modules_/grid/components/menu/columnMenu/GridFilterMenuItem.tsx index cacdd14e8313f..5c080e5140c32 100644 --- a/packages/grid/_modules_/grid/components/menu/columnMenu/GridFilterMenuItem.tsx +++ b/packages/grid/_modules_/grid/components/menu/columnMenu/GridFilterMenuItem.tsx @@ -62,6 +62,7 @@ GridFilterMenuItem.propTypes = { renderEditCell: PropTypes.func, renderHeader: PropTypes.func, resizable: PropTypes.bool, + shouldRenderFillerRows: PropTypes.bool, sortable: PropTypes.bool, sortComparator: PropTypes.func, type: PropTypes.string, diff --git a/packages/grid/_modules_/grid/components/menu/columnMenu/HideGridColMenuItem.tsx b/packages/grid/_modules_/grid/components/menu/columnMenu/HideGridColMenuItem.tsx index 8bc9d00ce8717..519da309f12cc 100644 --- a/packages/grid/_modules_/grid/components/menu/columnMenu/HideGridColMenuItem.tsx +++ b/packages/grid/_modules_/grid/components/menu/columnMenu/HideGridColMenuItem.tsx @@ -72,6 +72,7 @@ HideGridColMenuItem.propTypes = { renderEditCell: PropTypes.func, renderHeader: PropTypes.func, resizable: PropTypes.bool, + shouldRenderFillerRows: PropTypes.bool, sortable: PropTypes.bool, sortComparator: PropTypes.func, type: PropTypes.string, diff --git a/packages/grid/_modules_/grid/components/menu/columnMenu/SortGridMenuItems.tsx b/packages/grid/_modules_/grid/components/menu/columnMenu/SortGridMenuItems.tsx index 2e846ada96eeb..0df947a19a6bb 100644 --- a/packages/grid/_modules_/grid/components/menu/columnMenu/SortGridMenuItems.tsx +++ b/packages/grid/_modules_/grid/components/menu/columnMenu/SortGridMenuItems.tsx @@ -83,6 +83,7 @@ SortGridMenuItems.propTypes = { renderEditCell: PropTypes.func, renderHeader: PropTypes.func, resizable: PropTypes.bool, + shouldRenderFillerRows: PropTypes.bool, sortable: PropTypes.bool, sortComparator: PropTypes.func, type: PropTypes.string, diff --git a/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts index 66a7a6f363c89..59c530f91f38f 100644 --- a/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts +++ b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts @@ -39,7 +39,11 @@ export const useGridRowGroupsPreProcessing = (apiRef: GridApiRef) => { const preProcessingList = Array.from(rowGroupsPreProcessingRef.current.values()); while (!response && preProcessingList.length) { - response = preProcessingList.shift()!(...params); + const preProcessing = preProcessingList.shift(); + + if (preProcessing) { + response = preProcessing(...params); + } } if (!response) { From d38639daa57ef16a2f41f1a3de4b6e679484bf22 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 8 Oct 2021 16:46:23 +0200 Subject: [PATCH 161/390] Add filler doc to tree data --- .../CustomGroupingColumnTreeData.js | 2 +- .../CustomGroupingColumnTreeData.tsx | 2 +- .../data-grid/group-pivot/FillerTreeData.js | 31 +++++++++++++++++ .../data-grid/group-pivot/FillerTreeData.tsx | 33 +++++++++++++++++++ .../data-grid/group-pivot/group-pivot.md | 6 ++++ .../data-grid/scrolling/ScrollPlayground.js | 10 +++--- 6 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 docs/src/pages/components/data-grid/group-pivot/FillerTreeData.js create mode 100644 docs/src/pages/components/data-grid/group-pivot/FillerTreeData.tsx diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js index c805a5857e6ae..ee22ba8ba094a 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js @@ -18,7 +18,7 @@ const CustomGridTreeDataGroupingCell = (props) => { } return ( - +
{node.children?.size ? ( +
+ +
+ + ); +} diff --git a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx new file mode 100644 index 0000000000000..5bcfa5b4fe1b9 --- /dev/null +++ b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx @@ -0,0 +1,33 @@ +import * as React from 'react'; +import { DataGridPro, useGridApiRef } from '@mui/x-data-grid-pro'; +import { useDemoTreeData } from '@mui/x-data-grid-generator'; +import Button from '@mui/material/Button'; +import Stack from '@mui/material/Stack'; + +export default function SetRowExpansionTreeData() { + const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + const apiRef = useGridApiRef(); + + const toggleFirstRow = () => { + const rowId = apiRef.current.getRowIdFromRowIndex(0); + apiRef.current.UNSTABLE_setRowExpansion( + rowId, + !apiRef.current.UNSTABLE_getRowNode(rowId)?.expanded, + ); + }; + + return ( + + +
+ +
+
+ ); +} diff --git a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md index 3ee789046cbf0..3161791ab2f72 100644 --- a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md +++ b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md @@ -49,6 +49,16 @@ Use the `groupingColDef` prop to customize the rendering of the grouping column. {{"demo": "pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js", "bg": "inline", "defaultCodeOpen": false}} +### Group expansion + +Use the `defaultGroupingExpansionDepth` prop to expand all the groups up to a given depth when loading the data. + +{{"demo": "pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.js", "bg": "inline", "defaultCodeOpen": false}} + +Use the `UNSTABLE_setRowExpansion` method on `apiRef` to programmatically set the expansion of a row. + +{{"demo": "pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js", "bg": "inline", "defaultCodeOpen": false}} + ### Filler rows If some entries are missing to build the full tree, the `DataGridPro` will automatically create filler rows to fill those gaps. From 29274d551c5f19a4df2262c1e447094bfb32065b Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 11 Oct 2021 17:18:35 +0200 Subject: [PATCH 177/390] Work on docs --- .../data-grid/group-pivot/group-pivot.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md index 3161791ab2f72..a0f47e6f3bedb 100644 --- a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md +++ b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md @@ -26,7 +26,12 @@ const rows: GridRows = [ { id: 5, path: ['B'] } ]; - row.path} rows={rows} {/* ...other props */} /> + row.path} + rows={rows} + {/* ...other props */} +/> // With transformation const rows: GridRows = [ @@ -38,7 +43,12 @@ const rows: GridRows = [ { id: 5, path: 'B' } ]; - row.path.split('.')} rows={rows} {/* ...other props */} /> + row.path.split('.')} + rows={rows} + {/* ...other props */} +/> ``` {{"demo": "pages/components/data-grid/group-pivot/BasicTreeData.js", "bg": "inline", "defaultCodeOpen": false}} From 39e276a063756ac4a625d794d65a641676a38fb6 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 11 Oct 2021 17:32:32 +0200 Subject: [PATCH 178/390] Prettier --- .../data-grid/group-pivot/group-pivot.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md index a0f47e6f3bedb..f1824f24d523d 100644 --- a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md +++ b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md @@ -26,11 +26,11 @@ const rows: GridRows = [ { id: 5, path: ['B'] } ]; - row.path} - rows={rows} - {/* ...other props */} + row.path} + rows={rows} + {/* ...other props */} /> // With transformation @@ -43,11 +43,11 @@ const rows: GridRows = [ { id: 5, path: 'B' } ]; - row.path.split('.')} - rows={rows} - {/* ...other props */} + row.path.split('.')} + rows={rows} + {/* ...other props */} /> ``` From e23517d45bb22febbe9fd268ff61f2a1355deb5c Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 12 Oct 2021 16:28:09 +0200 Subject: [PATCH 179/390] Move to flat tree --- docs/pages/api-docs/data-grid/grid-api.md | 7 +- .../api-docs/data-grid/grid-filter-api.json | 2 +- docs/pages/playground.tsx | 21 ++- .../data-grid/group-pivot/BasicTreeData.js | 5 +- .../data-grid/group-pivot/BasicTreeData.tsx | 5 +- .../CustomGroupingColumnTreeData.js | 5 +- .../CustomGroupingColumnTreeData.tsx | 17 ++- .../DefaultGroupingExpansionDepthTreeData.js | 5 +- .../DefaultGroupingExpansionDepthTreeData.tsx | 5 +- .../DisableChildrenFilteringTreeData.js | 5 +- .../DisableChildrenFilteringTreeData.tsx | 5 +- .../DisableChildrenSortingTreeData.js | 5 +- .../DisableChildrenSortingTreeData.tsx | 5 +- .../group-pivot/SetRowExpansionTreeData.js | 5 +- .../group-pivot/SetRowExpansionTreeData.tsx | 5 +- .../data-grid/scrolling/ScrollPlayground.tsx | 6 +- .../grid/components/GridViewport.tsx | 48 +++--- .../grid/components/base/GridOverlays.tsx | 7 +- .../cell/GridTreeDataGroupingCell.tsx | 8 +- .../columnSelection/GridHeaderCheckbox.tsx | 4 +- .../features/export/useGridCsvExport.tsx | 4 +- .../features/filter/gridFilterSelector.ts | 81 +++------- .../hooks/features/filter/gridFilterState.ts | 14 -- .../hooks/features/filter/useGridFilter.ts | 141 ++++++------------ .../features/keyboard/useGridKeyboard.ts | 5 +- .../keyboard/useGridKeyboardNavigation.ts | 4 +- .../pagination/gridPaginationSelector.ts | 4 +- .../hooks/features/rows/gridRowsSelector.ts | 60 ++------ .../grid/hooks/features/rows/gridRowsState.ts | 7 +- .../grid/hooks/features/rows/gridRowsUtils.ts | 81 ++++++---- .../grid/hooks/features/rows/useGridRows.ts | 105 +++---------- .../features/selection/useGridSelection.ts | 6 +- .../features/sorting/gridSortingSelector.ts | 38 ++--- .../features/sorting/gridSortingState.ts | 16 +- .../hooks/features/sorting/useGridSorting.ts | 76 +++++++--- .../features/treeData/useGridTreeData.ts | 10 +- .../gridRowGroupsPreProcessingApi.ts | 1 - .../useGridRowGroupsPreProcessing.ts | 3 +- .../grid/models/api/gridFilterApi.ts | 3 +- .../_modules_/grid/models/api/gridRowApi.ts | 7 - .../_modules_/grid/models/api/gridSortApi.ts | 17 +-- .../grid/_modules_/grid/models/gridRows.ts | 8 +- .../src/useDemoTreeData.ts | 25 +++- .../src/stories/grid-tree-data.stories.tsx | 12 +- 44 files changed, 383 insertions(+), 520 deletions(-) diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index 83ceae2642c93..d9470f67e10ef 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -12,7 +12,7 @@ import { GridApi } from '@mui/x-data-grid-pro'; | Name | Type | Description | | :----------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| applyFilter | (item: GridFilterItem, linkOperator?: GridLinkOperator) => boolean | Applies a GridFilterItem on all rows. If no `linkOperator` is given, the "and" operator is used. | +| applyFilter | (item: GridFilterItem, linkOperator?: GridLinkOperator) => void | Applies a GridFilterItem on all rows. If no `linkOperator` is given, the "and" operator is used. | | applyFilterLinkOperator | (operator: GridLinkOperator) => void | Changes the GridLinkOperator used to connect the filters. | | applyFilters | () => void | Applies all filters on all rows. | | applySorting | () => void | Applies the current sort model to the rows. | @@ -35,7 +35,6 @@ import { GridApi } from '@mui/x-data-grid-pro'; | getColumnsMeta | () => GridColumnsMeta | Returns the GridColumnsMeta for each column. | | getDataAsCsv | (options?: GridExportCsvOptions) => string | Returns the grid data as a CSV string.
This method is used internally by `exportDataAsCsv`. | | getEditRowsModel | () => GridEditRowsModel | Gets the edit rows model of the grid. | -| getFlatSortedRowIds | () => GridRowId[] | Returns all row ids sorted according to the active sort model. | | getLocaleText | <T extends keyof GridLocaleText>(key: T) => GridLocaleText[T] | Returns the translation for the `key`. | | getRow | (id: GridRowId) => null \| GridRowData | Gets the row data with a given id. | | getRowElement | (id: GridRowId) => null \| HTMLDivElement | Gets the underlying DOM element for a row at the given `id`. | @@ -47,8 +46,8 @@ import { GridApi } from '@mui/x-data-grid-pro'; | getRowsCount | () => number | Gets the total number of rows in the grid. | | getScrollPosition | () => GridScrollParams | Returns the current scroll position. | | getSelectedRows | () => Map<GridRowId, GridRowData> | Returns an array of the selected rows. | -| getSortedRowIds | () => GridSortedRowsIdTreeNode[] | Returns all row ids sorted according to the active sort model. | -| getSortedRows | () => GridSortedRowsTree | Returns all rows sorted according to the active sort model. | +| getSortedRowIds | () => GridRowId[] | Returns all row ids sorted according to the active sort model. | +| getSortedRows | () => GridRowData[] | Returns all rows sorted according to the active sort model. | | getSortModel | () => GridSortModel | Returns the sort model currently applied to the grid. | | getVisibleColumns | () => GridStateColDef[] | Returns the currently visible columns. | | getVisibleRowModels | () => Map<GridRowId, GridRowData> | Returns a sorted `Map` containing only the visible rows. | diff --git a/docs/pages/api-docs/data-grid/grid-filter-api.json b/docs/pages/api-docs/data-grid/grid-filter-api.json index ce5acad0b1ac7..0bfb1c8f6c8bd 100644 --- a/docs/pages/api-docs/data-grid/grid-filter-api.json +++ b/docs/pages/api-docs/data-grid/grid-filter-api.json @@ -5,7 +5,7 @@ { "name": "applyFilter", "description": "Applies a GridFilterItem on all rows. If no linkOperator is given, the "and" operator is used.", - "type": "(item: GridFilterItem, linkOperator?: GridLinkOperator) => boolean" + "type": "(item: GridFilterItem, linkOperator?: GridLinkOperator) => void" }, { "name": "applyFilterLinkOperator", diff --git a/docs/pages/playground.tsx b/docs/pages/playground.tsx index 8d573e70e7b82..8edfbf8eb55d3 100644 --- a/docs/pages/playground.tsx +++ b/docs/pages/playground.tsx @@ -1,5 +1,22 @@ import * as React from 'react'; +import { DataGridPro } from '@mui/x-data-grid-pro'; +import { useDemoTreeData } from '@mui/x-data-grid-generator'; -export default function Playground() { - return
This file is listed in `.gitignore`
; +export default function BasicTreeData() { + const { data, loading } = useDemoTreeData({ rowLength: [2, 2], randomLength: false }); + + return ( +
+ +
+ ); } diff --git a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js index 813230d6a76ae..81761962ea92c 100644 --- a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js @@ -3,7 +3,10 @@ import { DataGridPro } from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; export default function BasicTreeData() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + const { data, loading } = useDemoTreeData({ + rowLength: [10, 5, 3], + randomLength: true, + }); return (
diff --git a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx index 813230d6a76ae..81761962ea92c 100644 --- a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx @@ -3,7 +3,10 @@ import { DataGridPro } from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; export default function BasicTreeData() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + const { data, loading } = useDemoTreeData({ + rowLength: [10, 5, 3], + randomLength: true, + }); return (
diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js index c3f124d521dd7..4fcf87f52e1c3 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js @@ -50,7 +50,10 @@ const groupingColDef = { }; export default function CustomGroupingColumnTreeData() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + const { data, loading } = useDemoTreeData({ + rowLength: [10, 5, 3], + randomLength: true, + }); return (
diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx index 4cbce887dc3de..1c685e73504a0 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx @@ -3,6 +3,7 @@ import { DataGridPro, DataGridProProps, GridRenderCellParams, + useGridRootProps, useGridSlotComponentProps, } from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; @@ -10,21 +11,22 @@ import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; const CustomGridTreeDataGroupingCell = (props: GridRenderCellParams) => { - const { id } = props; + const { id, row } = props; const { apiRef } = useGridSlotComponentProps(); + const rootProps = useGridRootProps(); const node = apiRef.current.UNSTABLE_getRowNode(id); - const path = apiRef.current.UNSTABLE_getRowPath(id); + const path = rootProps.getTreeDataPath!(row); - if (!node || !path) { + if (!node) { throw new Error(`MUI: No row with id #${id} found`); } return (
- {node.children?.size ? ( + {node.children?.length ? ( ) : ( @@ -47,7 +49,10 @@ const groupingColDef: DataGridProProps['groupingColDef'] = { }; export default function CustomGroupingColumnTreeData() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + const { data, loading } = useDemoTreeData({ + rowLength: [10, 5, 3], + randomLength: true, + }); return (
diff --git a/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.js b/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.js index 3fc715a563327..0bdc85586570b 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.js @@ -3,7 +3,10 @@ import { DataGridPro } from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; export default function DefaultGroupingExpansionDepthTreeData() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + const { data, loading } = useDemoTreeData({ + rowLength: [10, 5, 3], + randomLength: true, + }); return (
diff --git a/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.tsx index 3fc715a563327..0bdc85586570b 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.tsx @@ -3,7 +3,10 @@ import { DataGridPro } from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; export default function DefaultGroupingExpansionDepthTreeData() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + const { data, loading } = useDemoTreeData({ + rowLength: [10, 5, 3], + randomLength: true, + }); return (
diff --git a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js index d0bd7710e61bd..af42859a9c41d 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js @@ -3,7 +3,10 @@ import { DataGridPro } from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; export default function DisableChildrenFilteringTreeData() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + const { data, loading } = useDemoTreeData({ + rowLength: [10, 5, 3], + randomLength: true, + }); const [filterModel, setFilterModel] = React.useState({ items: [{ columnField: 'index', operatorValue: '>', value: 2 }], }); diff --git a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx index bf4c28073d61c..01bd356e25146 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx @@ -3,7 +3,10 @@ import { DataGridPro, GridFilterModel } from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; export default function DisableChildrenFilteringTreeData() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + const { data, loading } = useDemoTreeData({ + rowLength: [10, 5, 3], + randomLength: true, + }); const [filterModel, setFilterModel] = React.useState({ items: [{ columnField: 'index', operatorValue: '>', value: 2 }], }); diff --git a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js index e6682166777d3..bc5d1ef764980 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js @@ -3,7 +3,10 @@ import { DataGridPro } from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; export default function DisableChildrenSortingTreeData() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + const { data, loading } = useDemoTreeData({ + rowLength: [10, 5, 3], + randomLength: true, + }); const [sortModel, setSortingModel] = React.useState([ { field: 'index', sort: 'desc' }, ]); diff --git a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.tsx index e992b9b391362..b866dadf0b49c 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.tsx @@ -3,7 +3,10 @@ import { DataGridPro, GridSortModel } from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; export default function DisableChildrenSortingTreeData() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + const { data, loading } = useDemoTreeData({ + rowLength: [10, 5, 3], + randomLength: true, + }); const [sortModel, setSortingModel] = React.useState([ { field: 'index', sort: 'desc' }, ]); diff --git a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js index 5bcfa5b4fe1b9..0ab5d43d472f6 100644 --- a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js @@ -5,7 +5,10 @@ import Button from '@mui/material/Button'; import Stack from '@mui/material/Stack'; export default function SetRowExpansionTreeData() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + const { data, loading } = useDemoTreeData({ + rowLength: [10, 5, 3], + randomLength: true, + }); const apiRef = useGridApiRef(); const toggleFirstRow = () => { diff --git a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx index 5bcfa5b4fe1b9..0ab5d43d472f6 100644 --- a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx @@ -5,7 +5,10 @@ import Button from '@mui/material/Button'; import Stack from '@mui/material/Stack'; export default function SetRowExpansionTreeData() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + const { data, loading } = useDemoTreeData({ + rowLength: [10, 5, 3], + randomLength: true, + }); const apiRef = useGridApiRef(); const toggleFirstRow = () => { diff --git a/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.tsx b/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.tsx index 5b9ecca30a018..0abeacc228b92 100644 --- a/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.tsx +++ b/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.tsx @@ -10,7 +10,7 @@ import { gridVisibleRowCountSelector, visibleGridColumnsLengthSelector, visibleGridColumnsSelector, - gridSortedRowIdsFlatSelector, + gridSortedRowIdsSelector, GridCellParams, } from '@mui/x-data-grid-pro'; import { useDemoData } from '@mui/x-data-grid-generator'; @@ -31,7 +31,7 @@ export default function ScrollPlayground() { React.useEffect(() => { const { rowIndex, colIndex } = coordinates; apiRef.current.scrollToIndexes(coordinates); - const id = gridSortedRowIdsFlatSelector(apiRef.current.state)[rowIndex]; + const id = gridSortedRowIdsSelector(apiRef.current.state)[rowIndex]; const column = visibleGridColumnsSelector(apiRef.current.state)[colIndex]; apiRef.current.setCellFocus(id, column.field); }, [apiRef, coordinates]); @@ -57,7 +57,7 @@ export default function ScrollPlayground() { }; const handleCellClick = (params: GridCellParams) => { - const rowIndex = gridSortedRowIdsFlatSelector(apiRef.current.state).findIndex( + const rowIndex = gridSortedRowIdsSelector(apiRef.current.state).findIndex( (id) => id === params.id, ); const colIndex = visibleGridColumnsSelector(apiRef.current.state).findIndex( diff --git a/packages/grid/_modules_/grid/components/GridViewport.tsx b/packages/grid/_modules_/grid/components/GridViewport.tsx index 2877d9f3d62ad..b1a13ac64bdd0 100644 --- a/packages/grid/_modules_/grid/components/GridViewport.tsx +++ b/packages/grid/_modules_/grid/components/GridViewport.tsx @@ -3,8 +3,8 @@ import { visibleGridColumnsSelector } from '../hooks/features/columns/gridColumn import { useGridSelector } from '../hooks/features/core/useGridSelector'; import { gridDensityRowHeightSelector } from '../hooks/features/density/densitySelector'; import { - gridSortedVisibleRowsAsArraySelector, - TreeSortedVisibleRow, + gridSortedVisibleRowEntriesSelector, + gridSortedVisibleTopLevelRowEntriesSelector, } from '../hooks/features/filter/gridFilterSelector'; import { gridFocusCellSelector, @@ -25,19 +25,7 @@ import { gridScrollBarSizeSelector, } from '../hooks/root/gridContainerSizesSelector'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; - -const getRowsSlice = ( - rows: TreeSortedVisibleRow[], - startIndex: number, - endIndex: number, -): TreeSortedVisibleRow[] => { - const topLevelRows = rows.slice(startIndex, endIndex); - - const flattenRows = (nodes: TreeSortedVisibleRow[]) => - nodes.flatMap((node) => [node, ...(node.children ? flattenRows(node.children) : [])]); - - return flattenRows(topLevelRows); -}; +import {GridRowEntry, GridRowId} from '../models'; type ViewportType = React.ForwardRefExoticComponent>; @@ -53,7 +41,11 @@ export const GridViewport: ViewportType = React.forwardRef( const cellFocus = useGridSelector(apiRef, gridFocusCellSelector); const cellTabIndex = useGridSelector(apiRef, gridTabIndexCellSelector); const selection = useGridSelector(apiRef, gridSelectionStateSelector); - const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowsAsArraySelector); + const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowEntriesSelector); + const visibleSortedTopLevelRows = useGridSelector( + apiRef, + gridSortedVisibleTopLevelRowEntriesSelector, + ); const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector); const editRowsState = useGridSelector(apiRef, gridEditRowsStateSelector); @@ -79,11 +71,25 @@ export const GridViewport: ViewportType = React.forwardRef( return null; } - const renderedRows = getRowsSlice( - visibleSortedRows, - renderState.renderContext.firstRowIdx!, - renderState.renderContext.lastRowIdx!, - ); + // TODO: Improve with new virtualization + let renderedRows: GridRowEntry[] + + if (visibleSortedRows.length === 0) { + renderedRows = [] + } else { + const firstRowIdx = renderState.renderContext.firstRowIdx! + const lastRowIdx = renderState.renderContext.lastRowIdx! + + const getRowIndex = (id: GridRowId) => visibleSortedRows.findIndex(row => row.id === id) + + const startIndex = getRowIndex(visibleSortedTopLevelRows[firstRowIdx].id) + const endIndex = lastRowIdx >= visibleSortedTopLevelRows.length - 1 ? visibleSortedRows.length - 1 : getRowIndex(visibleSortedTopLevelRows[lastRowIdx + 1].id) + + renderedRows = visibleSortedRows.slice( + startIndex, + endIndex + 1, + ); + } return renderedRows.map((row, idx) => ( 0 && visibleTopLevelRowCount === 0; + const showNoResultsOverlay = !rootProps.loading && totalRowCount > 0 && visibleRowCount === 0; if (showNoRowsOverlay) { return ; diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index 637ffab8b530f..b01ddac53407b 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -21,7 +21,7 @@ const useStyles = makeStyles({ }); const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { - const { id } = props; + const { id, row } = props; const rootProps = useGridRootProps(); const apiRef = useGridApiContext(); @@ -32,16 +32,16 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { ? rootProps.components.TreeDataCollapseIcon : rootProps.components.TreeDataExpandIcon; - const path = apiRef.current.UNSTABLE_getRowPath(id); + const path = rootProps.getTreeDataPath!(row); - if (!node || !path) { + if (!node) { throw new Error(`MUI: No row with id #${id} found`); } return (
- {!!node.children?.size && ( + {!!node.children?.length && ( apiRef.current.UNSTABLE_setRowExpansion(id, !node?.expanded)} diff --git a/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx b/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx index 46e3176788e93..b5ba2c1f36c3b 100644 --- a/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx +++ b/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { GridEvents } from '../../constants/eventsConstants'; import { useGridSelector } from '../../hooks/features/core/useGridSelector'; import { gridSortedVisiblePaginatedRowsAsArrayFlatSelector } from '../../hooks/features/pagination/gridPaginationSelector'; -import { gridSortedVisibleRowsAsArrayFlatSelector } from '../../hooks/features/filter/gridFilterSelector'; +import { gridSortedVisibleRowEntriesSelector } from '../../hooks/features/filter/gridFilterSelector'; import { gridTabIndexColumnHeaderSelector } from '../../hooks/features/focus/gridFocusStateSelector'; import { gridRowCountSelector } from '../../hooks/features/rows/gridRowsSelector'; import { gridSelectionStateSelector } from '../../hooks/features/selection/gridSelectionSelector'; @@ -59,7 +59,7 @@ const GridHeaderCheckbox = React.forwardRef row.id); apiRef.current.selectRows(rowsToBeSelected, checked, !event.target.indeterminate); diff --git a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx index 2c0a6a81220c0..257dcd8d30d2d 100644 --- a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx +++ b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx @@ -3,7 +3,7 @@ import { GridApiRef } from '../../../models/api/gridApiRef'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridSelector } from '../core/useGridSelector'; import { allGridColumnsSelector, visibleGridColumnsSelector } from '../columns'; -import { gridSortedVisibleRowsAsArrayFlatSelector } from '../filter'; +import { gridSortedVisibleRowEntriesSelector } from '../filter'; import { gridSelectionStateSelector } from '../selection'; import { GridCsvExportApi } from '../../../models/api/gridCsvExportApi'; import { GridExportCsvOptions } from '../../../models/gridExport'; @@ -23,7 +23,7 @@ export const useGridCsvExport = (apiRef: GridApiRef): void => { const logger = useGridLogger(apiRef, 'useGridCsvExport'); const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); const columns = useGridSelector(apiRef, allGridColumnsSelector); - const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowsAsArrayFlatSelector); + const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowEntriesSelector); const selection = useGridSelector(apiRef, gridSelectionStateSelector); const getDataAsCsv = React.useCallback( diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index 2728a9e83073a..eb4bdc62b74db 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -2,9 +2,9 @@ import { createSelector } from 'reselect'; import { GridFilterItem } from '../../../models/gridFilterItem'; import { GridRowId, GridRowModel } from '../../../models/gridRows'; import { GridState } from '../core/gridState'; -import { gridSortedRowsSelector } from '../sorting/gridSortingSelector'; +import { gridSortedRowEntriesSelector } from '../sorting/gridSortingSelector'; import { gridColumnLookupSelector } from '../columns/gridColumnsSelector'; -import { GridSortedRowsTree } from '../sorting'; +import { gridRowTreeSelector } from '../rows'; export const gridFilterStateSelector = (state: GridState) => state.filter; @@ -23,72 +23,33 @@ export const gridVisibleRowsLookupSelector = createSelector( (filterState) => filterState.visibleRowsLookup, ); -export const gridVisibleRowCountSelector = createSelector( - gridFilterStateSelector, - (filterState) => filterState.visibleRowCount, +export const gridSortedVisibleRowEntriesSelector = createSelector( + gridVisibleRowsLookupSelector, + gridSortedRowEntriesSelector, + (visibleRowsLookup, sortedRows) => + sortedRows.filter((row) => visibleRowsLookup[row.id] !== false), ); -export const gridVisibleTopLevelRowCountSelector = createSelector( - gridFilterStateSelector, - (filterState) => filterState.visibleTopLevelRowCount, +export const gridSortedVisibleRowsMapSelector = createSelector( + gridSortedVisibleRowEntriesSelector, + (visibleSortedRows) => + new Map(visibleSortedRows.map((row) => [row.id, row.model])), ); -export const gridSortedVisibleRowsSelector = createSelector( - gridVisibleRowsLookupSelector, - gridSortedRowsSelector, - (visibleRowsLookup, sortedRowsTree) => { - const removeHiddenRows = (tree: GridSortedRowsTree) => { - const filteredRows: GridSortedRowsTree = new Map(); - - tree.forEach((row, id) => { - if (visibleRowsLookup[id] !== false) { - filteredRows.set(id, { - ...row, - children: row.children ? removeHiddenRows(row.children) : undefined, - }); - } - }); - - return filteredRows; - }; - - return removeHiddenRows(sortedRowsTree); - }, +export const gridSortedVisibleTopLevelRowEntriesSelector = createSelector( + gridSortedVisibleRowEntriesSelector, + gridRowTreeSelector, + (sortedVisibleRows, rowTree) => sortedVisibleRows.filter((row) => rowTree[row.id]?.depth === 0), ); -export type TreeSortedVisibleRow = { - id: GridRowId; - model: GridRowModel; - children?: TreeSortedVisibleRow[]; -}; - -export const gridSortedVisibleRowsAsArraySelector = createSelector( - gridSortedVisibleRowsSelector, - (rows) => { - const flattenRowIds = (tree: GridSortedRowsTree): TreeSortedVisibleRow[] => - Array.from(tree.entries()).map(([id, row]) => ({ - id, - model: row.model, - children: row.children ? flattenRowIds(row.children) : undefined, - })); - - return flattenRowIds(rows); - }, +export const gridVisibleRowCountSelector = createSelector( + gridSortedVisibleRowEntriesSelector, + (sortedVisibleRows) => sortedVisibleRows.length, ); -export type FlatSortedVisibleRow = { id: GridRowId; model: GridRowModel }; - -export const gridSortedVisibleRowsAsArrayFlatSelector = createSelector( - gridSortedVisibleRowsSelector, - (rows) => { - const flattenRowIds = (tree: GridSortedRowsTree): FlatSortedVisibleRow[] => - Array.from(tree.entries()).flatMap(([id, row]) => [ - { id, model: row.model }, - ...(row.children ? flattenRowIds(row.children) : []), - ]); - - return flattenRowIds(rows); - }, +export const gridVisibleTopLevelRowCountSelector = createSelector( + gridSortedVisibleTopLevelRowEntriesSelector, + (sortedVisibleTopLevelRows) => sortedVisibleTopLevelRows.length, ); export const activeGridFilterItemsSelector = createSelector( diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts index d7b7b0e0668dd..cef15c1371152 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts @@ -10,8 +10,6 @@ export const getDefaultGridFilterModel: () => GridFilterModel = () => ({ export const getEmptyVisibleRows = (): Omit => ({ visibleRowsLookup: {}, visibleRows: [], - visibleRowCount: 0, - visibleTopLevelRowCount: 0, }); export interface GridFilterState { @@ -28,16 +26,4 @@ export interface GridFilterState { * It also contains the expanded children rows */ visibleRows: GridRowId[]; - - /** - * Amount of rows after applying the filtering - * It also count the expanded children rows - */ - visibleRowCount: number; - - /** - * Amount of rows after applying the filtering - * It does not count the expanded children rows - */ - visibleTopLevelRowCount: number; } diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index fdf1aa596b3cf..901440c67c688 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -5,7 +5,6 @@ import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridFilterApi } from '../../../models/api/gridFilterApi'; import { GridFeatureModeConstant } from '../../../models/gridFeatureMode'; import { GridFilterItem, GridLinkOperator } from '../../../models/gridFilterItem'; -import { GridRowId } from '../../../models/gridRows'; import { isDeepEqual } from '../../../utils/utils'; import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; import { useGridApiMethod } from '../../root/useGridApiMethod'; @@ -13,24 +12,17 @@ import { useGridLogger } from '../../utils/useGridLogger'; import { filterableGridColumnsIdsSelector } from '../columns/gridColumnsSelector'; import { useGridState } from '../core/useGridState'; import { GridPreferencePanelsValue } from '../preferencesPanel/gridPreferencePanelsValue'; -import { - gridSortedRowIdsFlatSelector, - gridSortedRowsSelector, -} from '../sorting/gridSortingSelector'; +import { gridSortedRowIdsSelector } from '../sorting/gridSortingSelector'; import { getDefaultGridFilterModel, getEmptyVisibleRows } from './gridFilterState'; import { GridFilterModel } from '../../../models/gridFilterModel'; import { gridVisibleRowsLookupSelector, - gridSortedVisibleRowsSelector, + gridSortedVisibleRowsMapSelector, gridFilterModelSelector, } from './gridFilterSelector'; -import { GridSortedRowsTree } from '../sorting'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; -import { - gridExpandedRowCountSelector, - gridTopLevelRowCountSelector, -} from '../rows/gridRowsSelector'; +import { gridRowTreeSelector } from '../rows'; const checkFilterModelValidity = (model: GridFilterModel) => { if (model.items.length > 1) { @@ -86,16 +78,16 @@ export const useGridFilter = ( changeEvent: GridEvents.filterModelChange, }); - const applyFilter = React.useCallback( + const applyFilter = React.useCallback( (filterItem: GridFilterItem, linkOperator: GridLinkOperator = GridLinkOperator.And) => { if (!filterItem.columnField || !filterItem.operatorValue) { - return false; + return; } const column = apiRef.current.getColumn(filterItem.columnField); if (!column) { - return false; + return; } const parsedValue = column.valueParser @@ -123,120 +115,71 @@ export const useGridFilter = ( const applyFilterOnRow = filterOperator.getApplyFilterFn(newFilterItem, column)!; if (typeof applyFilterOnRow !== 'function') { - return false; + return; } setGridState((state) => { const visibleRowsLookup = { ...gridVisibleRowsLookupSelector(state) }; - let visibleRowCount = 0; - let visibleTopLevelRowCount = 0; - const visibleRows: GridRowId[] = []; // We run the selector on the state here to avoid rendering the rows and then filtering again. // This way we have latest rows on the first rendering - const rowTree = gridSortedRowsSelector(state); - - const filterRowTree = (tree: GridSortedRowsTree, depth = 0) => { - tree.forEach((node, id) => { - let hasCurrentFilter: boolean; - - if (depth > 0 && props.disableChildrenFiltering) { - hasCurrentFilter = true; - } else { - const params = apiRef.current.getCellParams(id, newFilterItem.columnField!); - hasCurrentFilter = applyFilterOnRow(params); - } - - const lookupElementBeforeCurrentFilter = visibleRowsLookup[id]; + const rows = gridSortedRowIdsSelector(state); + const rowTree = gridRowTreeSelector(state); - let isVisible: boolean; + rows.forEach((rowId) => { + const params = apiRef.current.getCellParams(rowId, newFilterItem.columnField!); + const depth = rowTree[rowId].depth; - if (lookupElementBeforeCurrentFilter == null) { - isVisible = hasCurrentFilter; - } else if (linkOperator === GridLinkOperator.And) { - isVisible = lookupElementBeforeCurrentFilter && hasCurrentFilter; + if (depth === 0 || !props.disableChildrenFiltering) { + const isShown = applyFilterOnRow(params); + if (visibleRowsLookup[rowId] == null) { + visibleRowsLookup[rowId] = isShown; } else { - isVisible = lookupElementBeforeCurrentFilter || hasCurrentFilter; - } - - visibleRowsLookup[id] = isVisible; - - if (isVisible) { - visibleRows.push(id); - visibleRowCount += 1; - if (depth === 0) { - visibleTopLevelRowCount += 1; - } + visibleRowsLookup[rowId] = + linkOperator === GridLinkOperator.And + ? visibleRowsLookup[rowId] && isShown + : visibleRowsLookup[rowId] || isShown; } - - if (node.children) { - filterRowTree(node.children, depth + 1); - } - }); - }; - - filterRowTree(rowTree); + } + }); return { ...state, filter: { ...state.filter, visibleRowsLookup, - visibleRows, - visibleRowCount, - visibleTopLevelRowCount, + visibleRows: Object.entries(visibleRowsLookup) + .filter(([, isVisible]) => isVisible) + .map(([id]) => id), }, }; }); forceUpdate(); - - return true; }, [apiRef, forceUpdate, logger, setGridState, props.disableChildrenFiltering], ); const applyFilters = React.useCallback(() => { - const { items, linkOperator } = gridFilterModelSelector(apiRef.current.state); - - let hasAppliedAtLeastOneFilter = false; - - if (props.filterMode === GridFeatureModeConstant.client) { - // Clearing filtered rows - setGridState((state) => ({ - ...state, - filter: { - ...state.filter, - ...getEmptyVisibleRows(), - }, - })); - - items.forEach((filterItem) => { - const hasAppliedFilter = apiRef.current.applyFilter(filterItem, linkOperator); - hasAppliedAtLeastOneFilter = hasAppliedAtLeastOneFilter || hasAppliedFilter; - }); + if (props.filterMode === GridFeatureModeConstant.server) { + forceUpdate(); + return; } - // If no filter has been applied, we set all rows to be visible - if (!hasAppliedAtLeastOneFilter) { - setGridState((state) => { - const rowIds = gridSortedRowIdsFlatSelector(state); - const visibleRowsLookup = Object.fromEntries(rowIds.map((rowId) => [rowId, true])); - const visibleRows = [...rowIds]; + // Clearing filtered rows + setGridState((state) => ({ + ...state, + filter: { + ...state.filter, + visibleRowsLookup: {}, + visibleRows: gridSortedRowIdsSelector(state), + }, + })); - return { - ...state, - filter: { - ...state.filter, - visibleRowsLookup, - visibleRows, - visibleRowCount: gridExpandedRowCountSelector(state), - visibleTopLevelRowCount: gridTopLevelRowCountSelector(state), - }, - }; - }); - } + const { items, linkOperator } = gridFilterModelSelector(apiRef.current.state); - apiRef.current.publishEvent(GridEvents.visibleRowsSet); + items.forEach((filterItem) => { + apiRef.current.applyFilter(filterItem, linkOperator); + }); forceUpdate(); }, [apiRef, setGridState, forceUpdate, props.filterMode]); @@ -361,7 +304,7 @@ export const useGridFilter = ( ); const getVisibleRowModels = React.useCallback( - () => gridSortedVisibleRowsSelector(apiRef.current.state), + () => gridSortedVisibleRowsMapSelector(apiRef.current.state), [apiRef], ); diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts index 04fa264c25176..246e3d682324c 100644 --- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts +++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts @@ -11,7 +11,7 @@ import { import { isEnterKey, isNavigationKey, isSpaceKey } from '../../../utils/keyboardUtils'; import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; import { GridCellModes } from '../../../models/gridEditRowModel'; -import { gridSortedVisibleRowsAsArrayFlatSelector } from '../filter'; +import { gridSortedVisibleRowEntriesSelector } from '../filter'; /** * @requires useGridSelection (method) @@ -37,8 +37,7 @@ export const useGridKeyboard = (apiRef: GridApiRef): void => { )! as HTMLElement; const startRowIndex = Number(rowEl.getAttribute('data-rowindex')); - const startId = gridSortedVisibleRowsAsArrayFlatSelector(apiRef.current.state)[startRowIndex] - .id; + const startId = gridSortedVisibleRowEntriesSelector(apiRef.current.state)[startRowIndex].id; if (startId === focusCell.id) { return; diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts index dd409d698e6cc..93e7b5c545cd0 100644 --- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts +++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts @@ -23,7 +23,7 @@ import { useGridLogger } from '../../utils/useGridLogger'; import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; import { GridComponentProps } from '../../../GridComponentProps'; import { - gridSortedVisibleRowsAsArrayFlatSelector, + gridSortedVisibleRowEntriesSelector, gridVisibleRowCountSelector, } from '../filter/gridFilterSelector'; @@ -81,7 +81,7 @@ export const useGridKeyboardNavigation = ( const totalVisibleRowCount = useGridSelector(apiRef, gridVisibleRowCountSelector); const colCount = useGridSelector(apiRef, visibleGridColumnsLengthSelector); const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); - const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowsAsArrayFlatSelector); + const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowEntriesSelector); const mapKey = (event: React.KeyboardEvent) => { if (isEnterKey(event.key)) { diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts index 82596c82c9758..41f6f9cf0e092 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts @@ -1,6 +1,6 @@ import { createSelector } from 'reselect'; import { GridState } from '../core/gridState'; -import { gridSortedVisibleRowsAsArrayFlatSelector } from '../filter/gridFilterSelector'; +import { gridSortedVisibleRowEntriesSelector } from '../filter/gridFilterSelector'; import { GridPaginationState } from './gridPaginationState'; export const gridPaginationSelector = (state: GridState): GridPaginationState => state.pagination; @@ -17,7 +17,7 @@ export const gridPageSizeSelector = createSelector( export const gridSortedVisiblePaginatedRowsAsArrayFlatSelector = createSelector( gridPaginationSelector, - gridSortedVisibleRowsAsArrayFlatSelector, + gridSortedVisibleRowEntriesSelector, (pagination, visibleSortedRows) => { const firstSelectedRowIndex = pagination.page * pagination.pageSize; const lastSelectedRowIndex = firstSelectedRowIndex + pagination.pageSize; diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts index 8c7df376e44c5..2fb6faf328a49 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts @@ -1,5 +1,4 @@ import { createSelector } from 'reselect'; -import { GridRowId, GridRowConfigTree } from '../../../models/gridRows'; import { GridState } from '../core/gridState'; export const gridRowsStateSelector = (state: GridState) => state.rows; @@ -19,57 +18,16 @@ export const gridRowsLookupSelector = createSelector( (rows) => rows.idRowsLookup, ); -export const gridRowsPathSelector = createSelector(gridRowsStateSelector, (rows) => rows.paths); - export const gridRowTreeSelector = createSelector(gridRowsStateSelector, (rows) => rows.tree); -export const gridRowExpandedTreeSelector = createSelector(gridRowTreeSelector, (rowsTree) => { - const removeCollapsedNodes = (tree: GridRowConfigTree) => { - const treeWithoutCollapsedChildren: GridRowConfigTree = new Map(); - - tree.forEach((node, id) => { - const children: GridRowConfigTree | undefined = - node.expanded && node.children ? removeCollapsedNodes(node.children) : undefined; - - treeWithoutCollapsedChildren.set(id, { - ...node, - children, - }); - }); - - return treeWithoutCollapsedChildren; - }; - - return removeCollapsedNodes(rowsTree); -}); - -export const gridExpandedRowCountSelector = createSelector( - gridRowExpandedTreeSelector, - (expandedRows) => { - const countNodes = (tree: GridRowConfigTree) => { - let count: number = 0; - - tree.forEach((node) => { - count += 1; - - if (node.children) { - count += countNodes(node.children); - } - }); - - return count; - }; - - return countNodes(expandedRows); - }, +export const gridRowExpandedTreeSelector = createSelector(gridRowTreeSelector, (rowsTree) => + Object.fromEntries( + Object.entries(rowsTree).filter( + ([, value]) => value.parent == null || rowsTree[value.parent]?.expanded, + ), + ), ); -export const gridRowIdsFlatSelector = createSelector(gridRowTreeSelector, (tree) => { - const flattenRowIds = (nodes: GridRowConfigTree): GridRowId[] => - Array.from(nodes.values()).flatMap((node) => [ - node.id, - ...(node.children ? flattenRowIds(node.children) : []), - ]); - - return flattenRowIds(tree); -}); +export const gridRowIdsSelector = createSelector(gridRowTreeSelector, (rowsTree) => + Object.values(rowsTree).map((node) => node.id), +); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts index fc8bcafec3169..9ef739049eda4 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts @@ -1,14 +1,9 @@ -import { GridRowId, GridRowConfigTree, GridRowsLookup } from '../../../models/gridRows'; +import { GridRowConfigTree, GridRowsLookup } from '../../../models/gridRows'; export interface GridRowsState { idRowsLookup: GridRowsLookup; tree: GridRowConfigTree; - /** - * Path in the tree to access a given row - */ - paths: Record; - /** * Amount of rows before applying the filtering * It also count the expanded and collapsed children rows diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts index 0cb01b29fe50c..72e8f7b7a0050 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts @@ -1,54 +1,81 @@ -import type { GridRowConfigTree, GridRowId, GridRowsLookup } from '../../../models'; +import type { + GridRowConfigTree, + GridRowConfigTreeNode, + GridRowId, + GridRowsLookup, +} from '../../../models'; + +export type GridNodeNameToIdTreeNode = { id: GridRowId; children: GridNodeNameToIdTree }; +export type GridNodeNameToIdTree = Map; export const insertLeafInTree = ({ tree, path, id, defaultGroupingExpansionDepth, - paths, idRowsLookup, + nodeNameToIdTree, }: { tree: GridRowConfigTree; path: string[]; id: GridRowId; defaultGroupingExpansionDepth: number; - paths: Record; idRowsLookup: GridRowsLookup; + nodeNameToIdTree: GridNodeNameToIdTree; }) => { - let subTree = tree; + let nodeNameToIdSubTree = nodeNameToIdTree; + let parentNode: GridRowConfigTreeNode | null = null; + + for (let depth = 0; depth < path.length; depth += 1) { + const nodeName = path[depth]; + let nodeId: GridRowId; + + const expanded = defaultGroupingExpansionDepth > depth; - path.forEach((nodeName, index) => { - if (index < path.length - 1) { - let parentNode = subTree.get(nodeName); + let nodeNameConfig = nodeNameToIdSubTree.get(nodeName); - if (!parentNode) { - const fillerPath = path.slice(0, index + 1); - const fillerId = `filler-row-${fillerPath.join('-')}`; - const childrenTree: GridRowConfigTree = new Map(); + if (!nodeNameConfig) { + nodeId = depth === path.length - 1 ? id : `filler-row-${path.slice(0, depth + 1).join('-')}`; + + nodeNameConfig = { id: nodeId, children: new Map() }; + nodeNameToIdSubTree.set(nodeName, nodeNameConfig); + } else { + nodeId = nodeNameConfig.id; + } + nodeNameToIdSubTree = nodeNameConfig.children; - parentNode = { - id: fillerId, + if (depth < path.length - 1) { + let node = tree[nodeId] ?? null; + if (!node) { + node = { + id: nodeId, fillerNode: true, - expanded: defaultGroupingExpansionDepth > index, - children: childrenTree, + expanded, + children: [], + parent: parentNode?.id ?? null, + depth, }; - subTree.set(nodeName, parentNode); - idRowsLookup[fillerId] = {}; - paths[fillerId] = fillerPath; + tree[nodeId] = node; + idRowsLookup[nodeId] = {}; } + } else { + tree[id] = { + id, + expanded: defaultGroupingExpansionDepth > depth, + parent: parentNode?.id ?? null, + depth, + }; + } + if (parentNode != null) { if (!parentNode.children) { - parentNode.children = new Map(); + parentNode.children = []; } - subTree = parentNode!.children!; - } else { - subTree.set(nodeName, { - id, - expanded: defaultGroupingExpansionDepth > index, - }); - paths[id] = path; + parentNode.children.push(nodeId); } - }); + + parentNode = tree[nodeId]!; + } }; diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 46b654dbda0a4..eb9b0318ccef3 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -10,8 +10,6 @@ import { GridRowsProp, GridRowIdGetter, GridRowData, - GridRowConfigTreeNode, - GridRowConfigTree, } from '../../../models/gridRows'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; @@ -22,14 +20,13 @@ import { gridRowCountSelector, gridRowsLookupSelector, gridRowTreeSelector, - gridRowIdsFlatSelector, - gridRowsPathSelector, + gridRowIdsSelector, } from './gridRowsSelector'; import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; export type GridRowsInternalCacheState = Omit< GridRowsState, - 'tree' | 'paths' | 'totalRowCount' | 'totalTopLevelRowCount' + 'tree' | 'totalRowCount' | 'totalTopLevelRowCount' > & { rowIds: GridRowId[]; @@ -86,64 +83,6 @@ export function convertGridRowsPropToState( return state; } -const getNodeFromTree = (tree: GridRowConfigTree, path: string[]): GridRowConfigTreeNode | null => { - if (path.length === 0) { - return null; - } - - const [nodeName, ...restPath] = path; - const node = tree.get(nodeName); - - if (!node) { - return null; - } - - if (restPath.length === 0) { - return node; - } - - if (!node.children) { - return null; - } - - return getNodeFromTree(node.children, restPath); -}; - -const setRowExpansionInTree = ( - id: GridRowId, - tree: GridRowConfigTree, - path: string[], - isExpanded: boolean, -) => { - if (path.length === 0) { - throw new Error(`MUI: Invalid path for row #${id}.`); - } - - const clonedMap = new Map(tree.entries()); - const [nodeName, ...restPath] = path; - const nodeBefore = clonedMap.get(nodeName); - - if (!nodeBefore) { - throw new Error(`MUI: Invalid path for row #${id}.`); - } - - if (restPath.length === 0) { - clonedMap.set(nodeName, { ...nodeBefore, expanded: isExpanded }); - } else { - if (!nodeBefore.children) { - throw new Error(`MUI: Invalid path for row #${id}.`); - } - - clonedMap.set(nodeName, { - ...nodeBefore, - expanded: nodeBefore.expanded || isExpanded, - children: setRowExpansionInTree(id, nodeBefore.children, restPath, isExpanded), - }); - } - - return clonedMap; -}; - const getRowsStateFromCache = ( rowsCache: GridRowsInternalCache, apiRef: GridApiRef, @@ -155,9 +94,12 @@ const getRowsStateFromCache = ( ids: rowIds, }); + const dataTopLevelRowCount = Object.values(groupingResponse.tree).filter( + (node) => node.parent == null, + ).length; const totalRowCount = propRowCount > rowIds.length ? propRowCount : rowIds.length; const totalTopLevelRowCount = - propRowCount > groupingResponse.tree.size ? propRowCount : groupingResponse.tree.size; + propRowCount > dataTopLevelRowCount ? propRowCount : dataTopLevelRowCount; return { ...rowState, ...groupingResponse, totalRowCount, totalTopLevelRowCount }; }; @@ -201,13 +143,13 @@ export const useGridRows = ( // TODO: Move in useGridSorting const getRowIndex = React.useCallback( - (id) => apiRef.current.getFlatSortedRowIds().indexOf(id), + (id) => apiRef.current.getSortedRowIds().indexOf(id), [apiRef], ); // TODO: Move in useGridSorting const getRowIdFromRowIndex = React.useCallback( - (index) => apiRef.current.getFlatSortedRowIds()[index], + (index) => apiRef.current.getSortedRowIds()[index], [apiRef], ); @@ -320,7 +262,7 @@ export const useGridRows = ( ); const getRowModels = React.useCallback(() => { - const allRows = gridRowIdsFlatSelector(apiRef.current.state); + const allRows = gridRowIdsSelector(apiRef.current.state); const idRowsLookup = gridRowsLookupSelector(apiRef.current.state); return new Map(allRows.map((id) => [id, idRowsLookup[id]])); @@ -332,20 +274,27 @@ export const useGridRows = ( ); const getAllRowIds = React.useCallback( - () => gridRowIdsFlatSelector(apiRef.current.state), + () => gridRowIdsSelector(apiRef.current.state), [apiRef], ); const setRowExpansion = React.useCallback( (id, isExpanded) => { setGridState((state) => { - const path = state.rows.paths[id]; + const node = state.rows.tree[id]; + + if (!node) { + throw new Error(`MUI: No row with id #${id} found`); + } + + const newTree = { ...state.rows.tree }; + newTree[id] = { ...node, expanded: isExpanded }; return { ...state, rows: { ...state.rows, - tree: setRowExpansionInTree(id, state.rows.tree, path, isExpanded), + tree: newTree, }, }; }); @@ -355,21 +304,8 @@ export const useGridRows = ( [apiRef, setGridState, forceUpdate], ); - const getRowPath = React.useCallback( - (id) => gridRowsPathSelector(apiRef.current.state)[id] ?? null, - [apiRef], - ); - const getRowNode = React.useCallback( - (id) => { - const path = apiRef.current.UNSTABLE_getRowPath(id); - - if (!path) { - throw new Error(`MUI: No row with id #${id} found in row path list`); - } - - return getNodeFromTree(gridRowTreeSelector(apiRef.current.state), path); - }, + (id) => gridRowTreeSelector(apiRef.current.state)[id] ?? null, [apiRef], ); @@ -417,7 +353,6 @@ export const useGridRows = ( getAllRowIds, setRows, updateRows, - UNSTABLE_getRowPath: getRowPath, UNSTABLE_setRowExpansion: setRowExpansion, UNSTABLE_getRowNode: getRowNode, }; diff --git a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts index 59fc51b372ec6..c265b5006544a 100644 --- a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts +++ b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts @@ -15,7 +15,7 @@ import { selectedGridRowsSelector, selectedIdsLookupSelector, } from './gridSelectionSelector'; -import { gridSortedVisibleRowsAsArrayFlatSelector } from '../filter/gridFilterSelector'; +import { gridSortedVisibleRowEntriesSelector } from '../filter/gridFilterSelector'; import { GridCellParams } from '../../../models/params/gridCellParams'; import { GridRowSelectionCheckboxParams } from '../../../models/params/gridRowSelectionCheckboxParams'; import { GridColumnsPreProcessing } from '../../root/columnsPreProcessing'; @@ -183,7 +183,7 @@ export const useGridSelection = ( logger.debug(`Expanding selection from row ${startId} to row ${endId}`); - const visibleRowIds = gridSortedVisibleRowsAsArrayFlatSelector(apiRef.current.state); + const visibleRowIds = gridSortedVisibleRowEntriesSelector(apiRef.current.state); const startIndex = visibleRowIds.findIndex((el) => el.id === startId); const endIndex = visibleRowIds.findIndex((el) => el.id === endId); const [start, end] = startIndex > endIndex ? [endIndex, startIndex] : [startIndex, endIndex]; @@ -200,7 +200,7 @@ export const useGridSelection = ( const startId = lastRowToggled.current ?? id; const isSelected = apiRef.current.isRowSelected(id); if (isSelected) { - const visibleRowIds = gridSortedVisibleRowsAsArrayFlatSelector(apiRef.current.state); + const visibleRowIds = gridSortedVisibleRowEntriesSelector(apiRef.current.state); const startIndex = visibleRowIds.findIndex((row) => row.id === startId); const endIndex = visibleRowIds.findIndex((row) => row.id === endId); if (startIndex > endIndex) { diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts index 84c1baab2ceaa..1ff9886d03d25 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts @@ -1,9 +1,8 @@ import { createSelector } from 'reselect'; -import { GridRowId } from '../../../models/gridRows'; import { GridSortDirection, GridSortModel } from '../../../models/gridSortModel'; import { GridState } from '../core/gridState'; import { gridRowsLookupSelector } from '../rows/gridRowsSelector'; -import { GridSortedRowsIdTreeNode, GridSortedRowsTree } from './gridSortingState'; +import type { GridRowId, GridRowModel } from '../../../models'; const gridSortingStateSelector = (state: GridState) => state.sorting; @@ -12,35 +11,20 @@ export const gridSortedRowIdsSelector = createSelector( (sortingState) => sortingState.sortedRows, ); -export const gridSortedRowIdsFlatSelector = createSelector( - gridSortedRowIdsSelector, - (sortedRowIds) => { - const flattenRowIds = (nodes: GridSortedRowsIdTreeNode[]): GridRowId[] => - nodes.flatMap((node) => [node.id, ...(node.children ? flattenRowIds(node.children) : [])]); - - return flattenRowIds(sortedRowIds); - }, -); - -export const gridSortedRowsSelector = createSelector( +export const gridSortedRowEntriesSelector = createSelector( gridSortedRowIdsSelector, gridRowsLookupSelector, - (sortedTree, idRowsLookup) => { - const buildMap = (nodes: GridSortedRowsIdTreeNode[]) => { - const map: GridSortedRowsTree = new Map(); - - nodes.forEach((node) => { - map.set(node.id, { - model: idRowsLookup[node.id], - children: node.children ? buildMap(node.children) : undefined, - }); - }); + (sortedIds, idRowsLookup) => sortedIds.map((id) => ({ id, model: idRowsLookup[id] })), +); - return map; - }; +export const gridSortedRowsMapSelector = createSelector( + gridSortedRowEntriesSelector, + (sortedRows) => new Map(sortedRows.map((row) => [row.id, row.model])), +); - return buildMap(sortedTree); - }, +export const gridSortedRowsAsArraySelector = createSelector( + gridSortedRowsMapSelector, + (sortedRows) => Array.from(sortedRows.values()), ); export const gridSortModelSelector = createSelector( diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts index 239d638d8cf21..b3cbbc989cb15 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingState.ts @@ -1,19 +1,7 @@ -import { GridRowId, GridRowModel } from '../../../models/gridRows'; +import { GridRowId } from '../../../models/gridRows'; import { GridSortModel } from '../../../models/gridSortModel'; -export type GridSortedRowsTree = Map; - -export interface GridSortedRowsTreeNode { - model: GridRowModel; - children?: GridSortedRowsTree; -} - -export interface GridSortedRowsIdTreeNode { - id: GridRowId; - children?: GridSortedRowsIdTreeNode[]; -} - export interface GridSortingState { - sortedRows: GridSortedRowsIdTreeNode[]; + sortedRows: GridRowId[]; sortModel: GridSortModel; } diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 4e2fa9de568f3..a792595c3e323 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -7,7 +7,7 @@ import { GridCellValue } from '../../../models/gridCell'; import { GridColDef } from '../../../models/colDef/gridColDef'; import { GridFeatureModeConstant } from '../../../models/gridFeatureMode'; import { GridColumnHeaderParams } from '../../../models/params/gridColumnHeaderParams'; -import { GridRowId, GridRowConfigTree } from '../../../models/gridRows'; +import { GridRowId, GridRowConfigTreeNode } from '../../../models/gridRows'; import { GridFieldComparatorList, GridSortItem, @@ -24,12 +24,10 @@ import { allGridColumnsSelector } from '../columns/gridColumnsSelector'; import { useGridState } from '../core/useGridState'; import { gridSortedRowIdsSelector, - gridSortedRowIdsFlatSelector, - gridSortedRowsSelector, + gridSortedRowsAsArraySelector, gridSortModelSelector, } from './gridSortingSelector'; import { gridRowExpandedTreeSelector } from '../rows'; -import { GridSortedRowsIdTreeNode } from './gridSortingState'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; @@ -184,32 +182,68 @@ export const useGridSorting = ( const comparatorList = buildComparatorList(sortModel); const aggregatedComparator = comparatorListAggregate(comparatorList); - const sortRowTree = (tree: GridRowConfigTree, depth = 0): GridSortedRowsIdTreeNode[] => { - if (depth > 0 && props.disableChildrenSorting) { - return Array.from(tree.values()).map((value) => ({ - id: value.id, - children: value.children ? sortRowTree(value.children, depth + 1) : undefined, - })); + const sortRowList = (rowList: GridRowConfigTreeNode[]): GridRowConfigTreeNode[] => { + if (rowList.length === 0) { + return []; } - const rowsWithParams = Array.from(tree.entries()).map(([name, value]) => { + const depth = rowList[0].depth; + + if ((depth > 0 && props.disableChildrenSorting) || skipServerSorting) { + return rowList; + } + + const rowsWithParams = rowList.map((value) => { const params = comparatorList.map((colComparator) => getSortCellParams(value.id, colComparator.field), ); - return { value, name, params }; + return { value, params }; }); const sortedRowsWithParams = skipServerSorting ? rowsWithParams : rowsWithParams.sort((a, b) => aggregatedComparator(a.params, b.params)); - return sortedRowsWithParams.map((node) => ({ - id: node.value.id, - children: node.value.children ? sortRowTree(node.value.children, depth + 1) : undefined, - })); + return sortedRowsWithParams.map((row) => row.value); }; - const sortedRows = sortRowTree(unsortedRowTree); + + const groupedByParentRows = new Map([[null, []]]); + Object.values(unsortedRowTree).forEach((node) => { + let group = groupedByParentRows.get(node.parent); + if (!group) { + group = []; + groupedByParentRows.set(node.parent, group); + } + group.push(node); + }); + + groupedByParentRows.forEach((unsortedRows, parent) => { + groupedByParentRows.set(parent, sortRowList(unsortedRows)); + }); + + let sortedRows: GridRowId[] = []; + const insertRowListIntoSortedRows = (startIndex: number, rowList: GridRowConfigTreeNode[]) => { + sortedRows = [ + ...sortedRows.slice(0, startIndex), + ...rowList.map((row) => row.id), + ...sortedRows.slice(startIndex), + ]; + + let treeSize = 0; + rowList.forEach((row) => { + treeSize += 1; + const children = groupedByParentRows.get(row.id); + if (children?.length) { + const subTreeSize = insertRowListIntoSortedRows(startIndex + treeSize, children); + treeSize += subTreeSize; + } + }); + + return treeSize; + }; + + insertRowListIntoSortedRows(0, groupedByParentRows.get(null)!); setGridState((state) => ({ ...state, @@ -264,7 +298,7 @@ export const useGridSorting = ( ); const getSortedRows = React.useCallback( - () => gridSortedRowsSelector(apiRef.current.state), + () => gridSortedRowsAsArraySelector(apiRef.current.state), [apiRef], ); @@ -273,16 +307,10 @@ export const useGridSorting = ( [apiRef], ); - const getFlatSortedRowIds = React.useCallback( - () => gridSortedRowIdsFlatSelector(apiRef.current.state), - [apiRef], - ); - const sortApi: GridSortApi = { getSortModel, getSortedRows, getSortedRowIds, - getFlatSortedRowIds, setSortModel, sortColumn, applySorting, diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index cd6f5b9c658a3..65a5090c80f42 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -10,7 +10,7 @@ import { GridCellParams, GridColDef, MuiEvent } from '../../../models'; import { isSpaceKey } from '../../../utils/keyboardUtils'; import { useFirstRender } from '../../utils/useFirstRender'; import { GridRowGroupingPreProcessing } from '../../root/rowGroupsPerProcessing'; -import { insertLeafInTree } from '../rows/gridRowsUtils'; +import { GridNodeNameToIdTree, insertLeafInTree } from '../rows/gridRowsUtils'; /** * Only available in DataGridPro @@ -60,24 +60,24 @@ export const useGridTreeData = ( })) .sort((a, b) => a.path.length - b.path.length); - const tree: GridRowConfigTree = new Map(); - const paths: Record = {}; + const tree: GridRowConfigTree = {}; const idRowsLookup: GridRowsLookup = { ...params.idRowsLookup }; + const nodeNameToIdTree: GridNodeNameToIdTree = new Map(); + rows.forEach((row) => { insertLeafInTree({ tree, path: row.path, id: row.id, defaultGroupingExpansionDepth: props.defaultGroupingExpansionDepth, - paths, idRowsLookup, + nodeNameToIdTree, }); }); return { tree, - paths, idRowsLookup, }; }; diff --git a/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts index 9042056bb08eb..dde1181f8bb2d 100644 --- a/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts @@ -7,7 +7,6 @@ export type RwoGroupParams = { export interface GridRowGroupingResult { tree: GridRowConfigTree; - paths: Record; idRowsLookup: GridRowsLookup; } diff --git a/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts index 59c530f91f38f..588424b99a7b1 100644 --- a/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts +++ b/packages/grid/_modules_/grid/hooks/root/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts @@ -9,8 +9,7 @@ import { GridEvents } from '../../../constants/eventsConstants'; import { useGridApiMethod } from '../useGridApiMethod'; const getFlatRowTree: GridRowGroupingPreProcessing = (params) => ({ - tree: new Map(params.ids.map((id) => [id.toString(), { id, depth: 0 }])), - paths: Object.fromEntries(params.ids.map((id) => [id, [id.toString()]])), + tree: Object.fromEntries(params.ids.map((id) => [id.toString(), { id, depth: 0, parent: null }])), idRowsLookup: params.idRowsLookup, }); diff --git a/packages/grid/_modules_/grid/models/api/gridFilterApi.ts b/packages/grid/_modules_/grid/models/api/gridFilterApi.ts index 5cc3d259fbaa9..e3e8a7b047d26 100644 --- a/packages/grid/_modules_/grid/models/api/gridFilterApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridFilterApi.ts @@ -24,9 +24,8 @@ export interface GridFilterApi { * Applies a [[GridFilterItem]] on all rows. If no `linkOperator` is given, the "and" operator is used. * @param {GridFilterItem} item The filter to be applied. * @param {GridLinkOperator} linkOperator The link operator to use. - * @returns {boolean} Returns `true` if the filter has been applied */ - applyFilter: (item: GridFilterItem, linkOperator?: GridLinkOperator) => boolean; + applyFilter: (item: GridFilterItem, linkOperator?: GridLinkOperator) => void; /** * Applies all filters on all rows. */ diff --git a/packages/grid/_modules_/grid/models/api/gridRowApi.ts b/packages/grid/_modules_/grid/models/api/gridRowApi.ts index dff8f0ab7486a..23569d69ee6e0 100644 --- a/packages/grid/_modules_/grid/models/api/gridRowApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridRowApi.ts @@ -61,11 +61,4 @@ export interface GridRowApi { * @ignore - do not document. */ UNSTABLE_setRowExpansion: (id: GridRowId, isExpanded: boolean) => void; - /** - * Gets the row path in the tree with a given id. - * @param {GridRowId} id the ID of the row to toggle. - * @returns {string[] | null} path The path of the row. - * @ignore - do not document. - */ - UNSTABLE_getRowPath: (id: GridRowId) => string[] | null; } diff --git a/packages/grid/_modules_/grid/models/api/gridSortApi.ts b/packages/grid/_modules_/grid/models/api/gridSortApi.ts index 154ac1b07c529..39d45f8126e43 100644 --- a/packages/grid/_modules_/grid/models/api/gridSortApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridSortApi.ts @@ -1,10 +1,6 @@ import { GridColDef } from '../colDef/gridColDef'; -import { GridRowId } from '../gridRows'; +import { GridRowId, GridRowModel } from '../gridRows'; import { GridSortDirection, GridSortModel } from '../gridSortModel'; -import { - GridSortedRowsIdTreeNode, - GridSortedRowsTree, -} from '../../hooks/features/sorting/gridSortingState'; /** * The sort API interface that is available in the grid [[apiRef]]. @@ -37,17 +33,12 @@ export interface GridSortApi { ) => void; /** * Returns all rows sorted according to the active sort model. - * @returns {GridSortedRowsTree} The sorted [[GridRowModel]] objects. + * @returns {GridRowModel[]} The sorted [[GridRowModel]] objects. */ - getSortedRows: () => GridSortedRowsTree; - /** - * Returns all row ids sorted according to the active sort model. - * @returns {GridSortedRowsIdTreeNode[]} The sorted [[GridRowId]] values. - */ - getSortedRowIds: () => GridSortedRowsIdTreeNode[]; + getSortedRows: () => GridRowModel[]; /** * Returns all row ids sorted according to the active sort model. * @returns {GridRowId[]} The sorted [[GridRowId]] values. */ - getFlatSortedRowIds: () => GridRowId[]; + getSortedRowIds: () => GridRowId[]; } diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index caf783b9b37aa..5d756c5f12b05 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -14,9 +14,11 @@ export interface GridRowModelUpdate extends GridRowData { export interface GridRowConfigTreeNode { id: GridRowId; - children?: GridRowConfigTree; + children?: GridRowId[]; + parent: GridRowId | null; descendantsCount?: number; expanded?: boolean; + depth: number; /** * If `true`, this node has been automatically added to fill a gap in the tree structure @@ -24,10 +26,12 @@ export interface GridRowConfigTreeNode { fillerNode?: boolean; } -export type GridRowConfigTree = Map; +export type GridRowConfigTree = Record; export type GridRowsLookup = Record; +export type GridRowEntry = { id: GridRowId; model: GridRowModel }; + /** * The type of Id supported by the grid. */ diff --git a/packages/grid/x-grid-data-generator/src/useDemoTreeData.ts b/packages/grid/x-grid-data-generator/src/useDemoTreeData.ts index 4b0e017aec8dd..fdee192368b9c 100644 --- a/packages/grid/x-grid-data-generator/src/useDemoTreeData.ts +++ b/packages/grid/x-grid-data-generator/src/useDemoTreeData.ts @@ -4,6 +4,7 @@ import { DataGridProProps, GridColumns, GridRowModel } from '@mui/x-data-grid-pr interface GridDemoTreeDataOptions { rowLength: number[]; + randomLength: boolean; } interface GridDemoTreeData extends GridDemoData, Pick {} @@ -33,16 +34,24 @@ interface GetTreeDataRowsOptions { rowLength: number[]; parentPath?: string[]; id?: number; + randomLength: boolean; } -const getTreeDataRows = ({ rowLength, parentPath = [], id = 0 }: GetTreeDataRowsOptions) => { +const getTreeDataRows = ({ + rowLength, + parentPath = [], + id = 0, + randomLength, +}: GetTreeDataRowsOptions) => { const rows: GridRowModel[] = []; const [currentDepthRowLength, ...restRowLength] = rowLength; - // For top level rows we respect the exact length given, for deeper rows we add a random factor + // For top level rows we respect the exact length given, for deeper rows we add a random factor is randomLength = true const realCurrentDepthRowLength = - parentPath.length === 0 ? currentDepthRowLength : randomInt(0, currentDepthRowLength * 2); + parentPath.length === 0 || !randomLength + ? currentDepthRowLength + : randomInt(0, currentDepthRowLength * 2); for (let index = 1; index < realCurrentDepthRowLength + 1; index += 1) { const path = [...parentPath, index.toString()]; @@ -52,7 +61,12 @@ const getTreeDataRows = ({ rowLength, parentPath = [], id = 0 }: GetTreeDataRows id += 1; if (restRowLength.length) { - const childrenRows = getTreeDataRows({ rowLength: restRowLength, parentPath: path, id }); + const childrenRows = getTreeDataRows({ + rowLength: restRowLength, + parentPath: path, + id, + randomLength, + }); rows.push(...childrenRows.rows); id = childrenRows.id; @@ -74,10 +88,11 @@ export const useDemoTreeData = (options: GridDemoTreeDataOptions) => { setResponse((prev) => (prev.loading ? prev : { ...prev, loading: true })); const { rows } = getTreeDataRows({ rowLength: rowLengthStr.split('-').map((el) => Number(el)), + randomLength: options.randomLength, }); setResponse((prev) => ({ ...prev, loading: false, data: { ...prev.data, rows } })); - }, [rowLengthStr]); + }, [rowLengthStr, options.randomLength]); return response; }; diff --git a/packages/storybook/src/stories/grid-tree-data.stories.tsx b/packages/storybook/src/stories/grid-tree-data.stories.tsx index fb5a3af19cf1a..91d23e3f17168 100644 --- a/packages/storybook/src/stories/grid-tree-data.stories.tsx +++ b/packages/storybook/src/stories/grid-tree-data.stories.tsx @@ -13,7 +13,7 @@ export default { } as Meta; export function BasicTreeData() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3], randomLength: true }); const [treeDataEnabled, setTreeDataEnabled] = React.useState(true); return ( @@ -32,7 +32,7 @@ export function BasicTreeData() { } export function CustomGroupingColumn() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3], randomLength: true }); const groupingColDef = React.useMemo( () => ({ @@ -53,13 +53,13 @@ export function CustomGroupingColumn() { } export function TreeDataWithCheckboxSelection() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3], randomLength: true }); return ; } export function TreeDataPagination() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3], randomLength: true }); return (
@@ -78,7 +78,7 @@ export function TreeDataPagination() { } export function TreeDataToolbar() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3] }); + const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3], randomLength: true }); return ( Date: Tue, 12 Oct 2021 16:43:54 +0200 Subject: [PATCH 180/390] Merge --- .../grid/components/GridViewport.tsx | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/grid/_modules_/grid/components/GridViewport.tsx b/packages/grid/_modules_/grid/components/GridViewport.tsx index 6c5ecc0e81bf2..ac48f57a8a405 100644 --- a/packages/grid/_modules_/grid/components/GridViewport.tsx +++ b/packages/grid/_modules_/grid/components/GridViewport.tsx @@ -2,7 +2,10 @@ import * as React from 'react'; import { visibleGridColumnsSelector } from '../hooks/features/columns/gridColumnsSelector'; import { useGridSelector } from '../hooks/features/core/useGridSelector'; import { gridDensityRowHeightSelector } from '../hooks/features/density/densitySelector'; -import { gridSortedVisibleRowEntriesSelector } from '../hooks/features/filter/gridFilterSelector'; +import { + gridSortedVisibleRowEntriesSelector, + gridSortedVisibleTopLevelRowEntriesSelector, +} from '../hooks/features/filter/gridFilterSelector'; import { gridFocusCellSelector, gridTabIndexCellSelector, @@ -20,6 +23,7 @@ import { gridScrollBarSizeSelector, } from '../hooks/root/gridContainerSizesSelector'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; +import { GridRowId } from '../models'; type ViewportType = React.ForwardRefExoticComponent>; @@ -36,6 +40,10 @@ export const GridViewport: ViewportType = React.forwardRef( const cellTabIndex = useGridSelector(apiRef, gridTabIndexCellSelector); const selection = useGridSelector(apiRef, gridSelectionStateSelector); const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowEntriesSelector); + const visibleSortedTopLevelRows = useGridSelector( + apiRef, + gridSortedVisibleTopLevelRowEntriesSelector, + ); const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector); const editRowsState = useGridSelector(apiRef, gridEditRowsStateSelector); @@ -61,10 +69,19 @@ export const GridViewport: ViewportType = React.forwardRef( return null; } - const renderedRows = visibleSortedRows.slice( - renderState.renderContext.firstRowIdx, - renderState.renderContext.lastRowIdx!, + const getVisibleRowIndex = (id: GridRowId) => + visibleSortedRows.findIndex((row) => row.id === id); + + const startIndex = getVisibleRowIndex( + visibleSortedTopLevelRows[renderState.renderContext.firstRowIdx!].id, ); + const isLastTopLevelRowVisible = + renderState.renderContext.lastRowIdx! > visibleSortedTopLevelRows.length; + const endIndex = isLastTopLevelRowVisible + ? visibleSortedRows.length - 1 + : getVisibleRowIndex(visibleSortedTopLevelRows[renderState.renderContext.lastRowIdx!].id); + + const renderedRows = visibleSortedRows.slice(startIndex, endIndex); const renderedColumns = visibleColumns.slice( renderState.renderContext.firstColIdx!, From 43b31b9fb6ab9dc90fcbf966be5e80322276e4ac Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Wed, 22 Sep 2021 23:36:49 -0300 Subject: [PATCH 181/390] [DataGrid] New virtualization implementation --- .../pages/api-docs/data-grid/data-grid-pro.md | 5 - docs/pages/api-docs/data-grid/data-grid.md | 5 - docs/scripts/generateProptypes.ts | 1 + .../grid/components/GridRenderingZone.tsx | 59 --- .../_modules_/grid/components/GridRow.tsx | 92 ++-- .../grid/components/GridScrollArea.tsx | 7 +- .../grid/components/GridStickyContainer.tsx | 60 --- .../grid/components/GridViewport.tsx | 107 ----- .../components/GridVirtualizedContainer.tsx | 331 ++++++++++++++ .../grid/components/base/GridBody.tsx | 54 ++- .../grid/components/cell/GridCell.tsx | 6 +- .../grid/components/cell/GridEmptyCell.tsx | 59 --- .../_modules_/grid/components/cell/index.ts | 1 - .../columnHeaders/GridColumnHeaderItem.tsx | 60 +-- .../columnHeaders/GridColumnHeaders.tsx | 193 ++++++-- .../GridColumnHeadersItemCollection.tsx | 2 +- .../containers/GridDataContainer.tsx | 40 -- .../components/containers/GridRootStyles.ts | 23 +- .../grid/components/containers/GridWindow.tsx | 93 ---- .../grid/components/containers/index.ts | 2 - .../grid/_modules_/grid/components/index.ts | 3 - packages/grid/_modules_/grid/gridClasses.ts | 19 +- .../columnReorder/useGridColumnReorder.tsx | 3 +- .../infiniteLoader/useGridInfiniteLoader.ts | 13 +- .../hooks/features/scroll/useGridScroll.ts | 18 +- .../hooks/features/virtualization/index.ts | 1 - .../virtualization/useGridNoVirtualization.ts | 89 ---- .../virtualization/useGridVirtualization.ts | 418 ------------------ .../_modules_/grid/models/gridOptions.tsx | 24 +- packages/grid/data-grid/src/DataGrid.tsx | 19 +- .../src/tests/keyboard.DataGrid.test.tsx | 8 +- .../src/tests/layout.DataGrid.test.tsx | 15 +- .../src/tests/pagination.DataGrid.test.tsx | 4 +- .../data-grid/src/useDataGridComponent.tsx | 6 - packages/grid/x-grid/src/DataGridPro.tsx | 19 +- .../tests/columnHeaders.DataGridPro.test.tsx | 4 +- .../src/tests/events.DataGridPro.test.tsx | 18 +- .../src/tests/rows.DataGridPro.test.tsx | 192 ++++---- .../x-grid/src/useDataGridProComponent.tsx | 6 - test/e2e/index.test.ts | 6 +- 40 files changed, 834 insertions(+), 1251 deletions(-) delete mode 100644 packages/grid/_modules_/grid/components/GridRenderingZone.tsx delete mode 100644 packages/grid/_modules_/grid/components/GridStickyContainer.tsx delete mode 100644 packages/grid/_modules_/grid/components/GridViewport.tsx create mode 100644 packages/grid/_modules_/grid/components/GridVirtualizedContainer.tsx delete mode 100644 packages/grid/_modules_/grid/components/cell/GridEmptyCell.tsx delete mode 100644 packages/grid/_modules_/grid/components/containers/GridDataContainer.tsx delete mode 100644 packages/grid/_modules_/grid/components/containers/GridWindow.tsx delete mode 100644 packages/grid/_modules_/grid/hooks/features/virtualization/useGridNoVirtualization.ts delete mode 100644 packages/grid/_modules_/grid/hooks/features/virtualization/useGridVirtualization.ts diff --git a/docs/pages/api-docs/data-grid/data-grid-pro.md b/docs/pages/api-docs/data-grid/data-grid-pro.md index 1fce187f49443..a9426a2ff5479 100644 --- a/docs/pages/api-docs/data-grid/data-grid-pro.md +++ b/docs/pages/api-docs/data-grid/data-grid-pro.md @@ -186,7 +186,6 @@ You can use the [slots API](/components/data-grid/components/#overriding-compone | columnSeparator--resizable | .MuiDataGrid-columnSeparator--resizable | Styles applied to the column header separator if the column is resizable. | | columnSeparator--resizing | .MuiDataGrid-columnSeparator--resizing | Styles applied to the column header separator if the column is being resized. | | columnSeparator | .MuiDataGrid-columnSeparator | Styles applied to the column header separator element. | -| dataContainer | .MuiDataGrid-dataContainer | Styles applied to the data container element. | | editBooleanCell | .MuiDataGrid-editBooleanCell | Styles applied to root of the boolean edit component. | | editInputCell | .MuiDataGrid-editInputCell | Styles applied to the root of the input component. | | filterIcon | .MuiDataGrid-filterIcon | Styles applied to the filter icon element. | @@ -198,7 +197,6 @@ You can use the [slots API](/components/data-grid/components/#overriding-compone | menuIconButton | .MuiDataGrid-menuIconButton | Styles applied to the menu icon button element. | | menuOpen | .MuiDataGrid-menuOpen | Styles applied to the menu icon element if the menu is open. | | overlay | .MuiDataGrid-overlay | Styles applied to the overlay element. | -| renderingZone | .MuiDataGrid-renderingZone | Styles applied to the rendering zone element. | | root | .MuiDataGrid-root | Styles applied to the root element. | | row--editable | .MuiDataGrid-row--editable | Styles applied to the row element if the row is editable. | | row--editing | .MuiDataGrid-row--editing | Styles applied to the row element if the row is in edit mode. | @@ -210,9 +208,6 @@ You can use the [slots API](/components/data-grid/components/#overriding-compone | selectedRowCount | .MuiDataGrid-selectedRowCount | Styles applied to the footer selected row count element. | | sortIcon | .MuiDataGrid-sortIcon | Styles applied to the sort icon element. | | toolbarContainer | .MuiDataGrid-toolbarContainer | Styles applied to the toolbar container element. | -| viewport | .MuiDataGrid-viewport | Styles applied to the viewport element. | -| window | .MuiDataGrid-window | Styles applied to the window element. | -| windowContainer | .MuiDataGrid-windowContainer | Styles applied to the window container element. | | withBorder | .MuiDataGrid-withBorder | Styles applied to both the cell and the column header if `showColumnRightBorder={true}`. | You can override the style of the component thanks to one of these customization points: diff --git a/docs/pages/api-docs/data-grid/data-grid.md b/docs/pages/api-docs/data-grid/data-grid.md index ea77c6a198cbe..6085416814343 100644 --- a/docs/pages/api-docs/data-grid/data-grid.md +++ b/docs/pages/api-docs/data-grid/data-grid.md @@ -174,7 +174,6 @@ You can use the [slots API](/components/data-grid/components/#overriding-compone | columnSeparator--resizable | .MuiDataGrid-columnSeparator--resizable | Styles applied to the column header separator if the column is resizable. | | columnSeparator--resizing | .MuiDataGrid-columnSeparator--resizing | Styles applied to the column header separator if the column is being resized. | | columnSeparator | .MuiDataGrid-columnSeparator | Styles applied to the column header separator element. | -| dataContainer | .MuiDataGrid-dataContainer | Styles applied to the data container element. | | editBooleanCell | .MuiDataGrid-editBooleanCell | Styles applied to root of the boolean edit component. | | editInputCell | .MuiDataGrid-editInputCell | Styles applied to the root of the input component. | | filterIcon | .MuiDataGrid-filterIcon | Styles applied to the filter icon element. | @@ -186,7 +185,6 @@ You can use the [slots API](/components/data-grid/components/#overriding-compone | menuIconButton | .MuiDataGrid-menuIconButton | Styles applied to the menu icon button element. | | menuOpen | .MuiDataGrid-menuOpen | Styles applied to the menu icon element if the menu is open. | | overlay | .MuiDataGrid-overlay | Styles applied to the overlay element. | -| renderingZone | .MuiDataGrid-renderingZone | Styles applied to the rendering zone element. | | root | .MuiDataGrid-root | Styles applied to the root element. | | row--editable | .MuiDataGrid-row--editable | Styles applied to the row element if the row is editable. | | row--editing | .MuiDataGrid-row--editing | Styles applied to the row element if the row is in edit mode. | @@ -198,9 +196,6 @@ You can use the [slots API](/components/data-grid/components/#overriding-compone | selectedRowCount | .MuiDataGrid-selectedRowCount | Styles applied to the footer selected row count element. | | sortIcon | .MuiDataGrid-sortIcon | Styles applied to the sort icon element. | | toolbarContainer | .MuiDataGrid-toolbarContainer | Styles applied to the toolbar container element. | -| viewport | .MuiDataGrid-viewport | Styles applied to the viewport element. | -| window | .MuiDataGrid-window | Styles applied to the window element. | -| windowContainer | .MuiDataGrid-windowContainer | Styles applied to the window container element. | | withBorder | .MuiDataGrid-withBorder | Styles applied to both the cell and the column header if `showColumnRightBorder={true}`. | You can override the style of the component thanks to one of these customization points: diff --git a/docs/scripts/generateProptypes.ts b/docs/scripts/generateProptypes.ts index 6788969b6dcab..1252293f4c8ad 100644 --- a/docs/scripts/generateProptypes.ts +++ b/docs/scripts/generateProptypes.ts @@ -25,6 +25,7 @@ async function generateProptypes(program: ttp.ts.Program, sourceFile: string) { 'renderedColumns', 'scrollBarState', 'renderState', + 'visibleColumns', 'cellFocus', 'cellTabIndex', ]; diff --git a/packages/grid/_modules_/grid/components/GridRenderingZone.tsx b/packages/grid/_modules_/grid/components/GridRenderingZone.tsx deleted file mode 100644 index 90a98c66a0122..0000000000000 --- a/packages/grid/_modules_/grid/components/GridRenderingZone.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { ElementSize } from '../models'; -import { getDataGridUtilityClass } from '../gridClasses'; -import { useGridRootProps } from '../hooks/utils/useGridRootProps'; -import { composeClasses } from '../utils/material-ui-utils'; -import { GridComponentProps } from '../GridComponentProps'; - -type WithChildren = { children?: React.ReactNode }; - -type OwnerState = { classes: GridComponentProps['classes'] }; - -const useUtilityClasses = (ownerState: OwnerState) => { - const { classes } = ownerState; - - const slots = { - root: ['renderingZone'], - }; - - return composeClasses(slots, getDataGridUtilityClass, classes); -}; - -const GridRenderingZone = React.forwardRef( - function GridRenderingZone(props, ref) { - const { height, width, children } = props; - const rootProps = useGridRootProps(); - const ownerState = { classes: rootProps.classes }; - const classes = useUtilityClasses(ownerState); - return ( -
- {children} -
- ); - }, -); - -GridRenderingZone.propTypes = { - // ----------------------------- Warning -------------------------------- - // | These PropTypes are generated from the TypeScript type definitions | - // | To update them edit the TypeScript types and run "yarn proptypes" | - // ---------------------------------------------------------------------- - /** - * The height of a container or HTMLElement. - */ - height: PropTypes.number.isRequired, - /** - * The width of a container or HTMLElement. - */ - width: PropTypes.number.isRequired, -} as any; - -export { GridRenderingZone }; diff --git a/packages/grid/_modules_/grid/components/GridRow.tsx b/packages/grid/_modules_/grid/components/GridRow.tsx index e1a1918870d15..d6f94fee26d68 100644 --- a/packages/grid/_modules_/grid/components/GridRow.tsx +++ b/packages/grid/_modules_/grid/components/GridRow.tsx @@ -11,31 +11,32 @@ import { composeClasses } from '../utils/material-ui-utils'; import { getDataGridUtilityClass, gridClasses } from '../gridClasses'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; import { GridComponentProps } from '../GridComponentProps'; +import { GridStateColDef } from '../models/colDef/gridColDef'; import { GridCellIdentifier } from '../hooks/features/focus/gridFocusState'; import { GridScrollBarState } from '../models/gridContainerProps'; -import { GridStateColDef } from '../models/colDef/gridColDef'; -import { GridEmptyCell } from './cell/GridEmptyCell'; -import { GridRenderingState } from '../hooks/features/virtualization/renderingState'; +import { gridColumnsMetaSelector } from '../hooks/features/columns/gridColumnsSelector'; +import { useGridSelector } from '../hooks/features/core/useGridSelector'; export interface GridRowProps { - id: GridRowId; + rowId: GridRowId; selected: boolean; index: number; rowHeight: number; + containerWidth: number; row: GridRowData; - renderState: GridRenderingState; firstColumnToRender: number; + lastColumnToRender: number; + visibleColumns: GridStateColDef[]; renderedColumns: GridStateColDef[]; - children: React.ReactNode; cellFocus: GridCellIdentifier | null; cellTabIndex: GridCellIdentifier | null; - editRowsModel: GridEditRowsModel; + editRowsState: GridEditRowsModel; scrollBarState: GridScrollBarState; onClick?: React.MouseEventHandler; onDoubleClick?: React.MouseEventHandler; } -type OwnerState = GridRowProps & { +type OwnerState = Pick & { editable: boolean; editing: boolean; classes?: GridComponentProps['classes']; @@ -51,33 +52,47 @@ const useUtilityClasses = (ownerState: OwnerState) => { return composeClasses(slots, getDataGridUtilityClass, classes); }; -function GridRow(props: GridRowProps) { +const EmptyCell = ({ width, height }) => { + if (!width || !height) { + return null; + } + + const style = { width, height }; + + return
; // TODO change to .MuiDataGrid-emptyCell or .MuiDataGrid-rowFiller +}; + +function GridRow(props: React.HTMLAttributes & GridRowProps) { const { selected, - id, + rowId, row, index, + style: styleProp, rowHeight, + className, + visibleColumns, renderedColumns, + containerWidth, firstColumnToRender, - children, + lastColumnToRender, cellFocus, cellTabIndex, - editRowsModel, + editRowsState, scrollBarState, // to be removed - renderState, // to be removed onClick, onDoubleClick, ...other } = props; - const ariaRowIndex = index + 2; // 1 for the header row and 1 as it's 1 based + const ariaRowIndex = index + 2; // 1 for the header row and 1 as it's 1-based const apiRef = useGridApiContext(); const rootProps = useGridRootProps(); + const columnsMeta = useGridSelector(apiRef, gridColumnsMetaSelector); const ownerState = { - ...props, + selected, classes: rootProps.classes, - editing: apiRef.current.getRowMode(id) === GridRowModes.Edit, + editing: apiRef.current.getRowMode(rowId) === GridRowModes.Edit, editable: rootProps.editMode === GridEditModes.Row, }; @@ -96,27 +111,28 @@ function GridRow(props: GridRowProps) { } // The row might have been deleted - if (!apiRef.current.getRow(id)) { + if (!apiRef.current.getRow(rowId)) { return; } - apiRef.current.publishEvent(eventName, apiRef.current.getRowParams(id), event); + apiRef.current.publishEvent(eventName, apiRef.current.getRowParams(rowId), event); if (propHandler) { propHandler(event); } }, - [apiRef, id], + [apiRef, rowId], ); const style = { maxHeight: rowHeight, minHeight: rowHeight, + ...styleProp, }; const rowClassName = typeof rootProps.getRowClassName === 'function' && - rootProps.getRowClassName(apiRef.current.getRowParams(id)); + rootProps.getRowClassName(apiRef.current.getRowParams(rowId)); const cells: JSX.Element[] = []; @@ -124,14 +140,14 @@ function GridRow(props: GridRowProps) { const column = renderedColumns[i]; const indexRelativeToAllColumns = firstColumnToRender + i; - const isLastColumn = indexRelativeToAllColumns === renderedColumns.length - 1; + const isLastColumn = indexRelativeToAllColumns === visibleColumns.length - 1; const removeLastBorderRight = isLastColumn && scrollBarState.hasScrollX && !scrollBarState.hasScrollY; const showRightBorder = !isLastColumn ? rootProps.showCellRightBorder : !removeLastBorderRight && rootProps.disableExtendRowFullWidth; - const cellParams = apiRef.current.getCellParams(id, column.field); + const cellParams = apiRef.current.getCellParams(rowId, column.field); const classNames: string[] = []; @@ -145,7 +161,7 @@ function GridRow(props: GridRowProps) { ); } - const editCellState = editRowsModel[id] && editRowsModel[id][column.field]; + const editCellState = editRowsState[rowId] ? editRowsState[rowId][column.field] : null; let content: React.ReactNode = null; if (editCellState == null && column.renderCell) { @@ -168,11 +184,12 @@ function GridRow(props: GridRowProps) { classNames.push(rootProps.getCellClassName(cellParams)); } - const hasFocus = cellFocus !== null && cellFocus.id === id && cellFocus.field === column.field; + const hasFocus = + cellFocus !== null && cellFocus.id === rowId && cellFocus.field === column.field; const tabIndex = cellTabIndex !== null && - cellTabIndex.id === id && + cellTabIndex.id === rowId && cellTabIndex.field === column.field && cellParams.cellMode === 'view' ? 0 @@ -180,11 +197,11 @@ function GridRow(props: GridRowProps) { cells.push( - {cells} - + {emptyCellWidth > 0 && }
); } @@ -230,19 +247,18 @@ GridRow.propTypes = { // ---------------------------------------------------------------------- cellFocus: PropTypes.object, cellTabIndex: PropTypes.object, - children: PropTypes.node, - editRowsModel: PropTypes.object.isRequired, + containerWidth: PropTypes.number.isRequired, + editRowsState: PropTypes.object.isRequired, firstColumnToRender: PropTypes.number.isRequired, - id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, index: PropTypes.number.isRequired, - onClick: PropTypes.func, - onDoubleClick: PropTypes.func, + lastColumnToRender: PropTypes.number.isRequired, renderedColumns: PropTypes.arrayOf(PropTypes.object).isRequired, - renderState: PropTypes.object.isRequired, row: PropTypes.object.isRequired, rowHeight: PropTypes.number.isRequired, + rowId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, scrollBarState: PropTypes.object.isRequired, selected: PropTypes.bool.isRequired, + visibleColumns: PropTypes.arrayOf(PropTypes.object).isRequired, } as any; export { GridRow }; diff --git a/packages/grid/_modules_/grid/components/GridScrollArea.tsx b/packages/grid/_modules_/grid/components/GridScrollArea.tsx index 450278420cb55..f8c4120e95560 100644 --- a/packages/grid/_modules_/grid/components/GridScrollArea.tsx +++ b/packages/grid/_modules_/grid/components/GridScrollArea.tsx @@ -8,6 +8,8 @@ import { getDataGridUtilityClass } from '../gridClasses'; import { composeClasses } from '../utils/material-ui-utils'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; import { GridComponentProps } from '../GridComponentProps'; +import { gridDensityHeaderHeightSelector } from '../hooks/features/density/densitySelector'; +import { useGridSelector } from '../hooks/features/core/useGridSelector'; const CLIFF = 1; const SLOP = 1.5; @@ -24,7 +26,7 @@ const useUtilityClasses = (ownerState: OwnerState) => { const { scrollDirection, classes } = ownerState; const slots = { - root: ['scrollArea', `scrollArea__${scrollDirection}`], + root: ['scrollArea', `scrollArea--${scrollDirection}`], }; return composeClasses(slots, getDataGridUtilityClass, classes); @@ -36,6 +38,7 @@ function GridScrollAreaRaw(props: ScrollAreaProps) { const apiRef = useGridApiContext(); const timeout = React.useRef(); const [dragging, setDragging] = React.useState(false); + const height = useGridSelector(apiRef, gridDensityHeaderHeightSelector); const scrollPosition = React.useRef({ left: 0, top: 0, @@ -90,7 +93,7 @@ function GridScrollAreaRaw(props: ScrollAreaProps) { useGridApiEventHandler(apiRef, GridEvents.columnHeaderDragEnd, toggleDragging); return dragging ? ( -
+
) : null; } diff --git a/packages/grid/_modules_/grid/components/GridStickyContainer.tsx b/packages/grid/_modules_/grid/components/GridStickyContainer.tsx deleted file mode 100644 index 4d3c10d8f0ddb..0000000000000 --- a/packages/grid/_modules_/grid/components/GridStickyContainer.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { getDataGridUtilityClass } from '../gridClasses'; -import { ElementSize } from '../models'; -import { useGridRootProps } from '../hooks/utils/useGridRootProps'; -import { composeClasses } from '../utils/material-ui-utils'; -import { GridComponentProps } from '../GridComponentProps'; - -interface GridStickyContainerProps extends ElementSize { - children: React.ReactNode; -} - -type OwnerState = { classes: GridComponentProps['classes'] }; - -const useUtilityClasses = (ownerState: OwnerState) => { - const { classes } = ownerState; - - const slots = { - root: ['viewport'], - }; - - return composeClasses(slots, getDataGridUtilityClass, classes); -}; - -function GridStickyContainer(props: GridStickyContainerProps) { - const { height, width, children } = props; - const rootProps = useGridRootProps(); - const ownerState = { classes: rootProps.classes }; - const classes = useUtilityClasses(ownerState); - return ( -
- {children} -
- ); -} - -GridStickyContainer.propTypes = { - // ----------------------------- Warning -------------------------------- - // | These PropTypes are generated from the TypeScript type definitions | - // | To update them edit the TypeScript types and run "yarn proptypes" | - // ---------------------------------------------------------------------- - children: PropTypes.node, - /** - * The height of a container or HTMLElement. - */ - height: PropTypes.number.isRequired, - /** - * The width of a container or HTMLElement. - */ - width: PropTypes.number.isRequired, -} as any; - -export { GridStickyContainer }; diff --git a/packages/grid/_modules_/grid/components/GridViewport.tsx b/packages/grid/_modules_/grid/components/GridViewport.tsx deleted file mode 100644 index 3189c0854aafc..0000000000000 --- a/packages/grid/_modules_/grid/components/GridViewport.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import * as React from 'react'; -import { visibleGridColumnsSelector } from '../hooks/features/columns/gridColumnsSelector'; -import { useGridSelector } from '../hooks/features/core/useGridSelector'; -import { gridDensityRowHeightSelector } from '../hooks/features/density/densitySelector'; -import { visibleSortedGridRowsAsArraySelector } from '../hooks/features/filter/gridFilterSelector'; -import { - gridFocusCellSelector, - gridTabIndexCellSelector, -} from '../hooks/features/focus/gridFocusStateSelector'; -import { gridEditRowsStateSelector } from '../hooks/features/rows/gridEditRowsSelector'; -import { gridSelectionStateSelector } from '../hooks/features/selection/gridSelectionSelector'; -import { gridRenderingSelector } from '../hooks/features/virtualization/renderingStateSelector'; -import { useGridApiContext } from '../hooks/root/useGridApiContext'; -import { GridDataContainer } from './containers/GridDataContainer'; -import { GridRenderingZone } from './GridRenderingZone'; -import { GridStickyContainer } from './GridStickyContainer'; -import { - gridContainerSizesSelector, - gridViewportSizesSelector, - gridScrollBarSizeSelector, -} from '../hooks/root/gridContainerSizesSelector'; -import { useGridRootProps } from '../hooks/utils/useGridRootProps'; - -type ViewportType = React.ForwardRefExoticComponent>; - -export const GridViewport: ViewportType = React.forwardRef( - function GridViewport(props, renderingZoneRef) { - const apiRef = useGridApiContext(); - const rootProps = useGridRootProps(); - const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); - const viewportSizes = useGridSelector(apiRef, gridViewportSizesSelector); - const scrollBarState = useGridSelector(apiRef, gridScrollBarSizeSelector); - const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); - const renderState = useGridSelector(apiRef, gridRenderingSelector); - const cellFocus = useGridSelector(apiRef, gridFocusCellSelector); - const cellTabIndex = useGridSelector(apiRef, gridTabIndexCellSelector); - const selection = useGridSelector(apiRef, gridSelectionStateSelector); - const visibleSortedRowsAsArray = useGridSelector(apiRef, visibleSortedGridRowsAsArraySelector); - const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector); - const editRowsState = useGridSelector(apiRef, gridEditRowsStateSelector); - - const filteredSelection = React.useMemo( - () => - typeof rootProps.isRowSelectable === 'function' - ? selection.filter((id) => rootProps.isRowSelectable!(apiRef.current.getRowParams(id))) - : selection, - [apiRef, rootProps.isRowSelectable, selection], - ); - - const selectionLookup = React.useMemo( - () => - filteredSelection.reduce((lookup, rowId) => { - lookup[rowId] = rowId; - return lookup; - }, {}), - [filteredSelection], - ); - - const getRowsElements = () => { - if (renderState.renderContext == null) { - return null; - } - - const renderedRows = visibleSortedRowsAsArray.slice( - renderState.renderContext.firstRowIdx, - renderState.renderContext.lastRowIdx!, - ); - - const renderedColumns = visibleColumns.slice( - renderState.renderContext.firstColIdx!, - renderState.renderContext.lastColIdx! + 1, - ); - - return renderedRows.map(([id, row], idx) => ( - - )); - }; - - return ( - - - - {getRowsElements()} - - - - ); - }, -); diff --git a/packages/grid/_modules_/grid/components/GridVirtualizedContainer.tsx b/packages/grid/_modules_/grid/components/GridVirtualizedContainer.tsx new file mode 100644 index 0000000000000..9751cf1ead03e --- /dev/null +++ b/packages/grid/_modules_/grid/components/GridVirtualizedContainer.tsx @@ -0,0 +1,331 @@ +import * as React from 'react'; +import clsx from 'clsx'; +import { useForkRef } from '@mui/material/utils'; +import { styled } from '@mui/material/styles'; +import { composeClasses } from '../utils/material-ui-utils'; +import { useGridRootProps } from '../hooks/utils/useGridRootProps'; +import { useGridApiContext } from '../hooks/root/useGridApiContext'; +import { useGridSelector } from '../hooks/features/core/useGridSelector'; +import { gridScrollBarSizeSelector } from '../hooks/root/gridContainerSizesSelector'; +import { + visibleGridColumnsSelector, + gridColumnsMetaSelector, +} from '../hooks/features/columns/gridColumnsSelector'; +import { + gridFocusCellSelector, + gridTabIndexCellSelector, +} from '../hooks/features/focus/gridFocusStateSelector'; +import { visibleSortedGridRowsAsArraySelector } from '../hooks/features/filter/gridFilterSelector'; +import { gridDensityRowHeightSelector } from '../hooks/features/density/densitySelector'; +import { gridEditRowsStateSelector } from '../hooks/features/rows/gridEditRowsSelector'; +import { GridEvents } from '../constants/eventsConstants'; +import { gridPaginationSelector } from '../hooks/features/pagination/gridPaginationSelector'; +import { useGridApiEventHandler } from '../hooks/root/useGridApiEventHandler'; +import { getDataGridUtilityClass } from '../gridClasses'; +import { GridComponentProps } from '../GridComponentProps'; +import { GridRowId } from '../models/gridRows'; + +type OwnerState = { classes: GridComponentProps['classes'] }; + +const useUtilityClasses = (ownerState: OwnerState) => { + const { classes } = ownerState; + + const slots = { + root: ['virtualizedContainer'], + renderingZone: ['renderingZone'], + content: ['virtualizedContainerContent'], + }; + + return composeClasses(slots, getDataGridUtilityClass, classes); +}; + +const VirtualizedContainerRoot = styled('div', { + name: 'MuiDataGrid', + slot: 'VirtualizedContainer', +})({ + overflow: 'auto', +}); + +const VirtualizedContainerContent = styled('div', { + name: 'MuiDataGrid', + slot: 'Content', +})({ + position: 'relative', + overflow: 'hidden', +}); + +const VirtualizedContainerRenderingZone = styled('div', { + name: 'MuiDataGrid', + slot: 'RenderingZone', +})({ + position: 'absolute', +}); + +// Uses binary search to avoid looping through all possible positions +export function getIndexFromScroll( + offset: number, + positions: number[], + sliceStart = 0, + sliceEnd = positions.length, +): number { + if (positions.length <= 0) { + return -1; + } + + if (sliceStart >= sliceEnd) { + return sliceStart; + } + + const pivot = sliceStart + Math.floor((sliceEnd - sliceStart) / 2); + const itemOffset = positions[pivot]; + return offset <= itemOffset + ? getIndexFromScroll(offset, positions, sliceStart, pivot) + : getIndexFromScroll(offset, positions, pivot + 1, sliceEnd); +} + +export interface RenderContext { + firstRowIndex: number; + lastRowIndex: number; + firstColumnIndex: number; + lastColumnIndex: number; +} + +interface GridVirtualizedContainerProps extends React.HTMLAttributes { + selectionLookup: Record; +} + +const GridVirtualizedContainer = React.forwardRef( + function GridVirtualizedContainer(props, ref) { + const { className, selectionLookup, ...other } = props; + const apiRef = useGridApiContext(); + const rootProps = useGridRootProps(); + const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); + const columnsMeta = useGridSelector(apiRef, gridColumnsMetaSelector); + const visibleSortedRowsAsArray = useGridSelector(apiRef, visibleSortedGridRowsAsArraySelector); + const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector); + const cellFocus = useGridSelector(apiRef, gridFocusCellSelector); + const cellTabIndex = useGridSelector(apiRef, gridTabIndexCellSelector); + const editRowsState = useGridSelector(apiRef, gridEditRowsStateSelector); + const scrollBarState = useGridSelector(apiRef, gridScrollBarSizeSelector); + const paginationState = useGridSelector(apiRef, gridPaginationSelector); + const renderingZoneRef = React.useRef(null); + const rootRef = React.useRef(null); + const handleRef = useForkRef(ref, rootRef); + const [renderContext, setRenderContext] = React.useState(null); + const prevRenderContext = React.useRef(renderContext); + const scrollPosition = React.useRef({ top: 0, left: 0 }); + const [containerWidth, setContainerWidth] = React.useState(null); + const ownerState = { classes: rootProps.classes }; + const classes = useUtilityClasses(ownerState); + + const rowsInCurrentPage = React.useMemo(() => { + if (rootProps.pagination && rootProps.paginationMode === 'client') { + const start = paginationState.pageSize * paginationState.page; + return visibleSortedRowsAsArray.slice(start, start + paginationState.pageSize); + } + return visibleSortedRowsAsArray; + }, [paginationState, rootProps.pagination, rootProps.paginationMode, visibleSortedRowsAsArray]); + + const computeRenderContext = React.useCallback(() => { + if (rootProps.disableVirtualization) { + return { + firstRowIndex: 0, + lastRowIndex: rowsInCurrentPage.length, + firstColumnIndex: 0, + lastColumnIndex: visibleColumns.length, + }; + } + + const { top, left } = scrollPosition.current!; + + const numberOfRowsToRender = rootProps.autoHeight + ? rowsInCurrentPage.length + : Math.floor(rootRef.current!.clientHeight / rowHeight); + + const firstRowIndex = Math.floor(top / rowHeight); + const lastRowIndex = firstRowIndex + numberOfRowsToRender; + + const { positions } = gridColumnsMetaSelector(apiRef.current.state); // To avoid infinite loop + const firstColumnIndex = getIndexFromScroll(left, positions); + const lastColumnIndex = getIndexFromScroll(left + containerWidth!, positions); + + return { + firstRowIndex, + lastRowIndex, + firstColumnIndex, + lastColumnIndex, + }; + }, [ + apiRef, + containerWidth, + rootProps.autoHeight, + rootProps.disableVirtualization, + rowHeight, + rowsInCurrentPage.length, + visibleColumns.length, + ]); + + React.useEffect(() => { + // Reset scroll + rootRef.current!.scrollLeft = 0; + rootRef.current!.scrollTop = 0; + + setContainerWidth(rootRef.current!.clientWidth); + }, []); + + React.useEffect(() => { + if (containerWidth == null) { + return; + } + + const initialRenderContext = computeRenderContext(); + prevRenderContext.current = initialRenderContext; + setRenderContext(initialRenderContext); + + const { top, left } = scrollPosition.current!; + const params = { top, left, renderContext: initialRenderContext }; + apiRef.current.publishEvent(GridEvents.rowsScroll, params); + }, [apiRef, computeRenderContext, containerWidth]); + + const handleResize = React.useCallback(() => { + if (rootRef.current) { + setContainerWidth(rootRef.current.clientWidth); + } + }, []); + + useGridApiEventHandler(apiRef, GridEvents.resize, handleResize); + + const handleScroll = (event: React.UIEvent) => { + const { scrollTop, scrollLeft } = event.currentTarget; + scrollPosition.current.top = scrollTop; + scrollPosition.current.left = scrollLeft; + + // On iOS and macOS, negative offsets are possible when swiping past the start + if (scrollLeft < 0 || scrollTop < 0) { + return; + } + + const nextRenderContext = computeRenderContext(); + + const rowsScrolledSincePreviousRender = Math.abs( + nextRenderContext.firstRowIndex - prevRenderContext.current!.firstRowIndex, + ); + + const columnsScrolledSincePreviousRender = Math.abs( + nextRenderContext.firstColumnIndex - prevRenderContext.current!.firstColumnIndex, + ); + + const shouldSetState = + rowsScrolledSincePreviousRender >= rootProps.rowThreshold || + columnsScrolledSincePreviousRender >= rootProps.columnThreshold; + + // TODO rename event to a wider name, it's not only fired for row scrolling + // TODO create a interface to type correctly the params + apiRef.current.publishEvent(GridEvents.rowsScroll, { + top: scrollTop, + left: scrollLeft, + renderContext: shouldSetState ? nextRenderContext : prevRenderContext.current, + }); + + if (shouldSetState) { + setRenderContext(nextRenderContext); + prevRenderContext.current = nextRenderContext; + + const top = Math.max(nextRenderContext.firstRowIndex - rootProps.rowBuffer, 0) * rowHeight; + const firstColumnToRender = Math.max( + nextRenderContext.firstColumnIndex - rootProps.columnBuffer, + 0, + ); + const left = columnsMeta.positions[firstColumnToRender]; + renderingZoneRef.current!.style.transform = `translate3d(${left}px, ${top}px, 0px)`; + } + }; + + const getRows = () => { + if (!renderContext || containerWidth == null) { + return null; + } + + const rowBuffer = !rootProps.disableVirtualization ? rootProps.rowBuffer : 0; + const columnBuffer = !rootProps.disableVirtualization ? rootProps.columnBuffer : 0; + + const firstRowToRender = Math.max(renderContext.firstRowIndex - rowBuffer, 0); + const lastRowToRender = Math.min( + renderContext.lastRowIndex! + rowBuffer, + rowsInCurrentPage.length, + ); + + const firstColumnToRender = Math.max(renderContext.firstColumnIndex - columnBuffer, 0); + const lastColumnToRender = Math.min( + renderContext.lastColumnIndex + columnBuffer, + visibleColumns.length, + ); + + const renderedRows = rowsInCurrentPage.slice(firstRowToRender, lastRowToRender); + const renderedColumns = visibleColumns.slice(firstColumnToRender, lastColumnToRender); + + const rows: JSX.Element[] = []; + + for (let i = 0; i < renderedRows.length; i += 1) { + const [id, row] = renderedRows[i]; + + rows.push( + , + ); + } + + return rows; + }; + + const needsHorizontalScrollbar = containerWidth && columnsMeta.totalWidth > containerWidth; + + const contentSize = { + width: needsHorizontalScrollbar ? columnsMeta.totalWidth : 'auto', + // In cases where the columns exceed the available width, + // the horizontal scrollbar should be shown even when there're no rows. + // Keeping 1px as minimum height ensures that the scrollbar will visible if necessary. + height: Math.max(rowsInCurrentPage.length * rowHeight, 1), + }; + + if (rootProps.autoHeight && rowsInCurrentPage.length === 0) { + contentSize.height = 2 * rowHeight; // Give room to show the overlay when there no rows. + } + + return ( + + + + {getRows()} + + + + ); + }, +); + +export { GridVirtualizedContainer }; diff --git a/packages/grid/_modules_/grid/components/base/GridBody.tsx b/packages/grid/_modules_/grid/components/base/GridBody.tsx index 7ef169b343383..d382a012a5c8e 100644 --- a/packages/grid/_modules_/grid/components/base/GridBody.tsx +++ b/packages/grid/_modules_/grid/components/base/GridBody.tsx @@ -6,11 +6,14 @@ import { ElementSize } from '../../models/elementSize'; import { GridColumnsHeader } from '../columnHeaders/GridColumnHeaders'; import { GridColumnsContainer } from '../containers/GridColumnsContainer'; import { GridMainContainer } from '../containers/GridMainContainer'; -import { GridWindow } from '../containers/GridWindow'; import { GridAutoSizer } from '../GridAutoSizer'; -import { GridViewport } from '../GridViewport'; import { GridOverlays } from './GridOverlays'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; +import { GridVirtualizedContainer } from '../GridVirtualizedContainer'; +import { useGridSelector } from '../../hooks/features/core/useGridSelector'; +import { gridSelectionStateSelector } from '../../hooks/features/selection/gridSelectionSelector'; +import { gridDensityHeaderHeightSelector } from '../../hooks/features/density/densitySelector'; +import { GridScrollArea } from '../GridScrollArea'; interface GridBodyProps { children?: React.ReactNode; @@ -20,6 +23,8 @@ function GridBody(props: GridBodyProps) { const { children } = props; const apiRef = useGridApiContext(); const rootProps = useGridRootProps(); + const selection = useGridSelector(apiRef, gridSelectionStateSelector); + const headerHeight = useGridSelector(apiRef, gridDensityHeaderHeightSelector); const columnsHeaderRef = React.useRef(null); const columnsContainerRef = React.useRef(null); @@ -28,30 +33,61 @@ function GridBody(props: GridBodyProps) { apiRef.current.columnHeadersContainerElementRef = columnsContainerRef; apiRef.current.columnHeadersElementRef = columnsHeaderRef; - apiRef.current.windowRef = windowRef; - apiRef.current.renderingZoneRef = renderingZoneRef; + apiRef.current.windowRef = windowRef; // TODO rename, it's not attached to the window anymore + apiRef.current.renderingZoneRef = renderingZoneRef; // TODO remove, nobody should have access to internal parts of the virtualization const handleResize = React.useCallback( (size: ElementSize) => apiRef.current.publishEvent(GridEvents.resize, size), [apiRef], ); + const filteredSelection = React.useMemo( + () => + typeof rootProps.isRowSelectable === 'function' + ? selection.filter((id) => rootProps.isRowSelectable!(apiRef.current.getRowParams(id))) + : selection, + [apiRef, rootProps.isRowSelectable, selection], + ); + + const selectionLookup = React.useMemo( + () => + filteredSelection.reduce((lookup, rowId) => { + lookup[rowId] = rowId; + return lookup; + }, {}), + [filteredSelection], + ); + return ( + + - {(size: any) => ( - - - - )} + {(size: { height?: number; width: number }) => { + const style = { + width: size.width, + // If `autoHeight` is on, there will be no height value. + // In this case, let the container to grow whatever it needs. + height: size.height ? size.height - headerHeight : 'auto', + marginTop: headerHeight, + }; + + return ( + + ); + }} {children} diff --git a/packages/grid/_modules_/grid/components/cell/GridCell.tsx b/packages/grid/_modules_/grid/components/cell/GridCell.tsx index a120815285146..d6de061734b5e 100644 --- a/packages/grid/_modules_/grid/components/cell/GridCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridCell.tsx @@ -76,7 +76,7 @@ const useUtilityClasses = (ownerState: OwnerState) => { return composeClasses(slots, getDataGridUtilityClass, classes); }; -function GridCellRaw(props: GridCellProps) { +function GridCell(props: GridCellProps) { const { align, children, @@ -205,9 +205,7 @@ function GridCellRaw(props: GridCellProps) { ); } -const GridCell = React.memo(GridCellRaw); - -GridCellRaw.propTypes = { +GridCell.propTypes = { // ----------------------------- Warning -------------------------------- // | These PropTypes are generated from the TypeScript type definitions | // | To update them edit the TypeScript types and run "yarn proptypes" | diff --git a/packages/grid/_modules_/grid/components/cell/GridEmptyCell.tsx b/packages/grid/_modules_/grid/components/cell/GridEmptyCell.tsx deleted file mode 100644 index 4b2def0c5bbd5..0000000000000 --- a/packages/grid/_modules_/grid/components/cell/GridEmptyCell.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { getDataGridUtilityClass } from '../../gridClasses'; -import { GridComponentProps } from '../../GridComponentProps'; -import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; -import { composeClasses } from '../../utils/material-ui-utils'; - -export interface GridEmptyCellProps { - width?: number; - height?: number; -} - -type OwnerState = { classes: GridComponentProps['classes'] }; - -const useUtilityClasses = (ownerState: OwnerState) => { - const { classes } = ownerState; - - const slots = { - root: ['cell'], - }; - - return composeClasses(slots, getDataGridUtilityClass, classes); -}; - -function GridEmptyCellRaw({ width, height }: GridEmptyCellProps) { - const rootProps = useGridRootProps(); - const ownerState = { classes: rootProps.classes }; - const classes = useUtilityClasses(ownerState); - - if (!width || !height) { - return null; - } - - return ( -
- ); -} - -GridEmptyCellRaw.propTypes = { - // ----------------------------- Warning -------------------------------- - // | These PropTypes are generated from the TypeScript type definitions | - // | To update them edit the TypeScript types and run "yarn proptypes" | - // ---------------------------------------------------------------------- - height: PropTypes.number, - width: PropTypes.number, -} as any; - -const GridEmptyCell = React.memo(GridEmptyCellRaw); - -export { GridEmptyCell }; diff --git a/packages/grid/_modules_/grid/components/cell/index.ts b/packages/grid/_modules_/grid/components/cell/index.ts index 1c4a129360715..7c89b3415d931 100644 --- a/packages/grid/_modules_/grid/components/cell/index.ts +++ b/packages/grid/_modules_/grid/components/cell/index.ts @@ -1,6 +1,5 @@ export * from './GridCell'; export * from './GridEditInputCell'; export * from './GridEditSingleSelectCell'; -export * from './GridEmptyCell'; export * from './GridActionsCell'; export * from './GridActionsCellItem'; diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderItem.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderItem.tsx index d8fc666db8a4f..576c0c4351bcc 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderItem.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderItem.tsx @@ -96,7 +96,7 @@ function GridColumnHeaderItem(props: GridColumnHeaderItemProps) { } const publish = React.useCallback( - (eventName: string) => (event: React.MouseEvent | React.DragEvent) => + (eventName: string) => (event: React.SyntheticEvent) => apiRef.current.publishEvent( eventName, apiRef.current.getColumnHeaderParams(column.field), @@ -105,37 +105,24 @@ function GridColumnHeaderItem(props: GridColumnHeaderItemProps) { [apiRef, column.field], ); - const mouseEventsHandlers = React.useMemo( - () => ({ - onClick: publish(GridEvents.columnHeaderClick), - onDoubleClick: publish(GridEvents.columnHeaderDoubleClick), - onMouseOver: publish(GridEvents.columnHeaderOver), - onMouseOut: publish(GridEvents.columnHeaderOut), - onMouseEnter: publish(GridEvents.columnHeaderEnter), - onMouseLeave: publish(GridEvents.columnHeaderLeave), - onKeyDown: publish(GridEvents.columnHeaderKeyDown), - onFocus: publish(GridEvents.columnHeaderFocus), - onBlur: publish(GridEvents.columnHeaderBlur), - }), - [publish], - ); - - const draggableEventHandlers = React.useMemo( - () => ({ - onDragStart: publish(GridEvents.columnHeaderDragStart), - onDragEnter: publish(GridEvents.columnHeaderDragEnter), - onDragOver: publish(GridEvents.columnHeaderDragOver), - onDragEnd: publish(GridEvents.columnHeaderDragEnd), - }), - [publish], - ); + const mouseEventsHandlers = { + onClick: publish(GridEvents.columnHeaderClick), + onDoubleClick: publish(GridEvents.columnHeaderDoubleClick), + onMouseOver: publish(GridEvents.columnHeaderOver), // TODO remove as it's not used + onMouseOut: publish(GridEvents.columnHeaderOut), // TODO remove as it's not used + onMouseEnter: publish(GridEvents.columnHeaderEnter), // TODO remove as it's not used + onMouseLeave: publish(GridEvents.columnHeaderLeave), // TODO remove as it's not used + onKeyDown: publish(GridEvents.columnHeaderKeyDown), + onFocus: publish(GridEvents.columnHeaderFocus), + onBlur: publish(GridEvents.columnHeaderBlur), + }; - const resizeEventHandlers = React.useMemo( - () => ({ - onMouseDown: publish(GridEvents.columnSeparatorMouseDown), - }), - [publish], - ); + const draggableEventHandlers = { + onDragStart: publish(GridEvents.columnHeaderDragStart), + onDragEnter: publish(GridEvents.columnHeaderDragEnter), + onDragOver: publish(GridEvents.columnHeaderDragOver), + onDragEnd: publish(GridEvents.columnHeaderDragEnd), + }; const removeLastBorderRight = isLastColumn && hasScrollX && !hasScrollY; const showRightBorder = !isLastColumn @@ -152,11 +139,9 @@ function GridColumnHeaderItem(props: GridColumnHeaderItemProps) { const width = column.computedWidth; - let ariaSort: any; + let ariaSort: 'ascending' | 'descending' | undefined; if (sortDirection != null) { - ariaSort = { - 'aria-sort': sortDirection === 'asc' ? 'ascending' : 'descending', - }; + ariaSort = sortDirection === 'asc' ? 'ascending' : 'descending'; } const columnMenuIconButton = !rootProps.disableColumnMenu && !column.disableColumnMenu && ( @@ -199,7 +184,6 @@ function GridColumnHeaderItem(props: GridColumnHeaderItemProps) {
state.scrollBar; +import { RenderContext } from '../GridVirtualizedContainer'; +import { GridColumnHeaderItem } from './GridColumnHeaderItem'; +import { filterGridColumnLookupSelector } from '../../hooks/features/filter/gridFilterSelector'; +import { gridColumnMenuSelector } from '../../hooks/features/columnMenu/columnMenuSelector'; +import { gridSortColumnLookupSelector } from '../../hooks/features/sorting/gridSortingSelector'; +import { + gridTabIndexColumnHeaderSelector, + gridTabIndexCellSelector, + gridFocusColumnHeaderSelector, +} from '../../hooks/features/focus/gridFocusStateSelector'; type OwnerState = { classes?: GridComponentProps['classes']; dragCol: string; }; +const Root = styled('div')({ + // TODO give a slot name and replicate structure from main virtualization + display: 'flex', + position: 'absolute', +}); + const useUtilityClasses = (ownerState: OwnerState) => { const { dragCol, classes } = ownerState; @@ -35,7 +49,7 @@ const useUtilityClasses = (ownerState: OwnerState) => { return composeClasses(slots, getDataGridUtilityClass, classes); }; -export const GridColumnsHeader = React.forwardRef(function GridColumnsHeader( +export const GridColumnsHeader = React.forwardRef(function GridColumnsHeader( props, ref, ) { @@ -43,22 +57,78 @@ export const GridColumnsHeader = React.forwardRef(function G const [resizeCol, setResizeCol] = React.useState(''); const apiRef = useGridApiContext(); - const columns = useGridSelector(apiRef, visibleGridColumnsSelector); - const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); + const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); + const columnsMeta = useGridSelector(apiRef, gridColumnsMetaSelector); + const scrollBarState = useGridSelector(apiRef, gridScrollBarSizeSelector); + const tabIndexState = useGridSelector(apiRef, gridTabIndexColumnHeaderSelector); + const cellTabIndexState = useGridSelector(apiRef, gridTabIndexCellSelector); + const columnHeaderFocus = useGridSelector(apiRef, gridFocusColumnHeaderSelector); const headerHeight = useGridSelector(apiRef, gridDensityHeaderHeightSelector); - const renderCtx = useGridSelector(apiRef, gridRenderingSelector).renderContext; - const { hasScrollX } = useGridSelector(apiRef, gridScrollbarStateSelector); + const filterColumnLookup = useGridSelector(apiRef, filterGridColumnLookupSelector); + const sortColumnLookup = useGridSelector(apiRef, gridSortColumnLookupSelector); + const columnMenuState = useGridSelector(apiRef, gridColumnMenuSelector); const rootProps = useGridRootProps(); + const wrapperRef = React.useRef(null); + const handleRef = useForkRef(ref, wrapperRef); + const [renderContext, setRenderContext] = React.useState(null); + const prevRenderContext = React.useRef(renderContext); + const prevScrollLeft = React.useRef(0); + const [width, setWidth] = React.useState(); const ownerState = { dragCol, classes: rootProps.classes }; const classes = useUtilityClasses(ownerState); - const renderedCols = React.useMemo(() => { - if (renderCtx == null) { + const renderedColumns = React.useMemo(() => { + if (renderContext == null) { return []; } - return columns.slice(renderCtx.firstColIdx, renderCtx.lastColIdx! + 1); - }, [columns, renderCtx]); + + const firstColumnToRender = Math.max( + renderContext.firstColumnIndex! - rootProps.columnBuffer, + 0, + ); + + const lastColumnToRender = Math.min( + renderContext.lastColumnIndex! + rootProps.columnBuffer, + visibleColumns.length, + ); + + return visibleColumns.slice(firstColumnToRender, lastColumnToRender); + }, [renderContext, rootProps.columnBuffer, visibleColumns]); + + React.useEffect(() => { + apiRef.current.columnHeadersContainerElementRef!.current!.scrollLeft = 0; + }, [apiRef]); + + const handleScroll = React.useCallback( + ({ left, renderContext: nextRenderContext }) => { + if (!wrapperRef.current) { + return; + } + + // Ignore vertical scroll. + // Excepts the first event which sets the previous render context. + if (prevScrollLeft.current === left && prevRenderContext.current) { + return; + } + prevScrollLeft.current = left; + + if (nextRenderContext !== prevRenderContext.current || !prevRenderContext.current) { + setRenderContext(nextRenderContext); + prevRenderContext.current = nextRenderContext; + + const firstColumnToRender = Math.max( + nextRenderContext!.firstColumnIndex - rootProps.columnBuffer, + 0, + ); + const offset = columnsMeta.positions[firstColumnToRender]; + wrapperRef!.current!.style.transform = `translate3d(${offset}px, 0px, 0px)`; + } + + apiRef.current.columnHeadersContainerElementRef!.current!.scrollLeft = left; + }, + [apiRef, columnsMeta.positions, rootProps.columnBuffer], + ); const handleColumnResizeStart = React.useCallback( (params: { field: string }) => setResizeCol(params.field), @@ -76,25 +146,84 @@ export const GridColumnsHeader = React.forwardRef(function G useGridApiEventHandler(apiRef, GridEvents.columnHeaderDragStart, handleColumnReorderStart); useGridApiEventHandler(apiRef, GridEvents.columnHeaderDragEnd, handleColumnReorderStop); + useGridApiEventHandler(apiRef, GridEvents.rowsScroll, handleScroll); + + const getColumns = () => { + if (!renderContext) { + return null; + } + + const columns: JSX.Element[] = []; + + const firstColumnToRender = Math.max( + renderContext!.firstColumnIndex! - rootProps.columnBuffer, + 0, + ); + + for (let i = 0; i < renderedColumns.length; i += 1) { + const column = renderedColumns[i]; + + const columnIndex = firstColumnToRender + i; + const isFirstColumn = columnIndex === 0; + const hasTabbableElement = !(tabIndexState === null && cellTabIndexState === null); + const tabIndex = + (tabIndexState !== null && tabIndexState.field === column.field) || + (isFirstColumn && !hasTabbableElement) + ? 0 + : -1; + const hasFocus = columnHeaderFocus !== null && columnHeaderFocus.field === column.field; + const open = columnMenuState.open && columnMenuState.field === column.field; + + columns.push( + , + ); + } + + return columns; + }; + + const updateWidth = React.useCallback(() => { + const newWidth = Math.max( + apiRef.current.columnHeadersContainerElementRef?.current?.clientWidth || 0, + columnsMeta.totalWidth, + ); + setWidth(newWidth); + }, [apiRef, columnsMeta.totalWidth]); + + const handleResize = React.useCallback(() => { + updateWidth(); + }, [updateWidth]); + + useGridApiEventHandler(apiRef, GridEvents.resize, handleResize); + return ( - - +
- - - + {getColumns()}
- -
+ ); }); diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx index d2b16a8c658e8..8a0b177fa3071 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx @@ -59,7 +59,7 @@ function GridColumnHeadersItemCollection(props: GridColumnHeadersItemCollectionP return ( ; - -type OwnerState = { classes: GridComponentProps['classes'] }; - -const useUtilityClasses = (ownerState: OwnerState) => { - const { classes } = ownerState; - - const slots = { - root: ['dataContainer'], - }; - - return composeClasses(slots, getDataGridUtilityClass, classes); -}; - -export function GridDataContainer(props: GridDataContainerProps) { - const { className, ...other } = props; - const apiRef = useGridApiContext(); - const dataContainerSizes = useGridSelector(apiRef, gridDataContainerSizesSelector); - const rootProps = useGridRootProps(); - const ownerState = { classes: rootProps.classes }; - const classes = useUtilityClasses(ownerState); - - const style: any = { - // TODO remove min - minWidth: dataContainerSizes?.width, - minHeight: dataContainerSizes?.height, - }; - - return
; -} diff --git a/packages/grid/_modules_/grid/components/containers/GridRootStyles.ts b/packages/grid/_modules_/grid/components/containers/GridRootStyles.ts index beb7794c5e8e4..38fd5a5b60c5a 100644 --- a/packages/grid/_modules_/grid/components/containers/GridRootStyles.ts +++ b/packages/grid/_modules_/grid/components/containers/GridRootStyles.ts @@ -54,7 +54,7 @@ export const useStyles = makeStyles( right: 0, overflow: 'hidden', display: 'flex', - flexDirection: 'column', + alignItems: 'center', borderBottom: `1px solid ${borderColor}`, }, [`& .${gridClasses.scrollArea}`]: { @@ -207,27 +207,6 @@ export const useStyles = makeStyles( [`& .${gridClasses.columnHeaderWrapper}.scroll .${gridClasses.columnHeader}:last-child`]: { borderRight: 'none', }, - [`& .${gridClasses.dataContainer}`]: { - position: 'relative', - flexGrow: 1, - display: 'flex', - flexDirection: 'column', - }, - [`& .${gridClasses.window}`]: { - position: 'absolute', - bottom: 0, - left: 0, - right: 0, - overflowX: 'auto', - }, - [`& .${gridClasses.viewport}`]: { - position: 'sticky', - top: 0, - left: 0, - display: 'flex', - flexDirection: 'column', - overflow: 'hidden', - }, [`& .${gridClasses.row}`]: { display: 'flex', width: 'fit-content', diff --git a/packages/grid/_modules_/grid/components/containers/GridWindow.tsx b/packages/grid/_modules_/grid/components/containers/GridWindow.tsx deleted file mode 100644 index 8fe5c624c5e5d..0000000000000 --- a/packages/grid/_modules_/grid/components/containers/GridWindow.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { useGridSelector } from '../../hooks/features/core/useGridSelector'; -import { - gridDensityHeaderHeightSelector, - gridDensityRowHeightSelector, -} from '../../hooks/features/density/densitySelector'; -import { gridDataContainerHeightSelector } from '../../hooks/root/gridContainerSizesSelector'; -import { useGridApiContext } from '../../hooks/root/useGridApiContext'; -import { getDataGridUtilityClass } from '../../gridClasses'; -import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; -import { composeClasses } from '../../utils/material-ui-utils'; -import { GridComponentProps } from '../../GridComponentProps'; - -export interface GridWindowProps extends React.HTMLAttributes { - size: { width: number; height: number }; -} - -type OwnerState = GridWindowProps & { - classes?: GridComponentProps['classes']; -}; - -const useUtilityClasses = (ownerState: OwnerState) => { - const { classes } = ownerState; - - const slots = { - root: ['windowContainer'], - window: ['window'], - }; - - return composeClasses(slots, getDataGridUtilityClass, classes); -}; - -const GridWindow = React.forwardRef(function GridWindow( - props, - ref, -) { - const { className, size, ...other } = props; - const apiRef = useGridApiContext(); - const rootProps = useGridRootProps(); - const headerHeight = useGridSelector(apiRef, gridDensityHeaderHeightSelector); - const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector); - const dataContainerHeight = useGridSelector(apiRef, gridDataContainerHeightSelector); - const ownerProps = { ...props, classes: rootProps.classes }; - const classes = useUtilityClasses(ownerProps); - - React.useEffect(() => { - // refs are run before effect. Waiting for an effect guarantees that - // windowRef is resolved first. - // Once windowRef is resolved, we can update the size of the container. - apiRef.current.resize(); - }, [apiRef]); - - const containerHeight = React.useMemo(() => { - if (!rootProps.autoHeight) { - return size.height; - } - // If we have no rows, we give the size of 2 rows to display the no rows overlay - const dataHeight = dataContainerHeight < rowHeight ? rowHeight * 2 : dataContainerHeight; - return headerHeight + dataHeight; - }, [rootProps.autoHeight, dataContainerHeight, headerHeight, rowHeight, size.height]); - - return ( -
-
-
- ); -}); - -GridWindow.propTypes = { - // ----------------------------- Warning -------------------------------- - // | These PropTypes are generated from the TypeScript type definitions | - // | To update them edit the TypeScript types and run "yarn proptypes" | - // ---------------------------------------------------------------------- - size: PropTypes /* @typescript-to-proptypes-ignore */.shape({ - height: PropTypes.number, - width: PropTypes.number, - }).isRequired, -} as any; - -export { GridWindow }; diff --git a/packages/grid/_modules_/grid/components/containers/index.ts b/packages/grid/_modules_/grid/components/containers/index.ts index b0224a114230d..a59bff5ee776a 100644 --- a/packages/grid/_modules_/grid/components/containers/index.ts +++ b/packages/grid/_modules_/grid/components/containers/index.ts @@ -1,7 +1,5 @@ export * from './GridRoot'; export * from './GridColumnsContainer'; -export * from './GridDataContainer'; export * from './GridFooterContainer'; export * from './GridOverlay'; -export * from './GridWindow'; export * from './GridToolbarContainer'; diff --git a/packages/grid/_modules_/grid/components/index.ts b/packages/grid/_modules_/grid/components/index.ts index a9c4ef6fc38b6..44acbafd27418 100644 --- a/packages/grid/_modules_/grid/components/index.ts +++ b/packages/grid/_modules_/grid/components/index.ts @@ -15,10 +15,7 @@ export * from './GridHeader'; export * from './GridLoadingOverlay'; export * from './GridNoRowsOverlay'; export * from './GridPagination'; -export * from './GridRenderingZone'; export * from './GridRowCount'; export * from './GridRow'; export * from './GridSelectedRowCount'; -export * from './GridStickyContainer'; -export * from './GridViewport'; export * from './GridScrollArea'; diff --git a/packages/grid/_modules_/grid/gridClasses.ts b/packages/grid/_modules_/grid/gridClasses.ts index 1ead0f27ad889..49f5125de959c 100644 --- a/packages/grid/_modules_/grid/gridClasses.ts +++ b/packages/grid/_modules_/grid/gridClasses.ts @@ -121,10 +121,6 @@ export interface GridClasses { * Styles applied to the column header separator element. */ columnSeparator: string; - /** - * Styles applied to the data container element. - */ - dataContainer: string; /** * Styles applied to root of the boolean edit component. */ @@ -169,10 +165,6 @@ export interface GridClasses { * Styles applied to the overlay element. */ overlay: string; - /** - * Styles applied to the rendering zone element. - */ - renderingZone: string; /** * Styles applied to the root element. */ @@ -225,10 +217,6 @@ export interface GridClasses { * Styles applied to the window element. */ window: string; - /** - * Styles applied to the window container element. - */ - windowContainer: string; /** * Styles applied to both the cell and the column header if `showColumnRightBorder={true}`. */ @@ -273,7 +261,6 @@ export const gridClasses = generateUtilityClasses('MuiDataGrid', [ 'columnSeparator--resizable', 'columnSeparator--resizing', 'columnSeparator', - 'dataContainer', 'editBooleanCell', 'editInputCell', 'filterIcon', @@ -285,8 +272,6 @@ export const gridClasses = generateUtilityClasses('MuiDataGrid', [ 'menuIconButton', 'menuOpen', 'overlay', - 'renderingZone', - 'root', 'root', 'row--editable', 'row--editing', @@ -298,8 +283,6 @@ export const gridClasses = generateUtilityClasses('MuiDataGrid', [ 'selectedRowCount', 'sortIcon', 'toolbarContainer', - 'viewport', - 'window', - 'windowContainer', + 'virtualizedContainer', 'withBorder', ]); diff --git a/packages/grid/_modules_/grid/hooks/features/columnReorder/useGridColumnReorder.tsx b/packages/grid/_modules_/grid/hooks/features/columnReorder/useGridColumnReorder.tsx index a9d59d5e93b97..abd30e6c26694 100644 --- a/packages/grid/_modules_/grid/hooks/features/columnReorder/useGridColumnReorder.tsx +++ b/packages/grid/_modules_/grid/hooks/features/columnReorder/useGridColumnReorder.tsx @@ -184,7 +184,8 @@ export const useGridColumnReorder = ( // Check if the column was dropped outside the grid. if (event.dataTransfer.dropEffect === 'none') { - apiRef.current.setColumnIndex(params.field, originColumnIndex.current!); + // Accessing params.field may contain the wrong field as header elements are reused + apiRef.current.setColumnIndex(dragColField, originColumnIndex.current!); originColumnIndex.current = null; } diff --git a/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts b/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts index f2943682bf6bf..261272b276ecd 100644 --- a/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts +++ b/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts @@ -65,15 +65,16 @@ export const useGridInfiniteLoader = ( [apiRef, props.scrollEndThreshold, visibleColumns, containerSizes], ); - const handleGridScroll = React.useCallback(() => { - const scrollPosition = apiRef.current.getScrollPosition(); - - handleRowsScrollEnd(scrollPosition); - }, [apiRef, handleRowsScrollEnd]); + const handleGridScroll = React.useCallback( + ({ left, top }) => { + handleRowsScrollEnd({ left, top }); + }, + [handleRowsScrollEnd], + ); // TODO: Check if onViewportRowsChange works as expected once virtualization is reworked React.useEffect(() => { - const renderContext = renderState.renderContext!; + const renderContext = renderState?.renderContext; if (!renderContext) { return; diff --git a/packages/grid/_modules_/grid/hooks/features/scroll/useGridScroll.ts b/packages/grid/_modules_/grid/hooks/features/scroll/useGridScroll.ts index 9f9ee00ab3d53..92fc10ad02efb 100644 --- a/packages/grid/_modules_/grid/hooks/features/scroll/useGridScroll.ts +++ b/packages/grid/_modules_/grid/hooks/features/scroll/useGridScroll.ts @@ -13,7 +13,6 @@ import { gridRowCountSelector } from '../rows/gridRowsSelector'; import { gridDensityRowHeightSelector } from '../density/densitySelector'; import { GridScrollParams } from '../../../models/params/gridScrollParams'; import { GridScrollApi } from '../../../models/api/gridScrollApi'; -import { gridScrollSelector } from '../virtualization/renderingStateSelector'; import { useGridApiMethod } from '../../root/useGridApiMethod'; import { useNativeEventListener } from '../../root/useNativeEventListener'; @@ -125,10 +124,12 @@ export const useGridScroll = ( [windowRef, colRef, logger], ); - const getScrollPosition = React.useCallback( - () => gridScrollSelector(apiRef.current.state), - [apiRef], - ); + const getScrollPosition = React.useCallback(() => { + if (!windowRef?.current) { + return { top: 0, left: 0 }; + } + return { top: windowRef.current.scrollTop, left: windowRef.current.scrollLeft }; + }, [windowRef]); const scrollApi: GridScrollApi = { scroll, @@ -148,11 +149,4 @@ export const useGridScroll = ( 'scroll', preventScroll, ); - - useNativeEventListener( - apiRef, - () => apiRef.current?.columnHeadersContainerElementRef?.current, - 'scroll', - preventScroll, - ); }; diff --git a/packages/grid/_modules_/grid/hooks/features/virtualization/index.ts b/packages/grid/_modules_/grid/hooks/features/virtualization/index.ts index 7d38bb0509086..b24fd946957e4 100644 --- a/packages/grid/_modules_/grid/hooks/features/virtualization/index.ts +++ b/packages/grid/_modules_/grid/hooks/features/virtualization/index.ts @@ -1,2 +1 @@ -export * from './useGridVirtualization'; export * from './renderingState'; diff --git a/packages/grid/_modules_/grid/hooks/features/virtualization/useGridNoVirtualization.ts b/packages/grid/_modules_/grid/hooks/features/virtualization/useGridNoVirtualization.ts deleted file mode 100644 index d9c3c387cef97..0000000000000 --- a/packages/grid/_modules_/grid/hooks/features/virtualization/useGridNoVirtualization.ts +++ /dev/null @@ -1,89 +0,0 @@ -import * as React from 'react'; -import { GridComponentProps } from '../../../GridComponentProps'; -import { GridApiRef } from '../../../models/api/gridApiRef'; -import { useNativeEventListener } from '../../root/useNativeEventListener'; -import { useGridScrollFn } from '../../utils/useGridScrollFn'; -import { visibleGridColumnsSelector } from '../columns/gridColumnsSelector'; -import { useGridSelector } from '../core'; -import { useGridState } from '../core/useGridState'; -import { gridPaginationSelector } from '../pagination/gridPaginationSelector'; -import { gridContainerSizesSelector } from '../../root/gridContainerSizesSelector'; - -/** - * @requires useGridPage (state) - * @requires useGridPageSize (state) - * @requires useGridColumns (state) - * @requires useGridContainerProps (state) - */ -export const useGridNoVirtualization = ( - apiRef: GridApiRef, - props: Pick, -): void => { - const windowRef = apiRef.current.windowRef; - const columnsHeaderRef = apiRef.current.columnHeadersElementRef; - const renderingZoneRef = apiRef.current.renderingZoneRef; - const [, setGridState, forceUpdate] = useGridState(apiRef); - const [scrollTo] = useGridScrollFn(apiRef, renderingZoneRef!, columnsHeaderRef!); - const paginationState = useGridSelector(apiRef, gridPaginationSelector); - const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); - const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); - - const syncState = React.useCallback(() => { - if (!containerSizes || !windowRef?.current) { - return; - } - - let firstRowIdx = 0; - const { page, pageSize } = paginationState; - if (props.pagination && props.paginationMode === 'client') { - firstRowIdx = pageSize * page; - } - const lastRowIdx = firstRowIdx + containerSizes.virtualRowsCount; - const lastColIdx = visibleColumns.length > 0 ? visibleColumns.length - 1 : 0; - const renderContext = { firstRowIdx, lastRowIdx, firstColIdx: 0, lastColIdx }; - - const scrollParams = { - top: windowRef.current!.scrollTop, - left: windowRef.current!.scrollLeft, - }; - - setGridState((state) => ({ - ...state, - rendering: { - ...state.rendering, - virtualPage: 0, - renderContext, - realScroll: scrollParams, - renderingZoneScroll: scrollParams, - }, - })); - forceUpdate(); - }, [ - containerSizes, - paginationState, - props.pagination, - props.paginationMode, - setGridState, - forceUpdate, - visibleColumns.length, - windowRef, - ]); - - React.useEffect(() => { - if (!props.disableVirtualization) { - return; - } - syncState(); - }, [props.disableVirtualization, syncState]); - - const handleScroll = React.useCallback(() => { - if (!props.disableVirtualization || !windowRef?.current) { - return; - } - const { scrollLeft, scrollTop } = windowRef.current; - scrollTo({ top: scrollTop, left: scrollLeft }); - syncState(); - }, [props.disableVirtualization, scrollTo, windowRef, syncState]); - - useNativeEventListener(apiRef, windowRef!, 'scroll', handleScroll, { passive: true }); -}; diff --git a/packages/grid/_modules_/grid/hooks/features/virtualization/useGridVirtualization.ts b/packages/grid/_modules_/grid/hooks/features/virtualization/useGridVirtualization.ts deleted file mode 100644 index d4c48d7c546f3..0000000000000 --- a/packages/grid/_modules_/grid/hooks/features/virtualization/useGridVirtualization.ts +++ /dev/null @@ -1,418 +0,0 @@ -import * as React from 'react'; -import { GridEvents } from '../../../constants/eventsConstants'; -import { GridApiRef } from '../../../models/api/gridApiRef'; -import { GridVirtualizationApi } from '../../../models/api/gridVirtualizationApi'; -import { - GridRenderContextProps, - GridRenderRowProps, - GridRenderColumnsProps, -} from '../../../models/gridRenderContextProps'; -import { GridContainerProps } from '../../../models/gridContainerProps'; -import { isDeepEqual } from '../../../utils/utils'; -import { useEnhancedEffect } from '../../../utils/material-ui-utils'; -import { - gridColumnsMetaSelector, - visibleGridColumnsSelector, -} from '../columns/gridColumnsSelector'; -import { useGridSelector } from '../core/useGridSelector'; -import { useGridState } from '../core/useGridState'; -import { gridPaginationSelector } from '../pagination/gridPaginationSelector'; -import { gridRowCountSelector } from '../rows/gridRowsSelector'; -import { useGridApiMethod } from '../../root/useGridApiMethod'; -import { useNativeEventListener } from '../../root/useNativeEventListener'; -import { useGridLogger } from '../../utils/useGridLogger'; -import { useGridScrollFn } from '../../utils/useGridScrollFn'; -import { GridRenderingState } from './renderingState'; -import { GridComponentProps } from '../../../GridComponentProps'; -import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; -import { useGridStateInit } from '../../utils/useGridStateInit'; - -// Uses binary search to avoid looping through all possible positions -function getIdxFromScroll( - offset: number, - positions: number[], - sliceStart = 0, - sliceEnd = positions.length, -): number { - if (positions.length <= 0) { - return -1; - } - - if (sliceStart >= sliceEnd) { - return sliceStart; - } - - const pivot = sliceStart + Math.floor((sliceEnd - sliceStart) / 2); - const itemOffset = positions[pivot]; - return offset <= itemOffset - ? getIdxFromScroll(offset, positions, sliceStart, pivot) - : getIdxFromScroll(offset, positions, pivot + 1, sliceEnd); -} - -/** - * @requires useGridColumns (state) - * @requires useGridPage (state) - * @requires useGridPageSize (state) - * @requires useGridRows (state) - */ -export const useGridVirtualization = ( - apiRef: GridApiRef, - props: Pick< - GridComponentProps, - | 'pagination' - | 'paginationMode' - | 'columnBuffer' - | 'disableExtendRowFullWidth' - | 'disableVirtualization' - >, -): void => { - const logger = useGridLogger(apiRef, 'useGridVirtualization'); - - useGridStateInit(apiRef, (state) => ({ - ...state, - rendering: { - realScroll: { left: 0, top: 0 }, - renderContext: null, - renderingZoneScroll: { left: 0, top: 0 }, - virtualPage: 0, - virtualRowsCount: 0, - }, - })); - - const colRef = apiRef.current.columnHeadersElementRef!; - const windowRef = apiRef.current.windowRef!; - const renderingZoneRef = apiRef.current.renderingZoneRef!; - - const [gridState, setGridState, forceUpdate] = useGridState(apiRef); - const paginationState = useGridSelector(apiRef, gridPaginationSelector); - const totalRowCount = useGridSelector(apiRef, gridRowCountSelector); - const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); - const columnsMeta = useGridSelector(apiRef, gridColumnsMetaSelector); - const renderedColRef = React.useRef(null); - const containerPropsRef = React.useRef(null); - const lastScrollLeftRef = React.useRef(0); - - const [scrollTo] = useGridScrollFn(apiRef, renderingZoneRef, colRef); - - const setRenderingState = React.useCallback( - (newState: Partial) => { - let stateChanged = false; - setGridState((state) => { - const currentRenderingState = { ...state.rendering, ...newState }; - if (!isDeepEqual(state.rendering, currentRenderingState)) { - stateChanged = true; - return { ...state, rendering: currentRenderingState }; - } - return state; - }); - return stateChanged; - }, - [setGridState], - ); - - const getRenderRowProps = React.useCallback( - (page: number) => { - if (apiRef.current.state.containerSizes == null) { - return null; - } - let minRowIdx = 0; - if (props.pagination && props.paginationMode === 'client') { - minRowIdx = paginationState.pageSize * paginationState.page; - } - - const firstRowIdx = page * apiRef.current.state.containerSizes.viewportPageSize + minRowIdx; - let lastRowIdx = firstRowIdx + apiRef.current.state.containerSizes.renderingZonePageSize; - const maxIndex = apiRef.current.state.containerSizes.virtualRowsCount + minRowIdx; - if (lastRowIdx > maxIndex) { - lastRowIdx = maxIndex; - } - - const rowProps: GridRenderRowProps = { page, firstRowIdx, lastRowIdx }; - return rowProps; - }, - [ - apiRef, - props.pagination, - paginationState.pageSize, - props.paginationMode, - paginationState.page, - ], - ); - - const getRenderingState = React.useCallback((): Partial | null => { - if (apiRef.current.state.containerSizes == null) { - return null; - } - - const newRenderCtx: Partial = { - ...renderedColRef.current, - ...getRenderRowProps(apiRef.current.state.rendering.virtualPage), - paginationCurrentPage: paginationState.page, - pageSize: paginationState.pageSize, - }; - return newRenderCtx; - }, [renderedColRef, getRenderRowProps, apiRef, paginationState.page, paginationState.pageSize]); - - const reRender = React.useCallback(() => { - const renderingState = getRenderingState(); - const hasChanged = setRenderingState({ renderContext: renderingState }); - if (hasChanged) { - logger.debug('reRender: trigger rendering'); - forceUpdate(); - } - }, [getRenderingState, logger, forceUpdate, setRenderingState]); - - const getColumnIdxFromScroll = React.useCallback( - (left: number) => getIdxFromScroll(left, columnsMeta.positions), - [columnsMeta.positions], - ); - - const getColumnFromScroll = React.useCallback( - (left: number) => { - if (!visibleColumns.length) { - return null; - } - return visibleColumns[getColumnIdxFromScroll(left)]; - }, - [getColumnIdxFromScroll, visibleColumns], - ); - - const updateRenderedCols = React.useCallback( - (containerProps: GridContainerProps | null, scrollLeft: number) => { - if (!containerProps) { - return false; - } - - containerPropsRef.current = containerProps; - const windowWidth = containerProps.windowSizes.width; - - lastScrollLeftRef.current = scrollLeft; - logger.debug( - `GridColumns from ${getColumnFromScroll(scrollLeft)?.field} to ${ - getColumnFromScroll(scrollLeft + windowWidth)?.field - }`, - ); - const firstDisplayedIdx = getColumnIdxFromScroll(scrollLeft); - const lastDisplayedIdx = getColumnIdxFromScroll(scrollLeft + windowWidth); - const prevFirstColIdx = renderedColRef?.current?.firstColIdx || 0; - const prevLastColIdx = renderedColRef?.current?.lastColIdx || 0; - const columnBuffer = props.columnBuffer; - const tolerance = columnBuffer > 1 ? columnBuffer - 1 : columnBuffer; // Math.floor(columnBuffer / 2); - const diffFirst = Math.abs(firstDisplayedIdx - tolerance - prevFirstColIdx); - const diffLast = Math.abs(lastDisplayedIdx + tolerance - prevLastColIdx); - logger.debug(`Column buffer: ${columnBuffer}, tolerance: ${tolerance}`); - logger.debug(`Previous values => first: ${prevFirstColIdx}, last: ${prevLastColIdx}`); - logger.debug( - `Current displayed values => first: ${firstDisplayedIdx}, last: ${lastDisplayedIdx}`, - ); - logger.debug(`Difference with first: ${diffFirst} and last: ${diffLast} `); - - const lastVisibleColIdx = visibleColumns.length > 0 ? visibleColumns.length - 1 : 0; - const firstColIdx = - firstDisplayedIdx - columnBuffer >= 0 ? firstDisplayedIdx - columnBuffer : 0; - const newRenderedColState = { - leftEmptyWidth: columnsMeta.positions[firstColIdx], - rightEmptyWidth: 0, - firstColIdx, - lastColIdx: - lastDisplayedIdx + columnBuffer >= lastVisibleColIdx - ? lastVisibleColIdx - : lastDisplayedIdx + columnBuffer, - }; - - if (apiRef.current.state.scrollBar.hasScrollX) { - newRenderedColState.rightEmptyWidth = - columnsMeta.totalWidth - - columnsMeta.positions[newRenderedColState.lastColIdx] - - visibleColumns[newRenderedColState.lastColIdx].computedWidth; - } else if (!props.disableExtendRowFullWidth) { - newRenderedColState.rightEmptyWidth = - apiRef.current.state.viewportSizes.width - columnsMeta.totalWidth; - } - - if (!isDeepEqual(newRenderedColState, renderedColRef.current)) { - renderedColRef.current = newRenderedColState; - logger.debug('New columns state to render', newRenderedColState); - return true; - } - logger.debug(`No rendering needed on columns`); - return false; - }, - [ - apiRef, - columnsMeta.positions, - columnsMeta.totalWidth, - getColumnFromScroll, - getColumnIdxFromScroll, - logger, - props.columnBuffer, - props.disableExtendRowFullWidth, - visibleColumns, - ], - ); - - const updateViewport = React.useCallback( - (forceReRender = false) => { - if (props.disableVirtualization) { - return; - } - - const lastState = apiRef.current.state; - const containerProps = lastState.containerSizes; - if (!windowRef || !windowRef.current || !containerProps) { - return; - } - const scrollBar = lastState.scrollBar; - - const { scrollLeft, scrollTop } = windowRef.current; - logger.debug(`Handling scroll Left: ${scrollLeft} Top: ${scrollTop}`); - - let requireRerender = updateRenderedCols(containerProps, scrollLeft); - - const rzScrollLeft = scrollLeft; - const maxScrollHeight = lastState.containerSizes!.renderingZoneScrollHeight; - - const page = lastState.rendering.virtualPage; - const nextPage = maxScrollHeight > 0 ? Math.floor(scrollTop / maxScrollHeight) : 0; - const rzScrollTop = scrollTop % maxScrollHeight; - - const scrollParams = { - left: scrollBar.hasScrollX ? rzScrollLeft : 0, - top: containerProps.isVirtualized ? rzScrollTop : scrollTop, - }; - - if (containerProps.isVirtualized && page !== nextPage) { - setRenderingState({ virtualPage: nextPage }); - logger.debug(`Changing page from ${page} to ${nextPage}`); - requireRerender = true; - } else { - if (!containerProps.isVirtualized && page > 0) { - logger.debug(`Virtualization disabled, setting virtualPage to 0`); - setRenderingState({ virtualPage: 0 }); - } - - scrollTo(scrollParams); - } - setRenderingState({ - renderingZoneScroll: scrollParams, - realScroll: { - left: windowRef.current.scrollLeft, - top: windowRef.current.scrollTop, - }, - }); - apiRef.current.publishEvent(GridEvents.rowsScroll, scrollParams); - - const pageChanged = - lastState.rendering.renderContext && - lastState.rendering.renderContext.paginationCurrentPage !== paginationState.page; - if (forceReRender || requireRerender || pageChanged) { - reRender(); - } - }, - [ - apiRef, - logger, - paginationState.page, - reRender, - scrollTo, - setRenderingState, - updateRenderedCols, - windowRef, - props.disableVirtualization, - ], - ); - - const resetScroll = React.useCallback(() => { - scrollTo({ left: 0, top: 0 }); - setRenderingState({ virtualPage: 0 }); - - if (windowRef && windowRef.current) { - windowRef.current.scrollTop = 0; - windowRef.current.scrollLeft = 0; - } - setRenderingState({ renderingZoneScroll: { left: 0, top: 0 } }); - }, [scrollTo, setRenderingState, windowRef]); - - const handleScroll = React.useCallback(() => { - if (props.disableVirtualization) { - return; - } - - // On iOS the inertia scrolling allows to return negative values. - if (windowRef.current!.scrollLeft < 0 || windowRef.current!.scrollTop < 0) { - return; - } - - if (apiRef.current.updateViewport) { - apiRef.current.updateViewport(); - } - }, [props.disableVirtualization, windowRef, apiRef]); - - const getContainerPropsState = React.useCallback( - () => gridState.containerSizes, - [gridState.containerSizes], - ); - - const getRenderContextState = React.useCallback(() => { - return gridState.rendering.renderContext || undefined; - }, [gridState.rendering.renderContext]); - - useEnhancedEffect(() => { - if (props.disableVirtualization) { - return; - } - - if (renderingZoneRef && renderingZoneRef.current) { - logger.debug('applying scrollTop ', gridState.rendering.renderingZoneScroll.top); - scrollTo(gridState.rendering.renderingZoneScroll); - } - }); - - const virtualApi: Partial = { - getContainerPropsState, - getRenderContextState, - updateViewport, - }; - useGridApiMethod(apiRef, virtualApi, 'GridVirtualizationApi'); - - React.useEffect(() => { - if ( - gridState.rendering.renderContext?.paginationCurrentPage !== paginationState.page && - apiRef.current.updateViewport - ) { - logger.debug(`State paginationState.page changed to ${paginationState.page}. `); - apiRef.current.updateViewport(true); - resetScroll(); - } - }, [ - apiRef, - paginationState.page, - gridState.rendering.renderContext?.paginationCurrentPage, - logger, - resetScroll, - ]); - - React.useEffect(() => { - if (apiRef.current.updateViewport) { - logger.debug(`totalRowCount has changed to ${totalRowCount}, updating viewport.`); - apiRef.current.updateViewport(true); - } - }, [ - logger, - totalRowCount, - gridState.viewportSizes, - gridState.scrollBar, - gridState.containerSizes, - apiRef, - ]); - - useNativeEventListener(apiRef, windowRef, 'scroll', handleScroll, { passive: true }); - - const resetRenderedColState = React.useCallback(() => { - logger.debug('Clearing previous renderedColRef'); - renderedColRef.current = null; - }, [logger, renderedColRef]); - - useGridApiEventHandler(apiRef, GridEvents.columnsChange, resetRenderedColState); - useGridApiEventHandler(apiRef, GridEvents.debouncedResize, resetRenderedColState); -}; diff --git a/packages/grid/_modules_/grid/models/gridOptions.tsx b/packages/grid/_modules_/grid/models/gridOptions.tsx index 2711e3cbfa3a9..cda35d3f4d973 100644 --- a/packages/grid/_modules_/grid/models/gridOptions.tsx +++ b/packages/grid/_modules_/grid/models/gridOptions.tsx @@ -54,10 +54,25 @@ export interface GridSimpleOptions { */ checkboxSelectionVisibleOnly: boolean; /** - * Number of columns rendered outside the grid viewport. - * @default 2 + * Number of extra columns to be rendered before/after the visible slice. + * @default 3 */ columnBuffer: number; + /** + * Number of extra rows to be rendered before/after the visible slice. + * @default 3 + */ + rowBuffer: number; + /** + * Number of rows from the `rowBuffer` that can be visible before a new slice is rendered. + * @default 3 + */ + rowThreshold: number; + /** + * Number of rows from the `columnBuffer` that can be visible before a new slice is rendered. + * @default 3 + */ + columnThreshold: number; /** * Set the density of the grid. * @default "standard" @@ -234,7 +249,10 @@ export const GRID_DEFAULT_SIMPLE_OPTIONS: GridSimpleOptions = { autoPageSize: false, checkboxSelection: false, checkboxSelectionVisibleOnly: false, - columnBuffer: 2, + columnBuffer: 3, + rowBuffer: 3, + columnThreshold: 3, + rowThreshold: 3, density: GridDensityTypes.Standard, disableExtendRowFullWidth: false, disableColumnFilter: false, diff --git a/packages/grid/data-grid/src/DataGrid.tsx b/packages/grid/data-grid/src/DataGrid.tsx index 0ffe125483cc5..58c73a57515a3 100644 --- a/packages/grid/data-grid/src/DataGrid.tsx +++ b/packages/grid/data-grid/src/DataGrid.tsx @@ -74,8 +74,8 @@ DataGridRaw.propTypes = { */ className: PropTypes.string, /** - * Number of columns rendered outside the grid viewport. - * @default 2 + * Number of extra columns to be rendered before/after the visible slice. + * @default 3 */ columnBuffer: PropTypes.number, /** @@ -94,6 +94,11 @@ DataGridRaw.propTypes = { } return null; }), + /** + * Number of rows from the `columnBuffer` that can be visible before a new slice is rendered. + * @default 3 + */ + columnThreshold: PropTypes.number, /** * Extend native column types with your new column types. */ @@ -524,6 +529,11 @@ DataGridRaw.propTypes = { * @default "client" */ paginationMode: PropTypes.oneOf(['client', 'server']), + /** + * Number of extra rows to be rendered before/after the visible slice. + * @default 3 + */ + rowBuffer: PropTypes.number, /** * Set the total number of rows, if it is different than the length of the value `rows` prop. */ @@ -542,6 +552,11 @@ DataGridRaw.propTypes = { * @default [25, 50, 100] */ rowsPerPageOptions: PropTypes.arrayOf(PropTypes.number), + /** + * Number of rows from the `rowBuffer` that can be visible before a new slice is rendered. + * @default 3 + */ + rowThreshold: PropTypes.number, /** * Override the height/width of the grid inner scrollbar. */ diff --git a/packages/grid/data-grid/src/tests/keyboard.DataGrid.test.tsx b/packages/grid/data-grid/src/tests/keyboard.DataGrid.test.tsx index 827f4d923e05c..9f668e478639b 100644 --- a/packages/grid/data-grid/src/tests/keyboard.DataGrid.test.tsx +++ b/packages/grid/data-grid/src/tests/keyboard.DataGrid.test.tsx @@ -258,10 +258,12 @@ describe(' - Keyboard', () => { } render(); getColumnHeaderCell(0).focus(); - const gridWindow = document.querySelector('.MuiDataGrid-window')! as HTMLElement; - expect(gridWindow.scrollLeft).to.equal(0); + const virtualizedContainer = document.querySelector( + '.MuiDataGrid-virtualizedContainer', + )! as HTMLElement; + expect(virtualizedContainer.scrollLeft).to.equal(0); fireEvent.keyDown(document.activeElement!, { key: 'ArrowRight' }); - expect(gridWindow.scrollLeft).not.to.equal(0); + expect(virtualizedContainer.scrollLeft).not.to.equal(0); }); it('Shift + Space should select a row', () => { diff --git a/packages/grid/data-grid/src/tests/layout.DataGrid.test.tsx b/packages/grid/data-grid/src/tests/layout.DataGrid.test.tsx index bf5459556b091..67f723522b4d9 100644 --- a/packages/grid/data-grid/src/tests/layout.DataGrid.test.tsx +++ b/packages/grid/data-grid/src/tests/layout.DataGrid.test.tsx @@ -605,8 +605,11 @@ describe(' - Layout & Warnings', () => { />
, ); - const gridWindow = document.querySelector('.MuiDataGrid-window'); - const scrollBarSize = gridWindow!.scrollHeight - gridWindow!.clientHeight; + const virtualizedContainer = document.querySelector( + '.MuiDataGrid-virtualizedContainer', + ); + const scrollBarSize = + virtualizedContainer!.offsetHeight - virtualizedContainer!.clientHeight; expect(scrollBarSize).not.to.equal(0); expect(document.querySelector('.MuiDataGrid-main')!.clientHeight).to.equal( scrollBarSize + headerHeight + rowHeight * baselineProps.rows.length, @@ -625,9 +628,9 @@ describe(' - Layout & Warnings', () => { ); }; render(); - const gridWindow = document.querySelector('.MuiDataGrid-window'); + const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer'); // It should not have a horizontal scrollbar - expect(gridWindow!.scrollWidth - gridWindow!.clientWidth).to.equal(0); + expect(virtualizedContainer!.scrollWidth - virtualizedContainer!.clientWidth).to.equal(0); }); it('should have a horizontal scrollbar when there are more columns to show and no rows', function test() { @@ -640,8 +643,8 @@ describe(' - Layout & Warnings', () => {
, ); - const gridWindow = document.querySelector('.MuiDataGrid-window'); - expect(gridWindow!.scrollWidth - gridWindow!.clientWidth).not.to.equal(0); + const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer'); + expect(virtualizedContainer!.scrollWidth - virtualizedContainer!.clientWidth).not.to.equal(0); }); }); diff --git a/packages/grid/data-grid/src/tests/pagination.DataGrid.test.tsx b/packages/grid/data-grid/src/tests/pagination.DataGrid.test.tsx index 080c2a9780bf2..39a0718fab4ee 100644 --- a/packages/grid/data-grid/src/tests/pagination.DataGrid.test.tsx +++ b/packages/grid/data-grid/src/tests/pagination.DataGrid.test.tsx @@ -474,7 +474,7 @@ describe(' - Pagination', () => { (heightAfter - headerHeight - footerHeight) / rowHeight, ); - let rows = document.querySelectorAll('.MuiDataGrid-viewport [role="row"]'); + let rows = document.querySelectorAll('.MuiDataGrid-renderingZone [role="row"]'); expect(rows.length).to.equal(expectedViewportRowsLengthBefore); setProps({ height: heightAfter }); @@ -485,7 +485,7 @@ describe(' - Pagination', () => { ), ); - rows = document.querySelectorAll('.MuiDataGrid-viewport [role="row"]'); + rows = document.querySelectorAll('.MuiDataGrid-renderingZone [role="row"]'); expect(rows.length).to.equal(expectedViewportRowsLengthAfter); expect(onPageSizeChange.lastCall.args[0]).to.equal(expectedViewportRowsLengthAfter); diff --git a/packages/grid/data-grid/src/useDataGridComponent.tsx b/packages/grid/data-grid/src/useDataGridComponent.tsx index 27be7daf48699..b4ce41b241c42 100644 --- a/packages/grid/data-grid/src/useDataGridComponent.tsx +++ b/packages/grid/data-grid/src/useDataGridComponent.tsx @@ -19,15 +19,12 @@ import { useGridParamsApi } from '../../_modules_/grid/hooks/features/rows/useGr import { useGridRows } from '../../_modules_/grid/hooks/features/rows/useGridRows'; import { useGridSelection } from '../../_modules_/grid/hooks/features/selection/useGridSelection'; import { useGridSorting } from '../../_modules_/grid/hooks/features/sorting/useGridSorting'; -import { useGridVirtualization } from '../../_modules_/grid/hooks/features/virtualization/useGridVirtualization'; -import { useGridNoVirtualization } from '../../_modules_/grid/hooks/features/virtualization/useGridNoVirtualization'; import { useGridScroll } from '../../_modules_/grid/hooks/features/scroll/useGridScroll'; import { useApi } from '../../_modules_/grid/hooks/root/useApi'; import { useGridEvents } from '../../_modules_/grid/hooks/root/useGridEvents'; import { useGridContainerProps } from '../../_modules_/grid/hooks/root/useGridContainerProps'; import { useErrorHandler } from '../../_modules_/grid/hooks/utils/useErrorHandler'; import { useGridLoggerFactory } from '../../_modules_/grid/hooks/utils/useGridLogger'; -import { useRenderInfoLog } from '../../_modules_/grid/hooks/utils/useRenderInfoLog'; import { useGridResizeContainer } from '../../_modules_/grid/hooks/utils/useGridResizeContainer'; import { useStateProp } from '../../_modules_/grid/hooks/utils/useStateProp'; import { GridApiRef } from '../../_modules_/grid/models/api/gridApiRef'; @@ -54,8 +51,6 @@ export const useDataGridComponent = (apiRef: GridApiRef, props: GridComponentPro useGridPage(apiRef, props); useGridContainerProps(apiRef, props); useGridScroll(apiRef, props); - useGridNoVirtualization(apiRef, props); - useGridVirtualization(apiRef, props); useGridColumnMenu(apiRef); useGridKeyboard(apiRef); useGridKeyboardNavigation(apiRef, props); @@ -63,5 +58,4 @@ export const useDataGridComponent = (apiRef: GridApiRef, props: GridComponentPro useGridClipboard(apiRef); useGridEvents(apiRef, props); useStateProp(apiRef, props); - useRenderInfoLog(apiRef); }; diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index 09262d0dcd912..4abf115263c72 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -109,14 +109,19 @@ DataGridProRaw.propTypes = { */ className: PropTypes.string, /** - * Number of columns rendered outside the grid viewport. - * @default 2 + * Number of extra columns to be rendered before/after the visible slice. + * @default 3 */ columnBuffer: PropTypes.number, /** * Set of columns of type [[GridColumns]]. */ columns: PropTypes.arrayOf(PropTypes.object).isRequired, + /** + * Number of rows from the `columnBuffer` that can be visible before a new slice is rendered. + * @default 3 + */ + columnThreshold: PropTypes.number, /** * Extend native column types with your new column types. */ @@ -573,6 +578,11 @@ DataGridProRaw.propTypes = { * @default "client" */ paginationMode: PropTypes.oneOf(['client', 'server']), + /** + * Number of extra rows to be rendered before/after the visible slice. + * @default 3 + */ + rowBuffer: PropTypes.number, /** * Set the total number of rows, if it is different than the length of the value `rows` prop. */ @@ -591,6 +601,11 @@ DataGridProRaw.propTypes = { * @default [25, 50, 100] */ rowsPerPageOptions: PropTypes.arrayOf(PropTypes.number), + /** + * Number of rows from the `rowBuffer` that can be visible before a new slice is rendered. + * @default 3 + */ + rowThreshold: PropTypes.number, /** * Override the height/width of the grid inner scrollbar. */ diff --git a/packages/grid/x-grid/src/tests/columnHeaders.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/columnHeaders.DataGridPro.test.tsx index d59215cb5f26c..7b76ecf7673df 100644 --- a/packages/grid/x-grid/src/tests/columnHeaders.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/columnHeaders.DataGridPro.test.tsx @@ -54,8 +54,8 @@ describe(' - Column Headers', () => { const menuIconButton = columnCell.querySelector('button[aria-label="Menu"]'); fireEvent.click(menuIconButton); await waitFor(() => expect(screen.queryByRole('menu')).not.to.equal(null)); - const gridWindow = document.querySelector('.MuiDataGrid-window')!; - gridWindow.dispatchEvent(new Event('scroll')); + const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; + virtualizedContainer.dispatchEvent(new Event('scroll')); await waitFor(() => expect(screen.queryByRole('menu')).to.equal(null)); }); diff --git a/packages/grid/x-grid/src/tests/events.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/events.DataGridPro.test.tsx index 9fabe4d5c06a2..9365d250f1468 100644 --- a/packages/grid/x-grid/src/tests/events.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/events.DataGridPro.test.tsx @@ -278,7 +278,7 @@ describe(' - Events Params', () => { const handleRowsScrollEnd = spy(); render(); - apiRef.current.publishEvent(GridEvents.rowsScroll); + apiRef.current.publishEvent(GridEvents.rowsScroll, { left: 0, top: 3 * 52 }); expect(handleRowsScrollEnd.callCount).to.equal(1); }); @@ -323,14 +323,16 @@ describe(' - Events Params', () => { />
, ); - const gridWindow = container.querySelector('.MuiDataGrid-window'); + const virtualizedContainer = container.querySelector('.MuiDataGrid-virtualizedContainer'); // arbitrary number to make sure that the bottom of the grid window is reached. - gridWindow.scrollTop = 12345; - gridWindow.dispatchEvent(new Event('scroll')); + virtualizedContainer.scrollTop = 12345; + virtualizedContainer.dispatchEvent(new Event('scroll')); expect(handleRowsScrollEnd.callCount).to.equal(1); }); - it('call onViewportRowsChange when the viewport rows change', async () => { + // TODO check if rowsScroll is not enough + // eslint-disable-next-line mocha/no-skipped-tests + it.skip('call onViewportRowsChange when the viewport rows change', async () => { const handleViewportRowsChange = spy(); // TODO: Set the dimensions of the grid once the Windows test issues are resolved. const { container } = render( @@ -341,11 +343,11 @@ describe(' - Events Params', () => { expect(handleViewportRowsChange.lastCall.args[0].firstRowIndex).to.equal(0); expect(handleViewportRowsChange.lastCall.args[0].lastRowIndex).to.equal(6); // should be pageSize + 1 }); - const gridWindow = container.querySelector('.MuiDataGrid-window'); + const virtualizedContainer = container.querySelector('.MuiDataGrid-virtualizedContainer'); // scroll 6 rows so that the renderContext is updated. To be changed to a scroll of 1 row. // TODO: set RowHeight directly. Currently 52 is used because the test fails under Windows. - gridWindow.scrollTop = 52 * 6; - gridWindow.dispatchEvent(new Event('scroll')); + virtualizedContainer.scrollTop = 52 * 6; + virtualizedContainer.dispatchEvent(new Event('scroll')); await waitFor(() => { expect(handleViewportRowsChange.lastCall.args[0].firstRowIndex).to.equal(6); // should be 1 expect(handleViewportRowsChange.lastCall.args[0].lastRowIndex).to.equal(12); // should be pageSize + 1 diff --git a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx index dc398913343e9..b1516e4455657 100644 --- a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx @@ -6,7 +6,7 @@ import { } from 'test/utils'; import { useFakeTimers, spy } from 'sinon'; import { expect } from 'chai'; -import { getCell, getColumnValues } from 'test/utils/helperFn'; +import { getCell, getRow, getColumnValues } from 'test/utils/helperFn'; import { GridApiRef, GridComponentProps, @@ -431,19 +431,34 @@ describe(' - Rows', () => { }); it('should render last row when scrolling to the bottom', () => { - render(); - const totalHeight = apiRef.current.state.containerSizes?.totalSizes.height!; + const rowHeight = 50; + const rowBuffer = 4; + const nbRows = 996; + const height = 600; + render( + , + ); - const gridWindow = document.querySelector('.MuiDataGrid-window')!; + const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; const renderingZone = document.querySelector('.MuiDataGrid-renderingZone')! as HTMLElement; - gridWindow.scrollTop = 10e6; // scroll to the bottom - gridWindow.dispatchEvent(new Event('scroll')); + virtualizedContainer.scrollTop = 10e6; // scroll to the bottom + virtualizedContainer.dispatchEvent(new Event('scroll')); const lastCell = document.querySelector('[role="row"]:last-child [role="cell"]:first-child')!; expect(lastCell).to.have.text('995'); - expect(renderingZone.children.length).to.equal(16); - expect(renderingZone.style.transform).to.equal('translate3d(0px, -312px, 0px)'); - expect(gridWindow.scrollHeight).to.equal(totalHeight); + expect(renderingZone.children.length).to.equal(Math.floor(height / rowHeight) + rowBuffer); + const distanceToFirstRow = (nbRows - renderingZone.children.length) * rowHeight; + expect(renderingZone.style.transform).to.equal( + `translate3d(0px, ${distanceToFirstRow}px, 0px)`, + ); + expect(virtualizedContainer.scrollHeight).to.equal(nbRows * rowHeight); }); it('Rows should not be virtualized when the grid is in pagination autoPageSize', () => { @@ -460,47 +475,74 @@ describe(' - Rows', () => { expect(isVirtualized).to.equal(false); }); - it('should set the virtual page to 0 when resetting rows to a non virtualized length', () => { - const { setProps } = render(); - - const gridWindow = document.querySelector('.MuiDataGrid-window')!; - gridWindow.scrollTop = 10e6; // scroll to the bottom - gridWindow.dispatchEvent(new Event('scroll')); - - let lastCell = document.querySelector('[role="row"]:last-child [role="cell"]:first-child')!; - expect(lastCell).to.have.text('995'); - - let virtualPage = apiRef.current.state.rendering!.virtualPage; - expect(virtualPage).to.equal(98); - - setProps({ nbRows: 9 }); - - lastCell = document.querySelector('[role="row"]:last-child [role="cell"]:first-child')!; - expect(lastCell).to.have.text('8'); - - const renderingZone = document.querySelector('.MuiDataGrid-renderingZone')! as HTMLElement; - expect(renderingZone.children.length).to.equal(9); - - virtualPage = apiRef.current.state.rendering!.virtualPage; - expect(virtualPage).to.equal(0); + it('should render extra columns when the columnBuffer prop is present', () => { + const border = 1; + const width = 300 + border * 2; + const columnBuffer = 2; + const columnWidth = 100; + render(); + const firstRow = getRow(0); + expect(firstRow.children).to.have.length(Math.floor(width / columnWidth) + columnBuffer); + const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; + virtualizedContainer.scrollLeft = 301; + virtualizedContainer.dispatchEvent(new Event('scroll')); + expect(firstRow.children).to.have.length( + columnBuffer + Math.floor(width / columnWidth) + columnBuffer, + ); + }); - const isVirtualized = apiRef.current.state.containerSizes!.isVirtualized; - expect(isVirtualized).to.equal(false); + it('should render new rows when scrolling past the rowThreshold value', () => { + const rowThreshold = 3; + const rowHeight = 50; + render( + , + ); + const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; + const renderingZone = document.querySelector('.MuiDataGrid-renderingZone')!; + const firstRow = renderingZone.firstChild; + expect(firstRow).to.have.attr('data-rowindex', '0'); + virtualizedContainer.scrollTop = rowThreshold * rowHeight; + virtualizedContainer.dispatchEvent(new Event('scroll')); + expect(firstRow).to.have.attr('data-rowindex', '3'); + }); + + it('should render new columns when scrolling past the columnThreshold value', () => { + const columnThreshold = 3; + const columnWidth = 100; + render( + , + ); + const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; + const renderingZone = document.querySelector('.MuiDataGrid-renderingZone')!; + const firstRow = renderingZone.querySelector('[role="row"]:first-child')!; + const firstColumn = firstRow.firstChild!; + expect(firstColumn).to.have.attr('data-colindex', '0'); + virtualizedContainer.scrollLeft = columnThreshold * columnWidth; + virtualizedContainer.dispatchEvent(new Event('scroll')); + expect(firstColumn).to.have.attr('data-colindex', '3'); }); describe('Pagination', () => { it('should render only the pageSize', () => { - render(); - const gridWindow = document.querySelector('.MuiDataGrid-window')!; - gridWindow.scrollTop = 10e6; // scroll to the bottom - gridWindow.dispatchEvent(new Event('scroll')); + const rowHeight = 50; + const nbRows = 32; + render( + , + ); + const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; + virtualizedContainer.scrollTop = 10e6; // scroll to the bottom + virtualizedContainer.dispatchEvent(new Event('scroll')); const lastCell = document.querySelector( '[role="row"]:last-child [role="cell"]:first-child', )!; expect(lastCell).to.have.text('31'); - const totalHeight = apiRef.current.state.containerSizes?.totalSizes.height!; - expect(gridWindow.scrollHeight).to.equal(totalHeight); + expect(virtualizedContainer.scrollHeight).to.equal(nbRows * rowHeight); }); it('should not virtualized the last page if smaller than viewport', () => { @@ -513,28 +555,24 @@ describe(' - Rows', () => { height={500} />, ); - const gridWindow = document.querySelector('.MuiDataGrid-window')!; - gridWindow.scrollTop = 10e6; // scroll to the bottom - gridWindow.dispatchEvent(new Event('scroll')); + const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; + virtualizedContainer.scrollTop = 10e6; // scroll to the bottom + virtualizedContainer.dispatchEvent(new Event('scroll')); const lastCell = document.querySelector( '[role="row"]:last-child [role="cell"]:first-child', )!; expect(lastCell).to.have.text('99'); - expect(gridWindow.scrollTop).to.equal(0); - expect(gridWindow.scrollHeight).to.equal(gridWindow.clientHeight); - - const isVirtualized = apiRef.current.state.containerSizes!.isVirtualized; - expect(isVirtualized).to.equal(false); - const virtualRowsCount = apiRef.current.state.containerSizes!.virtualRowsCount; - expect(virtualRowsCount).to.equal(4); + expect(virtualizedContainer.scrollTop).to.equal(0); + expect(virtualizedContainer.scrollHeight).to.equal(virtualizedContainer.clientHeight); + expect(document.querySelector('.MuiDataGrid-renderingZone')!.children).to.have.length(4); }); it('should paginate small dataset in auto page-size #1492', () => { render( , ); - const gridWindow = document.querySelector('.MuiDataGrid-window')!; + const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; const lastCell = document.querySelector( '[role="row"]:last-child [role="cell"]:first-child', @@ -543,13 +581,9 @@ describe(' - Rows', () => { const rows = document.querySelectorAll('.MuiDataGrid-row[role="row"]')!; expect(rows.length).to.equal(7); - expect(gridWindow.scrollTop).to.equal(0); - expect(gridWindow.scrollHeight).to.equal(gridWindow.clientHeight); - - const isVirtualized = apiRef.current.state.containerSizes!.isVirtualized; - expect(isVirtualized).to.equal(false); - const virtualRowsCount = apiRef.current.state.containerSizes!.virtualRowsCount; - expect(virtualRowsCount).to.equal(7); + expect(virtualizedContainer.scrollTop).to.equal(0); + expect(virtualizedContainer.scrollHeight).to.equal(virtualizedContainer.clientHeight); + expect(document.querySelector('.MuiDataGrid-renderingZone')!.children).to.have.length(7); }); }); @@ -568,9 +602,9 @@ describe(' - Rows', () => { rowHeight={rowHeight} />, ); - const gridWindow = document.querySelector('.MuiDataGrid-window')!; + const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; apiRef.current.scrollToIndexes({ rowIndex: 4, colIndex: 0 }); - expect(gridWindow.scrollTop).to.equal(rowHeight - offset); + expect(virtualizedContainer.scrollTop).to.equal(rowHeight - offset); }); it('should scroll correctly when the given index is partially visible at the top', () => { @@ -587,15 +621,15 @@ describe(' - Rows', () => { rowHeight={rowHeight} />, ); - const gridWindow = document.querySelector('.MuiDataGrid-window')!; - gridWindow.scrollTop = offset; - gridWindow.dispatchEvent(new Event('scroll')); // Simulate browser behavior + const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; + virtualizedContainer.scrollTop = offset; + virtualizedContainer.dispatchEvent(new Event('scroll')); // Simulate browser behavior apiRef.current.scrollToIndexes({ rowIndex: 2, colIndex: 0 }); - expect(gridWindow.scrollTop).to.equal(offset); + expect(virtualizedContainer.scrollTop).to.equal(offset); apiRef.current.scrollToIndexes({ rowIndex: 1, colIndex: 0 }); - expect(gridWindow.scrollTop).to.equal(offset); + expect(virtualizedContainer.scrollTop).to.equal(offset); apiRef.current.scrollToIndexes({ rowIndex: 0, colIndex: 0 }); - expect(gridWindow.scrollTop).to.equal(0); + expect(virtualizedContainer.scrollTop).to.equal(0); }); it('should scroll correctly when the given colIndex is partially visible at the right', () => { @@ -610,10 +644,10 @@ describe(' - Rows', () => { { field: 'age', width: columnWidth }, ]; render(); - const gridWindow = document.querySelector('.MuiDataGrid-window')!; - expect(gridWindow.scrollLeft).to.equal(0); + const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; + expect(virtualizedContainer.scrollLeft).to.equal(0); apiRef.current.scrollToIndexes({ rowIndex: 0, colIndex: 2 }); - expect(gridWindow.scrollLeft).to.equal(columnWidth * 3 - width); + expect(virtualizedContainer.scrollLeft).to.equal(columnWidth * 3 - width); }); it('should not scroll when going back', () => { @@ -628,13 +662,13 @@ describe(' - Rows', () => { { field: 'age', width: columnWidth }, ]; render(); - const gridWindow = document.querySelector('.MuiDataGrid-window')!; - expect(gridWindow.scrollLeft).to.equal(0); + const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; + expect(virtualizedContainer.scrollLeft).to.equal(0); apiRef.current.scrollToIndexes({ rowIndex: 0, colIndex: 2 }); - gridWindow.dispatchEvent(new Event('scroll')); // Simulate browser behavior - expect(gridWindow.scrollLeft).to.equal(columnWidth * 3 - width); + virtualizedContainer.dispatchEvent(new Event('scroll')); // Simulate browser behavior + expect(virtualizedContainer.scrollLeft).to.equal(columnWidth * 3 - width); apiRef.current.scrollToIndexes({ rowIndex: 0, colIndex: 1 }); - expect(gridWindow.scrollLeft).to.equal(columnWidth * 3 - width); + expect(virtualizedContainer.scrollLeft).to.equal(columnWidth * 3 - width); }); }); }); @@ -670,16 +704,6 @@ describe(' - Rows', () => { apiRef.current.setPage(1); expect(document.querySelectorAll('[role="row"][data-rowindex]')).to.have.length(50); }); - - it('should translate to the correct position on scroll', () => { - render(); - const gridWindow = document.querySelector('.MuiDataGrid-window')!; - const renderingZone = document.querySelector('.MuiDataGrid-renderingZone')! as HTMLElement; - expect(renderingZone.style.transform).to.equal('translate3d(0px, 0px, 0px)'); - gridWindow.scrollTop = 100; - gridWindow.dispatchEvent(new Event('scroll')); - expect(renderingZone.style.transform).to.equal('translate3d(0px, -100px, 0px)'); - }); }); describe('Cell focus', () => { diff --git a/packages/grid/x-grid/src/useDataGridProComponent.tsx b/packages/grid/x-grid/src/useDataGridProComponent.tsx index 45c41592f32ab..56252489e2b6d 100644 --- a/packages/grid/x-grid/src/useDataGridProComponent.tsx +++ b/packages/grid/x-grid/src/useDataGridProComponent.tsx @@ -22,15 +22,12 @@ import { useGridParamsApi } from '../../_modules_/grid/hooks/features/rows/useGr import { useGridRows } from '../../_modules_/grid/hooks/features/rows/useGridRows'; import { useGridSelection } from '../../_modules_/grid/hooks/features/selection/useGridSelection'; import { useGridSorting } from '../../_modules_/grid/hooks/features/sorting/useGridSorting'; -import { useGridVirtualization } from '../../_modules_/grid/hooks/features/virtualization/useGridVirtualization'; -import { useGridNoVirtualization } from '../../_modules_/grid/hooks/features/virtualization/useGridNoVirtualization'; import { useGridScroll } from '../../_modules_/grid/hooks/features/scroll/useGridScroll'; import { useApi } from '../../_modules_/grid/hooks/root/useApi'; import { useGridEvents } from '../../_modules_/grid/hooks/root/useGridEvents'; import { useGridContainerProps } from '../../_modules_/grid/hooks/root/useGridContainerProps'; import { useErrorHandler } from '../../_modules_/grid/hooks/utils/useErrorHandler'; import { useGridLoggerFactory } from '../../_modules_/grid/hooks/utils/useGridLogger'; -import { useRenderInfoLog } from '../../_modules_/grid/hooks/utils/useRenderInfoLog'; import { useGridResizeContainer } from '../../_modules_/grid/hooks/utils/useGridResizeContainer'; import { useStateProp } from '../../_modules_/grid/hooks/utils/useStateProp'; import { GridApiRef } from '../../_modules_/grid/models/api/gridApiRef'; @@ -59,8 +56,6 @@ export const useDataGridProComponent = (apiRef: GridApiRef, props: GridComponent useGridPage(apiRef, props); useGridContainerProps(apiRef, props); useGridScroll(apiRef, props); - useGridNoVirtualization(apiRef, props); - useGridVirtualization(apiRef, props); useGridInfiniteLoader(apiRef, props); useGridColumnMenu(apiRef); useGridKeyboard(apiRef); @@ -69,5 +64,4 @@ export const useDataGridProComponent = (apiRef: GridApiRef, props: GridComponent useGridClipboard(apiRef); useGridEvents(apiRef, props); useStateProp(apiRef, props); - useRenderInfoLog(apiRef); }; diff --git a/test/e2e/index.test.ts b/test/e2e/index.test.ts index 309cd9518f519..f723eac3065b0 100644 --- a/test/e2e/index.test.ts +++ b/test/e2e/index.test.ts @@ -202,12 +202,14 @@ describe('e2e', () => { document.querySelector('[role="row"][data-rowindex="3"] [role="cell"]')!.scrollIntoView(), ); const scrollTop = await page.evaluate( - () => document.querySelector('.MuiDataGrid-window')!.scrollTop!, + () => document.querySelector('.MuiDataGrid-virtualizedContainer')!.scrollTop!, ); expect(scrollTop).not.to.equal(0); await page.click('[role="row"][data-rowindex="3"] [role="cell"]'); expect( - await page.evaluate(() => document.querySelector('.MuiDataGrid-window')!.scrollTop!), + await page.evaluate( + () => document.querySelector('.MuiDataGrid-virtualizedContainer')!.scrollTop!, + ), ).to.equal(scrollTop); }); }); From 3883d05098399268980b8084850b7316aad98f70 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 13 Oct 2021 09:18:27 +0200 Subject: [PATCH 182/390] Prettier --- .../grid/_modules_/grid/hooks/core/useGridInitialization.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/hooks/core/useGridInitialization.ts b/packages/grid/_modules_/grid/hooks/core/useGridInitialization.ts index f20527bb7284a..79a09b52a8b7f 100644 --- a/packages/grid/_modules_/grid/hooks/core/useGridInitialization.ts +++ b/packages/grid/_modules_/grid/hooks/core/useGridInitialization.ts @@ -7,7 +7,7 @@ import { useGridErrorHandler } from './useGridErrorHandler'; import { useGridControlState } from './useGridControlState'; import { useGridLocaleText } from './useGridLocaleText'; import { useGridStateProp } from './useGridStateProp'; -import {useGridRowGroupsPreProcessing} from "./rowGroupsPerProcessing"; +import { useGridRowGroupsPreProcessing } from './rowGroupsPerProcessing'; /** * Initialize the technical pieces of the DataGrid (logger, state, ...) that any DataGrid implementation needs From feba08f709c2748e4c2e35d822cfb443f0ce946b Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 13 Oct 2021 09:32:25 +0200 Subject: [PATCH 183/390] Merge --- .../grid/components/cell/GridTreeDataGroupingCell.tsx | 2 +- .../_modules_/grid/hooks/features/rows/useGridRows.ts | 2 +- .../grid/hooks/features/treeData/useGridTreeData.ts | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index b01ddac53407b..efc0db40db98a 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -4,7 +4,7 @@ import { makeStyles } from '@mui/styles'; import IconButton from '@mui/material/IconButton'; import Box from '@mui/material/Box'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; -import { useGridApiContext } from '../../hooks/root/useGridApiContext'; +import { useGridApiContext } from '../../hooks/utils/useGridApiContext'; import { GridRenderCellParams } from '../../models/params/gridCellParams'; const useStyles = makeStyles({ diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 833d8b201e86b..bf58bfcb09cf7 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -22,7 +22,7 @@ import { gridRowTreeSelector, gridRowIdsSelector, } from './gridRowsSelector'; -import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; +import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; export type GridRowsInternalCacheState = Omit< GridRowsState, diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 65a5090c80f42..2f13007460a74 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -1,15 +1,15 @@ import * as React from 'react'; -import { GridRowConfigTree, GridRowId, GridRowsLookup } from '../../../models/gridRows'; +import { GridRowConfigTree, GridRowsLookup } from '../../../models/gridRows'; import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridComponentProps } from '../../../GridComponentProps'; -import { GridColumnsPreProcessing } from '../../root/columnsPreProcessing'; +import { GridColumnsPreProcessing } from '../../core/columnsPreProcessing'; import { GRID_TREE_DATA_GROUP_COL_DEF } from './gridTreeDataGroupColDef'; -import { useGridApiEventHandler } from '../../root/useGridApiEventHandler'; +import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { GridEvents } from '../../../constants'; import { GridCellParams, GridColDef, MuiEvent } from '../../../models'; import { isSpaceKey } from '../../../utils/keyboardUtils'; import { useFirstRender } from '../../utils/useFirstRender'; -import { GridRowGroupingPreProcessing } from '../../root/rowGroupsPerProcessing'; +import { GridRowGroupingPreProcessing } from '../../core/rowGroupsPerProcessing'; import { GridNodeNameToIdTree, insertLeafInTree } from '../rows/gridRowsUtils'; /** From 17f55b3848e34a46392bf167332717aa43affb07 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 13 Oct 2021 09:44:45 +0200 Subject: [PATCH 184/390] Work --- .../CustomGroupingColumnTreeData.js | 21 +++++++++++++------ .../DisableChildrenFilteringTreeData.js | 1 + .../DisableChildrenSortingTreeData.js | 1 + .../group-pivot/SetRowExpansionTreeData.js | 1 + .../data-grid/scrolling/ScrollPlayground.js | 6 +++--- .../columnSelection/GridHeaderCheckbox.tsx | 4 ++-- .../features/filter/gridFilterSelector.ts | 7 ------- .../hooks/features/filter/useGridFilter.ts | 11 +++++----- .../pagination/gridPaginationSelector.ts | 2 +- .../features/sorting/gridSortingSelector.ts | 11 ---------- .../hooks/features/sorting/useGridSorting.ts | 10 ++++----- .../grid/_modules_/grid/models/gridRows.ts | 2 -- 12 files changed, 35 insertions(+), 42 deletions(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js index 4fcf87f52e1c3..00d6a11a610cb 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js @@ -1,26 +1,31 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { DataGridPro, useGridSlotComponentProps } from '@mui/x-data-grid-pro'; +import { + DataGridPro, + useGridRootProps, + useGridSlotComponentProps, +} from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; const CustomGridTreeDataGroupingCell = (props) => { - const { id } = props; + const { id, row } = props; const { apiRef } = useGridSlotComponentProps(); + const rootProps = useGridRootProps(); const node = apiRef.current.UNSTABLE_getRowNode(id); - const path = apiRef.current.UNSTABLE_getRowPath(id); + const path = rootProps.getTreeDataPath(row); - if (!node || !path) { + if (!node) { throw new Error(`MUI: No row with id #${id} found`); } return (
- {node.children?.size ? ( + {node.children?.length ? ( ) : ( @@ -43,6 +48,10 @@ CustomGridTreeDataGroupingCell.propTypes = { * The grid row id. */ id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + /** + * The row model of the row that the current cell belongs to. + */ + row: PropTypes.object.isRequired, }; const groupingColDef = { diff --git a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js index af42859a9c41d..c69433ee3d201 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js @@ -7,6 +7,7 @@ export default function DisableChildrenFilteringTreeData() { rowLength: [10, 5, 3], randomLength: true, }); + const [filterModel, setFilterModel] = React.useState({ items: [{ columnField: 'index', operatorValue: '>', value: 2 }], }); diff --git a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js index bc5d1ef764980..88a453bb118eb 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js @@ -7,6 +7,7 @@ export default function DisableChildrenSortingTreeData() { rowLength: [10, 5, 3], randomLength: true, }); + const [sortModel, setSortingModel] = React.useState([ { field: 'index', sort: 'desc' }, ]); diff --git a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js index 0ab5d43d472f6..6aebe6a09a9ad 100644 --- a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js @@ -9,6 +9,7 @@ export default function SetRowExpansionTreeData() { rowLength: [10, 5, 3], randomLength: true, }); + const apiRef = useGridApiRef(); const toggleFirstRow = () => { diff --git a/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.js b/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.js index ea8fbc691c494..506419fa0170d 100644 --- a/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.js +++ b/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.js @@ -10,7 +10,7 @@ import { gridVisibleRowCountSelector, visibleGridColumnsLengthSelector, visibleGridColumnsSelector, - gridSortedRowIdsFlatSelector, + gridSortedRowIdsSelector, } from '@mui/x-data-grid-pro'; import { useDemoData } from '@mui/x-data-grid-generator'; @@ -30,7 +30,7 @@ export default function ScrollPlayground() { React.useEffect(() => { const { rowIndex, colIndex } = coordinates; apiRef.current.scrollToIndexes(coordinates); - const id = gridSortedRowIdsFlatSelector(apiRef.current.state)[rowIndex]; + const id = gridSortedRowIdsSelector(apiRef.current.state)[rowIndex]; const column = visibleGridColumnsSelector(apiRef.current.state)[colIndex]; apiRef.current.setCellFocus(id, column.field); }, [apiRef, coordinates]); @@ -56,7 +56,7 @@ export default function ScrollPlayground() { }; const handleCellClick = (params) => { - const rowIndex = gridSortedRowIdsFlatSelector(apiRef.current.state).findIndex( + const rowIndex = gridSortedRowIdsSelector(apiRef.current.state).findIndex( (id) => id === params.id, ); diff --git a/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx b/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx index 9cac45e35e49c..858e8736a1a0d 100644 --- a/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx +++ b/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { GridEvents } from '../../constants/eventsConstants'; import { useGridSelector } from '../../hooks/utils/useGridSelector'; -import { gridSortedVisiblePaginatedRowsAsArrayFlatSelector } from '../../hooks/features/pagination/gridPaginationSelector'; +import { gridSortedVisiblePaginatedRowEntriesSelector } from '../../hooks/features/pagination/gridPaginationSelector'; import { gridSortedVisibleRowEntriesSelector } from '../../hooks/features/filter/gridFilterSelector'; import { gridTabIndexColumnHeaderSelector } from '../../hooks/features/focus/gridFocusStateSelector'; import { gridRowCountSelector } from '../../hooks/features/rows/gridRowsSelector'; @@ -58,7 +58,7 @@ const GridHeaderCheckbox = React.forwardRef row.id); diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index 6806955bc2770..946280af6be4b 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -1,6 +1,5 @@ import { createSelector } from 'reselect'; import { GridFilterItem } from '../../../models/gridFilterItem'; -import { GridRowId, GridRowModel } from '../../../models/gridRows'; import { GridState } from '../../../models/gridState'; import { gridSortedRowEntriesSelector } from '../sorting/gridSortingSelector'; import { gridColumnLookupSelector } from '../columns/gridColumnsSelector'; @@ -30,12 +29,6 @@ export const gridSortedVisibleRowEntriesSelector = createSelector( sortedRows.filter((row) => visibleRowsLookup[row.id] !== false), ); -export const gridSortedVisibleRowsMapSelector = createSelector( - gridSortedVisibleRowEntriesSelector, - (visibleSortedRows) => - new Map(visibleSortedRows.map((row) => [row.id, row.model])), -); - export const gridSortedVisibleTopLevelRowEntriesSelector = createSelector( gridSortedVisibleRowEntriesSelector, gridRowTreeSelector, diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 3e05606d8c09c..a48a46f835d6f 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -17,12 +17,13 @@ import { getDefaultGridFilterModel, getEmptyVisibleRows } from './gridFilterStat import { GridFilterModel } from '../../../models/gridFilterModel'; import { gridVisibleRowsLookupSelector, - gridSortedVisibleRowsMapSelector, gridFilterModelSelector, + gridSortedVisibleRowEntriesSelector, } from './gridFilterSelector'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; import { gridRowTreeSelector } from '../rows'; +import { GridRowId, GridRowModel } from '../../../models'; const checkFilterModelValidity = (model: GridFilterModel) => { if (model.items.length > 1) { @@ -303,10 +304,10 @@ export const useGridFilter = ( [apiRef, logger, setGridState], ); - const getVisibleRowModels = React.useCallback( - () => gridSortedVisibleRowsMapSelector(apiRef.current.state), - [apiRef], - ); + const getVisibleRowModels = React.useCallback(() => { + const visibleSortedRows = gridSortedVisibleRowEntriesSelector(apiRef.current.state); + return new Map(visibleSortedRows.map((row) => [row.id, row.model])); + }, [apiRef]); useGridApiMethod( apiRef, diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts index 3bbec069471e9..f369c866d8ac5 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts @@ -15,7 +15,7 @@ export const gridPageSizeSelector = createSelector( (pagination) => pagination.pageSize, ); -export const gridSortedVisiblePaginatedRowsAsArrayFlatSelector = createSelector( +export const gridSortedVisiblePaginatedRowEntriesSelector = createSelector( gridPaginationSelector, gridSortedVisibleRowEntriesSelector, (pagination, visibleSortedRows) => { diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts index 947adb577141e..b66cb4785e452 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts @@ -2,7 +2,6 @@ import { createSelector } from 'reselect'; import { GridSortDirection, GridSortModel } from '../../../models/gridSortModel'; import { GridState } from '../../../models/gridState'; import { gridRowsLookupSelector } from '../rows/gridRowsSelector'; -import type { GridRowId, GridRowModel } from '../../../models'; const gridSortingStateSelector = (state: GridState) => state.sorting; @@ -17,16 +16,6 @@ export const gridSortedRowEntriesSelector = createSelector( (sortedIds, idRowsLookup) => sortedIds.map((id) => ({ id, model: idRowsLookup[id] })), ); -export const gridSortedRowsMapSelector = createSelector( - gridSortedRowEntriesSelector, - (sortedRows) => new Map(sortedRows.map((row) => [row.id, row.model])), -); - -export const gridSortedRowsAsArraySelector = createSelector( - gridSortedRowsMapSelector, - (sortedRows) => Array.from(sortedRows.values()), -); - export const gridSortModelSelector = createSelector( gridSortingStateSelector, (sorting) => sorting.sortModel, diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index c52c35426fead..c1c64d478c053 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -24,7 +24,7 @@ import { allGridColumnsSelector } from '../columns/gridColumnsSelector'; import { useGridState } from '../../utils/useGridState'; import { gridSortedRowIdsSelector, - gridSortedRowsAsArraySelector, + gridSortedRowEntriesSelector, gridSortModelSelector, } from './gridSortingSelector'; import { gridRowExpandedTreeSelector } from '../rows'; @@ -297,10 +297,10 @@ export const useGridSorting = ( [apiRef], ); - const getSortedRows = React.useCallback( - () => gridSortedRowsAsArraySelector(apiRef.current.state), - [apiRef], - ); + const getSortedRows = React.useCallback(() => { + const sortedRows = gridSortedRowEntriesSelector(apiRef.current.state); + return sortedRows.map((row) => row.model); + }, [apiRef]); const getSortedRowIds = React.useCallback( () => gridSortedRowIdsSelector(apiRef.current.state), diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index 5d756c5f12b05..8364ba7077f26 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -30,8 +30,6 @@ export type GridRowConfigTree = Record; export type GridRowsLookup = Record; -export type GridRowEntry = { id: GridRowId; model: GridRowModel }; - /** * The type of Id supported by the grid. */ From e23aa30312ca3461a29a95e3022f64862815e7a7 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 13 Oct 2021 10:14:02 +0200 Subject: [PATCH 185/390] Fix --- .../hooks/features/rows/gridRowsSelector.ts | 8 -- .../hooks/features/sorting/useGridSorting.ts | 76 +++++++++---------- 2 files changed, 38 insertions(+), 46 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts index a63fdb46cfe44..c30ee988ca6a4 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts @@ -20,14 +20,6 @@ export const gridRowsLookupSelector = createSelector( export const gridRowTreeSelector = createSelector(gridRowsStateSelector, (rows) => rows.tree); -export const gridRowExpandedTreeSelector = createSelector(gridRowTreeSelector, (rowsTree) => - Object.fromEntries( - Object.entries(rowsTree).filter( - ([, value]) => value.parent == null || rowsTree[value.parent]?.expanded, - ), - ), -); - export const gridRowIdsSelector = createSelector(gridRowTreeSelector, (rowsTree) => Object.values(rowsTree).map((node) => node.id), ); diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index c1c64d478c053..2b9365c398581 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -27,7 +27,7 @@ import { gridSortedRowEntriesSelector, gridSortModelSelector, } from './gridSortingSelector'; -import { gridRowExpandedTreeSelector } from '../rows'; +import { gridRowTreeSelector } from '../rows'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; @@ -171,69 +171,69 @@ export const useGridSorting = ( ); const applySorting = React.useCallback(() => { - const unsortedRowTree = gridRowExpandedTreeSelector(apiRef.current.state); - const skipServerSorting = props.sortingMode === GridFeatureModeConstant.server; + const unsortedRows = gridRowTreeSelector(apiRef.current.state); - if (skipServerSorting) { + if (props.sortingMode === GridFeatureModeConstant.server) { logger.debug('Skipping sorting rows as sortingMode = server'); + setGridState((state) => ({ + ...state, + sorting: { ...state.sorting, sortedRows: apiRef.current.getAllRowIds() }, + })); + return; } const sortModel = gridSortModelSelector(apiRef.current.state); const comparatorList = buildComparatorList(sortModel); const aggregatedComparator = comparatorListAggregate(comparatorList); - const sortRowList = (rowList: GridRowConfigTreeNode[]): GridRowConfigTreeNode[] => { - if (rowList.length === 0) { - return []; - } - + const sortRowList = (rowList: GridRowConfigTreeNode[]): GridRowId[] => { const depth = rowList[0].depth; - - if ((depth > 0 && props.disableChildrenSorting) || skipServerSorting) { - return rowList; + if (depth > 0 && props.disableChildrenSorting || comparatorList.length === 0) { + return rowList.map(row => row.id); } - const rowsWithParams = rowList.map((value) => { - const params = comparatorList.map((colComparator) => - getSortCellParams(value.id, colComparator.field), - ); - - return { value, params }; - }); - - const sortedRowsWithParams = skipServerSorting - ? rowsWithParams - : rowsWithParams.sort((a, b) => aggregatedComparator(a.params, b.params)); - - return sortedRowsWithParams.map((row) => row.value); + return rowList + .map((value) => ({ + value, + params: comparatorList.map((colComparator) => + getSortCellParams(value.id, colComparator.field), + ), + })) + .sort((a, b) => aggregatedComparator(a.params, b.params)) + .map((row) => row.value.id); }; const groupedByParentRows = new Map([[null, []]]); - Object.values(unsortedRowTree).forEach((node) => { - let group = groupedByParentRows.get(node.parent); - if (!group) { - group = []; - groupedByParentRows.set(node.parent, group); + Object.values(unsortedRows).forEach((node) => { + const isExpanded = node.parent == null || unsortedRows[node.parent].expanded + + if (isExpanded) { + let group = groupedByParentRows.get(node.parent); + if (!group) { + group = []; + groupedByParentRows.set(node.parent, group); + } + group.push(node); } - group.push(node); }); - groupedByParentRows.forEach((unsortedRows, parent) => { - groupedByParentRows.set(parent, sortRowList(unsortedRows)); + const sortedGroupedByParentRows = new Map() + groupedByParentRows.forEach((rowList, parent) => { + sortedGroupedByParentRows.set(parent, sortRowList(rowList)); }); let sortedRows: GridRowId[] = []; - const insertRowListIntoSortedRows = (startIndex: number, rowList: GridRowConfigTreeNode[]) => { + const insertRowListIntoSortedRows = (startIndex: number, rowList: GridRowId[]) => { sortedRows = [ ...sortedRows.slice(0, startIndex), - ...rowList.map((row) => row.id), + ...rowList, ...sortedRows.slice(startIndex), ]; let treeSize = 0; - rowList.forEach((row) => { + rowList.forEach((rowId) => { treeSize += 1; - const children = groupedByParentRows.get(row.id); + const children = sortedGroupedByParentRows.get(rowId); if (children?.length) { const subTreeSize = insertRowListIntoSortedRows(startIndex + treeSize, children); treeSize += subTreeSize; @@ -243,7 +243,7 @@ export const useGridSorting = ( return treeSize; }; - insertRowListIntoSortedRows(0, groupedByParentRows.get(null)!); + insertRowListIntoSortedRows(0, sortedGroupedByParentRows.get(null)!); setGridState((state) => ({ ...state, From 26727cbe9ac27e4c339370c7a59c5d208e6126e7 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 13 Oct 2021 10:16:48 +0200 Subject: [PATCH 186/390] Work --- .../grid/hooks/features/sorting/useGridSorting.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 2b9365c398581..b729b83a9b6c2 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -171,8 +171,6 @@ export const useGridSorting = ( ); const applySorting = React.useCallback(() => { - const unsortedRows = gridRowTreeSelector(apiRef.current.state); - if (props.sortingMode === GridFeatureModeConstant.server) { logger.debug('Skipping sorting rows as sortingMode = server'); setGridState((state) => ({ @@ -182,6 +180,7 @@ export const useGridSorting = ( return; } + const rowTree = gridRowTreeSelector(apiRef.current.state); const sortModel = gridSortModelSelector(apiRef.current.state); const comparatorList = buildComparatorList(sortModel); const aggregatedComparator = comparatorListAggregate(comparatorList); @@ -204,8 +203,8 @@ export const useGridSorting = ( }; const groupedByParentRows = new Map([[null, []]]); - Object.values(unsortedRows).forEach((node) => { - const isExpanded = node.parent == null || unsortedRows[node.parent].expanded + Object.values(rowTree).forEach((node) => { + const isExpanded = node.parent == null || rowTree[node.parent].expanded if (isExpanded) { let group = groupedByParentRows.get(node.parent); From ec5f5bf223e2fdc01da1aca52b09cae110ab9eff Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 13 Oct 2021 10:22:11 +0200 Subject: [PATCH 187/390] Work --- .../hooks/features/sorting/useGridSorting.ts | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index b729b83a9b6c2..cc8bca46958aa 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -185,23 +185,7 @@ export const useGridSorting = ( const comparatorList = buildComparatorList(sortModel); const aggregatedComparator = comparatorListAggregate(comparatorList); - const sortRowList = (rowList: GridRowConfigTreeNode[]): GridRowId[] => { - const depth = rowList[0].depth; - if (depth > 0 && props.disableChildrenSorting || comparatorList.length === 0) { - return rowList.map(row => row.id); - } - - return rowList - .map((value) => ({ - value, - params: comparatorList.map((colComparator) => - getSortCellParams(value.id, colComparator.field), - ), - })) - .sort((a, b) => aggregatedComparator(a.params, b.params)) - .map((row) => row.value.id); - }; - + // Group the rows by parent const groupedByParentRows = new Map([[null, []]]); Object.values(rowTree).forEach((node) => { const isExpanded = node.parent == null || rowTree[node.parent].expanded @@ -216,11 +200,28 @@ export const useGridSorting = ( } }); + // Apply the sorting to each list of children const sortedGroupedByParentRows = new Map() groupedByParentRows.forEach((rowList, parent) => { - sortedGroupedByParentRows.set(parent, sortRowList(rowList)); + const depth = rowList[0].depth; + if (depth > 0 && props.disableChildrenSorting || comparatorList.length === 0) { + sortedGroupedByParentRows.set(parent, rowList.map(row => row.id)); + } + + const sortedRowList = rowList + .map((value) => ({ + value, + params: comparatorList.map((colComparator) => + getSortCellParams(value.id, colComparator.field), + ), + })) + .sort((a, b) => aggregatedComparator(a.params, b.params)) + .map((row) => row.value.id); + + sortedGroupedByParentRows.set(parent, sortedRowList) }); + // Flatten the sorted lists to have children just after their parent let sortedRows: GridRowId[] = []; const insertRowListIntoSortedRows = (startIndex: number, rowList: GridRowId[]) => { sortedRows = [ From c06a4cbcf88d889614bd6005c44669648d9ebd2e Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 13 Oct 2021 10:46:16 +0200 Subject: [PATCH 188/390] Work --- .../features/export/useGridCsvExport.tsx | 1 - .../hooks/features/rows/gridRowsSelector.ts | 4 +-- .../grid/hooks/features/rows/gridRowsState.ts | 3 +- .../grid/hooks/features/rows/gridRowsUtils.ts | 26 +++++++--------- .../grid/hooks/features/rows/useGridRows.ts | 12 +++---- .../hooks/features/sorting/useGridSorting.ts | 31 ++++++++++--------- .../features/treeData/useGridTreeData.ts | 7 ++--- 7 files changed, 39 insertions(+), 45 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx index 8dd7d3941a743..a3d6982f2d8da 100644 --- a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx +++ b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx @@ -42,7 +42,6 @@ export const useGridCsvExport = (apiRef: GridApiRef): void => { exportedColumns = validColumns.filter((column) => !column.disableExport); } - // TODO: Use a selector with only the ids let exportedRowIds = visibleSortedRows.map((el) => el.id); if (selection.length) { exportedRowIds = exportedRowIds.filter((id) => selection.includes(id)); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts index c30ee988ca6a4..3e3bf73770339 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts @@ -20,6 +20,4 @@ export const gridRowsLookupSelector = createSelector( export const gridRowTreeSelector = createSelector(gridRowsStateSelector, (rows) => rows.tree); -export const gridRowIdsSelector = createSelector(gridRowTreeSelector, (rowsTree) => - Object.values(rowsTree).map((node) => node.id), -); +export const gridRowIdsSelector = createSelector(gridRowsStateSelector, rows => rows.rowIds) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts index 9ef739049eda4..37d461266283f 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts @@ -1,7 +1,8 @@ -import { GridRowConfigTree, GridRowsLookup } from '../../../models/gridRows'; +import {GridRowConfigTree, GridRowId, GridRowsLookup} from '../../../models/gridRows'; export interface GridRowsState { idRowsLookup: GridRowsLookup; + rowIds: GridRowId[]; tree: GridRowConfigTree; /** diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts index 72e8f7b7a0050..fa4c55c68292a 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts @@ -5,24 +5,22 @@ import type { GridRowsLookup, } from '../../../models'; -export type GridNodeNameToIdTreeNode = { id: GridRowId; children: GridNodeNameToIdTree }; -export type GridNodeNameToIdTree = Map; +export type GridNodeNameToIdTree = { + [nodeName: string]: { id: GridRowId; children: GridNodeNameToIdTree }; +}; -export const insertLeafInTree = ({ - tree, - path, - id, - defaultGroupingExpansionDepth, - idRowsLookup, - nodeNameToIdTree, -}: { +interface InsertRowInTreeParams { tree: GridRowConfigTree; path: string[]; id: GridRowId; defaultGroupingExpansionDepth: number; idRowsLookup: GridRowsLookup; nodeNameToIdTree: GridNodeNameToIdTree; -}) => { +} + +export const insertRowInTree = (params: InsertRowInTreeParams) => { + const { tree, path, id, defaultGroupingExpansionDepth, idRowsLookup, nodeNameToIdTree } = params; + let nodeNameToIdSubTree = nodeNameToIdTree; let parentNode: GridRowConfigTreeNode | null = null; @@ -32,13 +30,13 @@ export const insertLeafInTree = ({ const expanded = defaultGroupingExpansionDepth > depth; - let nodeNameConfig = nodeNameToIdSubTree.get(nodeName); + let nodeNameConfig = nodeNameToIdSubTree[nodeName]; if (!nodeNameConfig) { nodeId = depth === path.length - 1 ? id : `filler-row-${path.slice(0, depth + 1).join('-')}`; - nodeNameConfig = { id: nodeId, children: new Map() }; - nodeNameToIdSubTree.set(nodeName, nodeNameConfig); + nodeNameConfig = { id: nodeId, children: {} }; + nodeNameToIdSubTree[nodeName] = nodeNameConfig; } else { nodeId = nodeNameConfig.id; } diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index bf58bfcb09cf7..8511b4698828f 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -87,7 +87,7 @@ const getRowsStateFromCache = ( rowsCache: GridRowsInternalCache, apiRef: GridApiRef, ): GridRowsState => { - const { rowIds, idRowsLookup, propRowCount = 0, propGetRowId, ...rowState } = rowsCache.state; + const { rowIds, idRowsLookup, propRowCount = 0 } = rowsCache.state; const groupingResponse = apiRef.current.groupRows({ idRowsLookup, @@ -101,7 +101,7 @@ const getRowsStateFromCache = ( const totalTopLevelRowCount = propRowCount > dataTopLevelRowCount ? propRowCount : dataTopLevelRowCount; - return { ...rowState, ...groupingResponse, totalRowCount, totalTopLevelRowCount }; + return { ...groupingResponse, rowIds, totalRowCount, totalTopLevelRowCount }; }; // The cache is always redefined synchronously in `useGridStateInit` so this object don't need to be regenerated across DataGrid instances. @@ -286,20 +286,16 @@ export const useGridRows = ( const setRowExpansion = React.useCallback( (id, isExpanded) => { setGridState((state) => { - const node = state.rows.tree[id]; - + const node = apiRef.current.UNSTABLE_getRowNode(id); if (!node) { throw new Error(`MUI: No row with id #${id} found`); } - const newTree = { ...state.rows.tree }; - newTree[id] = { ...node, expanded: isExpanded }; - return { ...state, rows: { ...state.rows, - tree: newTree, + tree: { ...state.rows.tree, [id]: { ...node, expanded: isExpanded } }, }, }; }); diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index cc8bca46958aa..7dd671339e40d 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -188,7 +188,7 @@ export const useGridSorting = ( // Group the rows by parent const groupedByParentRows = new Map([[null, []]]); Object.values(rowTree).forEach((node) => { - const isExpanded = node.parent == null || rowTree[node.parent].expanded + const isExpanded = node.parent == null || rowTree[node.parent].expanded; if (isExpanded) { let group = groupedByParentRows.get(node.parent); @@ -201,24 +201,27 @@ export const useGridSorting = ( }); // Apply the sorting to each list of children - const sortedGroupedByParentRows = new Map() + const sortedGroupedByParentRows = new Map(); groupedByParentRows.forEach((rowList, parent) => { const depth = rowList[0].depth; - if (depth > 0 && props.disableChildrenSorting || comparatorList.length === 0) { - sortedGroupedByParentRows.set(parent, rowList.map(row => row.id)); + if ((depth > 0 && props.disableChildrenSorting) || comparatorList.length === 0) { + sortedGroupedByParentRows.set( + parent, + rowList.map((row) => row.id), + ); } const sortedRowList = rowList - .map((value) => ({ - value, - params: comparatorList.map((colComparator) => - getSortCellParams(value.id, colComparator.field), - ), - })) - .sort((a, b) => aggregatedComparator(a.params, b.params)) - .map((row) => row.value.id); - - sortedGroupedByParentRows.set(parent, sortedRowList) + .map((value) => ({ + value, + params: comparatorList.map((colComparator) => + getSortCellParams(value.id, colComparator.field), + ), + })) + .sort((a, b) => aggregatedComparator(a.params, b.params)) + .map((row) => row.value.id); + + sortedGroupedByParentRows.set(parent, sortedRowList); }); // Flatten the sorted lists to have children just after their parent diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 2f13007460a74..f9b01ee03ce23 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -10,7 +10,7 @@ import { GridCellParams, GridColDef, MuiEvent } from '../../../models'; import { isSpaceKey } from '../../../utils/keyboardUtils'; import { useFirstRender } from '../../utils/useFirstRender'; import { GridRowGroupingPreProcessing } from '../../core/rowGroupsPerProcessing'; -import { GridNodeNameToIdTree, insertLeafInTree } from '../rows/gridRowsUtils'; +import { GridNodeNameToIdTree, insertRowInTree } from '../rows/gridRowsUtils'; /** * Only available in DataGridPro @@ -62,11 +62,10 @@ export const useGridTreeData = ( const tree: GridRowConfigTree = {}; const idRowsLookup: GridRowsLookup = { ...params.idRowsLookup }; - - const nodeNameToIdTree: GridNodeNameToIdTree = new Map(); + const nodeNameToIdTree: GridNodeNameToIdTree = {}; rows.forEach((row) => { - insertLeafInTree({ + insertRowInTree({ tree, path: row.path, id: row.id, From 8e4dd5117c8976ad0d2ae95574f1b8564abf8a76 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 13 Oct 2021 10:50:00 +0200 Subject: [PATCH 189/390] Code review --- .../gridRowGroupsPreProcessingApi.ts | 8 ++--- .../useGridRowGroupsPreProcessing.ts | 35 ++++++++++--------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts index 9042056bb08eb..1ef0117107ebe 100644 --- a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts @@ -1,6 +1,6 @@ import { GridRowConfigTree, GridRowId, GridRowsLookup } from '../../../models/gridRows'; -export type RwoGroupParams = { +export type RowGroupParams = { ids: GridRowId[]; idRowsLookup: GridRowsLookup; }; @@ -11,7 +11,7 @@ export interface GridRowGroupingResult { idRowsLookup: GridRowsLookup; } -export type GridRowGroupingPreProcessing = (params: RwoGroupParams) => GridRowGroupingResult | null; +export type GridRowGroupingPreProcessing = (params: RowGroupParams) => GridRowGroupingResult | null; export interface GridRowGroupsPreProcessingApi { /** @@ -20,7 +20,7 @@ export interface GridRowGroupsPreProcessingApi { * @param {GridRowGroupingPreProcessing} columnsPreProcessing Pre-processing to register. * @ignore - do not document */ - registerRowGroupsBuilder: ( + UNSTABLE_registerRowGroupsBuilder: ( processingName: string, groupingFunction: GridRowGroupingPreProcessing | null, ) => void; @@ -32,5 +32,5 @@ export interface GridRowGroupsPreProcessingApi { * @returns {GridRowGroupingResult} The grouped rows * @ignore - do not document */ - groupRows: (params: RwoGroupParams) => GridRowGroupingResult; + UNSTABLE_groupRows: (params: RowGroupParams) => GridRowGroupingResult; } diff --git a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts index 81f7a270fc3d9..5eb74c3bc0a4f 100644 --- a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts +++ b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts @@ -20,7 +20,7 @@ export const useGridRowGroupsPreProcessing = (apiRef: GridApiRef) => { ); const registerRowGroupsBuilder = React.useCallback< - GridRowGroupsPreProcessingApi['registerRowGroupsBuilder'] + GridRowGroupsPreProcessingApi['UNSTABLE_registerRowGroupsBuilder'] >( (processingName, rowGroupingPreProcessing) => { const rowGroupingPreProcessingBefore = @@ -34,28 +34,31 @@ export const useGridRowGroupsPreProcessing = (apiRef: GridApiRef) => { [apiRef], ); - const groupRows = React.useCallback((...params) => { - let response: GridRowGroupingResult | null = null; - const preProcessingList = Array.from(rowGroupsPreProcessingRef.current.values()); + const groupRows = React.useCallback( + (...params) => { + let response: GridRowGroupingResult | null = null; + const preProcessingList = Array.from(rowGroupsPreProcessingRef.current.values()); - while (!response && preProcessingList.length) { - const preProcessing = preProcessingList.shift(); + while (!response && preProcessingList.length) { + const preProcessing = preProcessingList.shift(); - if (preProcessing) { - response = preProcessing(...params); + if (preProcessing) { + response = preProcessing(...params); + } } - } - if (!response) { - return getFlatRowTree(...params)!; - } + if (!response) { + return getFlatRowTree(...params)!; + } - return response; - }, []); + return response; + }, + [], + ); const rowGroupsPreProcessingApi: GridRowGroupsPreProcessingApi = { - registerRowGroupsBuilder, - groupRows, + UNSTABLE_registerRowGroupsBuilder: registerRowGroupsBuilder, + UNSTABLE_groupRows: groupRows, }; useGridApiMethod(apiRef, rowGroupsPreProcessingApi, 'GridRowGroupsPreProcessing'); From 78d7f85b06ddba1d747db2e75913254fe3a13305 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 13 Oct 2021 10:53:34 +0200 Subject: [PATCH 190/390] Merge --- .../grid/_modules_/grid/hooks/features/rows/useGridRows.ts | 2 +- .../_modules_/grid/hooks/features/treeData/useGridTreeData.ts | 4 ++-- packages/grid/data-grid/src/useDataGridComponent.tsx | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 8511b4698828f..35309245f3009 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -89,7 +89,7 @@ const getRowsStateFromCache = ( ): GridRowsState => { const { rowIds, idRowsLookup, propRowCount = 0 } = rowsCache.state; - const groupingResponse = apiRef.current.groupRows({ + const groupingResponse = apiRef.current.UNSTABLE_groupRows({ idRowsLookup, ids: rowIds, }); diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index f9b01ee03ce23..9e294712d2b0c 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -45,7 +45,7 @@ export const useGridTreeData = ( const updateRowGrouping = React.useCallback(() => { if (!props.treeData) { - return apiRef.current.registerRowGroupsBuilder('treeData', null); + return apiRef.current.UNSTABLE_registerRowGroupsBuilder('treeData', null); } const groupRows: GridRowGroupingPreProcessing = (params) => { @@ -81,7 +81,7 @@ export const useGridTreeData = ( }; }; - return apiRef.current.registerRowGroupsBuilder('treeData', groupRows); + return apiRef.current.UNSTABLE_registerRowGroupsBuilder('treeData', groupRows); }, [apiRef, props.getTreeDataPath, props.treeData, props.defaultGroupingExpansionDepth]); useFirstRender(() => { diff --git a/packages/grid/data-grid/src/useDataGridComponent.tsx b/packages/grid/data-grid/src/useDataGridComponent.tsx index d1a8e107b4348..07606c3980c70 100644 --- a/packages/grid/data-grid/src/useDataGridComponent.tsx +++ b/packages/grid/data-grid/src/useDataGridComponent.tsx @@ -31,7 +31,6 @@ export const useDataGridComponent = (apiRef: GridApiRef, props: GridComponentPro useGridResizeContainer(apiRef, props); useGridSelection(apiRef, props); useGridColumns(apiRef, props); - useGridFreezeRows(apiRef, props); useGridRows(apiRef, props); useGridParamsApi(apiRef); useGridEditRows(apiRef, props); From 742843313bbdb561ef7a929a4e62c53c5044a09e Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 13 Oct 2021 10:55:11 +0200 Subject: [PATCH 191/390] Code review --- .../columnsPreProcessing/gridColumnsPreProcessingApi.ts | 4 ++-- .../columnsPreProcessing/useGridColumnsPreProcessing.ts | 8 ++++---- .../grid/hooks/features/columns/useGridColumns.ts | 9 ++++++--- .../grid/hooks/features/selection/useGridSelection.ts | 4 ++-- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/core/columnsPreProcessing/gridColumnsPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/core/columnsPreProcessing/gridColumnsPreProcessingApi.ts index 5bb8503864340..89e93838de9f9 100644 --- a/packages/grid/_modules_/grid/hooks/core/columnsPreProcessing/gridColumnsPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/core/columnsPreProcessing/gridColumnsPreProcessingApi.ts @@ -9,7 +9,7 @@ export interface GridColumnsPreProcessingApi { * @param {GridColumnsPreProcessing | null } columnsPreProcessing Pre-processing to register. * @ignore - do not document */ - registerColumnPreProcessing: ( + UNSTABLE_registerColumnPreProcessing: ( processingName: string, columnsPreProcessing: GridColumnsPreProcessing | null, ) => void; @@ -19,5 +19,5 @@ export interface GridColumnsPreProcessingApi { * @returns {GridColumns} The pre-processed columns * @ignore - do not document */ - applyAllColumnPreProcessing: (columns: GridColumns) => GridColumns; + UNSTABLE_applyAllColumnPreProcessing: (columns: GridColumns) => GridColumns; } diff --git a/packages/grid/_modules_/grid/hooks/core/columnsPreProcessing/useGridColumnsPreProcessing.ts b/packages/grid/_modules_/grid/hooks/core/columnsPreProcessing/useGridColumnsPreProcessing.ts index 620b7712f6956..604d8be78c67d 100644 --- a/packages/grid/_modules_/grid/hooks/core/columnsPreProcessing/useGridColumnsPreProcessing.ts +++ b/packages/grid/_modules_/grid/hooks/core/columnsPreProcessing/useGridColumnsPreProcessing.ts @@ -11,7 +11,7 @@ export const useGridColumnsPreProcessing = (apiRef: GridApiRef) => { const columnsPreProcessingRef = React.useRef(new Map()); const registerColumnPreProcessing = React.useCallback< - GridColumnsPreProcessingApi['registerColumnPreProcessing'] + GridColumnsPreProcessingApi['UNSTABLE_registerColumnPreProcessing'] >( (processingName, columnsPreProcessing) => { const columnPreProcessingBefore = columnsPreProcessingRef.current.get(processingName) ?? null; @@ -25,7 +25,7 @@ export const useGridColumnsPreProcessing = (apiRef: GridApiRef) => { ); const applyAllColumnPreProcessing = React.useCallback< - GridColumnsPreProcessingApi['applyAllColumnPreProcessing'] + GridColumnsPreProcessingApi['UNSTABLE_applyAllColumnPreProcessing'] >((columns) => { let preProcessedColumns = columns; @@ -39,8 +39,8 @@ export const useGridColumnsPreProcessing = (apiRef: GridApiRef) => { }, []); const columnsPreProcessingApi: GridColumnsPreProcessingApi = { - registerColumnPreProcessing, - applyAllColumnPreProcessing, + UNSTABLE_registerColumnPreProcessing: registerColumnPreProcessing, + UNSTABLE_applyAllColumnPreProcessing: applyAllColumnPreProcessing, }; useGridApiMethod(apiRef, columnsPreProcessingApi, 'GridColumnsPreProcessing'); diff --git a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts index d5f5b70f34166..e3842d994837e 100644 --- a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts +++ b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts @@ -132,7 +132,8 @@ export function useGridColumns( useGridStateInit(apiRef, (state) => { const hydratedColumns = hydrateColumnsType(props.columns, props.columnTypes); - const preProcessedColumns = apiRef.current.applyAllColumnPreProcessing(hydratedColumns); + const preProcessedColumns = + apiRef.current.UNSTABLE_applyAllColumnPreProcessing(hydratedColumns); const columns = upsertColumnsState(preProcessedColumns); let newColumns: GridColumns = columns.all.map((field) => columns.lookup[field]); newColumns = hydrateColumnsWidth(newColumns, 0); @@ -332,7 +333,8 @@ export function useGridColumns( const hydratedColumns = hydrateColumnsType(props.columns, props.columnTypes); - const preProcessedColumns = apiRef.current.applyAllColumnPreProcessing(hydratedColumns); + const preProcessedColumns = + apiRef.current.UNSTABLE_applyAllColumnPreProcessing(hydratedColumns); const columnState = upsertColumnsState(preProcessedColumns); setColumnsState(columnState); }, [logger, apiRef, setColumnsState, props.columns, props.columnTypes]); @@ -354,7 +356,8 @@ export function useGridColumns( logger.info(`Columns pre-processing have changed, regenerating the columns`); const hydratedColumns = hydrateColumnsType(props.columns, props.columnTypes); - const preProcessedColumns = apiRef.current.applyAllColumnPreProcessing(hydratedColumns); + const preProcessedColumns = + apiRef.current.UNSTABLE_applyAllColumnPreProcessing(hydratedColumns); const columnState = upsertColumnsState(preProcessedColumns); setColumnsState(columnState); }, [apiRef, logger, setColumnsState, props.columns, props.columnTypes]); diff --git a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts index b408e1d494009..86101b2fad129 100644 --- a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts +++ b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts @@ -356,7 +356,7 @@ export const useGridSelection = ( const updateColumnsPreProcessing = React.useCallback(() => { if (!props.checkboxSelection) { - apiRef.current.registerColumnPreProcessing('selection', null); + apiRef.current.UNSTABLE_registerColumnPreProcessing('selection', null); } else { const addCheckboxColumn: GridColumnsPreProcessing = (columns) => { const groupingColumn: GridColDef = { @@ -369,7 +369,7 @@ export const useGridSelection = ( return [groupingColumn, ...columns]; }; - apiRef.current.registerColumnPreProcessing('selection', addCheckboxColumn); + apiRef.current.UNSTABLE_registerColumnPreProcessing('selection', addCheckboxColumn); } }, [apiRef, props.checkboxSelection, classes]); From 53d6d8c7edff3c5deb7201ed4b33a6185941dbff Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 13 Oct 2021 10:56:22 +0200 Subject: [PATCH 192/390] Merge --- .../_modules_/grid/hooks/features/treeData/useGridTreeData.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 9e294712d2b0c..cbcd6f30ed0a9 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -26,7 +26,7 @@ export const useGridTreeData = ( ) => { const updateColumnsPreProcessing = React.useCallback(() => { if (!props.treeData) { - apiRef.current.registerColumnPreProcessing('treeData', null); + apiRef.current.UNSTABLE_registerColumnPreProcessing('treeData', null); } else { const addGroupingColumn: GridColumnsPreProcessing = (columns) => { const index = columns[0].type === 'checkboxSelection' ? 1 : 0; @@ -39,7 +39,7 @@ export const useGridTreeData = ( return [...columns.slice(0, index), groupingColumn, ...columns.slice(index)]; }; - apiRef.current.registerColumnPreProcessing('treeData', addGroupingColumn); + apiRef.current.UNSTABLE_registerColumnPreProcessing('treeData', addGroupingColumn); } }, [apiRef, props.treeData, props.groupingColDef]); From 4190d61a6b7ca6bd6d3855a4ebcd17014d21b2b4 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 13 Oct 2021 11:04:43 +0200 Subject: [PATCH 193/390] Clean --- packages/grid/_modules_/grid/models/gridRows.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index 8364ba7077f26..d081bff0f7ede 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -16,7 +16,6 @@ export interface GridRowConfigTreeNode { id: GridRowId; children?: GridRowId[]; parent: GridRowId | null; - descendantsCount?: number; expanded?: boolean; depth: number; From 11a712808d7c9fe97afaa011352cb2bb55785ae0 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 13 Oct 2021 11:12:40 +0200 Subject: [PATCH 194/390] Work --- packages/grid/_modules_/grid/components/GridViewport.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/grid/_modules_/grid/components/GridViewport.tsx b/packages/grid/_modules_/grid/components/GridViewport.tsx index 528b4a5b9a741..f72c49c9e2830 100644 --- a/packages/grid/_modules_/grid/components/GridViewport.tsx +++ b/packages/grid/_modules_/grid/components/GridViewport.tsx @@ -44,6 +44,7 @@ export const GridViewport: ViewportType = React.forwardRef( apiRef, gridSortedVisibleTopLevelRowEntriesSelector, ); + const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector); const editRowsState = useGridSelector(apiRef, gridEditRowsStateSelector); @@ -75,6 +76,7 @@ export const GridViewport: ViewportType = React.forwardRef( const startIndex = getVisibleRowIndex( visibleSortedTopLevelRows[renderState.renderContext.firstRowIdx!].id, ); + const isLastTopLevelRowVisible = renderState.renderContext.lastRowIdx! > visibleSortedTopLevelRows.length; const endIndex = isLastTopLevelRowVisible From 6b24db8356c72883b7f0820132ab3ea4b51b92b0 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 13 Oct 2021 11:14:37 +0200 Subject: [PATCH 195/390] Regen exports --- scripts/exportsSnapshot.json | 88 ++++++++++-------------------------- 1 file changed, 25 insertions(+), 63 deletions(-) diff --git a/scripts/exportsSnapshot.json b/scripts/exportsSnapshot.json index e59713ca81a6e..b31dadcd51201 100644 --- a/scripts/exportsSnapshot.json +++ b/scripts/exportsSnapshot.json @@ -14,7 +14,6 @@ { "name": "GridFilterMenuItem", "kind": "Namespace" }, { "name": "GridMenu", "kind": "Namespace" }, { "name": "GridRow", "kind": "Namespace" }, - { "name": "GridRowCells", "kind": "Namespace" }, { "name": "GridStickyContainer", "kind": "Namespace" }, { "name": "HideGridColMenuItem", "kind": "Namespace" }, { "name": "SortGridMenuItems", "kind": "Namespace" }, @@ -25,6 +24,7 @@ { "name": "GridLinkOperator", "kind": "Enumeration" }, { "name": "GridPreferencePanelsValue", "kind": "Enumeration" }, { "name": "GridRowModes", "kind": "Enumeration" }, + { "name": "GridSignature", "kind": "Enumeration" }, { "name": "AutoSizerProps", "kind": "Interface" }, { "name": "AutoSizerSize", "kind": "Interface" }, { "name": "CursorCoordinates", "kind": "Interface" }, @@ -65,6 +65,7 @@ { "name": "GridCsvExportApi", "kind": "Interface" }, { "name": "GridDensityApi", "kind": "Interface" }, { "name": "GridDensityOption", "kind": "Interface" }, + { "name": "GridDensityState", "kind": "Interface" }, { "name": "GridEditCellProps", "kind": "Interface" }, { "name": "GridEditCellPropsParams", "kind": "Interface" }, { "name": "GridEditCellValueParams", "kind": "Interface" }, @@ -79,6 +80,7 @@ { "name": "GridFilterItemProps", "kind": "Interface" }, { "name": "GridFilterModel", "kind": "Interface" }, { "name": "GridFilterOperator", "kind": "Interface" }, + { "name": "GridFilterState", "kind": "Interface" }, { "name": "GridFocusApi", "kind": "Interface" }, { "name": "GridFocusState", "kind": "Interface" }, { "name": "GridIconSlotsComponent", "kind": "Interface" }, @@ -88,6 +90,7 @@ { "name": "GridMenuProps", "kind": "Interface" }, { "name": "GridPageApi", "kind": "Interface" }, { "name": "GridPageSizeApi", "kind": "Interface" }, + { "name": "GridPaginationState", "kind": "Interface" }, { "name": "GridPanelClasses", "kind": "Interface" }, { "name": "GridPanelProps", "kind": "Interface" }, { "name": "GridParamsApi", "kind": "Interface" }, @@ -105,7 +108,6 @@ { "name": "GridRowProps", "kind": "Interface" }, { "name": "GridRowScrollEndParams", "kind": "Interface" }, { "name": "GridRowSelectionCheckboxParams", "kind": "Interface" }, - { "name": "GridRowsInternalCache", "kind": "Interface" }, { "name": "GridRowsState", "kind": "Interface" }, { "name": "GridScrollApi", "kind": "Interface" }, { "name": "GridScrollBarState", "kind": "Interface" }, @@ -131,7 +133,6 @@ { "name": "GridVirtualizationApi", "kind": "Interface" }, { "name": "GridWindowProps", "kind": "Interface" }, { "name": "Logger", "kind": "Interface" }, - { "name": "VisibleGridRowsState", "kind": "Interface" }, { "name": "DataGridProps", "kind": "Type alias" }, { "name": "FilterColumnLookup", "kind": "Type alias" }, { "name": "GridActionsCellItemProps", "kind": "Type alias" }, @@ -190,29 +191,14 @@ { "name": "DEFAULT_GRID_COL_TYPE_KEY", "kind": "Variable" }, { "name": "DataGrid", "kind": "Variable" }, { "name": "GRID_ACTIONS_COL_DEF", "kind": "Variable" }, - { "name": "GRID_BOOLEAN_COLUMN_TYPE", "kind": "Variable" }, - { "name": "GRID_CELL_CSS_CLASS", "kind": "Variable" }, - { "name": "GRID_CELL_CSS_CLASS_SUFFIX", "kind": "Variable" }, - { "name": "GRID_COLUMN_HEADER_CSS_CLASS", "kind": "Variable" }, - { "name": "GRID_COLUMN_HEADER_CSS_CLASS_SUFFIX", "kind": "Variable" }, - { "name": "GRID_COLUMN_HEADER_DRAGGING_CSS_CLASS", "kind": "Variable" }, - { "name": "GRID_COLUMN_HEADER_DROP_ZONE_CSS_CLASS", "kind": "Variable" }, - { "name": "GRID_COLUMN_HEADER_SEPARATOR_RESIZABLE_CSS_CLASS", "kind": "Variable" }, - { "name": "GRID_COLUMN_HEADER_TITLE_CSS_CLASS", "kind": "Variable" }, - { "name": "GRID_CSS_CLASS_PREFIX", "kind": "Variable" }, - { "name": "GRID_DATETIME_COLUMN_TYPE", "kind": "Variable" }, + { "name": "GRID_BOOLEAN_COL_DEF", "kind": "Variable" }, + { "name": "GRID_CHECKBOX_SELECTION_COL_DEF", "kind": "Variable" }, { "name": "GRID_DATETIME_COL_DEF", "kind": "Variable" }, - { "name": "GRID_DATE_COLUMN_TYPE", "kind": "Variable" }, { "name": "GRID_DATE_COL_DEF", "kind": "Variable" }, { "name": "GRID_DEFAULT_LOCALE_TEXT", "kind": "Variable" }, { "name": "GRID_EXPERIMENTAL_ENABLED", "kind": "Variable" }, - { "name": "GRID_NUMBER_COLUMN_TYPE", "kind": "Variable" }, { "name": "GRID_NUMERIC_COL_DEF", "kind": "Variable" }, - { "name": "GRID_ROOT_CSS_CLASS_SUFFIX", "kind": "Variable" }, - { "name": "GRID_ROW_CSS_CLASS", "kind": "Variable" }, - { "name": "GRID_ROW_CSS_CLASS_SUFFIX", "kind": "Variable" }, { "name": "GRID_SINGLE_SELECT_COL_DEF", "kind": "Variable" }, - { "name": "GRID_STRING_COLUMN_TYPE", "kind": "Variable" }, { "name": "GRID_STRING_COL_DEF", "kind": "Variable" }, { "name": "GridActionsCell", "kind": "Variable" }, { "name": "GridActionsCellItem", "kind": "Variable" }, @@ -291,41 +277,44 @@ { "name": "filterableGridColumnsIdsSelector", "kind": "Variable" }, { "name": "filterableGridColumnsSelector", "kind": "Variable" }, { "name": "frFR", "kind": "Variable" }, - { "name": "gridCheckboxSelectionColDef", "kind": "Variable" }, { "name": "gridClasses", "kind": "Variable" }, { "name": "gridColumnReorderDragColSelector", "kind": "Variable" }, { "name": "gridColumnsMetaSelector", "kind": "Variable" }, { "name": "gridColumnsTotalWidthSelector", "kind": "Variable" }, + { "name": "gridDensityHeaderHeightSelector", "kind": "Variable" }, + { "name": "gridDensityRowHeightSelector", "kind": "Variable" }, + { "name": "gridDensityValueSelector", "kind": "Variable" }, + { "name": "gridFilterModelSelector", "kind": "Variable" }, { "name": "gridFocusCellSelector", "kind": "Variable" }, { "name": "gridFocusColumnHeaderSelector", "kind": "Variable" }, + { "name": "gridPageSelector", "kind": "Variable" }, + { "name": "gridPageSizeSelector", "kind": "Variable" }, { "name": "gridPaginatedVisibleSortedGridRowIdsSelector", "kind": "Variable" }, { "name": "gridPanelClasses", "kind": "Variable" }, { "name": "gridResizingColumnFieldSelector", "kind": "Variable" }, { "name": "gridRowCountSelector", "kind": "Variable" }, { "name": "gridRowsLookupSelector", "kind": "Variable" }, + { "name": "gridScrollSelector", "kind": "Variable" }, { "name": "gridSortColumnLookupSelector", "kind": "Variable" }, { "name": "gridSortModelSelector", "kind": "Variable" }, { "name": "gridTabIndexCellSelector", "kind": "Variable" }, { "name": "gridTabIndexColumnHeaderSelector", "kind": "Variable" }, + { "name": "gridVisibleRowsLookupSelector", "kind": "Variable" }, { "name": "itIT", "kind": "Variable" }, { "name": "jaJP", "kind": "Variable" }, { "name": "koKR", "kind": "Variable" }, { "name": "nlNL", "kind": "Variable" }, { "name": "plPL", "kind": "Variable" }, - { "name": "plPLGrid", "kind": "Variable" }, { "name": "ptBR", "kind": "Variable" }, { "name": "ruRU", "kind": "Variable" }, - { "name": "ruRUGrid", "kind": "Variable" }, { "name": "selectedGridRowsCountSelector", "kind": "Variable" }, { "name": "selectedGridRowsSelector", "kind": "Variable" }, { "name": "selectedIdsLookupSelector", "kind": "Variable" }, { "name": "skSK", "kind": "Variable" }, - { "name": "skSKGrid", "kind": "Variable" }, { "name": "sortedGridRowIdsSelector", "kind": "Variable" }, { "name": "sortedGridRowsSelector", "kind": "Variable" }, { "name": "trTR", "kind": "Variable" }, { "name": "ukUA", "kind": "Variable" }, - { "name": "ukUAGrid", "kind": "Variable" }, { "name": "unorderedGridRowIdsSelector", "kind": "Variable" }, { "name": "unorderedGridRowModelsSelector", "kind": "Variable" }, { "name": "viVN", "kind": "Variable" }, @@ -336,7 +325,6 @@ { "name": "visibleSortedGridRowsAsArraySelector", "kind": "Variable" }, { "name": "visibleSortedGridRowsSelector", "kind": "Variable" }, { "name": "zhCN", "kind": "Variable" }, - { "name": "zhCNGrid", "kind": "Variable" }, { "name": "GridBody", "kind": "Function" }, { "name": "GridColumnHeaderItem", "kind": "Function" }, { "name": "GridColumnHeaderMenu", "kind": "Function" }, @@ -358,30 +346,21 @@ { "name": "GridPanelHeader", "kind": "Function" }, { "name": "GridPanelWrapper", "kind": "Function" }, { "name": "GridRow", "kind": "Function" }, - { "name": "GridRowCells", "kind": "Function" }, { "name": "GridStickyContainer", "kind": "Function" }, { "name": "GridToolbar", "kind": "Function" }, { "name": "GridToolbarColumnsButton", "kind": "Function" }, { "name": "GridToolbarDensitySelector", "kind": "Function" }, { "name": "allGridColumnsFieldsSelector", "kind": "Function" }, { "name": "checkGridRowIdIsValid", "kind": "Function" }, - { "name": "convertGridRowsPropToState", "kind": "Function" }, { "name": "getDataGridUtilityClass", "kind": "Function" }, + { "name": "getDefaultGridFilterModel", "kind": "Function" }, + { "name": "getGridBooleanOperators", "kind": "Function" }, { "name": "getGridColDef", "kind": "Function" }, { "name": "getGridDateOperators", "kind": "Function" }, { "name": "getGridDefaultColumnTypes", "kind": "Function" }, { "name": "getGridNumericColumnOperators", "kind": "Function" }, { "name": "getGridSingleSelectOperators", "kind": "Function" }, { "name": "getGridStringOperators", "kind": "Function" }, - { "name": "getInitialGridColumnReorderState", "kind": "Function" }, - { "name": "getInitialGridColumnResizeState", "kind": "Function" }, - { "name": "getInitialGridColumnsState", "kind": "Function" }, - { "name": "getInitialGridFilterState", "kind": "Function" }, - { "name": "getInitialGridRenderingState", "kind": "Function" }, - { "name": "getInitialGridRowState", "kind": "Function" }, - { "name": "getInitialGridSortingState", "kind": "Function" }, - { "name": "getInitialGridState", "kind": "Function" }, - { "name": "getInitialVisibleGridRowsState", "kind": "Function" }, { "name": "gridColumnLookupSelector", "kind": "Function" }, { "name": "gridColumnMenuSelector", "kind": "Function" }, { "name": "gridColumnReorderSelector", "kind": "Function" }, @@ -389,11 +368,13 @@ { "name": "gridColumnsSelector", "kind": "Function" }, { "name": "gridDateFormatter", "kind": "Function" }, { "name": "gridDateTimeFormatter", "kind": "Function" }, + { "name": "gridDensitySelector", "kind": "Function" }, { "name": "gridEditRowsStateSelector", "kind": "Function" }, - { "name": "gridFilterModelSelector", "kind": "Function" }, + { "name": "gridFilterStateSelector", "kind": "Function" }, { "name": "gridFocusStateSelector", "kind": "Function" }, { "name": "gridPaginationSelector", "kind": "Function" }, { "name": "gridPreferencePanelStateSelector", "kind": "Function" }, + { "name": "gridRenderingSelector", "kind": "Function" }, { "name": "gridRowsStateSelector", "kind": "Function" }, { "name": "gridScrollbarStateSelector", "kind": "Function" }, { "name": "gridSelectionStateSelector", "kind": "Function" }, @@ -402,38 +383,19 @@ { "name": "renderActionsCell", "kind": "Function" }, { "name": "renderEditInputCell", "kind": "Function" }, { "name": "renderEditSingleSelectCell", "kind": "Function" }, - { "name": "useApi", "kind": "Function" }, { "name": "useDataGridComponent", "kind": "Function" }, { "name": "useGridApi", "kind": "Function" }, + { "name": "useGridApiContext", "kind": "Function" }, + { "name": "useGridApiEventHandler", "kind": "Function" }, { "name": "useGridApiMethod", "kind": "Function" }, + { "name": "useGridApiOptionHandler", "kind": "Function" }, { "name": "useGridApiRef", "kind": "Function" }, - { "name": "useGridColumnMenu", "kind": "Function" }, - { "name": "useGridColumnReorder", "kind": "Function" }, - { "name": "useGridColumnResize", "kind": "Function" }, - { "name": "useGridColumns", "kind": "Function" }, - { "name": "useGridContainerProps", "kind": "Function" }, - { "name": "useGridControlState", "kind": "Function" }, - { "name": "useGridEditRows", "kind": "Function" }, - { "name": "useGridFilter", "kind": "Function" }, - { "name": "useGridFocus", "kind": "Function" }, - { "name": "useGridKeyboard", "kind": "Function" }, - { "name": "useGridKeyboardNavigation", "kind": "Function" }, { "name": "useGridLogger", "kind": "Function" }, - { "name": "useGridLoggerFactory", "kind": "Function" }, - { "name": "useGridPage", "kind": "Function" }, - { "name": "useGridPageSize", "kind": "Function" }, - { "name": "useGridParamsApi", "kind": "Function" }, - { "name": "useGridPreferencesPanel", "kind": "Function" }, + { "name": "useGridNativeEventListener", "kind": "Function" }, + { "name": "useGridProcessedProps", "kind": "Function" }, { "name": "useGridRootProps", "kind": "Function" }, - { "name": "useGridRows", "kind": "Function" }, - { "name": "useGridScroll", "kind": "Function" }, { "name": "useGridScrollFn", "kind": "Function" }, - { "name": "useGridSelection", "kind": "Function" }, { "name": "useGridSelector", "kind": "Function" }, { "name": "useGridSlotComponentProps", "kind": "Function" }, - { "name": "useGridSorting", "kind": "Function" }, - { "name": "useGridState", "kind": "Function" }, - { "name": "useGridVirtualization", "kind": "Function" }, - { "name": "useNativeEventListener", "kind": "Function" }, - { "name": "visibleGridRowsStateSelector", "kind": "Function" } + { "name": "useGridState", "kind": "Function" } ] From 79949948365fad56af6939711f398869c0d42508 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 13 Oct 2021 12:39:01 +0200 Subject: [PATCH 196/390] Prettier --- .../grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts | 2 +- .../grid/_modules_/grid/hooks/features/rows/gridRowsState.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts index 3e3bf73770339..1165f9979edc9 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts @@ -20,4 +20,4 @@ export const gridRowsLookupSelector = createSelector( export const gridRowTreeSelector = createSelector(gridRowsStateSelector, (rows) => rows.tree); -export const gridRowIdsSelector = createSelector(gridRowsStateSelector, rows => rows.rowIds) +export const gridRowIdsSelector = createSelector(gridRowsStateSelector, (rows) => rows.rowIds); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts index 37d461266283f..0ad455bfd4fc5 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts @@ -1,4 +1,4 @@ -import {GridRowConfigTree, GridRowId, GridRowsLookup} from '../../../models/gridRows'; +import { GridRowConfigTree, GridRowId, GridRowsLookup } from '../../../models/gridRows'; export interface GridRowsState { idRowsLookup: GridRowsLookup; From 9763402478794c77a899bd250f8751c2a69ea8ca Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 13 Oct 2021 13:05:03 +0200 Subject: [PATCH 197/390] Work --- .../grid/components/GridViewport.tsx | 21 +++++-------------- .../hooks/features/sorting/useGridSorting.ts | 8 +++++-- .../src/stories/grid-state.stories.tsx | 6 +----- 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/packages/grid/_modules_/grid/components/GridViewport.tsx b/packages/grid/_modules_/grid/components/GridViewport.tsx index f72c49c9e2830..2b89d5c95678a 100644 --- a/packages/grid/_modules_/grid/components/GridViewport.tsx +++ b/packages/grid/_modules_/grid/components/GridViewport.tsx @@ -3,7 +3,7 @@ import { visibleGridColumnsSelector } from '../hooks/features/columns/gridColumn import { useGridSelector } from '../hooks/utils/useGridSelector'; import { gridDensityRowHeightSelector } from '../hooks/features/density/densitySelector'; import { - gridSortedVisibleRowEntriesSelector, + // gridSortedVisibleRowEntriesSelector, gridSortedVisibleTopLevelRowEntriesSelector, } from '../hooks/features/filter/gridFilterSelector'; import { @@ -23,7 +23,6 @@ import { gridScrollBarSizeSelector, } from '../hooks/features/container/gridContainerSizesSelector'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; -import { GridRowId } from '../models'; type ViewportType = React.ForwardRefExoticComponent>; @@ -39,7 +38,7 @@ export const GridViewport: ViewportType = React.forwardRef( const cellFocus = useGridSelector(apiRef, gridFocusCellSelector); const cellTabIndex = useGridSelector(apiRef, gridTabIndexCellSelector); const selection = useGridSelector(apiRef, gridSelectionStateSelector); - const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowEntriesSelector); + // const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowEntriesSelector); const visibleSortedTopLevelRows = useGridSelector( apiRef, gridSortedVisibleTopLevelRowEntriesSelector, @@ -70,21 +69,11 @@ export const GridViewport: ViewportType = React.forwardRef( return null; } - const getVisibleRowIndex = (id: GridRowId) => - visibleSortedRows.findIndex((row) => row.id === id); - - const startIndex = getVisibleRowIndex( - visibleSortedTopLevelRows[renderState.renderContext.firstRowIdx!].id, + const renderedRows = visibleSortedTopLevelRows.slice( + renderState.renderContext.firstRowIdx!, + renderState.renderContext.lastRowIdx! + 1, ); - const isLastTopLevelRowVisible = - renderState.renderContext.lastRowIdx! > visibleSortedTopLevelRows.length; - const endIndex = isLastTopLevelRowVisible - ? visibleSortedRows.length - 1 - : getVisibleRowIndex(visibleSortedTopLevelRows[renderState.renderContext.lastRowIdx!].id); - - const renderedRows = visibleSortedRows.slice(startIndex, endIndex); - const renderedColumns = visibleColumns.slice( renderState.renderContext.firstColIdx!, renderState.renderContext.lastColIdx! + 1, diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 7dd671339e40d..5047d302dbf77 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -203,9 +203,13 @@ export const useGridSorting = ( // Apply the sorting to each list of children const sortedGroupedByParentRows = new Map(); groupedByParentRows.forEach((rowList, parent) => { + if (rowList.length === 0) { + return sortedGroupedByParentRows.set(parent, []); + } + const depth = rowList[0].depth; if ((depth > 0 && props.disableChildrenSorting) || comparatorList.length === 0) { - sortedGroupedByParentRows.set( + return sortedGroupedByParentRows.set( parent, rowList.map((row) => row.id), ); @@ -221,7 +225,7 @@ export const useGridSorting = ( .sort((a, b) => aggregatedComparator(a.params, b.params)) .map((row) => row.value.id); - sortedGroupedByParentRows.set(parent, sortedRowList); + return sortedGroupedByParentRows.set(parent, sortedRowList); }); // Flatten the sorted lists to have children just after their parent diff --git a/packages/storybook/src/stories/grid-state.stories.tsx b/packages/storybook/src/stories/grid-state.stories.tsx index fa646a1c36fb3..335320dfd5ff2 100644 --- a/packages/storybook/src/stories/grid-state.stories.tsx +++ b/packages/storybook/src/stories/grid-state.stories.tsx @@ -111,11 +111,7 @@ export function InitialState() { const [gridState, setGridState] = React.useState>({ sorting: { sortModel: [{ field: 'brand', sort: 'desc' }], - sortedRows: [ - { id: 2, children: [] }, - { id: 0, children: [] }, - { id: 1, children: [] }, - ], + sortedRows: [2, 0, 1], }, }); From 1bbdb964a3999aaaef4e6a66253964fb58e2206a Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Wed, 13 Oct 2021 09:26:40 -0300 Subject: [PATCH 198/390] 'rowsScrollEnd' should not be published on the first render --- .../hooks/features/infiniteLoader/useGridInfiniteLoader.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts b/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts index 6d9cb4bcb3190..a0ba718360695 100644 --- a/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts +++ b/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts @@ -41,7 +41,8 @@ export const useGridInfiniteLoader = ( const handleRowsScrollEnd = React.useCallback( (scrollPosition: GridScrollParams) => { - if (!containerSizes) { + // Exiting if scrollPosition.top === 0 is because this callback is also called on the first render. + if (!containerSizes || scrollPosition.top === 0) { return; } From 8fe1e912e83170f0dd25fb3de8c0c5d84a1cdec0 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 13 Oct 2021 14:31:58 +0200 Subject: [PATCH 199/390] Work --- .../grid/hooks/features/filter/gridFilterState.ts | 15 --------------- .../grid/hooks/features/filter/useGridFilter.ts | 5 +++-- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts index cef15c1371152..9bdb7f24e3368 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts @@ -7,23 +7,8 @@ export const getDefaultGridFilterModel: () => GridFilterModel = () => ({ linkOperator: GridLinkOperator.And, }); -export const getEmptyVisibleRows = (): Omit => ({ - visibleRowsLookup: {}, - visibleRows: [], -}); - export interface GridFilterState { filterModel: GridFilterModel; - - /** - * Visibility status of each row after applying the filtering - * The expanded children rows are also set to `true` - */ visibleRowsLookup: Record; - - /** - * Rows visible after applying the filtering - * It also contains the expanded children rows - */ visibleRows: GridRowId[]; } diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index a48a46f835d6f..ce5b8ee6f8bad 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -13,7 +13,7 @@ import { filterableGridColumnsIdsSelector } from '../columns/gridColumnsSelector import { useGridState } from '../../utils/useGridState'; import { GridPreferencePanelsValue } from '../preferencesPanel/gridPreferencePanelsValue'; import { gridSortedRowIdsSelector } from '../sorting/gridSortingSelector'; -import { getDefaultGridFilterModel, getEmptyVisibleRows } from './gridFilterState'; +import { getDefaultGridFilterModel } from './gridFilterState'; import { GridFilterModel } from '../../../models/gridFilterModel'; import { gridVisibleRowsLookupSelector, @@ -64,7 +64,8 @@ export const useGridFilter = ( ...state, filter: { filterModel: props.filterModel ?? getDefaultGridFilterModel(), - ...getEmptyVisibleRows(), + visibleRowsLookup: {}, + visibleRows: [], }, }; }); From 92cf93a9811a7e8de83eee44b3f7927cc9f58ce1 Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Wed, 13 Oct 2021 15:12:30 -0300 Subject: [PATCH 200/390] fix print export --- .../components/GridVirtualizedContainer.tsx | 36 +++++++++++-------- .../grid/components/base/GridBody.tsx | 21 +++++++++++ .../features/export/useGridPrintExport.tsx | 15 ++++---- .../api/gridDisableVirtualizationApi.ts | 5 +++ 4 files changed, 57 insertions(+), 20 deletions(-) diff --git a/packages/grid/_modules_/grid/components/GridVirtualizedContainer.tsx b/packages/grid/_modules_/grid/components/GridVirtualizedContainer.tsx index ef77b0bd40e98..cf59a4c11a5cd 100644 --- a/packages/grid/_modules_/grid/components/GridVirtualizedContainer.tsx +++ b/packages/grid/_modules_/grid/components/GridVirtualizedContainer.tsx @@ -33,7 +33,7 @@ const useUtilityClasses = (ownerState: OwnerState) => { const slots = { root: ['virtualizedContainer'], renderingZone: ['renderingZone'], - content: ['virtualizedContainerContent'], + content: ['content'], }; return composeClasses(slots, getDataGridUtilityClass, classes); @@ -92,11 +92,12 @@ export interface RenderContext { interface GridVirtualizedContainerProps extends React.HTMLAttributes { selectionLookup: Record; + disableVirtualization?: boolean; } const GridVirtualizedContainer = React.forwardRef( function GridVirtualizedContainer(props, ref) { - const { className, selectionLookup, ...other } = props; + const { className, selectionLookup, disableVirtualization, ...other } = props; const apiRef = useGridApiContext(); const rootProps = useGridRootProps(); const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); @@ -127,7 +128,7 @@ const GridVirtualizedContainer = React.forwardRef { - if (rootProps.disableVirtualization) { + if (disableVirtualization) { return { firstRowIndex: 0, lastRowIndex: rowsInCurrentPage.length, @@ -159,19 +160,23 @@ const GridVirtualizedContainer = React.forwardRef { - // Reset scroll - rootRef.current!.scrollLeft = 0; - rootRef.current!.scrollTop = 0; + if (disableVirtualization) { + renderingZoneRef.current!.style.transform = `translate3d(0px, 0px, 0px)`; + } else { + // TODO a scroll reset should not be necessary + rootRef.current!.scrollLeft = 0; + rootRef.current!.scrollTop = 0; + } setContainerWidth(rootRef.current!.clientWidth); - }, []); + }, [disableVirtualization]); React.useEffect(() => { if (containerWidth == null) { @@ -201,18 +206,21 @@ const GridVirtualizedContainer = React.forwardRef { + setIsVirtualizationDisabled(true); + }, []); + + const enableVirtualization = React.useCallback(() => { + setIsVirtualizationDisabled(false); + }, []); + + // The `useGridStateInit` hook can't be used here, because it only installs the + // method if it doesn't exist yet. Once installed, it's never updated again. + // This break the methods above, since their closure comes from the first time + // they were installed. Which means that calling `setIsVirtualizationDisabled` + // will trigger a re-render, but it won't update the state. That can be solved + // by migrating the virtualization status to the global state. + apiRef.current.UNSTABLE_disableVirtualization = disableVirtualization; + apiRef.current.UNSTABLE_enableVirtualization = enableVirtualization; const columnsHeaderRef = React.useRef(null); const columnsContainerRef = React.useRef(null); @@ -85,6 +105,7 @@ function GridBody(props: GridBodyProps) { ref={windowRef} style={style} selectionLookup={selectionLookup} // TODO pass it directly to the row via componentsProps + disableVirtualization={isVirtualizationDisabled} /> ); }} diff --git a/packages/grid/_modules_/grid/hooks/features/export/useGridPrintExport.tsx b/packages/grid/_modules_/grid/hooks/features/export/useGridPrintExport.tsx index d656d7ef630bb..9edb8f5fea486 100644 --- a/packages/grid/_modules_/grid/hooks/features/export/useGridPrintExport.tsx +++ b/packages/grid/_modules_/grid/hooks/features/export/useGridPrintExport.tsx @@ -118,16 +118,17 @@ export const useGridPrintExport = ( const gridRootElement = apiRef.current.rootElementRef!.current; const gridClone = gridRootElement!.cloneNode(true) as HTMLElement; - const gridCloneViewport: HTMLElement | null = gridClone.querySelector(gridClasses.viewport); + const gridCloneViewport: HTMLElement | null = gridClone.querySelector( + `.${gridClasses.virtualizedContainer}`, + ); // Expand the viewport window to prevent clipping - gridCloneViewport!.style.minWidth = '100%'; - gridCloneViewport!.style.maxWidth = '100%'; + gridCloneViewport!.style.height = 'auto'; let gridToolbarElementHeight = - gridRootElement!.querySelector(gridClasses.toolbarContainer)?.clientHeight || 0; + gridRootElement!.querySelector(`.${gridClasses.toolbarContainer}`)?.clientHeight || 0; let gridFooterElementHeight = - gridRootElement!.querySelector(gridClasses.footerContainer)?.clientHeight || 0; + gridRootElement!.querySelector(`.${gridClasses.footerContainer}`)?.clientHeight || 0; if (normalizeOptions.hideToolbar) { gridClone.querySelector(gridClasses.toolbarContainer)?.remove(); @@ -135,7 +136,7 @@ export const useGridPrintExport = ( } if (normalizeOptions.hideFooter) { - gridClone.querySelector(gridClasses.footerContainer)?.remove(); + gridClone.querySelector(`.${gridClasses.footerContainer}`)?.remove(); gridFooterElementHeight = 0; } @@ -219,6 +220,8 @@ export const useGridPrintExport = ( ...previousGridState.current, })); + apiRef.current.UNSTABLE_enableVirtualization(); + // Revert columns to their original state if (previousHiddenColumns.current.length) { apiRef.current.updateColumns( diff --git a/packages/grid/_modules_/grid/models/api/gridDisableVirtualizationApi.ts b/packages/grid/_modules_/grid/models/api/gridDisableVirtualizationApi.ts index 29228a89ec4db..6884ad4caf89a 100644 --- a/packages/grid/_modules_/grid/models/api/gridDisableVirtualizationApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridDisableVirtualizationApi.ts @@ -7,4 +7,9 @@ export interface GridDisableVirtualizationApi { * @ignore - do not document. Remove before releasing v5 stable version. */ UNSTABLE_disableVirtualization: () => void; + /** + * Enables grid's virtualization. + * @ignore - do not document. Remove before releasing v5 stable version. + */ + UNSTABLE_enableVirtualization: () => void; } From dcfc6e428cd6baacf07587a3f1604e26e06ae2f1 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 14 Oct 2021 09:37:54 +0200 Subject: [PATCH 201/390] Code review --- packages/grid/_modules_/grid/models/gridRows.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index caf783b9b37aa..9e9b8b543e939 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -12,9 +12,9 @@ export interface GridRowModelUpdate extends GridRowData { _action?: GridUpdateAction; } -export interface GridRowConfigTreeNode { +export interface GridRowTreeNodeConfig { id: GridRowId; - children?: GridRowConfigTree; + children?: GridRowTreeConfig; descendantsCount?: number; expanded?: boolean; @@ -24,7 +24,7 @@ export interface GridRowConfigTreeNode { fillerNode?: boolean; } -export type GridRowConfigTree = Map; +export type GridRowTreeConfig = Map; export type GridRowsLookup = Record; From ba38f6b3a8d8c410229638f589770c45830ef14f Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 14 Oct 2021 09:47:38 +0200 Subject: [PATCH 202/390] Fix --- .../rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts index 1ef0117107ebe..362702a2dfb43 100644 --- a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts @@ -1,4 +1,4 @@ -import { GridRowConfigTree, GridRowId, GridRowsLookup } from '../../../models/gridRows'; +import { GridRowTreeConfig, GridRowId, GridRowsLookup } from '../../../models/gridRows'; export type RowGroupParams = { ids: GridRowId[]; @@ -6,7 +6,7 @@ export type RowGroupParams = { }; export interface GridRowGroupingResult { - tree: GridRowConfigTree; + tree: GridRowTreeConfig; paths: Record; idRowsLookup: GridRowsLookup; } From 79404eca7cfd43fb54e8430a27040a15093bb751 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 14 Oct 2021 10:31:38 +0200 Subject: [PATCH 203/390] Adapt for new virtualization --- docs/pages/playground.tsx | 4 +- .../components/GridVirtualizedContainer.tsx | 18 +++++--- .../keyboard/useGridKeyboardNavigation.ts | 20 ++++----- .../pagination/gridPaginationSelector.ts | 41 ++++++++++++++++--- 4 files changed, 58 insertions(+), 25 deletions(-) diff --git a/docs/pages/playground.tsx b/docs/pages/playground.tsx index 8edfbf8eb55d3..593e0fc471305 100644 --- a/docs/pages/playground.tsx +++ b/docs/pages/playground.tsx @@ -3,7 +3,7 @@ import { DataGridPro } from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; export default function BasicTreeData() { - const { data, loading } = useDemoTreeData({ rowLength: [2, 2], randomLength: false }); + const { data, loading } = useDemoTreeData({ rowLength: [20, 20], randomLength: false }); return (
@@ -13,8 +13,6 @@ export default function BasicTreeData() { disableSelectionOnClick defaultGroupingExpansionDepth={1} sortModel={[{ field: 'index', sort: 'desc' }]} - pagination - pageSize={1} {...data} />
diff --git a/packages/grid/_modules_/grid/components/GridVirtualizedContainer.tsx b/packages/grid/_modules_/grid/components/GridVirtualizedContainer.tsx index de5af62a3060d..39b150a33b1d1 100644 --- a/packages/grid/_modules_/grid/components/GridVirtualizedContainer.tsx +++ b/packages/grid/_modules_/grid/components/GridVirtualizedContainer.tsx @@ -19,7 +19,7 @@ import { gridSortedVisibleRowEntriesSelector } from '../hooks/features/filter/gr import { gridDensityRowHeightSelector } from '../hooks/features/density/densitySelector'; import { gridEditRowsStateSelector } from '../hooks/features/editRows/gridEditRowsSelector'; import { GridEvents } from '../constants/eventsConstants'; -import { gridPaginationSelector } from '../hooks/features/pagination/gridPaginationSelector'; +import { gridSortedVisiblePaginatedRowEntriesSelector } from '../hooks/features/pagination/gridPaginationSelector'; import { useGridApiEventHandler } from '../hooks/utils/useGridApiEventHandler'; import { getDataGridUtilityClass } from '../gridClasses'; import { GridComponentProps } from '../GridComponentProps'; @@ -103,12 +103,15 @@ const GridVirtualizedContainer = React.forwardRef(null); const rootRef = React.useRef(null); const handleRef = useForkRef(ref, rootRef); @@ -121,11 +124,16 @@ const GridVirtualizedContainer = React.forwardRef { if (rootProps.pagination && rootProps.paginationMode === 'client') { - const start = paginationState.pageSize * paginationState.page; - return visibleSortedRowEntries.slice(start, start + paginationState.pageSize); + return paginatedRowEntries; } + return visibleSortedRowEntries; - }, [paginationState, rootProps.pagination, rootProps.paginationMode, visibleSortedRowEntries]); + }, [ + rootProps.pagination, + rootProps.paginationMode, + visibleSortedRowEntries, + paginatedRowEntries, + ]); const computeRenderContext = React.useCallback(() => { if (disableVirtualization) { diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts index 5b4dde185f099..c7c82aab68330 100644 --- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts +++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts @@ -18,14 +18,14 @@ import { import { gridContainerSizesSelector } from '../container/gridContainerSizesSelector'; import { visibleGridColumnsLengthSelector } from '../columns/gridColumnsSelector'; import { useGridSelector } from '../../utils/useGridSelector'; -import { gridPaginationSelector } from '../pagination/gridPaginationSelector'; +import { + gridPaginationSelector, + gridSortedVisiblePaginatedRowEntriesSelector, +} from '../pagination/gridPaginationSelector'; import { useGridLogger } from '../../utils/useGridLogger'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { GridComponentProps } from '../../../GridComponentProps'; -import { - gridSortedVisibleRowEntriesSelector, - gridVisibleRowCountSelector, -} from '../filter/gridFilterSelector'; +import { gridSortedVisibleRowEntriesSelector } from '../filter/gridFilterSelector'; const getNextCellIndexes = (key: string, indexes: GridCellIndexCoordinates) => { if (!isArrowKeys(key)) { @@ -78,7 +78,7 @@ export const useGridKeyboardNavigation = ( ): void => { const logger = useGridLogger(apiRef, 'useGridKeyboardNavigation'); const paginationState = useGridSelector(apiRef, gridPaginationSelector); - const totalVisibleRowCount = useGridSelector(apiRef, gridVisibleRowCountSelector); + const paginatedRowEntries = useGridSelector(apiRef, gridSortedVisiblePaginatedRowEntriesSelector); const colCount = useGridSelector(apiRef, visibleGridColumnsLengthSelector); const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowEntriesSelector); @@ -101,11 +101,7 @@ export const useGridKeyboardNavigation = ( const key = mapKey(event); const isCtrlPressed = event.ctrlKey || event.metaKey || event.shiftKey; - let rowCount = totalVisibleRowCount; - - if (props.pagination && totalVisibleRowCount > paginationState.pageSize) { - rowCount = paginationState.pageSize * (paginationState.page + 1); - } + const rowCount = paginatedRowEntries.length; let nextCellIndexes: GridCellIndexCoordinates; if (isArrowKeys(key)) { @@ -164,7 +160,7 @@ export const useGridKeyboardNavigation = ( [ apiRef, visibleSortedRows, - totalVisibleRowCount, + paginatedRowEntries, props.pagination, paginationState.pageSize, paginationState.page, diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts index f369c866d8ac5..d00afda511b50 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts @@ -1,7 +1,11 @@ import { createSelector } from 'reselect'; import { GridState } from '../../../models/gridState'; -import { gridSortedVisibleRowEntriesSelector } from '../filter/gridFilterSelector'; +import { + gridSortedVisibleRowEntriesSelector, + gridSortedVisibleTopLevelRowEntriesSelector, +} from '../filter/gridFilterSelector'; import { GridPaginationState } from './gridPaginationState'; +import { gridRowTreeSelector } from '../rows/gridRowsSelector'; export const gridPaginationSelector = (state: GridState): GridPaginationState => state.pagination; @@ -17,11 +21,38 @@ export const gridPageSizeSelector = createSelector( export const gridSortedVisiblePaginatedRowEntriesSelector = createSelector( gridPaginationSelector, + gridRowTreeSelector, gridSortedVisibleRowEntriesSelector, - (pagination, visibleSortedRows) => { - const firstSelectedRowIndex = pagination.page * pagination.pageSize; - const lastSelectedRowIndex = firstSelectedRowIndex + pagination.pageSize; + gridSortedVisibleTopLevelRowEntriesSelector, + (pagination, rowTree, visibleSortedRowEntries, visibleSortedTopLevelRowEntries) => { + const topLevelRowStart = pagination.pageSize * pagination.page; + const topLevelRowsInCurrentPage = visibleSortedTopLevelRowEntries.slice( + topLevelRowStart, + topLevelRowStart + pagination.pageSize, + ); + + const rowStart = visibleSortedRowEntries.findIndex( + (row) => row.id === visibleSortedTopLevelRowEntries[topLevelRowStart].id, + ); + let rowEnd = rowStart + 1; + let topLevelRowCount = 1; + + while ( + rowEnd < visibleSortedRowEntries.length && + topLevelRowCount <= topLevelRowsInCurrentPage.length + ) { + rowEnd += 1; + + if (rowEnd < visibleSortedRowEntries.length) { + const row = visibleSortedRowEntries[rowEnd]; + const depth = rowTree[row.id].depth; + + if (depth === 0) { + topLevelRowCount += 1; + } + } + } - return visibleSortedRows.slice(firstSelectedRowIndex, lastSelectedRowIndex); + return visibleSortedRowEntries.slice(rowStart, rowEnd); }, ); From 5ac5f27508770358a8eca81e6d5e7bd339d9c54b Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 14 Oct 2021 10:52:46 +0200 Subject: [PATCH 204/390] Fix --- .../pagination/gridPaginationSelector.ts | 46 +++++++++++-------- .../hooks/features/sorting/useGridSorting.ts | 6 ++- .../src/tests/filtering.DataGridPro.test.tsx | 2 +- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts index d00afda511b50..22f33bc6edb0d 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts @@ -25,34 +25,42 @@ export const gridSortedVisiblePaginatedRowEntriesSelector = createSelector( gridSortedVisibleRowEntriesSelector, gridSortedVisibleTopLevelRowEntriesSelector, (pagination, rowTree, visibleSortedRowEntries, visibleSortedTopLevelRowEntries) => { - const topLevelRowStart = pagination.pageSize * pagination.page; - const topLevelRowsInCurrentPage = visibleSortedTopLevelRowEntries.slice( - topLevelRowStart, - topLevelRowStart + pagination.pageSize, - ); + const topLevelFirstRowIndex = pagination.pageSize * pagination.page; + const topLevelFirstRow = visibleSortedTopLevelRowEntries[topLevelFirstRowIndex]; + + if (!topLevelFirstRow) { + return []; + } - const rowStart = visibleSortedRowEntries.findIndex( - (row) => row.id === visibleSortedTopLevelRowEntries[topLevelRowStart].id, + const topLevelInCurrentPageCount = visibleSortedTopLevelRowEntries.slice( + topLevelFirstRowIndex, + topLevelFirstRowIndex + pagination.pageSize, + ).length; + + const firstRowIndex = visibleSortedRowEntries.findIndex( + (row) => row.id === topLevelFirstRow.id, ); - let rowEnd = rowStart + 1; - let topLevelRowCount = 1; + let lastRowIndex = firstRowIndex; + let topLevelRowAdded = 0; while ( - rowEnd < visibleSortedRowEntries.length && - topLevelRowCount <= topLevelRowsInCurrentPage.length + lastRowIndex < visibleSortedRowEntries.length && + topLevelRowAdded <= topLevelInCurrentPageCount ) { - rowEnd += 1; + const row = visibleSortedRowEntries[lastRowIndex]; + const depth = rowTree[row.id].depth; - if (rowEnd < visibleSortedRowEntries.length) { - const row = visibleSortedRowEntries[rowEnd]; - const depth = rowTree[row.id].depth; + if (topLevelRowAdded < topLevelInCurrentPageCount || depth > 0) { + lastRowIndex += 1; + } - if (depth === 0) { - topLevelRowCount += 1; - } + if (depth === 0) { + topLevelRowAdded += 1; } } - return visibleSortedRowEntries.slice(rowStart, rowEnd); + console.log(visibleSortedRowEntries.slice(firstRowIndex, lastRowIndex).map((el) => el.id)); + + return visibleSortedRowEntries.slice(firstRowIndex, lastRowIndex); }, ); diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 6ae50e3f0698d..811194074cd9b 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -27,7 +27,7 @@ import { gridSortedRowEntriesSelector, gridSortModelSelector, } from './gridSortingSelector'; -import { gridRowTreeSelector } from '../rows'; +import { gridRowIdsSelector, gridRowTreeSelector } from '../rows'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; @@ -181,13 +181,15 @@ export const useGridSorting = ( } const rowTree = gridRowTreeSelector(apiRef.current.state); + const rowIds = gridRowIdsSelector(apiRef.current.state); const sortModel = gridSortModelSelector(apiRef.current.state); const comparatorList = buildComparatorList(sortModel); const aggregatedComparator = comparatorListAggregate(comparatorList); // Group the rows by parent const groupedByParentRows = new Map([[null, []]]); - Object.values(rowTree).forEach((node) => { + rowIds.forEach((rowId) => { + const node = rowTree[rowId]; const isExpanded = node.parent == null || rowTree[node.parent].expanded; if (isExpanded) { diff --git a/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx index 9c553a6c614e9..3c1f96b0966fc 100644 --- a/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx @@ -288,7 +288,7 @@ describe(' - Filter', () => { expect(getColumnValues()).to.deep.equal(['Adidas']); expect(apiRef.current.getVisibleRowModels().size).to.equal(1); - expect(apiRef.current.getVisibleRowModels().get(1)!.node).to.deep.equal({ + expect(apiRef.current.getVisibleRowModels().get(1)).to.deep.equal({ id: 1, brand: 'Adidas', }); From 3f72ef731161d42583c34d5025c0d3513693eed8 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 14 Oct 2021 11:10:36 +0200 Subject: [PATCH 205/390] Fix --- docs/pages/playground.tsx | 45 ++++++++++++------- .../CustomGroupingColumnTreeData.tsx | 11 ++--- .../cell/GridTreeDataGroupingCell.tsx | 8 ++-- .../gridRowGroupsPreProcessingApi.ts | 3 +- .../useGridRowGroupsPreProcessing.ts | 3 +- .../pagination/gridPaginationSelector.ts | 2 - .../grid/hooks/features/rows/gridRowsUtils.ts | 6 ++- .../grid/hooks/features/rows/useGridRows.ts | 4 +- .../features/treeData/useGridTreeData.ts | 5 ++- .../grid/_modules_/grid/models/gridRows.ts | 1 + 10 files changed, 51 insertions(+), 37 deletions(-) diff --git a/docs/pages/playground.tsx b/docs/pages/playground.tsx index 593e0fc471305..4f264b802996b 100644 --- a/docs/pages/playground.tsx +++ b/docs/pages/playground.tsx @@ -1,20 +1,33 @@ import * as React from 'react'; -import { DataGridPro } from '@mui/x-data-grid-pro'; -import { useDemoTreeData } from '@mui/x-data-grid-generator'; +import { DataGridPro, GridColumns, GridRowsProp } from '@mui/x-data-grid-pro'; -export default function BasicTreeData() { - const { data, loading } = useDemoTreeData({ rowLength: [20, 20], randomLength: false }); +const rows: GridRowsProp = [ + { path: ['A'] }, + { path: ['A', 'A', 'A', 'A'] }, + { path: ['A', 'A', 'A', 'B'] }, + { path: ['B'] }, + { path: ['C', 'A', 'A'] }, +]; - return ( -
- -
- ); +const columns: GridColumns = [ + { field: 'path', renderCell: (params) => (params.value as string[]).join('-') }, +]; + +const getTreeDataPath = (row: any) => row.path; + +const getRowId = (row: any) => row.path.join('-'); + +export default function FillerTreeData() { + return ( +
+ +
+ ); } diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx index 1c685e73504a0..967f9a78ec57d 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx @@ -3,7 +3,6 @@ import { DataGridPro, DataGridProProps, GridRenderCellParams, - useGridRootProps, useGridSlotComponentProps, } from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; @@ -11,20 +10,16 @@ import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; const CustomGridTreeDataGroupingCell = (props: GridRenderCellParams) => { - const { id, row } = props; - + const { id } = props; const { apiRef } = useGridSlotComponentProps(); - const rootProps = useGridRootProps(); const node = apiRef.current.UNSTABLE_getRowNode(id); - const path = rootProps.getTreeDataPath!(row); - if (!node) { throw new Error(`MUI: No row with id #${id} found`); } return ( - +
{node.children?.length ? ( ) : ( diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index efc0db40db98a..21dd442400e87 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -21,7 +21,7 @@ const useStyles = makeStyles({ }); const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { - const { id, row } = props; + const { id } = props; const rootProps = useGridRootProps(); const apiRef = useGridApiContext(); @@ -32,14 +32,12 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { ? rootProps.components.TreeDataCollapseIcon : rootProps.components.TreeDataExpandIcon; - const path = rootProps.getTreeDataPath!(row); - if (!node) { throw new Error(`MUI: No row with id #${id} found`); } return ( - +
{!!node.children?.length && ( { )}
- {path[path.length - 1]} + {node.label}
); }; diff --git a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts index 93fd146c6bcf2..41f121fcde57b 100644 --- a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts @@ -1,12 +1,13 @@ import { GridRowTreeConfig, GridRowId, GridRowsLookup } from '../../../models/gridRows'; export type RowGroupParams = { - ids: GridRowId[]; + rowIds: GridRowId[]; idRowsLookup: GridRowsLookup; }; export interface GridRowGroupingResult { tree: GridRowTreeConfig; + rowIds: GridRowId[] idRowsLookup: GridRowsLookup; } diff --git a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts index 7372fcab89a38..41f54610335f6 100644 --- a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts +++ b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts @@ -9,8 +9,9 @@ import { GridEvents } from '../../../constants/eventsConstants'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; const getFlatRowTree: GridRowGroupingPreProcessing = (params) => ({ - tree: Object.fromEntries(params.ids.map((id) => [id.toString(), { id, depth: 0, parent: null }])), + tree: Object.fromEntries(params.rowIds.map((id) => [id.toString(), { id, depth: 0, parent: null }])), idRowsLookup: params.idRowsLookup, + rowIds: params.rowIds, }); export const useGridRowGroupsPreProcessing = (apiRef: GridApiRef) => { diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts index 22f33bc6edb0d..3df93fb0b231e 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts @@ -59,8 +59,6 @@ export const gridSortedVisiblePaginatedRowEntriesSelector = createSelector( } } - console.log(visibleSortedRowEntries.slice(firstRowIndex, lastRowIndex).map((el) => el.id)); - return visibleSortedRowEntries.slice(firstRowIndex, lastRowIndex); }, ); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts index 50a96dc3a4946..00890716a8765 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts @@ -15,11 +15,12 @@ interface InsertRowInTreeParams { id: GridRowId; defaultGroupingExpansionDepth: number; idRowsLookup: GridRowsLookup; + rowIds: GridRowId[]; nodeNameToIdTree: GridNodeNameToIdTree; } export const insertRowInTree = (params: InsertRowInTreeParams) => { - const { tree, path, id, defaultGroupingExpansionDepth, idRowsLookup, nodeNameToIdTree } = params; + const { tree, path, id, defaultGroupingExpansionDepth, idRowsLookup, rowIds, nodeNameToIdTree } = params; let nodeNameToIdSubTree = nodeNameToIdTree; let parentNode: GridRowTreeNodeConfig | null = null; @@ -51,17 +52,20 @@ export const insertRowInTree = (params: InsertRowInTreeParams) => { expanded, children: [], parent: parentNode?.id ?? null, + label: path[depth], depth, }; tree[nodeId] = node; idRowsLookup[nodeId] = {}; + rowIds.push(nodeId) } } else { tree[id] = { id, expanded: defaultGroupingExpansionDepth > depth, parent: parentNode?.id ?? null, + label: path[depth], depth, }; } diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 35309245f3009..c218b2737a55f 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -91,7 +91,7 @@ const getRowsStateFromCache = ( const groupingResponse = apiRef.current.UNSTABLE_groupRows({ idRowsLookup, - ids: rowIds, + rowIds, }); const dataTopLevelRowCount = Object.values(groupingResponse.tree).filter( @@ -101,7 +101,7 @@ const getRowsStateFromCache = ( const totalTopLevelRowCount = propRowCount > dataTopLevelRowCount ? propRowCount : dataTopLevelRowCount; - return { ...groupingResponse, rowIds, totalRowCount, totalTopLevelRowCount }; + return { ...groupingResponse, totalRowCount, totalTopLevelRowCount }; }; // The cache is always redefined synchronously in `useGridStateInit` so this object don't need to be regenerated across DataGrid instances. diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 39efcac7cf5fe..4cfa4c187cabb 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -53,7 +53,7 @@ export const useGridTreeData = ( throw new Error('MUI: No getTreeDataPath given.'); } - const rows = params.ids + const rows = params.rowIds .map((rowId) => ({ id: rowId, path: props.getTreeDataPath!(params.idRowsLookup[rowId]), @@ -62,6 +62,7 @@ export const useGridTreeData = ( const tree: GridRowTreeConfig = {}; const idRowsLookup: GridRowsLookup = { ...params.idRowsLookup }; + const rowIds = [...params.rowIds] const nodeNameToIdTree: GridNodeNameToIdTree = {}; rows.forEach((row) => { @@ -71,6 +72,7 @@ export const useGridTreeData = ( id: row.id, defaultGroupingExpansionDepth: props.defaultGroupingExpansionDepth, idRowsLookup, + rowIds, nodeNameToIdTree, }); }); @@ -78,6 +80,7 @@ export const useGridTreeData = ( return { tree, idRowsLookup, + rowIds, }; }; diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index 457269dbe31bc..e3ad99cadd35d 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -18,6 +18,7 @@ export interface GridRowTreeNodeConfig { parent: GridRowId | null; expanded?: boolean; depth: number; + label: string /** * If `true`, this node has been automatically added to fill a gap in the tree structure From 11c34f6c366e0312519d5b5b6c6ae52ef103b848 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 14 Oct 2021 11:16:09 +0200 Subject: [PATCH 206/390] Regen doc ts --- .../CustomGroupingColumnTreeData.js | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js index 00d6a11a610cb..af934904a7325 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js @@ -1,29 +1,21 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { - DataGridPro, - useGridRootProps, - useGridSlotComponentProps, -} from '@mui/x-data-grid-pro'; +import { DataGridPro, useGridSlotComponentProps } from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; const CustomGridTreeDataGroupingCell = (props) => { - const { id, row } = props; - + const { id } = props; const { apiRef } = useGridSlotComponentProps(); - const rootProps = useGridRootProps(); const node = apiRef.current.UNSTABLE_getRowNode(id); - const path = rootProps.getTreeDataPath(row); - if (!node) { throw new Error(`MUI: No row with id #${id} found`); } return ( - +
{node.children?.length ? ( ) : ( @@ -48,10 +40,6 @@ CustomGridTreeDataGroupingCell.propTypes = { * The grid row id. */ id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, - /** - * The row model of the row that the current cell belongs to. - */ - row: PropTypes.object.isRequired, }; const groupingColDef = { From 600f9641c96d17bc8b51fe5386dc1d369a864650 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 14 Oct 2021 11:21:10 +0200 Subject: [PATCH 207/390] Fix --- docs/pages/playground.tsx | 36 +++++++++---------- .../gridRowGroupsPreProcessingApi.ts | 2 +- .../useGridRowGroupsPreProcessing.ts | 4 ++- .../grid/hooks/features/rows/gridRowsUtils.ts | 5 +-- .../features/treeData/useGridTreeData.ts | 2 +- .../grid/_modules_/grid/models/gridRows.ts | 2 +- 6 files changed, 27 insertions(+), 24 deletions(-) diff --git a/docs/pages/playground.tsx b/docs/pages/playground.tsx index 4f264b802996b..b3f71d82debae 100644 --- a/docs/pages/playground.tsx +++ b/docs/pages/playground.tsx @@ -2,15 +2,15 @@ import * as React from 'react'; import { DataGridPro, GridColumns, GridRowsProp } from '@mui/x-data-grid-pro'; const rows: GridRowsProp = [ - { path: ['A'] }, - { path: ['A', 'A', 'A', 'A'] }, - { path: ['A', 'A', 'A', 'B'] }, - { path: ['B'] }, - { path: ['C', 'A', 'A'] }, + { path: ['A'] }, + { path: ['A', 'A', 'A', 'A'] }, + { path: ['A', 'A', 'A', 'B'] }, + { path: ['B'] }, + { path: ['C', 'A', 'A'] }, ]; const columns: GridColumns = [ - { field: 'path', renderCell: (params) => (params.value as string[]).join('-') }, + { field: 'path', renderCell: (params) => (params.value as string[]).join('-') }, ]; const getTreeDataPath = (row: any) => row.path; @@ -18,16 +18,16 @@ const getTreeDataPath = (row: any) => row.path; const getRowId = (row: any) => row.path.join('-'); export default function FillerTreeData() { - return ( -
- -
- ); + return ( +
+ +
+ ); } diff --git a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts index 41f121fcde57b..5a51715ca20d3 100644 --- a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts @@ -7,7 +7,7 @@ export type RowGroupParams = { export interface GridRowGroupingResult { tree: GridRowTreeConfig; - rowIds: GridRowId[] + rowIds: GridRowId[]; idRowsLookup: GridRowsLookup; } diff --git a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts index 41f54610335f6..3b617aeb400f8 100644 --- a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts +++ b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts @@ -9,7 +9,9 @@ import { GridEvents } from '../../../constants/eventsConstants'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; const getFlatRowTree: GridRowGroupingPreProcessing = (params) => ({ - tree: Object.fromEntries(params.rowIds.map((id) => [id.toString(), { id, depth: 0, parent: null }])), + tree: Object.fromEntries( + params.rowIds.map((id) => [id.toString(), { id, depth: 0, parent: null, label: '' }]), + ), idRowsLookup: params.idRowsLookup, rowIds: params.rowIds, }); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts index 00890716a8765..0e970c529bfb7 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts @@ -20,7 +20,8 @@ interface InsertRowInTreeParams { } export const insertRowInTree = (params: InsertRowInTreeParams) => { - const { tree, path, id, defaultGroupingExpansionDepth, idRowsLookup, rowIds, nodeNameToIdTree } = params; + const { tree, path, id, defaultGroupingExpansionDepth, idRowsLookup, rowIds, nodeNameToIdTree } = + params; let nodeNameToIdSubTree = nodeNameToIdTree; let parentNode: GridRowTreeNodeConfig | null = null; @@ -58,7 +59,7 @@ export const insertRowInTree = (params: InsertRowInTreeParams) => { tree[nodeId] = node; idRowsLookup[nodeId] = {}; - rowIds.push(nodeId) + rowIds.push(nodeId); } } else { tree[id] = { diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 4cfa4c187cabb..6a041947bae92 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -62,7 +62,7 @@ export const useGridTreeData = ( const tree: GridRowTreeConfig = {}; const idRowsLookup: GridRowsLookup = { ...params.idRowsLookup }; - const rowIds = [...params.rowIds] + const rowIds = [...params.rowIds]; const nodeNameToIdTree: GridNodeNameToIdTree = {}; rows.forEach((row) => { diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index e3ad99cadd35d..675f3b0c7976b 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -18,7 +18,7 @@ export interface GridRowTreeNodeConfig { parent: GridRowId | null; expanded?: boolean; depth: number; - label: string + label: string; /** * If `true`, this node has been automatically added to fill a gap in the tree structure From 8d3b49f957cf78bab941f800005f8004a2893943 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 14 Oct 2021 11:49:09 +0200 Subject: [PATCH 208/390] Fix --- .../grid/_modules_/grid/GridComponentProps.ts | 2 ++ .../keyboard/useGridKeyboardNavigation.ts | 19 +++++++---------- .../pagination/gridPaginationSelector.ts | 21 ++++++++++++++++--- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/packages/grid/_modules_/grid/GridComponentProps.ts b/packages/grid/_modules_/grid/GridComponentProps.ts index 26836ae94f5cc..639f900a51ca9 100644 --- a/packages/grid/_modules_/grid/GridComponentProps.ts +++ b/packages/grid/_modules_/grid/GridComponentProps.ts @@ -69,6 +69,7 @@ interface GridComponentOtherProps { columnTypes?: GridColumnTypesRecord; /** * Set the total number of rows, if it is different than the length of the value `rows` prop. + * If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows. */ rowCount?: number; /** @@ -405,6 +406,7 @@ interface GridComponentOtherProps { onPageChange?: (page: number, details: GridCallbackDetails) => void; /** * Set the number of rows in one page. + * If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows. * @default 100 */ pageSize?: number; diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts index c7c82aab68330..0c3b1630ae983 100644 --- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts +++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts @@ -18,10 +18,7 @@ import { import { gridContainerSizesSelector } from '../container/gridContainerSizesSelector'; import { visibleGridColumnsLengthSelector } from '../columns/gridColumnsSelector'; import { useGridSelector } from '../../utils/useGridSelector'; -import { - gridPaginationSelector, - gridSortedVisiblePaginatedRowEntriesSelector, -} from '../pagination/gridPaginationSelector'; +import { gridPaginationRowRangeSelector } from '../pagination/gridPaginationSelector'; import { useGridLogger } from '../../utils/useGridLogger'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { GridComponentProps } from '../../../GridComponentProps'; @@ -77,8 +74,7 @@ export const useGridKeyboardNavigation = ( props: Pick, ): void => { const logger = useGridLogger(apiRef, 'useGridKeyboardNavigation'); - const paginationState = useGridSelector(apiRef, gridPaginationSelector); - const paginatedRowEntries = useGridSelector(apiRef, gridSortedVisiblePaginatedRowEntriesSelector); + const statePaginationRange = useGridSelector(apiRef, gridPaginationRowRangeSelector); const colCount = useGridSelector(apiRef, visibleGridColumnsLengthSelector); const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowEntriesSelector); @@ -101,7 +97,10 @@ export const useGridKeyboardNavigation = ( const key = mapKey(event); const isCtrlPressed = event.ctrlKey || event.metaKey || event.shiftKey; - const rowCount = paginatedRowEntries.length; + const paginatedRowRange = props.pagination + ? statePaginationRange! + : { firstRowIndex: 0, lastRowIndex: visibleSortedRows.length }; + const rowCount = paginatedRowRange.lastRowIndex - paginatedRowRange.firstRowIndex + 1; let nextCellIndexes: GridCellIndexCoordinates; if (isArrowKeys(key)) { @@ -119,7 +118,7 @@ export const useGridKeyboardNavigation = ( // In that case we go to first row, first col, or last row last col! let newRowIndex = 0; if (colIdx === 0) { - newRowIndex = props.pagination ? rowCount - paginationState.pageSize : 0; + newRowIndex = paginatedRowRange.firstRowIndex; } else { newRowIndex = rowCount - 1; } @@ -160,10 +159,8 @@ export const useGridKeyboardNavigation = ( [ apiRef, visibleSortedRows, - paginatedRowEntries, + statePaginationRange, props.pagination, - paginationState.pageSize, - paginationState.page, colCount, logger, containerSizes, diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts index 3df93fb0b231e..06d74901aae6c 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts @@ -19,7 +19,7 @@ export const gridPageSizeSelector = createSelector( (pagination) => pagination.pageSize, ); -export const gridSortedVisiblePaginatedRowEntriesSelector = createSelector( +export const gridPaginationRowRangeSelector = createSelector( gridPaginationSelector, gridRowTreeSelector, gridSortedVisibleRowEntriesSelector, @@ -29,7 +29,7 @@ export const gridSortedVisiblePaginatedRowEntriesSelector = createSelector( const topLevelFirstRow = visibleSortedTopLevelRowEntries[topLevelFirstRowIndex]; if (!topLevelFirstRow) { - return []; + return null; } const topLevelInCurrentPageCount = visibleSortedTopLevelRowEntries.slice( @@ -59,6 +59,21 @@ export const gridSortedVisiblePaginatedRowEntriesSelector = createSelector( } } - return visibleSortedRowEntries.slice(firstRowIndex, lastRowIndex); + return { firstRowIndex, lastRowIndex: lastRowIndex - 1 }; + }, +); + +export const gridSortedVisiblePaginatedRowEntriesSelector = createSelector( + gridSortedVisibleRowEntriesSelector, + gridPaginationRowRangeSelector, + (visibleSortedRowEntries, paginationRange) => { + if (!paginationRange) { + return []; + } + + return visibleSortedRowEntries.slice( + paginationRange.firstRowIndex, + paginationRange.lastRowIndex + 1, + ); }, ); From 7d5dbd891e436d2140c15ea01211f56e312453d7 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 14 Oct 2021 11:49:31 +0200 Subject: [PATCH 209/390] Proptypes --- packages/grid/data-grid/src/DataGrid.tsx | 2 ++ packages/grid/x-grid/src/DataGridPro.tsx | 2 ++ 2 files changed, 4 insertions(+) diff --git a/packages/grid/data-grid/src/DataGrid.tsx b/packages/grid/data-grid/src/DataGrid.tsx index a71b3decb202e..075da98068e3c 100644 --- a/packages/grid/data-grid/src/DataGrid.tsx +++ b/packages/grid/data-grid/src/DataGrid.tsx @@ -515,6 +515,7 @@ DataGridRaw.propTypes = { page: PropTypes.number, /** * Set the number of rows in one page. + * If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows. * @default 100 */ pageSize: chainPropTypes(PropTypes.number, (props: any) => { @@ -561,6 +562,7 @@ DataGridRaw.propTypes = { rowBuffer: PropTypes.number, /** * Set the total number of rows, if it is different than the length of the value `rows` prop. + * If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows. */ rowCount: PropTypes.number, /** diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index 5b46c4c7b6691..1ca70608a7487 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -588,6 +588,7 @@ DataGridProRaw.propTypes = { page: PropTypes.number, /** * Set the number of rows in one page. + * If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows. * @default 100 */ pageSize: PropTypes.number, @@ -610,6 +611,7 @@ DataGridProRaw.propTypes = { rowBuffer: PropTypes.number, /** * Set the total number of rows, if it is different than the length of the value `rows` prop. + * If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows. */ rowCount: PropTypes.number, /** From 1e04f8c8384b2a4e23b5f8faefe38c285c133d39 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 14 Oct 2021 12:04:23 +0200 Subject: [PATCH 210/390] Fix unit test tree data --- packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index 3091cb3555953..6befd1fae567b 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -16,6 +16,8 @@ import { useGridApiRef, } from '@mui/x-data-grid-pro'; +const isJSDOM = /jsdom/.test(window.navigator.userAgent); + const rowsWithoutFiller: GridRowsProp = [ { name: 'A', value: 10 }, { name: 'A.A', value: 4 }, @@ -36,6 +38,7 @@ const rowsWithFiller: GridRowsProp = [ ]; const baselineProps: DataGridProProps = { + autoHeight: isJSDOM, rows: rowsWithoutFiller, columns: [ { From 647becde750c1b06027ad9cb84a0008ce1ef46bc Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 14 Oct 2021 13:28:39 +0200 Subject: [PATCH 211/390] Work --- docs/pages/playground.tsx | 16 ++-- .../hooks/features/filter/useGridFilter.ts | 74 +++++++++++++++++-- .../grid/models/api/gridFilterApi.ts | 6 -- 3 files changed, 77 insertions(+), 19 deletions(-) diff --git a/docs/pages/playground.tsx b/docs/pages/playground.tsx index b3f71d82debae..a476ac0928a8f 100644 --- a/docs/pages/playground.tsx +++ b/docs/pages/playground.tsx @@ -2,21 +2,20 @@ import * as React from 'react'; import { DataGridPro, GridColumns, GridRowsProp } from '@mui/x-data-grid-pro'; const rows: GridRowsProp = [ - { path: ['A'] }, - { path: ['A', 'A', 'A', 'A'] }, - { path: ['A', 'A', 'A', 'B'] }, - { path: ['B'] }, - { path: ['C', 'A', 'A'] }, + { id: 0, path: ['A'] }, + { id: 1, path: ['A', 'A', 'A', 'A'] }, + { id: 2, path: ['A', 'A', 'A', 'B'] }, + { id: 3, path: ['B'] }, + { id: 4, path: ['C', 'A', 'A'] }, ]; const columns: GridColumns = [ + { field: 'id', type: 'number' }, { field: 'path', renderCell: (params) => (params.value as string[]).join('-') }, ]; const getTreeDataPath = (row: any) => row.path; -const getRowId = (row: any) => row.path.join('-'); - export default function FillerTreeData() { return (
@@ -26,7 +25,8 @@ export default function FillerTreeData() { rows={rows} columns={columns} getTreeDataPath={getTreeDataPath} - getRowId={getRowId} + filterModel={{ items: [{ columnField: 'id', operatorValue: '!=', value: 0 }] }} + defaultGroupingExpansionDepth={1} />
); diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index ce5b8ee6f8bad..cbd33c5f45e67 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -22,8 +22,8 @@ import { } from './gridFilterSelector'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; -import { gridRowTreeSelector } from '../rows'; -import { GridRowId, GridRowModel } from '../../../models'; +import { gridRowCountSelector, gridRowTreeSelector, gridTopLevelRowCountSelector } from '../rows'; +import { GridRowId, GridRowModel, GridRowTreeNodeConfig } from '../../../models'; const checkFilterModelValidity = (model: GridFilterModel) => { if (model.items.length > 1) { @@ -161,6 +161,62 @@ export const useGridFilter = ( [apiRef, forceUpdate, logger, setGridState, props.disableChildrenFiltering], ); + /** + * Apply the following constraints: + * - A child should not be visible if its parent is not + */ + const applyTreeFilterConstraints = React.useCallback(() => { + setGridState((state) => { + const visibleRowsLookup = { ...gridVisibleRowsLookupSelector(state) }; + const rowCount = gridRowCountSelector(state); + const topLevelRowCount = gridTopLevelRowCountSelector(state); + + // The tree is flat + if (rowCount === topLevelRowCount) { + return state; + } + + const rowTree = Object.values(gridRowTreeSelector(state)); + + // No filter applied or no row has children + if (Object.keys(visibleRowsLookup).length === 0) { + return state; + } + + const rowsGroupedByDepth: Record = {}; + rowTree.forEach((node) => { + if (!rowsGroupedByDepth[node.depth]) { + rowsGroupedByDepth[node.depth] = []; + } + + rowsGroupedByDepth[node.depth].push(node); + }); + const depths = Object.keys(rowsGroupedByDepth) + .map((depth) => Number(depth)) + .sort(); + + depths.forEach((depth) => { + rowsGroupedByDepth[depth].forEach((node) => { + if (node.parent != null && visibleRowsLookup[node.parent] === false) { + visibleRowsLookup[node.id] = false; + } + }); + }); + + return { + ...state, + filter: { + ...state.filter, + visibleRowsLookup, + visibleRows: Object.entries(visibleRowsLookup) + .filter(([, isVisible]) => isVisible) + .map(([id]) => id), + }, + }; + }); + }, [setGridState]); + + // TODO: Rework so applyFilter and applyTreeFilterConstraints returns the visibleRowsLookup and do a single setGridState at the end const applyFilters = React.useCallback(() => { if (props.filterMode === GridFeatureModeConstant.server) { forceUpdate(); @@ -180,10 +236,19 @@ export const useGridFilter = ( const { items, linkOperator } = gridFilterModelSelector(apiRef.current.state); items.forEach((filterItem) => { - apiRef.current.applyFilter(filterItem, linkOperator); + applyFilter(filterItem, linkOperator); }); + applyTreeFilterConstraints(); + forceUpdate(); - }, [apiRef, setGridState, forceUpdate, props.filterMode]); + }, [ + apiRef, + setGridState, + forceUpdate, + props.filterMode, + applyFilter, + applyTreeFilterConstraints, + ]); const upsertFilter = React.useCallback( (item) => { @@ -315,7 +380,6 @@ export const useGridFilter = ( { applyFilterLinkOperator, applyFilters, - applyFilter, deleteFilter, upsertFilter, setFilterModel, diff --git a/packages/grid/_modules_/grid/models/api/gridFilterApi.ts b/packages/grid/_modules_/grid/models/api/gridFilterApi.ts index e3e8a7b047d26..8de7d25c0f50d 100644 --- a/packages/grid/_modules_/grid/models/api/gridFilterApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridFilterApi.ts @@ -20,12 +20,6 @@ export interface GridFilterApi { * @param {GridFilterItem} item The filter to update. */ upsertFilter: (item: GridFilterItem) => void; - /** - * Applies a [[GridFilterItem]] on all rows. If no `linkOperator` is given, the "and" operator is used. - * @param {GridFilterItem} item The filter to be applied. - * @param {GridLinkOperator} linkOperator The link operator to use. - */ - applyFilter: (item: GridFilterItem, linkOperator?: GridLinkOperator) => void; /** * Applies all filters on all rows. */ From 834f6c1364f2de9e4631618bf789e44817f70562 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 14 Oct 2021 14:00:38 +0200 Subject: [PATCH 212/390] Rename filler rows --- docs/pages/api-docs/data-grid/grid-api.md | 1 - docs/pages/api-docs/data-grid/grid-col-def.md | 64 +++++++++---------- .../api-docs/data-grid/grid-filter-api.json | 5 -- .../data-grid/group-pivot/group-pivot.md | 4 +- .../_modules_/grid/components/GridRow.tsx | 2 +- .../columnHeaders/GridColumnHeaderItem.tsx | 2 +- .../menu/columnMenu/GridColumnsMenuItem.tsx | 2 +- .../menu/columnMenu/GridFilterMenuItem.tsx | 2 +- .../menu/columnMenu/HideGridColMenuItem.tsx | 2 +- .../menu/columnMenu/SortGridMenuItems.tsx | 2 +- .../grid/hooks/features/rows/gridRowsUtils.ts | 4 +- .../treeData/gridTreeDataGroupColDef.tsx | 2 +- .../grid/models/colDef/gridColDef.ts | 4 +- .../grid/_modules_/grid/models/gridRows.ts | 2 +- .../src/tests/treeData.DataGridPro.test.tsx | 10 +-- 15 files changed, 51 insertions(+), 57 deletions(-) diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index b4cf65bc6c2dd..aa91e1d36b739 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -12,7 +12,6 @@ import { GridApi } from '@mui/x-data-grid-pro'; | Name | Type | Description | | :----------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| applyFilter | (item: GridFilterItem, linkOperator?: GridLinkOperator) => void | Applies a GridFilterItem on all rows. If no `linkOperator` is given, the "and" operator is used. | | applyFilterLinkOperator | (operator: GridLinkOperator) => void | Changes the GridLinkOperator used to connect the filters. | | applyFilters | () => void | Applies all filters on all rows. | | applySorting | () => void | Applies the current sort model to the rows. | diff --git a/docs/pages/api-docs/data-grid/grid-col-def.md b/docs/pages/api-docs/data-grid/grid-col-def.md index 8837d8e059012..cbba7a1933526 100644 --- a/docs/pages/api-docs/data-grid/grid-col-def.md +++ b/docs/pages/api-docs/data-grid/grid-col-def.md @@ -12,35 +12,35 @@ import { GridColDef } from '@mui/x-data-grid'; ## Properties -| Name | Type | Default | Description | -| :------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------- | :----------------------------------------------- | :-------------------------------------------------------------------------------------------------------------- | -| align? | GridAlignment | | Allows to align the column values in cells. | -| cellClassName? | GridCellClassNamePropType | | Class name that will be added in cells for that column. | -| description? | string | | The description of the column rendered as tooltip if the column header name is not fully displayed. | -| disableColumnMenu? | boolean | false
| If `true`, the column menu is disabled for this column. | -| disableExport? | boolean | false
| If `true`, this column will not be included in exports. | -| disableReorder? | boolean | false
| If `true`, this column cannot be reordered. | -| editable? | boolean | false
| If `true`, the cells of the column are editable. | -| field | string | | The column identifier. It's used to map with GridRowData values. | -| filterable? | boolean | true
| If `true`, the column is filterable. | -| filterOperators? | GridFilterOperator[] | | Allows setting the filter operators for this column. | -| flex? | number | | If set, it indicates that a column has fluid width. Range [0, ∞). | -| headerAlign? | GridAlignment | | Header cell element alignment. | -| headerClassName? | GridColumnHeaderClassNamePropType | | Class name that will be added in the column header cell. | -| headerName? | string | | The title of the column rendered in the column header cell. | -| hide? | boolean | false
| If `true`, hide the column. | -| hideSortIcons? | boolean | false
| Toggle the visibility of the sort icons. | -| minWidth? | number | 50
| Sets the minimum width of a column. | -| renderCell? | (params: GridRenderCellParams) => ReactNode | | Allows to override the component rendered as cell for this column. | -| renderEditCell? | (params: GridRenderEditCellParams) => ReactNode | | Allows to override the component rendered in edit cell mode for this column. | -| renderHeader? | (params: GridColumnHeaderParams) => ReactNode | | Allows to render a component in the column header cell. | -| resizable? | boolean | true
| If `true`, the column is resizable. | -| shouldRenderFillerRows? | boolean | | If `true`, the `renderCell` / will be called for the filler rows | -| sortable? | boolean | true
| If `true`, the column is sortable. | -| sortComparator? | GridComparatorFn | | A comparator function used to sort rows. | -| type? | string | 'string'
| Type allows to merge this object with a default definition [GridColDef](/api/data-grid/grid-col-def/). | -| valueFormatter? | (params: GridValueFormatterParams) => GridCellValue | | Function that allows to apply a formatter before rendering its value. | -| valueGetter? | (params: GridValueGetterParams) => GridCellValue | | Function that allows to get a specific data instead of field to render in the cell. | -| valueOptions? | (string \| number \| { label: string; value: any })[] | | To be used in combination with `type: 'singleSelect'`. This is an array of the possible cell values and labels. | -| valueParser? | (value: GridCellValue, params?: GridCellParams) => GridCellValue | | Function that takes the user-entered value and converts it to a value used internally. | -| width? | number | 100
| Set the width of the column. | +| Name | Type | Default | Description | +| :-------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------- | :----------------------------------------------- | :-------------------------------------------------------------------------------------------------------------- | +| align? | GridAlignment | | Allows to align the column values in cells. | +| cellClassName? | GridCellClassNamePropType | | Class name that will be added in cells for that column. | +| description? | string | | The description of the column rendered as tooltip if the column header name is not fully displayed. | +| disableColumnMenu? | boolean | false
| If `true`, the column menu is disabled for this column. | +| disableExport? | boolean | false
| If `true`, this column will not be included in exports. | +| disableReorder? | boolean | false
| If `true`, this column cannot be reordered. | +| editable? | boolean | false
| If `true`, the cells of the column are editable. | +| field | string | | The column identifier. It's used to map with GridRowData values. | +| filterable? | boolean | true
| If `true`, the column is filterable. | +| filterOperators? | GridFilterOperator[] | | Allows setting the filter operators for this column. | +| flex? | number | | If set, it indicates that a column has fluid width. Range [0, ∞). | +| headerAlign? | GridAlignment | | Header cell element alignment. | +| headerClassName? | GridColumnHeaderClassNamePropType | | Class name that will be added in the column header cell. | +| headerName? | string | | The title of the column rendered in the column header cell. | +| hide? | boolean | false
| If `true`, hide the column. | +| hideSortIcons? | boolean | false
| Toggle the visibility of the sort icons. | +| minWidth? | number | 50
| Sets the minimum width of a column. | +| renderCell? | (params: GridRenderCellParams) => ReactNode | | Allows to override the component rendered as cell for this column. | +| renderEditCell? | (params: GridRenderEditCellParams) => ReactNode | | Allows to override the component rendered in edit cell mode for this column. | +| renderHeader? | (params: GridColumnHeaderParams) => ReactNode | | Allows to render a component in the column header cell. | +| resizable? | boolean | true
| If `true`, the column is resizable. | +| shouldRenderAutoGeneratedRows? | boolean | | If `true`, the `renderCell` / will be called for the auto generated rows | +| sortable? | boolean | true
| If `true`, the column is sortable. | +| sortComparator? | GridComparatorFn | | A comparator function used to sort rows. | +| type? | string | 'string'
| Type allows to merge this object with a default definition [GridColDef](/api/data-grid/grid-col-def/). | +| valueFormatter? | (params: GridValueFormatterParams) => GridCellValue | | Function that allows to apply a formatter before rendering its value. | +| valueGetter? | (params: GridValueGetterParams) => GridCellValue | | Function that allows to get a specific data instead of field to render in the cell. | +| valueOptions? | (string \| number \| { label: string; value: any })[] | | To be used in combination with `type: 'singleSelect'`. This is an array of the possible cell values and labels. | +| valueParser? | (value: GridCellValue, params?: GridCellParams) => GridCellValue | | Function that takes the user-entered value and converts it to a value used internally. | +| width? | number | 100
| Set the width of the column. | diff --git a/docs/pages/api-docs/data-grid/grid-filter-api.json b/docs/pages/api-docs/data-grid/grid-filter-api.json index 0bfb1c8f6c8bd..f77220bd8b14f 100644 --- a/docs/pages/api-docs/data-grid/grid-filter-api.json +++ b/docs/pages/api-docs/data-grid/grid-filter-api.json @@ -2,11 +2,6 @@ "name": "GridFilterApi", "description": "The filter API interface that is available in the grid apiRef.", "properties": [ - { - "name": "applyFilter", - "description": "Applies a GridFilterItem on all rows. If no linkOperator is given, the "and" operator is used.", - "type": "(item: GridFilterItem, linkOperator?: GridLinkOperator) => void" - }, { "name": "applyFilterLinkOperator", "description": "Changes the GridLinkOperator used to connect the filters.", diff --git a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md index f1824f24d523d..c6e7d4f7feb93 100644 --- a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md +++ b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md @@ -69,9 +69,9 @@ Use the `UNSTABLE_setRowExpansion` method on `apiRef` to programmatically set th {{"demo": "pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js", "bg": "inline", "defaultCodeOpen": false}} -### Filler rows +### Gaps in the tree -If some entries are missing to build the full tree, the `DataGridPro` will automatically create filler rows to fill those gaps. +If some entries are missing to build the full tree, the `DataGridPro` will automatically create rows to fill those gaps. {{"demo": "pages/components/data-grid/group-pivot/FillerTreeData.js", "bg": "inline", "defaultCodeOpen": false}} diff --git a/packages/grid/_modules_/grid/components/GridRow.tsx b/packages/grid/_modules_/grid/components/GridRow.tsx index d5de6621332f8..1302b09f35551 100644 --- a/packages/grid/_modules_/grid/components/GridRow.tsx +++ b/packages/grid/_modules_/grid/components/GridRow.tsx @@ -165,7 +165,7 @@ function GridRow(props: React.HTMLAttributes & GridRowProps) { const editCellState = editRowsState[rowId] ? editRowsState[rowId][column.field] : null; let content: React.ReactNode = null; - const skipRender = !column.shouldRenderFillerRows && rowNode?.fillerNode; + const skipRender = !column.shouldRenderAutoGeneratedRows && rowNode?.isAutoGenerated; if (editCellState == null && column.renderCell && !skipRender) { content = column.renderCell({ ...cellParams, api: apiRef.current }); diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderItem.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderItem.tsx index df1530be70f6f..a36f3f3c73641 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderItem.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaderItem.tsx @@ -270,7 +270,7 @@ GridColumnHeaderItem.propTypes = { renderEditCell: PropTypes.func, renderHeader: PropTypes.func, resizable: PropTypes.bool, - shouldRenderFillerRows: PropTypes.bool, + shouldRenderAutoGeneratedRows: PropTypes.bool, sortable: PropTypes.bool, sortComparator: PropTypes.func, type: PropTypes.string, diff --git a/packages/grid/_modules_/grid/components/menu/columnMenu/GridColumnsMenuItem.tsx b/packages/grid/_modules_/grid/components/menu/columnMenu/GridColumnsMenuItem.tsx index ac00d9d17f362..2163fb16a0ad4 100644 --- a/packages/grid/_modules_/grid/components/menu/columnMenu/GridColumnsMenuItem.tsx +++ b/packages/grid/_modules_/grid/components/menu/columnMenu/GridColumnsMenuItem.tsx @@ -65,7 +65,7 @@ GridColumnsMenuItem.propTypes = { renderEditCell: PropTypes.func, renderHeader: PropTypes.func, resizable: PropTypes.bool, - shouldRenderFillerRows: PropTypes.bool, + shouldRenderAutoGeneratedRows: PropTypes.bool, sortable: PropTypes.bool, sortComparator: PropTypes.func, type: PropTypes.string, diff --git a/packages/grid/_modules_/grid/components/menu/columnMenu/GridFilterMenuItem.tsx b/packages/grid/_modules_/grid/components/menu/columnMenu/GridFilterMenuItem.tsx index 46aa001fe5009..8e29e70419470 100644 --- a/packages/grid/_modules_/grid/components/menu/columnMenu/GridFilterMenuItem.tsx +++ b/packages/grid/_modules_/grid/components/menu/columnMenu/GridFilterMenuItem.tsx @@ -62,7 +62,7 @@ GridFilterMenuItem.propTypes = { renderEditCell: PropTypes.func, renderHeader: PropTypes.func, resizable: PropTypes.bool, - shouldRenderFillerRows: PropTypes.bool, + shouldRenderAutoGeneratedRows: PropTypes.bool, sortable: PropTypes.bool, sortComparator: PropTypes.func, type: PropTypes.string, diff --git a/packages/grid/_modules_/grid/components/menu/columnMenu/HideGridColMenuItem.tsx b/packages/grid/_modules_/grid/components/menu/columnMenu/HideGridColMenuItem.tsx index 9aefb5447c9ee..bd57dcb04480c 100644 --- a/packages/grid/_modules_/grid/components/menu/columnMenu/HideGridColMenuItem.tsx +++ b/packages/grid/_modules_/grid/components/menu/columnMenu/HideGridColMenuItem.tsx @@ -72,7 +72,7 @@ HideGridColMenuItem.propTypes = { renderEditCell: PropTypes.func, renderHeader: PropTypes.func, resizable: PropTypes.bool, - shouldRenderFillerRows: PropTypes.bool, + shouldRenderAutoGeneratedRows: PropTypes.bool, sortable: PropTypes.bool, sortComparator: PropTypes.func, type: PropTypes.string, diff --git a/packages/grid/_modules_/grid/components/menu/columnMenu/SortGridMenuItems.tsx b/packages/grid/_modules_/grid/components/menu/columnMenu/SortGridMenuItems.tsx index 12fb9b91b97c3..f0908f1eeece9 100644 --- a/packages/grid/_modules_/grid/components/menu/columnMenu/SortGridMenuItems.tsx +++ b/packages/grid/_modules_/grid/components/menu/columnMenu/SortGridMenuItems.tsx @@ -83,7 +83,7 @@ SortGridMenuItems.propTypes = { renderEditCell: PropTypes.func, renderHeader: PropTypes.func, resizable: PropTypes.bool, - shouldRenderFillerRows: PropTypes.bool, + shouldRenderAutoGeneratedRows: PropTypes.bool, sortable: PropTypes.bool, sortComparator: PropTypes.func, type: PropTypes.string, diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts index 0e970c529bfb7..77835579fe6f6 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts @@ -35,7 +35,7 @@ export const insertRowInTree = (params: InsertRowInTreeParams) => { let nodeNameConfig = nodeNameToIdSubTree[nodeName]; if (!nodeNameConfig) { - nodeId = depth === path.length - 1 ? id : `filler-row-${path.slice(0, depth + 1).join('-')}`; + nodeId = depth === path.length - 1 ? id : `auto-generated-row-${path.slice(0, depth + 1).join('-')}`; nodeNameConfig = { id: nodeId, children: {} }; nodeNameToIdSubTree[nodeName] = nodeNameConfig; @@ -49,7 +49,7 @@ export const insertRowInTree = (params: InsertRowInTreeParams) => { if (!node) { node = { id: nodeId, - fillerNode: true, + isAutoGenerated: true, expanded, children: [], parent: parentNode?.id ?? null, diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx index 2e7655d22f743..1937cf0bad017 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx +++ b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx @@ -10,7 +10,7 @@ export const GRID_TREE_DATA_GROUP_COL_DEF: GridColDef = { filterable: false, disableColumnMenu: true, disableReorder: true, - shouldRenderFillerRows: true, + shouldRenderAutoGeneratedRows: true, align: 'left', width: 200, renderCell: (params) => , diff --git a/packages/grid/_modules_/grid/models/colDef/gridColDef.ts b/packages/grid/_modules_/grid/models/colDef/gridColDef.ts index 08f20efe20738..9056032e929d3 100644 --- a/packages/grid/_modules_/grid/models/colDef/gridColDef.ts +++ b/packages/grid/_modules_/grid/models/colDef/gridColDef.ts @@ -157,9 +157,9 @@ export interface GridColDef { */ filterOperators?: GridFilterOperator[]; /** - * If `true`, the `renderCell` / will be called for the filler rows + * If `true`, the `renderCell` / will be called for the auto generated rows */ - shouldRenderFillerRows?: boolean; + shouldRenderAutoGeneratedRows?: boolean; /** * If `true`, this column cannot be reordered. * @default false diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index 675f3b0c7976b..b551a2540227b 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -23,7 +23,7 @@ export interface GridRowTreeNodeConfig { /** * If `true`, this node has been automatically added to fill a gap in the tree structure */ - fillerNode?: boolean; + isAutoGenerated?: boolean; } export type GridRowTreeConfig = Record; diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index 6befd1fae567b..2fae64a50f790 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -18,7 +18,7 @@ import { const isJSDOM = /jsdom/.test(window.navigator.userAgent); -const rowsWithoutFiller: GridRowsProp = [ +const rowsWithoutGap: GridRowsProp = [ { name: 'A', value: 10 }, { name: 'A.A', value: 4 }, { name: 'A.B', value: 6 }, @@ -29,7 +29,7 @@ const rowsWithoutFiller: GridRowsProp = [ { name: 'C', value: 5 }, ]; -const rowsWithFiller: GridRowsProp = [ +const rowsWithGap: GridRowsProp = [ { name: 'A', value: 10 }, { name: 'A.B', value: 6 }, { name: 'A.A', value: 4 }, @@ -39,7 +39,7 @@ const rowsWithFiller: GridRowsProp = [ const baselineProps: DataGridProProps = { autoHeight: isJSDOM, - rows: rowsWithoutFiller, + rows: rowsWithoutGap, columns: [ { field: 'name', @@ -211,8 +211,8 @@ describe(' - Tree Data', () => { expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); }); - it('should add filler rows if some parents do not exist', () => { - render(); + it('should add auto generated rows if some parents do not exist', () => { + render(); expect(getColumnValues(1)).to.deep.equal(['A', '']); expect(getColumnValues(0)).to.deep.equal(['A', 'B']); fireEvent.click(getCell(1, 0).querySelector('button')); From 4ca8e87917d2e434ddd88e2ce8c4033b6708c7f9 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 14 Oct 2021 14:00:57 +0200 Subject: [PATCH 213/390] Prettier --- .../grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts index 77835579fe6f6..3c892a0e3265c 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts @@ -35,7 +35,8 @@ export const insertRowInTree = (params: InsertRowInTreeParams) => { let nodeNameConfig = nodeNameToIdSubTree[nodeName]; if (!nodeNameConfig) { - nodeId = depth === path.length - 1 ? id : `auto-generated-row-${path.slice(0, depth + 1).join('-')}`; + nodeId = + depth === path.length - 1 ? id : `auto-generated-row-${path.slice(0, depth + 1).join('-')}`; nodeNameConfig = { id: nodeId, children: {} }; nodeNameToIdSubTree[nodeName] = nodeNameConfig; From d60e81b77019ee894e24aef59b14a7f80e187bfb Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 14 Oct 2021 14:51:46 +0200 Subject: [PATCH 214/390] Rework filtering --- .../hooks/features/filter/useGridFilter.ts | 295 ++++++++++-------- .../grid/models/gridFilterOperator.ts | 4 +- 2 files changed, 160 insertions(+), 139 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index cbd33c5f45e67..85ec8e0b5b384 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -15,15 +15,13 @@ import { GridPreferencePanelsValue } from '../preferencesPanel/gridPreferencePan import { gridSortedRowIdsSelector } from '../sorting/gridSortingSelector'; import { getDefaultGridFilterModel } from './gridFilterState'; import { GridFilterModel } from '../../../models/gridFilterModel'; -import { - gridVisibleRowsLookupSelector, - gridFilterModelSelector, - gridSortedVisibleRowEntriesSelector, -} from './gridFilterSelector'; +import { gridFilterModelSelector, gridSortedVisibleRowEntriesSelector } from './gridFilterSelector'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; -import { gridRowCountSelector, gridRowTreeSelector, gridTopLevelRowCountSelector } from '../rows'; -import { GridRowId, GridRowModel, GridRowTreeNodeConfig } from '../../../models'; +import { gridRowTreeSelector } from '../rows'; +import { GridRowId, GridRowModel } from '../../../models/gridRows'; + +type GridFilterItemApplier = (rowId: GridRowId) => boolean; const checkFilterModelValidity = (model: GridFilterModel) => { if (model.items.length > 1) { @@ -80,127 +78,180 @@ export const useGridFilter = ( changeEvent: GridEvents.filterModelChange, }); - const applyFilter = React.useCallback( - (filterItem: GridFilterItem, linkOperator: GridLinkOperator = GridLinkOperator.And) => { - if (!filterItem.columnField || !filterItem.operatorValue) { - return; - } + const buildAggregatedFilter = React.useCallback( + (filterModel: GridFilterModel) => { + const rowTree = gridRowTreeSelector(apiRef.current.state); - const column = apiRef.current.getColumn(filterItem.columnField); + const { items, linkOperator = GridLinkOperator.And } = filterModel; - if (!column) { - return; - } + const getFilterCallbackFromItem = ( + filterItem: GridFilterItem, + ): GridFilterItemApplier | null => { + if (!filterItem.columnField || !filterItem.operatorValue) { + return null; + } - const parsedValue = column.valueParser - ? column.valueParser(filterItem.value) - : filterItem.value; - const newFilterItem = { ...filterItem, value: parsedValue }; + const column = apiRef.current.getColumn(filterItem.columnField); + if (!column) { + return null; + } - logger.debug( - `Filtering column: ${newFilterItem.columnField} ${newFilterItem.operatorValue} ${newFilterItem.value} `, - ); + const parsedValue = column.valueParser + ? column.valueParser(filterItem.value) + : filterItem.value; + const newFilterItem: GridFilterItem = { ...filterItem, value: parsedValue }; - const filterOperators = column.filterOperators; - if (!filterOperators?.length) { - throw new Error(`MUI: No filter operators found for column '${column.field}'.`); - } + const filterOperators = column.filterOperators; + if (!filterOperators?.length) { + throw new Error(`MUI: No filter operators found for column '${column.field}'.`); + } - const filterOperator = filterOperators.find( - (operator) => operator.value === newFilterItem.operatorValue, - )!; - if (!filterOperator) { - throw new Error( - `MUI: No filter operator found for column '${column.field}' and operator value '${newFilterItem.operatorValue}'.`, - ); - } + const filterOperator = filterOperators.find( + (operator) => operator.value === newFilterItem.operatorValue, + )!; + if (!filterOperator) { + throw new Error( + `MUI: No filter operator found for column '${column.field}' and operator value '${newFilterItem.operatorValue}'.`, + ); + } + + const applyFilterOnRow = filterOperator.getApplyFilterFn(newFilterItem, column)!; + if (typeof applyFilterOnRow !== 'function') { + return null; + } + + return (rowId: GridRowId) => { + const cellParams = apiRef.current.getCellParams(rowId, newFilterItem.columnField!); + + return applyFilterOnRow(cellParams); + }; + }; + + const appliers = items + .map(getFilterCallbackFromItem) + .filter((callback): callback is GridFilterItemApplier => !!callback); - const applyFilterOnRow = filterOperator.getApplyFilterFn(newFilterItem, column)!; - if (typeof applyFilterOnRow !== 'function') { - return; + if (appliers.length === 0) { + return null; } - setGridState((state) => { - const visibleRowsLookup = { ...gridVisibleRowsLookupSelector(state) }; - - // We run the selector on the state here to avoid rendering the rows and then filtering again. - // This way we have latest rows on the first rendering - const rows = gridSortedRowIdsSelector(state); - const rowTree = gridRowTreeSelector(state); - - rows.forEach((rowId) => { - const params = apiRef.current.getCellParams(rowId, newFilterItem.columnField!); - const depth = rowTree[rowId].depth; - - if (depth === 0 || !props.disableChildrenFiltering) { - const isShown = applyFilterOnRow(params); - if (visibleRowsLookup[rowId] == null) { - visibleRowsLookup[rowId] = isShown; - } else { - visibleRowsLookup[rowId] = - linkOperator === GridLinkOperator.And - ? visibleRowsLookup[rowId] && isShown - : visibleRowsLookup[rowId] || isShown; - } + return (rowId: GridRowId) => { + const depth = rowTree[rowId].depth; + + if (depth > 0 && props.disableChildrenFiltering) { + return true; + } + + // We return `false` as soon as we have a failing filter + if (linkOperator === GridLinkOperator.And) { + let isPassingFilters = true; + let filterIndex = 0; + + while (isPassingFilters && filterIndex < appliers.length) { + isPassingFilters = appliers[filterIndex](rowId); + filterIndex += 1; } - }); - return { - ...state, - filter: { - ...state.filter, - visibleRowsLookup, - visibleRows: Object.entries(visibleRowsLookup) - .filter(([, isVisible]) => isVisible) - .map(([id]) => id), - }, - }; - }); - forceUpdate(); + return isPassingFilters; + } + + // We return `true` as soon as we have a passing filter + let isPassingFilters = false; + let filterIndex = 0; + + while (!isPassingFilters && filterIndex < appliers.length) { + isPassingFilters = appliers[filterIndex](rowId); + filterIndex += 1; + } + + return isPassingFilters; + }; }, - [apiRef, forceUpdate, logger, setGridState, props.disableChildrenFiltering], + [apiRef, props.disableChildrenFiltering], ); /** * Apply the following constraints: - * - A child should not be visible if its parent is not + * - A child passing the filter should not be visible if its parent is not + * - A parent passing the filter should not be visible if none of its descendant is passing the filter */ - const applyTreeFilterConstraints = React.useCallback(() => { - setGridState((state) => { - const visibleRowsLookup = { ...gridVisibleRowsLookupSelector(state) }; - const rowCount = gridRowCountSelector(state); - const topLevelRowCount = gridTopLevelRowCountSelector(state); + // const applyTreeFilterConstraints = React.useCallback(() => { + // setGridState((state) => { + // const visibleRowsLookup = { ...gridVisibleRowsLookupSelector(state) }; + // const rowCount = gridRowCountSelector(state); + // const topLevelRowCount = gridTopLevelRowCountSelector(state); + // + // // The tree is flat + // if (rowCount === topLevelRowCount) { + // return state; + // } + // + // const rowTree = gridRowTreeSelector(state); + // + // // No filter applied or no row has children + // if (Object.keys(visibleRowsLookup).length === 0) { + // return state; + // } + // + // const rowsGroupedByDepth: Record = {}; + // Object.values(rowTree).forEach((node) => { + // if (!rowsGroupedByDepth[node.depth]) { + // rowsGroupedByDepth[node.depth] = []; + // } + // + // rowsGroupedByDepth[node.depth].push(node); + // }); + // const depths = Object.keys(rowsGroupedByDepth) + // .map((depth) => Number(depth)) + // .sort(); + // + // depths.forEach((depth) => { + // rowsGroupedByDepth[depth].forEach((node) => { + // if (node.parent != null && visibleRowsLookup[node.parent] === false) { + // visibleRowsLookup[node.id] = false; + // } + // }); + // }); + // + // return { + // ...state, + // filter: { + // ...state.filter, + // visibleRowsLookup, + // visibleRows: Object.entries(visibleRowsLookup) + // .filter(([, isVisible]) => isVisible) + // .map(([id]) => id), + // }, + // }; + // }); + // }, [setGridState]); - // The tree is flat - if (rowCount === topLevelRowCount) { - return state; - } + const applyFilters = React.useCallback(() => { + if (props.filterMode === GridFeatureModeConstant.server) { + forceUpdate(); + return; + } - const rowTree = Object.values(gridRowTreeSelector(state)); + setGridState((state) => { + const filterModel = gridFilterModelSelector(state); + const filteringMethod = buildAggregatedFilter(filterModel); - // No filter applied or no row has children - if (Object.keys(visibleRowsLookup).length === 0) { - return state; + if (!filteringMethod) { + return { + ...state, + filter: { + ...state.filter, + visibleRowsLookup: {}, + visibleRows: gridSortedRowIdsSelector(state), + }, + }; } - const rowsGroupedByDepth: Record = {}; - rowTree.forEach((node) => { - if (!rowsGroupedByDepth[node.depth]) { - rowsGroupedByDepth[node.depth] = []; - } + const rows = gridSortedRowIdsSelector(apiRef.current.state); + const visibleRowsLookup: Record = {}; - rowsGroupedByDepth[node.depth].push(node); - }); - const depths = Object.keys(rowsGroupedByDepth) - .map((depth) => Number(depth)) - .sort(); - - depths.forEach((depth) => { - rowsGroupedByDepth[depth].forEach((node) => { - if (node.parent != null && visibleRowsLookup[node.parent] === false) { - visibleRowsLookup[node.id] = false; - } - }); + rows.forEach((rowId) => { + visibleRowsLookup[rowId] = filteringMethod(rowId); }); return { @@ -214,41 +265,9 @@ export const useGridFilter = ( }, }; }); - }, [setGridState]); - - // TODO: Rework so applyFilter and applyTreeFilterConstraints returns the visibleRowsLookup and do a single setGridState at the end - const applyFilters = React.useCallback(() => { - if (props.filterMode === GridFeatureModeConstant.server) { - forceUpdate(); - return; - } - - // Clearing filtered rows - setGridState((state) => ({ - ...state, - filter: { - ...state.filter, - visibleRowsLookup: {}, - visibleRows: gridSortedRowIdsSelector(state), - }, - })); - - const { items, linkOperator } = gridFilterModelSelector(apiRef.current.state); - - items.forEach((filterItem) => { - applyFilter(filterItem, linkOperator); - }); - applyTreeFilterConstraints(); forceUpdate(); - }, [ - apiRef, - setGridState, - forceUpdate, - props.filterMode, - applyFilter, - applyTreeFilterConstraints, - ]); + }, [apiRef, setGridState, forceUpdate, props.filterMode, buildAggregatedFilter]); const upsertFilter = React.useCallback( (item) => { diff --git a/packages/grid/_modules_/grid/models/gridFilterOperator.ts b/packages/grid/_modules_/grid/models/gridFilterOperator.ts index 3c94b8d8401a3..d1ffff9adecec 100644 --- a/packages/grid/_modules_/grid/models/gridFilterOperator.ts +++ b/packages/grid/_modules_/grid/models/gridFilterOperator.ts @@ -4,13 +4,15 @@ import { GridFilterItem } from './gridFilterItem'; import { GridCellParams } from './params/gridCellParams'; import type { GridStateColDef } from './colDef'; +export type GridFilterCallback = (params: GridCellParams) => boolean; + export interface GridFilterOperator { label?: string; value: string; getApplyFilterFn: ( filterItem: GridFilterItem, column: GridStateColDef, - ) => null | ((params: GridCellParams) => boolean); + ) => null | GridFilterCallback; InputComponent?: React.JSXElementConstructor; InputComponentProps?: Record; } From 233c6bd86bd85f0b0ef23f9cbc7784909f1f27f5 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 14 Oct 2021 15:06:58 +0200 Subject: [PATCH 215/390] [core] Rework filter application to prepare for tree data --- .../hooks/features/filter/useGridFilter.ts | 237 ++++++++++-------- .../grid/models/api/gridFilterApi.ts | 6 - .../src/tests/filtering.DataGridPro.test.tsx | 2 +- 3 files changed, 136 insertions(+), 109 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 638624dd9ade7..d58c865e68170 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -5,7 +5,7 @@ import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridFilterApi } from '../../../models/api/gridFilterApi'; import { GridFeatureModeConstant } from '../../../models/gridFeatureMode'; import { GridFilterItem, GridLinkOperator } from '../../../models/gridFilterItem'; -import { GridRowId, GridRowModel } from '../../../models/gridRows'; +import { GridRowId } from '../../../models/gridRows'; import { isDeepEqual } from '../../../utils/utils'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; @@ -13,17 +13,18 @@ import { useGridLogger } from '../../utils/useGridLogger'; import { filterableGridColumnsIdsSelector } from '../columns/gridColumnsSelector'; import { useGridState } from '../../utils/useGridState'; import { GridPreferencePanelsValue } from '../preferencesPanel/gridPreferencePanelsValue'; -import { sortedGridRowsSelector } from '../sorting/gridSortingSelector'; +import {sortedGridRowIdsSelector} from '../sorting/gridSortingSelector'; import { getDefaultGridFilterModel } from './gridFilterState'; import { GridFilterModel } from '../../../models/gridFilterModel'; import { - gridVisibleRowsLookupSelector, visibleSortedGridRowsSelector, gridFilterModelSelector, } from './gridFilterSelector'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; +type GridFilterItemApplier = (rowId: GridRowId) => boolean; + const checkFilterModelValidity = (model: GridFilterModel) => { if (model.items.length > 1) { const hasItemsWithoutIds = model.items.find((item) => item.id == null); @@ -75,106 +76,139 @@ export const useGridFilter = ( changeEvent: GridEvents.filterModelChange, }); - const applyFilter = React.useCallback( - (filterItem: GridFilterItem, linkOperator: GridLinkOperator = GridLinkOperator.And) => { - if (!filterItem.columnField || !filterItem.operatorValue) { - return; - } - - const column = apiRef.current.getColumn(filterItem.columnField); - - if (!column) { - return; - } - - const parsedValue = column.valueParser - ? column.valueParser(filterItem.value) - : filterItem.value; - const newFilterItem = { ...filterItem, value: parsedValue }; - - logger.debug( - `Filtering column: ${newFilterItem.columnField} ${newFilterItem.operatorValue} ${newFilterItem.value} `, - ); - - const filterOperators = column.filterOperators; - if (!filterOperators?.length) { - throw new Error(`MUI: No filter operators found for column '${column.field}'.`); - } - - const filterOperator = filterOperators.find( - (operator) => operator.value === newFilterItem.operatorValue, - )!; - if (!filterOperator) { - throw new Error( - `MUI: No filter operator found for column '${column.field}' and operator value '${newFilterItem.operatorValue}'.`, - ); - } - - const applyFilterOnRow = filterOperator.getApplyFilterFn(newFilterItem, column)!; - if (typeof applyFilterOnRow !== 'function') { - return; - } - - setGridState((state) => { - const visibleRowsLookup = { ...gridVisibleRowsLookupSelector(state) }; - - // We run the selector on the state here to avoid rendering the rows and then filtering again. - // This way we have latest rows on the first rendering - const rows = sortedGridRowsSelector(state); - - rows.forEach((row: GridRowModel, id: GridRowId) => { - const params = apiRef.current.getCellParams(id, newFilterItem.columnField!); - - const isShown = applyFilterOnRow(params); - if (visibleRowsLookup[id] == null) { - visibleRowsLookup[id] = isShown; - } else { - visibleRowsLookup[id] = - linkOperator === GridLinkOperator.And - ? visibleRowsLookup[id] && isShown - : visibleRowsLookup[id] || isShown; - } + const buildAggregatedFilterApplier = React.useCallback( + (filterModel: GridFilterModel): GridFilterItemApplier | null => { + const { items, linkOperator = GridLinkOperator.And } = filterModel; + + const getFilterCallbackFromItem = ( + filterItem: GridFilterItem, + ): GridFilterItemApplier | null => { + if (!filterItem.columnField || !filterItem.operatorValue) { + return null; + } + + const column = apiRef.current.getColumn(filterItem.columnField); + if (!column) { + return null; + } + + const parsedValue = column.valueParser + ? column.valueParser(filterItem.value) + : filterItem.value; + const newFilterItem: GridFilterItem = { ...filterItem, value: parsedValue }; + + const filterOperators = column.filterOperators; + if (!filterOperators?.length) { + throw new Error(`MUI: No filter operators found for column '${column.field}'.`); + } + + const filterOperator = filterOperators.find( + (operator) => operator.value === newFilterItem.operatorValue, + )!; + if (!filterOperator) { + throw new Error( + `MUI: No filter operator found for column '${column.field}' and operator value '${newFilterItem.operatorValue}'.`, + ); + } + + const applyFilterOnRow = filterOperator.getApplyFilterFn(newFilterItem, column)!; + if (typeof applyFilterOnRow !== 'function') { + return null; + } + + return (rowId: GridRowId) => { + const cellParams = apiRef.current.getCellParams(rowId, newFilterItem.columnField!); + + return applyFilterOnRow(cellParams); + }; + }; + + const appliers = items + .map(getFilterCallbackFromItem) + .filter((callback): callback is GridFilterItemApplier => !!callback); + + if (appliers.length === 0) { + return null; + } + + return (rowId: GridRowId) => { + // We return `false` as soon as we have a failing filter + if (linkOperator === GridLinkOperator.And) { + let isPassingFilters = true; + let filterIndex = 0; + + while (isPassingFilters && filterIndex < appliers.length) { + isPassingFilters = appliers[filterIndex](rowId); + filterIndex += 1; + } + + return isPassingFilters; + } + + // We return `true` as soon as we have a passing filter + let isPassingFilters = false; + let filterIndex = 0; + + while (!isPassingFilters && filterIndex < appliers.length) { + isPassingFilters = appliers[filterIndex](rowId); + filterIndex += 1; + } + + return isPassingFilters; + }; + }, + [apiRef], + ); + + const applyFilters = React.useCallback(() => { + setGridState((state) => { + const filterModel = gridFilterModelSelector(state); + + if (props.filterMode === GridFeatureModeConstant.server) { + return { + ...state, + filter: { + ...state.filter, + visibleRowsLookup: {}, + visibleRows: null, + }, + }; + } + + // No filter to apply + const filteringMethod = buildAggregatedFilterApplier(filterModel); + if (!filteringMethod) { + return { + ...state, + filter: { + ...state.filter, + visibleRowsLookup: {}, + visibleRows: null, + }, + }; + } + + const rowIds = sortedGridRowIdsSelector(apiRef.current.state); + const visibleRowsLookup: Record = {}; + + rowIds.forEach((rowId) => { + visibleRowsLookup[rowId] = filteringMethod(rowId); + }); + + return { + ...state, + filter: { + ...state.filter, + visibleRowsLookup, + visibleRows: Object.entries(visibleRowsLookup) + .filter(([, isVisible]) => isVisible) + .map(([id]) => id), + }, + }; }); - return { - ...state, - filter: { - ...state.filter, - visibleRowsLookup, - visibleRows: Object.entries(visibleRowsLookup) - .filter(([, isVisible]) => isVisible) - .map(([id]) => id), - }, - }; - }); - forceUpdate(); - }, - [apiRef, forceUpdate, logger, setGridState], - ); - - const applyFilters = React.useCallback(() => { - if (props.filterMode === GridFeatureModeConstant.server) { - forceUpdate(); - return; - } - - // Clearing filtered rows - setGridState((state) => ({ - ...state, - filter: { - ...state.filter, - visibleRowsLookup: {}, - visibleRows: null, - }, - })); - - const { items, linkOperator } = gridFilterModelSelector(apiRef.current.state); - - items.forEach((filterItem) => { - apiRef.current.applyFilter(filterItem, linkOperator); - }); - forceUpdate(); - }, [apiRef, setGridState, forceUpdate, props.filterMode]); + forceUpdate(); + }, [apiRef, setGridState, forceUpdate, props.filterMode, buildAggregatedFilterApplier]); const upsertFilter = React.useCallback( (item) => { @@ -306,7 +340,6 @@ export const useGridFilter = ( { applyFilterLinkOperator, applyFilters, - applyFilter, deleteFilter, upsertFilter, setFilterModel, diff --git a/packages/grid/_modules_/grid/models/api/gridFilterApi.ts b/packages/grid/_modules_/grid/models/api/gridFilterApi.ts index e3e8a7b047d26..8de7d25c0f50d 100644 --- a/packages/grid/_modules_/grid/models/api/gridFilterApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridFilterApi.ts @@ -20,12 +20,6 @@ export interface GridFilterApi { * @param {GridFilterItem} item The filter to update. */ upsertFilter: (item: GridFilterItem) => void; - /** - * Applies a [[GridFilterItem]] on all rows. If no `linkOperator` is given, the "and" operator is used. - * @param {GridFilterItem} item The filter to be applied. - * @param {GridLinkOperator} linkOperator The link operator to use. - */ - applyFilter: (item: GridFilterItem, linkOperator?: GridLinkOperator) => void; /** * Applies all filters on all rows. */ diff --git a/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx index 8b83482b969f8..963abede60170 100644 --- a/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx @@ -115,7 +115,7 @@ describe(' - Filter', () => { expect(getColumnValues()).to.deep.equal(['Asics']); }); - it('should apply the filterModel prop correctly on GridApiRef update row data', () => { + it.only('should apply the filterModel prop correctly on GridApiRef update row data', () => { render(); apiRef.current.updateRows([{ id: 1, brand: 'Fila' }]); apiRef.current.updateRows([{ id: 0, brand: 'Patagonia' }]); From a3bd7f1323068ee7f55ebae19b10d327432a4eb1 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 14 Oct 2021 15:13:51 +0200 Subject: [PATCH 216/390] Fix --- docs/pages/api-docs/data-grid/grid-api.md | 1 - .../api-docs/data-grid/grid-filter-api.json | 5 - .../hooks/features/filter/useGridFilter.ts | 270 +++++++++--------- .../src/tests/filtering.DataGridPro.test.tsx | 2 +- 4 files changed, 134 insertions(+), 144 deletions(-) diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index dbb195ab1e206..15422020c3521 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -12,7 +12,6 @@ import { GridApi } from '@mui/x-data-grid-pro'; | Name | Type | Description | | :----------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| applyFilter | (item: GridFilterItem, linkOperator?: GridLinkOperator) => void | Applies a GridFilterItem on all rows. If no `linkOperator` is given, the "and" operator is used. | | applyFilterLinkOperator | (operator: GridLinkOperator) => void | Changes the GridLinkOperator used to connect the filters. | | applyFilters | () => void | Applies all filters on all rows. | | applySorting | () => void | Applies the current sort model to the rows. | diff --git a/docs/pages/api-docs/data-grid/grid-filter-api.json b/docs/pages/api-docs/data-grid/grid-filter-api.json index 0bfb1c8f6c8bd..f77220bd8b14f 100644 --- a/docs/pages/api-docs/data-grid/grid-filter-api.json +++ b/docs/pages/api-docs/data-grid/grid-filter-api.json @@ -2,11 +2,6 @@ "name": "GridFilterApi", "description": "The filter API interface that is available in the grid apiRef.", "properties": [ - { - "name": "applyFilter", - "description": "Applies a GridFilterItem on all rows. If no linkOperator is given, the "and" operator is used.", - "type": "(item: GridFilterItem, linkOperator?: GridLinkOperator) => void" - }, { "name": "applyFilterLinkOperator", "description": "Changes the GridLinkOperator used to connect the filters.", diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index d58c865e68170..b811382025cdb 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -13,13 +13,10 @@ import { useGridLogger } from '../../utils/useGridLogger'; import { filterableGridColumnsIdsSelector } from '../columns/gridColumnsSelector'; import { useGridState } from '../../utils/useGridState'; import { GridPreferencePanelsValue } from '../preferencesPanel/gridPreferencePanelsValue'; -import {sortedGridRowIdsSelector} from '../sorting/gridSortingSelector'; +import { sortedGridRowIdsSelector } from '../sorting/gridSortingSelector'; import { getDefaultGridFilterModel } from './gridFilterState'; import { GridFilterModel } from '../../../models/gridFilterModel'; -import { - visibleSortedGridRowsSelector, - gridFilterModelSelector, -} from './gridFilterSelector'; +import { visibleSortedGridRowsSelector, gridFilterModelSelector } from './gridFilterSelector'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; @@ -76,139 +73,138 @@ export const useGridFilter = ( changeEvent: GridEvents.filterModelChange, }); - const buildAggregatedFilterApplier = React.useCallback( - (filterModel: GridFilterModel): GridFilterItemApplier | null => { - const { items, linkOperator = GridLinkOperator.And } = filterModel; - - const getFilterCallbackFromItem = ( - filterItem: GridFilterItem, - ): GridFilterItemApplier | null => { - if (!filterItem.columnField || !filterItem.operatorValue) { - return null; - } - - const column = apiRef.current.getColumn(filterItem.columnField); - if (!column) { - return null; - } - - const parsedValue = column.valueParser - ? column.valueParser(filterItem.value) - : filterItem.value; - const newFilterItem: GridFilterItem = { ...filterItem, value: parsedValue }; - - const filterOperators = column.filterOperators; - if (!filterOperators?.length) { - throw new Error(`MUI: No filter operators found for column '${column.field}'.`); - } - - const filterOperator = filterOperators.find( - (operator) => operator.value === newFilterItem.operatorValue, - )!; - if (!filterOperator) { - throw new Error( - `MUI: No filter operator found for column '${column.field}' and operator value '${newFilterItem.operatorValue}'.`, - ); - } - - const applyFilterOnRow = filterOperator.getApplyFilterFn(newFilterItem, column)!; - if (typeof applyFilterOnRow !== 'function') { - return null; - } - - return (rowId: GridRowId) => { - const cellParams = apiRef.current.getCellParams(rowId, newFilterItem.columnField!); - - return applyFilterOnRow(cellParams); - }; - }; - - const appliers = items - .map(getFilterCallbackFromItem) - .filter((callback): callback is GridFilterItemApplier => !!callback); - - if (appliers.length === 0) { - return null; - } - - return (rowId: GridRowId) => { - // We return `false` as soon as we have a failing filter - if (linkOperator === GridLinkOperator.And) { - let isPassingFilters = true; - let filterIndex = 0; - - while (isPassingFilters && filterIndex < appliers.length) { - isPassingFilters = appliers[filterIndex](rowId); - filterIndex += 1; - } - - return isPassingFilters; - } - - // We return `true` as soon as we have a passing filter - let isPassingFilters = false; - let filterIndex = 0; - - while (!isPassingFilters && filterIndex < appliers.length) { - isPassingFilters = appliers[filterIndex](rowId); - filterIndex += 1; - } - - return isPassingFilters; - }; + const buildAggregatedFilterApplier = React.useCallback( + (filterModel: GridFilterModel): GridFilterItemApplier | null => { + const { items, linkOperator = GridLinkOperator.And } = filterModel; + + const getFilterCallbackFromItem = ( + filterItem: GridFilterItem, + ): GridFilterItemApplier | null => { + if (!filterItem.columnField || !filterItem.operatorValue) { + return null; + } + + const column = apiRef.current.getColumn(filterItem.columnField); + if (!column) { + return null; + } + + const parsedValue = column.valueParser + ? column.valueParser(filterItem.value) + : filterItem.value; + const newFilterItem: GridFilterItem = { ...filterItem, value: parsedValue }; + + const filterOperators = column.filterOperators; + if (!filterOperators?.length) { + throw new Error(`MUI: No filter operators found for column '${column.field}'.`); + } + + const filterOperator = filterOperators.find( + (operator) => operator.value === newFilterItem.operatorValue, + )!; + if (!filterOperator) { + throw new Error( + `MUI: No filter operator found for column '${column.field}' and operator value '${newFilterItem.operatorValue}'.`, + ); + } + + const applyFilterOnRow = filterOperator.getApplyFilterFn(newFilterItem, column)!; + if (typeof applyFilterOnRow !== 'function') { + return null; + } + + return (rowId: GridRowId) => { + const cellParams = apiRef.current.getCellParams(rowId, newFilterItem.columnField!); + + return applyFilterOnRow(cellParams); + }; + }; + + const appliers = items + .map(getFilterCallbackFromItem) + .filter((callback): callback is GridFilterItemApplier => !!callback); + + if (appliers.length === 0) { + return null; + } + + return (rowId: GridRowId) => { + // We return `false` as soon as we have a failing filter + if (linkOperator === GridLinkOperator.And) { + let isPassingFilters = true; + let filterIndex = 0; + + while (isPassingFilters && filterIndex < appliers.length) { + isPassingFilters = appliers[filterIndex](rowId); + filterIndex += 1; + } + + return isPassingFilters; + } + + // We return `true` as soon as we have a passing filter + let isPassingFilters = false; + let filterIndex = 0; + + while (!isPassingFilters && filterIndex < appliers.length) { + isPassingFilters = appliers[filterIndex](rowId); + filterIndex += 1; + } + + return isPassingFilters; + }; + }, + [apiRef], + ); + + const applyFilters = React.useCallback(() => { + setGridState((state) => { + const filterModel = gridFilterModelSelector(state); + + if (props.filterMode === GridFeatureModeConstant.server) { + return { + ...state, + filter: { + ...state.filter, + visibleRowsLookup: {}, + visibleRows: null, + }, + }; + } + + // No filter to apply + const filteringMethod = buildAggregatedFilterApplier(filterModel); + if (!filteringMethod) { + return { + ...state, + filter: { + ...state.filter, + visibleRowsLookup: {}, + visibleRows: null, + }, + }; + } + + const rowIds = sortedGridRowIdsSelector(apiRef.current.state); + const visibleRowsLookup: Record = {}; + rowIds.forEach((rowId) => { + visibleRowsLookup[rowId] = filteringMethod(rowId); + }); + + return { + ...state, + filter: { + ...state.filter, + visibleRowsLookup, + visibleRows: Object.entries(visibleRowsLookup) + .filter(([, isVisible]) => isVisible) + .map(([id]) => id), }, - [apiRef], - ); - - const applyFilters = React.useCallback(() => { - setGridState((state) => { - const filterModel = gridFilterModelSelector(state); - - if (props.filterMode === GridFeatureModeConstant.server) { - return { - ...state, - filter: { - ...state.filter, - visibleRowsLookup: {}, - visibleRows: null, - }, - }; - } - - // No filter to apply - const filteringMethod = buildAggregatedFilterApplier(filterModel); - if (!filteringMethod) { - return { - ...state, - filter: { - ...state.filter, - visibleRowsLookup: {}, - visibleRows: null, - }, - }; - } - - const rowIds = sortedGridRowIdsSelector(apiRef.current.state); - const visibleRowsLookup: Record = {}; - - rowIds.forEach((rowId) => { - visibleRowsLookup[rowId] = filteringMethod(rowId); - }); - - return { - ...state, - filter: { - ...state.filter, - visibleRowsLookup, - visibleRows: Object.entries(visibleRowsLookup) - .filter(([, isVisible]) => isVisible) - .map(([id]) => id), - }, - }; - }); - - forceUpdate(); - }, [apiRef, setGridState, forceUpdate, props.filterMode, buildAggregatedFilterApplier]); + }; + }); + + forceUpdate(); + }, [apiRef, setGridState, forceUpdate, props.filterMode, buildAggregatedFilterApplier]); const upsertFilter = React.useCallback( (item) => { diff --git a/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx index 963abede60170..8b83482b969f8 100644 --- a/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx @@ -115,7 +115,7 @@ describe(' - Filter', () => { expect(getColumnValues()).to.deep.equal(['Asics']); }); - it.only('should apply the filterModel prop correctly on GridApiRef update row data', () => { + it('should apply the filterModel prop correctly on GridApiRef update row data', () => { render(); apiRef.current.updateRows([{ id: 1, brand: 'Fila' }]); apiRef.current.updateRows([{ id: 0, brand: 'Patagonia' }]); From b63088eac2e22c88b15b9f5d650faa5706cf1ca3 Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Thu, 14 Oct 2021 21:39:33 -0300 Subject: [PATCH 217/390] data-rowindex should work with pagination --- .../_modules_/grid/components/GridVirtualizedContainer.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/components/GridVirtualizedContainer.tsx b/packages/grid/_modules_/grid/components/GridVirtualizedContainer.tsx index cf59a4c11a5cd..54d1caa4a7366 100644 --- a/packages/grid/_modules_/grid/components/GridVirtualizedContainer.tsx +++ b/packages/grid/_modules_/grid/components/GridVirtualizedContainer.tsx @@ -271,6 +271,7 @@ const GridVirtualizedContainer = React.forwardRef, From 5f766e210a269ff07fc1a257e9c727a693e8e5ff Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Thu, 14 Oct 2021 22:06:21 -0300 Subject: [PATCH 218/390] remove gridVirtualizationApi --- .../grid/_modules_/grid/models/api/gridApi.ts | 2 -- .../grid/models/api/gridVirtualizationApi.ts | 26 ------------------- .../grid/_modules_/grid/models/api/index.ts | 1 - 3 files changed, 29 deletions(-) delete mode 100644 packages/grid/_modules_/grid/models/api/gridVirtualizationApi.ts diff --git a/packages/grid/_modules_/grid/models/api/gridApi.ts b/packages/grid/_modules_/grid/models/api/gridApi.ts index dcddcd7c1b456..e6457c874272f 100644 --- a/packages/grid/_modules_/grid/models/api/gridApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridApi.ts @@ -20,7 +20,6 @@ import { GridRowApi } from './gridRowApi'; import { GridSelectionApi } from './gridSelectionApi'; import { GridSortApi } from './gridSortApi'; import { GridStateApi } from './gridStateApi'; -import { GridVirtualizationApi } from './gridVirtualizationApi'; import { GridLoggerApi } from './gridLoggerApi'; import { GridScrollApi } from './gridScrollApi'; import type { GridRowGroupsPreProcessingApi } from '../../hooks/core/rowGroupsPerProcessing'; @@ -41,7 +40,6 @@ export interface GridApi GridColumnApi, GridSelectionApi, GridSortApi, - GridVirtualizationApi, GridPageApi, GridPageSizeApi, GridCsvExportApi, diff --git a/packages/grid/_modules_/grid/models/api/gridVirtualizationApi.ts b/packages/grid/_modules_/grid/models/api/gridVirtualizationApi.ts deleted file mode 100644 index 27b5771f9886b..0000000000000 --- a/packages/grid/_modules_/grid/models/api/gridVirtualizationApi.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { GridContainerProps } from '../gridContainerProps'; -import { GridRenderContextProps } from '../gridRenderContextProps'; - -/** - * The virtualization API interface that is available in the grid [[apiRef]]. - */ -export interface GridVirtualizationApi { - /** - * Get the current containerProps. - * @returns {GridContainerProps | null} The container properties. - * @ignore - do not document. - */ - getContainerPropsState: () => GridContainerProps | null; - /** - * Get the current renderContext. - * @returns {Partial | undefined} The render context. - * @ignore - do not document. - */ - getRenderContextState: () => Partial | undefined; - /** - * Refreshes the viewport cells according to the scroll positions - * @param {boolean} forceRender If `true`, forces a rerender. By default, it is `false`. - * @ignore - do not document. - */ - updateViewport: (forceRender?: boolean) => void; -} diff --git a/packages/grid/_modules_/grid/models/api/index.ts b/packages/grid/_modules_/grid/models/api/index.ts index 246b8208e7182..2e5529f8f1785 100644 --- a/packages/grid/_modules_/grid/models/api/index.ts +++ b/packages/grid/_modules_/grid/models/api/index.ts @@ -12,7 +12,6 @@ export * from './gridRowApi'; export * from './gridSelectionApi'; export * from './gridSortApi'; export * from './gridStateApi'; -export * from './gridVirtualizationApi'; export * from './gridLocaleTextApi'; export * from './gridCsvExportApi'; export * from './gridFocusApi'; From 8ccf8783988c969003cc311496ec10edfc1f3644 Mon Sep 17 00:00:00 2001 From: Matheus Wichman Date: Thu, 14 Oct 2021 22:22:49 -0300 Subject: [PATCH 219/390] rename GridVirtualizedContainer -> GridVirtualScroller --- ...dContainer.tsx => GridVirtualScroller.tsx} | 33 ++++---- .../grid/components/base/GridBody.tsx | 4 +- .../columnHeaders/GridColumnHeaders.tsx | 2 +- packages/grid/_modules_/grid/gridClasses.ts | 2 +- .../features/export/useGridPrintExport.tsx | 2 +- .../src/tests/keyboard.DataGrid.test.tsx | 8 +- .../src/tests/layout.DataGrid.test.tsx | 15 ++-- .../tests/columnHeaders.DataGridPro.test.tsx | 4 +- .../src/tests/events.DataGridPro.test.tsx | 12 +-- .../src/tests/rows.DataGridPro.test.tsx | 82 +++++++++---------- test/e2e/index.test.ts | 4 +- 11 files changed, 80 insertions(+), 88 deletions(-) rename packages/grid/_modules_/grid/components/{GridVirtualizedContainer.tsx => GridVirtualScroller.tsx} (93%) diff --git a/packages/grid/_modules_/grid/components/GridVirtualizedContainer.tsx b/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx similarity index 93% rename from packages/grid/_modules_/grid/components/GridVirtualizedContainer.tsx rename to packages/grid/_modules_/grid/components/GridVirtualScroller.tsx index 54d1caa4a7366..8c3834ff5622b 100644 --- a/packages/grid/_modules_/grid/components/GridVirtualizedContainer.tsx +++ b/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx @@ -31,7 +31,7 @@ const useUtilityClasses = (ownerState: OwnerState) => { const { classes } = ownerState; const slots = { - root: ['virtualizedContainer'], + root: ['virtualScroller'], renderingZone: ['renderingZone'], content: ['content'], }; @@ -39,14 +39,14 @@ const useUtilityClasses = (ownerState: OwnerState) => { return composeClasses(slots, getDataGridUtilityClass, classes); }; -const VirtualizedContainerRoot = styled('div', { +const VirtualScrollerRoot = styled('div', { name: 'MuiDataGrid', - slot: 'VirtualizedContainer', + slot: 'VirtualScroller', })({ overflow: 'auto', }); -const VirtualizedContainerContent = styled('div', { +const VirtualScrollerContent = styled('div', { name: 'MuiDataGrid', slot: 'Content', })({ @@ -54,7 +54,7 @@ const VirtualizedContainerContent = styled('div', { overflow: 'hidden', }); -const VirtualizedContainerRenderingZone = styled('div', { +const VirtualScrollerRenderingZone = styled('div', { name: 'MuiDataGrid', slot: 'RenderingZone', })({ @@ -90,13 +90,13 @@ export interface RenderContext { lastColumnIndex: number; } -interface GridVirtualizedContainerProps extends React.HTMLAttributes { +interface GridVirtualScrollerProps extends React.HTMLAttributes { selectionLookup: Record; disableVirtualization?: boolean; } -const GridVirtualizedContainer = React.forwardRef( - function GridVirtualizedContainer(props, ref) { +const GridVirtualScroller = React.forwardRef( + function GridVirtualScroller(props, ref) { const { className, selectionLookup, disableVirtualization, ...other } = props; const apiRef = useGridApiContext(); const rootProps = useGridRootProps(); @@ -318,23 +318,20 @@ const GridVirtualizedContainer = React.forwardRef - - + + {getRows()} - - - + + + ); }, ); -export { GridVirtualizedContainer }; +export { GridVirtualScroller }; diff --git a/packages/grid/_modules_/grid/components/base/GridBody.tsx b/packages/grid/_modules_/grid/components/base/GridBody.tsx index 2af4e58097ee2..25ac90039edbf 100644 --- a/packages/grid/_modules_/grid/components/base/GridBody.tsx +++ b/packages/grid/_modules_/grid/components/base/GridBody.tsx @@ -9,7 +9,7 @@ import { GridMainContainer } from '../containers/GridMainContainer'; import { GridAutoSizer } from '../GridAutoSizer'; import { GridOverlays } from './GridOverlays'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; -import { GridVirtualizedContainer } from '../GridVirtualizedContainer'; +import { GridVirtualScroller } from '../GridVirtualScroller'; import { useGridSelector } from '../../hooks/utils/useGridSelector'; import { gridSelectionStateSelector } from '../../hooks/features/selection/gridSelectionSelector'; import { gridDensityHeaderHeightSelector } from '../../hooks/features/density/densitySelector'; @@ -101,7 +101,7 @@ function GridBody(props: GridBodyProps) { }; return ( - - Keyboard', () => { } render(); getColumnHeaderCell(0).focus(); - const virtualizedContainer = document.querySelector( - '.MuiDataGrid-virtualizedContainer', - )! as HTMLElement; - expect(virtualizedContainer.scrollLeft).to.equal(0); + const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller')! as HTMLElement; + expect(virtualScroller.scrollLeft).to.equal(0); fireEvent.keyDown(document.activeElement!, { key: 'ArrowRight' }); - expect(virtualizedContainer.scrollLeft).not.to.equal(0); + expect(virtualScroller.scrollLeft).not.to.equal(0); }); it('Shift + Space should select a row', () => { diff --git a/packages/grid/data-grid/src/tests/layout.DataGrid.test.tsx b/packages/grid/data-grid/src/tests/layout.DataGrid.test.tsx index 67f723522b4d9..6b1dfc0ba825b 100644 --- a/packages/grid/data-grid/src/tests/layout.DataGrid.test.tsx +++ b/packages/grid/data-grid/src/tests/layout.DataGrid.test.tsx @@ -605,11 +605,8 @@ describe(' - Layout & Warnings', () => { />
, ); - const virtualizedContainer = document.querySelector( - '.MuiDataGrid-virtualizedContainer', - ); - const scrollBarSize = - virtualizedContainer!.offsetHeight - virtualizedContainer!.clientHeight; + const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller'); + const scrollBarSize = virtualScroller!.offsetHeight - virtualScroller!.clientHeight; expect(scrollBarSize).not.to.equal(0); expect(document.querySelector('.MuiDataGrid-main')!.clientHeight).to.equal( scrollBarSize + headerHeight + rowHeight * baselineProps.rows.length, @@ -628,9 +625,9 @@ describe(' - Layout & Warnings', () => { ); }; render(); - const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer'); + const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller'); // It should not have a horizontal scrollbar - expect(virtualizedContainer!.scrollWidth - virtualizedContainer!.clientWidth).to.equal(0); + expect(virtualScroller!.scrollWidth - virtualScroller!.clientWidth).to.equal(0); }); it('should have a horizontal scrollbar when there are more columns to show and no rows', function test() { @@ -643,8 +640,8 @@ describe(' - Layout & Warnings', () => {
, ); - const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer'); - expect(virtualizedContainer!.scrollWidth - virtualizedContainer!.clientWidth).not.to.equal(0); + const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller'); + expect(virtualScroller!.scrollWidth - virtualScroller!.clientWidth).not.to.equal(0); }); }); diff --git a/packages/grid/x-grid/src/tests/columnHeaders.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/columnHeaders.DataGridPro.test.tsx index 6b41c8a2f3d18..6fb1a54fcb7ca 100644 --- a/packages/grid/x-grid/src/tests/columnHeaders.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/columnHeaders.DataGridPro.test.tsx @@ -51,8 +51,8 @@ describe(' - Column Headers', () => { const menuIconButton = columnCell.querySelector('button[aria-label="Menu"]'); fireEvent.click(menuIconButton); await waitFor(() => expect(screen.queryByRole('menu')).not.to.equal(null)); - const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; - virtualizedContainer.dispatchEvent(new Event('scroll')); + const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller')!; + virtualScroller.dispatchEvent(new Event('scroll')); await waitFor(() => expect(screen.queryByRole('menu')).to.equal(null)); }); diff --git a/packages/grid/x-grid/src/tests/events.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/events.DataGridPro.test.tsx index a9c151c346a87..3d43996685955 100644 --- a/packages/grid/x-grid/src/tests/events.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/events.DataGridPro.test.tsx @@ -323,10 +323,10 @@ describe(' - Events Params', () => { />
, ); - const virtualizedContainer = container.querySelector('.MuiDataGrid-virtualizedContainer'); + const virtualScroller = container.querySelector('.MuiDataGrid-virtualScroller'); // arbitrary number to make sure that the bottom of the grid window is reached. - virtualizedContainer.scrollTop = 12345; - virtualizedContainer.dispatchEvent(new Event('scroll')); + virtualScroller.scrollTop = 12345; + virtualScroller.dispatchEvent(new Event('scroll')); expect(handleRowsScrollEnd.callCount).to.equal(1); }); @@ -343,11 +343,11 @@ describe(' - Events Params', () => { expect(handleViewportRowsChange.lastCall.args[0].firstRowIndex).to.equal(0); expect(handleViewportRowsChange.lastCall.args[0].lastRowIndex).to.equal(6); // should be pageSize + 1 }); - const virtualizedContainer = container.querySelector('.MuiDataGrid-virtualizedContainer'); + const virtualScroller = container.querySelector('.MuiDataGrid-virtualScroller'); // scroll 6 rows so that the renderContext is updated. To be changed to a scroll of 1 row. // TODO: set RowHeight directly. Currently 52 is used because the test fails under Windows. - virtualizedContainer.scrollTop = 52 * 6; - virtualizedContainer.dispatchEvent(new Event('scroll')); + virtualScroller.scrollTop = 52 * 6; + virtualScroller.dispatchEvent(new Event('scroll')); await waitFor(() => { expect(handleViewportRowsChange.lastCall.args[0].firstRowIndex).to.equal(6); // should be 1 expect(handleViewportRowsChange.lastCall.args[0].lastRowIndex).to.equal(12); // should be pageSize + 1 diff --git a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx index b1516e4455657..4a18dd102aa2e 100644 --- a/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/rows.DataGridPro.test.tsx @@ -446,10 +446,10 @@ describe(' - Rows', () => { />, ); - const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; + const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller')!; const renderingZone = document.querySelector('.MuiDataGrid-renderingZone')! as HTMLElement; - virtualizedContainer.scrollTop = 10e6; // scroll to the bottom - virtualizedContainer.dispatchEvent(new Event('scroll')); + virtualScroller.scrollTop = 10e6; // scroll to the bottom + virtualScroller.dispatchEvent(new Event('scroll')); const lastCell = document.querySelector('[role="row"]:last-child [role="cell"]:first-child')!; expect(lastCell).to.have.text('995'); @@ -458,7 +458,7 @@ describe(' - Rows', () => { expect(renderingZone.style.transform).to.equal( `translate3d(0px, ${distanceToFirstRow}px, 0px)`, ); - expect(virtualizedContainer.scrollHeight).to.equal(nbRows * rowHeight); + expect(virtualScroller.scrollHeight).to.equal(nbRows * rowHeight); }); it('Rows should not be virtualized when the grid is in pagination autoPageSize', () => { @@ -483,9 +483,9 @@ describe(' - Rows', () => { render(); const firstRow = getRow(0); expect(firstRow.children).to.have.length(Math.floor(width / columnWidth) + columnBuffer); - const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; - virtualizedContainer.scrollLeft = 301; - virtualizedContainer.dispatchEvent(new Event('scroll')); + const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller')!; + virtualScroller.scrollLeft = 301; + virtualScroller.dispatchEvent(new Event('scroll')); expect(firstRow.children).to.have.length( columnBuffer + Math.floor(width / columnWidth) + columnBuffer, ); @@ -497,12 +497,12 @@ describe(' - Rows', () => { render( , ); - const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; + const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller')!; const renderingZone = document.querySelector('.MuiDataGrid-renderingZone')!; const firstRow = renderingZone.firstChild; expect(firstRow).to.have.attr('data-rowindex', '0'); - virtualizedContainer.scrollTop = rowThreshold * rowHeight; - virtualizedContainer.dispatchEvent(new Event('scroll')); + virtualScroller.scrollTop = rowThreshold * rowHeight; + virtualScroller.dispatchEvent(new Event('scroll')); expect(firstRow).to.have.attr('data-rowindex', '3'); }); @@ -512,13 +512,13 @@ describe(' - Rows', () => { render( , ); - const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; + const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller')!; const renderingZone = document.querySelector('.MuiDataGrid-renderingZone')!; const firstRow = renderingZone.querySelector('[role="row"]:first-child')!; const firstColumn = firstRow.firstChild!; expect(firstColumn).to.have.attr('data-colindex', '0'); - virtualizedContainer.scrollLeft = columnThreshold * columnWidth; - virtualizedContainer.dispatchEvent(new Event('scroll')); + virtualScroller.scrollLeft = columnThreshold * columnWidth; + virtualScroller.dispatchEvent(new Event('scroll')); expect(firstColumn).to.have.attr('data-colindex', '3'); }); @@ -534,15 +534,15 @@ describe(' - Rows', () => { rowsPerPageOptions={[nbRows]} />, ); - const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; - virtualizedContainer.scrollTop = 10e6; // scroll to the bottom - virtualizedContainer.dispatchEvent(new Event('scroll')); + const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller')!; + virtualScroller.scrollTop = 10e6; // scroll to the bottom + virtualScroller.dispatchEvent(new Event('scroll')); const lastCell = document.querySelector( '[role="row"]:last-child [role="cell"]:first-child', )!; expect(lastCell).to.have.text('31'); - expect(virtualizedContainer.scrollHeight).to.equal(nbRows * rowHeight); + expect(virtualScroller.scrollHeight).to.equal(nbRows * rowHeight); }); it('should not virtualized the last page if smaller than viewport', () => { @@ -555,16 +555,16 @@ describe(' - Rows', () => { height={500} />, ); - const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; - virtualizedContainer.scrollTop = 10e6; // scroll to the bottom - virtualizedContainer.dispatchEvent(new Event('scroll')); + const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller')!; + virtualScroller.scrollTop = 10e6; // scroll to the bottom + virtualScroller.dispatchEvent(new Event('scroll')); const lastCell = document.querySelector( '[role="row"]:last-child [role="cell"]:first-child', )!; expect(lastCell).to.have.text('99'); - expect(virtualizedContainer.scrollTop).to.equal(0); - expect(virtualizedContainer.scrollHeight).to.equal(virtualizedContainer.clientHeight); + expect(virtualScroller.scrollTop).to.equal(0); + expect(virtualScroller.scrollHeight).to.equal(virtualScroller.clientHeight); expect(document.querySelector('.MuiDataGrid-renderingZone')!.children).to.have.length(4); }); @@ -572,7 +572,7 @@ describe(' - Rows', () => { render( , ); - const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; + const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller')!; const lastCell = document.querySelector( '[role="row"]:last-child [role="cell"]:first-child', @@ -581,8 +581,8 @@ describe(' - Rows', () => { const rows = document.querySelectorAll('.MuiDataGrid-row[role="row"]')!; expect(rows.length).to.equal(7); - expect(virtualizedContainer.scrollTop).to.equal(0); - expect(virtualizedContainer.scrollHeight).to.equal(virtualizedContainer.clientHeight); + expect(virtualScroller.scrollTop).to.equal(0); + expect(virtualScroller.scrollHeight).to.equal(virtualScroller.clientHeight); expect(document.querySelector('.MuiDataGrid-renderingZone')!.children).to.have.length(7); }); }); @@ -602,9 +602,9 @@ describe(' - Rows', () => { rowHeight={rowHeight} />, ); - const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; + const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller')!; apiRef.current.scrollToIndexes({ rowIndex: 4, colIndex: 0 }); - expect(virtualizedContainer.scrollTop).to.equal(rowHeight - offset); + expect(virtualScroller.scrollTop).to.equal(rowHeight - offset); }); it('should scroll correctly when the given index is partially visible at the top', () => { @@ -621,15 +621,15 @@ describe(' - Rows', () => { rowHeight={rowHeight} />, ); - const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; - virtualizedContainer.scrollTop = offset; - virtualizedContainer.dispatchEvent(new Event('scroll')); // Simulate browser behavior + const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller')!; + virtualScroller.scrollTop = offset; + virtualScroller.dispatchEvent(new Event('scroll')); // Simulate browser behavior apiRef.current.scrollToIndexes({ rowIndex: 2, colIndex: 0 }); - expect(virtualizedContainer.scrollTop).to.equal(offset); + expect(virtualScroller.scrollTop).to.equal(offset); apiRef.current.scrollToIndexes({ rowIndex: 1, colIndex: 0 }); - expect(virtualizedContainer.scrollTop).to.equal(offset); + expect(virtualScroller.scrollTop).to.equal(offset); apiRef.current.scrollToIndexes({ rowIndex: 0, colIndex: 0 }); - expect(virtualizedContainer.scrollTop).to.equal(0); + expect(virtualScroller.scrollTop).to.equal(0); }); it('should scroll correctly when the given colIndex is partially visible at the right', () => { @@ -644,10 +644,10 @@ describe(' - Rows', () => { { field: 'age', width: columnWidth }, ]; render(); - const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; - expect(virtualizedContainer.scrollLeft).to.equal(0); + const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller')!; + expect(virtualScroller.scrollLeft).to.equal(0); apiRef.current.scrollToIndexes({ rowIndex: 0, colIndex: 2 }); - expect(virtualizedContainer.scrollLeft).to.equal(columnWidth * 3 - width); + expect(virtualScroller.scrollLeft).to.equal(columnWidth * 3 - width); }); it('should not scroll when going back', () => { @@ -662,13 +662,13 @@ describe(' - Rows', () => { { field: 'age', width: columnWidth }, ]; render(); - const virtualizedContainer = document.querySelector('.MuiDataGrid-virtualizedContainer')!; - expect(virtualizedContainer.scrollLeft).to.equal(0); + const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller')!; + expect(virtualScroller.scrollLeft).to.equal(0); apiRef.current.scrollToIndexes({ rowIndex: 0, colIndex: 2 }); - virtualizedContainer.dispatchEvent(new Event('scroll')); // Simulate browser behavior - expect(virtualizedContainer.scrollLeft).to.equal(columnWidth * 3 - width); + virtualScroller.dispatchEvent(new Event('scroll')); // Simulate browser behavior + expect(virtualScroller.scrollLeft).to.equal(columnWidth * 3 - width); apiRef.current.scrollToIndexes({ rowIndex: 0, colIndex: 1 }); - expect(virtualizedContainer.scrollLeft).to.equal(columnWidth * 3 - width); + expect(virtualScroller.scrollLeft).to.equal(columnWidth * 3 - width); }); }); }); diff --git a/test/e2e/index.test.ts b/test/e2e/index.test.ts index f723eac3065b0..b9eb902238d9d 100644 --- a/test/e2e/index.test.ts +++ b/test/e2e/index.test.ts @@ -202,13 +202,13 @@ describe('e2e', () => { document.querySelector('[role="row"][data-rowindex="3"] [role="cell"]')!.scrollIntoView(), ); const scrollTop = await page.evaluate( - () => document.querySelector('.MuiDataGrid-virtualizedContainer')!.scrollTop!, + () => document.querySelector('.MuiDataGrid-virtualScroller')!.scrollTop!, ); expect(scrollTop).not.to.equal(0); await page.click('[role="row"][data-rowindex="3"] [role="cell"]'); expect( await page.evaluate( - () => document.querySelector('.MuiDataGrid-virtualizedContainer')!.scrollTop!, + () => document.querySelector('.MuiDataGrid-virtualScroller')!.scrollTop!, ), ).to.equal(scrollTop); }); From 67f6ef95da98857a2d058fe5f5779ef16cd6940f Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 15 Oct 2021 08:49:58 +0200 Subject: [PATCH 220/390] Code review --- .../gridColumnsPreProcessingApi.ts | 2 +- .../features/selection/useGridSelection.ts | 28 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/core/columnsPreProcessing/gridColumnsPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/core/columnsPreProcessing/gridColumnsPreProcessingApi.ts index 89e93838de9f9..47457520d9cf1 100644 --- a/packages/grid/_modules_/grid/hooks/core/columnsPreProcessing/gridColumnsPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/core/columnsPreProcessing/gridColumnsPreProcessingApi.ts @@ -11,7 +11,7 @@ export interface GridColumnsPreProcessingApi { */ UNSTABLE_registerColumnPreProcessing: ( processingName: string, - columnsPreProcessing: GridColumnsPreProcessing | null, + columnsPreProcessing: GridColumnsPreProcessing, ) => void; /** * Apply all the columns pre-processing diff --git a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts index 3898b485b84a0..61a0b63c44085 100644 --- a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts +++ b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts @@ -376,22 +376,22 @@ export const useGridSelection = ( }, [apiRef, isRowSelectable, isStateControlled]); const updateColumnsPreProcessing = React.useCallback(() => { - if (!props.checkboxSelection) { - apiRef.current.UNSTABLE_registerColumnPreProcessing('selection', null); - } else { - const addCheckboxColumn: GridColumnsPreProcessing = (columns) => { - const groupingColumn: GridColDef = { - ...GRID_CHECKBOX_SELECTION_COL_DEF, - cellClassName: classes.cellCheckbox, - headerClassName: classes.columnHeaderCheckbox, - headerName: apiRef.current.getLocaleText('checkboxSelectionHeaderName'), - }; - - return [groupingColumn, ...columns]; + const addCheckboxColumn: GridColumnsPreProcessing = (columns) => { + if (!props.checkboxSelection) { + return columns; + } + + const groupingColumn: GridColDef = { + ...GRID_CHECKBOX_SELECTION_COL_DEF, + cellClassName: classes.cellCheckbox, + headerClassName: classes.columnHeaderCheckbox, + headerName: apiRef.current.getLocaleText('checkboxSelectionHeaderName'), }; - apiRef.current.UNSTABLE_registerColumnPreProcessing('selection', addCheckboxColumn); - } + return [groupingColumn, ...columns]; + }; + + apiRef.current.UNSTABLE_registerColumnPreProcessing('selection', addCheckboxColumn); }, [apiRef, props.checkboxSelection, classes]); useFirstRender(() => { From 91414724ffbde25c1919184eba39271b2d119035 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 15 Oct 2021 09:11:18 +0200 Subject: [PATCH 221/390] Fix test --- .../grid/components/GridVirtualScroller.tsx | 6 +- .../hooks/features/filter/useGridFilter.ts | 113 +++++++++--------- 2 files changed, 60 insertions(+), 59 deletions(-) diff --git a/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx b/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx index 001ba446ab3df..ebbf86f62cce0 100644 --- a/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx +++ b/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx @@ -19,7 +19,10 @@ import { gridSortedVisibleRowEntriesSelector } from '../hooks/features/filter/gr import { gridDensityRowHeightSelector } from '../hooks/features/density/densitySelector'; import { gridEditRowsStateSelector } from '../hooks/features/editRows/gridEditRowsSelector'; import { GridEvents } from '../constants/eventsConstants'; -import { gridSortedVisiblePaginatedRowEntriesSelector } from '../hooks/features/pagination/gridPaginationSelector'; +import { + gridPaginationSelector, + gridSortedVisiblePaginatedRowEntriesSelector, +} from '../hooks/features/pagination/gridPaginationSelector'; import { useGridApiEventHandler } from '../hooks/utils/useGridApiEventHandler'; import { getDataGridUtilityClass } from '../gridClasses'; import { GridComponentProps } from '../GridComponentProps'; @@ -112,6 +115,7 @@ const GridVirtualScroller = React.forwardRef(null); const rootRef = React.useRef(null); const handleRef = useForkRef(ref, rootRef); diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index ce806e100dbc3..f126d1e9ee99b 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -5,7 +5,7 @@ import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridFilterApi } from '../../../models/api/gridFilterApi'; import { GridFeatureModeConstant } from '../../../models/gridFeatureMode'; import { GridFilterItem, GridLinkOperator } from '../../../models/gridFilterItem'; -import { GridRowId, GridRowModel } from '../../../models/gridRows'; +import { GridRowId, GridRowModel, GridRowTreeNodeConfig } from '../../../models/gridRows'; import { isDeepEqual } from '../../../utils/utils'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; @@ -13,15 +13,13 @@ import { useGridLogger } from '../../utils/useGridLogger'; import { filterableGridColumnsIdsSelector } from '../columns/gridColumnsSelector'; import { useGridState } from '../../utils/useGridState'; import { GridPreferencePanelsValue } from '../preferencesPanel/gridPreferencePanelsValue'; -import {gridSortedRowIdsSelector } from '../sorting/gridSortingSelector'; +import { gridSortedRowIdsSelector } from '../sorting/gridSortingSelector'; import { getDefaultGridFilterModel } from './gridFilterState'; import { GridFilterModel } from '../../../models/gridFilterModel'; -import { - gridFilterModelSelector, gridSortedVisibleRowEntriesSelector, -} from './gridFilterSelector'; +import { gridFilterModelSelector, gridSortedVisibleRowEntriesSelector } from './gridFilterSelector'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; -import { gridRowTreeSelector } from '../rows'; +import { gridRowCountSelector, gridRowTreeSelector, gridTopLevelRowCountSelector } from '../rows'; type GridFilterItemApplier = (rowId: GridRowId) => boolean; @@ -85,56 +83,46 @@ export const useGridFilter = ( * - A child passing the filter should not be visible if its parent is not * - A parent passing the filter should not be visible if none of its descendant is passing the filter */ - // const applyTreeFilterConstraints = React.useCallback(() => { - // setGridState((state) => { - // const visibleRowsLookup = { ...gridVisibleRowsLookupSelector(state) }; - // const rowCount = gridRowCountSelector(state); - // const topLevelRowCount = gridTopLevelRowCountSelector(state); - // - // // The tree is flat - // if (rowCount === topLevelRowCount) { - // return state; - // } - // - // const rowTree = gridRowTreeSelector(state); - // - // // No filter applied or no row has children - // if (Object.keys(visibleRowsLookup).length === 0) { - // return state; - // } - // - // const rowsGroupedByDepth: Record = {}; - // Object.values(rowTree).forEach((node) => { - // if (!rowsGroupedByDepth[node.depth]) { - // rowsGroupedByDepth[node.depth] = []; - // } - // - // rowsGroupedByDepth[node.depth].push(node); - // }); - // const depths = Object.keys(rowsGroupedByDepth) - // .map((depth) => Number(depth)) - // .sort(); - // - // depths.forEach((depth) => { - // rowsGroupedByDepth[depth].forEach((node) => { - // if (node.parent != null && visibleRowsLookup[node.parent] === false) { - // visibleRowsLookup[node.id] = false; - // } - // }); - // }); - // - // return { - // ...state, - // filter: { - // ...state.filter, - // visibleRowsLookup, - // visibleRows: Object.entries(visibleRowsLookup) - // .filter(([, isVisible]) => isVisible) - // .map(([id]) => id), - // }, - // }; - // }); - // }, [setGridState]); + const applyTreeFilterConstraints = React.useCallback( + (visibleRowsLookup: Record) => { + const rowCount = gridRowCountSelector(apiRef.current.state); + const topLevelRowCount = gridTopLevelRowCountSelector(apiRef.current.state); + const rowTree = gridRowTreeSelector(apiRef.current.state); + + // The tree is flat + if (rowCount === topLevelRowCount) { + return visibleRowsLookup; + } + + // No filter applied or no row has children + if (Object.keys(visibleRowsLookup).length === 0) { + return visibleRowsLookup; + } + + const rowsGroupedByDepth: Record = {}; + Object.values(rowTree).forEach((node) => { + if (!rowsGroupedByDepth[node.depth]) { + rowsGroupedByDepth[node.depth] = []; + } + + rowsGroupedByDepth[node.depth].push(node); + }); + const depths = Object.keys(rowsGroupedByDepth) + .map((depth) => Number(depth)) + .sort(); + + depths.forEach((depth) => { + rowsGroupedByDepth[depth].forEach((node) => { + if (node.parent != null && visibleRowsLookup[node.parent] === false) { + visibleRowsLookup[node.id] = false; + } + }); + }); + + return visibleRowsLookup; + }, + [apiRef], + ); const buildAggregatedFilterApplier = React.useCallback( (filterModel: GridFilterModel) => { @@ -257,11 +245,13 @@ export const useGridFilter = ( }; } - const visibleRowsLookup: Record = {}; + let visibleRowsLookup: Record = {}; rowIds.forEach((rowId) => { visibleRowsLookup[rowId] = filteringMethod(rowId); }); + visibleRowsLookup = applyTreeFilterConstraints(visibleRowsLookup); + return { ...state, filter: { @@ -275,7 +265,14 @@ export const useGridFilter = ( }); apiRef.current.publishEvent(GridEvents.visibleRowsSet); forceUpdate(); - }, [apiRef, setGridState, forceUpdate, props.filterMode, buildAggregatedFilterApplier]); + }, [ + apiRef, + setGridState, + forceUpdate, + props.filterMode, + buildAggregatedFilterApplier, + applyTreeFilterConstraints, + ]); const upsertFilter = React.useCallback( (item) => { From b15b8b49af8f00493f028899873ab6fcc10b9d21 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 15 Oct 2021 09:18:51 +0200 Subject: [PATCH 222/390] Fix --- packages/grid/_modules_/grid/constants/eventsConstants.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/grid/_modules_/grid/constants/eventsConstants.ts b/packages/grid/_modules_/grid/constants/eventsConstants.ts index e24e3886e3574..a36d106466509 100644 --- a/packages/grid/_modules_/grid/constants/eventsConstants.ts +++ b/packages/grid/_modules_/grid/constants/eventsConstants.ts @@ -264,6 +264,11 @@ export enum GridEvents { * Called with an array of strings corresponding to the field names. */ columnsChange = 'columnsChange', + /** + * Fired when a column pre-processing is changed + * @ignore - do not document + */ + columnsPreProcessingChange = 'columnsPreProcessingChange', /** * Fired when the row grouping function is changed * @ignore - do not document From 96ae4eb684b566d0518284d8d7fd187bef2b3ab0 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 15 Oct 2021 09:27:52 +0200 Subject: [PATCH 223/390] Fix --- .../features/treeData/useGridTreeData.ts | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 6a041947bae92..375232f08029f 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -25,22 +25,22 @@ export const useGridTreeData = ( >, ) => { const updateColumnsPreProcessing = React.useCallback(() => { - if (!props.treeData) { - apiRef.current.UNSTABLE_registerColumnPreProcessing('treeData', null); - } else { - const addGroupingColumn: GridColumnsPreProcessing = (columns) => { - const index = columns[0].type === 'checkboxSelection' ? 1 : 0; - const groupingColumn: GridColDef = { - ...GRID_TREE_DATA_GROUP_COL_DEF, - headerName: apiRef.current.getLocaleText('treeDataGroupingHeaderName'), - ...props.groupingColDef, - }; - - return [...columns.slice(0, index), groupingColumn, ...columns.slice(index)]; + const addGroupingColumn: GridColumnsPreProcessing = (columns) => { + if (!props.treeData) { + return columns; + } + + const index = columns[0].type === 'checkboxSelection' ? 1 : 0; + const groupingColumn: GridColDef = { + ...GRID_TREE_DATA_GROUP_COL_DEF, + headerName: apiRef.current.getLocaleText('treeDataGroupingHeaderName'), + ...props.groupingColDef, }; - apiRef.current.UNSTABLE_registerColumnPreProcessing('treeData', addGroupingColumn); - } + return [...columns.slice(0, index), groupingColumn, ...columns.slice(index)]; + }; + + apiRef.current.UNSTABLE_registerColumnPreProcessing('treeData', addGroupingColumn); }, [apiRef, props.treeData, props.groupingColDef]); const updateRowGrouping = React.useCallback(() => { From e36ffec31926e65581646985dff2785e61075c2b Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 15 Oct 2021 09:38:10 +0200 Subject: [PATCH 224/390] Docs --- .../data-grid/group-pivot/CustomGroupingColumnTreeData.js | 4 ++-- .../data-grid/group-pivot/CustomGroupingColumnTreeData.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js index af934904a7325..c26aa96b42d23 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js @@ -1,13 +1,13 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { DataGridPro, useGridSlotComponentProps } from '@mui/x-data-grid-pro'; +import { DataGridPro, useGridApiContext } from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; const CustomGridTreeDataGroupingCell = (props) => { const { id } = props; - const { apiRef } = useGridSlotComponentProps(); + const apiRef = useGridApiContext(); const node = apiRef.current.UNSTABLE_getRowNode(id); if (!node) { diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx index 967f9a78ec57d..dfbcaf35713d0 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx @@ -3,7 +3,7 @@ import { DataGridPro, DataGridProProps, GridRenderCellParams, - useGridSlotComponentProps, + useGridApiContext, } from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; import Box from '@mui/material/Box'; @@ -11,7 +11,7 @@ import Button from '@mui/material/Button'; const CustomGridTreeDataGroupingCell = (props: GridRenderCellParams) => { const { id } = props; - const { apiRef } = useGridSlotComponentProps(); + const apiRef = useGridApiContext(); const node = apiRef.current.UNSTABLE_getRowNode(id); if (!node) { From 01638c2d82bb090a8ef030e07911078ff33fb0eb Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 15 Oct 2021 11:32:43 +0200 Subject: [PATCH 225/390] Work filters --- docs/pages/playground.tsx | 31 ++---- .../data-grid/group-pivot/group-pivot.md | 5 + .../gridRowGroupsPreProcessingApi.ts | 1 + .../useGridRowGroupsPreProcessing.ts | 1 + .../hooks/features/filter/useGridFilter.ts | 103 +++++++----------- .../hooks/features/rows/gridRowsSelector.ts | 5 + .../grid/hooks/features/rows/gridRowsState.ts | 8 +- .../grid/hooks/features/rows/gridRowsUtils.ts | 29 ++--- .../grid/hooks/features/rows/useGridRows.ts | 5 +- .../features/treeData/useGridTreeData.ts | 26 ++--- .../src/useDemoTreeData.ts | 5 + .../src/tests/treeData.DataGridPro.test.tsx | 90 +++++++++++---- 12 files changed, 160 insertions(+), 149 deletions(-) diff --git a/docs/pages/playground.tsx b/docs/pages/playground.tsx index a476ac0928a8f..9364b6208194b 100644 --- a/docs/pages/playground.tsx +++ b/docs/pages/playground.tsx @@ -1,32 +1,21 @@ import * as React from 'react'; -import { DataGridPro, GridColumns, GridRowsProp } from '@mui/x-data-grid-pro'; +import { DataGridPro } from '@mui/x-data-grid-pro'; +import { useDemoTreeData } from '@mui/x-data-grid-generator'; -const rows: GridRowsProp = [ - { id: 0, path: ['A'] }, - { id: 1, path: ['A', 'A', 'A', 'A'] }, - { id: 2, path: ['A', 'A', 'A', 'B'] }, - { id: 3, path: ['B'] }, - { id: 4, path: ['C', 'A', 'A'] }, -]; +export default function BasicTreeData() { + const { data, loading } = useDemoTreeData({ + rowLength: [10, 5], + randomLength: true, + }); -const columns: GridColumns = [ - { field: 'id', type: 'number' }, - { field: 'path', renderCell: (params) => (params.value as string[]).join('-') }, -]; - -const getTreeDataPath = (row: any) => row.path; - -export default function FillerTreeData() { return (
boolean; @@ -78,56 +78,8 @@ export const useGridFilter = ( changeEvent: GridEvents.filterModelChange, }); - /** - * Apply the following constraints: - * - A child passing the filter should not be visible if its parent is not - * - A parent passing the filter should not be visible if none of its descendant is passing the filter - */ - const applyTreeFilterConstraints = React.useCallback( - (visibleRowsLookup: Record) => { - const rowCount = gridRowCountSelector(apiRef.current.state); - const topLevelRowCount = gridTopLevelRowCountSelector(apiRef.current.state); - const rowTree = gridRowTreeSelector(apiRef.current.state); - - // The tree is flat - if (rowCount === topLevelRowCount) { - return visibleRowsLookup; - } - - // No filter applied or no row has children - if (Object.keys(visibleRowsLookup).length === 0) { - return visibleRowsLookup; - } - - const rowsGroupedByDepth: Record = {}; - Object.values(rowTree).forEach((node) => { - if (!rowsGroupedByDepth[node.depth]) { - rowsGroupedByDepth[node.depth] = []; - } - - rowsGroupedByDepth[node.depth].push(node); - }); - const depths = Object.keys(rowsGroupedByDepth) - .map((depth) => Number(depth)) - .sort(); - - depths.forEach((depth) => { - rowsGroupedByDepth[depth].forEach((node) => { - if (node.parent != null && visibleRowsLookup[node.parent] === false) { - visibleRowsLookup[node.id] = false; - } - }); - }); - - return visibleRowsLookup; - }, - [apiRef], - ); - const buildAggregatedFilterApplier = React.useCallback( (filterModel: GridFilterModel) => { - const rowTree = gridRowTreeSelector(apiRef.current.state); - const { items, linkOperator = GridLinkOperator.And } = filterModel; const getFilterCallbackFromItem = ( @@ -182,12 +134,6 @@ export const useGridFilter = ( } return (rowId: GridRowId) => { - const depth = rowTree[rowId].depth; - - if (depth > 0 && props.disableChildrenFiltering) { - return true; - } - // We return `false` as soon as we have a failing filter if (linkOperator === GridLinkOperator.And) { let isPassingFilters = true; @@ -213,13 +159,13 @@ export const useGridFilter = ( return isPassingFilters; }; }, - [apiRef, props.disableChildrenFiltering], + [apiRef], ); const applyFilters = React.useCallback(() => { setGridState((state) => { const filterModel = gridFilterModelSelector(state); - const rowIds = gridSortedRowIdsSelector(apiRef.current.state); + const rowIds = gridSortedRowIdsSelector(state); if (props.filterMode === GridFeatureModeConstant.server) { return { @@ -245,12 +191,41 @@ export const useGridFilter = ( }; } - let visibleRowsLookup: Record = {}; - rowIds.forEach((rowId) => { - visibleRowsLookup[rowId] = filteringMethod(rowId); - }); + const visibleRowsLookup: Record = {}; + const shouldApplyTreeFiltering = gridRowTreeDepthSelector(state) > 1; + if (shouldApplyTreeFiltering) { + // A node is visible if + // - One of its children is passing the filter + // - He is passing the filter + const rowTree = gridRowTreeSelector(state); + const filterTreeNode = (node: GridRowTreeNodeConfig): boolean => { + let hasChildrenMatchingFilters = false; + node.children?.forEach((childId) => { + const isNodeMatchingFilters = filterTreeNode(rowTree[childId]); + hasChildrenMatchingFilters ||= isNodeMatchingFilters; + }); + + let isNodeMatchingFilters = hasChildrenMatchingFilters; + + if (!isNodeMatchingFilters) { + const shouldSkipFilters = props.disableChildrenFiltering && node.depth > 0; + isNodeMatchingFilters = shouldSkipFilters || filteringMethod(node.id); + } + + visibleRowsLookup[node.id] = isNodeMatchingFilters; + return isNodeMatchingFilters; + }; - visibleRowsLookup = applyTreeFilterConstraints(visibleRowsLookup); + Object.values(rowTree).forEach((node) => { + if (node.depth === 0) { + filterTreeNode(node); + } + }); + } else { + rowIds.forEach((rowId) => { + visibleRowsLookup[rowId] = filteringMethod(rowId); + }); + } return { ...state, @@ -271,7 +246,7 @@ export const useGridFilter = ( forceUpdate, props.filterMode, buildAggregatedFilterApplier, - applyTreeFilterConstraints, + props.disableChildrenFiltering, ]); const upsertFilter = React.useCallback( diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts index 1165f9979edc9..19423e7985f83 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts @@ -20,4 +20,9 @@ export const gridRowsLookupSelector = createSelector( export const gridRowTreeSelector = createSelector(gridRowsStateSelector, (rows) => rows.tree); +export const gridRowTreeDepthSelector = createSelector( + gridRowsStateSelector, + (rows) => rows.treeDepth, +); + export const gridRowIdsSelector = createSelector(gridRowsStateSelector, (rows) => rows.rowIds); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts index fbd2906e936ca..708d036e8540b 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts @@ -1,10 +1,6 @@ -import { GridRowTreeConfig, GridRowId, GridRowsLookup } from '../../../models/gridRows'; - -export interface GridRowsState { - idRowsLookup: GridRowsLookup; - rowIds: GridRowId[]; - tree: GridRowTreeConfig; +import { GridRowGroupingResult } from '../../core/rowGroupsPerProcessing'; +export interface GridRowsState extends GridRowGroupingResult { /** * Amount of rows before applying the filtering * It also count the expanded and collapsed children rows diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts index 3c892a0e3265c..eb4d39437a651 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts @@ -1,27 +1,20 @@ -import type { - GridRowTreeConfig, - GridRowTreeNodeConfig, - GridRowId, - GridRowsLookup, -} from '../../../models'; +import type { GridRowTreeNodeConfig, GridRowId } from '../../../models'; +import { GridRowGroupingResult } from '../../core/rowGroupsPerProcessing'; export type GridNodeNameToIdTree = { [nodeName: string]: { id: GridRowId; children: GridNodeNameToIdTree }; }; interface InsertRowInTreeParams { - tree: GridRowTreeConfig; + result: GridRowGroupingResult; path: string[]; id: GridRowId; defaultGroupingExpansionDepth: number; - idRowsLookup: GridRowsLookup; - rowIds: GridRowId[]; nodeNameToIdTree: GridNodeNameToIdTree; } export const insertRowInTree = (params: InsertRowInTreeParams) => { - const { tree, path, id, defaultGroupingExpansionDepth, idRowsLookup, rowIds, nodeNameToIdTree } = - params; + const { result, path, id, defaultGroupingExpansionDepth, nodeNameToIdTree } = params; let nodeNameToIdSubTree = nodeNameToIdTree; let parentNode: GridRowTreeNodeConfig | null = null; @@ -46,7 +39,7 @@ export const insertRowInTree = (params: InsertRowInTreeParams) => { nodeNameToIdSubTree = nodeNameConfig.children; if (depth < path.length - 1) { - let node = tree[nodeId] ?? null; + let node = result.tree[nodeId] ?? null; if (!node) { node = { id: nodeId, @@ -58,12 +51,12 @@ export const insertRowInTree = (params: InsertRowInTreeParams) => { depth, }; - tree[nodeId] = node; - idRowsLookup[nodeId] = {}; - rowIds.push(nodeId); + result.tree[nodeId] = node; + result.idRowsLookup[nodeId] = {}; + result.rowIds.push(nodeId); } } else { - tree[id] = { + result.tree[id] = { id, expanded: defaultGroupingExpansionDepth > depth, parent: parentNode?.id ?? null, @@ -80,6 +73,8 @@ export const insertRowInTree = (params: InsertRowInTreeParams) => { parentNode.children.push(nodeId); } - parentNode = tree[nodeId]!; + parentNode = result.tree[nodeId]!; } + + result.treeDepth = Math.max(result.treeDepth, path.length); }; diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index c218b2737a55f..405a9a08c4ba6 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -24,10 +24,7 @@ import { } from './gridRowsSelector'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; -export type GridRowsInternalCacheState = Omit< - GridRowsState, - 'tree' | 'totalRowCount' | 'totalTopLevelRowCount' -> & { +export type GridRowsInternalCacheState = Pick & { rowIds: GridRowId[]; /** diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 375232f08029f..da82f7c8957c9 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -1,5 +1,4 @@ import * as React from 'react'; -import { GridRowTreeConfig, GridRowsLookup } from '../../../models/gridRows'; import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridComponentProps } from '../../../GridComponentProps'; import { GridColumnsPreProcessing } from '../../core/columnsPreProcessing'; @@ -9,7 +8,10 @@ import { GridEvents } from '../../../constants'; import { GridCellParams, GridColDef, MuiEvent } from '../../../models'; import { isSpaceKey } from '../../../utils/keyboardUtils'; import { useFirstRender } from '../../utils/useFirstRender'; -import { GridRowGroupingPreProcessing } from '../../core/rowGroupsPerProcessing'; +import { + GridRowGroupingPreProcessing, + GridRowGroupingResult, +} from '../../core/rowGroupsPerProcessing'; import { GridNodeNameToIdTree, insertRowInTree } from '../rows/gridRowsUtils'; /** @@ -60,28 +62,26 @@ export const useGridTreeData = ( })) .sort((a, b) => a.path.length - b.path.length); - const tree: GridRowTreeConfig = {}; - const idRowsLookup: GridRowsLookup = { ...params.idRowsLookup }; - const rowIds = [...params.rowIds]; + const result: GridRowGroupingResult = { + tree: {}, + treeDepth: 1, + idRowsLookup: { ...params.idRowsLookup }, + rowIds: [...params.rowIds], + }; + const nodeNameToIdTree: GridNodeNameToIdTree = {}; rows.forEach((row) => { insertRowInTree({ - tree, + result, path: row.path, id: row.id, defaultGroupingExpansionDepth: props.defaultGroupingExpansionDepth, - idRowsLookup, - rowIds, nodeNameToIdTree, }); }); - return { - tree, - idRowsLookup, - rowIds, - }; + return result; }; return apiRef.current.UNSTABLE_registerRowGroupsBuilder('treeData', groupRows); diff --git a/packages/grid/x-grid-data-generator/src/useDemoTreeData.ts b/packages/grid/x-grid-data-generator/src/useDemoTreeData.ts index fdee192368b9c..99b86a4702976 100644 --- a/packages/grid/x-grid-data-generator/src/useDemoTreeData.ts +++ b/packages/grid/x-grid-data-generator/src/useDemoTreeData.ts @@ -15,6 +15,11 @@ interface GridDemoTreeDataResponse { } const TREE_DATA_COLUMNS: GridColumns = [ + { + field: 'id', + hide: true, + type: 'number', + }, { field: 'name', headerName: 'Name', diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index 2fae64a50f790..ee59e53e0450d 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -233,34 +233,76 @@ describe(' - Tree Data', () => { }); describe('filter', () => { - it('should filter every depth of the tree if props.disableChildrenFiltering = false', () => { - const { setProps } = render(); - fireEvent.click(getCell(0, 0).querySelector('button')); - expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'C']); - setProps({ - filterModel: { items: [{ columnField: 'name', value: 'A', operatorValue: 'endsWith' }] }, - }); - expect(getColumnValues(1)).to.deep.equal(['A', 'A.A']); + const filterBaselineProps = { + autoHeight: isJSDOM, + columns: [ + { + field: 'name', + width: 200, + }, + ], + treeData: true, + getTreeDataPath: (row) => row.name.split('.'), + getRowId: (row) => row.name, + }; + + const TestFilter = (props: Omit) => { + apiRef = useGridApiRef(); + + return ( +
+ +
+ ); + }; + + it('should not show a node if none of its children match the filters and it does not match the filters', () => { + render( + , + ); + + expect(getColumnValues(1)).to.deep.equal([]); }); - it('should only filter to top level rows if props.disableChildrenFiltering = true', () => { - const { setProps } = render(); - fireEvent.click(getCell(0, 0).querySelector('button')); - expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'C']); - setProps({ - filterModel: { items: [{ columnField: 'name', value: 'A', operatorValue: 'endsWith' }] }, - }); - expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B']); + it('should show a node if some of its children match the filters even if it does not match the filters', () => { + render( + , + ); + + expect(getColumnValues(1)).to.deep.equal(['B', 'B.A']); }); - it('should not show children when its parent does not match the filters', () => { - const { setProps } = render(); - fireEvent.click(getCell(0, 0).querySelector('button')); - expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'C']); - setProps({ - filterModel: { items: [{ columnField: 'name', value: 'A.B', operatorValue: 'equals' }] }, - }); - expect(getColumnValues(1)).to.deep.equal([]); + it('should show a node if none of its children match the filters but it does match the filters', () => { + render( + , + ); + + expect(getColumnValues(1)).to.deep.equal(['A']); + }); + + it('should not filter the children if props.disableChildrenFiltering = true', () => { + render( + , + ); + + expect(getColumnValues(1)).to.deep.equal(['B', 'B.A', 'B.B']); }); }); From 2ac0def5ea0ebe8339b8dc6f9c857e983dc5f0c0 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 15 Oct 2021 11:37:45 +0200 Subject: [PATCH 226/390] defaultGroupingExpansionDepth = -1 --- .../grid/hooks/features/rows/gridRowsUtils.ts | 4 +- .../src/tests/treeData.DataGridPro.test.tsx | 40 ++++++++++++++++--- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts index eb4d39437a651..d9c079a17ad10 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts @@ -23,7 +23,7 @@ export const insertRowInTree = (params: InsertRowInTreeParams) => { const nodeName = path[depth]; let nodeId: GridRowId; - const expanded = defaultGroupingExpansionDepth > depth; + const expanded = defaultGroupingExpansionDepth === -1 || defaultGroupingExpansionDepth > depth; let nodeNameConfig = nodeNameToIdSubTree[nodeName]; @@ -58,7 +58,7 @@ export const insertRowInTree = (params: InsertRowInTreeParams) => { } else { result.tree[id] = { id, - expanded: defaultGroupingExpansionDepth > depth, + expanded, parent: parentNode?.id ?? null, label: path[depth], depth, diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index ee59e53e0450d..ec26c3db297f3 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -26,6 +26,7 @@ const rowsWithoutGap: GridRowsProp = [ { name: 'B.A', value: 12 }, { name: 'B.B', value: 8 }, { name: 'B.B.A', value: 8 }, + { name: 'B.B.A.A', value: 8 }, { name: 'C', value: 5 }, ]; @@ -83,6 +84,7 @@ describe(' - Tree Data', () => { 'B.A', 'B.B', 'B.B.A', + 'B.B.A.A', 'C', ]); setProps({ treeData: true }); @@ -98,6 +100,7 @@ describe(' - Tree Data', () => { 'B.A', 'B.B', 'B.B.A', + 'B.B.A.A', 'C', ]); }); @@ -113,10 +116,20 @@ describe(' - Tree Data', () => { 'B.A', 'B.B', 'B.B.A', + 'B.B.A.A', 'C', ]); apiRef.current.updateRows([{ name: 'A.A', _action: 'delete' }]); - expect(getColumnValues(0)).to.deep.equal(['A', 'A.B', 'B', 'B.A', 'B.B', 'B.B.A', 'C']); + expect(getColumnValues(0)).to.deep.equal([ + 'A', + 'A.B', + 'B', + 'B.A', + 'B.B', + 'B.B.A', + 'B.B.A.A', + 'C', + ]); setProps({ treeData: true }); expect(getColumnHeadersTextContent()).to.deep.equal(['Group', 'name', 'value']); expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); @@ -169,6 +182,20 @@ describe(' - Tree Data', () => { ]); }); + it('should expand all rows if defaultGroupingExpansionDepth = -1', () => { + render(); + expect(getColumnValues(1)).to.deep.equal([ + 'A', + 'A.A', + 'A.B', + 'B', + 'B.A', + 'B.B', + 'B.B.A', + 'C', + ]); + }); + it('should not re-apply default expansion on rerender after expansion manually toggled', () => { const { setProps } = render(); expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B', 'B', 'B.A', 'B.B', 'C']); @@ -251,7 +278,12 @@ describe(' - Tree Data', () => { return (
- +
); }; @@ -260,7 +292,6 @@ describe(' - Tree Data', () => { render( , ); @@ -272,7 +303,6 @@ describe(' - Tree Data', () => { render( , ); @@ -284,7 +314,6 @@ describe(' - Tree Data', () => { render( , ); @@ -296,7 +325,6 @@ describe(' - Tree Data', () => { render( , From 058f342c54a58ff279d0fdbbb4055fd4ce7fe8d3 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 15 Oct 2021 11:40:30 +0200 Subject: [PATCH 227/390] Fix --- .../grid/_modules_/grid/hooks/features/filter/useGridFilter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 4a991bfd9dae9..ede85a6669b20 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -79,7 +79,7 @@ export const useGridFilter = ( }); const buildAggregatedFilterApplier = React.useCallback( - (filterModel: GridFilterModel) => { + (filterModel: GridFilterModel): GridFilterItemApplier | null => { const { items, linkOperator = GridLinkOperator.And } = filterModel; const getFilterCallbackFromItem = ( From c7bd8585af5db7aba65cec10aec2490fcc877c02 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 15 Oct 2021 11:42:52 +0200 Subject: [PATCH 228/390] Fix --- .../grid/hooks/features/filter/useGridFilter.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index ede85a6669b20..a07dc0287552b 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -200,11 +200,12 @@ export const useGridFilter = ( const rowTree = gridRowTreeSelector(state); const filterTreeNode = (node: GridRowTreeNodeConfig): boolean => { let hasChildrenMatchingFilters = false; - node.children?.forEach((childId) => { - const isNodeMatchingFilters = filterTreeNode(rowTree[childId]); - hasChildrenMatchingFilters ||= isNodeMatchingFilters; - }); - + if (node.expanded) { + node.children?.forEach((childId) => { + const isNodeMatchingFilters = filterTreeNode(rowTree[childId]); + hasChildrenMatchingFilters ||= isNodeMatchingFilters; + }); + } let isNodeMatchingFilters = hasChildrenMatchingFilters; if (!isNodeMatchingFilters) { From 0b8c13554e68cc60192d00376412a2e7d92b9d1b Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 15 Oct 2021 12:40:20 +0200 Subject: [PATCH 229/390] Work --- docs/pages/playground.tsx | 53 ++++++++++++++++-- .../CustomGroupingColumnTreeData.js | 16 +++++- .../CustomGroupingColumnTreeData.tsx | 11 +++- .../cell/GridTreeDataGroupingCell.tsx | 6 +- .../features/filter/gridFilterSelector.ts | 5 ++ .../hooks/features/filter/gridFilterState.ts | 1 + .../hooks/features/filter/useGridFilter.ts | 56 ++++++++++++------- .../grid/hooks/features/rows/gridRowsUtils.ts | 7 ++- .../grid/_modules_/grid/models/gridRows.ts | 1 + 9 files changed, 125 insertions(+), 31 deletions(-) diff --git a/docs/pages/playground.tsx b/docs/pages/playground.tsx index 9364b6208194b..2071fa5f5fac5 100644 --- a/docs/pages/playground.tsx +++ b/docs/pages/playground.tsx @@ -1,11 +1,54 @@ import * as React from 'react'; -import { DataGridPro } from '@mui/x-data-grid-pro'; +import { + DataGridPro, + DataGridProProps, + GridRenderCellParams, + useGridApiContext, + useGridSelector, + gridVisibleDescendantCountLookupSelector, +} from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; -export default function BasicTreeData() { +const CustomGridTreeDataGroupingCell = (props: GridRenderCellParams) => { + const { id } = props; + const apiRef = useGridApiContext(); + const descendantCountLookup = useGridSelector(apiRef, gridVisibleDescendantCountLookupSelector); + const node = apiRef.current.UNSTABLE_getRowNode(id); + const descendantCount = descendantCountLookup[id]; + + if (!node) { + throw new Error(`MUI: No row with id #${id} found`); + } + + return ( + +
+ {descendantCount > 0 ? ( + + ) : ( + + )} +
+
+ ); +}; + +const groupingColDef: DataGridProProps['groupingColDef'] = { + renderCell: (params) => , +}; + +export default function CustomGroupingColumnTreeData() { const { data, loading } = useDemoTreeData({ - rowLength: [10, 5], - randomLength: true, + rowLength: [2, 2, 2], + randomLength: false, }); return ( @@ -14,7 +57,7 @@ export default function BasicTreeData() { loading={loading} treeData disableSelectionOnClick - filterModel={{ items: [{ columnField: 'id', operatorValue: '=', value: 6 }] }} + groupingColDef={groupingColDef} {...data} />
diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js index c26aa96b42d23..3090d5afde733 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js @@ -1,6 +1,11 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { DataGridPro, useGridApiContext } from '@mui/x-data-grid-pro'; +import { + DataGridPro, + useGridApiContext, + useGridSelector, + gridVisibleDescendantCountLookupSelector, +} from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; @@ -8,7 +13,12 @@ import Button from '@mui/material/Button'; const CustomGridTreeDataGroupingCell = (props) => { const { id } = props; const apiRef = useGridApiContext(); + const descendantCountLookup = useGridSelector( + apiRef, + gridVisibleDescendantCountLookupSelector, + ); const node = apiRef.current.UNSTABLE_getRowNode(id); + const descendantCount = descendantCountLookup[id]; if (!node) { throw new Error(`MUI: No row with id #${id} found`); @@ -17,7 +27,7 @@ const CustomGridTreeDataGroupingCell = (props) => { return (
- {node.children?.length ? ( + {descendantCount > 0 ? ( ) : ( diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx index dfbcaf35713d0..0e2542fc8f997 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx @@ -4,6 +4,8 @@ import { DataGridProProps, GridRenderCellParams, useGridApiContext, + useGridSelector, + gridVisibleDescendantCountLookupSelector, } from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; import Box from '@mui/material/Box'; @@ -12,7 +14,12 @@ import Button from '@mui/material/Button'; const CustomGridTreeDataGroupingCell = (props: GridRenderCellParams) => { const { id } = props; const apiRef = useGridApiContext(); + const descendantCountLookup = useGridSelector( + apiRef, + gridVisibleDescendantCountLookupSelector, + ); const node = apiRef.current.UNSTABLE_getRowNode(id); + const descendantCount = descendantCountLookup[id]; if (!node) { throw new Error(`MUI: No row with id #${id} found`); @@ -21,7 +28,7 @@ const CustomGridTreeDataGroupingCell = (props: GridRenderCellParams) => { return (
- {node.children?.length ? ( + {descendantCount > 0 ? ( ) : ( diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index 21dd442400e87..fda15ed9b4b8d 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -6,6 +6,8 @@ import Box from '@mui/material/Box'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { useGridApiContext } from '../../hooks/utils/useGridApiContext'; import { GridRenderCellParams } from '../../models/params/gridCellParams'; +import { gridVisibleDescendantCountLookupSelector } from '../../hooks/features/filter/gridFilterSelector'; +import { useGridSelector } from '../../hooks/utils/useGridSelector'; const useStyles = makeStyles({ root: { @@ -25,8 +27,10 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { const rootProps = useGridRootProps(); const apiRef = useGridApiContext(); + const descendantCountLookup = useGridSelector(apiRef, gridVisibleDescendantCountLookupSelector); const classes = useStyles(); const node = apiRef.current.UNSTABLE_getRowNode(id); + const descendantCount = descendantCountLookup[id]; const Icon = node?.expanded ? rootProps.components.TreeDataCollapseIcon @@ -39,7 +43,7 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { return (
- {!!node.children?.length && ( + {descendantCount > 0 && ( apiRef.current.UNSTABLE_setRowExpansion(id, !node?.expanded)} diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index 946280af6be4b..6d8bbb0ead1bb 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -22,6 +22,11 @@ export const gridVisibleRowsLookupSelector = createSelector( (filterState) => filterState.visibleRowsLookup, ); +export const gridVisibleDescendantCountLookupSelector = createSelector( + gridFilterStateSelector, + (filterState) => filterState.visibleDescendantsCountLookup, +); + export const gridSortedVisibleRowEntriesSelector = createSelector( gridVisibleRowsLookupSelector, gridSortedRowEntriesSelector, diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts index 9bdb7f24e3368..e78c2886854d6 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts @@ -11,4 +11,5 @@ export interface GridFilterState { filterModel: GridFilterModel; visibleRowsLookup: Record; visibleRows: GridRowId[]; + visibleDescendantsCountLookup: Record; } diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index a07dc0287552b..fbfe542810679 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -18,8 +18,7 @@ import { GridFilterModel } from '../../../models/gridFilterModel'; import { gridFilterModelSelector, gridSortedVisibleRowEntriesSelector } from './gridFilterSelector'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; -import { gridRowTreeDepthSelector, gridRowTreeSelector } from '../rows'; -import { gridSortedRowIdsSelector } from '../sorting'; +import { gridRowIdsSelector, gridRowTreeDepthSelector, gridRowTreeSelector } from '../rows'; type GridFilterItemApplier = (rowId: GridRowId) => boolean; @@ -64,6 +63,7 @@ export const useGridFilter = ( filterModel: props.filterModel ?? getDefaultGridFilterModel(), visibleRowsLookup: {}, visibleRows: [], + visibleDescendantsCountLookup: {}, }, }; }); @@ -165,7 +165,9 @@ export const useGridFilter = ( const applyFilters = React.useCallback(() => { setGridState((state) => { const filterModel = gridFilterModelSelector(state); - const rowIds = gridSortedRowIdsSelector(state); + const rowIds = gridRowIdsSelector(state); + const rowTree = gridRowTreeSelector(state); + const shouldApplyTreeFiltering = gridRowTreeDepthSelector(state) > 1; if (props.filterMode === GridFeatureModeConstant.server) { return { @@ -173,6 +175,7 @@ export const useGridFilter = ( filter: { ...state.filter, visibleRowsLookup: {}, + visibleDescendantsCountLookup: {}, visibleRows: rowIds, }, }; @@ -181,40 +184,54 @@ export const useGridFilter = ( // No filter to apply const filteringMethod = buildAggregatedFilterApplier(filterModel); if (!filteringMethod) { + let visibleDescendantsCountLookup: Record = {}; + if (shouldApplyTreeFiltering) { + visibleDescendantsCountLookup = Object.fromEntries( + rowIds.map((rowId) => [rowId, rowTree[rowId].descendantCount ?? 0]), + ); + } + return { ...state, filter: { ...state.filter, visibleRowsLookup: {}, + visibleDescendantsCountLookup, visibleRows: rowIds, }, }; } const visibleRowsLookup: Record = {}; - const shouldApplyTreeFiltering = gridRowTreeDepthSelector(state) > 1; + const visibleDescendantsCountLookup: Record = {}; if (shouldApplyTreeFiltering) { // A node is visible if // - One of its children is passing the filter // - He is passing the filter - const rowTree = gridRowTreeSelector(state); const filterTreeNode = (node: GridRowTreeNodeConfig): boolean => { - let hasChildrenMatchingFilters = false; - if (node.expanded) { - node.children?.forEach((childId) => { - const isNodeMatchingFilters = filterTreeNode(rowTree[childId]); - hasChildrenMatchingFilters ||= isNodeMatchingFilters; - }); - } - let isNodeMatchingFilters = hasChildrenMatchingFilters; - - if (!isNodeMatchingFilters) { - const shouldSkipFilters = props.disableChildrenFiltering && node.depth > 0; - isNodeMatchingFilters = shouldSkipFilters || filteringMethod(node.id); + let visibleDescendantCount = 0; + node.children?.forEach((childId) => { + const childNode = rowTree[childId]; + const isNodeVisible = filterTreeNode(childNode); + + let count = visibleDescendantsCountLookup[childId] ?? 0; + if (isNodeVisible && !childNode.isAutoGenerated) { + count += 1; + } + + visibleDescendantCount += count; + }); + + const shouldSkipFilters = props.disableChildrenFiltering && node.depth > 0; + const isMatchingFilters = shouldSkipFilters || filteringMethod(node.id); + const shouldBeVisible = visibleDescendantCount > 0 || isMatchingFilters; + visibleRowsLookup[node.id] = shouldBeVisible; + + if (visibleDescendantCount > 0) { + visibleDescendantsCountLookup[node.id] = visibleDescendantCount; } - visibleRowsLookup[node.id] = isNodeMatchingFilters; - return isNodeMatchingFilters; + return shouldBeVisible; }; Object.values(rowTree).forEach((node) => { @@ -233,6 +250,7 @@ export const useGridFilter = ( filter: { ...state.filter, visibleRowsLookup, + visibleDescendantsCountLookup, visibleRows: Object.entries(visibleRowsLookup) .filter(([, isVisible]) => isVisible) .map(([id]) => id), diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts index d9c079a17ad10..78ea084b5c550 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts @@ -55,6 +55,8 @@ export const insertRowInTree = (params: InsertRowInTreeParams) => { result.idRowsLookup[nodeId] = {}; result.rowIds.push(nodeId); } + + node.descendantCount = (node.descendantCount ?? 0) + 1; } else { result.tree[id] = { id, @@ -70,7 +72,10 @@ export const insertRowInTree = (params: InsertRowInTreeParams) => { parentNode.children = []; } - parentNode.children.push(nodeId); + // TODO: Avoid linear complexity here + if (!parentNode.children.includes(nodeId)) { + parentNode.children.push(nodeId); + } } parentNode = result.tree[nodeId]!; diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index b551a2540227b..5d6d430ac67c7 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -15,6 +15,7 @@ export interface GridRowModelUpdate extends GridRowData { export interface GridRowTreeNodeConfig { id: GridRowId; children?: GridRowId[]; + descendantCount?: number; parent: GridRowId | null; expanded?: boolean; depth: number; From 8b630806ec3912ea9ef1f38d59b79082cf67c49a Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 15 Oct 2021 12:44:13 +0200 Subject: [PATCH 230/390] Fix --- .../grid/hooks/features/filter/useGridFilter.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index fbfe542810679..f4329a38ad723 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -214,12 +214,13 @@ export const useGridFilter = ( const childNode = rowTree[childId]; const isNodeVisible = filterTreeNode(childNode); - let count = visibleDescendantsCountLookup[childId] ?? 0; - if (isNodeVisible && !childNode.isAutoGenerated) { - count += 1; + let childrenVisibleNodeCount = visibleDescendantsCountLookup[childId] ?? 0; + // TODO: For column grouping, we do not want to count the intermediate depth nodes in the visible descendant count + if (isNodeVisible) { + childrenVisibleNodeCount += 1; } - visibleDescendantCount += count; + visibleDescendantCount += childrenVisibleNodeCount; }); const shouldSkipFilters = props.disableChildrenFiltering && node.depth > 0; From bd10675892162a33962abd862d89cb2c1815e545 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 15 Oct 2021 13:22:57 +0200 Subject: [PATCH 231/390] Rework tree builder --- .../CustomGroupingColumnTreeData.js | 1 + .../gridRowGroupsPreProcessingApi.ts | 8 +- .../grid/hooks/features/rows/gridRowsUtils.ts | 127 ++++++++++-------- .../features/treeData/useGridTreeData.ts | 30 +---- 4 files changed, 81 insertions(+), 85 deletions(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js index 3090d5afde733..5b2f1c00f7269 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js @@ -17,6 +17,7 @@ const CustomGridTreeDataGroupingCell = (props) => { apiRef, gridVisibleDescendantCountLookupSelector, ); + const node = apiRef.current.UNSTABLE_getRowNode(id); const descendantCount = descendantCountLookup[id]; diff --git a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts index 430e811ac9c4f..bbab11510d294 100644 --- a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts @@ -1,6 +1,6 @@ import { GridRowTreeConfig, GridRowId, GridRowsLookup } from '../../../models/gridRows'; -export type RowGroupParams = { +export type GridRowGroupParams = { rowIds: GridRowId[]; idRowsLookup: GridRowsLookup; }; @@ -12,7 +12,9 @@ export interface GridRowGroupingResult { idRowsLookup: GridRowsLookup; } -export type GridRowGroupingPreProcessing = (params: RowGroupParams) => GridRowGroupingResult | null; +export type GridRowGroupingPreProcessing = ( + params: GridRowGroupParams, +) => GridRowGroupingResult | null; export interface GridRowGroupsPreProcessingApi { /** @@ -33,5 +35,5 @@ export interface GridRowGroupsPreProcessingApi { * @returns {GridRowGroupingResult} The grouped rows * @ignore - do not document */ - UNSTABLE_groupRows: (params: RowGroupParams) => GridRowGroupingResult; + UNSTABLE_groupRows: (params: GridRowGroupParams) => GridRowGroupingResult; } diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts index 78ea084b5c550..a6aaca56697c4 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts @@ -1,85 +1,96 @@ import type { GridRowTreeNodeConfig, GridRowId } from '../../../models'; -import { GridRowGroupingResult } from '../../core/rowGroupsPerProcessing'; +import { GridRowGroupingResult, GridRowGroupParams } from '../../core/rowGroupsPerProcessing'; -export type GridNodeNameToIdTree = { +type GridNodeNameToIdTree = { [nodeName: string]: { id: GridRowId; children: GridNodeNameToIdTree }; }; -interface InsertRowInTreeParams { - result: GridRowGroupingResult; - path: string[]; - id: GridRowId; +interface GenerateRowTreeParams extends GridRowGroupParams { + rows: { id: GridRowId; path: string[] }[]; defaultGroupingExpansionDepth: number; - nodeNameToIdTree: GridNodeNameToIdTree; } -export const insertRowInTree = (params: InsertRowInTreeParams) => { - const { result, path, id, defaultGroupingExpansionDepth, nodeNameToIdTree } = params; +export const generateRowTree = (params: GenerateRowTreeParams) => { + const result: GridRowGroupingResult = { + tree: {}, + treeDepth: 1, + idRowsLookup: { ...params.idRowsLookup }, + rowIds: [...params.rowIds], + }; - let nodeNameToIdSubTree = nodeNameToIdTree; - let parentNode: GridRowTreeNodeConfig | null = null; + const nodeNameToIdTree: GridNodeNameToIdTree = {}; - for (let depth = 0; depth < path.length; depth += 1) { - const nodeName = path[depth]; - let nodeId: GridRowId; + params.rows.forEach((row) => { + let nodeNameToIdSubTree = nodeNameToIdTree; + let parentNode: GridRowTreeNodeConfig | null = null; - const expanded = defaultGroupingExpansionDepth === -1 || defaultGroupingExpansionDepth > depth; + for (let depth = 0; depth < row.path.length; depth += 1) { + const nodeName = row.path[depth]; + let nodeId: GridRowId; - let nodeNameConfig = nodeNameToIdSubTree[nodeName]; + const expanded = + params.defaultGroupingExpansionDepth === -1 || params.defaultGroupingExpansionDepth > depth; - if (!nodeNameConfig) { - nodeId = - depth === path.length - 1 ? id : `auto-generated-row-${path.slice(0, depth + 1).join('-')}`; + let nodeNameConfig = nodeNameToIdSubTree[nodeName]; - nodeNameConfig = { id: nodeId, children: {} }; - nodeNameToIdSubTree[nodeName] = nodeNameConfig; - } else { - nodeId = nodeNameConfig.id; - } - nodeNameToIdSubTree = nodeNameConfig.children; - - if (depth < path.length - 1) { - let node = result.tree[nodeId] ?? null; - if (!node) { - node = { - id: nodeId, - isAutoGenerated: true, + if (!nodeNameConfig) { + nodeId = + depth === row.path.length - 1 + ? row.id + : `auto-generated-row-${row.path.slice(0, depth + 1).join('-')}`; + + nodeNameConfig = { id: nodeId, children: {} }; + nodeNameToIdSubTree[nodeName] = nodeNameConfig; + } else { + nodeId = nodeNameConfig.id; + } + nodeNameToIdSubTree = nodeNameConfig.children; + + if (depth < row.path.length - 1) { + let node = result.tree[nodeId] ?? null; + if (!node) { + node = { + id: nodeId, + isAutoGenerated: true, + expanded, + children: [], + parent: parentNode?.id ?? null, + label: row.path[depth], + depth, + }; + + result.tree[nodeId] = node; + result.idRowsLookup[nodeId] = {}; + result.rowIds.push(nodeId); + } + + node.descendantCount = (node.descendantCount ?? 0) + 1; + } else { + result.tree[row.id] = { + id, expanded, - children: [], parent: parentNode?.id ?? null, - label: path[depth], + label: row.path[depth], depth, }; - - result.tree[nodeId] = node; - result.idRowsLookup[nodeId] = {}; - result.rowIds.push(nodeId); } - node.descendantCount = (node.descendantCount ?? 0) + 1; - } else { - result.tree[id] = { - id, - expanded, - parent: parentNode?.id ?? null, - label: path[depth], - depth, - }; - } + if (parentNode != null) { + if (!parentNode.children) { + parentNode.children = []; + } - if (parentNode != null) { - if (!parentNode.children) { - parentNode.children = []; + // TODO: Avoid linear complexity here + if (!parentNode.children.includes(nodeId)) { + parentNode.children.push(nodeId); + } } - // TODO: Avoid linear complexity here - if (!parentNode.children.includes(nodeId)) { - parentNode.children.push(nodeId); - } + parentNode = result.tree[nodeId]!; } - parentNode = result.tree[nodeId]!; - } + result.treeDepth = Math.max(result.treeDepth, row.path.length); + }); - result.treeDepth = Math.max(result.treeDepth, path.length); + return result; }; diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index da82f7c8957c9..d53ebb0dbbaa7 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -8,11 +8,8 @@ import { GridEvents } from '../../../constants'; import { GridCellParams, GridColDef, MuiEvent } from '../../../models'; import { isSpaceKey } from '../../../utils/keyboardUtils'; import { useFirstRender } from '../../utils/useFirstRender'; -import { - GridRowGroupingPreProcessing, - GridRowGroupingResult, -} from '../../core/rowGroupsPerProcessing'; -import { GridNodeNameToIdTree, insertRowInTree } from '../rows/gridRowsUtils'; +import { GridRowGroupingPreProcessing } from '../../core/rowGroupsPerProcessing'; +import { generateRowTree } from '../rows/gridRowsUtils'; /** * Only available in DataGridPro @@ -62,26 +59,11 @@ export const useGridTreeData = ( })) .sort((a, b) => a.path.length - b.path.length); - const result: GridRowGroupingResult = { - tree: {}, - treeDepth: 1, - idRowsLookup: { ...params.idRowsLookup }, - rowIds: [...params.rowIds], - }; - - const nodeNameToIdTree: GridNodeNameToIdTree = {}; - - rows.forEach((row) => { - insertRowInTree({ - result, - path: row.path, - id: row.id, - defaultGroupingExpansionDepth: props.defaultGroupingExpansionDepth, - nodeNameToIdTree, - }); + return generateRowTree({ + rows, + ...params, + defaultGroupingExpansionDepth: props.defaultGroupingExpansionDepth, }); - - return result; }; return apiRef.current.UNSTABLE_registerRowGroupsBuilder('treeData', groupRows); From 2238d0c7d08d063683483f6e2c4f3a121c673366 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 15 Oct 2021 13:23:44 +0200 Subject: [PATCH 232/390] Fix --- .../grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts index a6aaca56697c4..7cbbe3a3d5e8b 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts @@ -67,7 +67,7 @@ export const generateRowTree = (params: GenerateRowTreeParams) => { node.descendantCount = (node.descendantCount ?? 0) + 1; } else { result.tree[row.id] = { - id, + id: row.id, expanded, parent: parentNode?.id ?? null, label: row.path[depth], From 4318294e29e31dcdbb48aff76162d5fa1d3b60f4 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 15 Oct 2021 13:37:59 +0200 Subject: [PATCH 233/390] Work --- .../pages/components/data-grid/group-pivot/group-pivot.md | 1 + .../grid/components/cell/GridTreeDataGroupingCell.tsx | 5 ++++- packages/grid/_modules_/grid/components/icons/index.tsx | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md index 52ac466bc8a6e..903b772f6a742 100644 --- a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md +++ b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md @@ -62,6 +62,7 @@ Use the `groupingColDef` prop to customize the rendering of the grouping column. ### Group expansion Use the `defaultGroupingExpansionDepth` prop to expand all the groups up to a given depth when loading the data. +If you want to expand the whole tree, set `defaultGroupingExpansionDepth = -1` {{"demo": "pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.js", "bg": "inline", "defaultCodeOpen": false}} diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index fda15ed9b4b8d..132b4bd34bdad 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -58,7 +58,10 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { )}
- {node.label} + + {node.label} + {descendantCount > 0 ? ` (${descendantCount})` : ''} +
); }; diff --git a/packages/grid/_modules_/grid/components/icons/index.tsx b/packages/grid/_modules_/grid/components/icons/index.tsx index 568a9ad28e985..395644208d92c 100644 --- a/packages/grid/_modules_/grid/components/icons/index.tsx +++ b/packages/grid/_modules_/grid/components/icons/index.tsx @@ -12,8 +12,8 @@ export const GridArrowDownwardIcon = createSvgIcon( ); export const GridExpandMoreIcon = createSvgIcon( - , - 'ExpandMore', + , + 'KeyboardArrowRight', ); export const GridExpandLessIcon = createSvgIcon( From c6582deb80cc134482393d344d49ffc623e45b3c Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 15 Oct 2021 13:45:15 +0200 Subject: [PATCH 234/390] Fix --- docs/pages/playground.tsx | 59 ++----------------- .../features/treeData/useGridTreeData.ts | 2 + 2 files changed, 7 insertions(+), 54 deletions(-) diff --git a/docs/pages/playground.tsx b/docs/pages/playground.tsx index 2071fa5f5fac5..81761962ea92c 100644 --- a/docs/pages/playground.tsx +++ b/docs/pages/playground.tsx @@ -1,65 +1,16 @@ import * as React from 'react'; -import { - DataGridPro, - DataGridProProps, - GridRenderCellParams, - useGridApiContext, - useGridSelector, - gridVisibleDescendantCountLookupSelector, -} from '@mui/x-data-grid-pro'; +import { DataGridPro } from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; -import Box from '@mui/material/Box'; -import Button from '@mui/material/Button'; -const CustomGridTreeDataGroupingCell = (props: GridRenderCellParams) => { - const { id } = props; - const apiRef = useGridApiContext(); - const descendantCountLookup = useGridSelector(apiRef, gridVisibleDescendantCountLookupSelector); - const node = apiRef.current.UNSTABLE_getRowNode(id); - const descendantCount = descendantCountLookup[id]; - - if (!node) { - throw new Error(`MUI: No row with id #${id} found`); - } - - return ( - -
- {descendantCount > 0 ? ( - - ) : ( - - )} -
-
- ); -}; - -const groupingColDef: DataGridProProps['groupingColDef'] = { - renderCell: (params) => , -}; - -export default function CustomGroupingColumnTreeData() { +export default function BasicTreeData() { const { data, loading } = useDemoTreeData({ - rowLength: [2, 2, 2], - randomLength: false, + rowLength: [10, 5, 3], + randomLength: true, }); return (
- +
); } diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index d53ebb0dbbaa7..d985d6cd893ca 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -97,6 +97,8 @@ export const useGridTreeData = ( const cellParams = apiRef.current.getCellParams(params.id, params.field); if (cellParams.field === '__tree_data_group__' && isSpaceKey(event.key)) { event.stopPropagation(); + event.preventDefault(); + event.defaultMuiPrevented = true; apiRef.current.UNSTABLE_setRowExpansion( params.id, !apiRef.current.UNSTABLE_getRowNode(params.id)?.expanded, From 938cfa2764c9756f83a6c4eb1324c4a532e8cbcb Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 15 Oct 2021 13:54:06 +0200 Subject: [PATCH 235/390] Fix --- docs/pages/playground.tsx | 19 +++++++++++++++---- .../grid/components/GridVirtualScroller.tsx | 6 +++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/docs/pages/playground.tsx b/docs/pages/playground.tsx index 81761962ea92c..adedc3575f88c 100644 --- a/docs/pages/playground.tsx +++ b/docs/pages/playground.tsx @@ -2,15 +2,26 @@ import * as React from 'react'; import { DataGridPro } from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; +const groupingColDef = { minWidth: 400 }; + export default function BasicTreeData() { const { data, loading } = useDemoTreeData({ - rowLength: [10, 5, 3], - randomLength: true, + rowLength: [10, 10], + randomLength: false, }); return ( -
- +
+
); } diff --git a/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx b/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx index ebbf86f62cce0..2077b179ef84f 100644 --- a/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx +++ b/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx @@ -20,7 +20,7 @@ import { gridDensityRowHeightSelector } from '../hooks/features/density/densityS import { gridEditRowsStateSelector } from '../hooks/features/editRows/gridEditRowsSelector'; import { GridEvents } from '../constants/eventsConstants'; import { - gridPaginationSelector, + gridPaginationRowRangeSelector, gridSortedVisiblePaginatedRowEntriesSelector, } from '../hooks/features/pagination/gridPaginationSelector'; import { useGridApiEventHandler } from '../hooks/utils/useGridApiEventHandler'; @@ -106,6 +106,7 @@ const GridVirtualScroller = React.forwardRef(null); const rootRef = React.useRef(null); const handleRef = useForkRef(ref, rootRef); @@ -283,7 +283,7 @@ const GridVirtualScroller = React.forwardRef Date: Fri, 15 Oct 2021 13:56:44 +0200 Subject: [PATCH 236/390] Fix tests --- packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index ec26c3db297f3..64906dee45fb4 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -241,10 +241,8 @@ describe(' - Tree Data', () => { it('should add auto generated rows if some parents do not exist', () => { render(); expect(getColumnValues(1)).to.deep.equal(['A', '']); - expect(getColumnValues(0)).to.deep.equal(['A', 'B']); fireEvent.click(getCell(1, 0).querySelector('button')); expect(getColumnValues(1)).to.deep.equal(['A', '', 'B.A', 'B.B']); - expect(getColumnValues(0)).to.deep.equal(['A', 'B', 'A', 'B']); }); }); From e2df67911f183e943d9be9d3e64b045c2248cf75 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 15 Oct 2021 14:06:25 +0200 Subject: [PATCH 237/390] Improve perf of buildRowTree --- .../grid/hooks/features/rows/gridRowsUtils.ts | 61 ++++++++++++------- .../features/treeData/useGridTreeData.ts | 4 +- 2 files changed, 40 insertions(+), 25 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts index 7cbbe3a3d5e8b..cc51352e24cfa 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts @@ -1,4 +1,4 @@ -import type { GridRowTreeNodeConfig, GridRowId } from '../../../models'; +import type { GridRowTreeNodeConfig, GridRowId, GridRowTreeConfig } from '../../../models'; import { GridRowGroupingResult, GridRowGroupParams } from '../../core/rowGroupsPerProcessing'; type GridNodeNameToIdTree = { @@ -10,19 +10,23 @@ interface GenerateRowTreeParams extends GridRowGroupParams { defaultGroupingExpansionDepth: number; } -export const generateRowTree = (params: GenerateRowTreeParams) => { - const result: GridRowGroupingResult = { - tree: {}, - treeDepth: 1, - idRowsLookup: { ...params.idRowsLookup }, - rowIds: [...params.rowIds], - }; +interface TempRowTreeNode extends Omit { + children?: Record; +} + +type TempRowTree = Record; + +export const buildRowTree = (params: GenerateRowTreeParams): GridRowGroupingResult => { + const tempTree: TempRowTree = {}; + let treeDepth = 1; + const rowIds = [...params.rowIds]; + const idRowsLookup = { ...params.idRowsLookup }; const nodeNameToIdTree: GridNodeNameToIdTree = {}; params.rows.forEach((row) => { let nodeNameToIdSubTree = nodeNameToIdTree; - let parentNode: GridRowTreeNodeConfig | null = null; + let parentNode: TempRowTreeNode | null = null; for (let depth = 0; depth < row.path.length; depth += 1) { const nodeName = row.path[depth]; @@ -47,26 +51,26 @@ export const generateRowTree = (params: GenerateRowTreeParams) => { nodeNameToIdSubTree = nodeNameConfig.children; if (depth < row.path.length - 1) { - let node = result.tree[nodeId] ?? null; + let node = tempTree[nodeId] ?? null; if (!node) { node = { id: nodeId, isAutoGenerated: true, expanded, - children: [], + children: {}, parent: parentNode?.id ?? null, label: row.path[depth], depth, }; - result.tree[nodeId] = node; - result.idRowsLookup[nodeId] = {}; - result.rowIds.push(nodeId); + tempTree[nodeId] = node; + idRowsLookup[nodeId] = {}; + rowIds.push(nodeId); } node.descendantCount = (node.descendantCount ?? 0) + 1; } else { - result.tree[row.id] = { + tempTree[row.id] = { id: row.id, expanded, parent: parentNode?.id ?? null, @@ -77,20 +81,31 @@ export const generateRowTree = (params: GenerateRowTreeParams) => { if (parentNode != null) { if (!parentNode.children) { - parentNode.children = []; + parentNode.children = {}; } - // TODO: Avoid linear complexity here - if (!parentNode.children.includes(nodeId)) { - parentNode.children.push(nodeId); - } + parentNode.children[nodeId] = nodeId; } - parentNode = result.tree[nodeId]!; + parentNode = tempTree[nodeId]!; } - result.treeDepth = Math.max(result.treeDepth, row.path.length); + treeDepth = Math.max(treeDepth, row.path.length); + }); + + const tree: GridRowTreeConfig = {}; + rowIds.forEach((rowId) => { + const tempNode = tempTree[rowId]; + tree[rowId] = { + ...tempNode, + children: tempNode.children ? Object.values(tempNode.children) : undefined, + }; }); - return result; + return { + tree, + treeDepth, + rowIds, + idRowsLookup, + }; }; diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index d985d6cd893ca..5f6299d56d253 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -9,7 +9,7 @@ import { GridCellParams, GridColDef, MuiEvent } from '../../../models'; import { isSpaceKey } from '../../../utils/keyboardUtils'; import { useFirstRender } from '../../utils/useFirstRender'; import { GridRowGroupingPreProcessing } from '../../core/rowGroupsPerProcessing'; -import { generateRowTree } from '../rows/gridRowsUtils'; +import { buildRowTree } from '../rows/gridRowsUtils'; /** * Only available in DataGridPro @@ -59,7 +59,7 @@ export const useGridTreeData = ( })) .sort((a, b) => a.path.length - b.path.length); - return generateRowTree({ + return buildRowTree({ rows, ...params, defaultGroupingExpansionDepth: props.defaultGroupingExpansionDepth, From 03c1a50237bb158ccfc828ca463981644745730f Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 15 Oct 2021 14:08:04 +0200 Subject: [PATCH 238/390] Rework --- .../grid/hooks/features/treeData/useGridTreeData.ts | 2 +- .../rows/gridRowsUtils.ts => utils/rowTreeUtils.ts} | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) rename packages/grid/_modules_/grid/{hooks/features/rows/gridRowsUtils.ts => utils/rowTreeUtils.ts} (95%) diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 5f6299d56d253..5ef7e11758c55 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -8,8 +8,8 @@ import { GridEvents } from '../../../constants'; import { GridCellParams, GridColDef, MuiEvent } from '../../../models'; import { isSpaceKey } from '../../../utils/keyboardUtils'; import { useFirstRender } from '../../utils/useFirstRender'; +import { buildRowTree } from '../../../utils/rowTreeUtils'; import { GridRowGroupingPreProcessing } from '../../core/rowGroupsPerProcessing'; -import { buildRowTree } from '../rows/gridRowsUtils'; /** * Only available in DataGridPro diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts b/packages/grid/_modules_/grid/utils/rowTreeUtils.ts similarity index 95% rename from packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts rename to packages/grid/_modules_/grid/utils/rowTreeUtils.ts index cc51352e24cfa..6a69758a0fa79 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsUtils.ts +++ b/packages/grid/_modules_/grid/utils/rowTreeUtils.ts @@ -1,5 +1,8 @@ -import type { GridRowTreeNodeConfig, GridRowId, GridRowTreeConfig } from '../../../models'; -import { GridRowGroupingResult, GridRowGroupParams } from '../../core/rowGroupsPerProcessing'; +import type { GridRowTreeNodeConfig, GridRowId, GridRowTreeConfig } from '../models'; +import type { + GridRowGroupingResult, + GridRowGroupParams, +} from '../hooks/core/rowGroupsPerProcessing'; type GridNodeNameToIdTree = { [nodeName: string]: { id: GridRowId; children: GridNodeNameToIdTree }; From f15a828e6679c1608d38bf9bec6a7cfabab8c08b Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 15 Oct 2021 15:00:52 +0200 Subject: [PATCH 239/390] Improve basic tree data example --- .../data-grid/group-pivot/BasicTreeData.js | 38 ++++++++++++++---- .../data-grid/group-pivot/BasicTreeData.tsx | 40 +++++++++++++++---- 2 files changed, 63 insertions(+), 15 deletions(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js index 81761962ea92c..4a4be539c675b 100644 --- a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js @@ -1,16 +1,40 @@ import * as React from 'react'; import { DataGridPro } from '@mui/x-data-grid-pro'; -import { useDemoTreeData } from '@mui/x-data-grid-generator'; -export default function BasicTreeData() { - const { data, loading } = useDemoTreeData({ - rowLength: [10, 5, 3], - randomLength: true, - }); +const rows = [ + { path: ['Sarah'], jobTitle: 'CEO' }, + { path: ['Sarah', 'Thomas'], jobTitle: 'Head of Sales' }, + { path: ['Sarah', 'Thomas', 'Robert'], jobTitle: 'Sales Person' }, + { path: ['Sarah', 'Thomas', 'Karen'], jobTitle: 'Sales Person' }, + { path: ['Sarah', 'Thomas', 'Nancy'], jobTitle: 'Sales Person' }, + { path: ['Sarah', 'Thomas', 'Daniel'], jobTitle: 'Sales Person' }, + { path: ['Sarah', 'Thomas', 'Christopher'], jobTitle: 'Sales Person' }, + { path: ['Sarah', 'Thomas', 'Donald'], jobTitle: 'Sales Person' }, + { path: ['Sarah', 'Mary'], jobTitle: 'Head of Engineering' }, + { path: ['Sarah', 'Mary', 'Jennifer'], jobTitle: 'Tech lead front' }, + { path: ['Sarah', 'Mary', 'Jennifer', 'Linda'], jobTitle: 'Front-end developer' }, + { path: ['Sarah', 'Mary', 'Michael'], jobTitle: 'Tech lead devops' }, + { path: ['Sarah', 'Mary', 'Linda'], jobTitle: 'Tech lead back' }, + { path: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], jobTitle: 'Back-end developer' }, + { path: ['Sarah', 'Mary', 'Linda', 'William'], jobTitle: 'Back-end developer' }, +]; + +const columns = [{ field: 'jobTitle', width: 250 }]; +const getRowId = (row) => row.path.join('-'); +const getTreeDataPath = (row) => row.path; + +export default function BasicTreeData() { return (
- +
); } diff --git a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx index 81761962ea92c..e796ad5cb9b67 100644 --- a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx @@ -1,16 +1,40 @@ import * as React from 'react'; -import { DataGridPro } from '@mui/x-data-grid-pro'; -import { useDemoTreeData } from '@mui/x-data-grid-generator'; +import { DataGridPro, GridColumns, GridRowsProp } from '@mui/x-data-grid-pro'; -export default function BasicTreeData() { - const { data, loading } = useDemoTreeData({ - rowLength: [10, 5, 3], - randomLength: true, - }); +const rows: GridRowsProp = [ + { path: ['Sarah'], jobTitle: 'CEO' }, + { path: ['Sarah', 'Thomas'], jobTitle: 'Head of Sales' }, + { path: ['Sarah', 'Thomas', 'Robert'], jobTitle: 'Sales Person' }, + { path: ['Sarah', 'Thomas', 'Karen'], jobTitle: 'Sales Person' }, + { path: ['Sarah', 'Thomas', 'Nancy'], jobTitle: 'Sales Person' }, + { path: ['Sarah', 'Thomas', 'Daniel'], jobTitle: 'Sales Person' }, + { path: ['Sarah', 'Thomas', 'Christopher'], jobTitle: 'Sales Person' }, + { path: ['Sarah', 'Thomas', 'Donald'], jobTitle: 'Sales Person' }, + { path: ['Sarah', 'Mary'], jobTitle: 'Head of Engineering' }, + { path: ['Sarah', 'Mary', 'Jennifer'], jobTitle: 'Tech lead front' }, + { path: ['Sarah', 'Mary', 'Jennifer', 'Linda'], jobTitle: 'Front-end developer' }, + { path: ['Sarah', 'Mary', 'Michael'], jobTitle: 'Tech lead devops' }, + { path: ['Sarah', 'Mary', 'Linda'], jobTitle: 'Tech lead back' }, + { path: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], jobTitle: 'Back-end developer' }, + { path: ['Sarah', 'Mary', 'Linda', 'William'], jobTitle: 'Back-end developer' }, +]; + +const columns: GridColumns = [{ field: 'jobTitle', width: 250 }]; +const getRowId = (row) => row.path.join('-'); +const getTreeDataPath = (row) => row.path; + +export default function BasicTreeData() { return (
- +
); } From 235cf18995fb2a0361a64bb9a4f79af99d4be29d Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 18 Oct 2021 09:29:44 +0200 Subject: [PATCH 240/390] Fix --- .../CustomGroupingColumnTreeData.js | 18 ++++++++++++++++++ .../CustomGroupingColumnTreeData.tsx | 18 ++++++++++++++++++ .../cell/GridTreeDataGroupingCell.tsx | 12 ++++++++++++ 3 files changed, 48 insertions(+) diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js index 5b2f1c00f7269..6c626cec0b237 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js @@ -5,11 +5,19 @@ import { useGridApiContext, useGridSelector, gridVisibleDescendantCountLookupSelector, + GridEvents, } from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; +export const isNavigationKey = (key) => + key === 'Home' || + key === 'End' || + key.indexOf('Arrow') === 0 || + key.indexOf('Page') === 0 || + key === ' '; + const CustomGridTreeDataGroupingCell = (props) => { const { id } = props; const apiRef = useGridApiContext(); @@ -21,6 +29,15 @@ const CustomGridTreeDataGroupingCell = (props) => { const node = apiRef.current.UNSTABLE_getRowNode(id); const descendantCount = descendantCountLookup[id]; + const handleKeyDown = (event) => { + if (event.key === ' ') { + event.stopPropagation(); + } + if (isNavigationKey(event.key) && !event.shiftKey) { + apiRef.current.publishEvent(GridEvents.cellNavigationKeyDown, props, event); + } + }; + if (!node) { throw new Error(`MUI: No row with id #${id} found`); } @@ -33,6 +50,7 @@ const CustomGridTreeDataGroupingCell = (props) => { onClick={() => apiRef.current.UNSTABLE_setRowExpansion(id, !node?.expanded) } + onKeyDown={handleKeyDown} tabIndex={-1} size="small" > diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx index 0e2542fc8f997..7ffc647ee3dd3 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx @@ -6,11 +6,19 @@ import { useGridApiContext, useGridSelector, gridVisibleDescendantCountLookupSelector, + GridEvents, } from '@mui/x-data-grid-pro'; import { useDemoTreeData } from '@mui/x-data-grid-generator'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; +export const isNavigationKey = (key: string) => + key === 'Home' || + key === 'End' || + key.indexOf('Arrow') === 0 || + key.indexOf('Page') === 0 || + key === ' '; + const CustomGridTreeDataGroupingCell = (props: GridRenderCellParams) => { const { id } = props; const apiRef = useGridApiContext(); @@ -21,6 +29,15 @@ const CustomGridTreeDataGroupingCell = (props: GridRenderCellParams) => { const node = apiRef.current.UNSTABLE_getRowNode(id); const descendantCount = descendantCountLookup[id]; + const handleKeyDown = (event) => { + if (event.key === ' ') { + event.stopPropagation(); + } + if (isNavigationKey(event.key) && !event.shiftKey) { + apiRef.current.publishEvent(GridEvents.cellNavigationKeyDown, props, event); + } + }; + if (!node) { throw new Error(`MUI: No row with id #${id} found`); } @@ -33,6 +50,7 @@ const CustomGridTreeDataGroupingCell = (props: GridRenderCellParams) => { onClick={() => apiRef.current.UNSTABLE_setRowExpansion(id, !node?.expanded) } + onKeyDown={handleKeyDown} tabIndex={-1} size="small" > diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index 132b4bd34bdad..3ddb065d050c9 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -8,6 +8,8 @@ import { useGridApiContext } from '../../hooks/utils/useGridApiContext'; import { GridRenderCellParams } from '../../models/params/gridCellParams'; import { gridVisibleDescendantCountLookupSelector } from '../../hooks/features/filter/gridFilterSelector'; import { useGridSelector } from '../../hooks/utils/useGridSelector'; +import { isNavigationKey, isSpaceKey } from '../../utils/keyboardUtils'; +import { GridEvents } from '../../constants'; const useStyles = makeStyles({ root: { @@ -36,6 +38,15 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { ? rootProps.components.TreeDataCollapseIcon : rootProps.components.TreeDataExpandIcon; + const handleKeyDown = (event) => { + if (isSpaceKey(event.key)) { + event.stopPropagation(); + } + if (isNavigationKey(event.key) && !event.shiftKey) { + apiRef.current.publishEvent(GridEvents.cellNavigationKeyDown, props, event); + } + }; + if (!node) { throw new Error(`MUI: No row with id #${id} found`); } @@ -47,6 +58,7 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { apiRef.current.UNSTABLE_setRowExpansion(id, !node?.expanded)} + onKeyDown={handleKeyDown} tabIndex={-1} aria-label={ node.expanded From 176a60e04b82706a7c33b6e364a05177572123e6 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 18 Oct 2021 11:45:32 +0200 Subject: [PATCH 241/390] Handle tree data in useDemoData --- docs/pages/playground.tsx | 27 +++--- .../features/treeData/useGridTreeData.ts | 7 +- .../src/employees.columns.tsx | 1 + .../src/services/gridColDefGenerator.ts | 18 +++- .../src/services/random-generator.ts | 30 +++++- .../src/services/real-data-service.ts | 20 +++- .../x-grid-data-generator/src/useDemoData.ts | 96 ++++++++++++++++++- .../src/tests/treeData.DataGridPro.test.tsx | 27 +++++- 8 files changed, 199 insertions(+), 27 deletions(-) diff --git a/docs/pages/playground.tsx b/docs/pages/playground.tsx index adedc3575f88c..6272cae81237d 100644 --- a/docs/pages/playground.tsx +++ b/docs/pages/playground.tsx @@ -1,27 +1,22 @@ import * as React from 'react'; import { DataGridPro } from '@mui/x-data-grid-pro'; -import { useDemoTreeData } from '@mui/x-data-grid-generator'; - -const groupingColDef = { minWidth: 400 }; +import { useDemoData } from '@mui/x-data-grid-generator'; export default function BasicTreeData() { - const { data, loading } = useDemoTreeData({ - rowLength: [10, 10], - randomLength: false, + const { data, loading } = useDemoData({ + dataSet: 'Employee', + rowLength: 1000, + maxColumns: 6, + treeData: { + depth: 3, + averageChildren: 10, + groupingField: 'name', + }, }); return (
- +
); } diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 5ef7e11758c55..cd1315192dfd7 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -98,7 +98,12 @@ export const useGridTreeData = ( if (cellParams.field === '__tree_data_group__' && isSpaceKey(event.key)) { event.stopPropagation(); event.preventDefault(); - event.defaultMuiPrevented = true; + + const node = apiRef.current.UNSTABLE_getRowNode(params.id); + if (!node || node.descendantCount === 0) { + return; + } + apiRef.current.UNSTABLE_setRowExpansion( params.id, !apiRef.current.UNSTABLE_getRowNode(params.id)?.expanded, diff --git a/packages/grid/x-grid-data-generator/src/employees.columns.tsx b/packages/grid/x-grid-data-generator/src/employees.columns.tsx index 7517e13640c6d..4eeba30d6bacf 100644 --- a/packages/grid/x-grid-data-generator/src/employees.columns.tsx +++ b/packages/grid/x-grid-data-generator/src/employees.columns.tsx @@ -45,6 +45,7 @@ export const getEmployeeColumns = (): GridColDefGenerator[] => [ field: 'name', headerName: 'Name', generateData: randomName, + dataGeneratorUniquenessEnabled: true, width: 120, editable: true, }, diff --git a/packages/grid/x-grid-data-generator/src/services/gridColDefGenerator.ts b/packages/grid/x-grid-data-generator/src/services/gridColDefGenerator.ts index 741e55fbcf074..249c374644c64 100644 --- a/packages/grid/x-grid-data-generator/src/services/gridColDefGenerator.ts +++ b/packages/grid/x-grid-data-generator/src/services/gridColDefGenerator.ts @@ -1,5 +1,21 @@ import { GridColDef } from '@mui/x-data-grid-pro'; +export interface GridDataGeneratorContext { + /** + * Values already attributed to this column. + * Only defined if the column has the uniqueness mode activated. + * The keys represents the random value and the value represents the amount of rows that already have this value. + * This allows to data generators to add a suffix to the returned value to force the uniqueness. + */ + values?: Record; +} + export interface GridColDefGenerator extends GridColDef { - generateData?: (data: any) => any; + generateData?: (row: any, context: GridDataGeneratorContext) => any; + + /** + * If `true`, each row will have a distinct value + * @default false + */ + dataGeneratorUniquenessEnabled?: boolean; } diff --git a/packages/grid/x-grid-data-generator/src/services/random-generator.ts b/packages/grid/x-grid-data-generator/src/services/random-generator.ts index b50346b75e61f..ba8946c309569 100644 --- a/packages/grid/x-grid-data-generator/src/services/random-generator.ts +++ b/packages/grid/x-grid-data-generator/src/services/random-generator.ts @@ -10,6 +10,7 @@ import { STATUS_OPTIONS, TAXCODE_OPTIONS, } from './static-data'; +import { GridDataGeneratorContext } from './gridColDefGenerator'; const chanceId = globalChance(); let chance: Chance.Chance; @@ -20,6 +21,30 @@ if (process.env.DISABLE_CHANCE_RANDOM) { chance = chanceId; } +type ColumnDataGenerator = (data: any, context: GridDataGeneratorContext) => Value; + +/** + * Wrap a data generator that returns a string and add a prefix if the value generated has already been given + */ +const uniquenessHandler = + (generator: ColumnDataGenerator): ColumnDataGenerator => + (data, context) => { + const rawValue = generator(data, context); + + if (!context.values) { + return rawValue; + } + + const valueCount = (context.values[rawValue] ?? 0) + 1; + context.values[rawValue] = valueCount + 1; + + if (valueCount > 1) { + return `${rawValue} ${valueCount}`; + } + + return rawValue; + }; + function dateFuture(years?: number, refDate?: string) { let date = new Date(); if (typeof refDate !== 'undefined') { @@ -75,7 +100,8 @@ function datePast(years?: number, refDate?: string) { } export const random = (min: number, max: number): number => chance.floating({ min, max }); -export const randomInt = (min: number, max: number): number => Number(random(min, max).toFixed()); +export const randomInt = (min: number, max: number): number => + Math.floor(Math.random() * (max - min + 1) + min); export const randomPrice = (min = 0, max = 100000): number => Number(random(min, max).toFixed(2)); export const randomRate = (): number => random(0, 1); export const randomDate = (start: Date, end: Date) => @@ -116,7 +142,7 @@ export const randomCreatedDate = () => datePast(); export const randomUpdatedDate = () => dateRecent(); export const randomJobTitle = () => chance.profession(); export const randomRating = () => randomInt(1, 5); -export const randomName = () => chance.name(); +export const randomName = uniquenessHandler(() => chance.name()); export const generateFilledQuantity = (data: { quantity: number }) => Number((data.quantity * randomRate()).toFixed()) / data.quantity; diff --git a/packages/grid/x-grid-data-generator/src/services/real-data-service.ts b/packages/grid/x-grid-data-generator/src/services/real-data-service.ts index 420763e3253da..ae022fd8c4041 100644 --- a/packages/grid/x-grid-data-generator/src/services/real-data-service.ts +++ b/packages/grid/x-grid-data-generator/src/services/real-data-service.ts @@ -1,8 +1,8 @@ -import { GridRowData } from '@mui/x-data-grid-pro'; +import { DataGridProProps, GridRowData } from '@mui/x-data-grid-pro'; import asyncWorker from '../asyncWorker'; -import { GridColDefGenerator } from './gridColDefGenerator'; +import { GridColDefGenerator, GridDataGeneratorContext } from './gridColDefGenerator'; -export interface GridDemoData { +export interface GridDemoData extends Pick { rows: GridRowData[]; columns: GridColDefGenerator[]; } @@ -14,6 +14,7 @@ export function getRealData( return new Promise((resolve) => { const tasks = { current: rowLength }; const rows: GridDemoData['rows'] = []; + const indexedValues: { [field: string]: { [value: string]: number } } = {}; function work() { const row: any = {}; @@ -21,7 +22,18 @@ export function getRealData( for (let j = 0; j < columns.length; j += 1) { const column = columns[j]; if (column.generateData) { - row[column.field] = column.generateData(row); + const context: GridDataGeneratorContext = {}; + if (column.dataGeneratorUniquenessEnabled) { + let fieldValues = indexedValues[column.field]; + if (!fieldValues) { + fieldValues = {}; + indexedValues[column.field] = fieldValues; + } + + context.values = fieldValues; + } + + row[column.field] = column.generateData(row, context); } } diff --git a/packages/grid/x-grid-data-generator/src/useDemoData.ts b/packages/grid/x-grid-data-generator/src/useDemoData.ts index 31b6a49eea773..8bf5edec368b3 100644 --- a/packages/grid/x-grid-data-generator/src/useDemoData.ts +++ b/packages/grid/x-grid-data-generator/src/useDemoData.ts @@ -5,6 +5,8 @@ import { getCommodityColumns } from './commodities.columns'; import { getEmployeeColumns } from './employees.columns'; import asyncWorker from './asyncWorker'; import { GridColDefGenerator } from './services/gridColDefGenerator'; +import { GridRowData } from '../../_modules_'; +import { randomArrayItem } from './services'; const dataCache = new LRUCache({ max: 10, @@ -25,6 +27,25 @@ export interface DemoDataOptions { rowLength: number; maxColumns?: number; editable?: boolean; + treeData?: { + /** + * The field used to generate the path + * If not defined, the tree data will not be built + */ + groupingField?: string; + + /** + * The depth of the tree + * @default: 1 + */ + depth?: number; + + /** + * The average amount of children in a node + * @default: 2 + */ + averageChildren?: number; + }; } // Generate fake data from a seed. @@ -101,7 +122,15 @@ export const useDemoData = (options: DemoDataOptions): DemoDataReturnType => { return columns; }, [options.dataSet, options.editable, options.maxColumns]); - const [data, setData] = React.useState(() => ({ columns: getColumns(), rows: [] })); + const treeDataDepth = options.treeData?.depth ?? 1; + const hasTreeData = treeDataDepth > 1 && options.treeData?.groupingField; + + const [data, setData] = React.useState(() => ({ + columns: getColumns(), + rows: [], + getTreeDataPath: hasTreeData ? (row) => row.path : undefined, + treeData: hasTreeData ? true : undefined, + })); React.useEffect(() => { const cacheKey = `${options.dataSet}-${rowLength}-${index}-${options.maxColumns}`; @@ -129,6 +158,58 @@ export const useDemoData = (options: DemoDataOptions): DemoDataReturnType => { newData = await getRealData(rowLength, columns); } + const averageChildren = options.treeData?.averageChildren ?? 2; + + if (hasTreeData) { + const rowsByTreeDepth: Record< + number, + { [index: number]: { value: GridRowData; parentIndex: number | null } } + > = {}; + const rowsCount = newData.rows.length; + + for (let i = 0; i < rowsCount; i += 1) { + const row = newData.rows[i]; + + const currentChunk = + Math.floor((i * (averageChildren ** treeDataDepth - 1)) / rowsCount) + 1; + const currentDepth = Math.floor(Math.log(currentChunk) / Math.log(averageChildren)); + + if (!rowsByTreeDepth[currentDepth]) { + rowsByTreeDepth[currentDepth] = {}; + } + + rowsByTreeDepth[currentDepth][i] = { value: row, parentIndex: null }; + } + + for (let i = 0; i < treeDataDepth; i += 1) { + Object.keys(rowsByTreeDepth[i]).forEach((rowIndex) => { + const row = rowsByTreeDepth[i][Number(rowIndex)]; + const path: string[] = []; + let previousRow: { value: GridRowData; parentIndex: number | null } | null = null; + for (let k = i; k >= 0; k -= 1) { + let rowTemp: { value: GridRowData; parentIndex: number | null }; + if (k === i) { + if (i > 0) { + row.parentIndex = Number(randomArrayItem(Object.keys(rowsByTreeDepth[i - 1]))); + } + rowTemp = row; + } else { + rowTemp = rowsByTreeDepth[k][previousRow!.parentIndex!]; + } + + path.unshift(rowTemp.value[options.treeData?.groupingField!]); + + previousRow = rowTemp; + } + + row.value.path = path; + }); + } + + newData.getTreeDataPath = (row) => row.path; + newData.treeData = true; + } + if (!active) { return; } @@ -146,7 +227,18 @@ export const useDemoData = (options: DemoDataOptions): DemoDataReturnType => { return () => { active = false; }; - }, [rowLength, data.columns, options.dataSet, options.maxColumns, index, getColumns]); + }, [ + rowLength, + data.columns, + options.dataSet, + options.maxColumns, + treeDataDepth, + options.treeData?.groupingField, + options.treeData?.averageChildren, + hasTreeData, + index, + getColumns, + ]); return { data, diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index 64906dee45fb4..27d8e8d62f6f7 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -211,13 +211,21 @@ describe(' - Tree Data', () => { }); }); - describe('row grouping', () => { + describe('row grouping column', () => { it('should add a grouping column', () => { render(); const columnsHeader = getColumnHeadersTextContent(); expect(columnsHeader).to.deep.equal(['Group', 'name', 'value']); }); + it('should render a toggling icon only when a row has children', () => { + render(); + // No children + expect(getCell(2, 0).querySelectorAll('button')).to.have.length(0); + // Some children + expect(getCell(0, 0).querySelectorAll('button')).to.have.length(1); + }); + it('should toggle expansion when clicking on grouping column icon', () => { render(); expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); @@ -255,6 +263,23 @@ describe(' - Tree Data', () => { fireEvent.click(screen.getByRole('button', { name: /next page/i })); expect(getColumnValues(1)).to.deep.equal(['C']); }); + + it('should keep the row expansion when switching page', () => { + render(); + expect(getColumnValues(1)).to.deep.equal(['A']); + fireEvent.click(getCell(0, 0).querySelector('button')); + expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B']); + fireEvent.click(screen.getByRole('button', { name: /next page/i })); + expect(getColumnValues(1)).to.deep.equal(['B']); + fireEvent.click(getCell(3, 0).querySelector('button')); + expect(getColumnValues(1)).to.deep.equal(['B', 'B.A', 'B.B']); + fireEvent.click(screen.getByRole('button', { name: /previous page/i })); + expect(getColumnValues(1)).to.deep.equal(['A', 'A.A', 'A.B']); + fireEvent.click(getCell(0, 0).querySelector('button')); + expect(getColumnValues(1)).to.deep.equal(['A']); + fireEvent.click(screen.getByRole('button', { name: /next page/i })); + expect(getColumnValues(1)).to.deep.equal(['B', 'B.A', 'B.B']); + }); }); describe('filter', () => { From cb0ecca5bba1ae954988bc3398e7f667b791abb8 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 18 Oct 2021 12:45:39 +0200 Subject: [PATCH 242/390] Fix --- .../keyboard/useGridKeyboardNavigation.ts | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts index 0c3b1630ae983..dddfa5af7c1ad 100644 --- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts +++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts @@ -77,7 +77,6 @@ export const useGridKeyboardNavigation = ( const statePaginationRange = useGridSelector(apiRef, gridPaginationRowRangeSelector); const colCount = useGridSelector(apiRef, visibleGridColumnsLengthSelector); const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); - const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowEntriesSelector); const mapKey = (event: React.KeyboardEvent) => { if (isEnterKey(event.key)) { @@ -92,6 +91,7 @@ export const useGridKeyboardNavigation = ( const navigateCells = React.useCallback( (params: GridCellParams, event: React.KeyboardEvent) => { event.preventDefault(); + const visibleSortedRows = gridSortedVisibleRowEntriesSelector(apiRef.current.state); const colIndex = apiRef.current.getColumnIndex(params.field); const rowIndex = visibleSortedRows.findIndex((row) => row.id === params.id); @@ -156,20 +156,13 @@ export const useGridKeyboardNavigation = ( const node = visibleSortedRows[nextCellIndexes.rowIndex]; apiRef.current.setCellFocus(node.id, field); }, - [ - apiRef, - visibleSortedRows, - statePaginationRange, - props.pagination, - colCount, - logger, - containerSizes, - ], + [apiRef, statePaginationRange, props.pagination, colCount, logger, containerSizes], ); const navigateColumnHeaders = React.useCallback( (params: GridColumnHeaderParams, event: React.KeyboardEvent) => { event.preventDefault(); + const visibleSortedRows = gridSortedVisibleRowEntriesSelector(apiRef.current.state); let nextColumnHeaderIndexes: GridColumnHeaderIndexCoordinates | null; const colIndex = apiRef.current.getColumnIndex(params.field); const key = mapKey(event); @@ -213,7 +206,7 @@ export const useGridKeyboardNavigation = ( const field = apiRef.current.getVisibleColumns()[nextColumnHeaderIndexes.colIndex].field; apiRef.current.setColumnHeaderFocus(field, event); }, - [apiRef, colCount, containerSizes, logger, visibleSortedRows], + [apiRef, colCount, containerSizes, logger], ); useGridApiEventHandler(apiRef, GridEvents.cellNavigationKeyDown, navigateCells); From c643e5668a63629b3a6e3166ac140f9e956d3047 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 18 Oct 2021 12:49:02 +0200 Subject: [PATCH 243/390] Fix --- .../keyboard/useGridKeyboardNavigation.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts index dddfa5af7c1ad..077f0e7491cec 100644 --- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts +++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts @@ -77,6 +77,7 @@ export const useGridKeyboardNavigation = ( const statePaginationRange = useGridSelector(apiRef, gridPaginationRowRangeSelector); const colCount = useGridSelector(apiRef, visibleGridColumnsLengthSelector); const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); + const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowEntriesSelector); const mapKey = (event: React.KeyboardEvent) => { if (isEnterKey(event.key)) { @@ -91,7 +92,6 @@ export const useGridKeyboardNavigation = ( const navigateCells = React.useCallback( (params: GridCellParams, event: React.KeyboardEvent) => { event.preventDefault(); - const visibleSortedRows = gridSortedVisibleRowEntriesSelector(apiRef.current.state); const colIndex = apiRef.current.getColumnIndex(params.field); const rowIndex = visibleSortedRows.findIndex((row) => row.id === params.id); @@ -99,7 +99,7 @@ export const useGridKeyboardNavigation = ( const isCtrlPressed = event.ctrlKey || event.metaKey || event.shiftKey; const paginatedRowRange = props.pagination ? statePaginationRange! - : { firstRowIndex: 0, lastRowIndex: visibleSortedRows.length }; + : { firstRowIndex: 0, lastRowIndex: visibleSortedRows.length - 1 }; const rowCount = paginatedRowRange.lastRowIndex - paginatedRowRange.firstRowIndex + 1; let nextCellIndexes: GridCellIndexCoordinates; @@ -156,13 +156,20 @@ export const useGridKeyboardNavigation = ( const node = visibleSortedRows[nextCellIndexes.rowIndex]; apiRef.current.setCellFocus(node.id, field); }, - [apiRef, statePaginationRange, props.pagination, colCount, logger, containerSizes], + [ + apiRef, + visibleSortedRows, + statePaginationRange, + props.pagination, + colCount, + logger, + containerSizes, + ], ); const navigateColumnHeaders = React.useCallback( (params: GridColumnHeaderParams, event: React.KeyboardEvent) => { event.preventDefault(); - const visibleSortedRows = gridSortedVisibleRowEntriesSelector(apiRef.current.state); let nextColumnHeaderIndexes: GridColumnHeaderIndexCoordinates | null; const colIndex = apiRef.current.getColumnIndex(params.field); const key = mapKey(event); @@ -206,7 +213,7 @@ export const useGridKeyboardNavigation = ( const field = apiRef.current.getVisibleColumns()[nextColumnHeaderIndexes.colIndex].field; apiRef.current.setColumnHeaderFocus(field, event); }, - [apiRef, colCount, containerSizes, logger], + [apiRef, colCount, containerSizes, logger, visibleSortedRows], ); useGridApiEventHandler(apiRef, GridEvents.cellNavigationKeyDown, navigateCells); From 9737658a217e9cf166e7eee5b0ea7062489d0ace Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 18 Oct 2021 13:01:32 +0200 Subject: [PATCH 244/390] Work --- .../pagination/CursorPaginationGrid.tsx | 4 +-- .../pagination/ServerPaginationGrid.tsx | 4 +-- ...ontrolledSelectionServerPaginationGrid.tsx | 4 +-- .../data-grid/sorting/ServerSortingGrid.tsx | 7 +++-- .../src/services/real-data-service.ts | 10 +++--- .../x-grid-data-generator/src/useDemoData.ts | 31 ++++++++++++++----- .../src/useDemoTreeData.ts | 4 +-- 7 files changed, 42 insertions(+), 22 deletions(-) diff --git a/docs/src/pages/components/data-grid/pagination/CursorPaginationGrid.tsx b/docs/src/pages/components/data-grid/pagination/CursorPaginationGrid.tsx index fd45aeddbdd60..682a6cc4265a1 100644 --- a/docs/src/pages/components/data-grid/pagination/CursorPaginationGrid.tsx +++ b/docs/src/pages/components/data-grid/pagination/CursorPaginationGrid.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { GridRowsProp, DataGrid, GridRowId, GridRowModel } from '@mui/x-data-grid'; -import { useDemoData, GridDemoData } from '@mui/x-data-grid-generator'; +import { useDemoData, GeneratedDemoData } from '@mui/x-data-grid-generator'; interface ServerBasedGridResponse { rows: GridRowModel[]; @@ -11,7 +11,7 @@ const PAGE_SIZE = 5; function loadServerRows( cursor: GridRowId | null | undefined, - data: GridDemoData, + data: GeneratedDemoData, ): Promise { return new Promise((resolve) => { setTimeout(() => { diff --git a/docs/src/pages/components/data-grid/pagination/ServerPaginationGrid.tsx b/docs/src/pages/components/data-grid/pagination/ServerPaginationGrid.tsx index d212b2b523acb..cf0f347988c4d 100644 --- a/docs/src/pages/components/data-grid/pagination/ServerPaginationGrid.tsx +++ b/docs/src/pages/components/data-grid/pagination/ServerPaginationGrid.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import { GridRowsProp, DataGrid } from '@mui/x-data-grid'; -import { useDemoData, GridDemoData } from '@mui/x-data-grid-generator'; +import { useDemoData, GeneratedDemoData } from '@mui/x-data-grid-generator'; -function loadServerRows(page: number, data: GridDemoData): Promise { +function loadServerRows(page: number, data: GeneratedDemoData): Promise { return new Promise((resolve) => { setTimeout(() => { resolve(data.rows.slice(page * 5, (page + 1) * 5)); diff --git a/docs/src/pages/components/data-grid/selection/ControlledSelectionServerPaginationGrid.tsx b/docs/src/pages/components/data-grid/selection/ControlledSelectionServerPaginationGrid.tsx index 101aad2adb78f..a83b3846d96d8 100644 --- a/docs/src/pages/components/data-grid/selection/ControlledSelectionServerPaginationGrid.tsx +++ b/docs/src/pages/components/data-grid/selection/ControlledSelectionServerPaginationGrid.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import { DataGrid, GridRowsProp, GridSelectionModel } from '@mui/x-data-grid'; -import { GridDemoData, useDemoData } from '@mui/x-data-grid-generator'; +import { GeneratedDemoData, useDemoData } from '@mui/x-data-grid-generator'; -function loadServerRows(page: number, data: GridDemoData): Promise { +function loadServerRows(page: number, data: GeneratedDemoData): Promise { return new Promise((resolve) => { setTimeout(() => { resolve(data.rows.slice(page * 5, (page + 1) * 5)); diff --git a/docs/src/pages/components/data-grid/sorting/ServerSortingGrid.tsx b/docs/src/pages/components/data-grid/sorting/ServerSortingGrid.tsx index d5cfbef7a7fe3..066a02de63db5 100644 --- a/docs/src/pages/components/data-grid/sorting/ServerSortingGrid.tsx +++ b/docs/src/pages/components/data-grid/sorting/ServerSortingGrid.tsx @@ -1,8 +1,11 @@ import * as React from 'react'; import { GridRowsProp, DataGrid, GridSortModel } from '@mui/x-data-grid'; -import { useDemoData, GridDemoData } from '@mui/x-data-grid-generator'; +import { useDemoData, GeneratedDemoData } from '@mui/x-data-grid-generator'; -function loadServerRows(sortModel: GridSortModel, data: GridDemoData): Promise { +function loadServerRows( + sortModel: GridSortModel, + data: GeneratedDemoData, +): Promise { return new Promise((resolve) => { setTimeout(() => { if (sortModel.length === 0) { diff --git a/packages/grid/x-grid-data-generator/src/services/real-data-service.ts b/packages/grid/x-grid-data-generator/src/services/real-data-service.ts index ae022fd8c4041..c7908816152ec 100644 --- a/packages/grid/x-grid-data-generator/src/services/real-data-service.ts +++ b/packages/grid/x-grid-data-generator/src/services/real-data-service.ts @@ -1,8 +1,8 @@ -import { DataGridProProps, GridRowData } from '@mui/x-data-grid-pro'; +import { GridRowData } from '@mui/x-data-grid-pro'; import asyncWorker from '../asyncWorker'; import { GridColDefGenerator, GridDataGeneratorContext } from './gridColDefGenerator'; -export interface GridDemoData extends Pick { +export interface GeneratedDemoData { rows: GridRowData[]; columns: GridColDefGenerator[]; } @@ -10,10 +10,10 @@ export interface GridDemoData extends Pick { - return new Promise((resolve) => { +): Promise { + return new Promise((resolve) => { const tasks = { current: rowLength }; - const rows: GridDemoData['rows'] = []; + const rows: GeneratedDemoData['rows'] = []; const indexedValues: { [field: string]: { [value: string]: number } } = {}; function work() { diff --git a/packages/grid/x-grid-data-generator/src/useDemoData.ts b/packages/grid/x-grid-data-generator/src/useDemoData.ts index 8bf5edec368b3..3cde8ab37b84a 100644 --- a/packages/grid/x-grid-data-generator/src/useDemoData.ts +++ b/packages/grid/x-grid-data-generator/src/useDemoData.ts @@ -1,6 +1,7 @@ import * as React from 'react'; import LRUCache from 'lru-cache'; -import { GridDemoData, getRealData } from './services/real-data-service'; +import { DataGridProProps } from '@mui/x-data-grid-pro'; +import { GeneratedDemoData, getRealData } from './services/real-data-service'; import { getCommodityColumns } from './commodities.columns'; import { getEmployeeColumns } from './employees.columns'; import asyncWorker from './asyncWorker'; @@ -8,13 +9,13 @@ import { GridColDefGenerator } from './services/gridColDefGenerator'; import { GridRowData } from '../../_modules_'; import { randomArrayItem } from './services'; -const dataCache = new LRUCache({ +const dataCache = new LRUCache({ max: 10, maxAge: 60 * 5 * 1e3, // 5 minutes }); export type DemoDataReturnType = { - data: GridDemoData; + data: DemoData; loading: boolean; setRowLength: (count: number) => void; loadNewData: () => void; @@ -48,13 +49,17 @@ export interface DemoDataOptions { }; } +export interface DemoData + extends Pick, + GeneratedDemoData {} + // Generate fake data from a seed. // It's about x20 faster than getRealData. async function extrapolateSeed( rowLength: number, columns: GridColDefGenerator[], - data: GridDemoData, -): Promise { + data: GeneratedDemoData, +): Promise { return new Promise((resolve) => { const seed = data.rows; const rows = data.rows.slice(); @@ -125,7 +130,7 @@ export const useDemoData = (options: DemoDataOptions): DemoDataReturnType => { const treeDataDepth = options.treeData?.depth ?? 1; const hasTreeData = treeDataDepth > 1 && options.treeData?.groupingField; - const [data, setData] = React.useState(() => ({ + const [data, setData] = React.useState(() => ({ columns: getColumns(), rows: [], getTreeDataPath: hasTreeData ? (row) => row.path : undefined, @@ -149,7 +154,7 @@ export const useDemoData = (options: DemoDataOptions): DemoDataReturnType => { (async () => { setLoading(true); - let newData: GridDemoData; + let newData: DemoData; const columns = getColumns(); if (rowLength > 1000) { newData = await getRealData(1000, columns); @@ -167,6 +172,14 @@ export const useDemoData = (options: DemoDataOptions): DemoDataReturnType => { > = {}; const rowsCount = newData.rows.length; + const groupingCol = newData.columns.find( + (col) => col.field === options.treeData?.groupingField, + ); + + if (!groupingCol) { + throw new Error('MUI: The tree data grouping field does not exist'); + } + for (let i = 0; i < rowsCount; i += 1) { const row = newData.rows[i]; @@ -206,6 +219,10 @@ export const useDemoData = (options: DemoDataOptions): DemoDataReturnType => { }); } + groupingCol.hide = true; + newData.groupingColDef = { + headerName: groupingCol.headerName ?? groupingCol.field, + }; newData.getTreeDataPath = (row) => row.path; newData.treeData = true; } diff --git a/packages/grid/x-grid-data-generator/src/useDemoTreeData.ts b/packages/grid/x-grid-data-generator/src/useDemoTreeData.ts index 99b86a4702976..b3099a82ba5cd 100644 --- a/packages/grid/x-grid-data-generator/src/useDemoTreeData.ts +++ b/packages/grid/x-grid-data-generator/src/useDemoTreeData.ts @@ -1,5 +1,5 @@ import * as React from 'react'; -import { GridDemoData, randomInt } from '@mui/x-data-grid-generator/services'; +import { GeneratedDemoData, randomInt } from '@mui/x-data-grid-generator/services'; import { DataGridProProps, GridColumns, GridRowModel } from '@mui/x-data-grid-pro'; interface GridDemoTreeDataOptions { @@ -7,7 +7,7 @@ interface GridDemoTreeDataOptions { randomLength: boolean; } -interface GridDemoTreeData extends GridDemoData, Pick {} +interface GridDemoTreeData extends GeneratedDemoData, Pick {} interface GridDemoTreeDataResponse { loading: boolean; From 26a8baf6b1324260d056fb80aee3bfb7d15b99f7 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 18 Oct 2021 13:31:07 +0200 Subject: [PATCH 245/390] Work --- docs/pages/playground.tsx | 2 +- .../src/services/tree-data-generator.ts | 108 ++++++++++++++ .../x-grid-data-generator/src/useDemoData.ts | 133 ++++-------------- 3 files changed, 136 insertions(+), 107 deletions(-) create mode 100644 packages/grid/x-grid-data-generator/src/services/tree-data-generator.ts diff --git a/docs/pages/playground.tsx b/docs/pages/playground.tsx index 6272cae81237d..a977a29ffa472 100644 --- a/docs/pages/playground.tsx +++ b/docs/pages/playground.tsx @@ -8,7 +8,7 @@ export default function BasicTreeData() { rowLength: 1000, maxColumns: 6, treeData: { - depth: 3, + maxDepth: 3, averageChildren: 10, groupingField: 'name', }, diff --git a/packages/grid/x-grid-data-generator/src/services/tree-data-generator.ts b/packages/grid/x-grid-data-generator/src/services/tree-data-generator.ts new file mode 100644 index 0000000000000..9a22695cff2f5 --- /dev/null +++ b/packages/grid/x-grid-data-generator/src/services/tree-data-generator.ts @@ -0,0 +1,108 @@ +import { DataGridProProps } from '@mui/x-data-grid-pro'; +import { GridRowData } from '../../../_modules_'; +import { GeneratedDemoData } from './real-data-service'; +import { randomArrayItem } from './random-generator'; + +export interface AddPathToDemoDataOptions { + /** + * The field used to generate the path + * If not defined, the tree data will not be built + */ + groupingField?: string; + + /** + * The depth of the tree + * @default: 1 + */ + maxDepth?: number; + + /** + * The average amount of children in a node + * @default: 2 + */ + averageChildren?: number; +} + +export interface DemoTreeDataValue + extends Pick, + GeneratedDemoData {} + +interface RowWithParentIndex { + value: GridRowData; + parentIndex: number | null; +} + +export const addTreeDataOptionsToDemoData = ( + data: GeneratedDemoData, + options: AddPathToDemoDataOptions = {}, +): DemoTreeDataValue => { + const { averageChildren = 2, maxDepth = 1, groupingField } = options; + + const hasTreeData = maxDepth > 1 && groupingField != null; + if (!hasTreeData) { + return data; + } + + const rowsByTreeDepth: Record< + number, + { rows: { [index: number]: RowWithParentIndex }; rowIndexes: number[] } + > = {}; + const rowsCount = data.rows.length; + + const groupingCol = data.columns.find((col) => col.field === options.groupingField); + + if (!groupingCol) { + throw new Error('MUI: The tree data grouping field does not exist'); + } + groupingCol.hide = true; + + for (let i = 0; i < rowsCount; i += 1) { + const row = data.rows[i]; + + const currentChunk = Math.floor((i * (averageChildren ** maxDepth - 1)) / rowsCount) + 1; + const currentDepth = Math.floor(Math.log(currentChunk) / Math.log(averageChildren)); + + if (!rowsByTreeDepth[currentDepth]) { + rowsByTreeDepth[currentDepth] = { rows: {}, rowIndexes: [] }; + } + + rowsByTreeDepth[currentDepth].rows[i] = { value: row, parentIndex: null }; + rowsByTreeDepth[currentDepth].rowIndexes.push(i); + } + + Object.entries(rowsByTreeDepth).forEach(([depthStr, { rows }]) => { + const depth = Number(depthStr); + + Object.values(rows).forEach((row) => { + const path: string[] = []; + let previousRow: RowWithParentIndex | null = null; + for (let k = depth; k >= 0; k -= 1) { + let rowTemp: RowWithParentIndex; + if (k === depth) { + if (depth > 0) { + row.parentIndex = Number(randomArrayItem(rowsByTreeDepth[depth - 1].rowIndexes)); + } + rowTemp = row; + } else { + rowTemp = rowsByTreeDepth[k].rows[previousRow!.parentIndex!]; + } + + path.unshift(rowTemp.value[groupingField!]); + + previousRow = rowTemp; + } + + row.value.path = path; + }); + }); + + return { + ...data, + groupingColDef: { + headerName: groupingCol.headerName ?? groupingCol.field, + width: 250, + }, + getTreeDataPath: (row) => row.path, + treeData: true, + }; +}; diff --git a/packages/grid/x-grid-data-generator/src/useDemoData.ts b/packages/grid/x-grid-data-generator/src/useDemoData.ts index 3cde8ab37b84a..538ba056cff71 100644 --- a/packages/grid/x-grid-data-generator/src/useDemoData.ts +++ b/packages/grid/x-grid-data-generator/src/useDemoData.ts @@ -1,21 +1,23 @@ import * as React from 'react'; import LRUCache from 'lru-cache'; -import { DataGridProProps } from '@mui/x-data-grid-pro'; import { GeneratedDemoData, getRealData } from './services/real-data-service'; import { getCommodityColumns } from './commodities.columns'; import { getEmployeeColumns } from './employees.columns'; import asyncWorker from './asyncWorker'; import { GridColDefGenerator } from './services/gridColDefGenerator'; -import { GridRowData } from '../../_modules_'; -import { randomArrayItem } from './services'; +import { + AddPathToDemoDataOptions, + DemoTreeDataValue, + addTreeDataOptionsToDemoData, +} from './services/tree-data-generator'; -const dataCache = new LRUCache({ +const dataCache = new LRUCache({ max: 10, maxAge: 60 * 5 * 1e3, // 5 minutes }); export type DemoDataReturnType = { - data: DemoData; + data: DemoTreeDataValue; loading: boolean; setRowLength: (count: number) => void; loadNewData: () => void; @@ -23,36 +25,14 @@ export type DemoDataReturnType = { type DataSet = 'Commodity' | 'Employee'; -export interface DemoDataOptions { +export interface UseDemoDataOptions { dataSet: DataSet; rowLength: number; maxColumns?: number; editable?: boolean; - treeData?: { - /** - * The field used to generate the path - * If not defined, the tree data will not be built - */ - groupingField?: string; - - /** - * The depth of the tree - * @default: 1 - */ - depth?: number; - - /** - * The average amount of children in a node - * @default: 2 - */ - averageChildren?: number; - }; + treeData?: AddPathToDemoDataOptions; } -export interface DemoData - extends Pick, - GeneratedDemoData {} - // Generate fake data from a seed. // It's about x20 faster than getRealData. async function extrapolateSeed( @@ -110,7 +90,7 @@ const deepFreeze = (object: T): T => { return Object.freeze(object); }; -export const useDemoData = (options: DemoDataOptions): DemoDataReturnType => { +export const useDemoData = (options: UseDemoDataOptions): DemoDataReturnType => { const [rowLength, setRowLength] = React.useState(options.rowLength); const [index, setIndex] = React.useState(0); const [loading, setLoading] = React.useState(true); @@ -127,15 +107,15 @@ export const useDemoData = (options: DemoDataOptions): DemoDataReturnType => { return columns; }, [options.dataSet, options.editable, options.maxColumns]); - const treeDataDepth = options.treeData?.depth ?? 1; - const hasTreeData = treeDataDepth > 1 && options.treeData?.groupingField; - - const [data, setData] = React.useState(() => ({ - columns: getColumns(), - rows: [], - getTreeDataPath: hasTreeData ? (row) => row.path : undefined, - treeData: hasTreeData ? true : undefined, - })); + const [data, setData] = React.useState(() => + addTreeDataOptionsToDemoData( + { + columns: getColumns(), + rows: [], + }, + options.treeData, + ), + ); React.useEffect(() => { const cacheKey = `${options.dataSet}-${rowLength}-${index}-${options.maxColumns}`; @@ -154,7 +134,7 @@ export const useDemoData = (options: DemoDataOptions): DemoDataReturnType => { (async () => { setLoading(true); - let newData: DemoData; + let newData: DemoTreeDataValue; const columns = getColumns(); if (rowLength > 1000) { newData = await getRealData(1000, columns); @@ -163,74 +143,16 @@ export const useDemoData = (options: DemoDataOptions): DemoDataReturnType => { newData = await getRealData(rowLength, columns); } - const averageChildren = options.treeData?.averageChildren ?? 2; - - if (hasTreeData) { - const rowsByTreeDepth: Record< - number, - { [index: number]: { value: GridRowData; parentIndex: number | null } } - > = {}; - const rowsCount = newData.rows.length; - - const groupingCol = newData.columns.find( - (col) => col.field === options.treeData?.groupingField, - ); - - if (!groupingCol) { - throw new Error('MUI: The tree data grouping field does not exist'); - } - - for (let i = 0; i < rowsCount; i += 1) { - const row = newData.rows[i]; - - const currentChunk = - Math.floor((i * (averageChildren ** treeDataDepth - 1)) / rowsCount) + 1; - const currentDepth = Math.floor(Math.log(currentChunk) / Math.log(averageChildren)); - - if (!rowsByTreeDepth[currentDepth]) { - rowsByTreeDepth[currentDepth] = {}; - } - - rowsByTreeDepth[currentDepth][i] = { value: row, parentIndex: null }; - } - - for (let i = 0; i < treeDataDepth; i += 1) { - Object.keys(rowsByTreeDepth[i]).forEach((rowIndex) => { - const row = rowsByTreeDepth[i][Number(rowIndex)]; - const path: string[] = []; - let previousRow: { value: GridRowData; parentIndex: number | null } | null = null; - for (let k = i; k >= 0; k -= 1) { - let rowTemp: { value: GridRowData; parentIndex: number | null }; - if (k === i) { - if (i > 0) { - row.parentIndex = Number(randomArrayItem(Object.keys(rowsByTreeDepth[i - 1]))); - } - rowTemp = row; - } else { - rowTemp = rowsByTreeDepth[k][previousRow!.parentIndex!]; - } - - path.unshift(rowTemp.value[options.treeData?.groupingField!]); - - previousRow = rowTemp; - } - - row.value.path = path; - }); - } - - groupingCol.hide = true; - newData.groupingColDef = { - headerName: groupingCol.headerName ?? groupingCol.field, - }; - newData.getTreeDataPath = (row) => row.path; - newData.treeData = true; - } - if (!active) { return; } + newData = addTreeDataOptionsToDemoData(newData, { + maxDepth: options.treeData?.maxDepth, + groupingField: options.treeData?.groupingField, + averageChildren: options.treeData?.averageChildren, + }); + // It's quite slow. No need for it in production. if (process.env.NODE_ENV !== 'production') { deepFreeze(newData); @@ -249,10 +171,9 @@ export const useDemoData = (options: DemoDataOptions): DemoDataReturnType => { data.columns, options.dataSet, options.maxColumns, - treeDataDepth, + options.treeData?.maxDepth, options.treeData?.groupingField, options.treeData?.averageChildren, - hasTreeData, index, getColumns, ]); From 852993645d2a5dac84ac82a3f3260d9c1150f33d Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 18 Oct 2021 13:48:52 +0200 Subject: [PATCH 246/390] Handle new dataset in tree data --- .../grid/hooks/features/rows/useGridRows.ts | 27 +++++--- .../src/tests/treeData.DataGridPro.test.tsx | 67 ++++++++++++------- 2 files changed, 60 insertions(+), 34 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 405a9a08c4ba6..29c092d2e0be4 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -39,7 +39,8 @@ export type GridRowsInternalCacheState = Pick { @@ -108,7 +110,8 @@ const INITIAL_GRID_ROWS_INTERNAL_CACHE: GridRowsInternalCache = { propRowCount: undefined, propGetRowId: undefined, rowIds: [], - inputRows: [], + inputRowsBeforeUpdates: [], + inputRowsAfterUpdates: [], }, timeout: null, lastUpdateMs: 0, @@ -255,7 +258,7 @@ export const useGridRows = ( ...rowsCache.current.state, idRowsLookup, rowIds, - inputRows: rowIds.map((rowId) => idRowsLookup[rowId]), + inputRowsAfterUpdates: rowIds.map((rowId) => idRowsLookup[rowId]), }; throttledRowsChange(state, true); @@ -325,6 +328,11 @@ export const useGridRows = ( return; } + // The new rows have already been applied (most likely in the `GridEvents.rowGroupsPreProcessingChange` listener) + if (rowsCache.current.state.inputRowsBeforeUpdates === props.rows) { + return; + } + logger.debug(`Updating all rows, new length ${props.rows.length}`); throttledRowsChange( convertGridRowsPropToState(props.rows, props.rowCount, props.getRowId), @@ -334,11 +342,12 @@ export const useGridRows = ( const handleGroupRows = React.useCallback(() => { logger.info(`Row grouping pre-processing have changed, regenerating the row tree`); - throttledRowsChange( - convertGridRowsPropToState(rowsCache.current.state.inputRows, props.rowCount, props.getRowId), - false, - ); - }, [logger, throttledRowsChange, props.rowCount, props.getRowId]); + const rows = + rowsCache.current.state.inputRowsBeforeUpdates === props.rows + ? rowsCache.current.state.inputRowsAfterUpdates + : props.rows; + throttledRowsChange(convertGridRowsPropToState(rows, props.rowCount, props.getRowId), false); + }, [logger, throttledRowsChange, props.rowCount, props.getRowId, props.rows]); useGridApiEventHandler(apiRef, GridEvents.rowGroupsPreProcessingChange, handleGroupRows); diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index 27d8e8d62f6f7..aed2395f25258 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -19,23 +19,23 @@ import { const isJSDOM = /jsdom/.test(window.navigator.userAgent); const rowsWithoutGap: GridRowsProp = [ - { name: 'A', value: 10 }, - { name: 'A.A', value: 4 }, - { name: 'A.B', value: 6 }, - { name: 'B', value: 20 }, - { name: 'B.A', value: 12 }, - { name: 'B.B', value: 8 }, - { name: 'B.B.A', value: 8 }, - { name: 'B.B.A.A', value: 8 }, - { name: 'C', value: 5 }, + { name: 'A' }, + { name: 'A.A' }, + { name: 'A.B' }, + { name: 'B' }, + { name: 'B.A' }, + { name: 'B.B' }, + { name: 'B.B.A' }, + { name: 'B.B.A.A' }, + { name: 'C' }, ]; const rowsWithGap: GridRowsProp = [ - { name: 'A', value: 10 }, - { name: 'A.B', value: 6 }, - { name: 'A.A', value: 4 }, - { name: 'B.A', value: 3 }, - { name: 'B.B', value: 7 }, + { name: 'A' }, + { name: 'A.B' }, + { name: 'A.A' }, + { name: 'B.A' }, + { name: 'B.B' }, ]; const baselineProps: DataGridProProps = { @@ -46,10 +46,6 @@ const baselineProps: DataGridProProps = { field: 'name', width: 200, }, - { - field: 'value', - type: 'number', - }, ], treeData: true, getTreeDataPath: (row) => row.name.split('.'), @@ -75,7 +71,7 @@ describe(' - Tree Data', () => { describe('prop: treeData', () => { it('should support tree data toggling', () => { const { setProps } = render(); - expect(getColumnHeadersTextContent()).to.deep.equal(['name', 'value']); + expect(getColumnHeadersTextContent()).to.deep.equal(['name']); expect(getColumnValues(0)).to.deep.equal([ 'A', 'A.A', @@ -88,10 +84,10 @@ describe(' - Tree Data', () => { 'C', ]); setProps({ treeData: true }); - expect(getColumnHeadersTextContent()).to.deep.equal(['Group', 'name', 'value']); + expect(getColumnHeadersTextContent()).to.deep.equal(['Group', 'name']); expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); setProps({ treeData: false }); - expect(getColumnHeadersTextContent()).to.deep.equal(['name', 'value']); + expect(getColumnHeadersTextContent()).to.deep.equal(['name']); expect(getColumnValues(0)).to.deep.equal([ 'A', 'A.A', @@ -107,7 +103,7 @@ describe(' - Tree Data', () => { it('should support enabling treeData after apiRef.current.updateRows has modified the rows', async () => { const { setProps } = render(); - expect(getColumnHeadersTextContent()).to.deep.equal(['name', 'value']); + expect(getColumnHeadersTextContent()).to.deep.equal(['name']); expect(getColumnValues(0)).to.deep.equal([ 'A', 'A.A', @@ -131,11 +127,32 @@ describe(' - Tree Data', () => { 'C', ]); setProps({ treeData: true }); - expect(getColumnHeadersTextContent()).to.deep.equal(['Group', 'name', 'value']); + expect(getColumnHeadersTextContent()).to.deep.equal(['Group', 'name']); expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); fireEvent.click(getCell(0, 0).querySelector('button')); expect(getColumnValues(1)).to.deep.equal(['A', 'A.B', 'B', 'C']); }); + + it('should support new dataset', () => { + const { setProps } = render(); + setProps({ + rows: [ + { nameBis: 'A' }, + { nameBis: 'A.B' }, + { nameBis: 'A.A' }, + { nameBis: 'B.A' }, + { nameBis: 'B.B' }, + ], + columns: [ + { + field: 'nameBis', + width: 200, + }, + ], + getTreeDataPath: (row) => row.nameBis.split('.'), + getRowId: (row) => row.nameBis, + }); + }); }); describe('prop: getTreeDataPath', () => { @@ -207,7 +224,7 @@ describe(' - Tree Data', () => { describe('prop: groupingColDef', () => { it('should set the custom headerName', () => { render(); - expect(getColumnHeadersTextContent()).to.deep.equal(['Custom header name', 'name', 'value']); + expect(getColumnHeadersTextContent()).to.deep.equal(['Custom header name', 'name']); }); }); @@ -215,7 +232,7 @@ describe(' - Tree Data', () => { it('should add a grouping column', () => { render(); const columnsHeader = getColumnHeadersTextContent(); - expect(columnsHeader).to.deep.equal(['Group', 'name', 'value']); + expect(columnsHeader).to.deep.equal(['Group', 'name']); }); it('should render a toggling icon only when a row has children', () => { From 7e7b9dbb4e6d416e1c29016b09653bf299c665fd Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 18 Oct 2021 13:52:54 +0200 Subject: [PATCH 247/390] Fix --- .../x-grid/src/tests/treeData.DataGridPro.test.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index aed2395f25258..82c95a8bf31c6 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -137,11 +137,11 @@ describe(' - Tree Data', () => { const { setProps } = render(); setProps({ rows: [ - { nameBis: 'A' }, - { nameBis: 'A.B' }, - { nameBis: 'A.A' }, - { nameBis: 'B.A' }, - { nameBis: 'B.B' }, + { nameBis: '1' }, + { nameBis: '1.1' }, + { nameBis: '1.2' }, + { nameBis: '2' }, + { nameBis: '2.1' }, ], columns: [ { @@ -152,6 +152,8 @@ describe(' - Tree Data', () => { getTreeDataPath: (row) => row.nameBis.split('.'), getRowId: (row) => row.nameBis, }); + expect(getColumnHeadersTextContent()).to.deep.equal(['Group', 'nameBis']); + expect(getColumnValues(1)).to.deep.equal(['1', '2']); }); }); From 7e8ec5911b47a8bcb7651dec880732a70ad6ecc1 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 18 Oct 2021 14:00:11 +0200 Subject: [PATCH 248/390] Add test --- .../src/tests/treeData.DataGridPro.test.tsx | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index 82c95a8bf31c6..61970362d2a47 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -12,6 +12,7 @@ import { DataGridPro, DataGridProProps, GridApiRef, + GridLinkOperator, GridRowsProp, useGridApiRef, } from '@mui/x-data-grid-pro'; @@ -238,11 +239,23 @@ describe(' - Tree Data', () => { }); it('should render a toggling icon only when a row has children', () => { - render(); - // No children - expect(getCell(2, 0).querySelectorAll('button')).to.have.length(0); - // Some children - expect(getCell(0, 0).querySelectorAll('button')).to.have.length(1); + render( + , + ); + expect(getColumnValues(1)).to.deep.equal(['A', 'B']); + // No children after filtering + expect(getCell(0, 0).querySelectorAll('button')).to.have.length(0); + // Some children after filtering + expect(getCell(1, 0).querySelectorAll('button')).to.have.length(1); }); it('should toggle expansion when clicking on grouping column icon', () => { From 665991eee532229d84de246d8a0152e0b008d3e0 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 18 Oct 2021 15:28:28 +0200 Subject: [PATCH 249/390] Fix --- .../src/stories/playground/real-data-demo.stories.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/storybook/src/stories/playground/real-data-demo.stories.tsx b/packages/storybook/src/stories/playground/real-data-demo.stories.tsx index bee45ef9d3e78..d46ae6b1b41cf 100644 --- a/packages/storybook/src/stories/playground/real-data-demo.stories.tsx +++ b/packages/storybook/src/stories/playground/real-data-demo.stories.tsx @@ -2,7 +2,7 @@ import { DataGrid, GridToolbar } from '@mui/x-data-grid'; import * as React from 'react'; import { Story, Meta, DecoratorFn } from '@storybook/react'; import { DataGridProProps, GridPreferencePanelsValue, DataGridPro } from '@mui/x-data-grid-pro'; -import { useDemoData, DemoDataOptions } from '@mui/x-data-grid-generator'; +import { useDemoData, UseDemoDataOptions } from '@mui/x-data-grid-generator'; import { useData } from '../../hooks/useData'; import { ColumnMenuComponent, @@ -65,7 +65,7 @@ export default { }, } as Meta; -const DemoTemplate: Story = ({ +const DemoTemplate: Story = ({ rows, columns, dataSet, From 7a24dd52c3dd32f95b73e4f93115950347cc18f5 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 18 Oct 2021 16:01:00 +0200 Subject: [PATCH 250/390] Fix --- .../grid/x-grid-data-generator/src/services/random-generator.ts | 2 +- scripts/sizeSnapshot/create.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/grid/x-grid-data-generator/src/services/random-generator.ts b/packages/grid/x-grid-data-generator/src/services/random-generator.ts index ba8946c309569..55ec976e807b0 100644 --- a/packages/grid/x-grid-data-generator/src/services/random-generator.ts +++ b/packages/grid/x-grid-data-generator/src/services/random-generator.ts @@ -101,7 +101,7 @@ function datePast(years?: number, refDate?: string) { export const random = (min: number, max: number): number => chance.floating({ min, max }); export const randomInt = (min: number, max: number): number => - Math.floor(Math.random() * (max - min + 1) + min); + Math.floor(random(0, 1) * (max - min + 1) + min); export const randomPrice = (min = 0, max = 100000): number => Number(random(min, max).toFixed(2)); export const randomRate = (): number => random(0, 1); export const randomDate = (start: Date, end: Date) => diff --git a/scripts/sizeSnapshot/create.js b/scripts/sizeSnapshot/create.js index 36099518ee1d5..4551d54ec5035 100644 --- a/scripts/sizeSnapshot/create.js +++ b/scripts/sizeSnapshot/create.js @@ -29,7 +29,7 @@ async function getWebpackSizes(webpackEnvironment) { errors: true, }); throw new Error( - `The following errors occured during bundling of ${Object.keys( + `The following errors occurred during bundling of ${Object.keys( entrypoints, )} with webpack: \n${errors.join('\n')}`, ); From 7c6fd413536ded6638d63e6c6f443e0821ed361a Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 18 Oct 2021 17:30:31 +0200 Subject: [PATCH 251/390] Test e2e --- docs/pages/playground.tsx | 41 +++++--- test/e2e/index.test.ts | 214 +++++++++++++++++++------------------- 2 files changed, 133 insertions(+), 122 deletions(-) diff --git a/docs/pages/playground.tsx b/docs/pages/playground.tsx index a977a29ffa472..a32ebf86711b0 100644 --- a/docs/pages/playground.tsx +++ b/docs/pages/playground.tsx @@ -1,22 +1,33 @@ import * as React from 'react'; -import { DataGridPro } from '@mui/x-data-grid-pro'; -import { useDemoData } from '@mui/x-data-grid-generator'; +import { DataGrid } from '@mui/x-data-grid'; -export default function BasicTreeData() { - const { data, loading } = useDemoData({ - dataSet: 'Employee', - rowLength: 1000, - maxColumns: 6, - treeData: { - maxDepth: 3, - averageChildren: 10, - groupingField: 'name', +const baselineProps = { + rows: [ + { + id: 0, + brand: 'Nike', }, - }); + { + id: 1, + brand: 'Adidas', + }, + { + id: 2, + brand: 'Puma', + }, + ], + columns: [{ field: 'brand', width: 100 }], +}; +export default function KeyboardNavigationFocus() { return ( -
- -
+ + +
+ +
+
); } diff --git a/test/e2e/index.test.ts b/test/e2e/index.test.ts index b9eb902238d9d..174aba7f25070 100644 --- a/test/e2e/index.test.ts +++ b/test/e2e/index.test.ts @@ -105,112 +105,112 @@ describe('e2e', () => { await page.evaluate(() => document.activeElement?.getAttribute('data-testid')), ).to.equal('initial-focus'); }); - - it('should display the rows', async () => { - await renderFixture('DataGrid/ConcurrentReactUpdate'); - expect( - await page.evaluate(() => - Array.from(document.querySelectorAll('[role="cell"]')).map((node) => node.textContent), - ), - ).to.deep.equal(['1', '2']); - }); - - it('should work with a select as the edit cell', async () => { - await renderFixture('DataGrid/SelectEditCell'); - await page.dblclick('"Nike"'); - await page.click('"Gucci"'); - expect( - await page.evaluate(() => { - const selector = '[role="row"][data-rowindex="0"] [role="cell"][data-colindex="0"]'; - return document.querySelector(selector)!.textContent!; - }), - ).to.equal('Gucci'); - }); - - it('should reorder columns by dropping into the header', async () => { - await renderFixture('DataGrid/ColumnReorder'); - expect( - await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!), - ).to.equal('brandyear'); - const brand = await page.$('[role="columnheader"][aria-colindex="1"] > [draggable]'); - const brandBoundingBox = await brand?.boundingBox(); - const year = await page.$('[role="columnheader"][aria-colindex="2"] > [draggable]'); - const yearBoundingBox = await year?.boundingBox(); - if (brandBoundingBox && yearBoundingBox) { - // Based on https://stackoverflow.com/a/64746679/2801714 - await page.mouse.move( - brandBoundingBox.x + brandBoundingBox.width / 2, - brandBoundingBox.y + brandBoundingBox.height / 2, - { steps: 5 }, - ); - await page.mouse.down(); - await page.mouse.move( - yearBoundingBox.x + yearBoundingBox.width / 2, - yearBoundingBox.y + yearBoundingBox.height / 2, - { steps: 5 }, - ); - await page.mouse.up(); - } - expect( - await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!), - ).to.equal('yearbrand'); - }); - - it('should reorder columns by dropping into the body', async () => { - await renderFixture('DataGrid/ColumnReorder'); - expect( - await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!), - ).to.equal('brandyear'); - const brand = await page.$('[role="columnheader"][aria-colindex="1"] > [draggable]'); - const brandBoundingBox = await brand?.boundingBox(); - const cell = await page.$('[role="row"][data-rowindex="0"] [role="cell"][data-colindex="1"]'); - const cellBoundingBox = await cell?.boundingBox(); - if (brandBoundingBox && cellBoundingBox) { - // Based on https://stackoverflow.com/a/64746679/2801714 - await page.mouse.move( - brandBoundingBox.x + brandBoundingBox.width / 2, - brandBoundingBox.y + brandBoundingBox.height / 2, - { steps: 5 }, - ); - await page.mouse.down(); - await page.mouse.move( - cellBoundingBox.x + cellBoundingBox.width / 2, - cellBoundingBox.y + cellBoundingBox.height / 2, - { steps: 5 }, - ); - await page.mouse.up(); - } - expect( - await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!), - ).to.equal('yearbrand'); - }); - - it('should select one row', async () => { - await renderFixture('DataGrid/CheckboxSelection'); - await page.click('[role="row"][data-rowindex="0"] [role="cell"] input'); - expect( - await page.evaluate( - () => document.querySelector('[role="row"][data-rowindex="0"]')!.className!, - ), - ).to.contain('Mui-selected'); - }); - - it('should not scroll when changing the selected row', async () => { - await renderFixture('DataGrid/RowSelection'); - await page.click('[role="row"][data-rowindex="0"] [role="cell"]'); - await page.evaluate(() => - document.querySelector('[role="row"][data-rowindex="3"] [role="cell"]')!.scrollIntoView(), - ); - const scrollTop = await page.evaluate( - () => document.querySelector('.MuiDataGrid-virtualScroller')!.scrollTop!, - ); - expect(scrollTop).not.to.equal(0); - await page.click('[role="row"][data-rowindex="3"] [role="cell"]'); - expect( - await page.evaluate( - () => document.querySelector('.MuiDataGrid-virtualScroller')!.scrollTop!, - ), - ).to.equal(scrollTop); - }); + // + // it('should display the rows', async () => { + // await renderFixture('DataGrid/ConcurrentReactUpdate'); + // expect( + // await page.evaluate(() => + // Array.from(document.querySelectorAll('[role="cell"]')).map((node) => node.textContent), + // ), + // ).to.deep.equal(['1', '2']); + // }); + // + // it('should work with a select as the edit cell', async () => { + // await renderFixture('DataGrid/SelectEditCell'); + // await page.dblclick('"Nike"'); + // await page.click('"Gucci"'); + // expect( + // await page.evaluate(() => { + // const selector = '[role="row"][data-rowindex="0"] [role="cell"][data-colindex="0"]'; + // return document.querySelector(selector)!.textContent!; + // }), + // ).to.equal('Gucci'); + // }); + // + // it('should reorder columns by dropping into the header', async () => { + // await renderFixture('DataGrid/ColumnReorder'); + // expect( + // await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!), + // ).to.equal('brandyear'); + // const brand = await page.$('[role="columnheader"][aria-colindex="1"] > [draggable]'); + // const brandBoundingBox = await brand?.boundingBox(); + // const year = await page.$('[role="columnheader"][aria-colindex="2"] > [draggable]'); + // const yearBoundingBox = await year?.boundingBox(); + // if (brandBoundingBox && yearBoundingBox) { + // // Based on https://stackoverflow.com/a/64746679/2801714 + // await page.mouse.move( + // brandBoundingBox.x + brandBoundingBox.width / 2, + // brandBoundingBox.y + brandBoundingBox.height / 2, + // { steps: 5 }, + // ); + // await page.mouse.down(); + // await page.mouse.move( + // yearBoundingBox.x + yearBoundingBox.width / 2, + // yearBoundingBox.y + yearBoundingBox.height / 2, + // { steps: 5 }, + // ); + // await page.mouse.up(); + // } + // expect( + // await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!), + // ).to.equal('yearbrand'); + // }); + // + // it('should reorder columns by dropping into the body', async () => { + // await renderFixture('DataGrid/ColumnReorder'); + // expect( + // await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!), + // ).to.equal('brandyear'); + // const brand = await page.$('[role="columnheader"][aria-colindex="1"] > [draggable]'); + // const brandBoundingBox = await brand?.boundingBox(); + // const cell = await page.$('[role="row"][data-rowindex="0"] [role="cell"][data-colindex="1"]'); + // const cellBoundingBox = await cell?.boundingBox(); + // if (brandBoundingBox && cellBoundingBox) { + // // Based on https://stackoverflow.com/a/64746679/2801714 + // await page.mouse.move( + // brandBoundingBox.x + brandBoundingBox.width / 2, + // brandBoundingBox.y + brandBoundingBox.height / 2, + // { steps: 5 }, + // ); + // await page.mouse.down(); + // await page.mouse.move( + // cellBoundingBox.x + cellBoundingBox.width / 2, + // cellBoundingBox.y + cellBoundingBox.height / 2, + // { steps: 5 }, + // ); + // await page.mouse.up(); + // } + // expect( + // await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!), + // ).to.equal('yearbrand'); + // }); + // + // it('should select one row', async () => { + // await renderFixture('DataGrid/CheckboxSelection'); + // await page.click('[role="row"][data-rowindex="0"] [role="cell"] input'); + // expect( + // await page.evaluate( + // () => document.querySelector('[role="row"][data-rowindex="0"]')!.className!, + // ), + // ).to.contain('Mui-selected'); + // }); + // + // it('should not scroll when changing the selected row', async () => { + // await renderFixture('DataGrid/RowSelection'); + // await page.click('[role="row"][data-rowindex="0"] [role="cell"]'); + // await page.evaluate(() => + // document.querySelector('[role="row"][data-rowindex="3"] [role="cell"]')!.scrollIntoView(), + // ); + // const scrollTop = await page.evaluate( + // () => document.querySelector('.MuiDataGrid-virtualScroller')!.scrollTop!, + // ); + // expect(scrollTop).not.to.equal(0); + // await page.click('[role="row"][data-rowindex="3"] [role="cell"]'); + // expect( + // await page.evaluate( + // () => document.querySelector('.MuiDataGrid-virtualScroller')!.scrollTop!, + // ), + // ).to.equal(scrollTop); + // }); }); }); From 51c8b15d36b62a373ffeede56e8594018c88e179 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 18 Oct 2021 17:38:15 +0200 Subject: [PATCH 252/390] Fix --- test/e2e/index.test.ts | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/test/e2e/index.test.ts b/test/e2e/index.test.ts index 174aba7f25070..9bab032388865 100644 --- a/test/e2e/index.test.ts +++ b/test/e2e/index.test.ts @@ -91,7 +91,10 @@ describe('e2e', () => { 'cell', ); await page.keyboard.press('Tab'); - expect(await page.evaluate(() => document.activeElement?.textContent)).to.equal('100'); + console.log('Before') + const val = await page.evaluate(() => document.activeElement?.textContent) + console.log('After', val) + expect(val).to.equal('100'); expect(await page.evaluate(() => document.activeElement?.getAttribute('role'))).to.equal( 'button', ); @@ -105,15 +108,16 @@ describe('e2e', () => { await page.evaluate(() => document.activeElement?.getAttribute('data-testid')), ).to.equal('initial-focus'); }); - // - // it('should display the rows', async () => { - // await renderFixture('DataGrid/ConcurrentReactUpdate'); - // expect( - // await page.evaluate(() => - // Array.from(document.querySelectorAll('[role="cell"]')).map((node) => node.textContent), - // ), - // ).to.deep.equal(['1', '2']); - // }); + + it('should display the rows', async () => { + console.log('Next test') + await renderFixture('DataGrid/ConcurrentReactUpdate'); + expect( + await page.evaluate(() => + Array.from(document.querySelectorAll('[role="cell"]')).map((node) => node.textContent), + ), + ).to.deep.equal(['1', '2']); + }); // // it('should work with a select as the edit cell', async () => { // await renderFixture('DataGrid/SelectEditCell'); From 95aa245c21655896b99e2d5041b076d2fdcfccc2 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 18 Oct 2021 17:40:58 +0200 Subject: [PATCH 253/390] Fix --- test/e2e/index.test.ts | 205 +++++++++++++++++++++-------------------- 1 file changed, 106 insertions(+), 99 deletions(-) diff --git a/test/e2e/index.test.ts b/test/e2e/index.test.ts index 9bab032388865..e1fe0732de148 100644 --- a/test/e2e/index.test.ts +++ b/test/e2e/index.test.ts @@ -63,6 +63,7 @@ describe('e2e', () => { describe('', () => { it('should select the first column header when pressing tab key', async () => { + console.log('Start 1') await renderFixture('DataGrid/KeyboardNavigationFocus'); expect( @@ -74,6 +75,7 @@ describe('e2e', () => { }); it('should implement the roving tabindex pattern', async () => { + console.log('Start 2') await renderFixture('DataGrid/KeyboardNavigationFocus'); expect( @@ -110,7 +112,7 @@ describe('e2e', () => { }); it('should display the rows', async () => { - console.log('Next test') + console.log('Start 3') await renderFixture('DataGrid/ConcurrentReactUpdate'); expect( await page.evaluate(() => @@ -118,103 +120,108 @@ describe('e2e', () => { ), ).to.deep.equal(['1', '2']); }); - // - // it('should work with a select as the edit cell', async () => { - // await renderFixture('DataGrid/SelectEditCell'); - // await page.dblclick('"Nike"'); - // await page.click('"Gucci"'); - // expect( - // await page.evaluate(() => { - // const selector = '[role="row"][data-rowindex="0"] [role="cell"][data-colindex="0"]'; - // return document.querySelector(selector)!.textContent!; - // }), - // ).to.equal('Gucci'); - // }); - // - // it('should reorder columns by dropping into the header', async () => { - // await renderFixture('DataGrid/ColumnReorder'); - // expect( - // await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!), - // ).to.equal('brandyear'); - // const brand = await page.$('[role="columnheader"][aria-colindex="1"] > [draggable]'); - // const brandBoundingBox = await brand?.boundingBox(); - // const year = await page.$('[role="columnheader"][aria-colindex="2"] > [draggable]'); - // const yearBoundingBox = await year?.boundingBox(); - // if (brandBoundingBox && yearBoundingBox) { - // // Based on https://stackoverflow.com/a/64746679/2801714 - // await page.mouse.move( - // brandBoundingBox.x + brandBoundingBox.width / 2, - // brandBoundingBox.y + brandBoundingBox.height / 2, - // { steps: 5 }, - // ); - // await page.mouse.down(); - // await page.mouse.move( - // yearBoundingBox.x + yearBoundingBox.width / 2, - // yearBoundingBox.y + yearBoundingBox.height / 2, - // { steps: 5 }, - // ); - // await page.mouse.up(); - // } - // expect( - // await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!), - // ).to.equal('yearbrand'); - // }); - // - // it('should reorder columns by dropping into the body', async () => { - // await renderFixture('DataGrid/ColumnReorder'); - // expect( - // await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!), - // ).to.equal('brandyear'); - // const brand = await page.$('[role="columnheader"][aria-colindex="1"] > [draggable]'); - // const brandBoundingBox = await brand?.boundingBox(); - // const cell = await page.$('[role="row"][data-rowindex="0"] [role="cell"][data-colindex="1"]'); - // const cellBoundingBox = await cell?.boundingBox(); - // if (brandBoundingBox && cellBoundingBox) { - // // Based on https://stackoverflow.com/a/64746679/2801714 - // await page.mouse.move( - // brandBoundingBox.x + brandBoundingBox.width / 2, - // brandBoundingBox.y + brandBoundingBox.height / 2, - // { steps: 5 }, - // ); - // await page.mouse.down(); - // await page.mouse.move( - // cellBoundingBox.x + cellBoundingBox.width / 2, - // cellBoundingBox.y + cellBoundingBox.height / 2, - // { steps: 5 }, - // ); - // await page.mouse.up(); - // } - // expect( - // await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!), - // ).to.equal('yearbrand'); - // }); - // - // it('should select one row', async () => { - // await renderFixture('DataGrid/CheckboxSelection'); - // await page.click('[role="row"][data-rowindex="0"] [role="cell"] input'); - // expect( - // await page.evaluate( - // () => document.querySelector('[role="row"][data-rowindex="0"]')!.className!, - // ), - // ).to.contain('Mui-selected'); - // }); - // - // it('should not scroll when changing the selected row', async () => { - // await renderFixture('DataGrid/RowSelection'); - // await page.click('[role="row"][data-rowindex="0"] [role="cell"]'); - // await page.evaluate(() => - // document.querySelector('[role="row"][data-rowindex="3"] [role="cell"]')!.scrollIntoView(), - // ); - // const scrollTop = await page.evaluate( - // () => document.querySelector('.MuiDataGrid-virtualScroller')!.scrollTop!, - // ); - // expect(scrollTop).not.to.equal(0); - // await page.click('[role="row"][data-rowindex="3"] [role="cell"]'); - // expect( - // await page.evaluate( - // () => document.querySelector('.MuiDataGrid-virtualScroller')!.scrollTop!, - // ), - // ).to.equal(scrollTop); - // }); + + it('should work with a select as the edit cell', async () => { + console.log('Start 4') + await renderFixture('DataGrid/SelectEditCell'); + await page.dblclick('"Nike"'); + await page.click('"Gucci"'); + expect( + await page.evaluate(() => { + const selector = '[role="row"][data-rowindex="0"] [role="cell"][data-colindex="0"]'; + return document.querySelector(selector)!.textContent!; + }), + ).to.equal('Gucci'); + }); + + it('should reorder columns by dropping into the header', async () => { + console.log('Start 5') + await renderFixture('DataGrid/ColumnReorder'); + expect( + await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!), + ).to.equal('brandyear'); + const brand = await page.$('[role="columnheader"][aria-colindex="1"] > [draggable]'); + const brandBoundingBox = await brand?.boundingBox(); + const year = await page.$('[role="columnheader"][aria-colindex="2"] > [draggable]'); + const yearBoundingBox = await year?.boundingBox(); + if (brandBoundingBox && yearBoundingBox) { + // Based on https://stackoverflow.com/a/64746679/2801714 + await page.mouse.move( + brandBoundingBox.x + brandBoundingBox.width / 2, + brandBoundingBox.y + brandBoundingBox.height / 2, + { steps: 5 }, + ); + await page.mouse.down(); + await page.mouse.move( + yearBoundingBox.x + yearBoundingBox.width / 2, + yearBoundingBox.y + yearBoundingBox.height / 2, + { steps: 5 }, + ); + await page.mouse.up(); + } + expect( + await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!), + ).to.equal('yearbrand'); + }); + + it('should reorder columns by dropping into the body', async () => { + console.log('Start 6') + await renderFixture('DataGrid/ColumnReorder'); + expect( + await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!), + ).to.equal('brandyear'); + const brand = await page.$('[role="columnheader"][aria-colindex="1"] > [draggable]'); + const brandBoundingBox = await brand?.boundingBox(); + const cell = await page.$('[role="row"][data-rowindex="0"] [role="cell"][data-colindex="1"]'); + const cellBoundingBox = await cell?.boundingBox(); + if (brandBoundingBox && cellBoundingBox) { + // Based on https://stackoverflow.com/a/64746679/2801714 + await page.mouse.move( + brandBoundingBox.x + brandBoundingBox.width / 2, + brandBoundingBox.y + brandBoundingBox.height / 2, + { steps: 5 }, + ); + await page.mouse.down(); + await page.mouse.move( + cellBoundingBox.x + cellBoundingBox.width / 2, + cellBoundingBox.y + cellBoundingBox.height / 2, + { steps: 5 }, + ); + await page.mouse.up(); + } + expect( + await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!), + ).to.equal('yearbrand'); + }); + + it('should select one row', async () => { + console.log('Start 7') + await renderFixture('DataGrid/CheckboxSelection'); + await page.click('[role="row"][data-rowindex="0"] [role="cell"] input'); + expect( + await page.evaluate( + () => document.querySelector('[role="row"][data-rowindex="0"]')!.className!, + ), + ).to.contain('Mui-selected'); + }); + + it('should not scroll when changing the selected row', async () => { + console.log('Start 8') + await renderFixture('DataGrid/RowSelection'); + await page.click('[role="row"][data-rowindex="0"] [role="cell"]'); + await page.evaluate(() => + document.querySelector('[role="row"][data-rowindex="3"] [role="cell"]')!.scrollIntoView(), + ); + const scrollTop = await page.evaluate( + () => document.querySelector('.MuiDataGrid-virtualScroller')!.scrollTop!, + ); + expect(scrollTop).not.to.equal(0); + await page.click('[role="row"][data-rowindex="3"] [role="cell"]'); + expect( + await page.evaluate( + () => document.querySelector('.MuiDataGrid-virtualScroller')!.scrollTop!, + ), + ).to.equal(scrollTop); + }); }); }); From 7c4f5d6725c0688abff215d1aab5351c15654a5b Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 18 Oct 2021 17:52:44 +0200 Subject: [PATCH 254/390] Fix --- test/e2e/index.test.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/e2e/index.test.ts b/test/e2e/index.test.ts index e1fe0732de148..f26e9265b8bbb 100644 --- a/test/e2e/index.test.ts +++ b/test/e2e/index.test.ts @@ -93,10 +93,7 @@ describe('e2e', () => { 'cell', ); await page.keyboard.press('Tab'); - console.log('Before') - const val = await page.evaluate(() => document.activeElement?.textContent) - console.log('After', val) - expect(val).to.equal('100'); + expect(await page.evaluate(() => document.activeElement?.textContent)).to.equal('100'); expect(await page.evaluate(() => document.activeElement?.getAttribute('role'))).to.equal( 'button', ); From 926da49d0e1fa6c16049ffa4382214a1a88bd653 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 18 Oct 2021 17:59:27 +0200 Subject: [PATCH 255/390] Fix --- test/e2e/index.test.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/test/e2e/index.test.ts b/test/e2e/index.test.ts index f26e9265b8bbb..b9eb902238d9d 100644 --- a/test/e2e/index.test.ts +++ b/test/e2e/index.test.ts @@ -63,7 +63,6 @@ describe('e2e', () => { describe('', () => { it('should select the first column header when pressing tab key', async () => { - console.log('Start 1') await renderFixture('DataGrid/KeyboardNavigationFocus'); expect( @@ -75,7 +74,6 @@ describe('e2e', () => { }); it('should implement the roving tabindex pattern', async () => { - console.log('Start 2') await renderFixture('DataGrid/KeyboardNavigationFocus'); expect( @@ -109,7 +107,6 @@ describe('e2e', () => { }); it('should display the rows', async () => { - console.log('Start 3') await renderFixture('DataGrid/ConcurrentReactUpdate'); expect( await page.evaluate(() => @@ -119,7 +116,6 @@ describe('e2e', () => { }); it('should work with a select as the edit cell', async () => { - console.log('Start 4') await renderFixture('DataGrid/SelectEditCell'); await page.dblclick('"Nike"'); await page.click('"Gucci"'); @@ -132,7 +128,6 @@ describe('e2e', () => { }); it('should reorder columns by dropping into the header', async () => { - console.log('Start 5') await renderFixture('DataGrid/ColumnReorder'); expect( await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!), @@ -162,7 +157,6 @@ describe('e2e', () => { }); it('should reorder columns by dropping into the body', async () => { - console.log('Start 6') await renderFixture('DataGrid/ColumnReorder'); expect( await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!), @@ -192,7 +186,6 @@ describe('e2e', () => { }); it('should select one row', async () => { - console.log('Start 7') await renderFixture('DataGrid/CheckboxSelection'); await page.click('[role="row"][data-rowindex="0"] [role="cell"] input'); expect( @@ -203,7 +196,6 @@ describe('e2e', () => { }); it('should not scroll when changing the selected row', async () => { - console.log('Start 8') await renderFixture('DataGrid/RowSelection'); await page.click('[role="row"][data-rowindex="0"] [role="cell"]'); await page.evaluate(() => From 963cf74e590525edba7e14924394592ece72c851 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 19 Oct 2021 11:33:20 +0200 Subject: [PATCH 256/390] Improve doc --- docs/pages/playground.tsx | 151 ++++++++++++++--- .../data-grid/group-pivot/BasicTreeData.js | 115 ++++++++++--- .../data-grid/group-pivot/BasicTreeData.tsx | 115 ++++++++++--- .../CustomGroupingColumnTreeData.js | 133 +++++++++++++-- .../CustomGroupingColumnTreeData.tsx | 137 +++++++++++++-- .../DefaultGroupingExpansionDepthTreeData.js | 120 +++++++++++-- .../DefaultGroupingExpansionDepthTreeData.tsx | 122 ++++++++++++-- .../DisableChildrenFilteringTreeData.js | 151 +++++++++++++++-- .../DisableChildrenFilteringTreeData.tsx | 159 +++++++++++++++--- .../DisableChildrenSortingTreeData.js | 150 ++++++++++++++--- .../DisableChildrenSortingTreeData.tsx | 156 ++++++++++++++--- .../data-grid/group-pivot/FillerTreeData.js | 31 ---- .../data-grid/group-pivot/FillerTreeData.tsx | 33 ---- .../group-pivot/SetRowExpansionTreeData.js | 6 +- .../group-pivot/SetRowExpansionTreeData.tsx | 6 +- .../data-grid/group-pivot/TreeDataWithGap.js | 104 ++++++++++++ .../data-grid/group-pivot/TreeDataWithGap.tsx | 104 ++++++++++++ .../data-grid/group-pivot/group-pivot.md | 70 ++++---- .../components/data-grid/overview/overview.md | 2 +- .../_modules_/grid/components/GridRow.tsx | 2 +- .../cell/GridTreeDataGroupingCell.tsx | 4 +- .../grid/constants/localeTextConstants.ts | 2 +- .../hooks/features/filter/useGridFilter.ts | 4 +- .../grid/hooks/features/rows/useGridRows.ts | 10 +- .../features/treeData/useGridTreeData.ts | 6 +- packages/grid/_modules_/grid/locales/frFR.ts | 2 +- .../grid/models/api/gridLocaleTextApi.ts | 2 +- .../_modules_/grid/models/api/gridRowApi.ts | 4 +- 28 files changed, 1592 insertions(+), 309 deletions(-) delete mode 100644 docs/src/pages/components/data-grid/group-pivot/FillerTreeData.js delete mode 100644 docs/src/pages/components/data-grid/group-pivot/FillerTreeData.tsx create mode 100644 docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.js create mode 100644 docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.tsx diff --git a/docs/pages/playground.tsx b/docs/pages/playground.tsx index a32ebf86711b0..fb8e66bf94694 100644 --- a/docs/pages/playground.tsx +++ b/docs/pages/playground.tsx @@ -1,33 +1,130 @@ import * as React from 'react'; -import { DataGrid } from '@mui/x-data-grid'; +import { DataGridPro, GridColumns, GridRowsProp, GridSortModel } from '@mui/x-data-grid-pro'; +import Stack from '@mui/material/Stack'; +import Button from '@mui/material/Button'; -const baselineProps = { - rows: [ - { - id: 0, - brand: 'Nike', - }, - { - id: 1, - brand: 'Adidas', - }, - { - id: 2, - brand: 'Puma', - }, - ], - columns: [{ field: 'brand', width: 100 }], -}; +const rows: GridRowsProp = [ + { + hierarchy: ['Sarah', 'Thomas'], + jobTitle: 'Head of Sales', + recruitmentDate: new Date(2017, 3, 4), + id: 1, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Robert'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 11, 20), + id: 2, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Karen'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 10, 14), + id: 3, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Nancy'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2018, 3, 29), + id: 4, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Daniel'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 21), + id: 5, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Christopher'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 20), + id: 6, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Donald'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2019, 6, 28), + id: 7, + }, + { + hierarchy: ['Sarah', 'Mary'], + jobTitle: 'Head of Engineering', + recruitmentDate: new Date(2016, 3, 14), + id: 8, + }, + { + hierarchy: ['Sarah', 'Mary', 'Jennifer'], + jobTitle: 'Tech lead front', + recruitmentDate: new Date(2016, 5, 17), + id: 9, + }, + { + hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], + jobTitle: 'Front-end developer', + recruitmentDate: new Date(2019, 11, 7), + id: 10, + }, + { + hierarchy: ['Sarah', 'Mary', 'Michael'], + jobTitle: 'Tech lead devops', + recruitmentDate: new Date(2021, 7, 1), + id: 11, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda'], + jobTitle: 'Tech lead back', + recruitmentDate: new Date(2017, 0, 12), + id: 12, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2019, 2, 22), + id: 13, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2018, 4, 19), + id: 14, + }, +]; + +const columns: GridColumns = [ + { field: 'jobTitle', headerName: 'Job Title', width: 200 }, + { field: 'recruitmentDate', headerName: 'Recruitment Date', type: 'date', width: 150 }, +]; + +const getTreeDataPath = (row) => row.hierarchy; + +export default function DisableChildrenSortingTreeData() { + const [disableChildrenSorting, setDisableChildrenSorting] = React.useState(true); + const [sortModel, setSortModel] = React.useState([ + { field: 'jobTitle', sort: 'desc' }, + ]); + + console.log(disableChildrenSorting); -export default function KeyboardNavigationFocus() { return ( - - -
- -
-
+ + +
+ +
+
); } diff --git a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js index 4a4be539c675b..9d8b2e2d5712a 100644 --- a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js @@ -2,37 +2,112 @@ import * as React from 'react'; import { DataGridPro } from '@mui/x-data-grid-pro'; const rows = [ - { path: ['Sarah'], jobTitle: 'CEO' }, - { path: ['Sarah', 'Thomas'], jobTitle: 'Head of Sales' }, - { path: ['Sarah', 'Thomas', 'Robert'], jobTitle: 'Sales Person' }, - { path: ['Sarah', 'Thomas', 'Karen'], jobTitle: 'Sales Person' }, - { path: ['Sarah', 'Thomas', 'Nancy'], jobTitle: 'Sales Person' }, - { path: ['Sarah', 'Thomas', 'Daniel'], jobTitle: 'Sales Person' }, - { path: ['Sarah', 'Thomas', 'Christopher'], jobTitle: 'Sales Person' }, - { path: ['Sarah', 'Thomas', 'Donald'], jobTitle: 'Sales Person' }, - { path: ['Sarah', 'Mary'], jobTitle: 'Head of Engineering' }, - { path: ['Sarah', 'Mary', 'Jennifer'], jobTitle: 'Tech lead front' }, - { path: ['Sarah', 'Mary', 'Jennifer', 'Linda'], jobTitle: 'Front-end developer' }, - { path: ['Sarah', 'Mary', 'Michael'], jobTitle: 'Tech lead devops' }, - { path: ['Sarah', 'Mary', 'Linda'], jobTitle: 'Tech lead back' }, - { path: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], jobTitle: 'Back-end developer' }, - { path: ['Sarah', 'Mary', 'Linda', 'William'], jobTitle: 'Back-end developer' }, + { + hierarchy: ['Sarah', 'Thomas'], + jobTitle: 'Head of Sales', + recruitmentDate: new Date(2017, 3, 4), + id: 1, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Robert'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 11, 20), + id: 2, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Karen'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 10, 14), + id: 3, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Nancy'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2018, 3, 29), + id: 4, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Daniel'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 21), + id: 5, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Christopher'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 20), + id: 6, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Donald'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2019, 6, 28), + id: 7, + }, + { + hierarchy: ['Sarah', 'Mary'], + jobTitle: 'Head of Engineering', + recruitmentDate: new Date(2016, 3, 14), + id: 8, + }, + { + hierarchy: ['Sarah', 'Mary', 'Jennifer'], + jobTitle: 'Tech lead front', + recruitmentDate: new Date(2016, 5, 17), + id: 9, + }, + { + hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], + jobTitle: 'Front-end developer', + recruitmentDate: new Date(2019, 11, 7), + id: 10, + }, + { + hierarchy: ['Sarah', 'Mary', 'Michael'], + jobTitle: 'Tech lead devops', + recruitmentDate: new Date(2021, 7, 1), + id: 11, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda'], + jobTitle: 'Tech lead back', + recruitmentDate: new Date(2017, 0, 12), + id: 12, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2019, 2, 22), + id: 13, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2018, 4, 19), + id: 14, + }, ]; -const columns = [{ field: 'jobTitle', width: 250 }]; +const columns = [ + { field: 'jobTitle', headerName: 'Job Title', width: 200 }, + { + field: 'recruitmentDate', + headerName: 'Recruitment Date', + type: 'date', + width: 150, + }, +]; -const getRowId = (row) => row.path.join('-'); -const getTreeDataPath = (row) => row.path; +const getTreeDataPath = (row) => row.hierarchy; export default function BasicTreeData() { return ( -
+
diff --git a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx index e796ad5cb9b67..01ea79afb6892 100644 --- a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx @@ -2,37 +2,112 @@ import * as React from 'react'; import { DataGridPro, GridColumns, GridRowsProp } from '@mui/x-data-grid-pro'; const rows: GridRowsProp = [ - { path: ['Sarah'], jobTitle: 'CEO' }, - { path: ['Sarah', 'Thomas'], jobTitle: 'Head of Sales' }, - { path: ['Sarah', 'Thomas', 'Robert'], jobTitle: 'Sales Person' }, - { path: ['Sarah', 'Thomas', 'Karen'], jobTitle: 'Sales Person' }, - { path: ['Sarah', 'Thomas', 'Nancy'], jobTitle: 'Sales Person' }, - { path: ['Sarah', 'Thomas', 'Daniel'], jobTitle: 'Sales Person' }, - { path: ['Sarah', 'Thomas', 'Christopher'], jobTitle: 'Sales Person' }, - { path: ['Sarah', 'Thomas', 'Donald'], jobTitle: 'Sales Person' }, - { path: ['Sarah', 'Mary'], jobTitle: 'Head of Engineering' }, - { path: ['Sarah', 'Mary', 'Jennifer'], jobTitle: 'Tech lead front' }, - { path: ['Sarah', 'Mary', 'Jennifer', 'Linda'], jobTitle: 'Front-end developer' }, - { path: ['Sarah', 'Mary', 'Michael'], jobTitle: 'Tech lead devops' }, - { path: ['Sarah', 'Mary', 'Linda'], jobTitle: 'Tech lead back' }, - { path: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], jobTitle: 'Back-end developer' }, - { path: ['Sarah', 'Mary', 'Linda', 'William'], jobTitle: 'Back-end developer' }, + { + hierarchy: ['Sarah', 'Thomas'], + jobTitle: 'Head of Sales', + recruitmentDate: new Date(2017, 3, 4), + id: 1, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Robert'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 11, 20), + id: 2, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Karen'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 10, 14), + id: 3, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Nancy'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2018, 3, 29), + id: 4, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Daniel'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 21), + id: 5, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Christopher'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 20), + id: 6, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Donald'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2019, 6, 28), + id: 7, + }, + { + hierarchy: ['Sarah', 'Mary'], + jobTitle: 'Head of Engineering', + recruitmentDate: new Date(2016, 3, 14), + id: 8, + }, + { + hierarchy: ['Sarah', 'Mary', 'Jennifer'], + jobTitle: 'Tech lead front', + recruitmentDate: new Date(2016, 5, 17), + id: 9, + }, + { + hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], + jobTitle: 'Front-end developer', + recruitmentDate: new Date(2019, 11, 7), + id: 10, + }, + { + hierarchy: ['Sarah', 'Mary', 'Michael'], + jobTitle: 'Tech lead devops', + recruitmentDate: new Date(2021, 7, 1), + id: 11, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda'], + jobTitle: 'Tech lead back', + recruitmentDate: new Date(2017, 0, 12), + id: 12, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2019, 2, 22), + id: 13, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2018, 4, 19), + id: 14, + }, ]; -const columns: GridColumns = [{ field: 'jobTitle', width: 250 }]; +const columns: GridColumns = [ + { field: 'jobTitle', headerName: 'Job Title', width: 200 }, + { + field: 'recruitmentDate', + headerName: 'Recruitment Date', + type: 'date', + width: 150, + }, +]; -const getRowId = (row) => row.path.join('-'); -const getTreeDataPath = (row) => row.path; +const getTreeDataPath = (row) => row.hierarchy; export default function BasicTreeData() { return ( -
+
diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js index 6c626cec0b237..773197f8db2b3 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js @@ -7,7 +7,6 @@ import { gridVisibleDescendantCountLookupSelector, GridEvents, } from '@mui/x-data-grid-pro'; -import { useDemoTreeData } from '@mui/x-data-grid-generator'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; @@ -26,7 +25,7 @@ const CustomGridTreeDataGroupingCell = (props) => { gridVisibleDescendantCountLookupSelector, ); - const node = apiRef.current.UNSTABLE_getRowNode(id); + const node = apiRef.current.unstable_getRowNode(id); const descendantCount = descendantCountLookup[id]; const handleKeyDown = (event) => { @@ -48,13 +47,13 @@ const CustomGridTreeDataGroupingCell = (props) => { {descendantCount > 0 ? ( ) : ( @@ -71,24 +70,134 @@ CustomGridTreeDataGroupingCell.propTypes = { id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, }; +const rows = [ + { + hierarchy: ['Sarah'], + jobTitle: 'CEO', + recruitmentDate: new Date(2014, 7, 22), + id: 0, + }, + { + hierarchy: ['Sarah', 'Thomas'], + jobTitle: 'Head of Sales', + recruitmentDate: new Date(2017, 3, 4), + id: 1, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Robert'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 11, 20), + id: 2, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Karen'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 10, 14), + id: 3, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Nancy'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2018, 3, 29), + id: 4, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Daniel'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 21), + id: 5, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Christopher'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 20), + id: 6, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Donald'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2019, 6, 28), + id: 7, + }, + { + hierarchy: ['Sarah', 'Mary'], + jobTitle: 'Head of Engineering', + recruitmentDate: new Date(2016, 3, 14), + id: 8, + }, + { + hierarchy: ['Sarah', 'Mary', 'Jennifer'], + jobTitle: 'Tech lead front', + recruitmentDate: new Date(2016, 5, 17), + id: 9, + }, + { + hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], + jobTitle: 'Front-end developer', + recruitmentDate: new Date(2019, 11, 7), + id: 10, + }, + { + hierarchy: ['Sarah', 'Mary', 'Michael'], + jobTitle: 'Tech lead devops', + recruitmentDate: new Date(2021, 7, 1), + id: 11, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda'], + jobTitle: 'Tech lead back', + recruitmentDate: new Date(2017, 0, 12), + id: 12, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2019, 2, 22), + id: 13, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2018, 4, 19), + id: 14, + }, +]; + +const columns = [ + { + field: 'name', + headerName: 'Name', + valueGetter: (params) => { + const hierarchy = params.row.hierarchy; + return hierarchy[hierarchy.length - 1]; + }, + }, + { field: 'jobTitle', headerName: 'Job Title', width: 200 }, + { + field: 'recruitmentDate', + headerName: 'Recruitment Date', + type: 'date', + width: 150, + }, +]; + +const getTreeDataPath = (row) => row.hierarchy; + const groupingColDef = { + headerName: 'Hierarchy', renderCell: (params) => , }; export default function CustomGroupingColumnTreeData() { - const { data, loading } = useDemoTreeData({ - rowLength: [10, 5, 3], - randomLength: true, - }); - return ( -
+
); diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx index 7ffc647ee3dd3..453363ad74384 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx @@ -1,14 +1,15 @@ import * as React from 'react'; import { DataGridPro, - DataGridProProps, GridRenderCellParams, useGridApiContext, useGridSelector, gridVisibleDescendantCountLookupSelector, GridEvents, + GridColumns, + GridRowsProp, + DataGridProProps, } from '@mui/x-data-grid-pro'; -import { useDemoTreeData } from '@mui/x-data-grid-generator'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; @@ -26,7 +27,7 @@ const CustomGridTreeDataGroupingCell = (props: GridRenderCellParams) => { apiRef, gridVisibleDescendantCountLookupSelector, ); - const node = apiRef.current.UNSTABLE_getRowNode(id); + const node = apiRef.current.unstable_getRowNode(id); const descendantCount = descendantCountLookup[id]; const handleKeyDown = (event) => { @@ -48,13 +49,13 @@ const CustomGridTreeDataGroupingCell = (props: GridRenderCellParams) => { {descendantCount > 0 ? ( ) : ( @@ -64,24 +65,134 @@ const CustomGridTreeDataGroupingCell = (props: GridRenderCellParams) => { ); }; +const rows: GridRowsProp = [ + { + hierarchy: ['Sarah'], + jobTitle: 'CEO', + recruitmentDate: new Date(2014, 7, 22), + id: 0, + }, + { + hierarchy: ['Sarah', 'Thomas'], + jobTitle: 'Head of Sales', + recruitmentDate: new Date(2017, 3, 4), + id: 1, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Robert'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 11, 20), + id: 2, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Karen'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 10, 14), + id: 3, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Nancy'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2018, 3, 29), + id: 4, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Daniel'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 21), + id: 5, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Christopher'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 20), + id: 6, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Donald'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2019, 6, 28), + id: 7, + }, + { + hierarchy: ['Sarah', 'Mary'], + jobTitle: 'Head of Engineering', + recruitmentDate: new Date(2016, 3, 14), + id: 8, + }, + { + hierarchy: ['Sarah', 'Mary', 'Jennifer'], + jobTitle: 'Tech lead front', + recruitmentDate: new Date(2016, 5, 17), + id: 9, + }, + { + hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], + jobTitle: 'Front-end developer', + recruitmentDate: new Date(2019, 11, 7), + id: 10, + }, + { + hierarchy: ['Sarah', 'Mary', 'Michael'], + jobTitle: 'Tech lead devops', + recruitmentDate: new Date(2021, 7, 1), + id: 11, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda'], + jobTitle: 'Tech lead back', + recruitmentDate: new Date(2017, 0, 12), + id: 12, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2019, 2, 22), + id: 13, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2018, 4, 19), + id: 14, + }, +]; + +const columns: GridColumns = [ + { + field: 'name', + headerName: 'Name', + valueGetter: (params) => { + const hierarchy = params.row.hierarchy as string[]; + return hierarchy[hierarchy.length - 1]; + }, + }, + { field: 'jobTitle', headerName: 'Job Title', width: 200 }, + { + field: 'recruitmentDate', + headerName: 'Recruitment Date', + type: 'date', + width: 150, + }, +]; + +const getTreeDataPath = (row) => row.hierarchy; + const groupingColDef: DataGridProProps['groupingColDef'] = { + headerName: 'Hierarchy', renderCell: (params) => , }; export default function CustomGroupingColumnTreeData() { - const { data, loading } = useDemoTreeData({ - rowLength: [10, 5, 3], - randomLength: true, - }); - return ( -
+
); diff --git a/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.js b/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.js index 0bdc85586570b..d0d483bb7a670 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.js @@ -1,21 +1,121 @@ import * as React from 'react'; import { DataGridPro } from '@mui/x-data-grid-pro'; -import { useDemoTreeData } from '@mui/x-data-grid-generator'; -export default function DefaultGroupingExpansionDepthTreeData() { - const { data, loading } = useDemoTreeData({ - rowLength: [10, 5, 3], - randomLength: true, - }); +const rows = [ + { + hierarchy: ['Sarah'], + jobTitle: 'CEO', + recruitmentDate: new Date(2014, 7, 22), + id: 0, + }, + { + hierarchy: ['Sarah', 'Thomas'], + jobTitle: 'Head of Sales', + recruitmentDate: new Date(2017, 3, 4), + id: 1, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Robert'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 11, 20), + id: 2, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Karen'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 10, 14), + id: 3, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Nancy'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2018, 3, 29), + id: 4, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Daniel'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 21), + id: 5, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Christopher'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 20), + id: 6, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Donald'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2019, 6, 28), + id: 7, + }, + { + hierarchy: ['Sarah', 'Mary'], + jobTitle: 'Head of Engineering', + recruitmentDate: new Date(2016, 3, 14), + id: 8, + }, + { + hierarchy: ['Sarah', 'Mary', 'Jennifer'], + jobTitle: 'Tech lead front', + recruitmentDate: new Date(2016, 5, 17), + id: 9, + }, + { + hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], + jobTitle: 'Front-end developer', + recruitmentDate: new Date(2019, 11, 7), + id: 10, + }, + { + hierarchy: ['Sarah', 'Mary', 'Michael'], + jobTitle: 'Tech lead devops', + recruitmentDate: new Date(2021, 7, 1), + id: 11, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda'], + jobTitle: 'Tech lead back', + recruitmentDate: new Date(2017, 0, 12), + id: 12, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2019, 2, 22), + id: 13, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2018, 4, 19), + id: 14, + }, +]; + +const columns = [ + { field: 'jobTitle', headerName: 'Job Title', width: 200 }, + { + field: 'recruitmentDate', + headerName: 'Recruitment Date', + type: 'date', + width: 150, + }, +]; +const getTreeDataPath = (row) => row.hierarchy; + +export default function DefaultGroupingExpansionDepthTreeData() { return ( -
+
); diff --git a/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.tsx index 0bdc85586570b..085398d26134d 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.tsx @@ -1,21 +1,121 @@ import * as React from 'react'; -import { DataGridPro } from '@mui/x-data-grid-pro'; -import { useDemoTreeData } from '@mui/x-data-grid-generator'; +import { DataGridPro, GridColumns, GridRowsProp } from '@mui/x-data-grid-pro'; -export default function DefaultGroupingExpansionDepthTreeData() { - const { data, loading } = useDemoTreeData({ - rowLength: [10, 5, 3], - randomLength: true, - }); +const rows: GridRowsProp = [ + { + hierarchy: ['Sarah'], + jobTitle: 'CEO', + recruitmentDate: new Date(2014, 7, 22), + id: 0, + }, + { + hierarchy: ['Sarah', 'Thomas'], + jobTitle: 'Head of Sales', + recruitmentDate: new Date(2017, 3, 4), + id: 1, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Robert'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 11, 20), + id: 2, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Karen'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 10, 14), + id: 3, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Nancy'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2018, 3, 29), + id: 4, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Daniel'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 21), + id: 5, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Christopher'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 20), + id: 6, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Donald'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2019, 6, 28), + id: 7, + }, + { + hierarchy: ['Sarah', 'Mary'], + jobTitle: 'Head of Engineering', + recruitmentDate: new Date(2016, 3, 14), + id: 8, + }, + { + hierarchy: ['Sarah', 'Mary', 'Jennifer'], + jobTitle: 'Tech lead front', + recruitmentDate: new Date(2016, 5, 17), + id: 9, + }, + { + hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], + jobTitle: 'Front-end developer', + recruitmentDate: new Date(2019, 11, 7), + id: 10, + }, + { + hierarchy: ['Sarah', 'Mary', 'Michael'], + jobTitle: 'Tech lead devops', + recruitmentDate: new Date(2021, 7, 1), + id: 11, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda'], + jobTitle: 'Tech lead back', + recruitmentDate: new Date(2017, 0, 12), + id: 12, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2019, 2, 22), + id: 13, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2018, 4, 19), + id: 14, + }, +]; + +const columns: GridColumns = [ + { field: 'jobTitle', headerName: 'Job Title', width: 200 }, + { + field: 'recruitmentDate', + headerName: 'Recruitment Date', + type: 'date', + width: 150, + }, +]; +const getTreeDataPath = (row) => row.hierarchy; + +export default function DefaultGroupingExpansionDepthTreeData() { return ( -
+
); diff --git a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js index c69433ee3d201..838b38119e11c 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js @@ -1,28 +1,143 @@ import * as React from 'react'; import { DataGridPro } from '@mui/x-data-grid-pro'; -import { useDemoTreeData } from '@mui/x-data-grid-generator'; +import Stack from '@mui/material/Stack'; +import Checkbox from '@mui/material/Checkbox'; +import FormGroup from '@mui/material/FormGroup'; +import FormControlLabel from '@mui/material/FormControlLabel'; -export default function DisableChildrenFilteringTreeData() { - const { data, loading } = useDemoTreeData({ - rowLength: [10, 5, 3], - randomLength: true, - }); +const rows = [ + { + hierarchy: ['Thomas'], + jobTitle: 'Head of Sales', + recruitmentDate: new Date(2017, 3, 4), + id: 1, + }, + { + hierarchy: ['Thomas', 'Robert'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 11, 20), + id: 2, + }, + { + hierarchy: ['Thomas', 'Karen'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 10, 14), + id: 3, + }, + { + hierarchy: ['Thomas', 'Nancy'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2018, 3, 29), + id: 4, + }, + { + hierarchy: ['Thomas', 'Daniel'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 21), + id: 5, + }, + { + hierarchy: ['Thomas', 'Christopher'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 20), + id: 6, + }, + { + hierarchy: ['Thomas', 'Donald'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2019, 6, 28), + id: 7, + }, + { + hierarchy: ['Mary'], + jobTitle: 'Head of Engineering', + recruitmentDate: new Date(2016, 3, 14), + id: 8, + }, + { + hierarchy: ['Mary', 'Jennifer'], + jobTitle: 'Tech lead front', + recruitmentDate: new Date(2016, 5, 17), + id: 9, + }, + { + hierarchy: ['Mary', 'Jennifer', 'Anna'], + jobTitle: 'Front-end developer', + recruitmentDate: new Date(2019, 11, 7), + id: 10, + }, + { + hierarchy: ['Mary', 'Michael'], + jobTitle: 'Tech lead devops', + recruitmentDate: new Date(2021, 7, 1), + id: 11, + }, + { + hierarchy: ['Mary', 'Linda'], + jobTitle: 'Tech lead back', + recruitmentDate: new Date(2017, 0, 12), + id: 12, + }, + { + hierarchy: ['Mary', 'Linda', 'Elizabeth'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2019, 2, 22), + id: 13, + }, + { + hierarchy: ['Mary', 'Linda', 'William'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2018, 4, 19), + id: 14, + }, +]; + +const columns = [ + { field: 'jobTitle', headerName: 'Job Title', width: 200 }, + { + field: 'recruitmentDate', + headerName: 'Recruitment Date', + type: 'date', + width: 150, + }, +]; + +const getTreeDataPath = (row) => row.hierarchy; +export default function DisableChildrenSortingTreeData() { + const [disableChildrenFiltering, setDisableChildrenFiltering] = + React.useState(true); const [filterModel, setFilterModel] = React.useState({ - items: [{ columnField: 'index', operatorValue: '>', value: 2 }], + items: [ + { columnField: 'jobTitle', operatorValue: 'contains', value: 'Head of Sales' }, + ], }); return ( -
- -
+ + + setDisableChildrenFiltering(event.target.checked)} + /> + } + label="Enable `disableChildrenFiltering`" + /> + +
+ +
+
); } diff --git a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx index 01bd356e25146..197a3e1fd46a6 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx @@ -1,27 +1,148 @@ import * as React from 'react'; -import { DataGridPro, GridFilterModel } from '@mui/x-data-grid-pro'; -import { useDemoTreeData } from '@mui/x-data-grid-generator'; +import { + DataGridPro, + GridColumns, + GridRowsProp, + GridFilterModel, +} from '@mui/x-data-grid-pro'; +import Stack from '@mui/material/Stack'; +import Checkbox from '@mui/material/Checkbox'; +import FormGroup from '@mui/material/FormGroup'; +import FormControlLabel from '@mui/material/FormControlLabel'; -export default function DisableChildrenFilteringTreeData() { - const { data, loading } = useDemoTreeData({ - rowLength: [10, 5, 3], - randomLength: true, - }); +const rows: GridRowsProp = [ + { + hierarchy: ['Thomas'], + jobTitle: 'Head of Sales', + recruitmentDate: new Date(2017, 3, 4), + id: 1, + }, + { + hierarchy: ['Thomas', 'Robert'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 11, 20), + id: 2, + }, + { + hierarchy: ['Thomas', 'Karen'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 10, 14), + id: 3, + }, + { + hierarchy: ['Thomas', 'Nancy'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2018, 3, 29), + id: 4, + }, + { + hierarchy: ['Thomas', 'Daniel'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 21), + id: 5, + }, + { + hierarchy: ['Thomas', 'Christopher'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 20), + id: 6, + }, + { + hierarchy: ['Thomas', 'Donald'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2019, 6, 28), + id: 7, + }, + { + hierarchy: ['Mary'], + jobTitle: 'Head of Engineering', + recruitmentDate: new Date(2016, 3, 14), + id: 8, + }, + { + hierarchy: ['Mary', 'Jennifer'], + jobTitle: 'Tech lead front', + recruitmentDate: new Date(2016, 5, 17), + id: 9, + }, + { + hierarchy: ['Mary', 'Jennifer', 'Anna'], + jobTitle: 'Front-end developer', + recruitmentDate: new Date(2019, 11, 7), + id: 10, + }, + { + hierarchy: ['Mary', 'Michael'], + jobTitle: 'Tech lead devops', + recruitmentDate: new Date(2021, 7, 1), + id: 11, + }, + { + hierarchy: ['Mary', 'Linda'], + jobTitle: 'Tech lead back', + recruitmentDate: new Date(2017, 0, 12), + id: 12, + }, + { + hierarchy: ['Mary', 'Linda', 'Elizabeth'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2019, 2, 22), + id: 13, + }, + { + hierarchy: ['Mary', 'Linda', 'William'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2018, 4, 19), + id: 14, + }, +]; + +const columns: GridColumns = [ + { field: 'jobTitle', headerName: 'Job Title', width: 200 }, + { + field: 'recruitmentDate', + headerName: 'Recruitment Date', + type: 'date', + width: 150, + }, +]; + +const getTreeDataPath = (row) => row.hierarchy; + +export default function DisableChildrenSortingTreeData() { + const [disableChildrenFiltering, setDisableChildrenFiltering] = + React.useState(true); const [filterModel, setFilterModel] = React.useState({ - items: [{ columnField: 'index', operatorValue: '>', value: 2 }], + items: [ + { columnField: 'jobTitle', operatorValue: 'contains', value: 'Head of Sales' }, + ], }); return ( -
- -
+ + + setDisableChildrenFiltering(event.target.checked)} + /> + } + label="Enable `disableChildrenFiltering`" + /> + +
+ +
+
); } diff --git a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js index 88a453bb118eb..29b7087202899 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js @@ -1,28 +1,140 @@ import * as React from 'react'; import { DataGridPro } from '@mui/x-data-grid-pro'; -import { useDemoTreeData } from '@mui/x-data-grid-generator'; +import Stack from '@mui/material/Stack'; +import Checkbox from '@mui/material/Checkbox'; +import FormGroup from '@mui/material/FormGroup'; +import FormControlLabel from '@mui/material/FormControlLabel'; -export default function DisableChildrenSortingTreeData() { - const { data, loading } = useDemoTreeData({ - rowLength: [10, 5, 3], - randomLength: true, - }); +const rows = [ + { + hierarchy: ['Thomas'], + jobTitle: 'Head of Sales', + recruitmentDate: new Date(2017, 3, 4), + id: 1, + }, + { + hierarchy: ['Thomas', 'Robert'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 11, 20), + id: 2, + }, + { + hierarchy: ['Thomas', 'Karen'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 10, 14), + id: 3, + }, + { + hierarchy: ['Thomas', 'Nancy'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2018, 3, 29), + id: 4, + }, + { + hierarchy: ['Thomas', 'Daniel'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 21), + id: 5, + }, + { + hierarchy: ['Thomas', 'Christopher'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 20), + id: 6, + }, + { + hierarchy: ['Thomas', 'Donald'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2019, 6, 28), + id: 7, + }, + { + hierarchy: ['Mary'], + jobTitle: 'Head of Engineering', + recruitmentDate: new Date(2016, 3, 14), + id: 8, + }, + { + hierarchy: ['Mary', 'Jennifer'], + jobTitle: 'Tech lead front', + recruitmentDate: new Date(2016, 5, 17), + id: 9, + }, + { + hierarchy: ['Mary', 'Jennifer', 'Anna'], + jobTitle: 'Front-end developer', + recruitmentDate: new Date(2019, 11, 7), + id: 10, + }, + { + hierarchy: ['Mary', 'Michael'], + jobTitle: 'Tech lead devops', + recruitmentDate: new Date(2021, 7, 1), + id: 11, + }, + { + hierarchy: ['Mary', 'Linda'], + jobTitle: 'Tech lead back', + recruitmentDate: new Date(2017, 0, 12), + id: 12, + }, + { + hierarchy: ['Mary', 'Linda', 'Elizabeth'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2019, 2, 22), + id: 13, + }, + { + hierarchy: ['Mary', 'Linda', 'William'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2018, 4, 19), + id: 14, + }, +]; + +const columns = [ + { field: 'jobTitle', headerName: 'Job Title', width: 200 }, + { + field: 'recruitmentDate', + headerName: 'Recruitment Date', + type: 'date', + width: 150, + }, +]; - const [sortModel, setSortingModel] = React.useState([ - { field: 'index', sort: 'desc' }, +const getTreeDataPath = (row) => row.hierarchy; + +export default function DisableChildrenSortingTreeData() { + const [disableChildrenSorting, setDisableChildrenSorting] = React.useState(true); + const [sortModel, setSortModel] = React.useState([ + { field: 'recruitmentDate', sort: 'asc' }, ]); return ( -
- -
+ + + setDisableChildrenSorting(event.target.checked)} + /> + } + label="Enable `disableChildrenSorting`" + /> + +
+ +
+
); } diff --git a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.tsx index b866dadf0b49c..581b3922fe8c7 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.tsx @@ -1,27 +1,145 @@ import * as React from 'react'; -import { DataGridPro, GridSortModel } from '@mui/x-data-grid-pro'; -import { useDemoTreeData } from '@mui/x-data-grid-generator'; +import { + DataGridPro, + GridColumns, + GridRowsProp, + GridSortModel, +} from '@mui/x-data-grid-pro'; +import Stack from '@mui/material/Stack'; +import Checkbox from '@mui/material/Checkbox'; +import FormGroup from '@mui/material/FormGroup'; +import FormControlLabel from '@mui/material/FormControlLabel'; + +const rows: GridRowsProp = [ + { + hierarchy: ['Thomas'], + jobTitle: 'Head of Sales', + recruitmentDate: new Date(2017, 3, 4), + id: 1, + }, + { + hierarchy: ['Thomas', 'Robert'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 11, 20), + id: 2, + }, + { + hierarchy: ['Thomas', 'Karen'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 10, 14), + id: 3, + }, + { + hierarchy: ['Thomas', 'Nancy'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2018, 3, 29), + id: 4, + }, + { + hierarchy: ['Thomas', 'Daniel'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 21), + id: 5, + }, + { + hierarchy: ['Thomas', 'Christopher'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 20), + id: 6, + }, + { + hierarchy: ['Thomas', 'Donald'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2019, 6, 28), + id: 7, + }, + { + hierarchy: ['Mary'], + jobTitle: 'Head of Engineering', + recruitmentDate: new Date(2016, 3, 14), + id: 8, + }, + { + hierarchy: ['Mary', 'Jennifer'], + jobTitle: 'Tech lead front', + recruitmentDate: new Date(2016, 5, 17), + id: 9, + }, + { + hierarchy: ['Mary', 'Jennifer', 'Anna'], + jobTitle: 'Front-end developer', + recruitmentDate: new Date(2019, 11, 7), + id: 10, + }, + { + hierarchy: ['Mary', 'Michael'], + jobTitle: 'Tech lead devops', + recruitmentDate: new Date(2021, 7, 1), + id: 11, + }, + { + hierarchy: ['Mary', 'Linda'], + jobTitle: 'Tech lead back', + recruitmentDate: new Date(2017, 0, 12), + id: 12, + }, + { + hierarchy: ['Mary', 'Linda', 'Elizabeth'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2019, 2, 22), + id: 13, + }, + { + hierarchy: ['Mary', 'Linda', 'William'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2018, 4, 19), + id: 14, + }, +]; + +const columns: GridColumns = [ + { field: 'jobTitle', headerName: 'Job Title', width: 200 }, + { + field: 'recruitmentDate', + headerName: 'Recruitment Date', + type: 'date', + width: 150, + }, +]; + +const getTreeDataPath = (row) => row.hierarchy; export default function DisableChildrenSortingTreeData() { - const { data, loading } = useDemoTreeData({ - rowLength: [10, 5, 3], - randomLength: true, - }); - const [sortModel, setSortingModel] = React.useState([ - { field: 'index', sort: 'desc' }, + const [disableChildrenSorting, setDisableChildrenSorting] = React.useState(true); + const [sortModel, setSortModel] = React.useState([ + { field: 'recruitmentDate', sort: 'asc' }, ]); return ( -
- -
+ + + setDisableChildrenSorting(event.target.checked)} + /> + } + label="Enable `disableChildrenSorting`" + /> + +
+ +
+
); } diff --git a/docs/src/pages/components/data-grid/group-pivot/FillerTreeData.js b/docs/src/pages/components/data-grid/group-pivot/FillerTreeData.js deleted file mode 100644 index 3a8a81fb86578..0000000000000 --- a/docs/src/pages/components/data-grid/group-pivot/FillerTreeData.js +++ /dev/null @@ -1,31 +0,0 @@ -import * as React from 'react'; -import { DataGridPro } from '@mui/x-data-grid-pro'; - -const rows = [ - { path: ['A'] }, - { path: ['A', 'A', 'A', 'A'] }, - { path: ['A', 'A', 'A', 'B'] }, - { path: ['B'] }, - { path: ['C', 'A', 'A'] }, -]; - -const columns = [{ field: 'path', renderCell: (params) => params.value.join('-') }]; - -const getTreeDataPath = (row) => row.path; - -const getRowId = (row) => row.path.join('-'); - -export default function FillerTreeData() { - return ( -
- -
- ); -} diff --git a/docs/src/pages/components/data-grid/group-pivot/FillerTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/FillerTreeData.tsx deleted file mode 100644 index b3f71d82debae..0000000000000 --- a/docs/src/pages/components/data-grid/group-pivot/FillerTreeData.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import * as React from 'react'; -import { DataGridPro, GridColumns, GridRowsProp } from '@mui/x-data-grid-pro'; - -const rows: GridRowsProp = [ - { path: ['A'] }, - { path: ['A', 'A', 'A', 'A'] }, - { path: ['A', 'A', 'A', 'B'] }, - { path: ['B'] }, - { path: ['C', 'A', 'A'] }, -]; - -const columns: GridColumns = [ - { field: 'path', renderCell: (params) => (params.value as string[]).join('-') }, -]; - -const getTreeDataPath = (row: any) => row.path; - -const getRowId = (row: any) => row.path.join('-'); - -export default function FillerTreeData() { - return ( -
- -
- ); -} diff --git a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js index 6aebe6a09a9ad..b174375116880 100644 --- a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js @@ -14,16 +14,16 @@ export default function SetRowExpansionTreeData() { const toggleFirstRow = () => { const rowId = apiRef.current.getRowIdFromRowIndex(0); - apiRef.current.UNSTABLE_setRowExpansion( + apiRef.current.unstable_setRowExpansion( rowId, - !apiRef.current.UNSTABLE_getRowNode(rowId)?.expanded, + !apiRef.current.unstable_getRowNode(rowId)?.expanded, ); }; return ( -
+
{ const rowId = apiRef.current.getRowIdFromRowIndex(0); - apiRef.current.UNSTABLE_setRowExpansion( + apiRef.current.unstable_setRowExpansion( rowId, - !apiRef.current.UNSTABLE_getRowNode(rowId)?.expanded, + !apiRef.current.unstable_getRowNode(rowId)?.expanded, ); }; return ( -
+
params.id.toString().startsWith('auto-generated-row'), + shouldRenderAutoGeneratedRows: true, + }, +]; + +const getTreeDataPath = (row) => row.hierarchy; + +export default function TreeDataWithGap() { + return ( +
+ +
+ ); +} diff --git a/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.tsx b/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.tsx new file mode 100644 index 0000000000000..ebd5efc704ad9 --- /dev/null +++ b/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.tsx @@ -0,0 +1,104 @@ +import * as React from 'react'; +import { DataGridPro, GridColumns, GridRowsProp } from '@mui/x-data-grid-pro'; + +const rows: GridRowsProp = [ + { + hierarchy: ['Sarah', 'Thomas', 'Robert'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 11, 20), + id: 2, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Karen'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 10, 14), + id: 3, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Nancy'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2018, 3, 29), + id: 4, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Daniel'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 21), + id: 5, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Christopher'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 20), + id: 6, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Donald'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2019, 6, 28), + id: 7, + }, + { + hierarchy: ['Sarah', 'Mary', 'Jennifer'], + jobTitle: 'Tech lead front', + recruitmentDate: new Date(2016, 5, 17), + id: 9, + }, + { + hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], + jobTitle: 'Front-end developer', + recruitmentDate: new Date(2019, 11, 7), + id: 10, + }, + { + hierarchy: ['Sarah', 'Mary', 'Michael'], + jobTitle: 'Tech lead devops', + recruitmentDate: new Date(2021, 7, 1), + id: 11, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2019, 2, 22), + id: 13, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2018, 4, 19), + id: 14, + }, +]; + +const columns: GridColumns = [ + { field: 'jobTitle', headerName: 'Job Title', width: 200 }, + { + field: 'recruitmentDate', + headerName: 'Recruitment Date', + type: 'date', + width: 150, + }, + { + field: 'isAutoGenerated', + headerName: 'Gap', + type: 'boolean', + valueGetter: (params) => params.id.toString().startsWith('auto-generated-row'), + shouldRenderAutoGeneratedRows: true, + }, +]; + +const getTreeDataPath = (row) => row.hierarchy; + +export default function TreeDataWithGap() { + return ( +
+ +
+ ); +} diff --git a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md index 903b772f6a742..ad94477061709 100644 --- a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md +++ b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md @@ -6,49 +6,53 @@ title: Data Grid - Group & Pivot

Use grouping, pivoting and more to analyse the data in depth.

-## Tree data [](https://mui.com/store/items/material-ui-pro/) +## Tree Data [](https://mui.com/store/items/material-ui-pro/) -Tree data allows to display data with parent / child relationships. +Tree Data allows to display data with parent / child relationships. -### Basic example +### General behavior -To enable the Tree Data, you must use the `treeData` prop as well as provide a `getTreeDataPath` prop. -The `getTreeDataPath` function returns an array of strings which represents the path to a given element of the tree. +To enable the Tree Data, you simply have to use the `treeData` prop as well as provide a `getTreeDataPath` prop. +The `getTreeDataPath` function returns an array of strings which represents the path to a given row. ```tsx +// The following examples will both render the same tree +// - Sarah +// - Thomas +// - Robert +// - Karen + +const columns: GridColumns = [{ field: 'jobTitle', width: 250 }]; + // Without transformation -const rows: GridRows = [ - { id: 0, path: ['A'] }, - { id: 1, path: ['A', 'A'] }, - { id: 2, path: ['A', 'B'] }, - { id: 3, path: ['A', 'C'] }, - { id: 4, path: ['A', 'B', 'A'] }, - { id: 5, path: ['B'] } +const rows: GridRowsProp = [ + { path: ['Sarah'], jobTitle: 'CEO', id: 0 }, + { path: ['Sarah', 'Thomas'], jobTitle: 'Head of Sales', id: 1 }, + { path: ['Sarah', 'Thomas', 'Robert'], jobTitle: 'Sales Person', id: 2 }, + { path: ['Sarah', 'Thomas', 'Karen'], jobTitle: 'Sales Person', id: 3 }, ]; row.path} - rows={rows} - {/* ...other props */} -/> + treeData + getTreeDataPath={(row) => row.path} + rows={rows} + columns={columns} +/>; // With transformation -const rows: GridRows = [ - { id: 0, path: 'A' }, - { id: 1, path: 'A.A' }, - { id: 2, path: 'A.B' }, - { id: 3, path: 'A.C' }, - { id: 4, path: 'A.B.A' }, - { id: 5, path: 'B' } +const rows: GridRowsProp = [ + { path: 'Sarah', jobTitle: 'CEO', id: 0 }, + { path: 'Sarah/Thomas', jobTitle: 'Head of Sales', id: 1 }, + { path: 'Sarah/Thomas/Robert', jobTitle: 'Sales Person', id: 2 }, + { path: 'Sarah/Thomas/Karen', jobTitle: 'Sales Person', id: 3 }, ]; row.path.split('.')} - rows={rows} - {/* ...other props */} -/> + treeData + getTreeDataPath={(row) => row.path.split('/')} + rows={rows} + columns={columns} +/>; ``` {{"demo": "pages/components/data-grid/group-pivot/BasicTreeData.js", "bg": "inline", "defaultCodeOpen": false}} @@ -66,7 +70,7 @@ If you want to expand the whole tree, set `defaultGroupingExpansionDepth = -1` {{"demo": "pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.js", "bg": "inline", "defaultCodeOpen": false}} -Use the `UNSTABLE_setRowExpansion` method on `apiRef` to programmatically set the expansion of a row. +Use the `unstable_setRowExpansion` method on `apiRef` to programmatically set the expansion of a row. {{"demo": "pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js", "bg": "inline", "defaultCodeOpen": false}} @@ -74,7 +78,7 @@ Use the `UNSTABLE_setRowExpansion` method on `apiRef` to programmatically set th If some entries are missing to build the full tree, the `DataGridPro` will automatically create rows to fill those gaps. -{{"demo": "pages/components/data-grid/group-pivot/FillerTreeData.js", "bg": "inline", "defaultCodeOpen": false}} +{{"demo": "pages/components/data-grid/group-pivot/TreeDataWithGap.js", "bg": "inline", "defaultCodeOpen": false}} ### Filtering @@ -84,14 +88,14 @@ A node is included if one of the following criteria is met: - it is passing the filters By default, the filtering is applied to every depth of the tree. -You can limit the filtering to the top level rows by with the `disableChildrenFiltering`. +You can limit the filtering to the top level rows with the `disableChildrenFiltering` prop. {{"demo": "pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js", "bg": "inline", "defaultCodeOpen": false}} ### Sorting By default, the sorting is applied to every depth of the tree. -You can limit the filtering to the top level rows by with the `disableChildrenSorting`. +You can limit the filtering to the top level rows with the `disableChildrenSorting` prop. {{"demo": "pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js", "bg": "inline", "defaultCodeOpen": false}} diff --git a/docs/src/pages/components/data-grid/overview/overview.md b/docs/src/pages/components/data-grid/overview/overview.md index c10f09a3b7605..58d297f627c3f 100644 --- a/docs/src/pages/components/data-grid/overview/overview.md +++ b/docs/src/pages/components/data-grid/overview/overview.md @@ -77,6 +77,7 @@ We provide three options: - [Sorting](/components/data-grid/sorting) and [multi-sort](/components/data-grid/sorting/#multi-column-sorting) - [Selection](/components/data-grid/selection/) - [Column virtualization](/components/data-grid/virtualization/#column-virtualization) and [rows virtualization](/components/data-grid/virtualization/#row-virtualization) +- [Tree data](/components/data-grid/group-pivot/#tree-data) - [Resizable columns](/components/data-grid/columns/#column-resizing) - [100% customizable](/components/data-grid/style/) - Server-side data @@ -90,7 +91,6 @@ While development of the data grid component is moving fast, there are still man - Headless (hooks only) - [Column pinning](/components/data-grid/columns/#column-pinning) -- [Tree data](/components/data-grid/group-pivot/#tree-data) - [Excel export](/components/data-grid/export/) - [Range selection](/components/data-grid/selection/#range-selection) - [Group, Pivot, Aggregation](/components/data-grid/group-pivot/) diff --git a/packages/grid/_modules_/grid/components/GridRow.tsx b/packages/grid/_modules_/grid/components/GridRow.tsx index 1302b09f35551..7656cd1a44781 100644 --- a/packages/grid/_modules_/grid/components/GridRow.tsx +++ b/packages/grid/_modules_/grid/components/GridRow.tsx @@ -88,7 +88,7 @@ function GridRow(props: React.HTMLAttributes & GridRowProps) { const apiRef = useGridApiContext(); const rootProps = useGridRootProps(); const columnsMeta = useGridSelector(apiRef, gridColumnsMetaSelector); - const rowNode = apiRef.current.UNSTABLE_getRowNode(rowId); + const rowNode = apiRef.current.unstable_getRowNode(rowId); const ownerState = { selected, diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index 3ddb065d050c9..f5723b1d05bc6 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -31,7 +31,7 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { const apiRef = useGridApiContext(); const descendantCountLookup = useGridSelector(apiRef, gridVisibleDescendantCountLookupSelector); const classes = useStyles(); - const node = apiRef.current.UNSTABLE_getRowNode(id); + const node = apiRef.current.unstable_getRowNode(id); const descendantCount = descendantCountLookup[id]; const Icon = node?.expanded @@ -57,7 +57,7 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { {descendantCount > 0 && ( apiRef.current.UNSTABLE_setRowExpansion(id, !node?.expanded)} + onClick={() => apiRef.current.unstable_setRowExpansion(id, !node?.expanded)} onKeyDown={handleKeyDown} tabIndex={-1} aria-label={ diff --git a/packages/grid/_modules_/grid/constants/localeTextConstants.ts b/packages/grid/_modules_/grid/constants/localeTextConstants.ts index 5302879cbe34b..c806fc8da8eeb 100644 --- a/packages/grid/_modules_/grid/constants/localeTextConstants.ts +++ b/packages/grid/_modules_/grid/constants/localeTextConstants.ts @@ -105,7 +105,7 @@ export const GRID_DEFAULT_LOCALE_TEXT: GridLocaleText = { // Actions cell more text actionsCellMore: 'more', - // Tree data + // Tree Data treeDataGroupingHeaderName: 'Group', treeDataExpand: 'see children', treeDataCollapse: 'hide children', diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index f4329a38ad723..413e682f457f8 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -225,7 +225,8 @@ export const useGridFilter = ( const shouldSkipFilters = props.disableChildrenFiltering && node.depth > 0; const isMatchingFilters = shouldSkipFilters || filteringMethod(node.id); - const shouldBeVisible = visibleDescendantCount > 0 || isMatchingFilters; + const shouldBeVisible = + (!props.disableChildrenFiltering && visibleDescendantCount > 0) || isMatchingFilters; visibleRowsLookup[node.id] = shouldBeVisible; if (visibleDescendantCount > 0) { @@ -436,6 +437,7 @@ export const useGridFilter = ( isFirstRender.current = false; return; } + console.log('GO'); apiRef.current.applyFilters(); }, [apiRef, props.disableChildrenFiltering]); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 29c092d2e0be4..5095a96d8053e 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -283,10 +283,10 @@ export const useGridRows = ( [apiRef], ); - const setRowExpansion = React.useCallback( + const setRowExpansion = React.useCallback( (id, isExpanded) => { setGridState((state) => { - const node = apiRef.current.UNSTABLE_getRowNode(id); + const node = apiRef.current.unstable_getRowNode(id); if (!node) { throw new Error(`MUI: No row with id #${id} found`); } @@ -305,7 +305,7 @@ export const useGridRows = ( [apiRef, setGridState, forceUpdate], ); - const getRowNode = React.useCallback( + const getRowNode = React.useCallback( (id) => gridRowTreeSelector(apiRef.current.state)[id] ?? null, [apiRef], ); @@ -360,8 +360,8 @@ export const useGridRows = ( getAllRowIds, setRows, updateRows, - UNSTABLE_setRowExpansion: setRowExpansion, - UNSTABLE_getRowNode: getRowNode, + unstable_setRowExpansion: setRowExpansion, + unstable_getRowNode: getRowNode, }; useGridApiMethod(apiRef, rowApi, 'GridRowApi'); diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index cd1315192dfd7..081bb3f396fd2 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -99,14 +99,14 @@ export const useGridTreeData = ( event.stopPropagation(); event.preventDefault(); - const node = apiRef.current.UNSTABLE_getRowNode(params.id); + const node = apiRef.current.unstable_getRowNode(params.id); if (!node || node.descendantCount === 0) { return; } - apiRef.current.UNSTABLE_setRowExpansion( + apiRef.current.unstable_setRowExpansion( params.id, - !apiRef.current.UNSTABLE_getRowNode(params.id)?.expanded, + !apiRef.current.unstable_getRowNode(params.id)?.expanded, ); } }, diff --git a/packages/grid/_modules_/grid/locales/frFR.ts b/packages/grid/_modules_/grid/locales/frFR.ts index 005352eeeec0b..39e53a8720457 100644 --- a/packages/grid/_modules_/grid/locales/frFR.ts +++ b/packages/grid/_modules_/grid/locales/frFR.ts @@ -97,7 +97,7 @@ const frFRGrid: Partial = { // Actions cell more text actionsCellMore: 'Plus', - // Tree data + // Tree Data treeDataGroupingHeaderName: 'Groupe', }; diff --git a/packages/grid/_modules_/grid/models/api/gridLocaleTextApi.ts b/packages/grid/_modules_/grid/models/api/gridLocaleTextApi.ts index 2629c18be6272..7f6175489536d 100644 --- a/packages/grid/_modules_/grid/models/api/gridLocaleTextApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridLocaleTextApi.ts @@ -102,7 +102,7 @@ export interface GridLocaleText { // Actions cell more text actionsCellMore: string; - // Tree data + // Tree Data treeDataGroupingHeaderName: string; treeDataExpand: string; treeDataCollapse: string; diff --git a/packages/grid/_modules_/grid/models/api/gridRowApi.ts b/packages/grid/_modules_/grid/models/api/gridRowApi.ts index 756cda2644f9f..20ebeb98f1284 100644 --- a/packages/grid/_modules_/grid/models/api/gridRowApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridRowApi.ts @@ -53,12 +53,12 @@ export interface GridRowApi { * @returns {GridRowTreeNodeConfig} The row data. * @ignore - do not document. */ - UNSTABLE_getRowNode: (id: GridRowId) => GridRowTreeNodeConfig | null; + unstable_getRowNode: (id: GridRowId) => GridRowTreeNodeConfig | null; /** * Expand or collapse a row children. * @param {GridRowId} id the ID of the row to expand or collapse. * @param {boolean} isExpanded A boolean indicating if the row must be expanded or collapsed. * @ignore - do not document. */ - UNSTABLE_setRowExpansion: (id: GridRowId, isExpanded: boolean) => void; + unstable_setRowExpansion: (id: GridRowId, isExpanded: boolean) => void; } From 82497d22c5647afeca8bafeb15b3d8fc11d9db05 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 19 Oct 2021 11:35:19 +0200 Subject: [PATCH 257/390] Remove stories --- .../src/stories/grid-tree-data.stories.tsx | 106 ------------------ 1 file changed, 106 deletions(-) delete mode 100644 packages/storybook/src/stories/grid-tree-data.stories.tsx diff --git a/packages/storybook/src/stories/grid-tree-data.stories.tsx b/packages/storybook/src/stories/grid-tree-data.stories.tsx deleted file mode 100644 index 91d23e3f17168..0000000000000 --- a/packages/storybook/src/stories/grid-tree-data.stories.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import * as React from 'react'; -import { DataGridPro, GridToolbar, DataGridProProps } from '@mui/x-data-grid-pro'; -import { Meta } from '@storybook/react'; -import Button from '@mui/material/Button'; -import { useDemoTreeData } from '@mui/x-data-grid-generator'; - -export default { - title: 'X-Grid Tests/Tree Data', - component: DataGridPro, - parameters: { - options: { selectedPanel: 'storybook/storysource/panel' }, - }, -} as Meta; - -export function BasicTreeData() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3], randomLength: true }); - const [treeDataEnabled, setTreeDataEnabled] = React.useState(true); - - return ( - - - - - ); -} - -export function CustomGroupingColumn() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3], randomLength: true }); - - const groupingColDef = React.useMemo( - () => ({ - headerName: 'Custom header', - }), - [], - ); - - return ( - - ); -} - -export function TreeDataWithCheckboxSelection() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3], randomLength: true }); - - return ; -} - -export function TreeDataPagination() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3], randomLength: true }); - - return ( -
- -
- ); -} - -export function TreeDataToolbar() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3], randomLength: true }); - - return ( - - ); -} - -export function TreeDataAutoExpand() { - const { data, loading } = useDemoTreeData({ rowLength: [10, 5, 3], randomLength: true }); - - return ( - - ); -} From dc13482a1bf7a846b588791e929749f85880edba Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 19 Oct 2021 11:38:30 +0200 Subject: [PATCH 258/390] Add full example tree data --- .../group-pivot/TreeDataFullExample.js | 17 +++++++++++++++++ .../group-pivot/TreeDataFullExample.tsx | 17 +++++++++++++++++ .../data-grid/group-pivot/group-pivot.md | 4 ++++ 3 files changed, 38 insertions(+) create mode 100644 docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.js create mode 100644 docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.tsx diff --git a/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.js b/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.js new file mode 100644 index 0000000000000..948d0938d9f5d --- /dev/null +++ b/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.js @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { DataGridPro } from '@mui/x-data-grid-pro'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +export default function TreeDataFullExample() { + const { data, loading } = useDemoData({ + dataSet: 'Employee', + rowLength: 1000, + treeData: { maxDepth: 3, groupingField: 'name', averageChildren: 10 }, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.tsx b/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.tsx new file mode 100644 index 0000000000000..b8c9b4a24ad72 --- /dev/null +++ b/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { DataGridPro } from '@mui/x-data-grid-pro'; +import {useDemoData} from "@mui/x-data-grid-generator"; + + +export default function TreeDataFullExample() { + const { data, loading } = useDemoData({ dataSet: 'Employee', rowLength: 1000, treeData: { maxDepth: 3, groupingField: 'name', averageChildren: 10 } }); + + return ( +
+ +
+ ); +} diff --git a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md index ad94477061709..5a8ef58967462 100644 --- a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md +++ b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md @@ -99,6 +99,10 @@ You can limit the filtering to the top level rows with the `disableChildrenSorti {{"demo": "pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js", "bg": "inline", "defaultCodeOpen": false}} +### Full Example + +{{"demo": "pages/components/data-grid/group-pivot/TreeDataFullExample.js", "bg": "inline", "defaultCodeOpen": false}} + ## 🚧 Master detail [](https://mui.com/store/items/material-ui-pro/) > ⚠️ This feature isn't implemented yet. It's coming. From 481c3930745d8045078b368ac263a2e939728a00 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 19 Oct 2021 11:38:52 +0200 Subject: [PATCH 259/390] Prettier --- .../group-pivot/TreeDataFullExample.tsx | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.tsx b/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.tsx index b8c9b4a24ad72..948d0938d9f5d 100644 --- a/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.tsx @@ -1,17 +1,17 @@ import * as React from 'react'; import { DataGridPro } from '@mui/x-data-grid-pro'; -import {useDemoData} from "@mui/x-data-grid-generator"; - +import { useDemoData } from '@mui/x-data-grid-generator'; export default function TreeDataFullExample() { - const { data, loading } = useDemoData({ dataSet: 'Employee', rowLength: 1000, treeData: { maxDepth: 3, groupingField: 'name', averageChildren: 10 } }); + const { data, loading } = useDemoData({ + dataSet: 'Employee', + rowLength: 1000, + treeData: { maxDepth: 3, groupingField: 'name', averageChildren: 10 }, + }); - return ( -
- -
- ); + return ( +
+ +
+ ); } From 2192e409e09ebe285000efcb150bf7caf3404941 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 19 Oct 2021 11:52:09 +0200 Subject: [PATCH 260/390] Fix --- docs/pages/playground.tsx | 55 ++++++++----------- .../data-grid/group-pivot/BasicTreeData.js | 7 ++- .../data-grid/group-pivot/BasicTreeData.tsx | 7 ++- .../CustomGroupingColumnTreeData.js | 17 ++++-- .../CustomGroupingColumnTreeData.tsx | 13 +++-- .../DefaultGroupingExpansionDepthTreeData.js | 1 - .../DefaultGroupingExpansionDepthTreeData.tsx | 1 - .../group-pivot/SetRowExpansionTreeData.js | 8 +-- .../group-pivot/SetRowExpansionTreeData.tsx | 8 +-- .../data-grid/group-pivot/TreeDataWithGap.js | 1 - .../data-grid/group-pivot/TreeDataWithGap.tsx | 1 - .../cell/GridTreeDataGroupingCell.tsx | 10 +++- 12 files changed, 65 insertions(+), 64 deletions(-) diff --git a/docs/pages/playground.tsx b/docs/pages/playground.tsx index fb8e66bf94694..f3d111c0c6da9 100644 --- a/docs/pages/playground.tsx +++ b/docs/pages/playground.tsx @@ -1,9 +1,13 @@ import * as React from 'react'; -import { DataGridPro, GridColumns, GridRowsProp, GridSortModel } from '@mui/x-data-grid-pro'; -import Stack from '@mui/material/Stack'; -import Button from '@mui/material/Button'; +import { DataGridPro, GridColumns, GridRowsProp } from '@mui/x-data-grid-pro'; const rows: GridRowsProp = [ + { + hierarchy: ['Sarah'], + jobTitle: 'CEO', + recruitmentDate: new Date(2014, 7, 22), + id: 0, + }, { hierarchy: ['Sarah', 'Thomas'], jobTitle: 'Head of Sales', @@ -92,39 +96,26 @@ const rows: GridRowsProp = [ const columns: GridColumns = [ { field: 'jobTitle', headerName: 'Job Title', width: 200 }, - { field: 'recruitmentDate', headerName: 'Recruitment Date', type: 'date', width: 150 }, + { + field: 'recruitmentDate', + headerName: 'Recruitment Date', + type: 'date', + width: 150, + }, ]; const getTreeDataPath = (row) => row.hierarchy; -export default function DisableChildrenSortingTreeData() { - const [disableChildrenSorting, setDisableChildrenSorting] = React.useState(true); - const [sortModel, setSortModel] = React.useState([ - { field: 'jobTitle', sort: 'desc' }, - ]); - - console.log(disableChildrenSorting); - +export default function BasicTreeData() { return ( - - -
- -
-
+
+ +
); } diff --git a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js index 9d8b2e2d5712a..fea6f91713c00 100644 --- a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js @@ -2,6 +2,12 @@ import * as React from 'react'; import { DataGridPro } from '@mui/x-data-grid-pro'; const rows = [ + { + hierarchy: ['Sarah'], + jobTitle: 'CEO', + recruitmentDate: new Date(2014, 7, 22), + id: 0, + }, { hierarchy: ['Sarah', 'Thomas'], jobTitle: 'Head of Sales', @@ -107,7 +113,6 @@ export default function BasicTreeData() { treeData rows={rows} columns={columns} - disableSelectionOnClick getTreeDataPath={getTreeDataPath} />
diff --git a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx index 01ea79afb6892..2d65d4a80dfb9 100644 --- a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx @@ -2,6 +2,12 @@ import * as React from 'react'; import { DataGridPro, GridColumns, GridRowsProp } from '@mui/x-data-grid-pro'; const rows: GridRowsProp = [ + { + hierarchy: ['Sarah'], + jobTitle: 'CEO', + recruitmentDate: new Date(2014, 7, 22), + id: 0, + }, { hierarchy: ['Sarah', 'Thomas'], jobTitle: 'Head of Sales', @@ -107,7 +113,6 @@ export default function BasicTreeData() { treeData rows={rows} columns={columns} - disableSelectionOnClick getTreeDataPath={getTreeDataPath} />
diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js index 773197f8db2b3..3e7610a72f7fd 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js @@ -18,7 +18,7 @@ export const isNavigationKey = (key) => key === ' '; const CustomGridTreeDataGroupingCell = (props) => { - const { id } = props; + const { id, field } = props; const apiRef = useGridApiContext(); const descendantCountLookup = useGridSelector( apiRef, @@ -37,6 +37,12 @@ const CustomGridTreeDataGroupingCell = (props) => { } }; + const handleClick = (event) => { + apiRef.current.unstable_setRowExpansion(id, !node?.expanded); + apiRef.current.setCellFocus(id, field); + event.stopPropagation(); + }; + if (!node) { throw new Error(`MUI: No row with id #${id} found`); } @@ -46,9 +52,7 @@ const CustomGridTreeDataGroupingCell = (props) => {
{descendantCount > 0 ? (
- +
); diff --git a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx index 9e8c333209e8d..3e09f621ebffb 100644 --- a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx @@ -23,13 +23,7 @@ export default function SetRowExpansionTreeData() {
- +
); diff --git a/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.js b/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.js index 531800d7804da..f0912b5aabc3c 100644 --- a/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.js +++ b/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.js @@ -96,7 +96,6 @@ export default function TreeDataWithGap() { treeData rows={rows} columns={columns} - disableSelectionOnClick getTreeDataPath={getTreeDataPath} />
diff --git a/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.tsx b/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.tsx index ebd5efc704ad9..25bd06afb41e7 100644 --- a/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.tsx @@ -96,7 +96,6 @@ export default function TreeDataWithGap() { treeData rows={rows} columns={columns} - disableSelectionOnClick getTreeDataPath={getTreeDataPath} />
diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index f5723b1d05bc6..c06fbdcc6a0b9 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -25,7 +25,7 @@ const useStyles = makeStyles({ }); const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { - const { id } = props; + const { id, field } = props; const rootProps = useGridRootProps(); const apiRef = useGridApiContext(); @@ -47,6 +47,12 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { } }; + const handleClick = (event) => { + apiRef.current.unstable_setRowExpansion(id, !node?.expanded); + apiRef.current.setCellFocus(id, field); + event.stopPropagation(); + }; + if (!node) { throw new Error(`MUI: No row with id #${id} found`); } @@ -57,7 +63,7 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { {descendantCount > 0 && ( apiRef.current.unstable_setRowExpansion(id, !node?.expanded)} + onClick={handleClick} onKeyDown={handleKeyDown} tabIndex={-1} aria-label={ From 0d862c171716996b8c8895da0c6d303ebd211b5c Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 19 Oct 2021 11:59:44 +0200 Subject: [PATCH 261/390] Add node to params --- .../CustomGroupingColumnTreeData.js | 33 ++++++++++++++----- .../CustomGroupingColumnTreeData.tsx | 11 ++----- .../cell/GridTreeDataGroupingCell.tsx | 17 ++++------ .../hooks/features/rows/useGridParamsApi.ts | 8 +++-- .../grid/models/params/gridCellParams.ts | 6 +++- 5 files changed, 45 insertions(+), 30 deletions(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js index 3e7610a72f7fd..01e55cafc9090 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js @@ -18,14 +18,13 @@ export const isNavigationKey = (key) => key === ' '; const CustomGridTreeDataGroupingCell = (props) => { - const { id, field } = props; + const { id, field, rowNode } = props; const apiRef = useGridApiContext(); const descendantCountLookup = useGridSelector( apiRef, gridVisibleDescendantCountLookupSelector, ); - const node = apiRef.current.unstable_getRowNode(id); const descendantCount = descendantCountLookup[id]; const handleKeyDown = (event) => { @@ -38,17 +37,13 @@ const CustomGridTreeDataGroupingCell = (props) => { }; const handleClick = (event) => { - apiRef.current.unstable_setRowExpansion(id, !node?.expanded); + apiRef.current.unstable_setRowExpansion(id, !rowNode.expanded); apiRef.current.setCellFocus(id, field); event.stopPropagation(); }; - if (!node) { - throw new Error(`MUI: No row with id #${id} found`); - } - return ( - +
{descendantCount > 0 ? (
- {rowNode.label} + {rowNode.treeGroupingValue} {descendantCount > 0 ? ` (${descendantCount})` : ''}
diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index 5d6d430ac67c7..a3a2a319b27f9 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -13,13 +13,40 @@ export interface GridRowModelUpdate extends GridRowData { } export interface GridRowTreeNodeConfig { + /** + * The grid row id. + */ id: GridRowId; + + /** + * The id of the row children + */ children?: GridRowId[]; + + /** + * Amount of descendants (children, children's children, ...) before the filtering + */ descendantCount?: number; + + /** + * The row id of the parent (null if this row is a top level row) + */ parent: GridRowId | null; + + /** + * Current expansion status of the row + */ expanded?: boolean; + + /** + * 0-based depth of the row in the tree + */ depth: number; - label: string; + + /** + * The value used the group the children of the row + */ + treeGroupingValue: string; /** * If `true`, this node has been automatically added to fill a gap in the tree structure diff --git a/packages/grid/_modules_/grid/utils/rowTreeUtils.ts b/packages/grid/_modules_/grid/utils/rowTreeUtils.ts index 6a69758a0fa79..fca5f1a9c18bc 100644 --- a/packages/grid/_modules_/grid/utils/rowTreeUtils.ts +++ b/packages/grid/_modules_/grid/utils/rowTreeUtils.ts @@ -60,9 +60,8 @@ export const buildRowTree = (params: GenerateRowTreeParams): GridRowGroupingResu id: nodeId, isAutoGenerated: true, expanded, - children: {}, parent: parentNode?.id ?? null, - label: row.path[depth], + treeGroupingValue: row.path[depth], depth, }; @@ -77,7 +76,7 @@ export const buildRowTree = (params: GenerateRowTreeParams): GridRowGroupingResu id: row.id, expanded, parent: parentNode?.id ?? null, - label: row.path[depth], + treeGroupingValue: row.path[depth], depth, }; } From b8d0698a34d1ebb07b06358f1fb8fc930fd90de3 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 19 Oct 2021 12:20:19 +0200 Subject: [PATCH 264/390] Work --- .../grid/hooks/features/pagination/useGridPage.ts | 8 -------- .../_modules_/grid/hooks/features/rows/useGridRows.ts | 8 ++++++++ packages/grid/_modules_/grid/models/api/gridRowApi.ts | 4 ++-- packages/grid/_modules_/grid/models/colDef/gridColDef.ts | 2 +- packages/grid/_modules_/grid/models/gridFilterOperator.ts | 4 +--- packages/grid/_modules_/grid/models/gridOptions.tsx | 3 ++- 6 files changed, 14 insertions(+), 15 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts index 9548bb3e858fd..9933f2f6c43ff 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts @@ -66,14 +66,6 @@ export const useGridPage = ( changeEvent: GridEvents.pageChange, }); - apiRef.current.updateControlState({ - stateId: 'page', - propModel: props.page, - propOnChange: props.onPageChange, - stateSelector: gridPageSelector, - changeEvent: GridEvents.pageChange, - }); - const setPage = React.useCallback( (page: number) => { logger.debug(`Setting page to ${page}`); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 5095a96d8053e..45f9869e8a917 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -39,7 +39,15 @@ export type GridRowsInternalCacheState = Pick void; /** - * Gets the id of a row for a given index in the list of the sorted unfiltered rows. + * Gets the `GridRowId` of a row at a specific index. * @param {number} index The index of the row * @returns {GridRowId} The `GridRowId` of the row. */ getRowIdFromRowIndex: (index: number) => GridRowId; /** - * Gets the index of a row for a given id in the list of the sorted unfiltered rows. + * Gets the row index of a row with a given id. * @param {GridRowId} id The `GridRowId` of the row. * @returns {number} The index of the row. */ diff --git a/packages/grid/_modules_/grid/models/colDef/gridColDef.ts b/packages/grid/_modules_/grid/models/colDef/gridColDef.ts index 9056032e929d3..25ffecb1563c0 100644 --- a/packages/grid/_modules_/grid/models/colDef/gridColDef.ts +++ b/packages/grid/_modules_/grid/models/colDef/gridColDef.ts @@ -157,7 +157,7 @@ export interface GridColDef { */ filterOperators?: GridFilterOperator[]; /** - * If `true`, the `renderCell` / will be called for the auto generated rows + * If `true`, the `renderCell` will be called for the auto generated rows */ shouldRenderAutoGeneratedRows?: boolean; /** diff --git a/packages/grid/_modules_/grid/models/gridFilterOperator.ts b/packages/grid/_modules_/grid/models/gridFilterOperator.ts index d1ffff9adecec..3c94b8d8401a3 100644 --- a/packages/grid/_modules_/grid/models/gridFilterOperator.ts +++ b/packages/grid/_modules_/grid/models/gridFilterOperator.ts @@ -4,15 +4,13 @@ import { GridFilterItem } from './gridFilterItem'; import { GridCellParams } from './params/gridCellParams'; import type { GridStateColDef } from './colDef'; -export type GridFilterCallback = (params: GridCellParams) => boolean; - export interface GridFilterOperator { label?: string; value: string; getApplyFilterFn: ( filterItem: GridFilterItem, column: GridStateColDef, - ) => null | GridFilterCallback; + ) => null | ((params: GridCellParams) => boolean); InputComponent?: React.JSXElementConstructor; InputComponentProps?: Record; } diff --git a/packages/grid/_modules_/grid/models/gridOptions.tsx b/packages/grid/_modules_/grid/models/gridOptions.tsx index 37436c380f499..a1ad11c4c3453 100644 --- a/packages/grid/_modules_/grid/models/gridOptions.tsx +++ b/packages/grid/_modules_/grid/models/gridOptions.tsx @@ -223,7 +223,8 @@ export interface GridSimpleOptions { */ treeData: boolean; /** - * If defined, the row children will be automatically expanded up to this depth + * If above 0, the row children will be expanded up to this depth + * If equal to -1, all the row children will be expanded * @default 0 */ defaultGroupingExpansionDepth: number; From bb913947f787cd4a69440daa4a6f443994a062db Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 19 Oct 2021 12:23:48 +0200 Subject: [PATCH 265/390] Improve perf getFlatRowTree --- docs/pages/api-docs/data-grid/grid-api.md | 4 ++-- .../api-docs/data-grid/grid-cell-params.md | 1 + docs/pages/api-docs/data-grid/grid-col-def.md | 2 +- .../grid/components/cell/GridActionsCell.tsx | 2 +- .../cell/GridTreeDataGroupingCell.tsx | 2 +- .../GridCellCheckboxRenderer.tsx | 2 +- .../useGridRowGroupsPreProcessing.ts | 21 ++++++++++++------- packages/grid/data-grid/src/DataGrid.tsx | 3 ++- packages/grid/x-grid/src/DataGridPro.tsx | 3 ++- 9 files changed, 24 insertions(+), 16 deletions(-) diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index aa91e1d36b739..15422020c3521 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -38,8 +38,8 @@ import { GridApi } from '@mui/x-data-grid-pro'; | getLocaleText | <T extends keyof GridLocaleText>(key: T) => GridLocaleText[T] | Returns the translation for the `key`. | | getRow | (id: GridRowId) => null \| GridRowData | Gets the row data with a given id. | | getRowElement | (id: GridRowId) => null \| HTMLDivElement | Gets the underlying DOM element for a row at the given `id`. | -| getRowIdFromRowIndex | (index: number) => GridRowId | Gets the id of a row for a given index in the list of the sorted unfiltered rows. | -| getRowIndex | (id: GridRowId) => number | Gets the index of a row for a given id in the list of the sorted unfiltered rows. | +| getRowIdFromRowIndex | (index: number) => GridRowId | Gets the `GridRowId` of a row at a specific index. | +| getRowIndex | (id: GridRowId) => number | Gets the row index of a row with a given id. | | getRowMode | (id: GridRowId) => GridRowMode | Gets the mode of a row. | | getRowModels | () => Map<GridRowId, GridRowData> | Gets the full set of rows as Map<GridRowId, GridRowModel>. | | getRowParams | (id: GridRowId) => GridRowParams | Gets the [GridRowParams](/api/data-grid/grid-row-params/) object that is passed as argument in events. | diff --git a/docs/pages/api-docs/data-grid/grid-cell-params.md b/docs/pages/api-docs/data-grid/grid-cell-params.md index f1eb55ff434f8..735fde3aec644 100644 --- a/docs/pages/api-docs/data-grid/grid-cell-params.md +++ b/docs/pages/api-docs/data-grid/grid-cell-params.md @@ -23,5 +23,6 @@ import { GridCellParams } from '@mui/x-data-grid'; | id | GridRowId | The grid row id. | | isEditable? | boolean | If true, the cell is editable. | | row | GridRowData | The row model of the row that the current cell belongs to. | +| rowNode | GridRowTreeNodeConfig | The node of the row that the current cell belongs to | | tabIndex | 0 \| -1 | the tabIndex value. | | value | GridCellValue | The cell value, but if the column has valueGetter, use getValue. | diff --git a/docs/pages/api-docs/data-grid/grid-col-def.md b/docs/pages/api-docs/data-grid/grid-col-def.md index cbba7a1933526..3928b29548895 100644 --- a/docs/pages/api-docs/data-grid/grid-col-def.md +++ b/docs/pages/api-docs/data-grid/grid-col-def.md @@ -35,7 +35,7 @@ import { GridColDef } from '@mui/x-data-grid'; | renderEditCell? | (params: GridRenderEditCellParams) => ReactNode | | Allows to override the component rendered in edit cell mode for this column. | | renderHeader? | (params: GridColumnHeaderParams) => ReactNode | | Allows to render a component in the column header cell. | | resizable? | boolean | true
| If `true`, the column is resizable. | -| shouldRenderAutoGeneratedRows? | boolean | | If `true`, the `renderCell` / will be called for the auto generated rows | +| shouldRenderAutoGeneratedRows? | boolean | | If `true`, the `renderCell` will be called for the auto generated rows | | sortable? | boolean | true
| If `true`, the column is sortable. | | sortComparator? | GridComparatorFn | | A comparator function used to sort rows. | | type? | string | 'string'
| Type allows to merge this object with a default definition [GridColDef](/api/data-grid/grid-col-def/). | diff --git a/packages/grid/_modules_/grid/components/cell/GridActionsCell.tsx b/packages/grid/_modules_/grid/components/cell/GridActionsCell.tsx index 7b8ad6e586682..25988e7067b16 100644 --- a/packages/grid/_modules_/grid/components/cell/GridActionsCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridActionsCell.tsx @@ -154,8 +154,8 @@ GridActionsCell.propTypes = { expanded: PropTypes.bool, id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, isAutoGenerated: PropTypes.bool, - label: PropTypes.string.isRequired, parent: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + treeGroupingValue: PropTypes.string.isRequired, }).isRequired, /** * the tabIndex value. diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index b473275759aca..2536b47595097 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -145,8 +145,8 @@ GridTreeDataGroupingCell.propTypes = { expanded: PropTypes.bool, id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, isAutoGenerated: PropTypes.bool, - label: PropTypes.string.isRequired, parent: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + treeGroupingValue: PropTypes.string.isRequired, }).isRequired, /** * the tabIndex value. diff --git a/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx b/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx index 75eb3d855a6b5..7244a2aee6293 100644 --- a/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx +++ b/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx @@ -152,8 +152,8 @@ GridCellCheckboxForwardRef.propTypes = { expanded: PropTypes.bool, id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, isAutoGenerated: PropTypes.bool, - label: PropTypes.string.isRequired, parent: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + treeGroupingValue: PropTypes.string.isRequired, }).isRequired, /** * the tabIndex value. diff --git a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts index 262891b7ce41f..bf92f636d77e4 100644 --- a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts +++ b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts @@ -8,14 +8,19 @@ import { import { GridEvents } from '../../../constants/eventsConstants'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; -const getFlatRowTree: GridRowGroupingPreProcessing = (params) => ({ - tree: Object.fromEntries( - params.rowIds.map((id) => [id.toString(), { id, depth: 0, parent: null, label: '' }]), - ), - treeDepth: 1, - idRowsLookup: params.idRowsLookup, - rowIds: params.rowIds, -}); +const getFlatRowTree: GridRowGroupingPreProcessing = (params) => { + const tree = {}; + params.rowIds.forEach((rowId) => { + tree[rowId] = { id: rowId, depth: 0, parent: null, label: '' }; + }); + + return { + tree, + treeDepth: 1, + idRowsLookup: params.idRowsLookup, + rowIds: params.rowIds, + }; +}; export const useGridRowGroupsPreProcessing = (apiRef: GridApiRef) => { const rowGroupsPreProcessingRef = React.useRef( diff --git a/packages/grid/data-grid/src/DataGrid.tsx b/packages/grid/data-grid/src/DataGrid.tsx index 075da98068e3c..4e75c857fbbfb 100644 --- a/packages/grid/data-grid/src/DataGrid.tsx +++ b/packages/grid/data-grid/src/DataGrid.tsx @@ -112,7 +112,8 @@ DataGridRaw.propTypes = { */ componentsProps: PropTypes.object, /** - * If defined, the row children will be automatically expanded up to this depth + * If above 0, the row children will be expanded up to this depth + * If equal to -1, all the row children will be expanded * @default 0 */ defaultGroupingExpansionDepth: PropTypes.number, diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index 1ca70608a7487..6bbae0531fa37 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -135,7 +135,8 @@ DataGridProRaw.propTypes = { */ componentsProps: PropTypes.object, /** - * If defined, the row children will be automatically expanded up to this depth + * If above 0, the row children will be expanded up to this depth + * If equal to -1, all the row children will be expanded * @default 0 */ defaultGroupingExpansionDepth: PropTypes.number, From 898f3a8da1cb79ef1c76ab6039a5633269cbb310 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 19 Oct 2021 12:27:52 +0200 Subject: [PATCH 266/390] Small changes --- .../_modules_/grid/components/cell/GridActionsCell.tsx | 2 +- .../grid/components/cell/GridTreeDataGroupingCell.tsx | 4 ++-- .../columnSelection/GridCellCheckboxRenderer.tsx | 2 +- .../useGridRowGroupsPreProcessing.ts | 5 +++-- .../_modules_/grid/hooks/features/filter/useGridFilter.ts | 8 ++++---- packages/grid/_modules_/grid/models/gridRows.ts | 2 +- packages/grid/_modules_/grid/utils/rowTreeUtils.ts | 4 ++-- 7 files changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/grid/_modules_/grid/components/cell/GridActionsCell.tsx b/packages/grid/_modules_/grid/components/cell/GridActionsCell.tsx index 25988e7067b16..76bbd293efc4a 100644 --- a/packages/grid/_modules_/grid/components/cell/GridActionsCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridActionsCell.tsx @@ -155,7 +155,7 @@ GridActionsCell.propTypes = { id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, isAutoGenerated: PropTypes.bool, parent: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - treeGroupingValue: PropTypes.string.isRequired, + groupingValue: PropTypes.string.isRequired, }).isRequired, /** * the tabIndex value. diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index 2536b47595097..a016f09551c50 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -72,7 +72,7 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { )}
- {rowNode.treeGroupingValue} + {rowNode.groupingValue} {descendantCount > 0 ? ` (${descendantCount})` : ''} @@ -146,7 +146,7 @@ GridTreeDataGroupingCell.propTypes = { id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, isAutoGenerated: PropTypes.bool, parent: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - treeGroupingValue: PropTypes.string.isRequired, + groupingValue: PropTypes.string.isRequired, }).isRequired, /** * the tabIndex value. diff --git a/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx b/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx index 7244a2aee6293..ea4ae06ecbbe6 100644 --- a/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx +++ b/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx @@ -153,7 +153,7 @@ GridCellCheckboxForwardRef.propTypes = { id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, isAutoGenerated: PropTypes.bool, parent: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - treeGroupingValue: PropTypes.string.isRequired, + groupingValue: PropTypes.string.isRequired, }).isRequired, /** * the tabIndex value. diff --git a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts index bf92f636d77e4..6f3f739cf8237 100644 --- a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts +++ b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts @@ -1,5 +1,6 @@ import * as React from 'react'; import { GridApiRef } from '../../../models/api/gridApiRef'; +import { GridRowTreeConfig } from '../../../models/gridRows'; import { GridRowGroupsPreProcessingApi, GridRowGroupingPreProcessing, @@ -9,9 +10,9 @@ import { GridEvents } from '../../../constants/eventsConstants'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; const getFlatRowTree: GridRowGroupingPreProcessing = (params) => { - const tree = {}; + const tree: GridRowTreeConfig = {}; params.rowIds.forEach((rowId) => { - tree[rowId] = { id: rowId, depth: 0, parent: null, label: '' }; + tree[rowId] = { id: rowId, depth: 0, parent: null, groupingValue: '' }; }); return { diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 413e682f457f8..8aebc8f828844 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -184,11 +184,11 @@ export const useGridFilter = ( // No filter to apply const filteringMethod = buildAggregatedFilterApplier(filterModel); if (!filteringMethod) { - let visibleDescendantsCountLookup: Record = {}; + const visibleDescendantsCountLookup: Record = {}; if (shouldApplyTreeFiltering) { - visibleDescendantsCountLookup = Object.fromEntries( - rowIds.map((rowId) => [rowId, rowTree[rowId].descendantCount ?? 0]), - ); + rowIds.forEach((rowId) => { + visibleDescendantsCountLookup[rowId] = rowTree[rowId].descendantCount ?? 0; + }); } return { diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index a3a2a319b27f9..19728bbe4f907 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -46,7 +46,7 @@ export interface GridRowTreeNodeConfig { /** * The value used the group the children of the row */ - treeGroupingValue: string; + groupingValue: string; /** * If `true`, this node has been automatically added to fill a gap in the tree structure diff --git a/packages/grid/_modules_/grid/utils/rowTreeUtils.ts b/packages/grid/_modules_/grid/utils/rowTreeUtils.ts index fca5f1a9c18bc..094746973740d 100644 --- a/packages/grid/_modules_/grid/utils/rowTreeUtils.ts +++ b/packages/grid/_modules_/grid/utils/rowTreeUtils.ts @@ -61,7 +61,7 @@ export const buildRowTree = (params: GenerateRowTreeParams): GridRowGroupingResu isAutoGenerated: true, expanded, parent: parentNode?.id ?? null, - treeGroupingValue: row.path[depth], + groupingValue: row.path[depth], depth, }; @@ -76,7 +76,7 @@ export const buildRowTree = (params: GenerateRowTreeParams): GridRowGroupingResu id: row.id, expanded, parent: parentNode?.id ?? null, - treeGroupingValue: row.path[depth], + groupingValue: row.path[depth], depth, }; } From 77e2b15f616bd4d91551f1be659c5f3a9ba58e09 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 19 Oct 2021 12:47:56 +0200 Subject: [PATCH 267/390] Fix --- .../CustomGroupingColumnTreeData.js | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js index 01e55cafc9090..a5002589dbe34 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js @@ -75,18 +75,39 @@ CustomGridTreeDataGroupingCell.propTypes = { * The node of the row that the current cell belongs to */ rowNode: PropTypes.shape({ + /** + * The id of the row children + */ children: PropTypes.arrayOf( PropTypes.oneOfType([PropTypes.number, PropTypes.string]), ), + /** + * 0-based depth of the row in the tree + */ depth: PropTypes.number.isRequired, + /** + * Amount of descendants (children, children's children, ...) before the filtering + */ descendantCount: PropTypes.number, + /** + * Current expansion status of the row + */ expanded: PropTypes.bool, + /** + * The value used the group the children of the row + */ + groupingValue: PropTypes.string.isRequired, + /** + * The grid row id. + */ id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, /** * If `true`, this node has been automatically added to fill a gap in the tree structure */ isAutoGenerated: PropTypes.bool, - label: PropTypes.string.isRequired, + /** + * The row id of the parent (null if this row is a top level row) + */ parent: PropTypes.oneOfType([ PropTypes.oneOf([null]), PropTypes.number, From e6f9d2dd429ea8860b27b43013ec14d4aec638bd Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 19 Oct 2021 12:49:35 +0200 Subject: [PATCH 268/390] Proptypes --- .../grid/_modules_/grid/components/cell/GridActionsCell.tsx | 2 +- .../_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx | 2 +- .../components/columnSelection/GridCellCheckboxRenderer.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/grid/_modules_/grid/components/cell/GridActionsCell.tsx b/packages/grid/_modules_/grid/components/cell/GridActionsCell.tsx index 76bbd293efc4a..d685ada3ba0e7 100644 --- a/packages/grid/_modules_/grid/components/cell/GridActionsCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridActionsCell.tsx @@ -152,10 +152,10 @@ GridActionsCell.propTypes = { depth: PropTypes.number.isRequired, descendantCount: PropTypes.number, expanded: PropTypes.bool, + groupingValue: PropTypes.string.isRequired, id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, isAutoGenerated: PropTypes.bool, parent: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - groupingValue: PropTypes.string.isRequired, }).isRequired, /** * the tabIndex value. diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index a016f09551c50..5b46dbb6740e9 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -143,10 +143,10 @@ GridTreeDataGroupingCell.propTypes = { depth: PropTypes.number.isRequired, descendantCount: PropTypes.number, expanded: PropTypes.bool, + groupingValue: PropTypes.string.isRequired, id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, isAutoGenerated: PropTypes.bool, parent: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - groupingValue: PropTypes.string.isRequired, }).isRequired, /** * the tabIndex value. diff --git a/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx b/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx index ea4ae06ecbbe6..9644460ccc527 100644 --- a/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx +++ b/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx @@ -150,10 +150,10 @@ GridCellCheckboxForwardRef.propTypes = { depth: PropTypes.number.isRequired, descendantCount: PropTypes.number, expanded: PropTypes.bool, + groupingValue: PropTypes.string.isRequired, id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, isAutoGenerated: PropTypes.bool, parent: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - groupingValue: PropTypes.string.isRequired, }).isRequired, /** * the tabIndex value. From bb898af3d90f044ceeefae67f1c0381c1832250f Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 19 Oct 2021 13:25:53 +0200 Subject: [PATCH 269/390] Fix --- .../grid/_modules_/grid/components/cell/GridBooleanCell.tsx | 1 + packages/grid/data-grid/src/tests/filtering.DataGrid.test.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/components/cell/GridBooleanCell.tsx b/packages/grid/_modules_/grid/components/cell/GridBooleanCell.tsx index 0383eaaa8722e..0008bc34d6972 100644 --- a/packages/grid/_modules_/grid/components/cell/GridBooleanCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridBooleanCell.tsx @@ -26,6 +26,7 @@ export const GridBooleanCell = React.memo((props: GridRenderCellParams & SvgIcon api, field, row, + rowNode, colDef, cellMode, isEditable, diff --git a/packages/grid/data-grid/src/tests/filtering.DataGrid.test.tsx b/packages/grid/data-grid/src/tests/filtering.DataGrid.test.tsx index ed2a6434a5f34..f8e0066768ac1 100644 --- a/packages/grid/data-grid/src/tests/filtering.DataGrid.test.tsx +++ b/packages/grid/data-grid/src/tests/filtering.DataGrid.test.tsx @@ -113,7 +113,7 @@ describe(' - Filter', () => { .toErrorDev('`model.items` has more than 1 item'); }); - it('should apply the filterModel prop correctly', () => { + it.only('should apply the filterModel prop correctly', () => { render(); expect(getColumnValues()).to.deep.equal(['Adidas', 'Puma']); }); From c64c969a6b079d620a9609b7cbaa0978f848248a Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 19 Oct 2021 13:30:28 +0200 Subject: [PATCH 270/390] Remove useDemoTreeData --- .../group-pivot/SetRowExpansionTreeData.js | 121 +++++++++++++++-- .../group-pivot/SetRowExpansionTreeData.tsx | 127 ++++++++++++++++-- .../grid/x-grid-data-generator/src/index.ts | 1 - .../src/useDemoTreeData.ts | 103 -------------- 4 files changed, 232 insertions(+), 120 deletions(-) delete mode 100644 packages/grid/x-grid-data-generator/src/useDemoTreeData.ts diff --git a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js index c7461587e59a2..53a9204ee71cc 100644 --- a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js @@ -1,15 +1,114 @@ import * as React from 'react'; import { DataGridPro, useGridApiRef } from '@mui/x-data-grid-pro'; -import { useDemoTreeData } from '@mui/x-data-grid-generator'; -import Button from '@mui/material/Button'; import Stack from '@mui/material/Stack'; +import Button from '@mui/material/Button'; -export default function SetRowExpansionTreeData() { - const { data, loading } = useDemoTreeData({ - rowLength: [10, 5, 3], - randomLength: true, - }); +const rows = [ + { + hierarchy: ['Sarah'], + jobTitle: 'CEO', + recruitmentDate: new Date(2014, 7, 22), + id: 0, + }, + { + hierarchy: ['Sarah', 'Thomas'], + jobTitle: 'Head of Sales', + recruitmentDate: new Date(2017, 3, 4), + id: 1, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Robert'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 11, 20), + id: 2, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Karen'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 10, 14), + id: 3, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Nancy'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2018, 3, 29), + id: 4, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Daniel'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 21), + id: 5, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Christopher'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 20), + id: 6, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Donald'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2019, 6, 28), + id: 7, + }, + { + hierarchy: ['Sarah', 'Mary'], + jobTitle: 'Head of Engineering', + recruitmentDate: new Date(2016, 3, 14), + id: 8, + }, + { + hierarchy: ['Sarah', 'Mary', 'Jennifer'], + jobTitle: 'Tech lead front', + recruitmentDate: new Date(2016, 5, 17), + id: 9, + }, + { + hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], + jobTitle: 'Front-end developer', + recruitmentDate: new Date(2019, 11, 7), + id: 10, + }, + { + hierarchy: ['Sarah', 'Mary', 'Michael'], + jobTitle: 'Tech lead devops', + recruitmentDate: new Date(2021, 7, 1), + id: 11, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda'], + jobTitle: 'Tech lead back', + recruitmentDate: new Date(2017, 0, 12), + id: 12, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2019, 2, 22), + id: 13, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2018, 4, 19), + id: 14, + }, +]; +const columns = [ + { field: 'jobTitle', headerName: 'Job Title', width: 200 }, + { + field: 'recruitmentDate', + headerName: 'Recruitment Date', + type: 'date', + width: 150, + }, +]; + +const getTreeDataPath = (row) => row.hierarchy; + +export default function SetRowExpansionTreeData() { const apiRef = useGridApiRef(); const toggleFirstRow = () => { @@ -24,7 +123,13 @@ export default function SetRowExpansionTreeData() {
- +
); diff --git a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx index 3e09f621ebffb..34c7d6024e635 100644 --- a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx @@ -1,14 +1,119 @@ import * as React from 'react'; -import { DataGridPro, useGridApiRef } from '@mui/x-data-grid-pro'; -import { useDemoTreeData } from '@mui/x-data-grid-generator'; -import Button from '@mui/material/Button'; +import { + DataGridPro, + GridColumns, + GridRowsProp, + useGridApiRef, +} from '@mui/x-data-grid-pro'; import Stack from '@mui/material/Stack'; +import Button from '@mui/material/Button'; + +const rows: GridRowsProp = [ + { + hierarchy: ['Sarah'], + jobTitle: 'CEO', + recruitmentDate: new Date(2014, 7, 22), + id: 0, + }, + { + hierarchy: ['Sarah', 'Thomas'], + jobTitle: 'Head of Sales', + recruitmentDate: new Date(2017, 3, 4), + id: 1, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Robert'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 11, 20), + id: 2, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Karen'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 10, 14), + id: 3, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Nancy'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2018, 3, 29), + id: 4, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Daniel'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 21), + id: 5, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Christopher'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2020, 7, 20), + id: 6, + }, + { + hierarchy: ['Sarah', 'Thomas', 'Donald'], + jobTitle: 'Sales Person', + recruitmentDate: new Date(2019, 6, 28), + id: 7, + }, + { + hierarchy: ['Sarah', 'Mary'], + jobTitle: 'Head of Engineering', + recruitmentDate: new Date(2016, 3, 14), + id: 8, + }, + { + hierarchy: ['Sarah', 'Mary', 'Jennifer'], + jobTitle: 'Tech lead front', + recruitmentDate: new Date(2016, 5, 17), + id: 9, + }, + { + hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], + jobTitle: 'Front-end developer', + recruitmentDate: new Date(2019, 11, 7), + id: 10, + }, + { + hierarchy: ['Sarah', 'Mary', 'Michael'], + jobTitle: 'Tech lead devops', + recruitmentDate: new Date(2021, 7, 1), + id: 11, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda'], + jobTitle: 'Tech lead back', + recruitmentDate: new Date(2017, 0, 12), + id: 12, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2019, 2, 22), + id: 13, + }, + { + hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], + jobTitle: 'Back-end developer', + recruitmentDate: new Date(2018, 4, 19), + id: 14, + }, +]; + +const columns: GridColumns = [ + { field: 'jobTitle', headerName: 'Job Title', width: 200 }, + { + field: 'recruitmentDate', + headerName: 'Recruitment Date', + type: 'date', + width: 150, + }, +]; + +const getTreeDataPath = (row) => row.hierarchy; export default function SetRowExpansionTreeData() { - const { data, loading } = useDemoTreeData({ - rowLength: [10, 5, 3], - randomLength: true, - }); const apiRef = useGridApiRef(); const toggleFirstRow = () => { @@ -23,7 +128,13 @@ export default function SetRowExpansionTreeData() {
- +
); diff --git a/packages/grid/x-grid-data-generator/src/index.ts b/packages/grid/x-grid-data-generator/src/index.ts index 3b688cac86f5c..a5590f5ed67e6 100644 --- a/packages/grid/x-grid-data-generator/src/index.ts +++ b/packages/grid/x-grid-data-generator/src/index.ts @@ -3,4 +3,3 @@ export * from './services'; export * from './commodities.columns'; export * from './employees.columns'; export * from './useDemoData'; -export * from './useDemoTreeData'; diff --git a/packages/grid/x-grid-data-generator/src/useDemoTreeData.ts b/packages/grid/x-grid-data-generator/src/useDemoTreeData.ts deleted file mode 100644 index b3099a82ba5cd..0000000000000 --- a/packages/grid/x-grid-data-generator/src/useDemoTreeData.ts +++ /dev/null @@ -1,103 +0,0 @@ -import * as React from 'react'; -import { GeneratedDemoData, randomInt } from '@mui/x-data-grid-generator/services'; -import { DataGridProProps, GridColumns, GridRowModel } from '@mui/x-data-grid-pro'; - -interface GridDemoTreeDataOptions { - rowLength: number[]; - randomLength: boolean; -} - -interface GridDemoTreeData extends GeneratedDemoData, Pick {} - -interface GridDemoTreeDataResponse { - loading: boolean; - data: GridDemoTreeData; -} - -const TREE_DATA_COLUMNS: GridColumns = [ - { - field: 'id', - hide: true, - type: 'number', - }, - { - field: 'name', - headerName: 'Name', - width: 150, - }, - { - field: 'index', - headerName: 'Index', - width: 50, - type: 'number', - }, -]; - -const getTreeDataPath = (row: GridRowModel) => row.path.map((value) => `Element ${value}`); - -interface GetTreeDataRowsOptions { - rowLength: number[]; - parentPath?: string[]; - id?: number; - randomLength: boolean; -} - -const getTreeDataRows = ({ - rowLength, - parentPath = [], - id = 0, - randomLength, -}: GetTreeDataRowsOptions) => { - const rows: GridRowModel[] = []; - - const [currentDepthRowLength, ...restRowLength] = rowLength; - - // For top level rows we respect the exact length given, for deeper rows we add a random factor is randomLength = true - const realCurrentDepthRowLength = - parentPath.length === 0 || !randomLength - ? currentDepthRowLength - : randomInt(0, currentDepthRowLength * 2); - - for (let index = 1; index < realCurrentDepthRowLength + 1; index += 1) { - const path = [...parentPath, index.toString()]; - const name = `Element n°${path.join('-')}`; - rows.push({ name, path, index, id }); - - id += 1; - - if (restRowLength.length) { - const childrenRows = getTreeDataRows({ - rowLength: restRowLength, - parentPath: path, - id, - randomLength, - }); - - rows.push(...childrenRows.rows); - id = childrenRows.id; - } - } - - return { rows, id }; -}; - -export const useDemoTreeData = (options: GridDemoTreeDataOptions) => { - const [response, setResponse] = React.useState(() => ({ - loading: true, - data: { columns: TREE_DATA_COLUMNS, getTreeDataPath, rows: [] }, - })); - - const rowLengthStr = options.rowLength.join('-'); - - React.useEffect(() => { - setResponse((prev) => (prev.loading ? prev : { ...prev, loading: true })); - const { rows } = getTreeDataRows({ - rowLength: rowLengthStr.split('-').map((el) => Number(el)), - randomLength: options.randomLength, - }); - - setResponse((prev) => ({ ...prev, loading: false, data: { ...prev.data, rows } })); - }, [rowLengthStr, options.randomLength]); - - return response; -}; From e0b2ed45bfe1cd21c1f59a6c74d24fc5a0d1585f Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 19 Oct 2021 13:39:32 +0200 Subject: [PATCH 271/390] Fix --- .../grid/_modules_/grid/hooks/features/filter/useGridFilter.ts | 1 - packages/grid/data-grid/src/tests/filtering.DataGrid.test.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index b7318bf948f1b..1e2e188e12f79 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -441,7 +441,6 @@ export const useGridFilter = ( isFirstRender.current = false; return; } - console.log('GO'); apiRef.current.applyFilters(); }, [apiRef, props.disableChildrenFiltering]); diff --git a/packages/grid/data-grid/src/tests/filtering.DataGrid.test.tsx b/packages/grid/data-grid/src/tests/filtering.DataGrid.test.tsx index f8e0066768ac1..ed2a6434a5f34 100644 --- a/packages/grid/data-grid/src/tests/filtering.DataGrid.test.tsx +++ b/packages/grid/data-grid/src/tests/filtering.DataGrid.test.tsx @@ -113,7 +113,7 @@ describe(' - Filter', () => { .toErrorDev('`model.items` has more than 1 item'); }); - it.only('should apply the filterModel prop correctly', () => { + it('should apply the filterModel prop correctly', () => { render(); expect(getColumnValues()).to.deep.equal(['Adidas', 'Puma']); }); From 757215645ac713cbfdaca6036312eed7f8d04310 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 19 Oct 2021 13:49:48 +0200 Subject: [PATCH 272/390] Fix --- .../grid/_modules_/grid/components/cell/GridEditBooleanCell.tsx | 1 + .../grid/_modules_/grid/components/cell/GridEditDateCell.tsx | 1 + .../grid/_modules_/grid/components/cell/GridEditInputCell.tsx | 1 + .../_modules_/grid/components/cell/GridEditSingleSelectCell.tsx | 1 + 4 files changed, 4 insertions(+) diff --git a/packages/grid/_modules_/grid/components/cell/GridEditBooleanCell.tsx b/packages/grid/_modules_/grid/components/cell/GridEditBooleanCell.tsx index 87f8965535217..aff474c88efd6 100644 --- a/packages/grid/_modules_/grid/components/cell/GridEditBooleanCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridEditBooleanCell.tsx @@ -31,6 +31,7 @@ export function GridEditBooleanCell( api, field, row, + rowNode, colDef, cellMode, isEditable, diff --git a/packages/grid/_modules_/grid/components/cell/GridEditDateCell.tsx b/packages/grid/_modules_/grid/components/cell/GridEditDateCell.tsx index 1bcc641e230ad..01742bf99a1ee 100644 --- a/packages/grid/_modules_/grid/components/cell/GridEditDateCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridEditDateCell.tsx @@ -26,6 +26,7 @@ export function GridEditDateCell(props: GridRenderEditCellParams & InputBaseProp api, field, row, + rowNode, colDef, cellMode, isEditable, diff --git a/packages/grid/_modules_/grid/components/cell/GridEditInputCell.tsx b/packages/grid/_modules_/grid/components/cell/GridEditInputCell.tsx index a6849dbbad419..c64ea211901f9 100644 --- a/packages/grid/_modules_/grid/components/cell/GridEditInputCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridEditInputCell.tsx @@ -27,6 +27,7 @@ function GridEditInputCell(props: GridRenderEditCellParams & InputBaseProps) { api, field, row, + rowNode, colDef, cellMode, isEditable, diff --git a/packages/grid/_modules_/grid/components/cell/GridEditSingleSelectCell.tsx b/packages/grid/_modules_/grid/components/cell/GridEditSingleSelectCell.tsx index 7965db8628fc1..62c9a678886fe 100644 --- a/packages/grid/_modules_/grid/components/cell/GridEditSingleSelectCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridEditSingleSelectCell.tsx @@ -30,6 +30,7 @@ function GridEditSingleSelectCell(props: GridRenderEditCellParams & SelectProps) api, field, row, + rowNode, colDef, cellMode, isEditable, From fb5d752922ce58cff0b19b343c0e8902de01aaae Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 19 Oct 2021 14:10:02 +0200 Subject: [PATCH 273/390] Fix tests --- .../hooks/features/filter/useGridFilter.ts | 37 +++++++++++++++---- .../src/tests/treeData.DataGridPro.test.tsx | 3 +- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 1e2e188e12f79..44a313238d0ea 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -212,11 +212,20 @@ export const useGridFilter = ( // A node is visible if // - One of its children is passing the filter // - He is passing the filter - const filterTreeNode = (node: GridRowTreeNodeConfig): boolean => { + const filterTreeNode = ( + node: GridRowTreeNodeConfig, + isParentMatchingFilters: boolean, + ): boolean => { + const shouldSkipFilters = props.disableChildrenFiltering && node.depth > 0; + const isMatchingFilters = shouldSkipFilters ? null : filteringMethod(node.id); + let visibleDescendantCount = 0; node.children?.forEach((childId) => { const childNode = rowTree[childId]; - const isNodeVisible = filterTreeNode(childNode); + const isNodeVisible = filterTreeNode( + childNode, + isMatchingFilters ?? isParentMatchingFilters, + ); let childrenVisibleNodeCount = visibleDescendantsCountLookup[childId] ?? 0; // TODO: For column grouping, we do not want to count the intermediate depth nodes in the visible descendant count @@ -227,13 +236,25 @@ export const useGridFilter = ( visibleDescendantCount += childrenVisibleNodeCount; }); - const shouldSkipFilters = props.disableChildrenFiltering && node.depth > 0; - const isMatchingFilters = shouldSkipFilters || filteringMethod(node.id); - const shouldBeVisible = - (!props.disableChildrenFiltering && visibleDescendantCount > 0) || isMatchingFilters; + let shouldBeVisible: boolean; + switch (isMatchingFilters) { + case true: { + shouldBeVisible = true; + break; + } + case false: { + shouldBeVisible = visibleDescendantCount > 0; + break; + } + default: { + shouldBeVisible = isParentMatchingFilters; + break; + } + } + visibleRowsLookup[node.id] = shouldBeVisible; - if (visibleDescendantCount > 0) { + if (shouldBeVisible && visibleDescendantCount > 0) { visibleDescendantsCountLookup[node.id] = visibleDescendantCount; } @@ -242,7 +263,7 @@ export const useGridFilter = ( Object.values(rowTree).forEach((node) => { if (node.depth === 0) { - filterTreeNode(node); + filterTreeNode(node, true); } }); } else { diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index 61970362d2a47..f4092061aef78 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -380,8 +380,9 @@ describe(' - Tree Data', () => { render( , ); From 6c6580cab3af2db325f4cb1d9243cc9b19bcd0fb Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 20 Oct 2021 09:13:09 +0200 Subject: [PATCH 274/390] Code review --- .../hooks/features/filter/useGridFilter.ts | 24 ++++--------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 73733f9ef8262..809147c05a5a2 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -136,29 +136,13 @@ export const useGridFilter = ( } return (rowId: GridRowId) => { - // We return `false` as soon as we have a failing filter + // Return `false` as soon as we have a failing filter if (linkOperator === GridLinkOperator.And) { - let isPassingFilters = true; - let filterIndex = 0; - - while (isPassingFilters && filterIndex < appliers.length) { - isPassingFilters = appliers[filterIndex](rowId); - filterIndex += 1; - } - - return isPassingFilters; - } - - // We return `true` as soon as we have a passing filter - let isPassingFilters = false; - let filterIndex = 0; - - while (!isPassingFilters && filterIndex < appliers.length) { - isPassingFilters = appliers[filterIndex](rowId); - filterIndex += 1; + return appliers.every((applier) => applier(rowId)); } - return isPassingFilters; + // Return `true` as soon as we have a passing filter + return appliers.some((applier) => applier(rowId)); }; }, [apiRef], From cf695f65ca50fff15dcad110a6e44f01a3493124 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 20 Oct 2021 09:58:47 +0200 Subject: [PATCH 275/390] Fix --- docs/pages/playground.tsx | 142 ++++-------------- .../grid/components/GridVirtualScroller.tsx | 43 ++---- .../features/filter/gridFilterSelector.ts | 11 +- .../keyboard/useGridKeyboardNavigation.ts | 29 ++-- .../pagination/gridPaginationSelector.ts | 31 ++-- .../grid/hooks/utils/useRowsInCurrentPage.ts | 35 +++++ .../grid/_modules_/grid/models/gridRows.ts | 2 + 7 files changed, 116 insertions(+), 177 deletions(-) create mode 100644 packages/grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts diff --git a/docs/pages/playground.tsx b/docs/pages/playground.tsx index f3d111c0c6da9..bbc95a23a2824 100644 --- a/docs/pages/playground.tsx +++ b/docs/pages/playground.tsx @@ -1,121 +1,35 @@ import * as React from 'react'; -import { DataGridPro, GridColumns, GridRowsProp } from '@mui/x-data-grid-pro'; +import { useData } from 'storybook/src/hooks/useData'; +import { DataGrid } from '@mui/x-data-grid'; +import { GridColumns } from '../../packages/grid/_modules_'; -const rows: GridRowsProp = [ - { - hierarchy: ['Sarah'], - jobTitle: 'CEO', - recruitmentDate: new Date(2014, 7, 22), - id: 0, - }, - { - hierarchy: ['Sarah', 'Thomas'], - jobTitle: 'Head of Sales', - recruitmentDate: new Date(2017, 3, 4), - id: 1, - }, - { - hierarchy: ['Sarah', 'Thomas', 'Robert'], - jobTitle: 'Sales Person', - recruitmentDate: new Date(2020, 11, 20), - id: 2, - }, - { - hierarchy: ['Sarah', 'Thomas', 'Karen'], - jobTitle: 'Sales Person', - recruitmentDate: new Date(2020, 10, 14), - id: 3, - }, - { - hierarchy: ['Sarah', 'Thomas', 'Nancy'], - jobTitle: 'Sales Person', - recruitmentDate: new Date(2018, 3, 29), - id: 4, - }, - { - hierarchy: ['Sarah', 'Thomas', 'Daniel'], - jobTitle: 'Sales Person', - recruitmentDate: new Date(2020, 7, 21), - id: 5, - }, - { - hierarchy: ['Sarah', 'Thomas', 'Christopher'], - jobTitle: 'Sales Person', - recruitmentDate: new Date(2020, 7, 20), - id: 6, - }, - { - hierarchy: ['Sarah', 'Thomas', 'Donald'], - jobTitle: 'Sales Person', - recruitmentDate: new Date(2019, 6, 28), - id: 7, - }, - { - hierarchy: ['Sarah', 'Mary'], - jobTitle: 'Head of Engineering', - recruitmentDate: new Date(2016, 3, 14), - id: 8, - }, - { - hierarchy: ['Sarah', 'Mary', 'Jennifer'], - jobTitle: 'Tech lead front', - recruitmentDate: new Date(2016, 5, 17), - id: 9, - }, - { - hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], - jobTitle: 'Front-end developer', - recruitmentDate: new Date(2019, 11, 7), - id: 10, - }, - { - hierarchy: ['Sarah', 'Mary', 'Michael'], - jobTitle: 'Tech lead devops', - recruitmentDate: new Date(2021, 7, 1), - id: 11, - }, - { - hierarchy: ['Sarah', 'Mary', 'Linda'], - jobTitle: 'Tech lead back', - recruitmentDate: new Date(2017, 0, 12), - id: 12, - }, - { - hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], - jobTitle: 'Back-end developer', - recruitmentDate: new Date(2019, 2, 22), - id: 13, - }, - { - hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], - jobTitle: 'Back-end developer', - recruitmentDate: new Date(2018, 4, 19), - id: 14, - }, -]; +const KeyboardTest = (props: { + nbRows?: number; + checkboxSelection?: boolean; + disableVirtualization?: boolean; + filterModel?: any; + width?: number; +}) => { + const data = useData(props.nbRows || 100, 20); + const transformColSizes = (columns: GridColumns) => + columns.map((column) => ({ ...column, width: 60 })); -const columns: GridColumns = [ - { field: 'jobTitle', headerName: 'Job Title', width: 200 }, - { - field: 'recruitmentDate', - headerName: 'Recruitment Date', - type: 'date', - width: 150, - }, -]; - -const getTreeDataPath = (row) => row.hierarchy; - -export default function BasicTreeData() { return ( -
- +
); -} +}; + +export default () => ( + +); diff --git a/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx b/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx index 7bb8bf5ad5d9a..dc5f57193f8e4 100644 --- a/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx +++ b/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx @@ -15,18 +15,14 @@ import { gridFocusCellSelector, gridTabIndexCellSelector, } from '../hooks/features/focus/gridFocusStateSelector'; -import { gridSortedVisibleRowEntriesSelector } from '../hooks/features/filter/gridFilterSelector'; import { gridDensityRowHeightSelector } from '../hooks/features/density/densitySelector'; import { gridEditRowsStateSelector } from '../hooks/features/editRows/gridEditRowsSelector'; import { GridEvents } from '../constants/eventsConstants'; -import { - gridPaginationRowRangeSelector, - gridSortedVisiblePaginatedRowEntriesSelector, -} from '../hooks/features/pagination/gridPaginationSelector'; import { useGridApiEventHandler } from '../hooks/utils/useGridApiEventHandler'; import { getDataGridUtilityClass } from '../gridClasses'; import { GridComponentProps } from '../GridComponentProps'; import { GridRowId } from '../models/gridRows'; +import { useRowsInCurrentPage } from '../hooks/utils/useRowsInCurrentPage'; type OwnerState = { classes: GridComponentProps['classes'] }; @@ -108,17 +104,12 @@ const GridVirtualScroller = React.forwardRef(null); const rootRef = React.useRef(null); const handleRef = useForkRef(ref, rootRef); @@ -130,24 +121,11 @@ const GridVirtualScroller = React.forwardRef { - if (rootProps.pagination && rootProps.paginationMode === 'client') { - return paginatedRowEntries; - } - - return visibleSortedRowEntries; - }, [ - rootProps.pagination, - rootProps.paginationMode, - visibleSortedRowEntries, - paginatedRowEntries, - ]); - const computeRenderContext = React.useCallback(() => { if (disableVirtualization) { return { firstRowIndex: 0, - lastRowIndex: rowsInCurrentPage.length, + lastRowIndex: rowsInCurrentPage.rows.length, firstColumnIndex: 0, lastColumnIndex: visibleColumns.length, }; @@ -156,7 +134,7 @@ const GridVirtualScroller = React.forwardRef, @@ -328,10 +305,10 @@ const GridVirtualScroller = React.forwardRef state.filter; @@ -37,7 +37,14 @@ export const gridSortedVisibleRowEntriesSelector = createSelector( export const gridSortedVisibleTopLevelRowEntriesSelector = createSelector( gridSortedVisibleRowEntriesSelector, gridRowTreeSelector, - (sortedVisibleRows, rowTree) => sortedVisibleRows.filter((row) => rowTree[row.id]?.depth === 0), + gridRowTreeDepthSelector, + (sortedVisibleRows, rowTree, rowTreeDepth) => { + if (rowTreeDepth < 2) { + return sortedVisibleRows; + } + + return sortedVisibleRows.filter((row) => rowTree[row.id]?.depth === 0); + }, ); export const gridVisibleRowCountSelector = createSelector( diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts index 077f0e7491cec..ea183d9cf5878 100644 --- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts +++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts @@ -18,11 +18,11 @@ import { import { gridContainerSizesSelector } from '../container/gridContainerSizesSelector'; import { visibleGridColumnsLengthSelector } from '../columns/gridColumnsSelector'; import { useGridSelector } from '../../utils/useGridSelector'; -import { gridPaginationRowRangeSelector } from '../pagination/gridPaginationSelector'; import { useGridLogger } from '../../utils/useGridLogger'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { GridComponentProps } from '../../../GridComponentProps'; import { gridSortedVisibleRowEntriesSelector } from '../filter/gridFilterSelector'; +import { useRowsInCurrentPage } from '../../utils/useRowsInCurrentPage'; const getNextCellIndexes = (key: string, indexes: GridCellIndexCoordinates) => { if (!isArrowKeys(key)) { @@ -71,13 +71,13 @@ const getNextColumnHeaderIndexes = (key: string, indexes: GridColumnHeaderIndexC */ export const useGridKeyboardNavigation = ( apiRef: GridApiRef, - props: Pick, + props: Pick, ): void => { const logger = useGridLogger(apiRef, 'useGridKeyboardNavigation'); - const statePaginationRange = useGridSelector(apiRef, gridPaginationRowRangeSelector); const colCount = useGridSelector(apiRef, visibleGridColumnsLengthSelector); const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowEntriesSelector); + const rowsInCurrentPage = useRowsInCurrentPage(apiRef, props); const mapKey = (event: React.KeyboardEvent) => { if (isEnterKey(event.key)) { @@ -92,15 +92,18 @@ export const useGridKeyboardNavigation = ( const navigateCells = React.useCallback( (params: GridCellParams, event: React.KeyboardEvent) => { event.preventDefault(); + + if (!rowsInCurrentPage.range) { + return; + } + const colIndex = apiRef.current.getColumnIndex(params.field); const rowIndex = visibleSortedRows.findIndex((row) => row.id === params.id); const key = mapKey(event); const isCtrlPressed = event.ctrlKey || event.metaKey || event.shiftKey; - const paginatedRowRange = props.pagination - ? statePaginationRange! - : { firstRowIndex: 0, lastRowIndex: visibleSortedRows.length - 1 }; - const rowCount = paginatedRowRange.lastRowIndex - paginatedRowRange.firstRowIndex + 1; + const rowCount = + rowsInCurrentPage.range.lastRowIndex - rowsInCurrentPage.range.firstRowIndex + 1; let nextCellIndexes: GridCellIndexCoordinates; if (isArrowKeys(key)) { @@ -118,7 +121,7 @@ export const useGridKeyboardNavigation = ( // In that case we go to first row, first col, or last row last col! let newRowIndex = 0; if (colIdx === 0) { - newRowIndex = paginatedRowRange.firstRowIndex; + newRowIndex = rowsInCurrentPage.range.firstRowIndex; } else { newRowIndex = rowCount - 1; } @@ -156,15 +159,7 @@ export const useGridKeyboardNavigation = ( const node = visibleSortedRows[nextCellIndexes.rowIndex]; apiRef.current.setCellFocus(node.id, field); }, - [ - apiRef, - visibleSortedRows, - statePaginationRange, - props.pagination, - colCount, - logger, - containerSizes, - ], + [apiRef, visibleSortedRows, colCount, logger, containerSizes, rowsInCurrentPage], ); const navigateColumnHeaders = React.useCallback( diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts index 06d74901aae6c..220fb548a6357 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts @@ -5,7 +5,7 @@ import { gridSortedVisibleTopLevelRowEntriesSelector, } from '../filter/gridFilterSelector'; import { GridPaginationState } from './gridPaginationState'; -import { gridRowTreeSelector } from '../rows/gridRowsSelector'; +import { gridRowTreeDepthSelector, gridRowTreeSelector } from '../rows/gridRowsSelector'; export const gridPaginationSelector = (state: GridState): GridPaginationState => state.pagination; @@ -22,21 +22,30 @@ export const gridPageSizeSelector = createSelector( export const gridPaginationRowRangeSelector = createSelector( gridPaginationSelector, gridRowTreeSelector, + gridRowTreeDepthSelector, gridSortedVisibleRowEntriesSelector, gridSortedVisibleTopLevelRowEntriesSelector, - (pagination, rowTree, visibleSortedRowEntries, visibleSortedTopLevelRowEntries) => { - const topLevelFirstRowIndex = pagination.pageSize * pagination.page; - const topLevelFirstRow = visibleSortedTopLevelRowEntries[topLevelFirstRowIndex]; + (pagination, rowTree, rowTreeDepth, visibleSortedRowEntries, visibleSortedTopLevelRowEntries) => { + const visibleTopLevelRowCount = visibleSortedTopLevelRowEntries.length; + const topLevelFirstRowIndex = Math.min( + pagination.pageSize * pagination.page, + visibleTopLevelRowCount - 1, + ); + const topLevelLastRowIndex = Math.min( + topLevelFirstRowIndex + pagination.pageSize - 1, + visibleTopLevelRowCount - 1, + ); - if (!topLevelFirstRow) { + if (topLevelFirstRowIndex === -1 || topLevelLastRowIndex === -1) { return null; } - const topLevelInCurrentPageCount = visibleSortedTopLevelRowEntries.slice( - topLevelFirstRowIndex, - topLevelFirstRowIndex + pagination.pageSize, - ).length; + if (rowTreeDepth < 2) { + return { firstRowIndex: topLevelFirstRowIndex, lastRowIndex: topLevelLastRowIndex }; + } + const topLevelFirstRow = visibleSortedTopLevelRowEntries[topLevelFirstRowIndex]; + const topLevelRowsInCurrentPageCount = topLevelLastRowIndex - topLevelFirstRowIndex + 1; const firstRowIndex = visibleSortedRowEntries.findIndex( (row) => row.id === topLevelFirstRow.id, ); @@ -45,12 +54,12 @@ export const gridPaginationRowRangeSelector = createSelector( while ( lastRowIndex < visibleSortedRowEntries.length && - topLevelRowAdded <= topLevelInCurrentPageCount + topLevelRowAdded <= topLevelRowsInCurrentPageCount ) { const row = visibleSortedRowEntries[lastRowIndex]; const depth = rowTree[row.id].depth; - if (topLevelRowAdded < topLevelInCurrentPageCount || depth > 0) { + if (topLevelRowAdded < topLevelRowsInCurrentPageCount || depth > 0) { lastRowIndex += 1; } diff --git a/packages/grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts b/packages/grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts new file mode 100644 index 0000000000000..c50bf8407683f --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts @@ -0,0 +1,35 @@ +import { GridComponentProps } from '../../GridComponentProps'; +import { + gridPaginationRowRangeSelector, + gridSortedVisiblePaginatedRowEntriesSelector, +} from '../features/pagination/gridPaginationSelector'; +import { gridSortedVisibleRowEntriesSelector } from '../features/filter/gridFilterSelector'; +import type { GridApiRef, GridRowEntry } from '../../models'; +import { useGridState } from './useGridState'; + +export const useRowsInCurrentPage = ( + apiRef: GridApiRef, + props: Pick, +) => { + const [state] = useGridState(apiRef); + + let range: { firstRowIndex: number; lastRowIndex: number } | null; + let rows: GridRowEntry[]; + + if (props.pagination && props.paginationMode === 'client') { + range = gridPaginationRowRangeSelector(state); + rows = gridSortedVisiblePaginatedRowEntriesSelector(state); + } else { + rows = gridSortedVisibleRowEntriesSelector(state); + if (rows.length === 0) { + range = null; + } else { + range = { firstRowIndex: 0, lastRowIndex: rows.length - 1 }; + } + } + + return { + rows, + range, + }; +}; diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index 19728bbe4f907..741f9638b2f4f 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -63,6 +63,8 @@ export type GridRowsLookup = Record; */ export type GridRowId = string | number; +export type GridRowEntry = { id: GridRowId; model: GridRowModel }; + /** * The function to retrieve the id of a [[GridRowData]]. */ From 417f624126295a639722972f04ae0f66a7b1b2b5 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 20 Oct 2021 10:08:01 +0200 Subject: [PATCH 276/390] Fix --- packages/grid/_modules_/grid/components/GridVirtualScroller.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx b/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx index dc5f57193f8e4..57f9231d847cb 100644 --- a/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx +++ b/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx @@ -246,7 +246,7 @@ const GridVirtualScroller = React.forwardRef { - if (!renderContext || containerWidth == null) { + if (!rowsInCurrentPage.range || !renderContext || containerWidth == null) { return null; } From 0fa25767035ff0776e2fba84f6ff17991f98b7f2 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 20 Oct 2021 10:38:58 +0200 Subject: [PATCH 277/390] Improve doc examples --- .../data-grid/group-pivot/BasicTreeData.js | 34 +++++++++---------- .../data-grid/group-pivot/BasicTreeData.tsx | 34 +++++++++---------- .../CustomGroupingColumnTreeData.js | 34 +++++++++---------- .../CustomGroupingColumnTreeData.tsx | 34 +++++++++---------- .../DefaultGroupingExpansionDepthTreeData.js | 34 +++++++++---------- .../DefaultGroupingExpansionDepthTreeData.tsx | 34 +++++++++---------- .../DisableChildrenFilteringTreeData.js | 19 ++++++++--- .../DisableChildrenFilteringTreeData.tsx | 20 ++++++++--- .../DisableChildrenSortingTreeData.js | 8 ++++- .../DisableChildrenSortingTreeData.tsx | 8 ++++- .../group-pivot/SetRowExpansionTreeData.js | 34 +++++++++---------- .../group-pivot/SetRowExpansionTreeData.tsx | 34 +++++++++---------- .../data-grid/group-pivot/TreeDataWithGap.js | 30 +++++++++------- .../data-grid/group-pivot/TreeDataWithGap.tsx | 30 +++++++++------- 14 files changed, 217 insertions(+), 170 deletions(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js index fea6f91713c00..b53e0acbb0e05 100644 --- a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.js @@ -4,90 +4,90 @@ import { DataGridPro } from '@mui/x-data-grid-pro'; const rows = [ { hierarchy: ['Sarah'], - jobTitle: 'CEO', - recruitmentDate: new Date(2014, 7, 22), + jobTitle: 'Head of Human Resources', + recruitmentDate: new Date(2020, 8, 12), id: 0, }, { - hierarchy: ['Sarah', 'Thomas'], + hierarchy: ['Thomas'], jobTitle: 'Head of Sales', recruitmentDate: new Date(2017, 3, 4), id: 1, }, { - hierarchy: ['Sarah', 'Thomas', 'Robert'], + hierarchy: ['Thomas', 'Robert'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 11, 20), id: 2, }, { - hierarchy: ['Sarah', 'Thomas', 'Karen'], + hierarchy: ['Thomas', 'Karen'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 10, 14), id: 3, }, { - hierarchy: ['Sarah', 'Thomas', 'Nancy'], + hierarchy: ['Thomas', 'Nancy'], jobTitle: 'Sales Person', - recruitmentDate: new Date(2018, 3, 29), + recruitmentDate: new Date(2017, 10, 29), id: 4, }, { - hierarchy: ['Sarah', 'Thomas', 'Daniel'], + hierarchy: ['Thomas', 'Daniel'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 7, 21), id: 5, }, { - hierarchy: ['Sarah', 'Thomas', 'Christopher'], + hierarchy: ['Thomas', 'Christopher'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 7, 20), id: 6, }, { - hierarchy: ['Sarah', 'Thomas', 'Donald'], + hierarchy: ['Thomas', 'Donald'], jobTitle: 'Sales Person', recruitmentDate: new Date(2019, 6, 28), id: 7, }, { - hierarchy: ['Sarah', 'Mary'], + hierarchy: ['Mary'], jobTitle: 'Head of Engineering', recruitmentDate: new Date(2016, 3, 14), id: 8, }, { - hierarchy: ['Sarah', 'Mary', 'Jennifer'], + hierarchy: ['Mary', 'Jennifer'], jobTitle: 'Tech lead front', recruitmentDate: new Date(2016, 5, 17), id: 9, }, { - hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], + hierarchy: ['Mary', 'Jennifer', 'Anna'], jobTitle: 'Front-end developer', recruitmentDate: new Date(2019, 11, 7), id: 10, }, { - hierarchy: ['Sarah', 'Mary', 'Michael'], + hierarchy: ['Mary', 'Michael'], jobTitle: 'Tech lead devops', recruitmentDate: new Date(2021, 7, 1), id: 11, }, { - hierarchy: ['Sarah', 'Mary', 'Linda'], + hierarchy: ['Mary', 'Linda'], jobTitle: 'Tech lead back', recruitmentDate: new Date(2017, 0, 12), id: 12, }, { - hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], + hierarchy: ['Mary', 'Linda', 'Elizabeth'], jobTitle: 'Back-end developer', recruitmentDate: new Date(2019, 2, 22), id: 13, }, { - hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], + hierarchy: ['Mary', 'Linda', 'William'], jobTitle: 'Back-end developer', recruitmentDate: new Date(2018, 4, 19), id: 14, diff --git a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx index 2d65d4a80dfb9..517431a1af3ce 100644 --- a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx @@ -4,90 +4,90 @@ import { DataGridPro, GridColumns, GridRowsProp } from '@mui/x-data-grid-pro'; const rows: GridRowsProp = [ { hierarchy: ['Sarah'], - jobTitle: 'CEO', - recruitmentDate: new Date(2014, 7, 22), + jobTitle: 'Head of Human Resources', + recruitmentDate: new Date(2020, 8, 12), id: 0, }, { - hierarchy: ['Sarah', 'Thomas'], + hierarchy: ['Thomas'], jobTitle: 'Head of Sales', recruitmentDate: new Date(2017, 3, 4), id: 1, }, { - hierarchy: ['Sarah', 'Thomas', 'Robert'], + hierarchy: ['Thomas', 'Robert'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 11, 20), id: 2, }, { - hierarchy: ['Sarah', 'Thomas', 'Karen'], + hierarchy: ['Thomas', 'Karen'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 10, 14), id: 3, }, { - hierarchy: ['Sarah', 'Thomas', 'Nancy'], + hierarchy: ['Thomas', 'Nancy'], jobTitle: 'Sales Person', - recruitmentDate: new Date(2018, 3, 29), + recruitmentDate: new Date(2017, 10, 29), id: 4, }, { - hierarchy: ['Sarah', 'Thomas', 'Daniel'], + hierarchy: ['Thomas', 'Daniel'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 7, 21), id: 5, }, { - hierarchy: ['Sarah', 'Thomas', 'Christopher'], + hierarchy: ['Thomas', 'Christopher'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 7, 20), id: 6, }, { - hierarchy: ['Sarah', 'Thomas', 'Donald'], + hierarchy: ['Thomas', 'Donald'], jobTitle: 'Sales Person', recruitmentDate: new Date(2019, 6, 28), id: 7, }, { - hierarchy: ['Sarah', 'Mary'], + hierarchy: ['Mary'], jobTitle: 'Head of Engineering', recruitmentDate: new Date(2016, 3, 14), id: 8, }, { - hierarchy: ['Sarah', 'Mary', 'Jennifer'], + hierarchy: ['Mary', 'Jennifer'], jobTitle: 'Tech lead front', recruitmentDate: new Date(2016, 5, 17), id: 9, }, { - hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], + hierarchy: ['Mary', 'Jennifer', 'Anna'], jobTitle: 'Front-end developer', recruitmentDate: new Date(2019, 11, 7), id: 10, }, { - hierarchy: ['Sarah', 'Mary', 'Michael'], + hierarchy: ['Mary', 'Michael'], jobTitle: 'Tech lead devops', recruitmentDate: new Date(2021, 7, 1), id: 11, }, { - hierarchy: ['Sarah', 'Mary', 'Linda'], + hierarchy: ['Mary', 'Linda'], jobTitle: 'Tech lead back', recruitmentDate: new Date(2017, 0, 12), id: 12, }, { - hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], + hierarchy: ['Mary', 'Linda', 'Elizabeth'], jobTitle: 'Back-end developer', recruitmentDate: new Date(2019, 2, 22), id: 13, }, { - hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], + hierarchy: ['Mary', 'Linda', 'William'], jobTitle: 'Back-end developer', recruitmentDate: new Date(2018, 4, 19), id: 14, diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js index a5002589dbe34..d85eba31c570d 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js @@ -119,90 +119,90 @@ CustomGridTreeDataGroupingCell.propTypes = { const rows = [ { hierarchy: ['Sarah'], - jobTitle: 'CEO', - recruitmentDate: new Date(2014, 7, 22), + jobTitle: 'Head of Human Resources', + recruitmentDate: new Date(2020, 8, 12), id: 0, }, { - hierarchy: ['Sarah', 'Thomas'], + hierarchy: ['Thomas'], jobTitle: 'Head of Sales', recruitmentDate: new Date(2017, 3, 4), id: 1, }, { - hierarchy: ['Sarah', 'Thomas', 'Robert'], + hierarchy: ['Thomas', 'Robert'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 11, 20), id: 2, }, { - hierarchy: ['Sarah', 'Thomas', 'Karen'], + hierarchy: ['Thomas', 'Karen'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 10, 14), id: 3, }, { - hierarchy: ['Sarah', 'Thomas', 'Nancy'], + hierarchy: ['Thomas', 'Nancy'], jobTitle: 'Sales Person', - recruitmentDate: new Date(2018, 3, 29), + recruitmentDate: new Date(2017, 10, 29), id: 4, }, { - hierarchy: ['Sarah', 'Thomas', 'Daniel'], + hierarchy: ['Thomas', 'Daniel'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 7, 21), id: 5, }, { - hierarchy: ['Sarah', 'Thomas', 'Christopher'], + hierarchy: ['Thomas', 'Christopher'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 7, 20), id: 6, }, { - hierarchy: ['Sarah', 'Thomas', 'Donald'], + hierarchy: ['Thomas', 'Donald'], jobTitle: 'Sales Person', recruitmentDate: new Date(2019, 6, 28), id: 7, }, { - hierarchy: ['Sarah', 'Mary'], + hierarchy: ['Mary'], jobTitle: 'Head of Engineering', recruitmentDate: new Date(2016, 3, 14), id: 8, }, { - hierarchy: ['Sarah', 'Mary', 'Jennifer'], + hierarchy: ['Mary', 'Jennifer'], jobTitle: 'Tech lead front', recruitmentDate: new Date(2016, 5, 17), id: 9, }, { - hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], + hierarchy: ['Mary', 'Jennifer', 'Anna'], jobTitle: 'Front-end developer', recruitmentDate: new Date(2019, 11, 7), id: 10, }, { - hierarchy: ['Sarah', 'Mary', 'Michael'], + hierarchy: ['Mary', 'Michael'], jobTitle: 'Tech lead devops', recruitmentDate: new Date(2021, 7, 1), id: 11, }, { - hierarchy: ['Sarah', 'Mary', 'Linda'], + hierarchy: ['Mary', 'Linda'], jobTitle: 'Tech lead back', recruitmentDate: new Date(2017, 0, 12), id: 12, }, { - hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], + hierarchy: ['Mary', 'Linda', 'Elizabeth'], jobTitle: 'Back-end developer', recruitmentDate: new Date(2019, 2, 22), id: 13, }, { - hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], + hierarchy: ['Mary', 'Linda', 'William'], jobTitle: 'Back-end developer', recruitmentDate: new Date(2018, 4, 19), id: 14, diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx index 7ea8c6c479ecf..eec85d0e6096c 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx @@ -67,90 +67,90 @@ const CustomGridTreeDataGroupingCell = (props: GridRenderCellParams) => { const rows: GridRowsProp = [ { hierarchy: ['Sarah'], - jobTitle: 'CEO', - recruitmentDate: new Date(2014, 7, 22), + jobTitle: 'Head of Human Resources', + recruitmentDate: new Date(2020, 8, 12), id: 0, }, { - hierarchy: ['Sarah', 'Thomas'], + hierarchy: ['Thomas'], jobTitle: 'Head of Sales', recruitmentDate: new Date(2017, 3, 4), id: 1, }, { - hierarchy: ['Sarah', 'Thomas', 'Robert'], + hierarchy: ['Thomas', 'Robert'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 11, 20), id: 2, }, { - hierarchy: ['Sarah', 'Thomas', 'Karen'], + hierarchy: ['Thomas', 'Karen'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 10, 14), id: 3, }, { - hierarchy: ['Sarah', 'Thomas', 'Nancy'], + hierarchy: ['Thomas', 'Nancy'], jobTitle: 'Sales Person', - recruitmentDate: new Date(2018, 3, 29), + recruitmentDate: new Date(2017, 10, 29), id: 4, }, { - hierarchy: ['Sarah', 'Thomas', 'Daniel'], + hierarchy: ['Thomas', 'Daniel'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 7, 21), id: 5, }, { - hierarchy: ['Sarah', 'Thomas', 'Christopher'], + hierarchy: ['Thomas', 'Christopher'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 7, 20), id: 6, }, { - hierarchy: ['Sarah', 'Thomas', 'Donald'], + hierarchy: ['Thomas', 'Donald'], jobTitle: 'Sales Person', recruitmentDate: new Date(2019, 6, 28), id: 7, }, { - hierarchy: ['Sarah', 'Mary'], + hierarchy: ['Mary'], jobTitle: 'Head of Engineering', recruitmentDate: new Date(2016, 3, 14), id: 8, }, { - hierarchy: ['Sarah', 'Mary', 'Jennifer'], + hierarchy: ['Mary', 'Jennifer'], jobTitle: 'Tech lead front', recruitmentDate: new Date(2016, 5, 17), id: 9, }, { - hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], + hierarchy: ['Mary', 'Jennifer', 'Anna'], jobTitle: 'Front-end developer', recruitmentDate: new Date(2019, 11, 7), id: 10, }, { - hierarchy: ['Sarah', 'Mary', 'Michael'], + hierarchy: ['Mary', 'Michael'], jobTitle: 'Tech lead devops', recruitmentDate: new Date(2021, 7, 1), id: 11, }, { - hierarchy: ['Sarah', 'Mary', 'Linda'], + hierarchy: ['Mary', 'Linda'], jobTitle: 'Tech lead back', recruitmentDate: new Date(2017, 0, 12), id: 12, }, { - hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], + hierarchy: ['Mary', 'Linda', 'Elizabeth'], jobTitle: 'Back-end developer', recruitmentDate: new Date(2019, 2, 22), id: 13, }, { - hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], + hierarchy: ['Mary', 'Linda', 'William'], jobTitle: 'Back-end developer', recruitmentDate: new Date(2018, 4, 19), id: 14, diff --git a/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.js b/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.js index fc5fb5376338d..61a48100c6b66 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.js @@ -4,90 +4,90 @@ import { DataGridPro } from '@mui/x-data-grid-pro'; const rows = [ { hierarchy: ['Sarah'], - jobTitle: 'CEO', - recruitmentDate: new Date(2014, 7, 22), + jobTitle: 'Head of Human Resources', + recruitmentDate: new Date(2020, 8, 12), id: 0, }, { - hierarchy: ['Sarah', 'Thomas'], + hierarchy: ['Thomas'], jobTitle: 'Head of Sales', recruitmentDate: new Date(2017, 3, 4), id: 1, }, { - hierarchy: ['Sarah', 'Thomas', 'Robert'], + hierarchy: ['Thomas', 'Robert'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 11, 20), id: 2, }, { - hierarchy: ['Sarah', 'Thomas', 'Karen'], + hierarchy: ['Thomas', 'Karen'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 10, 14), id: 3, }, { - hierarchy: ['Sarah', 'Thomas', 'Nancy'], + hierarchy: ['Thomas', 'Nancy'], jobTitle: 'Sales Person', - recruitmentDate: new Date(2018, 3, 29), + recruitmentDate: new Date(2017, 10, 29), id: 4, }, { - hierarchy: ['Sarah', 'Thomas', 'Daniel'], + hierarchy: ['Thomas', 'Daniel'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 7, 21), id: 5, }, { - hierarchy: ['Sarah', 'Thomas', 'Christopher'], + hierarchy: ['Thomas', 'Christopher'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 7, 20), id: 6, }, { - hierarchy: ['Sarah', 'Thomas', 'Donald'], + hierarchy: ['Thomas', 'Donald'], jobTitle: 'Sales Person', recruitmentDate: new Date(2019, 6, 28), id: 7, }, { - hierarchy: ['Sarah', 'Mary'], + hierarchy: ['Mary'], jobTitle: 'Head of Engineering', recruitmentDate: new Date(2016, 3, 14), id: 8, }, { - hierarchy: ['Sarah', 'Mary', 'Jennifer'], + hierarchy: ['Mary', 'Jennifer'], jobTitle: 'Tech lead front', recruitmentDate: new Date(2016, 5, 17), id: 9, }, { - hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], + hierarchy: ['Mary', 'Jennifer', 'Anna'], jobTitle: 'Front-end developer', recruitmentDate: new Date(2019, 11, 7), id: 10, }, { - hierarchy: ['Sarah', 'Mary', 'Michael'], + hierarchy: ['Mary', 'Michael'], jobTitle: 'Tech lead devops', recruitmentDate: new Date(2021, 7, 1), id: 11, }, { - hierarchy: ['Sarah', 'Mary', 'Linda'], + hierarchy: ['Mary', 'Linda'], jobTitle: 'Tech lead back', recruitmentDate: new Date(2017, 0, 12), id: 12, }, { - hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], + hierarchy: ['Mary', 'Linda', 'Elizabeth'], jobTitle: 'Back-end developer', recruitmentDate: new Date(2019, 2, 22), id: 13, }, { - hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], + hierarchy: ['Mary', 'Linda', 'William'], jobTitle: 'Back-end developer', recruitmentDate: new Date(2018, 4, 19), id: 14, diff --git a/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.tsx index 93ca3f69d417b..4b43a12797a74 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.tsx @@ -4,90 +4,90 @@ import { DataGridPro, GridColumns, GridRowsProp } from '@mui/x-data-grid-pro'; const rows: GridRowsProp = [ { hierarchy: ['Sarah'], - jobTitle: 'CEO', - recruitmentDate: new Date(2014, 7, 22), + jobTitle: 'Head of Human Resources', + recruitmentDate: new Date(2020, 8, 12), id: 0, }, { - hierarchy: ['Sarah', 'Thomas'], + hierarchy: ['Thomas'], jobTitle: 'Head of Sales', recruitmentDate: new Date(2017, 3, 4), id: 1, }, { - hierarchy: ['Sarah', 'Thomas', 'Robert'], + hierarchy: ['Thomas', 'Robert'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 11, 20), id: 2, }, { - hierarchy: ['Sarah', 'Thomas', 'Karen'], + hierarchy: ['Thomas', 'Karen'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 10, 14), id: 3, }, { - hierarchy: ['Sarah', 'Thomas', 'Nancy'], + hierarchy: ['Thomas', 'Nancy'], jobTitle: 'Sales Person', - recruitmentDate: new Date(2018, 3, 29), + recruitmentDate: new Date(2017, 10, 29), id: 4, }, { - hierarchy: ['Sarah', 'Thomas', 'Daniel'], + hierarchy: ['Thomas', 'Daniel'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 7, 21), id: 5, }, { - hierarchy: ['Sarah', 'Thomas', 'Christopher'], + hierarchy: ['Thomas', 'Christopher'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 7, 20), id: 6, }, { - hierarchy: ['Sarah', 'Thomas', 'Donald'], + hierarchy: ['Thomas', 'Donald'], jobTitle: 'Sales Person', recruitmentDate: new Date(2019, 6, 28), id: 7, }, { - hierarchy: ['Sarah', 'Mary'], + hierarchy: ['Mary'], jobTitle: 'Head of Engineering', recruitmentDate: new Date(2016, 3, 14), id: 8, }, { - hierarchy: ['Sarah', 'Mary', 'Jennifer'], + hierarchy: ['Mary', 'Jennifer'], jobTitle: 'Tech lead front', recruitmentDate: new Date(2016, 5, 17), id: 9, }, { - hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], + hierarchy: ['Mary', 'Jennifer', 'Anna'], jobTitle: 'Front-end developer', recruitmentDate: new Date(2019, 11, 7), id: 10, }, { - hierarchy: ['Sarah', 'Mary', 'Michael'], + hierarchy: ['Mary', 'Michael'], jobTitle: 'Tech lead devops', recruitmentDate: new Date(2021, 7, 1), id: 11, }, { - hierarchy: ['Sarah', 'Mary', 'Linda'], + hierarchy: ['Mary', 'Linda'], jobTitle: 'Tech lead back', recruitmentDate: new Date(2017, 0, 12), id: 12, }, { - hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], + hierarchy: ['Mary', 'Linda', 'Elizabeth'], jobTitle: 'Back-end developer', recruitmentDate: new Date(2019, 2, 22), id: 13, }, { - hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], + hierarchy: ['Mary', 'Linda', 'William'], jobTitle: 'Back-end developer', recruitmentDate: new Date(2018, 4, 19), id: 14, diff --git a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js index 838b38119e11c..c9d365081c2e5 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js @@ -1,11 +1,17 @@ import * as React from 'react'; -import { DataGridPro } from '@mui/x-data-grid-pro'; +import { DataGridPro, GridLinkOperator } from '@mui/x-data-grid-pro'; import Stack from '@mui/material/Stack'; import Checkbox from '@mui/material/Checkbox'; import FormGroup from '@mui/material/FormGroup'; import FormControlLabel from '@mui/material/FormControlLabel'; const rows = [ + { + hierarchy: ['Sarah'], + jobTitle: 'Head of Human Resources', + recruitmentDate: new Date(2020, 8, 12), + id: 0, + }, { hierarchy: ['Thomas'], jobTitle: 'Head of Sales', @@ -27,7 +33,7 @@ const rows = [ { hierarchy: ['Thomas', 'Nancy'], jobTitle: 'Sales Person', - recruitmentDate: new Date(2018, 3, 29), + recruitmentDate: new Date(2017, 10, 29), id: 4, }, { @@ -108,8 +114,13 @@ export default function DisableChildrenSortingTreeData() { const [disableChildrenFiltering, setDisableChildrenFiltering] = React.useState(true); const [filterModel, setFilterModel] = React.useState({ + linkOperator: GridLinkOperator.Or, items: [ - { columnField: 'jobTitle', operatorValue: 'contains', value: 'Head of Sales' }, + { + columnField: 'recruitmentDate', + operatorValue: 'before', + value: '2018-01-01', + }, ], }); @@ -135,7 +146,7 @@ export default function DisableChildrenSortingTreeData() { getTreeDataPath={getTreeDataPath} filterModel={filterModel} onFilterModelChange={setFilterModel} - defaultGroupingExpansionDepth={-1} + defaultGroupingExpansionDepth={1} />
diff --git a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx index 197a3e1fd46a6..65878431aef13 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx @@ -2,8 +2,9 @@ import * as React from 'react'; import { DataGridPro, GridColumns, - GridRowsProp, GridFilterModel, + GridLinkOperator, + GridRowsProp, } from '@mui/x-data-grid-pro'; import Stack from '@mui/material/Stack'; import Checkbox from '@mui/material/Checkbox'; @@ -11,6 +12,12 @@ import FormGroup from '@mui/material/FormGroup'; import FormControlLabel from '@mui/material/FormControlLabel'; const rows: GridRowsProp = [ + { + hierarchy: ['Sarah'], + jobTitle: 'Head of Human Resources', + recruitmentDate: new Date(2020, 8, 12), + id: 0, + }, { hierarchy: ['Thomas'], jobTitle: 'Head of Sales', @@ -32,7 +39,7 @@ const rows: GridRowsProp = [ { hierarchy: ['Thomas', 'Nancy'], jobTitle: 'Sales Person', - recruitmentDate: new Date(2018, 3, 29), + recruitmentDate: new Date(2017, 10, 29), id: 4, }, { @@ -113,8 +120,13 @@ export default function DisableChildrenSortingTreeData() { const [disableChildrenFiltering, setDisableChildrenFiltering] = React.useState(true); const [filterModel, setFilterModel] = React.useState({ + linkOperator: GridLinkOperator.Or, items: [ - { columnField: 'jobTitle', operatorValue: 'contains', value: 'Head of Sales' }, + { + columnField: 'recruitmentDate', + operatorValue: 'before', + value: '2018-01-01', + }, ], }); @@ -140,7 +152,7 @@ export default function DisableChildrenSortingTreeData() { getTreeDataPath={getTreeDataPath} filterModel={filterModel} onFilterModelChange={setFilterModel} - defaultGroupingExpansionDepth={-1} + defaultGroupingExpansionDepth={1} />
diff --git a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js index 29b7087202899..6c61742e0b2f2 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js @@ -6,6 +6,12 @@ import FormGroup from '@mui/material/FormGroup'; import FormControlLabel from '@mui/material/FormControlLabel'; const rows = [ + { + hierarchy: ['Sarah'], + jobTitle: 'Head of Human Resources', + recruitmentDate: new Date(2020, 8, 12), + id: 0, + }, { hierarchy: ['Thomas'], jobTitle: 'Head of Sales', @@ -27,7 +33,7 @@ const rows = [ { hierarchy: ['Thomas', 'Nancy'], jobTitle: 'Sales Person', - recruitmentDate: new Date(2018, 3, 29), + recruitmentDate: new Date(2017, 10, 29), id: 4, }, { diff --git a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.tsx index 581b3922fe8c7..191698fe25c1b 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.tsx @@ -11,6 +11,12 @@ import FormGroup from '@mui/material/FormGroup'; import FormControlLabel from '@mui/material/FormControlLabel'; const rows: GridRowsProp = [ + { + hierarchy: ['Sarah'], + jobTitle: 'Head of Human Resources', + recruitmentDate: new Date(2020, 8, 12), + id: 0, + }, { hierarchy: ['Thomas'], jobTitle: 'Head of Sales', @@ -32,7 +38,7 @@ const rows: GridRowsProp = [ { hierarchy: ['Thomas', 'Nancy'], jobTitle: 'Sales Person', - recruitmentDate: new Date(2018, 3, 29), + recruitmentDate: new Date(2017, 10, 29), id: 4, }, { diff --git a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js index 53a9204ee71cc..36de96eaa2108 100644 --- a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js @@ -6,90 +6,90 @@ import Button from '@mui/material/Button'; const rows = [ { hierarchy: ['Sarah'], - jobTitle: 'CEO', - recruitmentDate: new Date(2014, 7, 22), + jobTitle: 'Head of Human Resources', + recruitmentDate: new Date(2020, 8, 12), id: 0, }, { - hierarchy: ['Sarah', 'Thomas'], + hierarchy: ['Thomas'], jobTitle: 'Head of Sales', recruitmentDate: new Date(2017, 3, 4), id: 1, }, { - hierarchy: ['Sarah', 'Thomas', 'Robert'], + hierarchy: ['Thomas', 'Robert'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 11, 20), id: 2, }, { - hierarchy: ['Sarah', 'Thomas', 'Karen'], + hierarchy: ['Thomas', 'Karen'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 10, 14), id: 3, }, { - hierarchy: ['Sarah', 'Thomas', 'Nancy'], + hierarchy: ['Thomas', 'Nancy'], jobTitle: 'Sales Person', - recruitmentDate: new Date(2018, 3, 29), + recruitmentDate: new Date(2017, 10, 29), id: 4, }, { - hierarchy: ['Sarah', 'Thomas', 'Daniel'], + hierarchy: ['Thomas', 'Daniel'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 7, 21), id: 5, }, { - hierarchy: ['Sarah', 'Thomas', 'Christopher'], + hierarchy: ['Thomas', 'Christopher'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 7, 20), id: 6, }, { - hierarchy: ['Sarah', 'Thomas', 'Donald'], + hierarchy: ['Thomas', 'Donald'], jobTitle: 'Sales Person', recruitmentDate: new Date(2019, 6, 28), id: 7, }, { - hierarchy: ['Sarah', 'Mary'], + hierarchy: ['Mary'], jobTitle: 'Head of Engineering', recruitmentDate: new Date(2016, 3, 14), id: 8, }, { - hierarchy: ['Sarah', 'Mary', 'Jennifer'], + hierarchy: ['Mary', 'Jennifer'], jobTitle: 'Tech lead front', recruitmentDate: new Date(2016, 5, 17), id: 9, }, { - hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], + hierarchy: ['Mary', 'Jennifer', 'Anna'], jobTitle: 'Front-end developer', recruitmentDate: new Date(2019, 11, 7), id: 10, }, { - hierarchy: ['Sarah', 'Mary', 'Michael'], + hierarchy: ['Mary', 'Michael'], jobTitle: 'Tech lead devops', recruitmentDate: new Date(2021, 7, 1), id: 11, }, { - hierarchy: ['Sarah', 'Mary', 'Linda'], + hierarchy: ['Mary', 'Linda'], jobTitle: 'Tech lead back', recruitmentDate: new Date(2017, 0, 12), id: 12, }, { - hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], + hierarchy: ['Mary', 'Linda', 'Elizabeth'], jobTitle: 'Back-end developer', recruitmentDate: new Date(2019, 2, 22), id: 13, }, { - hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], + hierarchy: ['Mary', 'Linda', 'William'], jobTitle: 'Back-end developer', recruitmentDate: new Date(2018, 4, 19), id: 14, diff --git a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx index 34c7d6024e635..9138176a6b22f 100644 --- a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx @@ -11,90 +11,90 @@ import Button from '@mui/material/Button'; const rows: GridRowsProp = [ { hierarchy: ['Sarah'], - jobTitle: 'CEO', - recruitmentDate: new Date(2014, 7, 22), + jobTitle: 'Head of Human Resources', + recruitmentDate: new Date(2020, 8, 12), id: 0, }, { - hierarchy: ['Sarah', 'Thomas'], + hierarchy: ['Thomas'], jobTitle: 'Head of Sales', recruitmentDate: new Date(2017, 3, 4), id: 1, }, { - hierarchy: ['Sarah', 'Thomas', 'Robert'], + hierarchy: ['Thomas', 'Robert'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 11, 20), id: 2, }, { - hierarchy: ['Sarah', 'Thomas', 'Karen'], + hierarchy: ['Thomas', 'Karen'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 10, 14), id: 3, }, { - hierarchy: ['Sarah', 'Thomas', 'Nancy'], + hierarchy: ['Thomas', 'Nancy'], jobTitle: 'Sales Person', - recruitmentDate: new Date(2018, 3, 29), + recruitmentDate: new Date(2017, 10, 29), id: 4, }, { - hierarchy: ['Sarah', 'Thomas', 'Daniel'], + hierarchy: ['Thomas', 'Daniel'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 7, 21), id: 5, }, { - hierarchy: ['Sarah', 'Thomas', 'Christopher'], + hierarchy: ['Thomas', 'Christopher'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 7, 20), id: 6, }, { - hierarchy: ['Sarah', 'Thomas', 'Donald'], + hierarchy: ['Thomas', 'Donald'], jobTitle: 'Sales Person', recruitmentDate: new Date(2019, 6, 28), id: 7, }, { - hierarchy: ['Sarah', 'Mary'], + hierarchy: ['Mary'], jobTitle: 'Head of Engineering', recruitmentDate: new Date(2016, 3, 14), id: 8, }, { - hierarchy: ['Sarah', 'Mary', 'Jennifer'], + hierarchy: ['Mary', 'Jennifer'], jobTitle: 'Tech lead front', recruitmentDate: new Date(2016, 5, 17), id: 9, }, { - hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], + hierarchy: ['Mary', 'Jennifer', 'Anna'], jobTitle: 'Front-end developer', recruitmentDate: new Date(2019, 11, 7), id: 10, }, { - hierarchy: ['Sarah', 'Mary', 'Michael'], + hierarchy: ['Mary', 'Michael'], jobTitle: 'Tech lead devops', recruitmentDate: new Date(2021, 7, 1), id: 11, }, { - hierarchy: ['Sarah', 'Mary', 'Linda'], + hierarchy: ['Mary', 'Linda'], jobTitle: 'Tech lead back', recruitmentDate: new Date(2017, 0, 12), id: 12, }, { - hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], + hierarchy: ['Mary', 'Linda', 'Elizabeth'], jobTitle: 'Back-end developer', recruitmentDate: new Date(2019, 2, 22), id: 13, }, { - hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], + hierarchy: ['Mary', 'Linda', 'William'], jobTitle: 'Back-end developer', recruitmentDate: new Date(2018, 4, 19), id: 14, diff --git a/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.js b/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.js index f0912b5aabc3c..7ec62dd707007 100644 --- a/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.js +++ b/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.js @@ -3,67 +3,73 @@ import { DataGridPro } from '@mui/x-data-grid-pro'; const rows = [ { - hierarchy: ['Sarah', 'Thomas', 'Robert'], + hierarchy: ['Sarah'], + jobTitle: 'Head of Human Resources', + recruitmentDate: new Date(2020, 8, 12), + id: 0, + }, + { + hierarchy: ['Thomas', 'Robert'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 11, 20), id: 2, }, { - hierarchy: ['Sarah', 'Thomas', 'Karen'], + hierarchy: ['Thomas', 'Karen'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 10, 14), id: 3, }, { - hierarchy: ['Sarah', 'Thomas', 'Nancy'], + hierarchy: ['Thomas', 'Nancy'], jobTitle: 'Sales Person', - recruitmentDate: new Date(2018, 3, 29), + recruitmentDate: new Date(2017, 10, 29), id: 4, }, { - hierarchy: ['Sarah', 'Thomas', 'Daniel'], + hierarchy: ['Thomas', 'Daniel'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 7, 21), id: 5, }, { - hierarchy: ['Sarah', 'Thomas', 'Christopher'], + hierarchy: ['Thomas', 'Christopher'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 7, 20), id: 6, }, { - hierarchy: ['Sarah', 'Thomas', 'Donald'], + hierarchy: ['Thomas', 'Donald'], jobTitle: 'Sales Person', recruitmentDate: new Date(2019, 6, 28), id: 7, }, { - hierarchy: ['Sarah', 'Mary', 'Jennifer'], + hierarchy: ['Mary', 'Jennifer'], jobTitle: 'Tech lead front', recruitmentDate: new Date(2016, 5, 17), id: 9, }, { - hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], + hierarchy: ['Mary', 'Jennifer', 'Anna'], jobTitle: 'Front-end developer', recruitmentDate: new Date(2019, 11, 7), id: 10, }, { - hierarchy: ['Sarah', 'Mary', 'Michael'], + hierarchy: ['Mary', 'Michael'], jobTitle: 'Tech lead devops', recruitmentDate: new Date(2021, 7, 1), id: 11, }, { - hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], + hierarchy: ['Mary', 'Linda', 'Elizabeth'], jobTitle: 'Back-end developer', recruitmentDate: new Date(2019, 2, 22), id: 13, }, { - hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], + hierarchy: ['Mary', 'Linda', 'William'], jobTitle: 'Back-end developer', recruitmentDate: new Date(2018, 4, 19), id: 14, diff --git a/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.tsx b/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.tsx index 25bd06afb41e7..13a2f9dcc6ec7 100644 --- a/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.tsx @@ -3,67 +3,73 @@ import { DataGridPro, GridColumns, GridRowsProp } from '@mui/x-data-grid-pro'; const rows: GridRowsProp = [ { - hierarchy: ['Sarah', 'Thomas', 'Robert'], + hierarchy: ['Sarah'], + jobTitle: 'Head of Human Resources', + recruitmentDate: new Date(2020, 8, 12), + id: 0, + }, + { + hierarchy: ['Thomas', 'Robert'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 11, 20), id: 2, }, { - hierarchy: ['Sarah', 'Thomas', 'Karen'], + hierarchy: ['Thomas', 'Karen'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 10, 14), id: 3, }, { - hierarchy: ['Sarah', 'Thomas', 'Nancy'], + hierarchy: ['Thomas', 'Nancy'], jobTitle: 'Sales Person', - recruitmentDate: new Date(2018, 3, 29), + recruitmentDate: new Date(2017, 10, 29), id: 4, }, { - hierarchy: ['Sarah', 'Thomas', 'Daniel'], + hierarchy: ['Thomas', 'Daniel'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 7, 21), id: 5, }, { - hierarchy: ['Sarah', 'Thomas', 'Christopher'], + hierarchy: ['Thomas', 'Christopher'], jobTitle: 'Sales Person', recruitmentDate: new Date(2020, 7, 20), id: 6, }, { - hierarchy: ['Sarah', 'Thomas', 'Donald'], + hierarchy: ['Thomas', 'Donald'], jobTitle: 'Sales Person', recruitmentDate: new Date(2019, 6, 28), id: 7, }, { - hierarchy: ['Sarah', 'Mary', 'Jennifer'], + hierarchy: ['Mary', 'Jennifer'], jobTitle: 'Tech lead front', recruitmentDate: new Date(2016, 5, 17), id: 9, }, { - hierarchy: ['Sarah', 'Mary', 'Jennifer', 'Anna'], + hierarchy: ['Mary', 'Jennifer', 'Anna'], jobTitle: 'Front-end developer', recruitmentDate: new Date(2019, 11, 7), id: 10, }, { - hierarchy: ['Sarah', 'Mary', 'Michael'], + hierarchy: ['Mary', 'Michael'], jobTitle: 'Tech lead devops', recruitmentDate: new Date(2021, 7, 1), id: 11, }, { - hierarchy: ['Sarah', 'Mary', 'Linda', 'Elizabeth'], + hierarchy: ['Mary', 'Linda', 'Elizabeth'], jobTitle: 'Back-end developer', recruitmentDate: new Date(2019, 2, 22), id: 13, }, { - hierarchy: ['Sarah', 'Mary', 'Linda', 'William'], + hierarchy: ['Mary', 'Linda', 'William'], jobTitle: 'Back-end developer', recruitmentDate: new Date(2018, 4, 19), id: 14, From 599a3e2d97e26171e0268028f187a9b1bbf1589d Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 20 Oct 2021 10:47:05 +0200 Subject: [PATCH 278/390] Work on demos --- docs/pages/playground.tsx | 40 +++++-------------- .../group-pivot/TreeDataFullExample.js | 2 +- .../group-pivot/TreeDataFullExample.tsx | 2 +- 3 files changed, 13 insertions(+), 31 deletions(-) diff --git a/docs/pages/playground.tsx b/docs/pages/playground.tsx index bbc95a23a2824..fc209c6b89fe5 100644 --- a/docs/pages/playground.tsx +++ b/docs/pages/playground.tsx @@ -1,35 +1,17 @@ +import { useDemoData } from '@mui/x-data-grid-generator'; +import { DataGridPro } from '@mui/x-data-grid-pro'; import * as React from 'react'; -import { useData } from 'storybook/src/hooks/useData'; -import { DataGrid } from '@mui/x-data-grid'; -import { GridColumns } from '../../packages/grid/_modules_'; -const KeyboardTest = (props: { - nbRows?: number; - checkboxSelection?: boolean; - disableVirtualization?: boolean; - filterModel?: any; - width?: number; -}) => { - const data = useData(props.nbRows || 100, 20); - const transformColSizes = (columns: GridColumns) => - columns.map((column) => ({ ...column, width: 60 })); +export default function TreeDataFullExample() { + const { data, loading } = useDemoData({ + dataSet: 'Employee', + rowLength: 1000, + treeData: { maxDepth: 2, groupingField: 'name', averageChildren: 200 }, + }); return ( -
- +
+
); -}; - -export default () => ( - -); +} diff --git a/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.js b/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.js index 948d0938d9f5d..643c6c8959b67 100644 --- a/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.js +++ b/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.js @@ -6,7 +6,7 @@ export default function TreeDataFullExample() { const { data, loading } = useDemoData({ dataSet: 'Employee', rowLength: 1000, - treeData: { maxDepth: 3, groupingField: 'name', averageChildren: 10 }, + treeData: { maxDepth: 2, groupingField: 'name', averageChildren: 200 }, }); return ( diff --git a/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.tsx b/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.tsx index 948d0938d9f5d..643c6c8959b67 100644 --- a/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.tsx @@ -6,7 +6,7 @@ export default function TreeDataFullExample() { const { data, loading } = useDemoData({ dataSet: 'Employee', rowLength: 1000, - treeData: { maxDepth: 3, groupingField: 'name', averageChildren: 10 }, + treeData: { maxDepth: 2, groupingField: 'name', averageChildren: 200 }, }); return ( From bc3c13f6121ad54acc6fd09ca0e964e69be815ae Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 20 Oct 2021 16:06:11 +0200 Subject: [PATCH 279/390] Block treeData for DataGrid --- packages/grid/data-grid/src/DataGridProps.ts | 1 + packages/grid/data-grid/src/useDataGridProps.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/grid/data-grid/src/DataGridProps.ts b/packages/grid/data-grid/src/DataGridProps.ts index cd35bad7b69e9..06d39b1987cce 100644 --- a/packages/grid/data-grid/src/DataGridProps.ts +++ b/packages/grid/data-grid/src/DataGridProps.ts @@ -19,6 +19,7 @@ export type DataGridProps = Omit< | 'options' | 'onRowsScrollEnd' | 'scrollEndThreshold' + | 'treeData' | 'signature' > & { pagination?: true; diff --git a/packages/grid/data-grid/src/useDataGridProps.ts b/packages/grid/data-grid/src/useDataGridProps.ts index b3252c1a92ba0..a4196ea79bb67 100644 --- a/packages/grid/data-grid/src/useDataGridProps.ts +++ b/packages/grid/data-grid/src/useDataGridProps.ts @@ -22,6 +22,7 @@ const FORCED_PROPS: { [key in ForcedPropsKey]-?: GridInputComponentProps[key] } onRowsScrollEnd: undefined, checkboxSelectionVisibleOnly: false, scrollEndThreshold: undefined, + treeData: false, signature: 'DataGrid', }; From f106168b2e729f86825c9a9ac294361ed3857452 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 20 Oct 2021 17:56:59 +0200 Subject: [PATCH 280/390] Proptypes --- packages/grid/data-grid/src/DataGrid.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/grid/data-grid/src/DataGrid.tsx b/packages/grid/data-grid/src/DataGrid.tsx index e2fba301608fc..ac1354652d5de 100644 --- a/packages/grid/data-grid/src/DataGrid.tsx +++ b/packages/grid/data-grid/src/DataGrid.tsx @@ -655,9 +655,4 @@ DataGridRaw.propTypes = { * @ignore */ style: PropTypes.object, - /** - * If `true`, the rows will be gathered in a tree structure, following the `getTreeDataPath` prop - * @default false - */ - treeData: PropTypes.bool, } as any; From a3bb2840624bcbd8a22fa65efbcad3860970e651 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 21 Oct 2021 10:36:43 +0200 Subject: [PATCH 281/390] Update --- scripts/exportsSnapshot.json | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/scripts/exportsSnapshot.json b/scripts/exportsSnapshot.json index b31dadcd51201..b3ca9f8191959 100644 --- a/scripts/exportsSnapshot.json +++ b/scripts/exportsSnapshot.json @@ -2,6 +2,7 @@ { "name": "GridActionsCell", "kind": "Namespace" }, { "name": "GridActionsCellItem", "kind": "Namespace" }, { "name": "GridBody", "kind": "Namespace" }, + { "name": "GridCell", "kind": "Namespace" }, { "name": "GridColumnHeaderItem", "kind": "Namespace" }, { "name": "GridColumnHeaderMenu", "kind": "Namespace" }, { "name": "GridColumnHeaderTitle", "kind": "Namespace" }, @@ -14,7 +15,6 @@ { "name": "GridFilterMenuItem", "kind": "Namespace" }, { "name": "GridMenu", "kind": "Namespace" }, { "name": "GridRow", "kind": "Namespace" }, - { "name": "GridStickyContainer", "kind": "Namespace" }, { "name": "HideGridColMenuItem", "kind": "Namespace" }, { "name": "SortGridMenuItems", "kind": "Namespace" }, { "name": "GridCellModes", "kind": "Enumeration" }, @@ -63,18 +63,19 @@ { "name": "GridControlStateApi", "kind": "Interface" }, { "name": "GridCoreApi", "kind": "Interface" }, { "name": "GridCsvExportApi", "kind": "Interface" }, + { "name": "GridCsvExportOptions", "kind": "Interface" }, { "name": "GridDensityApi", "kind": "Interface" }, { "name": "GridDensityOption", "kind": "Interface" }, { "name": "GridDensityState", "kind": "Interface" }, + { "name": "GridDisableVirtualizationApi", "kind": "Interface" }, { "name": "GridEditCellProps", "kind": "Interface" }, { "name": "GridEditCellPropsParams", "kind": "Interface" }, { "name": "GridEditCellValueParams", "kind": "Interface" }, { "name": "GridEditRowApi", "kind": "Interface" }, - { "name": "GridEmptyCellProps", "kind": "Interface" }, { "name": "GridEventsApi", "kind": "Interface" }, - { "name": "GridExportCsvOptions", "kind": "Interface" }, { "name": "GridFilterApi", "kind": "Interface" }, { "name": "GridFilterFormProps", "kind": "Interface" }, + { "name": "GridFilterInitialState", "kind": "Interface" }, { "name": "GridFilterInputValueProps", "kind": "Interface" }, { "name": "GridFilterItem", "kind": "Interface" }, { "name": "GridFilterItemProps", "kind": "Interface" }, @@ -83,7 +84,9 @@ { "name": "GridFilterState", "kind": "Interface" }, { "name": "GridFocusApi", "kind": "Interface" }, { "name": "GridFocusState", "kind": "Interface" }, + { "name": "GridHeaderSelectionCheckboxParams", "kind": "Interface" }, { "name": "GridIconSlotsComponent", "kind": "Interface" }, + { "name": "GridInitialState", "kind": "Interface" }, { "name": "GridInputComponentProps", "kind": "Interface" }, { "name": "GridLocaleText", "kind": "Interface" }, { "name": "GridLocaleTextApi", "kind": "Interface" }, @@ -96,6 +99,8 @@ { "name": "GridParamsApi", "kind": "Interface" }, { "name": "GridPreferencePanelState", "kind": "Interface" }, { "name": "GridPreferencesPanelApi", "kind": "Interface" }, + { "name": "GridPrintExportApi", "kind": "Interface" }, + { "name": "GridPrintExportOptions", "kind": "Interface" }, { "name": "GridRenderCellParams", "kind": "Interface" }, { "name": "GridRenderColumnsProps", "kind": "Interface" }, { "name": "GridRenderEditCellParams", "kind": "Interface" }, @@ -108,18 +113,19 @@ { "name": "GridRowProps", "kind": "Interface" }, { "name": "GridRowScrollEndParams", "kind": "Interface" }, { "name": "GridRowSelectionCheckboxParams", "kind": "Interface" }, + { "name": "GridRowTreeNodeConfig", "kind": "Interface" }, { "name": "GridRowsState", "kind": "Interface" }, { "name": "GridScrollApi", "kind": "Interface" }, { "name": "GridScrollBarState", "kind": "Interface" }, { "name": "GridScrollParams", "kind": "Interface" }, { "name": "GridSelectionApi", "kind": "Interface" }, - { "name": "GridSlotComponentProps", "kind": "Interface" }, { "name": "GridSlotsComponent", "kind": "Interface" }, { "name": "GridSlotsComponentsProps", "kind": "Interface" }, { "name": "GridSortApi", "kind": "Interface" }, { "name": "GridSortCellParams", "kind": "Interface" }, { "name": "GridSortItem", "kind": "Interface" }, { "name": "GridSortModelParams", "kind": "Interface" }, + { "name": "GridSortingInitialState", "kind": "Interface" }, { "name": "GridSortingState", "kind": "Interface" }, { "name": "GridState", "kind": "Interface" }, { "name": "GridStateApi", "kind": "Interface" }, @@ -129,9 +135,6 @@ { "name": "GridToolbarFilterButtonProps", "kind": "Interface" }, { "name": "GridTypeFilterInputValueProps", "kind": "Interface" }, { "name": "GridValueFormatterParams", "kind": "Interface" }, - { "name": "GridViewportRowsChangeParams", "kind": "Interface" }, - { "name": "GridVirtualizationApi", "kind": "Interface" }, - { "name": "GridWindowProps", "kind": "Interface" }, { "name": "Logger", "kind": "Interface" }, { "name": "DataGridProps", "kind": "Type alias" }, { "name": "FilterColumnLookup", "kind": "Type alias" }, @@ -158,7 +161,6 @@ { "name": "GridEditRowProps", "kind": "Type alias" }, { "name": "GridEditRowsModel", "kind": "Type alias" }, { "name": "GridEnrichedColDef", "kind": "Type alias" }, - { "name": "GridExportCsvDelimiter", "kind": "Type alias" }, { "name": "GridExportFormat", "kind": "Type alias" }, { "name": "GridFeatureMode", "kind": "Type alias" }, { "name": "GridFieldComparatorList", "kind": "Type alias" }, @@ -166,6 +168,7 @@ { "name": "GridInputSelectionModel", "kind": "Type alias" }, { "name": "GridNativeColTypes", "kind": "Type alias" }, { "name": "GridOverlayProps", "kind": "Type alias" }, + { "name": "GridPreferencePanelInitialState", "kind": "Type alias" }, { "name": "GridRenderContextProps", "kind": "Type alias" }, { "name": "GridRootContainerRef", "kind": "Type alias" }, { "name": "GridRootProps", "kind": "Type alias" }, @@ -174,6 +177,7 @@ { "name": "GridRowIdGetter", "kind": "Type alias" }, { "name": "GridRowMode", "kind": "Type alias" }, { "name": "GridRowModel", "kind": "Type alias" }, + { "name": "GridRowTreeConfig", "kind": "Type alias" }, { "name": "GridRowsLookup", "kind": "Type alias" }, { "name": "GridRowsProp", "kind": "Type alias" }, { "name": "GridScrollFn", "kind": "Type alias" }, @@ -207,7 +211,6 @@ { "name": "GridArrowDownwardIcon", "kind": "Variable" }, { "name": "GridArrowUpwardIcon", "kind": "Variable" }, { "name": "GridAutoSizer", "kind": "Variable" }, - { "name": "GridCell", "kind": "Variable" }, { "name": "GridCellCheckboxForwardRef", "kind": "Variable" }, { "name": "GridCellCheckboxRenderer", "kind": "Variable" }, { "name": "GridCheckCircleIcon", "kind": "Variable" }, @@ -222,7 +225,6 @@ { "name": "GridColumnsHeader", "kind": "Variable" }, { "name": "GridColumnsMenuItem", "kind": "Variable" }, { "name": "GridDragIcon", "kind": "Variable" }, - { "name": "GridEmptyCell", "kind": "Variable" }, { "name": "GridFeatureModeConstant", "kind": "Variable" }, { "name": "GridFilterAltIcon", "kind": "Variable" }, { "name": "GridFilterListIcon", "kind": "Variable" }, @@ -241,7 +243,6 @@ { "name": "GridPagination", "kind": "Variable" }, { "name": "GridPanel", "kind": "Variable" }, { "name": "GridPreferencesPanel", "kind": "Variable" }, - { "name": "GridRenderingZone", "kind": "Variable" }, { "name": "GridRoot", "kind": "Variable" }, { "name": "GridRowCount", "kind": "Variable" }, { "name": "GridSaveAltIcon", "kind": "Variable" }, @@ -250,14 +251,15 @@ { "name": "GridSelectedRowCount", "kind": "Variable" }, { "name": "GridSeparatorIcon", "kind": "Variable" }, { "name": "GridTableRowsIcon", "kind": "Variable" }, + { "name": "GridToolbar", "kind": "Variable" }, + { "name": "GridToolbarColumnsButton", "kind": "Variable" }, { "name": "GridToolbarContainer", "kind": "Variable" }, + { "name": "GridToolbarDensitySelector", "kind": "Variable" }, { "name": "GridToolbarExport", "kind": "Variable" }, { "name": "GridToolbarFilterButton", "kind": "Variable" }, { "name": "GridTripleDotsVerticalIcon", "kind": "Variable" }, { "name": "GridViewHeadlineIcon", "kind": "Variable" }, { "name": "GridViewStreamIcon", "kind": "Variable" }, - { "name": "GridViewport", "kind": "Variable" }, - { "name": "GridWindow", "kind": "Variable" }, { "name": "HideGridColMenuItem", "kind": "Variable" }, { "name": "MAX_PAGE_SIZE", "kind": "Variable" }, { "name": "SUBMIT_FILTER_STROKE_TIME", "kind": "Variable" }, @@ -326,12 +328,12 @@ { "name": "visibleSortedGridRowsSelector", "kind": "Variable" }, { "name": "zhCN", "kind": "Variable" }, { "name": "GridBody", "kind": "Function" }, + { "name": "GridCell", "kind": "Function" }, { "name": "GridColumnHeaderItem", "kind": "Function" }, { "name": "GridColumnHeaderMenu", "kind": "Function" }, { "name": "GridColumnHeaderTitle", "kind": "Function" }, { "name": "GridColumnHeadersItemCollection", "kind": "Function" }, { "name": "GridColumnsPanel", "kind": "Function" }, - { "name": "GridDataContainer", "kind": "Function" }, { "name": "GridEditInputCell", "kind": "Function" }, { "name": "GridEditSingleSelectCell", "kind": "Function" }, { "name": "GridErrorHandler", "kind": "Function" }, @@ -346,10 +348,6 @@ { "name": "GridPanelHeader", "kind": "Function" }, { "name": "GridPanelWrapper", "kind": "Function" }, { "name": "GridRow", "kind": "Function" }, - { "name": "GridStickyContainer", "kind": "Function" }, - { "name": "GridToolbar", "kind": "Function" }, - { "name": "GridToolbarColumnsButton", "kind": "Function" }, - { "name": "GridToolbarDensitySelector", "kind": "Function" }, { "name": "allGridColumnsFieldsSelector", "kind": "Function" }, { "name": "checkGridRowIdIsValid", "kind": "Function" }, { "name": "getDataGridUtilityClass", "kind": "Function" }, @@ -376,7 +374,6 @@ { "name": "gridPreferencePanelStateSelector", "kind": "Function" }, { "name": "gridRenderingSelector", "kind": "Function" }, { "name": "gridRowsStateSelector", "kind": "Function" }, - { "name": "gridScrollbarStateSelector", "kind": "Function" }, { "name": "gridSelectionStateSelector", "kind": "Function" }, { "name": "gridTabIndexStateSelector", "kind": "Function" }, { "name": "gridViewportSizeStateSelector", "kind": "Function" }, @@ -396,6 +393,5 @@ { "name": "useGridRootProps", "kind": "Function" }, { "name": "useGridScrollFn", "kind": "Function" }, { "name": "useGridSelector", "kind": "Function" }, - { "name": "useGridSlotComponentProps", "kind": "Function" }, { "name": "useGridState", "kind": "Function" } ] From 71beae4af7c4604f7e04b4a7876877867d798a28 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 21 Oct 2021 13:37:57 +0200 Subject: [PATCH 282/390] Work --- .../grid/_modules_/grid/GridComponentProps.ts | 6 ++- .../features/treeData/useGridTreeData.ts | 37 +++++++++++++++---- .../grid/models/colDef/gridColDef.ts | 4 ++ 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/packages/grid/_modules_/grid/GridComponentProps.ts b/packages/grid/_modules_/grid/GridComponentProps.ts index 47861147387cb..f7faaf116f3fd 100644 --- a/packages/grid/_modules_/grid/GridComponentProps.ts +++ b/packages/grid/_modules_/grid/GridComponentProps.ts @@ -1,7 +1,7 @@ import * as React from 'react'; import { GridInitialState, GridState } from './models/gridState'; import { GridApiRef } from './models/api/gridApiRef'; -import { GridColDef, GridColumns } from './models/colDef/gridColDef'; +import { GridColDef, GridColDefOverrideParams, GridColumns } from './models/colDef/gridColDef'; import { GridSimpleOptions, GridProcessedMergedOptions, @@ -501,5 +501,7 @@ interface GridComponentOtherProps { /** * The grouping column used by the tree data */ - groupingColDef?: Partial; + groupingColDef?: + | Partial + | ((params: GridColDefOverrideParams) => Partial); } diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 081bb3f396fd2..b693261f67d94 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -5,11 +5,12 @@ import { GridColumnsPreProcessing } from '../../core/columnsPreProcessing'; import { GRID_TREE_DATA_GROUP_COL_DEF } from './gridTreeDataGroupColDef'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { GridEvents } from '../../../constants'; -import { GridCellParams, GridColDef, MuiEvent } from '../../../models'; +import { GridCellParams, GridColDef, GridColDefOverrideParams, MuiEvent } from '../../../models'; import { isSpaceKey } from '../../../utils/keyboardUtils'; import { useFirstRender } from '../../utils/useFirstRender'; import { buildRowTree } from '../../../utils/rowTreeUtils'; import { GridRowGroupingPreProcessing } from '../../core/rowGroupsPerProcessing'; +import { isFunction } from '../../../utils/utils'; /** * Only available in DataGridPro @@ -23,6 +24,31 @@ export const useGridTreeData = ( 'treeData' | 'getTreeDataPath' | 'groupingColDef' | 'defaultGroupingExpansionDepth' >, ) => { + const groupingColDef = React.useMemo(() => { + const propGroupingColDef = props.groupingColDef; + + const baseColDef: GridColDef = { + ...GRID_TREE_DATA_GROUP_COL_DEF, + headerName: apiRef.current.getLocaleText('treeDataGroupingHeaderName'), + }; + let customGroupingColDef: Partial; + + if (isFunction(propGroupingColDef)) { + const params: GridColDefOverrideParams = { + colDef: baseColDef, + }; + + customGroupingColDef = propGroupingColDef(params); + } else { + customGroupingColDef = propGroupingColDef ?? {}; + } + + return { + ...baseColDef, + ...customGroupingColDef, + }; + }, [apiRef, props.groupingColDef]); + const updateColumnsPreProcessing = React.useCallback(() => { const addGroupingColumn: GridColumnsPreProcessing = (columns) => { if (!props.treeData) { @@ -30,17 +56,12 @@ export const useGridTreeData = ( } const index = columns[0].type === 'checkboxSelection' ? 1 : 0; - const groupingColumn: GridColDef = { - ...GRID_TREE_DATA_GROUP_COL_DEF, - headerName: apiRef.current.getLocaleText('treeDataGroupingHeaderName'), - ...props.groupingColDef, - }; - return [...columns.slice(0, index), groupingColumn, ...columns.slice(index)]; + return [...columns.slice(0, index), groupingColDef, ...columns.slice(index)]; }; apiRef.current.UNSTABLE_registerColumnPreProcessing('treeData', addGroupingColumn); - }, [apiRef, props.treeData, props.groupingColDef]); + }, [apiRef, props.treeData, groupingColDef]); const updateRowGrouping = React.useCallback(() => { if (!props.treeData) { diff --git a/packages/grid/_modules_/grid/models/colDef/gridColDef.ts b/packages/grid/_modules_/grid/models/colDef/gridColDef.ts index 25ffecb1563c0..09610606e952f 100644 --- a/packages/grid/_modules_/grid/models/colDef/gridColDef.ts +++ b/packages/grid/_modules_/grid/models/colDef/gridColDef.ts @@ -208,3 +208,7 @@ export interface GridColumnsState { all: string[]; lookup: GridColumnLookup; } + +export interface GridColDefOverrideParams { + colDef: GridColDef; +} From 7b5649293f12a0cbbb10f3e28ccccb0d17e035d1 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 21 Oct 2021 16:28:27 +0200 Subject: [PATCH 283/390] Work --- docs/pages/playground.tsx | 16 +------ .../CustomGroupingColumnTreeData.tsx | 22 ++++------ .../cell/GridTreeDataGroupingCell.tsx | 43 ++++++++++++------- .../treeData/gridTreeDataGroupColDef.tsx | 21 ++++++++- .../features/treeData/useGridTreeData.ts | 2 +- packages/grid/data-grid/src/DataGrid.tsx | 2 +- packages/grid/x-grid/src/DataGridPro.tsx | 2 +- .../src/tests/treeData.DataGridPro.test.tsx | 26 +++++------ 8 files changed, 74 insertions(+), 60 deletions(-) diff --git a/docs/pages/playground.tsx b/docs/pages/playground.tsx index fc209c6b89fe5..8d573e70e7b82 100644 --- a/docs/pages/playground.tsx +++ b/docs/pages/playground.tsx @@ -1,17 +1,5 @@ -import { useDemoData } from '@mui/x-data-grid-generator'; -import { DataGridPro } from '@mui/x-data-grid-pro'; import * as React from 'react'; -export default function TreeDataFullExample() { - const { data, loading } = useDemoData({ - dataSet: 'Employee', - rowLength: 1000, - treeData: { maxDepth: 2, groupingField: 'name', averageChildren: 200 }, - }); - - return ( -
- -
- ); +export default function Playground() { + return
This file is listed in `.gitignore`
; } diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx index eec85d0e6096c..b8ada7d7d9452 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx @@ -3,12 +3,11 @@ import { DataGridPro, GridRenderCellParams, useGridApiContext, - useGridSelector, - gridVisibleDescendantCountLookupSelector, GridEvents, GridColumns, GridRowsProp, DataGridProProps, + GridTreeDataGroupingCellValue, } from '@mui/x-data-grid-pro'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; @@ -20,14 +19,11 @@ export const isNavigationKey = (key: string) => key.indexOf('Page') === 0 || key === ' '; -const CustomGridTreeDataGroupingCell = (props: GridRenderCellParams) => { - const { id, field, rowNode } = props; +const CustomGridTreeDataGroupingCell = ( + props: GridRenderCellParams, +) => { + const { id, field, value } = props; const apiRef = useGridApiContext(); - const descendantCountLookup = useGridSelector( - apiRef, - gridVisibleDescendantCountLookupSelector, - ); - const descendantCount = descendantCountLookup[id]; const handleKeyDown = (event) => { if (event.key === ' ') { @@ -39,22 +35,22 @@ const CustomGridTreeDataGroupingCell = (props: GridRenderCellParams) => { }; const handleClick = (event) => { - apiRef.current.unstable_setRowExpansion(id, !rowNode.expanded); + apiRef.current.unstable_setRowExpansion(id, !value.expanded); apiRef.current.setCellFocus(id, field); event.stopPropagation(); }; return ( - +
- {descendantCount > 0 ? ( + {value.visibleDescendantCount > 0 ? ( ) : ( diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index 1d0e087781f23..ea63fcbc1d47a 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -6,8 +6,6 @@ import Box from '@mui/material/Box'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { useGridApiContext } from '../../hooks/utils/useGridApiContext'; import { GridRenderCellParams } from '../../models/params/gridCellParams'; -import { gridVisibleDescendantCountLookupSelector } from '../../hooks/features/filter/gridFilterSelector'; -import { useGridSelector } from '../../hooks/utils/useGridSelector'; import { isNavigationKey, isSpaceKey } from '../../utils/keyboardUtils'; import { GridEvents } from '../../constants'; @@ -24,16 +22,21 @@ const useStyles = makeStyles({ }, }); -const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { - const { id, field, rowNode } = props; +export interface GridTreeDataGroupingCellValue { + label: string; + visibleDescendantCount: number; + depth: number; + expanded: boolean; +} + +const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { + const { id, field, value } = props; const rootProps = useGridRootProps(); const apiRef = useGridApiContext(); - const descendantCountLookup = useGridSelector(apiRef, gridVisibleDescendantCountLookupSelector); const classes = useStyles(); - const descendantCount = descendantCountLookup[id]; - const Icon = rowNode.expanded + const Icon = value.expanded ? rootProps.components.TreeDataCollapseIcon : rootProps.components.TreeDataExpandIcon; @@ -47,22 +50,22 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams) => { }; const handleClick = (event) => { - apiRef.current.unstable_setRowExpansion(id, !rowNode.expanded); + apiRef.current.unstable_setRowExpansion(id, !value.expanded); apiRef.current.setCellFocus(id, field); event.stopPropagation(); }; return ( - +
- {descendantCount > 0 && ( + {value.visibleDescendantCount > 0 && ( { )}
- {rowNode.groupingValue} - {descendantCount > 0 ? ` (${descendantCount})` : ''} + {value.label} + {value.visibleDescendantCount > 0 ? ` (${value.visibleDescendantCount})` : ''}
); @@ -103,7 +106,12 @@ GridTreeDataGroupingCell.propTypes = { /** * The cell value formatted with the column valueFormatter. */ - formattedValue: PropTypes.any.isRequired, + formattedValue: PropTypes.shape({ + depth: PropTypes.number.isRequired, + expanded: PropTypes.bool.isRequired, + label: PropTypes.string.isRequired, + visibleDescendantCount: PropTypes.number.isRequired, + }).isRequired, /** * Get the cell value of a row and field. * @param {GridRowId} id The row id. @@ -149,7 +157,12 @@ GridTreeDataGroupingCell.propTypes = { /** * The cell value, but if the column has valueGetter, use getValue. */ - value: PropTypes.any.isRequired, + value: PropTypes.shape({ + depth: PropTypes.number.isRequired, + expanded: PropTypes.bool.isRequired, + label: PropTypes.string.isRequired, + visibleDescendantCount: PropTypes.number.isRequired, + }).isRequired, } as any; export { GridTreeDataGroupingCell }; diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx index 1937cf0bad017..fb99760f2c514 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx +++ b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx @@ -1,11 +1,20 @@ import * as React from 'react'; import { GridColDef } from '../../../models/colDef/gridColDef'; -import { GridTreeDataGroupingCell } from '../../../components/cell/GridTreeDataGroupingCell'; +import { + GridTreeDataGroupingCell, + GridTreeDataGroupingCellValue, +} from '../../../components/cell/GridTreeDataGroupingCell'; import { GRID_STRING_COL_DEF } from '../../../models/colDef/gridStringColDef'; +import { gridVisibleDescendantCountLookupSelector } from '../filter/gridFilterSelector'; +import { GridRenderCellParams } from '../../../models'; +/** + * TODO: Add sorting and filtering on the value and the visibleDescendantCount + */ export const GRID_TREE_DATA_GROUP_COL_DEF: GridColDef = { ...GRID_STRING_COL_DEF, field: '__tree_data_group__', + type: 'treeDataGroup', sortable: false, filterable: false, disableColumnMenu: true, @@ -13,5 +22,13 @@ export const GRID_TREE_DATA_GROUP_COL_DEF: GridColDef = { shouldRenderAutoGeneratedRows: true, align: 'left', width: 200, - renderCell: (params) => , + valueGetter: ({ rowNode, api }): GridTreeDataGroupingCellValue => ({ + label: rowNode.groupingValue, + depth: rowNode.depth, + expanded: rowNode.expanded ?? false, + visibleDescendantCount: gridVisibleDescendantCountLookupSelector(api.state)[rowNode.id], + }), + renderCell: (params: GridRenderCellParams) => ( + + ), }; diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index b693261f67d94..c99625805c74b 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -116,7 +116,7 @@ export const useGridTreeData = ( const handleCellKeyDown = React.useCallback( (params: GridCellParams, event: MuiEvent) => { const cellParams = apiRef.current.getCellParams(params.id, params.field); - if (cellParams.field === '__tree_data_group__' && isSpaceKey(event.key)) { + if (cellParams.colDef.type === 'treeDataGroup' && isSpaceKey(event.key)) { event.stopPropagation(); event.preventDefault(); diff --git a/packages/grid/data-grid/src/DataGrid.tsx b/packages/grid/data-grid/src/DataGrid.tsx index ac1354652d5de..8cfa87ef588ce 100644 --- a/packages/grid/data-grid/src/DataGrid.tsx +++ b/packages/grid/data-grid/src/DataGrid.tsx @@ -227,7 +227,7 @@ DataGridRaw.propTypes = { /** * The grouping column used by the tree data */ - groupingColDef: PropTypes.object, + groupingColDef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), /** * Set the height in pixel of the column headers in the grid. * @default 56 diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index d928ecf9aa5a0..646fd2178186f 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -273,7 +273,7 @@ DataGridProRaw.propTypes = { /** * The grouping column used by the tree data */ - groupingColDef: PropTypes.object, + groupingColDef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), /** * Set the height in pixel of the column headers in the grid. * @default 56 diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index f4092061aef78..382761bf6da3e 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -87,19 +87,19 @@ describe(' - Tree Data', () => { setProps({ treeData: true }); expect(getColumnHeadersTextContent()).to.deep.equal(['Group', 'name']); expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); - setProps({ treeData: false }); - expect(getColumnHeadersTextContent()).to.deep.equal(['name']); - expect(getColumnValues(0)).to.deep.equal([ - 'A', - 'A.A', - 'A.B', - 'B', - 'B.A', - 'B.B', - 'B.B.A', - 'B.B.A.A', - 'C', - ]); + // setProps({ treeData: false }); + // expect(getColumnHeadersTextContent()).to.deep.equal(['name']); + // expect(getColumnValues(0)).to.deep.equal([ + // 'A', + // 'A.A', + // 'A.B', + // 'B', + // 'B.A', + // 'B.B', + // 'B.B.A', + // 'B.B.A.A', + // 'C', + // ]); }); it('should support enabling treeData after apiRef.current.updateRows has modified the rows', async () => { From d938414bb788a7a6e97f381f05de35eafa01d519 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 09:14:21 +0200 Subject: [PATCH 284/390] Work --- .../_modules_/grid/hooks/features/rows/useGridRows.ts | 2 +- .../features/treeData/gridTreeDataGroupColDef.tsx | 2 +- .../grid/hooks/features/treeData/useGridTreeData.ts | 6 +++--- .../src/services/real-data-service.ts | 4 ++-- .../src/services/tree-data-generator.ts | 5 ++--- packages/grid/x-grid/src/DataGridPro.tsx | 9 ++++++++- .../x-grid/src/tests/treeData.DataGridPro.test.tsx | 10 ++++++++++ 7 files changed, 27 insertions(+), 11 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 45f9869e8a917..18df1bd84521f 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -96,7 +96,7 @@ const getRowsStateFromCache = ( ): GridRowsState => { const { rowIds, idRowsLookup, propRowCount = 0 } = rowsCache.state; - const groupingResponse = apiRef.current.UNSTABLE_groupRows({ + const groupingResponse = apiRef.current.unstable_groupRows({ idRowsLookup, rowIds, }); diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx index fb99760f2c514..49064c8491bbe 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx +++ b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx @@ -26,7 +26,7 @@ export const GRID_TREE_DATA_GROUP_COL_DEF: GridColDef = { label: rowNode.groupingValue, depth: rowNode.depth, expanded: rowNode.expanded ?? false, - visibleDescendantCount: gridVisibleDescendantCountLookupSelector(api.state)[rowNode.id], + visibleDescendantCount: gridVisibleDescendantCountLookupSelector(api.state)[rowNode.id] ?? 0, }), renderCell: (params: GridRenderCellParams) => ( diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index c99625805c74b..890537ccfb2cd 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -60,12 +60,12 @@ export const useGridTreeData = ( return [...columns.slice(0, index), groupingColDef, ...columns.slice(index)]; }; - apiRef.current.UNSTABLE_registerColumnPreProcessing('treeData', addGroupingColumn); + apiRef.current.unstable_registerColumnPreProcessing('treeData', addGroupingColumn); }, [apiRef, props.treeData, groupingColDef]); const updateRowGrouping = React.useCallback(() => { if (!props.treeData) { - return apiRef.current.UNSTABLE_registerRowGroupsBuilder('treeData', null); + return apiRef.current.unstable_registerRowGroupsBuilder('treeData', null); } const groupRows: GridRowGroupingPreProcessing = (params) => { @@ -87,7 +87,7 @@ export const useGridTreeData = ( }); }; - return apiRef.current.UNSTABLE_registerRowGroupsBuilder('treeData', groupRows); + return apiRef.current.unstable_registerRowGroupsBuilder('treeData', groupRows); }, [apiRef, props.getTreeDataPath, props.treeData, props.defaultGroupingExpansionDepth]); useFirstRender(() => { diff --git a/packages/grid/x-grid-data-generator/src/services/real-data-service.ts b/packages/grid/x-grid-data-generator/src/services/real-data-service.ts index c7908816152ec..beaff71050940 100644 --- a/packages/grid/x-grid-data-generator/src/services/real-data-service.ts +++ b/packages/grid/x-grid-data-generator/src/services/real-data-service.ts @@ -1,9 +1,9 @@ -import { GridRowData } from '@mui/x-data-grid-pro'; +import { GridRowModel } from '@mui/x-data-grid-pro'; import asyncWorker from '../asyncWorker'; import { GridColDefGenerator, GridDataGeneratorContext } from './gridColDefGenerator'; export interface GeneratedDemoData { - rows: GridRowData[]; + rows: GridRowModel[]; columns: GridColDefGenerator[]; } diff --git a/packages/grid/x-grid-data-generator/src/services/tree-data-generator.ts b/packages/grid/x-grid-data-generator/src/services/tree-data-generator.ts index 9a22695cff2f5..944d415b78f3d 100644 --- a/packages/grid/x-grid-data-generator/src/services/tree-data-generator.ts +++ b/packages/grid/x-grid-data-generator/src/services/tree-data-generator.ts @@ -1,5 +1,4 @@ -import { DataGridProProps } from '@mui/x-data-grid-pro'; -import { GridRowData } from '../../../_modules_'; +import { DataGridProProps, GridRowModel } from '@mui/x-data-grid-pro'; import { GeneratedDemoData } from './real-data-service'; import { randomArrayItem } from './random-generator'; @@ -28,7 +27,7 @@ export interface DemoTreeDataValue GeneratedDemoData {} interface RowWithParentIndex { - value: GridRowData; + value: GridRowModel; parentIndex: number | null; } diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index 646fd2178186f..45b9dd35616c4 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -233,7 +233,14 @@ DataGridProRaw.propTypes = { * Set it to 'server' if you would like to handle filtering on the server-side. * @default "client" */ - filterMode: PropTypes.oneOf(['client', 'server']), + filterMode: chainPropTypes(PropTypes.oneOf(['client', 'server']), (props: any) => { + if (props.treeData && props.filterMode === 'server') { + return new Error( + 'MUI: The `filterMode="server"` prop is not available when the `treeData` is enabled.', + ); + } + return null; + }), /** * Set the filter model of the grid. */ diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index 382761bf6da3e..81301e9c1bc72 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -388,6 +388,16 @@ describe(' - Tree Data', () => { expect(getColumnValues(1)).to.deep.equal(['B', 'B.A', 'B.B']); }); + + it('should throw an error when using filterMode="server" and treeData', () => { + expect(() => { + render(); + }) + // @ts-expect-error need to migrate helpers to TypeScript + .toErrorDev( + 'MUI: The `filterMode="server"` prop is not available when the `treeData` is enabled.', + ); + }); }); describe('sorting', () => { From e6a68ff81a3e1b96768907d95525b03d9ca5210e Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 09:55:07 +0200 Subject: [PATCH 285/390] Improve perf on treeData --- .../gridRowGroupsPreProcessingApi.ts | 8 +- .../useGridRowGroupsPreProcessing.ts | 4 +- .../hooks/features/rows/gridRowsSelector.ts | 2 +- .../grid/hooks/features/rows/useGridRows.ts | 171 +++++++++++------- .../features/treeData/useGridTreeData.ts | 2 +- .../grid/_modules_/grid/utils/rowTreeUtils.ts | 8 +- 6 files changed, 115 insertions(+), 80 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts index 56b6a302b7971..b8bbe7c2d87f7 100644 --- a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts @@ -1,14 +1,14 @@ import { GridRowTreeConfig, GridRowId, GridRowsLookup } from '../../../models/gridRows'; -export type GridRowGroupParams = { - rowIds: GridRowId[]; +export interface GridRowGroupParams { + ids: GridRowId[]; idRowsLookup: GridRowsLookup; -}; +} export interface GridRowGroupingResult { tree: GridRowTreeConfig; treeDepth: number; - rowIds: GridRowId[]; + ids: GridRowId[]; idRowsLookup: GridRowsLookup; } diff --git a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts index 9d5f48d2e1e91..01853b9a1e833 100644 --- a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts +++ b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts @@ -11,7 +11,7 @@ import { useGridApiMethod } from '../../utils/useGridApiMethod'; const getFlatRowTree: GridRowGroupingPreProcessing = (params) => { const tree: GridRowTreeConfig = {}; - params.rowIds.forEach((rowId) => { + params.ids.forEach((rowId) => { tree[rowId] = { id: rowId, depth: 0, parent: null, groupingValue: '' }; }); @@ -19,7 +19,7 @@ const getFlatRowTree: GridRowGroupingPreProcessing = (params) => { tree, treeDepth: 1, idRowsLookup: params.idRowsLookup, - rowIds: params.rowIds, + ids: params.ids, }; }; diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts index 19423e7985f83..40ad066a9ff02 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts @@ -25,4 +25,4 @@ export const gridRowTreeDepthSelector = createSelector( (rows) => rows.treeDepth, ); -export const gridRowIdsSelector = createSelector(gridRowsStateSelector, (rows) => rows.rowIds); +export const gridRowIdsSelector = createSelector(gridRowsStateSelector, (rows) => rows.ids); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 18df1bd84521f..469cb2a8b8703 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -24,32 +24,23 @@ import { } from './gridRowsSelector'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; -export type GridRowsInternalCacheState = Pick & { - rowIds: GridRowId[]; +type GridRowsInternalCacheStateValue = Pick; - /** - * The rowCount property when the internal cache was created - * We are storing it instead of accessing it directly when storing the cache to avoid synchronization issues - */ - propRowCount?: number; +interface GridRowsInternalCacheState { + value: GridRowsInternalCacheStateValue; /** - * The getRowId property when the internal cache was created + * The value of the properties used by the grouping when the internal cache was created * We are storing it instead of accessing it directly when storing the cache to avoid synchronization issues */ - propGetRowId?: GridRowIdGetter; + props: Pick; /** * The rows as they were the last time all the rows have been updated at once * It is used to avoid processing several time the same set of rows */ - inputRowsBeforeUpdates: GridRowsProp; - - /** - * The rows as they were the last time the tree structure has been generated - */ - inputRowsAfterUpdates: GridRowsProp; -}; + rowsBeforeUpdate: GridRowsProp; +} export interface GridRowsInternalCache { state: GridRowsInternalCacheState; @@ -67,44 +58,60 @@ function getGridRowId( return id; } -export function convertGridRowsPropToState( - inputRows: GridRowsProp, - propRowCount?: number, - propGetRowId?: GridRowIdGetter, -): GridRowsInternalCacheState { - const state: GridRowsInternalCacheState = { - idRowsLookup: {}, - rowIds: [], - propRowCount, - propGetRowId, - inputRowsBeforeUpdates: inputRows, - inputRowsAfterUpdates: inputRows, - }; +interface ConvertGridRowsPropToStateParams { + prevState: GridRowsInternalCacheState; + props?: Pick; + rows?: GridRowsProp; +} - inputRows.forEach((rowData) => { - const id = getGridRowId(rowData, propGetRowId); - state.idRowsLookup[id] = rowData; - state.rowIds.push(id); - }); +export function convertGridRowsPropToState({ + prevState, + rows: inputRows, + props: inputProps, +}: ConvertGridRowsPropToStateParams): GridRowsInternalCacheState { + const props = inputProps ?? prevState.props; + + let value: GridRowsInternalCacheStateValue; + if (inputRows) { + value = { + idRowsLookup: {}, + ids: [], + }; - return state; + inputRows.forEach((rowData) => { + const id = getGridRowId(rowData, props.getRowId); + value.idRowsLookup[id] = rowData; + value.ids.push(id); + }); + } else { + value = prevState.value; + } + + return { + value, + props, + rowsBeforeUpdate: inputRows ?? prevState.rowsBeforeUpdate, + }; } const getRowsStateFromCache = ( rowsCache: GridRowsInternalCache, apiRef: GridApiRef, ): GridRowsState => { - const { rowIds, idRowsLookup, propRowCount = 0 } = rowsCache.state; + const { + props: { rowCount: propRowCount = 0 }, + value: { ids, idRowsLookup }, + } = rowsCache.state; const groupingResponse = apiRef.current.unstable_groupRows({ idRowsLookup, - rowIds, + ids, }); const dataTopLevelRowCount = Object.values(groupingResponse.tree).filter( (node) => node.parent == null, ).length; - const totalRowCount = propRowCount > rowIds.length ? propRowCount : rowIds.length; + const totalRowCount = propRowCount > ids.length ? propRowCount : ids.length; const totalTopLevelRowCount = propRowCount > dataTopLevelRowCount ? propRowCount : dataTopLevelRowCount; @@ -114,12 +121,15 @@ const getRowsStateFromCache = ( // The cache is always redefined synchronously in `useGridStateInit` so this object don't need to be regenerated across DataGrid instances. const INITIAL_GRID_ROWS_INTERNAL_CACHE: GridRowsInternalCache = { state: { - idRowsLookup: {}, - propRowCount: undefined, - propGetRowId: undefined, - rowIds: [], - inputRowsBeforeUpdates: [], - inputRowsAfterUpdates: [], + value: { + idRowsLookup: {}, + ids: [], + }, + props: { + rowCount: undefined, + getRowId: undefined, + }, + rowsBeforeUpdate: [], }, timeout: null, lastUpdateMs: 0, @@ -142,11 +152,14 @@ export const useGridRows = ( const rowsCache = React.useRef(INITIAL_GRID_ROWS_INTERNAL_CACHE); useGridStateInit(apiRef, (state) => { - rowsCache.current.state = convertGridRowsPropToState( - props.rows, - props.rowCount, - props.getRowId, - ); + rowsCache.current.state = convertGridRowsPropToState({ + rows: props.rows, + props: { + rowCount: props.rowCount, + getRowId: props.getRowId, + }, + prevState: rowsCache.current.state, + }); rowsCache.current.lastUpdateMs = Date.now(); return { ...state, rows: getRowsStateFromCache(rowsCache.current, apiRef) }; @@ -211,9 +224,12 @@ export const useGridRows = ( const setRows = React.useCallback( (rows) => { logger.debug(`Updating all rows, new length ${rows.length}`); - throttledRowsChange(convertGridRowsPropToState(rows, props.rowCount, props.getRowId), true); + throttledRowsChange( + convertGridRowsPropToState({ rows, prevState: rowsCache.current.state }), + true, + ); }, - [logger, throttledRowsChange, props.rowCount, props.getRowId], + [logger, throttledRowsChange], ); const updateRows = React.useCallback( @@ -237,36 +253,36 @@ export const useGridRows = ( const deletedRowIds: GridRowId[] = []; - const idRowsLookup = { ...rowsCache.current.state.idRowsLookup }; - let rowIds = [...rowsCache.current.state.rowIds]; + const newStateValue: GridRowsInternalCacheStateValue = { + idRowsLookup: { ...rowsCache.current.state.value.idRowsLookup }, + ids: [...rowsCache.current.state.value.ids], + }; uniqUpdates.forEach((partialRow, id) => { // eslint-disable-next-line no-underscore-dangle if (partialRow._action === 'delete') { - delete idRowsLookup[id]; + delete newStateValue.idRowsLookup[id]; deletedRowIds.push(id); return; } const oldRow = apiRef.current.getRow(id); if (!oldRow) { - idRowsLookup[id] = partialRow; - rowIds.push(id); + newStateValue.idRowsLookup[id] = partialRow; + newStateValue.ids.push(id); return; } - idRowsLookup[id] = { ...apiRef.current.getRow(id), ...partialRow }; + newStateValue.idRowsLookup[id] = { ...apiRef.current.getRow(id), ...partialRow }; }); if (deletedRowIds.length > 0) { - rowIds = rowIds.filter((id) => !deletedRowIds.includes(id)); + newStateValue.ids = newStateValue.ids.filter((id) => !deletedRowIds.includes(id)); } const state: GridRowsInternalCacheState = { ...rowsCache.current.state, - idRowsLookup, - rowIds, - inputRowsAfterUpdates: rowIds.map((rowId) => idRowsLookup[rowId]), + value: newStateValue, }; throttledRowsChange(state, true); @@ -337,24 +353,43 @@ export const useGridRows = ( } // The new rows have already been applied (most likely in the `GridEvents.rowGroupsPreProcessingChange` listener) - if (rowsCache.current.state.inputRowsBeforeUpdates === props.rows) { + if (rowsCache.current.state.rowsBeforeUpdate === props.rows) { return; } logger.debug(`Updating all rows, new length ${props.rows.length}`); throttledRowsChange( - convertGridRowsPropToState(props.rows, props.rowCount, props.getRowId), + convertGridRowsPropToState({ + rows: props.rows, + props: { rowCount: props.rowCount, getRowId: props.getRowId }, + prevState: rowsCache.current.state, + }), false, ); }, [props.rows, props.rowCount, props.getRowId, logger, throttledRowsChange]); const handleGroupRows = React.useCallback(() => { logger.info(`Row grouping pre-processing have changed, regenerating the row tree`); - const rows = - rowsCache.current.state.inputRowsBeforeUpdates === props.rows - ? rowsCache.current.state.inputRowsAfterUpdates - : props.rows; - throttledRowsChange(convertGridRowsPropToState(rows, props.rowCount, props.getRowId), false); + + let rows: GridRowsProp | undefined; + if (rowsCache.current.state.rowsBeforeUpdate === props.rows) { + // The `props.rows` has not changed since the last row grouping + // We can keep the potential updates stored in `inputRowsAfterUpdates` on the new grouping + rows = undefined; + } else { + // The `props.rows` has changed since the last row grouping + // We must use the new `props.rows` on the new grouping + // This occurs because this event is triggered before the `useEffect` on the rows when both the grouping pre-processing and the rows changes on the same render + rows = props.rows; + } + throttledRowsChange( + convertGridRowsPropToState({ + rows, + props: { rowCount: props.rowCount, getRowId: props.getRowId }, + prevState: rowsCache.current.state, + }), + false, + ); }, [logger, throttledRowsChange, props.rowCount, props.getRowId, props.rows]); useGridApiEventHandler(apiRef, GridEvents.rowGroupsPreProcessingChange, handleGroupRows); diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 890537ccfb2cd..e5ec3defd7fdf 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -73,7 +73,7 @@ export const useGridTreeData = ( throw new Error('MUI: No getTreeDataPath given.'); } - const rows = params.rowIds + const rows = params.ids .map((rowId) => ({ id: rowId, path: props.getTreeDataPath!(params.idRowsLookup[rowId]), diff --git a/packages/grid/_modules_/grid/utils/rowTreeUtils.ts b/packages/grid/_modules_/grid/utils/rowTreeUtils.ts index 094746973740d..890fd145626b5 100644 --- a/packages/grid/_modules_/grid/utils/rowTreeUtils.ts +++ b/packages/grid/_modules_/grid/utils/rowTreeUtils.ts @@ -22,7 +22,7 @@ type TempRowTree = Record; export const buildRowTree = (params: GenerateRowTreeParams): GridRowGroupingResult => { const tempTree: TempRowTree = {}; let treeDepth = 1; - const rowIds = [...params.rowIds]; + const ids = [...params.ids]; const idRowsLookup = { ...params.idRowsLookup }; const nodeNameToIdTree: GridNodeNameToIdTree = {}; @@ -67,7 +67,7 @@ export const buildRowTree = (params: GenerateRowTreeParams): GridRowGroupingResu tempTree[nodeId] = node; idRowsLookup[nodeId] = {}; - rowIds.push(nodeId); + ids.push(nodeId); } node.descendantCount = (node.descendantCount ?? 0) + 1; @@ -96,7 +96,7 @@ export const buildRowTree = (params: GenerateRowTreeParams): GridRowGroupingResu }); const tree: GridRowTreeConfig = {}; - rowIds.forEach((rowId) => { + ids.forEach((rowId) => { const tempNode = tempTree[rowId]; tree[rowId] = { ...tempNode, @@ -107,7 +107,7 @@ export const buildRowTree = (params: GenerateRowTreeParams): GridRowGroupingResu return { tree, treeDepth, - rowIds, + ids, idRowsLookup, }; }; From 044117d6815a0bf0ee1a04c1c2f3abea291579fc Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 09:59:02 +0200 Subject: [PATCH 286/390] Work --- .../grid/hooks/features/rows/useGridRows.ts | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 469cb2a8b8703..94bcfb2274d75 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -23,11 +23,10 @@ import { gridRowIdsSelector, } from './gridRowsSelector'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; - -type GridRowsInternalCacheStateValue = Pick; +import { GridRowGroupParams } from '../../core/rowGroupsPerProcessing'; interface GridRowsInternalCacheState { - value: GridRowsInternalCacheStateValue; + value: GridRowGroupParams; /** * The value of the properties used by the grouping when the internal cache was created @@ -71,7 +70,7 @@ export function convertGridRowsPropToState({ }: ConvertGridRowsPropToStateParams): GridRowsInternalCacheState { const props = inputProps ?? prevState.props; - let value: GridRowsInternalCacheStateValue; + let value: GridRowGroupParams; if (inputRows) { value = { idRowsLookup: {}, @@ -100,18 +99,16 @@ const getRowsStateFromCache = ( ): GridRowsState => { const { props: { rowCount: propRowCount = 0 }, - value: { ids, idRowsLookup }, + value, } = rowsCache.state; - const groupingResponse = apiRef.current.unstable_groupRows({ - idRowsLookup, - ids, - }); + const groupingResponse = apiRef.current.unstable_groupRows(value); const dataTopLevelRowCount = Object.values(groupingResponse.tree).filter( (node) => node.parent == null, ).length; - const totalRowCount = propRowCount > ids.length ? propRowCount : ids.length; + const totalRowCount = + propRowCount > groupingResponse.ids.length ? propRowCount : groupingResponse.ids.length; const totalTopLevelRowCount = propRowCount > dataTopLevelRowCount ? propRowCount : dataTopLevelRowCount; @@ -253,7 +250,7 @@ export const useGridRows = ( const deletedRowIds: GridRowId[] = []; - const newStateValue: GridRowsInternalCacheStateValue = { + const newStateValue: GridRowGroupParams = { idRowsLookup: { ...rowsCache.current.state.value.idRowsLookup }, ids: [...rowsCache.current.state.value.ids], }; From ee42b8298db94a4ce7ea15e0d19b04b117d3f663 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 10:02:45 +0200 Subject: [PATCH 287/390] Fix --- packages/grid/_modules_/grid/components/GridRow.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/grid/_modules_/grid/components/GridRow.tsx b/packages/grid/_modules_/grid/components/GridRow.tsx index 4ad689eeeec49..4ce0f38a45f05 100644 --- a/packages/grid/_modules_/grid/components/GridRow.tsx +++ b/packages/grid/_modules_/grid/components/GridRow.tsx @@ -88,7 +88,6 @@ function GridRow(props: React.HTMLAttributes & GridRowProps) { const apiRef = useGridApiContext(); const rootProps = useGridRootProps(); const columnsMeta = useGridSelector(apiRef, gridColumnsMetaSelector); - const rowNode = apiRef.current.unstable_getRowNode(rowId); const ownerState = { selected, @@ -165,7 +164,7 @@ function GridRow(props: React.HTMLAttributes & GridRowProps) { const editCellState = editRowsState[rowId] ? editRowsState[rowId][column.field] : null; let content: React.ReactNode = null; - const skipRender = !column.shouldRenderAutoGeneratedRows && rowNode?.isAutoGenerated; + const skipRender = !column.shouldRenderAutoGeneratedRows && cellParams.rowNode?.isAutoGenerated; if (editCellState == null && column.renderCell && !skipRender) { content = column.renderCell({ ...cellParams, api: apiRef.current }); From bc4f766db449089f79e18e0932a26713fd6df8d7 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 10:04:47 +0200 Subject: [PATCH 288/390] Comments --- .../grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts b/packages/grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts index c50bf8407683f..184b56b0206da 100644 --- a/packages/grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts +++ b/packages/grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts @@ -7,6 +7,12 @@ import { gridSortedVisibleRowEntriesSelector } from '../features/filter/gridFilt import type { GridApiRef, GridRowEntry } from '../../models'; import { useGridState } from './useGridState'; +/** + * Compute the list of the rows in the current page + * - If the pagination is disabled or in server mode, it equals all the visible rows + * - If the row tree has several layers, it contains up to `state.pageSize` top level rows and all their descendants + * - If the row tree is flat, it only contains up to `state.pageSize` rows + */ export const useRowsInCurrentPage = ( apiRef: GridApiRef, props: Pick, From 189ceedb71fa455b82e27588f8f07647dd5789a3 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 10:08:25 +0200 Subject: [PATCH 289/390] Fix --- .../grid/x-grid/src/tests/filtering.DataGridPro.test.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx index 6dfc028cb0e35..6ac56b097b84b 100644 --- a/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/filtering.DataGridPro.test.tsx @@ -288,10 +288,7 @@ describe(' - Filter', () => { expect(getColumnValues()).to.deep.equal(['Adidas']); expect(apiRef.current.getVisibleRowModels().size).to.equal(1); - expect(apiRef.current.getVisibleRowModels().get(1)).to.deep.equal({ - id: 1, - brand: 'Adidas', - }); + expect(apiRef.current.getVisibleRowModels().get(1)).to.deep.equal({ id: 1, brand: 'Adidas' }); }); describe('performance', () => { From c6184249a276ae2af406a2c67dbb615b77d25ec7 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 10:13:27 +0200 Subject: [PATCH 290/390] [core] Simplify the CSV export row id list generation --- .../features/clipboard/useGridClipboard.ts | 8 ++++---- .../export/serializers/csvSerializer.ts | 17 ++--------------- .../hooks/features/export/useGridCsvExport.tsx | 11 ++++++++--- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts b/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts index d3e260e9b2f23..2e54812588fd2 100644 --- a/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts +++ b/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts @@ -5,6 +5,7 @@ import { visibleGridColumnsSelector } from '../columns/gridColumnsSelector'; import { GRID_CHECKBOX_SELECTION_COL_DEF } from '../../../models/colDef'; import { GridClipboardApi } from '../../../models/api'; import { useGridApiMethod, useGridNativeEventListener, useGridSelector } from '../../utils'; +import { GridRowId } from '../../../models'; function writeToClipboardPolyfill(data: string) { const span = document.createElement('span'); @@ -38,19 +39,18 @@ export const useGridClipboard = (apiRef: GridApiRef): void => { const copySelectedRowsToClipboard = React.useCallback( (includeHeaders = false) => { - const selectedRows = apiRef.current.getSelectedRows(); + const selectedRows: GridRowId[] = Array.from(apiRef.current.getSelectedRows().keys()); const filteredColumns = visibleColumns.filter( (column) => column.field !== GRID_CHECKBOX_SELECTION_COL_DEF.field, ); - if (selectedRows.size === 0 || filteredColumns.length === 0) { + if (selectedRows.length === 0 || filteredColumns.length === 0) { return; } const data = buildCSV({ columns: visibleColumns, - rows: selectedRows, - selectedRowIds: [], + rowIds: selectedRows, includeHeaders, getCellParams: apiRef.current.getCellParams, delimiterCharacter: '\t', diff --git a/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts b/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts index 895240571809e..a531ee46ab9e2 100644 --- a/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts +++ b/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts @@ -34,27 +34,14 @@ export function serialiseRow( interface BuildCSVOptions { columns: GridStateColDef[]; - rows: Map; - selectedRowIds: GridRowId[]; + rowIds: GridRowId[]; getCellParams: (id: GridRowId, field: string) => GridCellParams; delimiterCharacter: string; includeHeaders?: boolean; } export function buildCSV(options: BuildCSVOptions): string { - const { - columns, - rows, - selectedRowIds, - getCellParams, - delimiterCharacter, - includeHeaders = true, - } = options; - let rowIds = [...rows.keys()]; - - if (selectedRowIds.length) { - rowIds = rowIds.filter((id) => selectedRowIds.includes(id)); - } + const { columns, rowIds, getCellParams, delimiterCharacter, includeHeaders = true } = options; const CSVBody = rowIds .reduce( diff --git a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx index 28bf945f8f118..97c4ac04abf30 100644 --- a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx +++ b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx @@ -3,7 +3,7 @@ import { GridApiRef } from '../../../models/api/gridApiRef'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; import { useGridSelector } from '../../utils/useGridSelector'; import { allGridColumnsSelector, visibleGridColumnsSelector } from '../columns'; -import { visibleSortedGridRowsSelector } from '../filter'; +import { visibleSortedGridRowIdsSelector, visibleSortedGridRowsSelector } from '../filter'; import { gridSelectionStateSelector } from '../selection'; import { GridCsvExportApi } from '../../../models/api/gridCsvExportApi'; import { GridCsvExportOptions } from '../../../models/gridExport'; @@ -24,6 +24,7 @@ export const useGridCsvExport = (apiRef: GridApiRef): void => { const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); const columns = useGridSelector(apiRef, allGridColumnsSelector); const visibleSortedRows = useGridSelector(apiRef, visibleSortedGridRowsSelector); + const visibleSortedRowIds = useGridSelector(apiRef, visibleSortedGridRowIdsSelector); const selection = useGridSelector(apiRef, gridSelectionStateSelector); const getDataAsCsv = React.useCallback( @@ -42,10 +43,14 @@ export const useGridCsvExport = (apiRef: GridApiRef): void => { exportedColumns = validColumns.filter((column) => !column.disableExport); } + let exportedRowIds = visibleSortedRowIds; + if (selection.length) { + exportedRowIds = visibleSortedRowIds.filter((id) => selection.includes(id)); + } + return buildCSV({ columns: exportedColumns, - rows: visibleSortedRows, - selectedRowIds: selection, + rowIds: exportedRowIds, getCellParams: apiRef.current.getCellParams, delimiterCharacter: options?.delimiter || ',', }); From b4d477a6ca3ee0c0afdd52e8a5a4d63a6852490c Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 10:14:58 +0200 Subject: [PATCH 291/390] Fix --- .../_modules_/grid/hooks/features/clipboard/useGridClipboard.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts b/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts index 2e54812588fd2..5a66c8de57e93 100644 --- a/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts +++ b/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts @@ -5,7 +5,7 @@ import { visibleGridColumnsSelector } from '../columns/gridColumnsSelector'; import { GRID_CHECKBOX_SELECTION_COL_DEF } from '../../../models/colDef'; import { GridClipboardApi } from '../../../models/api'; import { useGridApiMethod, useGridNativeEventListener, useGridSelector } from '../../utils'; -import { GridRowId } from '../../../models'; +import { GridRowId } from '../../../models/gridRows'; function writeToClipboardPolyfill(data: string) { const span = document.createElement('span'); From f834beddaea949eaa2bd364e95361abd55176520 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 10:17:28 +0200 Subject: [PATCH 292/390] Fix --- .../_modules_/grid/hooks/features/export/useGridCsvExport.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx index 97c4ac04abf30..fbdf87a4bed56 100644 --- a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx +++ b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx @@ -45,7 +45,7 @@ export const useGridCsvExport = (apiRef: GridApiRef): void => { let exportedRowIds = visibleSortedRowIds; if (selection.length) { - exportedRowIds = visibleSortedRowIds.filter((id) => selection.includes(id)); + exportedRowIds = exportedRowIds.filter((id) => selection.includes(id)); } return buildCSV({ From 9da97db75cf065bee99727bf8b81a1e5a103b6dd Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 10:23:25 +0200 Subject: [PATCH 293/390] Improve --- .../features/clipboard/useGridClipboard.ts | 14 +++++++++----- .../hooks/features/export/useGridCsvExport.tsx | 18 ++++++++---------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts b/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts index 5a66c8de57e93..7cc6f959718bf 100644 --- a/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts +++ b/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts @@ -5,7 +5,7 @@ import { visibleGridColumnsSelector } from '../columns/gridColumnsSelector'; import { GRID_CHECKBOX_SELECTION_COL_DEF } from '../../../models/colDef'; import { GridClipboardApi } from '../../../models/api'; import { useGridApiMethod, useGridNativeEventListener, useGridSelector } from '../../utils'; -import { GridRowId } from '../../../models/gridRows'; +import { visibleSortedGridRowIdsSelector } from '../filter'; function writeToClipboardPolyfill(data: string) { const span = document.createElement('span'); @@ -36,21 +36,25 @@ function writeToClipboardPolyfill(data: string) { */ export const useGridClipboard = (apiRef: GridApiRef): void => { const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); + const visibleSortedRowIds = useGridSelector(apiRef, visibleSortedGridRowIdsSelector); const copySelectedRowsToClipboard = React.useCallback( (includeHeaders = false) => { - const selectedRows: GridRowId[] = Array.from(apiRef.current.getSelectedRows().keys()); const filteredColumns = visibleColumns.filter( (column) => column.field !== GRID_CHECKBOX_SELECTION_COL_DEF.field, ); - if (selectedRows.length === 0 || filteredColumns.length === 0) { + const selectedRows = apiRef.current.getSelectedRows(); + const exportedRowIds = + selectedRows.size > 0 ? visibleSortedRowIds.filter((id) => selectedRows.has(id)) : []; + + if (exportedRowIds.length === 0 || filteredColumns.length === 0) { return; } const data = buildCSV({ columns: visibleColumns, - rowIds: selectedRows, + rowIds: exportedRowIds, includeHeaders, getCellParams: apiRef.current.getCellParams, delimiterCharacter: '\t', @@ -64,7 +68,7 @@ export const useGridClipboard = (apiRef: GridApiRef): void => { writeToClipboardPolyfill(data); } }, - [apiRef, visibleColumns], + [apiRef, visibleColumns, visibleSortedRowIds], ); const handleKeydown = React.useCallback( diff --git a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx index fbdf87a4bed56..8f12d98c734cb 100644 --- a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx +++ b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx @@ -3,8 +3,7 @@ import { GridApiRef } from '../../../models/api/gridApiRef'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; import { useGridSelector } from '../../utils/useGridSelector'; import { allGridColumnsSelector, visibleGridColumnsSelector } from '../columns'; -import { visibleSortedGridRowIdsSelector, visibleSortedGridRowsSelector } from '../filter'; -import { gridSelectionStateSelector } from '../selection'; +import { visibleSortedGridRowIdsSelector } from '../filter'; import { GridCsvExportApi } from '../../../models/api/gridCsvExportApi'; import { GridCsvExportOptions } from '../../../models/gridExport'; import { useGridLogger } from '../../utils/useGridLogger'; @@ -22,10 +21,8 @@ import { GridStateColDef } from '../../../models'; export const useGridCsvExport = (apiRef: GridApiRef): void => { const logger = useGridLogger(apiRef, 'useGridCsvExport'); const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); - const columns = useGridSelector(apiRef, allGridColumnsSelector); - const visibleSortedRows = useGridSelector(apiRef, visibleSortedGridRowsSelector); const visibleSortedRowIds = useGridSelector(apiRef, visibleSortedGridRowIdsSelector); - const selection = useGridSelector(apiRef, gridSelectionStateSelector); + const columns = useGridSelector(apiRef, allGridColumnsSelector); const getDataAsCsv = React.useCallback( (options?: GridCsvExportOptions): string => { @@ -43,10 +40,11 @@ export const useGridCsvExport = (apiRef: GridApiRef): void => { exportedColumns = validColumns.filter((column) => !column.disableExport); } - let exportedRowIds = visibleSortedRowIds; - if (selection.length) { - exportedRowIds = exportedRowIds.filter((id) => selection.includes(id)); - } + const selectedRows = apiRef.current.getSelectedRows(); + const exportedRowIds = + selectedRows.size > 0 + ? visibleSortedRowIds.filter((id) => selectedRows.has(id)) + : visibleSortedRowIds; return buildCSV({ columns: exportedColumns, @@ -55,7 +53,7 @@ export const useGridCsvExport = (apiRef: GridApiRef): void => { delimiterCharacter: options?.delimiter || ',', }); }, - [logger, visibleColumns, columns, visibleSortedRows, selection, apiRef], + [logger, visibleColumns, columns, visibleSortedRowIds, apiRef], ); const exportDataAsCsv = React.useCallback( From 8b0d941e038d3ebde48b5ebe5f3b31e3a5d69fa7 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 10:36:25 +0200 Subject: [PATCH 294/390] Fix --- .../features/clipboard/useGridClipboard.ts | 24 ++++--------------- .../export/serializers/csvSerializer.ts | 4 ++-- .../features/export/useGridCsvExport.tsx | 2 +- .../colDef/gridCheckboxSelectionColDef.tsx | 1 + .../grid/_modules_/grid/models/gridExport.ts | 6 +++++ 5 files changed, 14 insertions(+), 23 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts b/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts index 7cc6f959718bf..dc9cd9cd2be11 100644 --- a/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts +++ b/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts @@ -30,34 +30,18 @@ function writeToClipboardPolyfill(data: string) { } /** - * @requires useGridColumns (state) - * @requires useGridParamsApi (method) + * @requires useGridCsvExport (method) * @requires useGridSelection (method) */ export const useGridClipboard = (apiRef: GridApiRef): void => { - const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); - const visibleSortedRowIds = useGridSelector(apiRef, visibleSortedGridRowIdsSelector); - const copySelectedRowsToClipboard = React.useCallback( (includeHeaders = false) => { - const filteredColumns = visibleColumns.filter( - (column) => column.field !== GRID_CHECKBOX_SELECTION_COL_DEF.field, - ); - - const selectedRows = apiRef.current.getSelectedRows(); - const exportedRowIds = - selectedRows.size > 0 ? visibleSortedRowIds.filter((id) => selectedRows.has(id)) : []; - - if (exportedRowIds.length === 0 || filteredColumns.length === 0) { + if (apiRef.current.getSelectedRows().size === 0) { return; } - const data = buildCSV({ - columns: visibleColumns, - rowIds: exportedRowIds, + const data = apiRef.current.getDataAsCsv({ includeHeaders, - getCellParams: apiRef.current.getCellParams, - delimiterCharacter: '\t', }); if (navigator.clipboard) { @@ -68,7 +52,7 @@ export const useGridClipboard = (apiRef: GridApiRef): void => { writeToClipboardPolyfill(data); } }, - [apiRef, visibleColumns, visibleSortedRowIds], + [apiRef], ); const handleKeydown = React.useCallback( diff --git a/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts b/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts index a531ee46ab9e2..fc75b786deb2f 100644 --- a/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts +++ b/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts @@ -37,11 +37,11 @@ interface BuildCSVOptions { rowIds: GridRowId[]; getCellParams: (id: GridRowId, field: string) => GridCellParams; delimiterCharacter: string; - includeHeaders?: boolean; + includeHeaders: boolean; } export function buildCSV(options: BuildCSVOptions): string { - const { columns, rowIds, getCellParams, delimiterCharacter, includeHeaders = true } = options; + const { columns, rowIds, getCellParams, delimiterCharacter, includeHeaders } = options; const CSVBody = rowIds .reduce( diff --git a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx index 8f12d98c734cb..01fcf78ef9ed2 100644 --- a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx +++ b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx @@ -36,7 +36,6 @@ export const useGridCsvExport = (apiRef: GridApiRef): void => { .filter((column): column is GridStateColDef => !!column); } else { const validColumns = options?.allColumns ? columns : visibleColumns; - exportedColumns = validColumns.filter((column) => !column.disableExport); } @@ -51,6 +50,7 @@ export const useGridCsvExport = (apiRef: GridApiRef): void => { rowIds: exportedRowIds, getCellParams: apiRef.current.getCellParams, delimiterCharacter: options?.delimiter || ',', + includeHeaders: options?.includeHeaders ?? true, }); }, [logger, visibleColumns, columns, visibleSortedRowIds, apiRef], diff --git a/packages/grid/_modules_/grid/models/colDef/gridCheckboxSelectionColDef.tsx b/packages/grid/_modules_/grid/models/colDef/gridCheckboxSelectionColDef.tsx index 2ac0b503fd209..48891a7547ed9 100644 --- a/packages/grid/_modules_/grid/models/colDef/gridCheckboxSelectionColDef.tsx +++ b/packages/grid/_modules_/grid/models/colDef/gridCheckboxSelectionColDef.tsx @@ -15,6 +15,7 @@ export const GRID_CHECKBOX_SELECTION_COL_DEF: GridColDef = { filterable: false, disableColumnMenu: true, disableReorder: true, + disableExport: true, valueGetter: (params) => { const selectionLookup = selectedIdsLookupSelector(params.api.state); return selectionLookup[params.id] !== undefined; diff --git a/packages/grid/_modules_/grid/models/gridExport.ts b/packages/grid/_modules_/grid/models/gridExport.ts index 6ace46dabe7d2..303d76b39f1bc 100644 --- a/packages/grid/_modules_/grid/models/gridExport.ts +++ b/packages/grid/_modules_/grid/models/gridExport.ts @@ -28,6 +28,12 @@ export interface GridCsvExportOptions { * @default false */ allColumns?: boolean; + + /** + * If `true, the first row of the CSV will include the headers of the grid. + * @default true + */ + includeHeaders?: boolean; } /** From d72e039a9a199e5bb9313c863fbce21cdfed3841 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 10:38:50 +0200 Subject: [PATCH 295/390] Fix --- .../grid/hooks/features/clipboard/useGridClipboard.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts b/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts index dc9cd9cd2be11..190bba34fdc00 100644 --- a/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts +++ b/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts @@ -1,11 +1,7 @@ import * as React from 'react'; import { GridApiRef } from '../../../models/api/gridApiRef'; -import { buildCSV } from '../export/serializers/csvSerializer'; -import { visibleGridColumnsSelector } from '../columns/gridColumnsSelector'; -import { GRID_CHECKBOX_SELECTION_COL_DEF } from '../../../models/colDef'; import { GridClipboardApi } from '../../../models/api'; -import { useGridApiMethod, useGridNativeEventListener, useGridSelector } from '../../utils'; -import { visibleSortedGridRowIdsSelector } from '../filter'; +import { useGridApiMethod, useGridNativeEventListener } from '../../utils'; function writeToClipboardPolyfill(data: string) { const span = document.createElement('span'); From f3ab0324cee4e78e243c23b32f16f5551ef3a4ed Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 10:48:01 +0200 Subject: [PATCH 296/390] Work --- .../export/serializers/csvSerializer.ts | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts b/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts index fc75b786deb2f..40eced1a7cca3 100644 --- a/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts +++ b/packages/grid/_modules_/grid/hooks/features/export/serializers/csvSerializer.ts @@ -3,10 +3,10 @@ import { GRID_CHECKBOX_SELECTION_COL_DEF, GridStateColDef, GridRowId, - GridRowModel, + GridCellValue, } from '../../../../models'; -const serialiseCellValue = (value: any, delimiterCharacter: string) => { +const serialiseCellValue = (value: GridCellValue, delimiterCharacter: string) => { if (typeof value === 'string') { const formattedValue = value.replace(/"/g, '""'); return formattedValue.includes(delimiterCharacter) ? `"${formattedValue}"` : formattedValue; @@ -15,22 +15,15 @@ const serialiseCellValue = (value: any, delimiterCharacter: string) => { return value; }; -export function serialiseRow( +const serialiseRow = ( id: GridRowId, columns: GridStateColDef[], getCellParams: (id: GridRowId, field: string) => GridCellParams, delimiterCharacter: string, -): Array { - const mappedRow: string[] = []; - columns.forEach( - (column) => - column.field !== GRID_CHECKBOX_SELECTION_COL_DEF.field && - mappedRow.push( - serialiseCellValue(getCellParams(id, column.field).formattedValue, delimiterCharacter), - ), +) => + columns.map((column) => + serialiseCellValue(getCellParams(id, column.field).formattedValue, delimiterCharacter), ); - return mappedRow; -} interface BuildCSVOptions { columns: GridStateColDef[]; From aae463eafe49609369724d69c5f897b62fba4e9b Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 10:56:47 +0200 Subject: [PATCH 297/390] Fix --- .../_modules_/grid/hooks/features/clipboard/useGridClipboard.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts b/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts index 190bba34fdc00..03c7e59106c48 100644 --- a/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts +++ b/packages/grid/_modules_/grid/hooks/features/clipboard/useGridClipboard.ts @@ -38,6 +38,7 @@ export const useGridClipboard = (apiRef: GridApiRef): void => { const data = apiRef.current.getDataAsCsv({ includeHeaders, + delimiter: '\t', }); if (navigator.clipboard) { From 0f61a898d35a72ba2099d81075ee6783bbc591db Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 11:30:09 +0200 Subject: [PATCH 298/390] docs --- .../api-docs/data-grid/grid-csv-export-options.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/pages/api-docs/data-grid/grid-csv-export-options.md b/docs/pages/api-docs/data-grid/grid-csv-export-options.md index a25f00e213bfe..0b059629ca9f0 100644 --- a/docs/pages/api-docs/data-grid/grid-csv-export-options.md +++ b/docs/pages/api-docs/data-grid/grid-csv-export-options.md @@ -12,10 +12,11 @@ import { GridCsvExportOptions } from '@mui/x-data-grid'; ## Properties -| Name | Type | Default | Description | -| :-------------------------------------------------------------------------------------------- | :-------------------------------------- | :------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | -| allColumns? | boolean | false
| If `true`, the hidden columns will also be exported. | -| delimiter? | string | ','
| The character used to separate fields. | -| fields? | string[] | | The columns exported in the CSV.
This should only be used if you want to restrict the columns exports. | -| fileName? | string | `document.title`
| The string used as the file name. | -| utf8WithBom? | boolean | false
| If `true`, the UTF-8 Byte Order Mark (BOM) prefixes the exported file.
This can allow Excel to automatically detect file encoding as UTF-8. | +| Name | Type | Default | Description | +| :----------------------------------------------------------------------------------------------- | :-------------------------------------- | :------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | +| allColumns? | boolean | false
| If `true`, the hidden columns will also be exported. | +| delimiter? | string | ','
| The character used to separate fields. | +| fields? | string[] | | The columns exported in the CSV.
This should only be used if you want to restrict the columns exports. | +| fileName? | string | `document.title`
| The string used as the file name. | +| includeHeaders? | boolean | true
| If `true, the first row of the CSV will include the headers of the grid. | +| utf8WithBom? | boolean | false
| If `true`, the UTF-8 Byte Order Mark (BOM) prefixes the exported file.
This can allow Excel to automatically detect file encoding as UTF-8. | From 5643dedb8dddc9357fdc36ec448d31f4d3974249 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 12:36:53 +0200 Subject: [PATCH 299/390] Work --- .../columnSelection/GridHeaderCheckbox.tsx | 18 +++++++++--------- .../hooks/features/export/useGridCsvExport.tsx | 7 +++---- .../features/filter/gridFilterSelector.ts | 5 +++++ .../hooks/features/keyboard/useGridKeyboard.ts | 4 ++-- .../pagination/gridPaginationSelector.ts | 16 ++++++++++++++++ .../features/selection/useGridSelection.ts | 13 ++++++++----- 6 files changed, 43 insertions(+), 20 deletions(-) diff --git a/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx b/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx index f91a06fe1671d..9ea06eead2419 100644 --- a/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx +++ b/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx @@ -12,8 +12,8 @@ import { getDataGridUtilityClass } from '../../gridClasses'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { GridComponentProps } from '../../GridComponentProps'; import { GridHeaderSelectionCheckboxParams } from '../../models/params/gridHeaderSelectionCheckboxParams'; -import { gridSortedVisibleRowEntriesSelector } from '../../hooks/features/filter/gridFilterSelector'; -import { gridSortedVisiblePaginatedRowEntriesSelector } from '../../hooks/features/pagination/gridPaginationSelector'; +import { gridSortedVisibleRowIdsSelector } from '../../hooks/features/filter/gridFilterSelector'; +import { gridSortedVisiblePaginatedRowIdsSelector } from '../../hooks/features/pagination/gridPaginationSelector'; type OwnerState = { classes: GridComponentProps['classes'] }; @@ -36,10 +36,10 @@ const GridHeaderCheckbox = React.forwardRef { if (!rootProps.pagination || !rootProps.checkboxSelectionVisibleOnly) { - return visibleRows.map((row) => row.id); + return visibleRowIds; } - return paginatedVisibleRows.map((row) => row.id); + return paginatedVisibleRowIds; }, [ rootProps.pagination, rootProps.checkboxSelectionVisibleOnly, - paginatedVisibleRows, - visibleRows, + paginatedVisibleRowIds, + visibleRowIds, ]); // Amount of rows selected and that could be selected / unselected by toggling this checkbox diff --git a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx index 6d3da2456f5c9..3c31636ed5dab 100644 --- a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx +++ b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx @@ -3,7 +3,7 @@ import { GridApiRef } from '../../../models/api/gridApiRef'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; import { useGridSelector } from '../../utils/useGridSelector'; import { allGridColumnsSelector, visibleGridColumnsSelector } from '../columns'; -import { gridSortedVisibleRowEntriesSelector } from '../filter'; +import { gridSortedVisibleRowIdsSelector } from '../filter'; import { GridCsvExportApi } from '../../../models/api/gridCsvExportApi'; import { GridCsvExportOptions } from '../../../models/gridExport'; import { useGridLogger } from '../../utils/useGridLogger'; @@ -21,7 +21,7 @@ import { GridStateColDef } from '../../../models'; export const useGridCsvExport = (apiRef: GridApiRef): void => { const logger = useGridLogger(apiRef, 'useGridCsvExport'); const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); - const visibleSortedRowEntries = useGridSelector(apiRef, gridSortedVisibleRowEntriesSelector); + const visibleSortedRowIds = useGridSelector(apiRef, gridSortedVisibleRowIdsSelector); const columns = useGridSelector(apiRef, allGridColumnsSelector); const getDataAsCsv = React.useCallback( @@ -39,7 +39,6 @@ export const useGridCsvExport = (apiRef: GridApiRef): void => { exportedColumns = validColumns.filter((column) => !column.disableExport); } - const visibleSortedRowIds = visibleSortedRowEntries.map((el) => el.id); const selectedRows = apiRef.current.getSelectedRows(); const exportedRowIds = selectedRows.size > 0 @@ -54,7 +53,7 @@ export const useGridCsvExport = (apiRef: GridApiRef): void => { includeHeaders: options?.includeHeaders ?? true, }); }, - [logger, visibleColumns, columns, visibleSortedRowEntries, apiRef], + [logger, visibleColumns, columns, visibleSortedRowIds, apiRef], ); const exportDataAsCsv = React.useCallback( diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index 9f6e25b9e2aa4..034d8c7b288b4 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -34,6 +34,11 @@ export const gridSortedVisibleRowEntriesSelector = createSelector( sortedRows.filter((row) => visibleRowsLookup[row.id] !== false), ); +export const gridSortedVisibleRowIdsSelector = createSelector( + gridSortedVisibleRowEntriesSelector, + (visibleSortedRowEntries) => visibleSortedRowEntries.map((row) => row.id), +); + export const gridSortedVisibleTopLevelRowEntriesSelector = createSelector( gridSortedVisibleRowEntriesSelector, gridRowTreeSelector, diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts index 23ac1c346d455..990008ff6d200 100644 --- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts +++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts @@ -11,7 +11,7 @@ import { import { isEnterKey, isNavigationKey, isSpaceKey } from '../../../utils/keyboardUtils'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { GridCellModes } from '../../../models/gridEditRowModel'; -import { gridSortedVisibleRowEntriesSelector } from '../filter'; +import { gridSortedVisibleRowIdsSelector } from '../filter/gridFilterSelector'; /** * @requires useGridSelection (method) @@ -37,7 +37,7 @@ export const useGridKeyboard = (apiRef: GridApiRef): void => { )! as HTMLElement; const startRowIndex = Number(rowEl.getAttribute('data-rowindex')); - const startId = gridSortedVisibleRowEntriesSelector(apiRef.current.state)[startRowIndex].id; + const startId = gridSortedVisibleRowIdsSelector(apiRef.current.state)[startRowIndex]; if (startId === focusCell.id) { return; diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts index 220fb548a6357..c8868ab210ccc 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts @@ -2,6 +2,7 @@ import { createSelector } from 'reselect'; import { GridState } from '../../../models/gridState'; import { gridSortedVisibleRowEntriesSelector, + gridSortedVisibleRowIdsSelector, gridSortedVisibleTopLevelRowEntriesSelector, } from '../filter/gridFilterSelector'; import { GridPaginationState } from './gridPaginationState'; @@ -86,3 +87,18 @@ export const gridSortedVisiblePaginatedRowEntriesSelector = createSelector( ); }, ); + +export const gridSortedVisiblePaginatedRowIdsSelector = createSelector( + gridSortedVisibleRowIdsSelector, + gridPaginationRowRangeSelector, + (visibleSortedRowIds, paginationRange) => { + if (!paginationRange) { + return []; + } + + return visibleSortedRowIds.slice( + paginationRange.firstRowIndex, + paginationRange.lastRowIndex + 1, + ); + }, +); diff --git a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts index ab838885d18f4..c89a2be71d8b0 100644 --- a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts +++ b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts @@ -16,8 +16,11 @@ import { selectedGridRowsSelector, selectedIdsLookupSelector, } from './gridSelectionSelector'; -import { gridSortedVisiblePaginatedRowEntriesSelector } from '../pagination'; -import { gridSortedVisibleRowEntriesSelector } from '../filter/gridFilterSelector'; +import { gridSortedVisiblePaginatedRowIdsSelector } from '../pagination/gridPaginationSelector'; +import { + gridSortedVisibleRowEntriesSelector, + gridSortedVisibleRowIdsSelector, +} from '../filter/gridFilterSelector'; import { GridHeaderSelectionCheckboxParams } from '../../../models/params/gridHeaderSelectionCheckboxParams'; import { GridCellParams } from '../../../models/params/gridCellParams'; import { GridRowSelectionCheckboxParams } from '../../../models/params/gridRowSelectionCheckboxParams'; @@ -317,10 +320,10 @@ export const useGridSelection = ( props.checkboxSelectionVisibleOnly && props.pagination; const selector = shouldLimitSelectionToCurrentPage - ? gridSortedVisiblePaginatedRowEntriesSelector - : gridSortedVisibleRowEntriesSelector; + ? gridSortedVisiblePaginatedRowIdsSelector + : gridSortedVisibleRowIdsSelector; - const rowsToBeSelected = selector(apiRef.current.state).map((row) => row.id); + const rowsToBeSelected = selector(apiRef.current.state); apiRef.current.selectRows(rowsToBeSelected, params.value); }, From cbd0275ec012ada0ad7d5ddd4a50904e41a08061 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 12:37:55 +0200 Subject: [PATCH 300/390] Fix --- .../grid/components/columnSelection/GridHeaderCheckbox.tsx | 4 ++-- .../grid/hooks/features/pagination/gridPaginationSelector.ts | 4 ++-- .../grid/hooks/features/selection/useGridSelection.ts | 4 ++-- .../grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx b/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx index 9ea06eead2419..9fafa1291d2ab 100644 --- a/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx +++ b/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx @@ -13,7 +13,7 @@ import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { GridComponentProps } from '../../GridComponentProps'; import { GridHeaderSelectionCheckboxParams } from '../../models/params/gridHeaderSelectionCheckboxParams'; import { gridSortedVisibleRowIdsSelector } from '../../hooks/features/filter/gridFilterSelector'; -import { gridSortedVisiblePaginatedRowIdsSelector } from '../../hooks/features/pagination/gridPaginationSelector'; +import { gridPaginatedVisibleSortedGridRowIdsSelector } from '../../hooks/features/pagination/gridPaginationSelector'; type OwnerState = { classes: GridComponentProps['classes'] }; @@ -39,7 +39,7 @@ const GridHeaderCheckbox = React.forwardRef { @@ -88,7 +88,7 @@ export const gridSortedVisiblePaginatedRowEntriesSelector = createSelector( }, ); -export const gridSortedVisiblePaginatedRowIdsSelector = createSelector( +export const gridPaginatedVisibleSortedGridRowIdsSelector = createSelector( gridSortedVisibleRowIdsSelector, gridPaginationRowRangeSelector, (visibleSortedRowIds, paginationRange) => { diff --git a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts index c89a2be71d8b0..514f4d33ac4e7 100644 --- a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts +++ b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts @@ -16,7 +16,7 @@ import { selectedGridRowsSelector, selectedIdsLookupSelector, } from './gridSelectionSelector'; -import { gridSortedVisiblePaginatedRowIdsSelector } from '../pagination/gridPaginationSelector'; +import { gridPaginatedVisibleSortedGridRowIdsSelector } from '../pagination/gridPaginationSelector'; import { gridSortedVisibleRowEntriesSelector, gridSortedVisibleRowIdsSelector, @@ -320,7 +320,7 @@ export const useGridSelection = ( props.checkboxSelectionVisibleOnly && props.pagination; const selector = shouldLimitSelectionToCurrentPage - ? gridSortedVisiblePaginatedRowIdsSelector + ? gridPaginatedVisibleSortedGridRowIdsSelector : gridSortedVisibleRowIdsSelector; const rowsToBeSelected = selector(apiRef.current.state); diff --git a/packages/grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts b/packages/grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts index 184b56b0206da..10b0e056c098c 100644 --- a/packages/grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts +++ b/packages/grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts @@ -1,7 +1,7 @@ import { GridComponentProps } from '../../GridComponentProps'; import { gridPaginationRowRangeSelector, - gridSortedVisiblePaginatedRowEntriesSelector, + gridPaginatedVisibleSortedGridRowEntriesSelector, } from '../features/pagination/gridPaginationSelector'; import { gridSortedVisibleRowEntriesSelector } from '../features/filter/gridFilterSelector'; import type { GridApiRef, GridRowEntry } from '../../models'; @@ -24,7 +24,7 @@ export const useRowsInCurrentPage = ( if (props.pagination && props.paginationMode === 'client') { range = gridPaginationRowRangeSelector(state); - rows = gridSortedVisiblePaginatedRowEntriesSelector(state); + rows = gridPaginatedVisibleSortedGridRowEntriesSelector(state); } else { rows = gridSortedVisibleRowEntriesSelector(state); if (rows.length === 0) { From 7e58bac75effa7c7efe9eecb471fae65085ed13c Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 13:02:15 +0200 Subject: [PATCH 301/390] [DataGrid] Unify filtering / sorting / pagination / rows selectors --- .../data-grid/scrolling/ScrollPlayground.js | 4 +-- .../data-grid/scrolling/ScrollPlayground.tsx | 10 +++--- .../_modules_/grid/components/GridFooter.tsx | 4 +-- .../grid/components/GridVirtualScroller.tsx | 14 ++++----- .../grid/components/base/GridOverlays.tsx | 4 +-- .../columnSelection/GridHeaderCheckbox.tsx | 14 ++++----- .../container/useGridContainerProps.ts | 4 +-- .../features/export/useGridCsvExport.tsx | 4 +-- .../features/export/useGridPrintExport.tsx | 4 +-- .../features/filter/gridFilterSelector.ts | 31 ++++++------------- .../hooks/features/filter/useGridFilter.ts | 16 +++++----- .../features/keyboard/useGridKeyboard.ts | 4 +-- .../keyboard/useGridKeyboardNavigation.ts | 22 ++++++------- .../pagination/gridPaginationSelector.ts | 4 +-- .../hooks/features/pagination/useGridPage.ts | 4 +-- .../hooks/features/rows/gridRowsSelector.ts | 7 +---- .../grid/hooks/features/rows/useGridRows.ts | 6 ++-- .../features/selection/useGridSelection.ts | 8 ++--- .../features/sorting/gridSortingSelector.ts | 26 ++++++---------- .../hooks/features/sorting/useGridSorting.ts | 14 ++++----- 20 files changed, 90 insertions(+), 114 deletions(-) diff --git a/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.js b/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.js index beddfb4b08c8d..e06f72a0a7784 100644 --- a/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.js +++ b/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.js @@ -7,7 +7,7 @@ import HomeIcon from '@mui/icons-material/Home'; import { DataGridPro, useGridApiRef, - visibleGridRowCountSelector, + gridVisibleRowCountSelector, visibleGridColumnsLengthSelector, visibleGridColumnsSelector, visibleSortedGridRowIdsSelector, @@ -36,7 +36,7 @@ export default function ScrollPlayground() { }, [apiRef, coordinates]); const handleClick = (position) => () => { - const maxRowIndex = visibleGridRowCountSelector(apiRef.current.state) - 1; + const maxRowIndex = gridVisibleRowCountSelector(apiRef.current.state) - 1; const maxColIndex = visibleGridColumnsLengthSelector(apiRef.current.state) - 1; setCoordinates((coords) => { diff --git a/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.tsx b/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.tsx index 82b90f94cca5d..ef7be1b136cd5 100644 --- a/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.tsx +++ b/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.tsx @@ -7,10 +7,10 @@ import HomeIcon from '@mui/icons-material/Home'; import { DataGridPro, useGridApiRef, - visibleGridRowCountSelector, + gridVisibleRowCountSelector, visibleGridColumnsLengthSelector, visibleGridColumnsSelector, - visibleSortedGridRowIdsSelector, + gridSortedVisibleRowIdsSelector, GridCellParams, } from '@mui/x-data-grid-pro'; import { useDemoData } from '@mui/x-data-grid-generator'; @@ -31,13 +31,13 @@ export default function ScrollPlayground() { React.useEffect(() => { const { rowIndex, colIndex } = coordinates; apiRef.current.scrollToIndexes(coordinates); - const id = visibleSortedGridRowIdsSelector(apiRef.current.state)[rowIndex]; + const id = gridSortedVisibleRowIdsSelector(apiRef.current.state)[rowIndex]; const column = visibleGridColumnsSelector(apiRef.current.state)[colIndex]; apiRef.current.setCellFocus(id, column.field); }, [apiRef, coordinates]); const handleClick = (position: string) => () => { - const maxRowIndex = visibleGridRowCountSelector(apiRef.current.state) - 1; + const maxRowIndex = gridVisibleRowCountSelector(apiRef.current.state) - 1; const maxColIndex = visibleGridColumnsLengthSelector(apiRef.current.state) - 1; setCoordinates((coords) => { @@ -57,7 +57,7 @@ export default function ScrollPlayground() { }; const handleCellClick = (params: GridCellParams) => { - const rowIndex = visibleSortedGridRowIdsSelector(apiRef.current.state).findIndex( + const rowIndex = gridSortedVisibleRowIdsSelector(apiRef.current.state).findIndex( (id) => id === params.id, ); const colIndex = visibleGridColumnsSelector(apiRef.current.state).findIndex( diff --git a/packages/grid/_modules_/grid/components/GridFooter.tsx b/packages/grid/_modules_/grid/components/GridFooter.tsx index e7fed93f24be6..0f45eff837106 100644 --- a/packages/grid/_modules_/grid/components/GridFooter.tsx +++ b/packages/grid/_modules_/grid/components/GridFooter.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { useGridSelector } from '../hooks/utils/useGridSelector'; import { gridRowCountSelector } from '../hooks/features/rows/gridRowsSelector'; import { selectedGridRowsCountSelector } from '../hooks/features/selection/gridSelectionSelector'; -import { visibleGridRowCountSelector } from '../hooks/features/filter/gridFilterSelector'; +import { gridVisibleRowCountSelector } from '../hooks/features/filter/gridFilterSelector'; import { useGridApiContext } from '../hooks/utils/useGridApiContext'; import { GridRowCount } from './GridRowCount'; import { GridSelectedRowCount } from './GridSelectedRowCount'; @@ -15,7 +15,7 @@ export const GridFooter = React.forwardRef 0 ? ( diff --git a/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx b/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx index 056ddf29a024a..d294b65773172 100644 --- a/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx +++ b/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx @@ -15,7 +15,7 @@ import { gridFocusCellSelector, gridTabIndexCellSelector, } from '../hooks/features/focus/gridFocusStateSelector'; -import { visibleSortedGridRowsAsArraySelector } from '../hooks/features/filter/gridFilterSelector'; +import { gridSortedVisibleRowEntriesSelector } from '../hooks/features/filter/gridFilterSelector'; import { gridDensityRowHeightSelector } from '../hooks/features/density/densitySelector'; import { gridEditRowsStateSelector } from '../hooks/features/editRows/gridEditRowsSelector'; import { GridEvents } from '../constants/eventsConstants'; @@ -105,7 +105,7 @@ const GridVirtualScroller = React.forwardRef { if (rootProps.pagination && rootProps.paginationMode === 'client') { const start = paginationState.pageSize * paginationState.page; - return visibleSortedRowsAsArray.slice(start, start + paginationState.pageSize); + return visibleSortedRowEntries.slice(start, start + paginationState.pageSize); } - return visibleSortedRowsAsArray; - }, [paginationState, rootProps.pagination, rootProps.paginationMode, visibleSortedRowsAsArray]); + return visibleSortedRowEntries; + }, [paginationState, rootProps.pagination, rootProps.paginationMode, visibleSortedRowEntries]); const computeRenderContext = React.useCallback(() => { if (disableVirtualization) { @@ -282,12 +282,12 @@ const GridVirtualScroller = React.forwardRef 0 && visibleRowCount === 0; diff --git a/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx b/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx index 49146cdd8ae33..9fafa1291d2ab 100644 --- a/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx +++ b/packages/grid/_modules_/grid/components/columnSelection/GridHeaderCheckbox.tsx @@ -12,7 +12,7 @@ import { getDataGridUtilityClass } from '../../gridClasses'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { GridComponentProps } from '../../GridComponentProps'; import { GridHeaderSelectionCheckboxParams } from '../../models/params/gridHeaderSelectionCheckboxParams'; -import { visibleSortedGridRowIdsSelector } from '../../hooks/features/filter/gridFilterSelector'; +import { gridSortedVisibleRowIdsSelector } from '../../hooks/features/filter/gridFilterSelector'; import { gridPaginatedVisibleSortedGridRowIdsSelector } from '../../hooks/features/pagination/gridPaginationSelector'; type OwnerState = { classes: GridComponentProps['classes'] }; @@ -36,8 +36,8 @@ const GridHeaderCheckbox = React.forwardRef { if (!rootProps.pagination || !rootProps.checkboxSelectionVisibleOnly) { - return visibleRows; + return visibleRowIds; } - return paginatedVisibleRows; + return paginatedVisibleRowIds; }, [ rootProps.pagination, rootProps.checkboxSelectionVisibleOnly, - paginatedVisibleRows, - visibleRows, + paginatedVisibleRowIds, + visibleRowIds, ]); // Amount of rows selected and that could be selected / unselected by toggling this checkbox diff --git a/packages/grid/_modules_/grid/hooks/features/container/useGridContainerProps.ts b/packages/grid/_modules_/grid/hooks/features/container/useGridContainerProps.ts index 7ecfcdd5804de..ece93a01a713b 100644 --- a/packages/grid/_modules_/grid/hooks/features/container/useGridContainerProps.ts +++ b/packages/grid/_modules_/grid/hooks/features/container/useGridContainerProps.ts @@ -14,7 +14,7 @@ import { GridState } from '../../../models/gridState'; import { useGridSelector } from '../../utils/useGridSelector'; import { useGridState } from '../../utils/useGridState'; import { gridDensityRowHeightSelector } from '../density/densitySelector'; -import { visibleGridRowCountSelector } from '../filter/gridFilterSelector'; +import { gridVisibleRowCountSelector } from '../filter/gridFilterSelector'; import { gridPaginationSelector } from '../pagination/gridPaginationSelector'; import { useGridLogger } from '../../utils/useGridLogger'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; @@ -71,7 +71,7 @@ export const useGridContainerProps = ( const windowSizesRef = React.useRef({ width: 0, height: 0 }); const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector); const columnsTotalWidth = useGridSelector(apiRef, gridColumnsTotalWidthSelector); - const visibleRowsCount = useGridSelector(apiRef, visibleGridRowCountSelector); + const visibleRowsCount = useGridSelector(apiRef, gridVisibleRowCountSelector); const paginationState = useGridSelector(apiRef, gridPaginationSelector); const windowRef = apiRef.current.windowRef; diff --git a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx index 01fcf78ef9ed2..3c31636ed5dab 100644 --- a/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx +++ b/packages/grid/_modules_/grid/hooks/features/export/useGridCsvExport.tsx @@ -3,7 +3,7 @@ import { GridApiRef } from '../../../models/api/gridApiRef'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; import { useGridSelector } from '../../utils/useGridSelector'; import { allGridColumnsSelector, visibleGridColumnsSelector } from '../columns'; -import { visibleSortedGridRowIdsSelector } from '../filter'; +import { gridSortedVisibleRowIdsSelector } from '../filter'; import { GridCsvExportApi } from '../../../models/api/gridCsvExportApi'; import { GridCsvExportOptions } from '../../../models/gridExport'; import { useGridLogger } from '../../utils/useGridLogger'; @@ -21,7 +21,7 @@ import { GridStateColDef } from '../../../models'; export const useGridCsvExport = (apiRef: GridApiRef): void => { const logger = useGridLogger(apiRef, 'useGridCsvExport'); const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); - const visibleSortedRowIds = useGridSelector(apiRef, visibleSortedGridRowIdsSelector); + const visibleSortedRowIds = useGridSelector(apiRef, gridSortedVisibleRowIdsSelector); const columns = useGridSelector(apiRef, allGridColumnsSelector); const getDataAsCsv = React.useCallback( diff --git a/packages/grid/_modules_/grid/hooks/features/export/useGridPrintExport.tsx b/packages/grid/_modules_/grid/hooks/features/export/useGridPrintExport.tsx index 0e20eefd66846..3dcebbe8c12f5 100644 --- a/packages/grid/_modules_/grid/hooks/features/export/useGridPrintExport.tsx +++ b/packages/grid/_modules_/grid/hooks/features/export/useGridPrintExport.tsx @@ -3,7 +3,7 @@ import { ownerDocument } from '@mui/material/utils'; import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridPrintExportApi } from '../../../models/api/gridPrintExportApi'; import { useGridLogger } from '../../utils/useGridLogger'; -import { visibleGridRowCountSelector } from '../filter/gridFilterSelector'; +import { gridVisibleRowCountSelector } from '../filter/gridFilterSelector'; import { GridComponentProps } from '../../../GridComponentProps'; import { GridPrintExportOptions } from '../../../models/gridExport'; @@ -39,7 +39,7 @@ export const useGridPrintExport = ( const [gridState, setGridState] = useGridState(apiRef); const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector); const headerHeight = useGridSelector(apiRef, gridDensityHeaderHeightSelector); - const visibleRowCount = useGridSelector(apiRef, visibleGridRowCountSelector); + const visibleRowCount = useGridSelector(apiRef, gridVisibleRowCountSelector); const columns = useGridSelector(apiRef, allGridColumnsSelector); const doc = React.useRef(null); const previousGridState = React.useRef(); diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index 676f58935a662..14d77285e7f72 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -1,9 +1,8 @@ import { createSelector } from 'reselect'; import { GridFilterItem } from '../../../models/gridFilterItem'; -import { GridRowId, GridRowModel } from '../../../models/gridRows'; import { GridState } from '../../../models/gridState'; import { gridRowCountSelector } from '../rows/gridRowsSelector'; -import { sortedGridRowsSelector } from '../sorting/gridSortingSelector'; +import { gridSortedRowEntriesSelector } from '../sorting/gridSortingSelector'; import { gridColumnLookupSelector } from '../columns/gridColumnsSelector'; export const gridFilterStateSelector = (state: GridState) => state.filter; @@ -18,31 +17,19 @@ export const gridVisibleRowsLookupSelector = createSelector( (filterState) => filterState.visibleRowsLookup, ); -export const visibleSortedGridRowsSelector = createSelector( +export const gridSortedVisibleRowEntriesSelector = createSelector( gridVisibleRowsLookupSelector, - sortedGridRowsSelector, - (visibleRowsLookup, sortedRows) => { - const map = new Map(); - sortedRows.forEach((row, id) => { - if (visibleRowsLookup[id] !== false) { - map.set(id, row); - } - }); - return map; - }, -); - -export const visibleSortedGridRowsAsArraySelector = createSelector( - visibleSortedGridRowsSelector, - (visibleSortedRows) => [...visibleSortedRows.entries()], + gridSortedRowEntriesSelector, + (visibleRowsLookup, sortedRows) => + sortedRows.filter((row) => visibleRowsLookup[row.id] !== false), ); -export const visibleSortedGridRowIdsSelector = createSelector( - visibleSortedGridRowsSelector, - (visibleSortedRows) => [...visibleSortedRows.keys()], +export const gridSortedVisibleRowIdsSelector = createSelector( + gridSortedVisibleRowEntriesSelector, + (visibleSortedRowEntries) => visibleSortedRowEntries.map((row) => row.id), ); -export const visibleGridRowCountSelector = createSelector( +export const gridVisibleRowCountSelector = createSelector( gridFilterStateSelector, gridRowCountSelector, (filterState, totalRowsCount) => { diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index bd78ae179052d..382f06b6ed3fb 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -13,12 +13,12 @@ import { useGridLogger } from '../../utils/useGridLogger'; import { filterableGridColumnsIdsSelector } from '../columns/gridColumnsSelector'; import { useGridState } from '../../utils/useGridState'; import { GridPreferencePanelsValue } from '../preferencesPanel/gridPreferencePanelsValue'; -import { sortedGridRowsSelector } from '../sorting/gridSortingSelector'; +import { gridSortedRowIdsSelector } from '../sorting/gridSortingSelector'; import { getDefaultGridFilterModel } from './gridFilterState'; import { GridFilterModel } from '../../../models/gridFilterModel'; import { gridVisibleRowsLookupSelector, - visibleSortedGridRowsSelector, + gridSortedVisibleRowEntriesSelector, gridFilterModelSelector, } from './gridFilterSelector'; import { useGridStateInit } from '../../utils/useGridStateInit'; @@ -127,9 +127,9 @@ export const useGridFilter = ( // We run the selector on the state here to avoid rendering the rows and then filtering again. // This way we have latest rows on the first rendering - const rows = sortedGridRowsSelector(state); + const rows = gridSortedRowIdsSelector(state); - rows.forEach((row: GridRowModel, id: GridRowId) => { + rows.forEach((id) => { const params = apiRef.current.getCellParams(id, newFilterItem.columnField!); const isShown = applyFilterOnRow(params); @@ -304,10 +304,10 @@ export const useGridFilter = ( [apiRef, logger, setGridState], ); - const getVisibleRowModels = React.useCallback( - () => visibleSortedGridRowsSelector(apiRef.current.state), - [apiRef], - ); + const getVisibleRowModels = React.useCallback(() => { + const visibleSortedRows = gridSortedVisibleRowEntriesSelector(apiRef.current.state); + return new Map(visibleSortedRows.map((row) => [row.id, row.model])); + }, [apiRef]); useGridApiMethod( apiRef, diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts index b8c82eac1e0af..990008ff6d200 100644 --- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts +++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts @@ -11,7 +11,7 @@ import { import { isEnterKey, isNavigationKey, isSpaceKey } from '../../../utils/keyboardUtils'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { GridCellModes } from '../../../models/gridEditRowModel'; -import { visibleSortedGridRowIdsSelector } from '../filter'; +import { gridSortedVisibleRowIdsSelector } from '../filter/gridFilterSelector'; /** * @requires useGridSelection (method) @@ -37,7 +37,7 @@ export const useGridKeyboard = (apiRef: GridApiRef): void => { )! as HTMLElement; const startRowIndex = Number(rowEl.getAttribute('data-rowindex')); - const startId = visibleSortedGridRowIdsSelector(apiRef.current.state)[startRowIndex]; + const startId = gridSortedVisibleRowIdsSelector(apiRef.current.state)[startRowIndex]; if (startId === focusCell.id) { return; diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts index d985a9cc75d7a..a7e24f6bd47a6 100644 --- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts +++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts @@ -23,8 +23,8 @@ import { useGridLogger } from '../../utils/useGridLogger'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { GridComponentProps } from '../../../GridComponentProps'; import { - visibleGridRowCountSelector, - visibleSortedGridRowsAsArraySelector, + gridVisibleRowCountSelector, + gridSortedVisibleRowEntriesSelector, } from '../filter/gridFilterSelector'; const getNextCellIndexes = (key: string, indexes: GridCellIndexCoordinates) => { @@ -78,10 +78,10 @@ export const useGridKeyboardNavigation = ( ): void => { const logger = useGridLogger(apiRef, 'useGridKeyboardNavigation'); const paginationState = useGridSelector(apiRef, gridPaginationSelector); - const totalVisibleRowCount = useGridSelector(apiRef, visibleGridRowCountSelector); + const totalVisibleRowCount = useGridSelector(apiRef, gridVisibleRowCountSelector); const colCount = useGridSelector(apiRef, visibleGridColumnsLengthSelector); const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); - const visibleSortedRowsAsArray = useGridSelector(apiRef, visibleSortedGridRowsAsArraySelector); + const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowEntriesSelector); const mapKey = (event: React.KeyboardEvent) => { if (isEnterKey(event.key)) { @@ -97,7 +97,7 @@ export const useGridKeyboardNavigation = ( (params: GridCellParams, event: React.KeyboardEvent) => { event.preventDefault(); const colIndex = apiRef.current.getColumnIndex(params.field); - const rowIndex = visibleSortedRowsAsArray.findIndex(([id]) => id === params.id); + const rowIndex = visibleSortedRows.findIndex((row) => row.id === params.id); const key = mapKey(event); const isCtrlPressed = event.ctrlKey || event.metaKey || event.shiftKey; @@ -158,12 +158,12 @@ export const useGridKeyboardNavigation = ( ); apiRef.current.scrollToIndexes(nextCellIndexes); const field = apiRef.current.getVisibleColumns()[nextCellIndexes.colIndex].field; - const [id] = visibleSortedRowsAsArray[nextCellIndexes.rowIndex]; - apiRef.current.setCellFocus(id, field); + const node = visibleSortedRows[nextCellIndexes.rowIndex]; + apiRef.current.setCellFocus(node.id, field); }, [ apiRef, - visibleSortedRowsAsArray, + visibleSortedRows, totalVisibleRowCount, props.pagination, paginationState.pageSize, @@ -204,8 +204,8 @@ export const useGridKeyboardNavigation = ( if (!nextColumnHeaderIndexes) { const field = apiRef.current.getVisibleColumns()[colIndex].field; - const [id] = visibleSortedRowsAsArray[0]; - apiRef.current.setCellFocus(id, field); + const node = visibleSortedRows[0]; + apiRef.current.setCellFocus(node.id, field); return; } @@ -220,7 +220,7 @@ export const useGridKeyboardNavigation = ( const field = apiRef.current.getVisibleColumns()[nextColumnHeaderIndexes.colIndex].field; apiRef.current.setColumnHeaderFocus(field, event); }, - [apiRef, colCount, containerSizes, logger, visibleSortedRowsAsArray], + [apiRef, colCount, containerSizes, logger, visibleSortedRows], ); useGridApiEventHandler(apiRef, GridEvents.cellNavigationKeyDown, navigateCells); diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts index 8568abac14bcf..59266d96036c6 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts @@ -1,6 +1,6 @@ import { createSelector } from 'reselect'; import { GridState } from '../../../models/gridState'; -import { visibleSortedGridRowIdsSelector } from '../filter/gridFilterSelector'; +import { gridSortedVisibleRowIdsSelector } from '../filter/gridFilterSelector'; import { GridPaginationState } from './gridPaginationState'; export const gridPaginationSelector = (state: GridState): GridPaginationState => state.pagination; @@ -17,7 +17,7 @@ export const gridPageSizeSelector = createSelector( export const gridPaginatedVisibleSortedGridRowIdsSelector = createSelector( gridPaginationSelector, - visibleSortedGridRowIdsSelector, + gridSortedVisibleRowIdsSelector, (pagination, visibleSortedRows) => { const firstSelectedRowIndex = pagination.page * pagination.pageSize; const lastSelectedRowIndex = firstSelectedRowIndex + pagination.pageSize; diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts index 26812257e6223..b03a1fcc11180 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts @@ -11,7 +11,7 @@ import { GridEvents } from '../../../constants/eventsConstants'; import { GridComponentProps } from '../../../GridComponentProps'; import { GridPageApi } from '../../../models/api/gridPageApi'; import { GridPaginationState } from './gridPaginationState'; -import { visibleGridRowCountSelector } from '../filter'; +import { gridVisibleRowCountSelector } from '../filter'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { gridPageSelector } from './gridPaginationSelector'; @@ -56,7 +56,7 @@ export const useGridPage = ( })); const [, setGridState, forceUpdate] = useGridState(apiRef); - const visibleRowCount = useGridSelector(apiRef, visibleGridRowCountSelector); + const visibleRowCount = useGridSelector(apiRef, gridVisibleRowCountSelector); apiRef.current.updateControlState({ stateId: 'page', diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts index ade2812c85984..9e08ac056ce1f 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts @@ -14,12 +14,7 @@ export const gridRowsLookupSelector = createSelector( (rows: GridRowsState) => rows.idRowsLookup, ); -export const unorderedGridRowIdsSelector = createSelector( +export const gridRowIdsSelector = createSelector( gridRowsStateSelector, (rows: GridRowsState) => rows.allRows, ); - -export const unorderedGridRowModelsSelector = createSelector( - gridRowsStateSelector, - (rows: GridRowsState) => rows.allRows.map((id) => rows.idRowsLookup[id]), -); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 41149d76f9e46..72720ebe78e90 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -18,7 +18,7 @@ import { GridRowsState } from './gridRowsState'; import { gridRowCountSelector, gridRowsLookupSelector, - unorderedGridRowIdsSelector, + gridRowIdsSelector, } from './gridRowsSelector'; export interface GridRowsInternalCache { @@ -225,7 +225,7 @@ export const useGridRows = ( ); const getRowModels = React.useCallback(() => { - const allRows = unorderedGridRowIdsSelector(apiRef.current.state); + const allRows = gridRowIdsSelector(apiRef.current.state); const idRowsLookup = gridRowsLookupSelector(apiRef.current.state); return new Map(allRows.map((id) => [id, idRowsLookup[id]])); @@ -237,7 +237,7 @@ export const useGridRows = ( ); const getAllRowIds = React.useCallback( - () => unorderedGridRowIdsSelector(apiRef.current.state), + () => gridRowIdsSelector(apiRef.current.state), [apiRef], ); diff --git a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts index 38cd12f3fd54c..3a2dbbdbb7c4f 100644 --- a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts +++ b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts @@ -17,7 +17,7 @@ import { selectedIdsLookupSelector, } from './gridSelectionSelector'; import { gridPaginatedVisibleSortedGridRowIdsSelector } from '../pagination'; -import { visibleSortedGridRowIdsSelector } from '../filter/gridFilterSelector'; +import { gridSortedVisibleRowIdsSelector } from '../filter/gridFilterSelector'; import { GridHeaderSelectionCheckboxParams } from '../../../models/params/gridHeaderSelectionCheckboxParams'; import { GridCellParams } from '../../../models/params/gridCellParams'; import { GridRowSelectionCheckboxParams } from '../../../models/params/gridRowSelectionCheckboxParams'; @@ -185,7 +185,7 @@ export const useGridSelection = ( logger.debug(`Expanding selection from row ${startId} to row ${endId}`); - const visibleRowIds = visibleSortedGridRowIdsSelector(apiRef.current.state); + const visibleRowIds = gridSortedVisibleRowIdsSelector(apiRef.current.state); const startIndex = visibleRowIds.indexOf(startId); const endIndex = visibleRowIds.indexOf(endId); const [start, end] = startIndex > endIndex ? [endIndex, startIndex] : [startIndex, endIndex]; @@ -202,7 +202,7 @@ export const useGridSelection = ( const startId = lastRowToggled.current ?? id; const isSelected = apiRef.current.isRowSelected(id); if (isSelected) { - const visibleRowIds = visibleSortedGridRowIdsSelector(apiRef.current.state); + const visibleRowIds = gridSortedVisibleRowIdsSelector(apiRef.current.state); const startIndex = visibleRowIds.findIndex((rowId) => rowId === startId); const endIndex = visibleRowIds.findIndex((rowId) => rowId === endId); if (startIndex > endIndex) { @@ -318,7 +318,7 @@ export const useGridSelection = ( const rowsToBeSelected = shouldLimitSelectionToCurrentPage ? gridPaginatedVisibleSortedGridRowIdsSelector(apiRef.current.state) - : visibleSortedGridRowIdsSelector(apiRef.current.state); + : gridSortedVisibleRowIdsSelector(apiRef.current.state); apiRef.current.selectRows(rowsToBeSelected, params.value); }, diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts index 47d616863b1cb..2db211ee9101c 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts @@ -1,33 +1,27 @@ import { createSelector } from 'reselect'; -import { GridRowId, GridRowModel, GridRowsLookup } from '../../../models/gridRows'; +import { GridRowId } from '../../../models/gridRows'; import { GridSortDirection, GridSortModel } from '../../../models/gridSortModel'; import { GridState } from '../../../models/gridState'; -import { gridRowsLookupSelector, unorderedGridRowIdsSelector } from '../rows/gridRowsSelector'; +import { gridRowsLookupSelector, gridRowIdsSelector } from '../rows/gridRowsSelector'; import { GridSortingState } from './gridSortingState'; -const sortingGridStateSelector = (state: GridState) => state.sorting; +const gridSortingStateSelector = (state: GridState) => state.sorting; -export const sortedGridRowIdsSelector = createSelector( - sortingGridStateSelector, - unorderedGridRowIdsSelector, +export const gridSortedRowIdsSelector = createSelector( + gridSortingStateSelector, + gridRowIdsSelector, (sortingState: GridSortingState, allRows: GridRowId[]) => sortingState.sortedRows.length ? sortingState.sortedRows : allRows, ); -export const sortedGridRowsSelector = createSelector( - sortedGridRowIdsSelector, +export const gridSortedRowEntriesSelector = createSelector( + gridSortedRowIdsSelector, gridRowsLookupSelector, - (sortedIds: GridRowId[], idRowsLookup: GridRowsLookup) => { - const map = new Map(); - sortedIds.forEach((id) => { - map.set(id, idRowsLookup[id]); - }); - return map; - }, + (sortedIds, idRowsLookup) => sortedIds.map((id) => ({ id, model: idRowsLookup[id] })), ); export const gridSortModelSelector = createSelector( - sortingGridStateSelector, + gridSortingStateSelector, (sorting) => sorting.sortModel, ); diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 4bb2100c40f19..7917305a8722f 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -24,8 +24,8 @@ import { allGridColumnsSelector } from '../columns/gridColumnsSelector'; import { useGridState } from '../../utils/useGridState'; import { gridSortModelSelector, - sortedGridRowIdsSelector, - sortedGridRowsSelector, + gridSortedRowIdsSelector, + gridSortedRowEntriesSelector, } from './gridSortingSelector'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; @@ -247,13 +247,13 @@ export const useGridSorting = ( [apiRef], ); - const getSortedRows = React.useCallback( - () => Object.values(sortedGridRowsSelector(apiRef.current.state)), - [apiRef], - ); + const getSortedRows = React.useCallback(() => { + const sortedRows = gridSortedRowEntriesSelector(apiRef.current.state); + return sortedRows.map((row) => row.model); + }, [apiRef]); const getSortedRowIds = React.useCallback( - () => sortedGridRowIdsSelector(apiRef.current.state), + () => gridSortedRowIdsSelector(apiRef.current.state), [apiRef], ); From 238d3b98e3ec182b4793d33861b33f48e386864d Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 13:12:19 +0200 Subject: [PATCH 302/390] Rename --- .../components/data-grid/scrolling/ScrollPlayground.tsx | 6 +++--- .../_modules_/grid/components/GridVirtualScroller.tsx | 4 ++-- .../components/columnSelection/GridHeaderCheckbox.tsx | 4 ++-- .../grid/hooks/features/export/useGridCsvExport.tsx | 4 ++-- .../grid/hooks/features/filter/gridFilterSelector.ts | 6 +++--- .../_modules_/grid/hooks/features/filter/useGridFilter.ts | 4 ++-- .../grid/hooks/features/keyboard/useGridKeyboard.ts | 4 ++-- .../hooks/features/keyboard/useGridKeyboardNavigation.ts | 4 ++-- .../hooks/features/pagination/gridPaginationSelector.ts | 4 ++-- .../grid/hooks/features/selection/useGridSelection.ts | 8 ++++---- 10 files changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.tsx b/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.tsx index ef7be1b136cd5..c1d5b65d0c0bf 100644 --- a/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.tsx +++ b/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.tsx @@ -10,7 +10,7 @@ import { gridVisibleRowCountSelector, visibleGridColumnsLengthSelector, visibleGridColumnsSelector, - gridSortedVisibleRowIdsSelector, + gridVisibleSortedRowIdsSelector, GridCellParams, } from '@mui/x-data-grid-pro'; import { useDemoData } from '@mui/x-data-grid-generator'; @@ -31,7 +31,7 @@ export default function ScrollPlayground() { React.useEffect(() => { const { rowIndex, colIndex } = coordinates; apiRef.current.scrollToIndexes(coordinates); - const id = gridSortedVisibleRowIdsSelector(apiRef.current.state)[rowIndex]; + const id = gridVisibleSortedRowIdsSelector(apiRef.current.state)[rowIndex]; const column = visibleGridColumnsSelector(apiRef.current.state)[colIndex]; apiRef.current.setCellFocus(id, column.field); }, [apiRef, coordinates]); @@ -57,7 +57,7 @@ export default function ScrollPlayground() { }; const handleCellClick = (params: GridCellParams) => { - const rowIndex = gridSortedVisibleRowIdsSelector(apiRef.current.state).findIndex( + const rowIndex = gridVisibleSortedRowIdsSelector(apiRef.current.state).findIndex( (id) => id === params.id, ); const colIndex = visibleGridColumnsSelector(apiRef.current.state).findIndex( diff --git a/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx b/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx index d294b65773172..796920d7bbcd8 100644 --- a/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx +++ b/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx @@ -15,7 +15,7 @@ import { gridFocusCellSelector, gridTabIndexCellSelector, } from '../hooks/features/focus/gridFocusStateSelector'; -import { gridSortedVisibleRowEntriesSelector } from '../hooks/features/filter/gridFilterSelector'; +import { gridVisibleSortedRowEntriesSelector } from '../hooks/features/filter/gridFilterSelector'; import { gridDensityRowHeightSelector } from '../hooks/features/density/densitySelector'; import { gridEditRowsStateSelector } from '../hooks/features/editRows/gridEditRowsSelector'; import { GridEvents } from '../constants/eventsConstants'; @@ -105,7 +105,7 @@ const GridVirtualScroller = React.forwardRef { const logger = useGridLogger(apiRef, 'useGridCsvExport'); const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); - const visibleSortedRowIds = useGridSelector(apiRef, gridSortedVisibleRowIdsSelector); + const visibleSortedRowIds = useGridSelector(apiRef, gridVisibleSortedRowIdsSelector); const columns = useGridSelector(apiRef, allGridColumnsSelector); const getDataAsCsv = React.useCallback( diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index 14d77285e7f72..6f13823e0669e 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -17,15 +17,15 @@ export const gridVisibleRowsLookupSelector = createSelector( (filterState) => filterState.visibleRowsLookup, ); -export const gridSortedVisibleRowEntriesSelector = createSelector( +export const gridVisibleSortedRowEntriesSelector = createSelector( gridVisibleRowsLookupSelector, gridSortedRowEntriesSelector, (visibleRowsLookup, sortedRows) => sortedRows.filter((row) => visibleRowsLookup[row.id] !== false), ); -export const gridSortedVisibleRowIdsSelector = createSelector( - gridSortedVisibleRowEntriesSelector, +export const gridVisibleSortedRowIdsSelector = createSelector( + gridVisibleSortedRowEntriesSelector, (visibleSortedRowEntries) => visibleSortedRowEntries.map((row) => row.id), ); diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 382f06b6ed3fb..b1d77157b8653 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -18,7 +18,7 @@ import { getDefaultGridFilterModel } from './gridFilterState'; import { GridFilterModel } from '../../../models/gridFilterModel'; import { gridVisibleRowsLookupSelector, - gridSortedVisibleRowEntriesSelector, + gridVisibleSortedRowEntriesSelector, gridFilterModelSelector, } from './gridFilterSelector'; import { useGridStateInit } from '../../utils/useGridStateInit'; @@ -305,7 +305,7 @@ export const useGridFilter = ( ); const getVisibleRowModels = React.useCallback(() => { - const visibleSortedRows = gridSortedVisibleRowEntriesSelector(apiRef.current.state); + const visibleSortedRows = gridVisibleSortedRowEntriesSelector(apiRef.current.state); return new Map(visibleSortedRows.map((row) => [row.id, row.model])); }, [apiRef]); diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts index 990008ff6d200..11b2446916be2 100644 --- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts +++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts @@ -11,7 +11,7 @@ import { import { isEnterKey, isNavigationKey, isSpaceKey } from '../../../utils/keyboardUtils'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { GridCellModes } from '../../../models/gridEditRowModel'; -import { gridSortedVisibleRowIdsSelector } from '../filter/gridFilterSelector'; +import { gridVisibleSortedRowIdsSelector } from '../filter/gridFilterSelector'; /** * @requires useGridSelection (method) @@ -37,7 +37,7 @@ export const useGridKeyboard = (apiRef: GridApiRef): void => { )! as HTMLElement; const startRowIndex = Number(rowEl.getAttribute('data-rowindex')); - const startId = gridSortedVisibleRowIdsSelector(apiRef.current.state)[startRowIndex]; + const startId = gridVisibleSortedRowIdsSelector(apiRef.current.state)[startRowIndex]; if (startId === focusCell.id) { return; diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts index a7e24f6bd47a6..ece57dc3f4d5e 100644 --- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts +++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts @@ -24,7 +24,7 @@ import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { GridComponentProps } from '../../../GridComponentProps'; import { gridVisibleRowCountSelector, - gridSortedVisibleRowEntriesSelector, + gridVisibleSortedRowEntriesSelector, } from '../filter/gridFilterSelector'; const getNextCellIndexes = (key: string, indexes: GridCellIndexCoordinates) => { @@ -81,7 +81,7 @@ export const useGridKeyboardNavigation = ( const totalVisibleRowCount = useGridSelector(apiRef, gridVisibleRowCountSelector); const colCount = useGridSelector(apiRef, visibleGridColumnsLengthSelector); const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); - const visibleSortedRows = useGridSelector(apiRef, gridSortedVisibleRowEntriesSelector); + const visibleSortedRows = useGridSelector(apiRef, gridVisibleSortedRowEntriesSelector); const mapKey = (event: React.KeyboardEvent) => { if (isEnterKey(event.key)) { diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts index 59266d96036c6..1ed45757237db 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts @@ -1,6 +1,6 @@ import { createSelector } from 'reselect'; import { GridState } from '../../../models/gridState'; -import { gridSortedVisibleRowIdsSelector } from '../filter/gridFilterSelector'; +import { gridVisibleSortedRowIdsSelector } from '../filter/gridFilterSelector'; import { GridPaginationState } from './gridPaginationState'; export const gridPaginationSelector = (state: GridState): GridPaginationState => state.pagination; @@ -17,7 +17,7 @@ export const gridPageSizeSelector = createSelector( export const gridPaginatedVisibleSortedGridRowIdsSelector = createSelector( gridPaginationSelector, - gridSortedVisibleRowIdsSelector, + gridVisibleSortedRowIdsSelector, (pagination, visibleSortedRows) => { const firstSelectedRowIndex = pagination.page * pagination.pageSize; const lastSelectedRowIndex = firstSelectedRowIndex + pagination.pageSize; diff --git a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts index 3a2dbbdbb7c4f..dea3aec9c8c98 100644 --- a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts +++ b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts @@ -17,7 +17,7 @@ import { selectedIdsLookupSelector, } from './gridSelectionSelector'; import { gridPaginatedVisibleSortedGridRowIdsSelector } from '../pagination'; -import { gridSortedVisibleRowIdsSelector } from '../filter/gridFilterSelector'; +import { gridVisibleSortedRowIdsSelector } from '../filter/gridFilterSelector'; import { GridHeaderSelectionCheckboxParams } from '../../../models/params/gridHeaderSelectionCheckboxParams'; import { GridCellParams } from '../../../models/params/gridCellParams'; import { GridRowSelectionCheckboxParams } from '../../../models/params/gridRowSelectionCheckboxParams'; @@ -185,7 +185,7 @@ export const useGridSelection = ( logger.debug(`Expanding selection from row ${startId} to row ${endId}`); - const visibleRowIds = gridSortedVisibleRowIdsSelector(apiRef.current.state); + const visibleRowIds = gridVisibleSortedRowIdsSelector(apiRef.current.state); const startIndex = visibleRowIds.indexOf(startId); const endIndex = visibleRowIds.indexOf(endId); const [start, end] = startIndex > endIndex ? [endIndex, startIndex] : [startIndex, endIndex]; @@ -202,7 +202,7 @@ export const useGridSelection = ( const startId = lastRowToggled.current ?? id; const isSelected = apiRef.current.isRowSelected(id); if (isSelected) { - const visibleRowIds = gridSortedVisibleRowIdsSelector(apiRef.current.state); + const visibleRowIds = gridVisibleSortedRowIdsSelector(apiRef.current.state); const startIndex = visibleRowIds.findIndex((rowId) => rowId === startId); const endIndex = visibleRowIds.findIndex((rowId) => rowId === endId); if (startIndex > endIndex) { @@ -318,7 +318,7 @@ export const useGridSelection = ( const rowsToBeSelected = shouldLimitSelectionToCurrentPage ? gridPaginatedVisibleSortedGridRowIdsSelector(apiRef.current.state) - : gridSortedVisibleRowIdsSelector(apiRef.current.state); + : gridVisibleSortedRowIdsSelector(apiRef.current.state); apiRef.current.selectRows(rowsToBeSelected, params.value); }, From 836c1be097b8611d805408b0998d8d75d55f72ff Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 13:19:40 +0200 Subject: [PATCH 303/390] Fix --- .../columnHeaders/GridColumnHeaders.tsx | 4 ++-- .../GridColumnHeadersItemCollection.tsx | 4 ++-- .../toolbar/GridToolbarFilterButton.tsx | 14 +++++--------- .../hooks/features/filter/gridFilterSelector.ts | 17 ++++++----------- 4 files changed, 15 insertions(+), 24 deletions(-) diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx index 01031cd53671b..a9d7a013ace2e 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx @@ -18,7 +18,7 @@ import { GridEvents } from '../../constants/eventsConstants'; import { GridColumnHeaderParams } from '../../models/params/gridColumnHeaderParams'; import { RenderContext } from '../GridVirtualScroller'; import { GridColumnHeaderItem } from './GridColumnHeaderItem'; -import { filterGridColumnLookupSelector } from '../../hooks/features/filter/gridFilterSelector'; +import { gridFilterActiveItemsLookupSelector } from '../../hooks/features/filter/gridFilterSelector'; import { gridColumnMenuSelector } from '../../hooks/features/columnMenu/columnMenuSelector'; import { gridSortColumnLookupSelector } from '../../hooks/features/sorting/gridSortingSelector'; import { @@ -57,7 +57,7 @@ export const GridColumnsHeader = React.forwardRef(function const cellTabIndexState = useGridSelector(apiRef, gridTabIndexCellSelector); const columnHeaderFocus = useGridSelector(apiRef, gridFocusColumnHeaderSelector); const headerHeight = useGridSelector(apiRef, gridDensityHeaderHeightSelector); - const filterColumnLookup = useGridSelector(apiRef, filterGridColumnLookupSelector); + const filterColumnLookup = useGridSelector(apiRef, gridFilterActiveItemsLookupSelector); const sortColumnLookup = useGridSelector(apiRef, gridSortColumnLookupSelector); const columnMenuState = useGridSelector(apiRef, gridColumnMenuSelector); const rootProps = useGridRootProps(); diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx index b646ca63026b5..784d722343362 100644 --- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx +++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeadersItemCollection.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { useGridSelector } from '../../hooks/utils/useGridSelector'; -import { filterGridColumnLookupSelector } from '../../hooks/features/filter/gridFilterSelector'; +import { gridFilterActiveItemsLookupSelector } from '../../hooks/features/filter/gridFilterSelector'; import { gridFocusColumnHeaderSelector, gridTabIndexCellSelector, @@ -27,7 +27,7 @@ function GridColumnHeadersItemCollection(props: GridColumnHeadersItemCollectionP const { columns, dragCol, resizeCol } = props; const apiRef = useGridApiContext(); const sortColumnLookup = useGridSelector(apiRef, gridSortColumnLookupSelector); - const filterColumnLookup = useGridSelector(apiRef, filterGridColumnLookupSelector); + const filterColumnLookup = useGridSelector(apiRef, gridFilterActiveItemsLookupSelector); const columnHeaderFocus = useGridSelector(apiRef, gridFocusColumnHeaderSelector); const renderCtx = useGridSelector(apiRef, gridRenderingSelector).renderContext; const tabIndexState = useGridSelector(apiRef, gridTabIndexColumnHeaderSelector); diff --git a/packages/grid/_modules_/grid/components/toolbar/GridToolbarFilterButton.tsx b/packages/grid/_modules_/grid/components/toolbar/GridToolbarFilterButton.tsx index 1de0b73c02d26..6ff8a3fda5c5b 100644 --- a/packages/grid/_modules_/grid/components/toolbar/GridToolbarFilterButton.tsx +++ b/packages/grid/_modules_/grid/components/toolbar/GridToolbarFilterButton.tsx @@ -8,10 +8,7 @@ import Tooltip, { TooltipProps } from '@mui/material/Tooltip'; import { capitalize } from '@mui/material/utils'; import { gridColumnLookupSelector } from '../../hooks/features/columns/gridColumnsSelector'; import { useGridSelector } from '../../hooks/utils/useGridSelector'; -import { - activeGridFilterItemsSelector, - filterGridItemsCounterSelector, -} from '../../hooks/features/filter/gridFilterSelector'; +import { gridFilterActiveItemsSelector } from '../../hooks/features/filter/gridFilterSelector'; import { gridPreferencePanelStateSelector } from '../../hooks/features/preferencesPanel/gridPreferencePanelSelector'; import { GridPreferencePanelsValue } from '../../hooks/features/preferencesPanel/gridPreferencePanelsValue'; import { GridTranslationKeys } from '../../models/api/gridLocaleTextApi'; @@ -56,8 +53,7 @@ const GridToolbarFilterButton = React.forwardRef - {apiRef.current.getLocaleText('toolbarFiltersTooltipActive')(counter)} + {apiRef.current.getLocaleText('toolbarFiltersTooltipActive')(activeFilters.length)} {activeFilters.map((item, index) => ({ ...(lookup[item.columnField!] && ( @@ -95,7 +91,7 @@ const GridToolbarFilterButton = React.forwardRef
); - }, [apiRef, preferencePanel.open, counter, activeFilters, lookup, classes]); + }, [apiRef, preferencePanel.open, activeFilters, lookup, classes]); const toggleFilter = (event) => { const { open, openedPanelValue } = preferencePanel; diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index 6f13823e0669e..f28f8db4ef420 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -40,7 +40,7 @@ export const gridVisibleRowCountSelector = createSelector( }, ); -export const activeGridFilterItemsSelector = createSelector( +export const gridFilterActiveItemsSelector = createSelector( gridFilterModelSelector, gridColumnLookupSelector, (filterModel, columnLookup) => @@ -64,23 +64,18 @@ export const activeGridFilterItemsSelector = createSelector( }), ); -export const filterGridItemsCounterSelector = createSelector( - activeGridFilterItemsSelector, - (activeFilters) => activeFilters.length, -); - -export type FilterColumnLookup = Record; -export const filterGridColumnLookupSelector = createSelector( - activeGridFilterItemsSelector, +export type GridFilterActiveItemsLookup = { [columnField: string]: GridFilterItem[] }; +export const gridFilterActiveItemsLookupSelector = createSelector( + gridFilterActiveItemsSelector, (activeFilters) => { - const result: FilterColumnLookup = activeFilters.reduce((res, filterItem) => { + const result: GridFilterActiveItemsLookup = activeFilters.reduce((res, filterItem) => { if (!res[filterItem.columnField!]) { res[filterItem.columnField!] = [filterItem]; } else { res[filterItem.columnField!].push(filterItem); } return res; - }, {} as FilterColumnLookup); + }, {} as GridFilterActiveItemsLookup); return result; }, From 75e3b55443f7cf7a67f22e68f6b8cf6e1eddce0c Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 13:22:20 +0200 Subject: [PATCH 304/390] Fix --- .../grid/components/toolbar/GridToolbarFilterButton.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/components/toolbar/GridToolbarFilterButton.tsx b/packages/grid/_modules_/grid/components/toolbar/GridToolbarFilterButton.tsx index 6ff8a3fda5c5b..c367d5c72cabe 100644 --- a/packages/grid/_modules_/grid/components/toolbar/GridToolbarFilterButton.tsx +++ b/packages/grid/_modules_/grid/components/toolbar/GridToolbarFilterButton.tsx @@ -116,7 +116,7 @@ const GridToolbarFilterButton = React.forwardRef + } From 0fb48e26a2b98ea390837857ec311538eeb84532 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 13:28:06 +0200 Subject: [PATCH 305/390] docs ts --- .../components/data-grid/scrolling/ScrollPlayground.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.js b/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.js index e06f72a0a7784..5fec4858e46fe 100644 --- a/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.js +++ b/docs/src/pages/components/data-grid/scrolling/ScrollPlayground.js @@ -10,7 +10,7 @@ import { gridVisibleRowCountSelector, visibleGridColumnsLengthSelector, visibleGridColumnsSelector, - visibleSortedGridRowIdsSelector, + gridVisibleSortedRowIdsSelector, } from '@mui/x-data-grid-pro'; import { useDemoData } from '@mui/x-data-grid-generator'; @@ -30,7 +30,7 @@ export default function ScrollPlayground() { React.useEffect(() => { const { rowIndex, colIndex } = coordinates; apiRef.current.scrollToIndexes(coordinates); - const id = visibleSortedGridRowIdsSelector(apiRef.current.state)[rowIndex]; + const id = gridVisibleSortedRowIdsSelector(apiRef.current.state)[rowIndex]; const column = visibleGridColumnsSelector(apiRef.current.state)[colIndex]; apiRef.current.setCellFocus(id, column.field); }, [apiRef, coordinates]); @@ -56,7 +56,7 @@ export default function ScrollPlayground() { }; const handleCellClick = (params) => { - const rowIndex = visibleSortedGridRowIdsSelector(apiRef.current.state).findIndex( + const rowIndex = gridVisibleSortedRowIdsSelector(apiRef.current.state).findIndex( (id) => id === params.id, ); From 83c1c36c83b65c7f5840c937e7ac3375587a2dbc Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 14:19:36 +0200 Subject: [PATCH 306/390] Fix --- packages/grid/_modules_/grid/models/gridRows.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index 2e15a13a891b2..53b93cb160922 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -37,6 +37,8 @@ export type GridRowsLookup = Record; */ export type GridRowId = string | number; +export type GridRowEntry = { id: GridRowId; model: GridRowModel }; + /** * The function to retrieve the id of a [[GridRowModel]]. */ From 1d2efde187479753eb277aa4cd84e98499faebac Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 14:57:29 +0200 Subject: [PATCH 307/390] Fix --- .../_modules_/grid/hooks/features/selection/useGridSelection.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts index 7970f9e8365c1..dea3aec9c8c98 100644 --- a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts +++ b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts @@ -16,7 +16,7 @@ import { selectedGridRowsSelector, selectedIdsLookupSelector, } from './gridSelectionSelector'; -import { gridPaginatedVisibleSortedGridRowIdsSelector } from '../pagination/gridPaginationSelector'; +import { gridPaginatedVisibleSortedGridRowIdsSelector } from '../pagination'; import { gridVisibleSortedRowIdsSelector } from '../filter/gridFilterSelector'; import { GridHeaderSelectionCheckboxParams } from '../../../models/params/gridHeaderSelectionCheckboxParams'; import { GridCellParams } from '../../../models/params/gridCellParams'; From 56e7806873e054877c8abbf13341b1fddbb461d1 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 14:58:02 +0200 Subject: [PATCH 308/390] Rework example --- .../CustomGroupingColumnTreeData.js | 68 +++---------------- 1 file changed, 11 insertions(+), 57 deletions(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js index d85eba31c570d..f9d1dec4a32d7 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js @@ -1,12 +1,6 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { - DataGridPro, - useGridApiContext, - useGridSelector, - gridVisibleDescendantCountLookupSelector, - GridEvents, -} from '@mui/x-data-grid-pro'; +import { DataGridPro, useGridApiContext, GridEvents } from '@mui/x-data-grid-pro'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; @@ -18,14 +12,8 @@ export const isNavigationKey = (key) => key === ' '; const CustomGridTreeDataGroupingCell = (props) => { - const { id, field, rowNode } = props; + const { id, field, value } = props; const apiRef = useGridApiContext(); - const descendantCountLookup = useGridSelector( - apiRef, - gridVisibleDescendantCountLookupSelector, - ); - - const descendantCount = descendantCountLookup[id]; const handleKeyDown = (event) => { if (event.key === ' ') { @@ -37,22 +25,22 @@ const CustomGridTreeDataGroupingCell = (props) => { }; const handleClick = (event) => { - apiRef.current.unstable_setRowExpansion(id, !rowNode.expanded); + apiRef.current.unstable_setRowExpansion(id, !value.expanded); apiRef.current.setCellFocus(id, field); event.stopPropagation(); }; return ( - +
- {descendantCount > 0 ? ( + {value.visibleDescendantCount > 0 ? ( ) : ( @@ -72,47 +60,13 @@ CustomGridTreeDataGroupingCell.propTypes = { */ id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, /** - * The node of the row that the current cell belongs to + * The cell value, but if the column has valueGetter, use getValue. */ - rowNode: PropTypes.shape({ - /** - * The id of the row children - */ - children: PropTypes.arrayOf( - PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - ), - /** - * 0-based depth of the row in the tree - */ + value: PropTypes.shape({ depth: PropTypes.number.isRequired, - /** - * Amount of descendants (children, children's children, ...) before the filtering - */ - descendantCount: PropTypes.number, - /** - * Current expansion status of the row - */ - expanded: PropTypes.bool, - /** - * The value used the group the children of the row - */ - groupingValue: PropTypes.string.isRequired, - /** - * The grid row id. - */ - id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, - /** - * If `true`, this node has been automatically added to fill a gap in the tree structure - */ - isAutoGenerated: PropTypes.bool, - /** - * The row id of the parent (null if this row is a top level row) - */ - parent: PropTypes.oneOfType([ - PropTypes.oneOf([null]), - PropTypes.number, - PropTypes.string, - ]).isRequired, + expanded: PropTypes.bool.isRequired, + label: PropTypes.string.isRequired, + visibleDescendantCount: PropTypes.number.isRequired, }).isRequired, }; From 2751de1a94758ee72ca402471544bdd7817f86ed Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 15:12:21 +0200 Subject: [PATCH 309/390] Work --- packages/grid/_modules_/grid/GridComponentProps.ts | 2 +- .../grid/hooks/features/sorting/useGridSorting.ts | 2 +- packages/grid/_modules_/grid/locales/frFR.ts | 2 ++ packages/grid/_modules_/grid/models/gridRows.ts | 2 +- packages/grid/_modules_/grid/utils/rowTreeUtils.ts | 9 ++++++--- packages/grid/data-grid/src/DataGrid.tsx | 2 +- .../grid/data-grid/src/tests/selection.DataGrid.test.tsx | 2 +- .../src/services/gridColDefGenerator.ts | 1 + packages/grid/x-grid/src/DataGridPro.tsx | 2 +- 9 files changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/grid/_modules_/grid/GridComponentProps.ts b/packages/grid/_modules_/grid/GridComponentProps.ts index f49642084cf54..4c5e96bd0ccb6 100644 --- a/packages/grid/_modules_/grid/GridComponentProps.ts +++ b/packages/grid/_modules_/grid/GridComponentProps.ts @@ -394,7 +394,7 @@ interface GridComponentOtherProps { onPageChange?: (page: number, details: GridCallbackDetails) => void; /** * Set the number of rows in one page. - * If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows. + * If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows wanted on each page. * @default 100 */ pageSize?: number; diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 7f2e6f1f0eb79..f4094b7d0178e 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -333,7 +333,7 @@ export const useGridSorting = ( } }, [apiRef, props.sortModel]); - // The filter options have changed + // The sorting options have changed const isFirstRender = React.useRef(true); React.useEffect(() => { if (isFirstRender.current) { diff --git a/packages/grid/_modules_/grid/locales/frFR.ts b/packages/grid/_modules_/grid/locales/frFR.ts index 39e53a8720457..c5b93f77025f2 100644 --- a/packages/grid/_modules_/grid/locales/frFR.ts +++ b/packages/grid/_modules_/grid/locales/frFR.ts @@ -99,6 +99,8 @@ const frFRGrid: Partial = { // Tree Data treeDataGroupingHeaderName: 'Groupe', + treeDataExpand: 'afficher les enfants', + treeDataCollapse: 'masquer les enfants', }; export const frFR: Localization = getGridLocalization(frFRGrid, frFRCore); diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index 7c39fca336eb0..15e3d7cc0db9e 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -48,7 +48,7 @@ export interface GridRowTreeNodeConfig { depth: number; /** - * The value used the group the children of the row + * The value used to group the children of this row */ groupingValue: string; diff --git a/packages/grid/_modules_/grid/utils/rowTreeUtils.ts b/packages/grid/_modules_/grid/utils/rowTreeUtils.ts index 890fd145626b5..b2394778e2457 100644 --- a/packages/grid/_modules_/grid/utils/rowTreeUtils.ts +++ b/packages/grid/_modules_/grid/utils/rowTreeUtils.ts @@ -17,10 +17,13 @@ interface TempRowTreeNode extends Omit { children?: Record; } -type TempRowTree = Record; - +/** + * Transform a list of rows into a tree structure where each row references its parent and children. + * Add the auto generated row to the `ids` and `idRowsLookup`. + */ export const buildRowTree = (params: GenerateRowTreeParams): GridRowGroupingResult => { - const tempTree: TempRowTree = {}; + // During the build, we store the children as a Record to avoid linear complexity when checking if a children is already defined. + const tempTree: Record = {}; let treeDepth = 1; const ids = [...params.ids]; const idRowsLookup = { ...params.idRowsLookup }; diff --git a/packages/grid/data-grid/src/DataGrid.tsx b/packages/grid/data-grid/src/DataGrid.tsx index 698ba493a7a31..fe91fe9926b3e 100644 --- a/packages/grid/data-grid/src/DataGrid.tsx +++ b/packages/grid/data-grid/src/DataGrid.tsx @@ -522,7 +522,7 @@ DataGridRaw.propTypes = { page: PropTypes.number, /** * Set the number of rows in one page. - * If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows. + * If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows wanted on each page. * @default 100 */ pageSize: chainPropTypes(PropTypes.number, (props: any) => { diff --git a/packages/grid/data-grid/src/tests/selection.DataGrid.test.tsx b/packages/grid/data-grid/src/tests/selection.DataGrid.test.tsx index e094349b036dc..880fee55b51ba 100644 --- a/packages/grid/data-grid/src/tests/selection.DataGrid.test.tsx +++ b/packages/grid/data-grid/src/tests/selection.DataGrid.test.tsx @@ -86,7 +86,7 @@ describe(' - Selection', () => { }); describe('prop: checkboxSelection = true (multi selection)', () => { - it('should allow to toggle checkboxSelection', () => { + it('should allow to toggle prop.checkboxSelection', () => { const { setProps } = render(); expect(getColumnHeadersTextContent()).to.deep.equal(['id', 'Currency Pair']); expect(getColumnHeaderCell(0).querySelectorAll('input')).to.have.length(0); diff --git a/packages/grid/x-grid-data-generator/src/services/gridColDefGenerator.ts b/packages/grid/x-grid-data-generator/src/services/gridColDefGenerator.ts index 249c374644c64..aa22d7fb21918 100644 --- a/packages/grid/x-grid-data-generator/src/services/gridColDefGenerator.ts +++ b/packages/grid/x-grid-data-generator/src/services/gridColDefGenerator.ts @@ -15,6 +15,7 @@ export interface GridColDefGenerator extends GridColDef { /** * If `true`, each row will have a distinct value + * If several rows are generated with the same value, then a suffix will be added to the 2nd, 3rd, ... * @default false */ dataGeneratorUniquenessEnabled?: boolean; diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index d25e0a1c179b7..b94c60b79d880 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -595,7 +595,7 @@ DataGridProRaw.propTypes = { page: PropTypes.number, /** * Set the number of rows in one page. - * If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows. + * If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows wanted on each page. * @default 100 */ pageSize: PropTypes.number, From c3a2889779b209b88874e73dfcbad43dc0e543f6 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 15:19:59 +0200 Subject: [PATCH 310/390] Fix --- .../features/infiniteLoader/useGridInfiniteLoader.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts b/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts index 8f1a7221558b7..56e8f040f54f6 100644 --- a/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts +++ b/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts @@ -11,7 +11,7 @@ import { GridRowScrollEndParams } from '../../../models/params/gridRowScrollEndP import { visibleGridColumnsSelector } from '../columns/gridColumnsSelector'; import { GridComponentProps } from '../../../GridComponentProps'; import { GridScrollParams } from '../../../models/params/gridScrollParams'; -import { visibleSortedGridRowsAsArraySelector } from '../filter/gridFilterSelector'; +import { gridVisibleSortedRowEntriesSelector } from '../filter/gridFilterSelector'; import { gridPaginationSelector } from '../pagination/gridPaginationSelector'; import { gridDensityRowHeightSelector } from '../density/densitySelector'; @@ -30,22 +30,22 @@ export const useGridInfiniteLoader = ( ): void => { const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); - const visibleSortedRowsAsArray = useGridSelector(apiRef, visibleSortedGridRowsAsArraySelector); + const visibleSortedRowEntries = useGridSelector(apiRef, gridVisibleSortedRowEntriesSelector); const paginationState = useGridSelector(apiRef, gridPaginationSelector); const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector); const rowsInCurrentPage = React.useMemo(() => { if (props.pagination && props.paginationMode === 'client') { const start = paginationState.pageSize * paginationState.page; - return visibleSortedRowsAsArray.slice(start, start + paginationState.pageSize); + return visibleSortedRowEntries.slice(start, start + paginationState.pageSize); } - return visibleSortedRowsAsArray; + return visibleSortedRowEntries; }, [ paginationState.page, paginationState.pageSize, props.pagination, props.paginationMode, - visibleSortedRowsAsArray, + visibleSortedRowEntries, ]); const contentHeight = Math.max(rowsInCurrentPage.length * rowHeight, 1); From 9f515ea71d4a29b7ab7abb2ad3638beba537259a Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 22 Oct 2021 15:21:19 +0200 Subject: [PATCH 311/390] Merge --- .../infiniteLoader/useGridInfiniteLoader.ts | 23 +++---------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts b/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts index 56e8f040f54f6..5c8759c50a6cd 100644 --- a/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts +++ b/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts @@ -11,9 +11,8 @@ import { GridRowScrollEndParams } from '../../../models/params/gridRowScrollEndP import { visibleGridColumnsSelector } from '../columns/gridColumnsSelector'; import { GridComponentProps } from '../../../GridComponentProps'; import { GridScrollParams } from '../../../models/params/gridScrollParams'; -import { gridVisibleSortedRowEntriesSelector } from '../filter/gridFilterSelector'; -import { gridPaginationSelector } from '../pagination/gridPaginationSelector'; import { gridDensityRowHeightSelector } from '../density/densitySelector'; +import {useRowsInCurrentPage} from "../../utils/useRowsInCurrentPage"; /** * Only available in DataGridPro @@ -30,25 +29,9 @@ export const useGridInfiniteLoader = ( ): void => { const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); - const visibleSortedRowEntries = useGridSelector(apiRef, gridVisibleSortedRowEntriesSelector); - const paginationState = useGridSelector(apiRef, gridPaginationSelector); + const rowsInCurrentPage = useRowsInCurrentPage(apiRef, props) const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector); - - const rowsInCurrentPage = React.useMemo(() => { - if (props.pagination && props.paginationMode === 'client') { - const start = paginationState.pageSize * paginationState.page; - return visibleSortedRowEntries.slice(start, start + paginationState.pageSize); - } - return visibleSortedRowEntries; - }, [ - paginationState.page, - paginationState.pageSize, - props.pagination, - props.paginationMode, - visibleSortedRowEntries, - ]); - - const contentHeight = Math.max(rowsInCurrentPage.length * rowHeight, 1); + const contentHeight = Math.max(rowsInCurrentPage.rows.length * rowHeight, 1); const isInScrollBottomArea = React.useRef(false); From 1aeb582cbc4679c51fbf8425d5eea8e50c6c66cc Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 25 Oct 2021 09:42:30 +0200 Subject: [PATCH 312/390] Fix --- docs/scripts/buildApi.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/scripts/buildApi.ts b/docs/scripts/buildApi.ts index 7e673a0950bb5..ad815ae85f029 100644 --- a/docs/scripts/buildApi.ts +++ b/docs/scripts/buildApi.ts @@ -735,11 +735,6 @@ async function run(argv: { outputDirectory?: string }) { // } // })!; - const exports = (project.children ?? []).map((child) => ({ - name: child.name, - kind: child?.kindString, - })); - writePrettifiedFile( path.resolve(workspaceRoot, 'scripts/exportsSnapshot.json'), JSON.stringify(exports), @@ -801,7 +796,12 @@ async function run(argv: { outputDirectory?: string }) { exclude: ['**/*.test.ts'], tsconfig: 'packages/grid/data-grid/tsconfig.json', }); - const project = app.convert(); + const project = app.convert()!; + + const exports = (project.children ?? []).map((child) => ({ + name: child.name, + kind: child?.kindString, + })); apisToGenerate.forEach((apiName) => { const reflection = project.findReflectionByName(apiName) as TypeDoc.DeclarationReflection; From 79edd85ebeaba319d0e5b069674e7cfc96bb33ad Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 25 Oct 2021 09:45:47 +0200 Subject: [PATCH 313/390] Fix --- .../hooks/features/infiniteLoader/useGridInfiniteLoader.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts b/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts index 5c8759c50a6cd..3f383d485451d 100644 --- a/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts +++ b/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts @@ -12,7 +12,7 @@ import { visibleGridColumnsSelector } from '../columns/gridColumnsSelector'; import { GridComponentProps } from '../../../GridComponentProps'; import { GridScrollParams } from '../../../models/params/gridScrollParams'; import { gridDensityRowHeightSelector } from '../density/densitySelector'; -import {useRowsInCurrentPage} from "../../utils/useRowsInCurrentPage"; +import { useRowsInCurrentPage } from '../../utils/useRowsInCurrentPage'; /** * Only available in DataGridPro @@ -29,7 +29,7 @@ export const useGridInfiniteLoader = ( ): void => { const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); - const rowsInCurrentPage = useRowsInCurrentPage(apiRef, props) + const rowsInCurrentPage = useRowsInCurrentPage(apiRef, props); const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector); const contentHeight = Math.max(rowsInCurrentPage.rows.length * rowHeight, 1); From 2140cea415ef2dffe58f26e20ed937b535a83c16 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 25 Oct 2021 09:58:18 +0200 Subject: [PATCH 314/390] Work --- packages/grid/_modules_/grid/components/GridVirtualScroller.tsx | 1 - .../grid/hooks/features/pagination/gridPaginationSelector.ts | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx b/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx index cb2b2cb710d44..adf59f033ae08 100644 --- a/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx +++ b/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx @@ -15,7 +15,6 @@ import { gridFocusCellSelector, gridTabIndexCellSelector, } from '../hooks/features/focus/gridFocusStateSelector'; -import { gridVisibleSortedRowEntriesSelector } from '../hooks/features/filter/gridFilterSelector'; import { gridDensityRowHeightSelector } from '../hooks/features/density/densitySelector'; import { gridEditRowsStateSelector } from '../hooks/features/editRows/gridEditRowsSelector'; import { GridEvents } from '../constants/eventsConstants'; diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts index 76a91c00c7811..ddaafc846bc16 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts @@ -37,10 +37,12 @@ export const gridPaginationRowRangeSelector = createSelector( visibleTopLevelRowCount - 1, ); + // The range contains no element if (topLevelFirstRowIndex === -1 || topLevelLastRowIndex === -1) { return null; } + // The tree is flat, their is no need to look for children if (rowTreeDepth < 2) { return { firstRowIndex: topLevelFirstRowIndex, lastRowIndex: topLevelLastRowIndex }; } From ba139a2435cc6fbf4093af1bcf3842615f5365cf Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 25 Oct 2021 10:16:08 +0200 Subject: [PATCH 315/390] docs --- docs/pages/api-docs/data-grid/data-grid-pro.json | 14 +++++++++++--- docs/pages/api-docs/data-grid/data-grid.json | 9 ++++++++- docs/pages/api-docs/data-grid/grid-col-def.md | 4 ++-- .../api-docs/data-grid/data-grid-pro-pt.json | 16 ++++++++++++---- .../api-docs/data-grid/data-grid-pro-zh.json | 16 ++++++++++++---- .../api-docs/data-grid/data-grid-pro.json | 16 ++++++++++++---- .../api-docs/data-grid/data-grid-pt.json | 13 ++++++++++--- .../api-docs/data-grid/data-grid-zh.json | 13 ++++++++++--- .../api-docs/data-grid/data-grid.json | 13 ++++++++++--- 9 files changed, 87 insertions(+), 27 deletions(-) diff --git a/docs/pages/api-docs/data-grid/data-grid-pro.json b/docs/pages/api-docs/data-grid/data-grid-pro.json index ec3cd814fe163..add546a733151 100644 --- a/docs/pages/api-docs/data-grid/data-grid-pro.json +++ b/docs/pages/api-docs/data-grid/data-grid-pro.json @@ -24,6 +24,7 @@ "columnTypes": { "type": { "name": "object" } }, "components": { "type": { "name": "object" } }, "componentsProps": { "type": { "name": "object" } }, + "defaultGroupingExpansionDepth": { "type": { "name": "number" }, "default": "0" }, "density": { "type": { "name": "enum", @@ -31,6 +32,8 @@ }, "default": "\"standard\"" }, + "disableChildrenFiltering": { "type": { "name": "bool" } }, + "disableChildrenSorting": { "type": { "name": "bool" } }, "disableColumnFilter": { "type": { "name": "bool" } }, "disableColumnMenu": { "type": { "name": "bool" } }, "disableColumnReorder": { "type": { "name": "bool" } }, @@ -50,7 +53,7 @@ "editRowsModel": { "type": { "name": "object" } }, "error": { "type": { "name": "any" } }, "filterMode": { - "type": { "name": "enum", "description": "'client'
| 'server'" }, + "type": { "name": "custom", "description": "'client'
| 'server'" }, "default": "\"client\"" }, "filterModel": { @@ -62,6 +65,8 @@ "getCellClassName": { "type": { "name": "func" } }, "getRowClassName": { "type": { "name": "func" } }, "getRowId": { "type": { "name": "func" } }, + "getTreeDataPath": { "type": { "name": "func" } }, + "groupingColDef": { "type": { "name": "union", "description": "func
| object" } }, "headerHeight": { "type": { "name": "number" }, "default": "56" }, "hideFooter": { "type": { "name": "bool" } }, "hideFooterPagination": { "type": { "name": "bool" } }, @@ -163,7 +168,8 @@ "description": "Array<{ field: string, sort?: 'asc'
| 'desc' }>" } }, - "throttleRowsMs": { "type": { "name": "number" }, "default": "0" } + "throttleRowsMs": { "type": { "name": "number" }, "default": "0" }, + "treeData": { "type": { "name": "bool" } } }, "slots": { "BooleanCellFalseIcon": { "default": "GridCloseIcon", "type": { "name": "elementType" } }, @@ -211,7 +217,9 @@ "Panel": { "default": "GridPanel", "type": { "name": "elementType" } }, "PreferencesPanel": { "default": "GridPreferencesPanel", "type": { "name": "elementType" } }, "Row": { "type": { "name": "elementType" } }, - "Toolbar": { "default": "null", "type": { "name": "elementType | null" } } + "Toolbar": { "default": "null", "type": { "name": "elementType | null" } }, + "TreeDataCollapseIcon": { "type": { "name": "elementType" } }, + "TreeDataExpandIcon": { "type": { "name": "elementType" } } }, "name": "DataGridPro", "styles": { diff --git a/docs/pages/api-docs/data-grid/data-grid.json b/docs/pages/api-docs/data-grid/data-grid.json index 86838a5e84245..d3abdc6c45fef 100644 --- a/docs/pages/api-docs/data-grid/data-grid.json +++ b/docs/pages/api-docs/data-grid/data-grid.json @@ -16,6 +16,7 @@ "columnTypes": { "type": { "name": "object" } }, "components": { "type": { "name": "object" } }, "componentsProps": { "type": { "name": "object" } }, + "defaultGroupingExpansionDepth": { "type": { "name": "number" }, "default": "0" }, "density": { "type": { "name": "enum", @@ -23,6 +24,8 @@ }, "default": "\"standard\"" }, + "disableChildrenFiltering": { "type": { "name": "bool" } }, + "disableChildrenSorting": { "type": { "name": "bool" } }, "disableColumnFilter": { "type": { "name": "bool" } }, "disableColumnMenu": { "type": { "name": "bool" } }, "disableColumnSelector": { "type": { "name": "bool" } }, @@ -44,6 +47,8 @@ "getCellClassName": { "type": { "name": "func" } }, "getRowClassName": { "type": { "name": "func" } }, "getRowId": { "type": { "name": "func" } }, + "getTreeDataPath": { "type": { "name": "func" } }, + "groupingColDef": { "type": { "name": "union", "description": "func
| object" } }, "headerHeight": { "type": { "name": "number" }, "default": "56" }, "hideFooter": { "type": { "name": "bool" } }, "hideFooterPagination": { "type": { "name": "bool" } }, @@ -185,7 +190,9 @@ "Panel": { "default": "GridPanel", "type": { "name": "elementType" } }, "PreferencesPanel": { "default": "GridPreferencesPanel", "type": { "name": "elementType" } }, "Row": { "type": { "name": "elementType" } }, - "Toolbar": { "default": "null", "type": { "name": "elementType | null" } } + "Toolbar": { "default": "null", "type": { "name": "elementType | null" } }, + "TreeDataCollapseIcon": { "type": { "name": "elementType" } }, + "TreeDataExpandIcon": { "type": { "name": "elementType" } } }, "name": "DataGrid", "styles": { diff --git a/docs/pages/api-docs/data-grid/grid-col-def.md b/docs/pages/api-docs/data-grid/grid-col-def.md index ee6902670447b..702ad4a8fbdd4 100644 --- a/docs/pages/api-docs/data-grid/grid-col-def.md +++ b/docs/pages/api-docs/data-grid/grid-col-def.md @@ -35,7 +35,7 @@ import { GridColDef } from '@mui/x-data-grid'; | renderEditCell? | (params: GridRenderEditCellParams) => ReactNode | | Allows to override the component rendered in edit cell mode for this column. | | renderHeader? | (params: GridColumnHeaderParams) => ReactNode | | Allows to render a component in the column header cell. | | resizable? | boolean | true
| If `true`, the column is resizable. | -| shouldRenderAutoGeneratedRows? | boolean | | If `true`, the `renderCell` will be called for the auto generated rows | +| shouldRenderAutoGeneratedRows? | boolean | | If `true`, the `renderCell` will be called for the auto generated rows | | sortable? | boolean | true
| If `true`, the column is sortable. | | sortComparator? | GridComparatorFn | | A comparator function used to sort rows. | | type? | string | 'string'
| Type allows to merge this object with a default definition [GridColDef](/api/data-grid/grid-col-def/). | @@ -43,5 +43,5 @@ import { GridColDef } from '@mui/x-data-grid'; | valueGetter? | (params: GridValueGetterParams) => GridCellValue | | Function that allows to get a specific data instead of field to render in the cell. | | valueOptions? | (string \| number \| { label: string; value: any })[] | | To be used in combination with `type: 'singleSelect'`. This is an array of the possible cell values and labels. | | valueParser? | (value: GridCellValue, params?: GridCellParams<any, any, any>) => GridCellValue | | Function that takes the user-entered value and converts it to a value used internally. | -| valueSetter? | (params: GridValueSetterParams) => { [key: string]: any } | | Function that allows to customize how the entered value is stored in the row.
It only works with cell/row editing. | +| valueSetter? | (params: GridValueSetterParams) => { [key: string]: any } | | Function that allows to customize how the entered value is stored in the row.
It only works with cell/row editing. | | width? | number | 100
| Set the width of the column. | diff --git a/docs/translations/api-docs/data-grid/data-grid-pro-pt.json b/docs/translations/api-docs/data-grid/data-grid-pro-pt.json index 164aeb8950544..43ed2a29895be 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro-pt.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro-pt.json @@ -15,7 +15,10 @@ "columnTypes": "Extend native column types with your new column types.", "components": "Overrideable components.", "componentsProps": "Overrideable components props dynamically passed to the component at rendering.", + "defaultGroupingExpansionDepth": "If above 0, the row children will be expanded up to this depth If equal to -1, all the row children will be expanded", "density": "Set the density of the grid.", + "disableChildrenFiltering": "If true, the filtering will only be applied to the top level rows", + "disableChildrenSorting": "If true, the sorting will only be applied to the top level rows", "disableColumnFilter": "If true, column filters are disabled.", "disableColumnMenu": "If true, the column menu is disabled.", "disableColumnReorder": "If true, reordering columns is disabled.", @@ -36,6 +39,8 @@ "getCellClassName": "Function that applies CSS classes dynamically on cells.

Signature:
function(params: GridCellParams) => string
params: With all properties from GridCellParams.
returns (string): The CSS class to apply to the cell.", "getRowClassName": "Function that applies CSS classes dynamically on rows.

Signature:
function(params: GridRowParams) => string
params: With all properties from GridRowParams.
returns (string): The CSS class to apply to the row.", "getRowId": "Return the id of a given GridRowModel.", + "getTreeDataPath": "Determines the path of a row in the tree data

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row", + "groupingColDef": "The grouping column used by the tree data", "headerHeight": "Set the height in pixel of the column headers in the grid.", "hideFooter": "If true, the footer component is hidden.", "hideFooterPagination": "If true, the pagination component in the footer is hidden.", @@ -84,11 +89,11 @@ "onSortModelChange": "Callback fired when the sort model changes before a column is sorted.

Signature:
function(model: GridSortModel, details: GridCallbackDetails) => void
model: With all properties from GridSortModel.
details: Additional details for this callback.", "onStateChange": "Callback fired when the state of the grid is updated.

Signature:
function(state: GridState, event: MuiEvent, details: GridCallbackDetails) => void
state: The new state.
event: The event object.
details: Additional details for this callback.", "page": "The zero-based index of the current page.", - "pageSize": "Set the number of rows in one page.", + "pageSize": "Set the number of rows in one page. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows wanted on each page.", "pagination": "If true, pagination is enabled.", "paginationMode": "Pagination can be processed on the server or client-side. Set it to 'client' if you would like to handle the pagination on the client-side. Set it to 'server' if you would like to handle the pagination on the server-side.", "rowBuffer": "Number of extra rows to be rendered before/after the visible slice.", - "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop.", + "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows.", "rowHeight": "Set the height in pixel of a row in the grid.", "rows": "Set of rows of type GridRowsProp.", "rowsPerPageOptions": "Select the pageSize dynamically using the component UI.", @@ -101,7 +106,8 @@ "sortingMode": "Sorting can be processed on the server or client-side. Set it to 'client' if you would like to handle sorting on the client-side. Set it to 'server' if you would like to handle sorting on the server-side.", "sortingOrder": "The order of the sorting sequence.", "sortModel": "Set the sort model of the grid.", - "throttleRowsMs": "If positive, the Grid will throttle updates coming from apiRef.current.updateRows and apiRef.current.setRows. It can be useful if you have a high update rate but do not want to do heavy work like filtering / sorting or rendering on each individual update." + "throttleRowsMs": "If positive, the Grid will throttle updates coming from apiRef.current.updateRows and apiRef.current.setRows. It can be useful if you have a high update rate but do not want to do heavy work like filtering / sorting or rendering on each individual update.", + "treeData": "If true, the rows will be gathered in a tree structure, following the getTreeDataPath prop" }, "classDescriptions": { "autoHeight": { @@ -417,6 +423,8 @@ "DensityStandardIcon": "Icon displayed on the standard density option in the toolbar.", "DensityComfortableIcon": "Icon displayed on the "comfortable" density option in the toolbar.", "ExportIcon": "Icon displayed on the open export button present in the toolbar by default.", - "MoreActionsIcon": "Icon displayed on the actions column type to open the menu." + "MoreActionsIcon": "Icon displayed on the actions column type to open the menu.", + "TreeDataExpandIcon": "Icon displayed on the tree data toggling column when the children are collapsed", + "TreeDataCollapseIcon": "Icon displayed on the tree data toggling column when the children are expanded" } } diff --git a/docs/translations/api-docs/data-grid/data-grid-pro-zh.json b/docs/translations/api-docs/data-grid/data-grid-pro-zh.json index 164aeb8950544..43ed2a29895be 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro-zh.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro-zh.json @@ -15,7 +15,10 @@ "columnTypes": "Extend native column types with your new column types.", "components": "Overrideable components.", "componentsProps": "Overrideable components props dynamically passed to the component at rendering.", + "defaultGroupingExpansionDepth": "If above 0, the row children will be expanded up to this depth If equal to -1, all the row children will be expanded", "density": "Set the density of the grid.", + "disableChildrenFiltering": "If true, the filtering will only be applied to the top level rows", + "disableChildrenSorting": "If true, the sorting will only be applied to the top level rows", "disableColumnFilter": "If true, column filters are disabled.", "disableColumnMenu": "If true, the column menu is disabled.", "disableColumnReorder": "If true, reordering columns is disabled.", @@ -36,6 +39,8 @@ "getCellClassName": "Function that applies CSS classes dynamically on cells.

Signature:
function(params: GridCellParams) => string
params: With all properties from GridCellParams.
returns (string): The CSS class to apply to the cell.", "getRowClassName": "Function that applies CSS classes dynamically on rows.

Signature:
function(params: GridRowParams) => string
params: With all properties from GridRowParams.
returns (string): The CSS class to apply to the row.", "getRowId": "Return the id of a given GridRowModel.", + "getTreeDataPath": "Determines the path of a row in the tree data

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row", + "groupingColDef": "The grouping column used by the tree data", "headerHeight": "Set the height in pixel of the column headers in the grid.", "hideFooter": "If true, the footer component is hidden.", "hideFooterPagination": "If true, the pagination component in the footer is hidden.", @@ -84,11 +89,11 @@ "onSortModelChange": "Callback fired when the sort model changes before a column is sorted.

Signature:
function(model: GridSortModel, details: GridCallbackDetails) => void
model: With all properties from GridSortModel.
details: Additional details for this callback.", "onStateChange": "Callback fired when the state of the grid is updated.

Signature:
function(state: GridState, event: MuiEvent, details: GridCallbackDetails) => void
state: The new state.
event: The event object.
details: Additional details for this callback.", "page": "The zero-based index of the current page.", - "pageSize": "Set the number of rows in one page.", + "pageSize": "Set the number of rows in one page. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows wanted on each page.", "pagination": "If true, pagination is enabled.", "paginationMode": "Pagination can be processed on the server or client-side. Set it to 'client' if you would like to handle the pagination on the client-side. Set it to 'server' if you would like to handle the pagination on the server-side.", "rowBuffer": "Number of extra rows to be rendered before/after the visible slice.", - "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop.", + "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows.", "rowHeight": "Set the height in pixel of a row in the grid.", "rows": "Set of rows of type GridRowsProp.", "rowsPerPageOptions": "Select the pageSize dynamically using the component UI.", @@ -101,7 +106,8 @@ "sortingMode": "Sorting can be processed on the server or client-side. Set it to 'client' if you would like to handle sorting on the client-side. Set it to 'server' if you would like to handle sorting on the server-side.", "sortingOrder": "The order of the sorting sequence.", "sortModel": "Set the sort model of the grid.", - "throttleRowsMs": "If positive, the Grid will throttle updates coming from apiRef.current.updateRows and apiRef.current.setRows. It can be useful if you have a high update rate but do not want to do heavy work like filtering / sorting or rendering on each individual update." + "throttleRowsMs": "If positive, the Grid will throttle updates coming from apiRef.current.updateRows and apiRef.current.setRows. It can be useful if you have a high update rate but do not want to do heavy work like filtering / sorting or rendering on each individual update.", + "treeData": "If true, the rows will be gathered in a tree structure, following the getTreeDataPath prop" }, "classDescriptions": { "autoHeight": { @@ -417,6 +423,8 @@ "DensityStandardIcon": "Icon displayed on the standard density option in the toolbar.", "DensityComfortableIcon": "Icon displayed on the "comfortable" density option in the toolbar.", "ExportIcon": "Icon displayed on the open export button present in the toolbar by default.", - "MoreActionsIcon": "Icon displayed on the actions column type to open the menu." + "MoreActionsIcon": "Icon displayed on the actions column type to open the menu.", + "TreeDataExpandIcon": "Icon displayed on the tree data toggling column when the children are collapsed", + "TreeDataCollapseIcon": "Icon displayed on the tree data toggling column when the children are expanded" } } diff --git a/docs/translations/api-docs/data-grid/data-grid-pro.json b/docs/translations/api-docs/data-grid/data-grid-pro.json index 164aeb8950544..43ed2a29895be 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro.json @@ -15,7 +15,10 @@ "columnTypes": "Extend native column types with your new column types.", "components": "Overrideable components.", "componentsProps": "Overrideable components props dynamically passed to the component at rendering.", + "defaultGroupingExpansionDepth": "If above 0, the row children will be expanded up to this depth If equal to -1, all the row children will be expanded", "density": "Set the density of the grid.", + "disableChildrenFiltering": "If true, the filtering will only be applied to the top level rows", + "disableChildrenSorting": "If true, the sorting will only be applied to the top level rows", "disableColumnFilter": "If true, column filters are disabled.", "disableColumnMenu": "If true, the column menu is disabled.", "disableColumnReorder": "If true, reordering columns is disabled.", @@ -36,6 +39,8 @@ "getCellClassName": "Function that applies CSS classes dynamically on cells.

Signature:
function(params: GridCellParams) => string
params: With all properties from GridCellParams.
returns (string): The CSS class to apply to the cell.", "getRowClassName": "Function that applies CSS classes dynamically on rows.

Signature:
function(params: GridRowParams) => string
params: With all properties from GridRowParams.
returns (string): The CSS class to apply to the row.", "getRowId": "Return the id of a given GridRowModel.", + "getTreeDataPath": "Determines the path of a row in the tree data

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row", + "groupingColDef": "The grouping column used by the tree data", "headerHeight": "Set the height in pixel of the column headers in the grid.", "hideFooter": "If true, the footer component is hidden.", "hideFooterPagination": "If true, the pagination component in the footer is hidden.", @@ -84,11 +89,11 @@ "onSortModelChange": "Callback fired when the sort model changes before a column is sorted.

Signature:
function(model: GridSortModel, details: GridCallbackDetails) => void
model: With all properties from GridSortModel.
details: Additional details for this callback.", "onStateChange": "Callback fired when the state of the grid is updated.

Signature:
function(state: GridState, event: MuiEvent, details: GridCallbackDetails) => void
state: The new state.
event: The event object.
details: Additional details for this callback.", "page": "The zero-based index of the current page.", - "pageSize": "Set the number of rows in one page.", + "pageSize": "Set the number of rows in one page. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows wanted on each page.", "pagination": "If true, pagination is enabled.", "paginationMode": "Pagination can be processed on the server or client-side. Set it to 'client' if you would like to handle the pagination on the client-side. Set it to 'server' if you would like to handle the pagination on the server-side.", "rowBuffer": "Number of extra rows to be rendered before/after the visible slice.", - "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop.", + "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows.", "rowHeight": "Set the height in pixel of a row in the grid.", "rows": "Set of rows of type GridRowsProp.", "rowsPerPageOptions": "Select the pageSize dynamically using the component UI.", @@ -101,7 +106,8 @@ "sortingMode": "Sorting can be processed on the server or client-side. Set it to 'client' if you would like to handle sorting on the client-side. Set it to 'server' if you would like to handle sorting on the server-side.", "sortingOrder": "The order of the sorting sequence.", "sortModel": "Set the sort model of the grid.", - "throttleRowsMs": "If positive, the Grid will throttle updates coming from apiRef.current.updateRows and apiRef.current.setRows. It can be useful if you have a high update rate but do not want to do heavy work like filtering / sorting or rendering on each individual update." + "throttleRowsMs": "If positive, the Grid will throttle updates coming from apiRef.current.updateRows and apiRef.current.setRows. It can be useful if you have a high update rate but do not want to do heavy work like filtering / sorting or rendering on each individual update.", + "treeData": "If true, the rows will be gathered in a tree structure, following the getTreeDataPath prop" }, "classDescriptions": { "autoHeight": { @@ -417,6 +423,8 @@ "DensityStandardIcon": "Icon displayed on the standard density option in the toolbar.", "DensityComfortableIcon": "Icon displayed on the "comfortable" density option in the toolbar.", "ExportIcon": "Icon displayed on the open export button present in the toolbar by default.", - "MoreActionsIcon": "Icon displayed on the actions column type to open the menu." + "MoreActionsIcon": "Icon displayed on the actions column type to open the menu.", + "TreeDataExpandIcon": "Icon displayed on the tree data toggling column when the children are collapsed", + "TreeDataCollapseIcon": "Icon displayed on the tree data toggling column when the children are expanded" } } diff --git a/docs/translations/api-docs/data-grid/data-grid-pt.json b/docs/translations/api-docs/data-grid/data-grid-pt.json index 132e73b1d116e..8453fa991d383 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pt.json +++ b/docs/translations/api-docs/data-grid/data-grid-pt.json @@ -13,7 +13,10 @@ "columnTypes": "Extend native column types with your new column types.", "components": "Overrideable components.", "componentsProps": "Overrideable components props dynamically passed to the component at rendering.", + "defaultGroupingExpansionDepth": "If above 0, the row children will be expanded up to this depth If equal to -1, all the row children will be expanded", "density": "Set the density of the grid.", + "disableChildrenFiltering": "If true, the filtering will only be applied to the top level rows", + "disableChildrenSorting": "If true, the sorting will only be applied to the top level rows", "disableColumnFilter": "If true, column filters are disabled.", "disableColumnMenu": "If true, the column menu is disabled.", "disableColumnSelector": "If true, hiding/showing columns is disabled.", @@ -29,6 +32,8 @@ "getCellClassName": "Function that applies CSS classes dynamically on cells.

Signature:
function(params: GridCellParams) => string
params: With all properties from GridCellParams.
returns (string): The CSS class to apply to the cell.", "getRowClassName": "Function that applies CSS classes dynamically on rows.

Signature:
function(params: GridRowParams) => string
params: With all properties from GridRowParams.
returns (string): The CSS class to apply to the row.", "getRowId": "Return the id of a given GridRowModel.", + "getTreeDataPath": "Determines the path of a row in the tree data

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row", + "groupingColDef": "The grouping column used by the tree data", "headerHeight": "Set the height in pixel of the column headers in the grid.", "hideFooter": "If true, the footer component is hidden.", "hideFooterPagination": "If true, the pagination component in the footer is hidden.", @@ -75,10 +80,10 @@ "onSortModelChange": "Callback fired when the sort model changes before a column is sorted.

Signature:
function(model: GridSortModel, details: GridCallbackDetails) => void
model: With all properties from GridSortModel.
details: Additional details for this callback.", "onStateChange": "Callback fired when the state of the grid is updated.

Signature:
function(state: GridState, event: MuiEvent, details: GridCallbackDetails) => void
state: The new state.
event: The event object.
details: Additional details for this callback.", "page": "The zero-based index of the current page.", - "pageSize": "Set the number of rows in one page.", + "pageSize": "Set the number of rows in one page. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows wanted on each page.", "paginationMode": "Pagination can be processed on the server or client-side. Set it to 'client' if you would like to handle the pagination on the client-side. Set it to 'server' if you would like to handle the pagination on the server-side.", "rowBuffer": "Number of extra rows to be rendered before/after the visible slice.", - "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop.", + "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows.", "rowHeight": "Set the height in pixel of a row in the grid.", "rows": "Set of rows of type GridRowsProp.", "rowsPerPageOptions": "Select the pageSize dynamically using the component UI.", @@ -405,6 +410,8 @@ "DensityStandardIcon": "Icon displayed on the standard density option in the toolbar.", "DensityComfortableIcon": "Icon displayed on the "comfortable" density option in the toolbar.", "ExportIcon": "Icon displayed on the open export button present in the toolbar by default.", - "MoreActionsIcon": "Icon displayed on the actions column type to open the menu." + "MoreActionsIcon": "Icon displayed on the actions column type to open the menu.", + "TreeDataExpandIcon": "Icon displayed on the tree data toggling column when the children are collapsed", + "TreeDataCollapseIcon": "Icon displayed on the tree data toggling column when the children are expanded" } } diff --git a/docs/translations/api-docs/data-grid/data-grid-zh.json b/docs/translations/api-docs/data-grid/data-grid-zh.json index 132e73b1d116e..8453fa991d383 100644 --- a/docs/translations/api-docs/data-grid/data-grid-zh.json +++ b/docs/translations/api-docs/data-grid/data-grid-zh.json @@ -13,7 +13,10 @@ "columnTypes": "Extend native column types with your new column types.", "components": "Overrideable components.", "componentsProps": "Overrideable components props dynamically passed to the component at rendering.", + "defaultGroupingExpansionDepth": "If above 0, the row children will be expanded up to this depth If equal to -1, all the row children will be expanded", "density": "Set the density of the grid.", + "disableChildrenFiltering": "If true, the filtering will only be applied to the top level rows", + "disableChildrenSorting": "If true, the sorting will only be applied to the top level rows", "disableColumnFilter": "If true, column filters are disabled.", "disableColumnMenu": "If true, the column menu is disabled.", "disableColumnSelector": "If true, hiding/showing columns is disabled.", @@ -29,6 +32,8 @@ "getCellClassName": "Function that applies CSS classes dynamically on cells.

Signature:
function(params: GridCellParams) => string
params: With all properties from GridCellParams.
returns (string): The CSS class to apply to the cell.", "getRowClassName": "Function that applies CSS classes dynamically on rows.

Signature:
function(params: GridRowParams) => string
params: With all properties from GridRowParams.
returns (string): The CSS class to apply to the row.", "getRowId": "Return the id of a given GridRowModel.", + "getTreeDataPath": "Determines the path of a row in the tree data

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row", + "groupingColDef": "The grouping column used by the tree data", "headerHeight": "Set the height in pixel of the column headers in the grid.", "hideFooter": "If true, the footer component is hidden.", "hideFooterPagination": "If true, the pagination component in the footer is hidden.", @@ -75,10 +80,10 @@ "onSortModelChange": "Callback fired when the sort model changes before a column is sorted.

Signature:
function(model: GridSortModel, details: GridCallbackDetails) => void
model: With all properties from GridSortModel.
details: Additional details for this callback.", "onStateChange": "Callback fired when the state of the grid is updated.

Signature:
function(state: GridState, event: MuiEvent, details: GridCallbackDetails) => void
state: The new state.
event: The event object.
details: Additional details for this callback.", "page": "The zero-based index of the current page.", - "pageSize": "Set the number of rows in one page.", + "pageSize": "Set the number of rows in one page. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows wanted on each page.", "paginationMode": "Pagination can be processed on the server or client-side. Set it to 'client' if you would like to handle the pagination on the client-side. Set it to 'server' if you would like to handle the pagination on the server-side.", "rowBuffer": "Number of extra rows to be rendered before/after the visible slice.", - "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop.", + "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows.", "rowHeight": "Set the height in pixel of a row in the grid.", "rows": "Set of rows of type GridRowsProp.", "rowsPerPageOptions": "Select the pageSize dynamically using the component UI.", @@ -405,6 +410,8 @@ "DensityStandardIcon": "Icon displayed on the standard density option in the toolbar.", "DensityComfortableIcon": "Icon displayed on the "comfortable" density option in the toolbar.", "ExportIcon": "Icon displayed on the open export button present in the toolbar by default.", - "MoreActionsIcon": "Icon displayed on the actions column type to open the menu." + "MoreActionsIcon": "Icon displayed on the actions column type to open the menu.", + "TreeDataExpandIcon": "Icon displayed on the tree data toggling column when the children are collapsed", + "TreeDataCollapseIcon": "Icon displayed on the tree data toggling column when the children are expanded" } } diff --git a/docs/translations/api-docs/data-grid/data-grid.json b/docs/translations/api-docs/data-grid/data-grid.json index 132e73b1d116e..8453fa991d383 100644 --- a/docs/translations/api-docs/data-grid/data-grid.json +++ b/docs/translations/api-docs/data-grid/data-grid.json @@ -13,7 +13,10 @@ "columnTypes": "Extend native column types with your new column types.", "components": "Overrideable components.", "componentsProps": "Overrideable components props dynamically passed to the component at rendering.", + "defaultGroupingExpansionDepth": "If above 0, the row children will be expanded up to this depth If equal to -1, all the row children will be expanded", "density": "Set the density of the grid.", + "disableChildrenFiltering": "If true, the filtering will only be applied to the top level rows", + "disableChildrenSorting": "If true, the sorting will only be applied to the top level rows", "disableColumnFilter": "If true, column filters are disabled.", "disableColumnMenu": "If true, the column menu is disabled.", "disableColumnSelector": "If true, hiding/showing columns is disabled.", @@ -29,6 +32,8 @@ "getCellClassName": "Function that applies CSS classes dynamically on cells.

Signature:
function(params: GridCellParams) => string
params: With all properties from GridCellParams.
returns (string): The CSS class to apply to the cell.", "getRowClassName": "Function that applies CSS classes dynamically on rows.

Signature:
function(params: GridRowParams) => string
params: With all properties from GridRowParams.
returns (string): The CSS class to apply to the row.", "getRowId": "Return the id of a given GridRowModel.", + "getTreeDataPath": "Determines the path of a row in the tree data

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row", + "groupingColDef": "The grouping column used by the tree data", "headerHeight": "Set the height in pixel of the column headers in the grid.", "hideFooter": "If true, the footer component is hidden.", "hideFooterPagination": "If true, the pagination component in the footer is hidden.", @@ -75,10 +80,10 @@ "onSortModelChange": "Callback fired when the sort model changes before a column is sorted.

Signature:
function(model: GridSortModel, details: GridCallbackDetails) => void
model: With all properties from GridSortModel.
details: Additional details for this callback.", "onStateChange": "Callback fired when the state of the grid is updated.

Signature:
function(state: GridState, event: MuiEvent, details: GridCallbackDetails) => void
state: The new state.
event: The event object.
details: Additional details for this callback.", "page": "The zero-based index of the current page.", - "pageSize": "Set the number of rows in one page.", + "pageSize": "Set the number of rows in one page. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows wanted on each page.", "paginationMode": "Pagination can be processed on the server or client-side. Set it to 'client' if you would like to handle the pagination on the client-side. Set it to 'server' if you would like to handle the pagination on the server-side.", "rowBuffer": "Number of extra rows to be rendered before/after the visible slice.", - "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop.", + "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows.", "rowHeight": "Set the height in pixel of a row in the grid.", "rows": "Set of rows of type GridRowsProp.", "rowsPerPageOptions": "Select the pageSize dynamically using the component UI.", @@ -405,6 +410,8 @@ "DensityStandardIcon": "Icon displayed on the standard density option in the toolbar.", "DensityComfortableIcon": "Icon displayed on the "comfortable" density option in the toolbar.", "ExportIcon": "Icon displayed on the open export button present in the toolbar by default.", - "MoreActionsIcon": "Icon displayed on the actions column type to open the menu." + "MoreActionsIcon": "Icon displayed on the actions column type to open the menu.", + "TreeDataExpandIcon": "Icon displayed on the tree data toggling column when the children are collapsed", + "TreeDataCollapseIcon": "Icon displayed on the tree data toggling column when the children are expanded" } } From 4c5b049588cd2f79ad1af0c3d8c19f7fc295b503 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 25 Oct 2021 10:25:41 +0200 Subject: [PATCH 316/390] [core] Adapt the data generator for Tree Data --- .../pagination/CursorPaginationGrid.tsx | 8 +- .../pagination/ServerPaginationGrid.tsx | 4 +- .../data-grid/rows/InfiniteLoadingGrid.tsx | 4 +- ...ontrolledSelectionServerPaginationGrid.tsx | 4 +- .../data-grid/sorting/ServerSortingGrid.tsx | 4 +- .../x-grid-data-generator/src/datagen-cli.ts | 4 +- .../src/employees.columns.tsx | 1 + .../src/services/gridColDefGenerator.ts | 19 ++- .../src/services/random-generator.ts | 30 ++++- .../src/services/real-data-service.ts | 41 ++++--- .../src/services/tree-data-generator.ts | 113 ++++++++++++++++++ .../x-grid-data-generator/src/useDemoData.ts | 54 +++++++-- .../playground/real-data-demo.stories.tsx | 4 +- 13 files changed, 244 insertions(+), 46 deletions(-) create mode 100644 packages/grid/x-grid-data-generator/src/services/tree-data-generator.ts diff --git a/docs/src/pages/components/data-grid/pagination/CursorPaginationGrid.tsx b/docs/src/pages/components/data-grid/pagination/CursorPaginationGrid.tsx index 06c61f0abb346..fd45aeddbdd60 100644 --- a/docs/src/pages/components/data-grid/pagination/CursorPaginationGrid.tsx +++ b/docs/src/pages/components/data-grid/pagination/CursorPaginationGrid.tsx @@ -1,9 +1,9 @@ import * as React from 'react'; -import { GridRowsProp, DataGrid, GridRowId } from '@mui/x-data-grid'; -import { useDemoData, GridData, DataRowModel } from '@mui/x-data-grid-generator'; +import { GridRowsProp, DataGrid, GridRowId, GridRowModel } from '@mui/x-data-grid'; +import { useDemoData, GridDemoData } from '@mui/x-data-grid-generator'; interface ServerBasedGridResponse { - rows: DataRowModel[]; + rows: GridRowModel[]; nextCursor: GridRowId | null | undefined; } @@ -11,7 +11,7 @@ const PAGE_SIZE = 5; function loadServerRows( cursor: GridRowId | null | undefined, - data: GridData, + data: GridDemoData, ): Promise { return new Promise((resolve) => { setTimeout(() => { diff --git a/docs/src/pages/components/data-grid/pagination/ServerPaginationGrid.tsx b/docs/src/pages/components/data-grid/pagination/ServerPaginationGrid.tsx index 5de91ef5a51f6..d212b2b523acb 100644 --- a/docs/src/pages/components/data-grid/pagination/ServerPaginationGrid.tsx +++ b/docs/src/pages/components/data-grid/pagination/ServerPaginationGrid.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import { GridRowsProp, DataGrid } from '@mui/x-data-grid'; -import { useDemoData, GridData } from '@mui/x-data-grid-generator'; +import { useDemoData, GridDemoData } from '@mui/x-data-grid-generator'; -function loadServerRows(page: number, data: GridData): Promise { +function loadServerRows(page: number, data: GridDemoData): Promise { return new Promise((resolve) => { setTimeout(() => { resolve(data.rows.slice(page * 5, (page + 1) * 5)); diff --git a/docs/src/pages/components/data-grid/rows/InfiniteLoadingGrid.tsx b/docs/src/pages/components/data-grid/rows/InfiniteLoadingGrid.tsx index 641ae104b6683..c960a9c821a21 100644 --- a/docs/src/pages/components/data-grid/rows/InfiniteLoadingGrid.tsx +++ b/docs/src/pages/components/data-grid/rows/InfiniteLoadingGrid.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { DataGridPro, GridOverlay } from '@mui/x-data-grid-pro'; import { useDemoData, - getRealData, + getRealGridData, getCommodityColumns, } from '@mui/x-data-grid-generator'; import LinearProgress from '@mui/material/LinearProgress'; @@ -39,7 +39,7 @@ export default function InfiniteLoadingGrid() { const loadServerRows = async (newRowLength) => { setLoading(true); - const newData = await getRealData(newRowLength, getCommodityColumns()); + const newData = await getRealGridData(newRowLength, getCommodityColumns()); // Simulate network throttle await sleep(Math.random() * 500 + 100); diff --git a/docs/src/pages/components/data-grid/selection/ControlledSelectionServerPaginationGrid.tsx b/docs/src/pages/components/data-grid/selection/ControlledSelectionServerPaginationGrid.tsx index c7e5964694e4d..101aad2adb78f 100644 --- a/docs/src/pages/components/data-grid/selection/ControlledSelectionServerPaginationGrid.tsx +++ b/docs/src/pages/components/data-grid/selection/ControlledSelectionServerPaginationGrid.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import { DataGrid, GridRowsProp, GridSelectionModel } from '@mui/x-data-grid'; -import { GridData, useDemoData } from '@mui/x-data-grid-generator'; +import { GridDemoData, useDemoData } from '@mui/x-data-grid-generator'; -function loadServerRows(page: number, data: GridData): Promise { +function loadServerRows(page: number, data: GridDemoData): Promise { return new Promise((resolve) => { setTimeout(() => { resolve(data.rows.slice(page * 5, (page + 1) * 5)); diff --git a/docs/src/pages/components/data-grid/sorting/ServerSortingGrid.tsx b/docs/src/pages/components/data-grid/sorting/ServerSortingGrid.tsx index 9775ee6ebd6fb..d5cfbef7a7fe3 100644 --- a/docs/src/pages/components/data-grid/sorting/ServerSortingGrid.tsx +++ b/docs/src/pages/components/data-grid/sorting/ServerSortingGrid.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import { GridRowsProp, DataGrid, GridSortModel } from '@mui/x-data-grid'; -import { useDemoData, GridData } from '@mui/x-data-grid-generator'; +import { useDemoData, GridDemoData } from '@mui/x-data-grid-generator'; -function loadServerRows(sortModel: GridSortModel, data: GridData): Promise { +function loadServerRows(sortModel: GridSortModel, data: GridDemoData): Promise { return new Promise((resolve) => { setTimeout(() => { if (sortModel.length === 0) { diff --git a/packages/grid/x-grid-data-generator/src/datagen-cli.ts b/packages/grid/x-grid-data-generator/src/datagen-cli.ts index 4e403ccbee2ef..232d3200b7810 100644 --- a/packages/grid/x-grid-data-generator/src/datagen-cli.ts +++ b/packages/grid/x-grid-data-generator/src/datagen-cli.ts @@ -3,10 +3,10 @@ import * as fs from 'fs'; import * as yargs from 'yargs'; import { getCommodityColumns } from './commodities.columns'; import { getEmployeeColumns } from './employees.columns'; -import { getRealData } from './services'; +import { getRealGridData } from './services'; const loadData = async (size: number, dataset: string): Promise => { - const result = await getRealData( + const result = await getRealGridData( Number(size), dataset.toLowerCase() === 'commodity' ? getCommodityColumns() : getEmployeeColumns(), ); diff --git a/packages/grid/x-grid-data-generator/src/employees.columns.tsx b/packages/grid/x-grid-data-generator/src/employees.columns.tsx index 7517e13640c6d..4eeba30d6bacf 100644 --- a/packages/grid/x-grid-data-generator/src/employees.columns.tsx +++ b/packages/grid/x-grid-data-generator/src/employees.columns.tsx @@ -45,6 +45,7 @@ export const getEmployeeColumns = (): GridColDefGenerator[] => [ field: 'name', headerName: 'Name', generateData: randomName, + dataGeneratorUniquenessEnabled: true, width: 120, editable: true, }, diff --git a/packages/grid/x-grid-data-generator/src/services/gridColDefGenerator.ts b/packages/grid/x-grid-data-generator/src/services/gridColDefGenerator.ts index 741e55fbcf074..aa22d7fb21918 100644 --- a/packages/grid/x-grid-data-generator/src/services/gridColDefGenerator.ts +++ b/packages/grid/x-grid-data-generator/src/services/gridColDefGenerator.ts @@ -1,5 +1,22 @@ import { GridColDef } from '@mui/x-data-grid-pro'; +export interface GridDataGeneratorContext { + /** + * Values already attributed to this column. + * Only defined if the column has the uniqueness mode activated. + * The keys represents the random value and the value represents the amount of rows that already have this value. + * This allows to data generators to add a suffix to the returned value to force the uniqueness. + */ + values?: Record; +} + export interface GridColDefGenerator extends GridColDef { - generateData?: (data: any) => any; + generateData?: (row: any, context: GridDataGeneratorContext) => any; + + /** + * If `true`, each row will have a distinct value + * If several rows are generated with the same value, then a suffix will be added to the 2nd, 3rd, ... + * @default false + */ + dataGeneratorUniquenessEnabled?: boolean; } diff --git a/packages/grid/x-grid-data-generator/src/services/random-generator.ts b/packages/grid/x-grid-data-generator/src/services/random-generator.ts index b50346b75e61f..55ec976e807b0 100644 --- a/packages/grid/x-grid-data-generator/src/services/random-generator.ts +++ b/packages/grid/x-grid-data-generator/src/services/random-generator.ts @@ -10,6 +10,7 @@ import { STATUS_OPTIONS, TAXCODE_OPTIONS, } from './static-data'; +import { GridDataGeneratorContext } from './gridColDefGenerator'; const chanceId = globalChance(); let chance: Chance.Chance; @@ -20,6 +21,30 @@ if (process.env.DISABLE_CHANCE_RANDOM) { chance = chanceId; } +type ColumnDataGenerator = (data: any, context: GridDataGeneratorContext) => Value; + +/** + * Wrap a data generator that returns a string and add a prefix if the value generated has already been given + */ +const uniquenessHandler = + (generator: ColumnDataGenerator): ColumnDataGenerator => + (data, context) => { + const rawValue = generator(data, context); + + if (!context.values) { + return rawValue; + } + + const valueCount = (context.values[rawValue] ?? 0) + 1; + context.values[rawValue] = valueCount + 1; + + if (valueCount > 1) { + return `${rawValue} ${valueCount}`; + } + + return rawValue; + }; + function dateFuture(years?: number, refDate?: string) { let date = new Date(); if (typeof refDate !== 'undefined') { @@ -75,7 +100,8 @@ function datePast(years?: number, refDate?: string) { } export const random = (min: number, max: number): number => chance.floating({ min, max }); -export const randomInt = (min: number, max: number): number => Number(random(min, max).toFixed()); +export const randomInt = (min: number, max: number): number => + Math.floor(random(0, 1) * (max - min + 1) + min); export const randomPrice = (min = 0, max = 100000): number => Number(random(min, max).toFixed(2)); export const randomRate = (): number => random(0, 1); export const randomDate = (start: Date, end: Date) => @@ -116,7 +142,7 @@ export const randomCreatedDate = () => datePast(); export const randomUpdatedDate = () => dateRecent(); export const randomJobTitle = () => chance.profession(); export const randomRating = () => randomInt(1, 5); -export const randomName = () => chance.name(); +export const randomName = uniquenessHandler(() => chance.name()); export const generateFilledQuantity = (data: { quantity: number }) => Number((data.quantity * randomRate()).toFixed()) / data.quantity; diff --git a/packages/grid/x-grid-data-generator/src/services/real-data-service.ts b/packages/grid/x-grid-data-generator/src/services/real-data-service.ts index 65724d862e4c6..1de8a8ee3dd7f 100644 --- a/packages/grid/x-grid-data-generator/src/services/real-data-service.ts +++ b/packages/grid/x-grid-data-generator/src/services/real-data-service.ts @@ -1,20 +1,20 @@ -import { GridColDef, GridRowId } from '@mui/x-data-grid-pro'; +import { GridRowModel } from '@mui/x-data-grid-pro'; import asyncWorker from '../asyncWorker'; +import { GridColDefGenerator, GridDataGeneratorContext } from './gridColDefGenerator'; -export interface DataRowModel { - id: GridRowId; - [field: string]: any; +export interface GridDemoData { + rows: GridRowModel[]; + columns: GridColDefGenerator[]; } -export interface GridData { - columns: GridColDef[]; - rows: DataRowModel[]; -} - -export function getRealData(rowLength: number, columns: any[]): Promise { - return new Promise((resolve) => { +export function getRealGridData( + rowLength: number, + columns: GridColDefGenerator[], +): Promise { + return new Promise((resolve) => { const tasks = { current: rowLength }; - const data: DataRowModel[] = []; + const rows: GridDemoData['rows'] = []; + const indexedValues: { [field: string]: { [value: string]: number } } = {}; function work() { const row: any = {}; @@ -22,17 +22,28 @@ export function getRealData(rowLength: number, columns: any[]): Promise resolve({ columns, rows: data }), + done: () => resolve({ columns, rows }), tasks, }); }); diff --git a/packages/grid/x-grid-data-generator/src/services/tree-data-generator.ts b/packages/grid/x-grid-data-generator/src/services/tree-data-generator.ts new file mode 100644 index 0000000000000..c013ea10dd204 --- /dev/null +++ b/packages/grid/x-grid-data-generator/src/services/tree-data-generator.ts @@ -0,0 +1,113 @@ +import { GridRowModel } from '@mui/x-data-grid-pro'; +import { GridDemoData } from './real-data-service'; +import { randomArrayItem } from './random-generator'; + +export interface AddPathToDemoDataOptions { + /** + * The field used to generate the path + * If not defined, the tree data will not be built + */ + groupingField?: string; + + /** + * The depth of the tree + * @default: 1 + */ + maxDepth?: number; + + /** + * The average amount of children in a node + * @default: 2 + */ + averageChildren?: number; +} + +/** + * TODO: Use `Pick` once the tree data PR has been merged + */ +export interface DemoTreeDataValue + extends GridDemoData { + getTreeDataPath?: any, + groupingColDef?: any + treeData?: boolean +} + +interface RowWithParentIndex { + value: GridRowModel; + parentIndex: number | null; +} + +export const addTreeDataOptionsToDemoData = ( + data: GridDemoData, + options: AddPathToDemoDataOptions = {}, +): DemoTreeDataValue => { + const { averageChildren = 2, maxDepth = 1, groupingField } = options; + + const hasTreeData = maxDepth > 1 && groupingField != null; + if (!hasTreeData) { + return data; + } + + const rowsByTreeDepth: Record< + number, + { rows: { [index: number]: RowWithParentIndex }; rowIndexes: number[] } + > = {}; + const rowsCount = data.rows.length; + + const groupingCol = data.columns.find((col) => col.field === options.groupingField); + + if (!groupingCol) { + throw new Error('MUI: The tree data grouping field does not exist'); + } + groupingCol.hide = true; + + for (let i = 0; i < rowsCount; i += 1) { + const row = data.rows[i]; + + const currentChunk = Math.floor((i * (averageChildren ** maxDepth - 1)) / rowsCount) + 1; + const currentDepth = Math.floor(Math.log(currentChunk) / Math.log(averageChildren)); + + if (!rowsByTreeDepth[currentDepth]) { + rowsByTreeDepth[currentDepth] = { rows: {}, rowIndexes: [] }; + } + + rowsByTreeDepth[currentDepth].rows[i] = { value: row, parentIndex: null }; + rowsByTreeDepth[currentDepth].rowIndexes.push(i); + } + + Object.entries(rowsByTreeDepth).forEach(([depthStr, { rows }]) => { + const depth = Number(depthStr); + + Object.values(rows).forEach((row) => { + const path: string[] = []; + let previousRow: RowWithParentIndex | null = null; + for (let k = depth; k >= 0; k -= 1) { + let rowTemp: RowWithParentIndex; + if (k === depth) { + if (depth > 0) { + row.parentIndex = Number(randomArrayItem(rowsByTreeDepth[depth - 1].rowIndexes)); + } + rowTemp = row; + } else { + rowTemp = rowsByTreeDepth[k].rows[previousRow!.parentIndex!]; + } + + path.unshift(rowTemp.value[groupingField!]); + + previousRow = rowTemp; + } + + row.value.path = path; + }); + }); + + return { + ...data, + groupingColDef: { + headerName: groupingCol.headerName ?? groupingCol.field, + width: 250, + }, + getTreeDataPath: (row) => row.path, + treeData: true, + }; +}; diff --git a/packages/grid/x-grid-data-generator/src/useDemoData.ts b/packages/grid/x-grid-data-generator/src/useDemoData.ts index 4912570fd31b5..f9355edf8e412 100644 --- a/packages/grid/x-grid-data-generator/src/useDemoData.ts +++ b/packages/grid/x-grid-data-generator/src/useDemoData.ts @@ -1,18 +1,23 @@ import * as React from 'react'; import LRUCache from 'lru-cache'; -import { GridData, getRealData } from './services/real-data-service'; +import { GridDemoData, getRealGridData } from './services/real-data-service'; import { getCommodityColumns } from './commodities.columns'; import { getEmployeeColumns } from './employees.columns'; import asyncWorker from './asyncWorker'; import { GridColDefGenerator } from './services/gridColDefGenerator'; +import { + AddPathToDemoDataOptions, + DemoTreeDataValue, + addTreeDataOptionsToDemoData, +} from './services/tree-data-generator'; -const dataCache = new LRUCache({ +const dataCache = new LRUCache({ max: 10, maxAge: 60 * 5 * 1e3, // 5 minutes }); export type DemoDataReturnType = { - data: GridData; + data: DemoTreeDataValue; loading: boolean; setRowLength: (count: number) => void; loadNewData: () => void; @@ -20,11 +25,12 @@ export type DemoDataReturnType = { type DataSet = 'Commodity' | 'Employee'; -export interface DemoDataOptions { +export interface UseDemoDataOptions { dataSet: DataSet; rowLength: number; maxColumns?: number; editable?: boolean; + treeData?: AddPathToDemoDataOptions; } // Generate fake data from a seed. @@ -32,8 +38,8 @@ export interface DemoDataOptions { async function extrapolateSeed( rowLength: number, columns: GridColDefGenerator[], - data: GridData, -): Promise { + data: GridDemoData, +): Promise { return new Promise((resolve) => { const seed = data.rows; const rows = data.rows.slice(); @@ -84,7 +90,7 @@ const deepFreeze = (object: T): T => { return Object.freeze(object); }; -export const useDemoData = (options: DemoDataOptions): DemoDataReturnType => { +export const useDemoData = (options: UseDemoDataOptions): DemoDataReturnType => { const [rowLength, setRowLength] = React.useState(options.rowLength); const [index, setIndex] = React.useState(0); const [loading, setLoading] = React.useState(true); @@ -101,7 +107,15 @@ export const useDemoData = (options: DemoDataOptions): DemoDataReturnType => { return columns; }, [options.dataSet, options.editable, options.maxColumns]); - const [data, setData] = React.useState(() => ({ columns: getColumns(), rows: [] })); + const [data, setData] = React.useState(() => + addTreeDataOptionsToDemoData( + { + columns: getColumns(), + rows: [], + }, + options.treeData, + ), + ); React.useEffect(() => { const cacheKey = `${options.dataSet}-${rowLength}-${index}-${options.maxColumns}`; @@ -120,19 +134,25 @@ export const useDemoData = (options: DemoDataOptions): DemoDataReturnType => { (async () => { setLoading(true); - let newData: GridData; + let newData: DemoTreeDataValue; const columns = getColumns(); if (rowLength > 1000) { - newData = await getRealData(1000, columns); + newData = await getRealGridData(1000, columns); newData = await extrapolateSeed(rowLength, columns, newData); } else { - newData = await getRealData(rowLength, columns); + newData = await getRealGridData(rowLength, columns); } if (!active) { return; } + newData = addTreeDataOptionsToDemoData(newData, { + maxDepth: options.treeData?.maxDepth, + groupingField: options.treeData?.groupingField, + averageChildren: options.treeData?.averageChildren, + }); + // It's quite slow. No need for it in production. if (process.env.NODE_ENV !== 'production') { deepFreeze(newData); @@ -146,7 +166,17 @@ export const useDemoData = (options: DemoDataOptions): DemoDataReturnType => { return () => { active = false; }; - }, [rowLength, data.columns, options.dataSet, options.maxColumns, index, getColumns]); + }, [ + rowLength, + data.columns, + options.dataSet, + options.maxColumns, + options.treeData?.maxDepth, + options.treeData?.groupingField, + options.treeData?.averageChildren, + index, + getColumns, + ]); return { data, diff --git a/packages/storybook/src/stories/playground/real-data-demo.stories.tsx b/packages/storybook/src/stories/playground/real-data-demo.stories.tsx index f562a33f33260..5c7e0e4aae6e1 100644 --- a/packages/storybook/src/stories/playground/real-data-demo.stories.tsx +++ b/packages/storybook/src/stories/playground/real-data-demo.stories.tsx @@ -2,7 +2,7 @@ import { DataGrid, GridToolbar } from '@mui/x-data-grid'; import * as React from 'react'; import { Story, Meta, DecoratorFn } from '@storybook/react'; import { DataGridProProps, GridPreferencePanelsValue, DataGridPro } from '@mui/x-data-grid-pro'; -import { useDemoData, DemoDataOptions } from '@mui/x-data-grid-generator'; +import { useDemoData, UseDemoDataOptions } from '@mui/x-data-grid-generator'; import { useData } from '../../hooks/useData'; import { ColumnMenuComponent, @@ -65,7 +65,7 @@ export default { }, } as Meta; -const DemoTemplate: Story = ({ +const DemoTemplate: Story = ({ rows, columns, dataSet, From 1a4e921e5d4ce6e2d8993f7e099299aa673f1659 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 25 Oct 2021 10:33:43 +0200 Subject: [PATCH 317/390] Fix --- .../grid/hooks/utils/useRowsInCurrentPage.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts b/packages/grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts index e150f0e7f0679..3a283cae87402 100644 --- a/packages/grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts +++ b/packages/grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts @@ -1,3 +1,4 @@ +import * as React from 'react'; import { GridComponentProps } from '../../GridComponentProps'; import { gridPaginationRowRangeSelector, @@ -34,8 +35,11 @@ export const useRowsInCurrentPage = ( } } - return { - rows, - range, - }; + return React.useMemo( + () => ({ + rows, + range, + }), + [rows, range], + ); }; From 48793001f2db6b665f77efa5c25703bd2c268ebf Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 25 Oct 2021 10:34:52 +0200 Subject: [PATCH 318/390] Fix --- .../src/services/tree-data-generator.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/grid/x-grid-data-generator/src/services/tree-data-generator.ts b/packages/grid/x-grid-data-generator/src/services/tree-data-generator.ts index c013ea10dd204..addd068f2a257 100644 --- a/packages/grid/x-grid-data-generator/src/services/tree-data-generator.ts +++ b/packages/grid/x-grid-data-generator/src/services/tree-data-generator.ts @@ -25,11 +25,10 @@ export interface AddPathToDemoDataOptions { /** * TODO: Use `Pick` once the tree data PR has been merged */ -export interface DemoTreeDataValue - extends GridDemoData { - getTreeDataPath?: any, - groupingColDef?: any - treeData?: boolean +export interface DemoTreeDataValue extends GridDemoData { + getTreeDataPath?: any; + groupingColDef?: any; + treeData?: boolean; } interface RowWithParentIndex { From cae575432d8351f0b438ced59549bfb48a9c2d9c Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 25 Oct 2021 10:35:06 +0200 Subject: [PATCH 319/390] Fix --- .../pages/components/data-grid/rows/InfiniteLoadingGrid.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/pages/components/data-grid/rows/InfiniteLoadingGrid.js b/docs/src/pages/components/data-grid/rows/InfiniteLoadingGrid.js index 860af52c27de6..4b4d896cab573 100644 --- a/docs/src/pages/components/data-grid/rows/InfiniteLoadingGrid.js +++ b/docs/src/pages/components/data-grid/rows/InfiniteLoadingGrid.js @@ -2,7 +2,7 @@ import * as React from 'react'; import { DataGridPro, GridOverlay } from '@mui/x-data-grid-pro'; import { useDemoData, - getRealData, + getRealGridData, getCommodityColumns, } from '@mui/x-data-grid-generator'; import LinearProgress from '@mui/material/LinearProgress'; @@ -39,7 +39,7 @@ export default function InfiniteLoadingGrid() { const loadServerRows = async (newRowLength) => { setLoading(true); - const newData = await getRealData(newRowLength, getCommodityColumns()); + const newData = await getRealGridData(newRowLength, getCommodityColumns()); // Simulate network throttle await sleep(Math.random() * 500 + 100); From b1686772f3d84c397a7ce9899fcf215524cd1943 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 25 Oct 2021 13:39:22 +0200 Subject: [PATCH 320/390] Work --- docs/scripts/buildApi.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/scripts/buildApi.ts b/docs/scripts/buildApi.ts index ad815ae85f029..400086e6c72a3 100644 --- a/docs/scripts/buildApi.ts +++ b/docs/scripts/buildApi.ts @@ -735,12 +735,6 @@ async function run(argv: { outputDirectory?: string }) { // } // })!; - writePrettifiedFile( - path.resolve(workspaceRoot, 'scripts/exportsSnapshot.json'), - JSON.stringify(exports), - prettierConfigPath, - ); - const apisToGenerate = [ 'GridApi', 'GridColDef', @@ -803,6 +797,12 @@ async function run(argv: { outputDirectory?: string }) { kind: child?.kindString, })); + writePrettifiedFile( + path.resolve(workspaceRoot, 'scripts/exportsSnapshot.json'), + JSON.stringify(exports), + prettierConfigPath, + ); + apisToGenerate.forEach((apiName) => { const reflection = project.findReflectionByName(apiName) as TypeDoc.DeclarationReflection; if (!reflection) { From a3828c4a56bc3914656dac8e0e71702817f802e9 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 25 Oct 2021 13:39:59 +0200 Subject: [PATCH 321/390] Fix --- scripts/exportsSnapshot.json | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/exportsSnapshot.json b/scripts/exportsSnapshot.json index b3ca9f8191959..a5212e9e1f9dd 100644 --- a/scripts/exportsSnapshot.json +++ b/scripts/exportsSnapshot.json @@ -135,6 +135,7 @@ { "name": "GridToolbarFilterButtonProps", "kind": "Interface" }, { "name": "GridTypeFilterInputValueProps", "kind": "Interface" }, { "name": "GridValueFormatterParams", "kind": "Interface" }, + { "name": "GridValueSetterParams", "kind": "Interface" }, { "name": "Logger", "kind": "Interface" }, { "name": "DataGridProps", "kind": "Type alias" }, { "name": "FilterColumnLookup", "kind": "Type alias" }, From bbdf0920b8b16ae9d8fe3819f43fda7408097df5 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 25 Oct 2021 14:03:01 +0200 Subject: [PATCH 322/390] Fix --- .../grid/_modules_/grid/GridComponentProps.ts | 16 +++++++++----- .../grid/components/GridVirtualScroller.tsx | 22 +++++++++---------- .../hooks/features/filter/gridFilterState.ts | 11 ++++++++++ .../infiniteLoader/useGridInfiniteLoader.ts | 6 ++--- .../keyboard/useGridKeyboardNavigation.ts | 13 +++++------ .../features/treeData/useGridTreeData.ts | 15 +++++-------- ...InCurrentPage.ts => useCurrentPageRows.ts} | 2 +- .../grid/models/colDef/gridColDef.ts | 7 ++++++ 8 files changed, 55 insertions(+), 37 deletions(-) rename packages/grid/_modules_/grid/hooks/utils/{useRowsInCurrentPage.ts => useCurrentPageRows.ts} (97%) diff --git a/packages/grid/_modules_/grid/GridComponentProps.ts b/packages/grid/_modules_/grid/GridComponentProps.ts index 75454d2cd2cc8..b6944277a2a12 100644 --- a/packages/grid/_modules_/grid/GridComponentProps.ts +++ b/packages/grid/_modules_/grid/GridComponentProps.ts @@ -1,7 +1,11 @@ import * as React from 'react'; import { GridInitialState, GridState } from './models/gridState'; import { GridApiRef } from './models/api/gridApiRef'; -import { GridColDef, GridColDefOverrideParams, GridColumns } from './models/colDef/gridColDef'; +import { + GridColDefOverride, + GridColDefOverrideCallback, + GridColumns, +} from './models/colDef/gridColDef'; import { GridSimpleOptions, GridProcessedMergedOptions, @@ -100,9 +104,11 @@ interface GridComponentOtherProps { */ isRowSelectable?: (params: GridRowParams) => boolean; /** - * Determines the path of a row in the tree data + * Determines the path of a row in the tree data. + * For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"] + * Note than all paths must contain at least one element. * @param {GridRowModel} row The row from which we want the path. - * @returns {string[]} the path to the row + * @returns {string[]} the path to the row. */ getTreeDataPath?: (row: GridRowModel) => string[]; /** @@ -497,7 +503,5 @@ interface GridComponentOtherProps { /** * The grouping column used by the tree data */ - groupingColDef?: - | Partial - | ((params: GridColDefOverrideParams) => Partial); + groupingColDef?: GridColDefOverride | GridColDefOverrideCallback; } diff --git a/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx b/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx index adf59f033ae08..35c8d7a7adc4d 100644 --- a/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx +++ b/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx @@ -22,7 +22,7 @@ import { useGridApiEventHandler } from '../hooks/utils/useGridApiEventHandler'; import { getDataGridUtilityClass } from '../gridClasses'; import { GridComponentProps } from '../GridComponentProps'; import { GridRowId } from '../models/gridRows'; -import { useRowsInCurrentPage } from '../hooks/utils/useRowsInCurrentPage'; +import { useCurrentPageRows } from '../hooks/utils/useCurrentPageRows'; type OwnerState = { classes: GridComponentProps['classes'] }; @@ -109,7 +109,7 @@ const GridVirtualScroller = React.forwardRef(null); const rootRef = React.useRef(null); const handleRef = useForkRef(ref, rootRef); @@ -125,7 +125,7 @@ const GridVirtualScroller = React.forwardRef { - if (!rowsInCurrentPage.range || !renderContext || containerWidth == null) { + if (!currentPage.range || !renderContext || containerWidth == null) { return null; } @@ -256,7 +256,7 @@ const GridVirtualScroller = React.forwardRef, @@ -305,10 +305,10 @@ const GridVirtualScroller = React.forwardRef GridFilterModel = () => ({ export interface GridFilterState { filterModel: GridFilterModel; + + /** + * If a row is not registered in this lookup, it is supposed to be visible + */ visibleRowsLookup: Record; visibleRows: GridRowId[]; + + /** + * Amount of visible descendants for each row + * For the Tree Data, it includes all the intermediate depth levels (= amount of children + amount of grand children + ...) + * For the Row Grouping by Column, it does not include the intermediate depth levels (= amount of descendant of maximum depth) + * If a row is not registered in this lookup, it is supposed to have no descendant. + */ visibleDescendantsCountLookup: Record; } diff --git a/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts b/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts index 3f383d485451d..53ddbb4f9e174 100644 --- a/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts +++ b/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts @@ -12,7 +12,7 @@ import { visibleGridColumnsSelector } from '../columns/gridColumnsSelector'; import { GridComponentProps } from '../../../GridComponentProps'; import { GridScrollParams } from '../../../models/params/gridScrollParams'; import { gridDensityRowHeightSelector } from '../density/densitySelector'; -import { useRowsInCurrentPage } from '../../utils/useRowsInCurrentPage'; +import { useCurrentPageRows } from '../../utils/useCurrentPageRows'; /** * Only available in DataGridPro @@ -29,9 +29,9 @@ export const useGridInfiniteLoader = ( ): void => { const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); - const rowsInCurrentPage = useRowsInCurrentPage(apiRef, props); + const currentPage = useCurrentPageRows(apiRef, props); const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector); - const contentHeight = Math.max(rowsInCurrentPage.rows.length * rowHeight, 1); + const contentHeight = Math.max(currentPage.rows.length * rowHeight, 1); const isInScrollBottomArea = React.useRef(false); diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts index d31499f66e59a..09e189a1a3dbd 100644 --- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts +++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts @@ -22,7 +22,7 @@ import { useGridLogger } from '../../utils/useGridLogger'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { GridComponentProps } from '../../../GridComponentProps'; import { gridVisibleSortedRowEntriesSelector } from '../filter/gridFilterSelector'; -import { useRowsInCurrentPage } from '../../utils/useRowsInCurrentPage'; +import { useCurrentPageRows } from '../../utils/useCurrentPageRows'; const getNextCellIndexes = (key: string, indexes: GridCellIndexCoordinates) => { if (!isArrowKeys(key)) { @@ -77,7 +77,7 @@ export const useGridKeyboardNavigation = ( const colCount = useGridSelector(apiRef, visibleGridColumnsLengthSelector); const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); const visibleSortedRows = useGridSelector(apiRef, gridVisibleSortedRowEntriesSelector); - const rowsInCurrentPage = useRowsInCurrentPage(apiRef, props); + const currentPage = useCurrentPageRows(apiRef, props); const mapKey = (event: React.KeyboardEvent) => { if (isEnterKey(event.key)) { @@ -93,7 +93,7 @@ export const useGridKeyboardNavigation = ( (params: GridCellParams, event: React.KeyboardEvent) => { event.preventDefault(); - if (!rowsInCurrentPage.range) { + if (!currentPage.range) { return; } @@ -102,8 +102,7 @@ export const useGridKeyboardNavigation = ( const key = mapKey(event); const isCtrlPressed = event.ctrlKey || event.metaKey || event.shiftKey; - const rowCount = - rowsInCurrentPage.range.lastRowIndex - rowsInCurrentPage.range.firstRowIndex + 1; + const rowCount = currentPage.range.lastRowIndex - currentPage.range.firstRowIndex + 1; let nextCellIndexes: GridCellIndexCoordinates; if (isArrowKeys(key)) { @@ -121,7 +120,7 @@ export const useGridKeyboardNavigation = ( // In that case we go to first row, first col, or last row last col! let newRowIndex = 0; if (colIdx === 0) { - newRowIndex = rowsInCurrentPage.range.firstRowIndex; + newRowIndex = currentPage.range.firstRowIndex; } else { newRowIndex = rowCount - 1; } @@ -159,7 +158,7 @@ export const useGridKeyboardNavigation = ( const node = visibleSortedRows[nextCellIndexes.rowIndex]; apiRef.current.setCellFocus(node.id, field); }, - [apiRef, visibleSortedRows, colCount, logger, containerSizes, rowsInCurrentPage], + [apiRef, visibleSortedRows, colCount, logger, containerSizes, currentPage], ); const navigateColumnHeaders = React.useCallback( diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index e5ec3defd7fdf..b01bd44ceb167 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -31,21 +31,21 @@ export const useGridTreeData = ( ...GRID_TREE_DATA_GROUP_COL_DEF, headerName: apiRef.current.getLocaleText('treeDataGroupingHeaderName'), }; - let customGroupingColDef: Partial; + let colDefOverride: Partial; if (isFunction(propGroupingColDef)) { const params: GridColDefOverrideParams = { colDef: baseColDef, }; - customGroupingColDef = propGroupingColDef(params); + colDefOverride = propGroupingColDef(params); } else { - customGroupingColDef = propGroupingColDef ?? {}; + colDefOverride = propGroupingColDef ?? {}; } return { ...baseColDef, - ...customGroupingColDef, + ...colDefOverride, }; }, [apiRef, props.groupingColDef]); @@ -121,14 +121,11 @@ export const useGridTreeData = ( event.preventDefault(); const node = apiRef.current.unstable_getRowNode(params.id); - if (!node || node.descendantCount === 0) { + if (!node?.descendantCount) { return; } - apiRef.current.unstable_setRowExpansion( - params.id, - !apiRef.current.unstable_getRowNode(params.id)?.expanded, - ); + apiRef.current.unstable_setRowExpansion(params.id, !node.expanded); } }, [apiRef], diff --git a/packages/grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts b/packages/grid/_modules_/grid/hooks/utils/useCurrentPageRows.ts similarity index 97% rename from packages/grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts rename to packages/grid/_modules_/grid/hooks/utils/useCurrentPageRows.ts index 3a283cae87402..7949ff1122378 100644 --- a/packages/grid/_modules_/grid/hooks/utils/useRowsInCurrentPage.ts +++ b/packages/grid/_modules_/grid/hooks/utils/useCurrentPageRows.ts @@ -14,7 +14,7 @@ import { useGridState } from './useGridState'; * - If the row tree has several layers, it contains up to `state.pageSize` top level rows and all their descendants * - If the row tree is flat, it only contains up to `state.pageSize` rows */ -export const useRowsInCurrentPage = ( +export const useCurrentPageRows = ( apiRef: GridApiRef, props: Pick, ) => { diff --git a/packages/grid/_modules_/grid/models/colDef/gridColDef.ts b/packages/grid/_modules_/grid/models/colDef/gridColDef.ts index 5f57d2e1e9bfe..19fb4a7e668a2 100644 --- a/packages/grid/_modules_/grid/models/colDef/gridColDef.ts +++ b/packages/grid/_modules_/grid/models/colDef/gridColDef.ts @@ -218,6 +218,13 @@ export interface GridColumnsState { lookup: GridColumnLookup; } +export type GridColDefOverride = Omit, 'field'>; + +export type GridColDefOverrideCallback = (params: GridColDefOverrideParams) => GridColDefOverride; + export interface GridColDefOverrideParams { + /** + * The column we are generating before the override. + */ colDef: GridColDef; } From 87b4820a66b34297e5e06d13df5cb9bacd1a0330 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 25 Oct 2021 14:07:02 +0200 Subject: [PATCH 323/390] Fix --- .../grid/hooks/features/filter/gridFilterSelector.ts | 10 +++++----- .../grid/hooks/features/filter/gridFilterState.ts | 2 +- .../grid/hooks/features/filter/useGridFilter.ts | 6 ------ 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index 18a61db369305..c1d2745d0a093 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -12,11 +12,6 @@ export const gridFilterModelSelector = createSelector( (filterState) => filterState.filterModel, ); -export const gridVisibleRowsSelector = createSelector( - gridFilterStateSelector, - (filterState) => filterState.visibleRows, -); - export const gridVisibleRowsLookupSelector = createSelector( gridFilterStateSelector, (filterState) => filterState.visibleRowsLookup, @@ -39,6 +34,11 @@ export const gridVisibleSortedRowIdsSelector = createSelector( (visibleSortedRowEntries) => visibleSortedRowEntries.map((row) => row.id), ); +/** + * @deprecated Use `gridVisibleSortedRowIdsSelector` instead + */ +export const gridVisibleRowsSelector = gridVisibleSortedRowIdsSelector; + export const gridVisibleSortedTopLevelRowEntriesSelector = createSelector( gridVisibleSortedRowEntriesSelector, gridRowTreeSelector, diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts index 25a6f99da3c14..233a2bee3694a 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts @@ -11,10 +11,10 @@ export interface GridFilterState { filterModel: GridFilterModel; /** + * Visibility status for each row * If a row is not registered in this lookup, it is supposed to be visible */ visibleRowsLookup: Record; - visibleRows: GridRowId[]; /** * Amount of visible descendants for each row diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 2692159c47eb0..63b07dc935cf9 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -66,7 +66,6 @@ export const useGridFilter = ( props.initialState?.filter?.filterModel ?? getDefaultGridFilterModel(), visibleRowsLookup: {}, - visibleRows: [], visibleDescendantsCountLookup: {}, }, }; @@ -164,7 +163,6 @@ export const useGridFilter = ( ...state.filter, visibleRowsLookup: {}, visibleDescendantsCountLookup: {}, - visibleRows: rowIds, }, }; } @@ -185,7 +183,6 @@ export const useGridFilter = ( ...state.filter, visibleRowsLookup: {}, visibleDescendantsCountLookup, - visibleRows: rowIds, }, }; } @@ -262,9 +259,6 @@ export const useGridFilter = ( ...state.filter, visibleRowsLookup, visibleDescendantsCountLookup, - visibleRows: Object.entries(visibleRowsLookup) - .filter(([, isVisible]) => isVisible) - .map(([id]) => id), }, }; }); From 4ec7b62e3bf114e6a60f00ba6c3369f01dfa11b8 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 25 Oct 2021 14:12:04 +0200 Subject: [PATCH 324/390] Remove free version props --- packages/grid/data-grid/src/DataGrid.tsx | 26 ------------------- packages/grid/data-grid/src/DataGridProps.ts | 5 ++++ .../grid/data-grid/src/useDataGridProps.ts | 7 ++++- packages/grid/x-grid/src/DataGridPro.tsx | 6 +++-- 4 files changed, 15 insertions(+), 29 deletions(-) diff --git a/packages/grid/data-grid/src/DataGrid.tsx b/packages/grid/data-grid/src/DataGrid.tsx index d9986609ca208..188ae38a9f20d 100644 --- a/packages/grid/data-grid/src/DataGrid.tsx +++ b/packages/grid/data-grid/src/DataGrid.tsx @@ -111,27 +111,11 @@ DataGridRaw.propTypes = { * Overrideable components props dynamically passed to the component at rendering. */ componentsProps: PropTypes.object, - /** - * If above 0, the row children will be expanded up to this depth - * If equal to -1, all the row children will be expanded - * @default 0 - */ - defaultGroupingExpansionDepth: PropTypes.number, /** * Set the density of the grid. * @default "standard" */ density: PropTypes.oneOf(['comfortable', 'compact', 'standard']), - /** - * If `true`, the filtering will only be applied to the top level rows - * @default false - */ - disableChildrenFiltering: PropTypes.bool, - /** - * If `true`, the sorting will only be applied to the top level rows - * @default false - */ - disableChildrenSorting: PropTypes.bool, /** * If `true`, column filters are disabled. * @default false @@ -218,16 +202,6 @@ DataGridRaw.propTypes = { * Return the id of a given [[GridRowModel]]. */ getRowId: PropTypes.func, - /** - * Determines the path of a row in the tree data - * @param {GridRowModel} row The row from which we want the path. - * @returns {string[]} the path to the row - */ - getTreeDataPath: PropTypes.func, - /** - * The grouping column used by the tree data - */ - groupingColDef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), /** * Set the height in pixel of the column headers in the grid. * @default 56 diff --git a/packages/grid/data-grid/src/DataGridProps.ts b/packages/grid/data-grid/src/DataGridProps.ts index 06d39b1987cce..066a2ecbca4ed 100644 --- a/packages/grid/data-grid/src/DataGridProps.ts +++ b/packages/grid/data-grid/src/DataGridProps.ts @@ -14,12 +14,17 @@ export type DataGridProps = Omit< | 'disableMultipleColumnsFiltering' | 'disableMultipleColumnsSorting' | 'disableMultipleSelection' + | 'disableChildrenFiltering' + | 'disableChildrenSorting' | 'throttleRowsMs' | 'hideFooterRowCount' | 'options' | 'onRowsScrollEnd' | 'scrollEndThreshold' | 'treeData' + | 'getTreeDataPath' + | 'groupingColDef' + | 'defaultGroupingExpansionDepth' | 'signature' > & { pagination?: true; diff --git a/packages/grid/data-grid/src/useDataGridProps.ts b/packages/grid/data-grid/src/useDataGridProps.ts index be19d721fd5aa..944034095d1bf 100644 --- a/packages/grid/data-grid/src/useDataGridProps.ts +++ b/packages/grid/data-grid/src/useDataGridProps.ts @@ -16,13 +16,18 @@ const FORCED_PROPS: { [key in ForcedPropsKey]-?: GridInputComponentProps[key] } disableMultipleColumnsFiltering: true, disableMultipleColumnsSorting: true, disableMultipleSelection: true, + disableChildrenFiltering: undefined, + disableChildrenSorting: undefined, + getTreeDataPath: undefined, + groupingColDef: undefined, throttleRowsMs: undefined, hideFooterRowCount: false, pagination: true, onRowsScrollEnd: undefined, checkboxSelectionVisibleOnly: false, scrollEndThreshold: undefined, - treeData: false, + defaultGroupingExpansionDepth: undefined, + treeData: undefined, signature: 'DataGrid', }; diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index ed95298909688..91945acf234dd 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -272,9 +272,11 @@ DataGridProRaw.propTypes = { */ getRowId: PropTypes.func, /** - * Determines the path of a row in the tree data + * Determines the path of a row in the tree data. + * For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"] + * Note than all paths must contain at least one element. * @param {GridRowModel} row The row from which we want the path. - * @returns {string[]} the path to the row + * @returns {string[]} the path to the row. */ getTreeDataPath: PropTypes.func, /** From d0d5058e3c64718792906298846cc8efd4827e42 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 25 Oct 2021 14:22:08 +0200 Subject: [PATCH 325/390] docs --- docs/pages/api-docs/data-grid/data-grid.json | 5 ----- docs/translations/api-docs/data-grid/data-grid-pro-pt.json | 2 +- docs/translations/api-docs/data-grid/data-grid-pro-zh.json | 2 +- docs/translations/api-docs/data-grid/data-grid-pro.json | 2 +- docs/translations/api-docs/data-grid/data-grid-pt.json | 5 ----- docs/translations/api-docs/data-grid/data-grid-zh.json | 5 ----- docs/translations/api-docs/data-grid/data-grid.json | 5 ----- 7 files changed, 3 insertions(+), 23 deletions(-) diff --git a/docs/pages/api-docs/data-grid/data-grid.json b/docs/pages/api-docs/data-grid/data-grid.json index d3abdc6c45fef..0401b166c88cd 100644 --- a/docs/pages/api-docs/data-grid/data-grid.json +++ b/docs/pages/api-docs/data-grid/data-grid.json @@ -16,7 +16,6 @@ "columnTypes": { "type": { "name": "object" } }, "components": { "type": { "name": "object" } }, "componentsProps": { "type": { "name": "object" } }, - "defaultGroupingExpansionDepth": { "type": { "name": "number" }, "default": "0" }, "density": { "type": { "name": "enum", @@ -24,8 +23,6 @@ }, "default": "\"standard\"" }, - "disableChildrenFiltering": { "type": { "name": "bool" } }, - "disableChildrenSorting": { "type": { "name": "bool" } }, "disableColumnFilter": { "type": { "name": "bool" } }, "disableColumnMenu": { "type": { "name": "bool" } }, "disableColumnSelector": { "type": { "name": "bool" } }, @@ -47,8 +44,6 @@ "getCellClassName": { "type": { "name": "func" } }, "getRowClassName": { "type": { "name": "func" } }, "getRowId": { "type": { "name": "func" } }, - "getTreeDataPath": { "type": { "name": "func" } }, - "groupingColDef": { "type": { "name": "union", "description": "func
| object" } }, "headerHeight": { "type": { "name": "number" }, "default": "56" }, "hideFooter": { "type": { "name": "bool" } }, "hideFooterPagination": { "type": { "name": "bool" } }, diff --git a/docs/translations/api-docs/data-grid/data-grid-pro-pt.json b/docs/translations/api-docs/data-grid/data-grid-pro-pt.json index 43ed2a29895be..8f32d917b5528 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro-pt.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro-pt.json @@ -39,7 +39,7 @@ "getCellClassName": "Function that applies CSS classes dynamically on cells.

Signature:
function(params: GridCellParams) => string
params: With all properties from GridCellParams.
returns (string): The CSS class to apply to the cell.", "getRowClassName": "Function that applies CSS classes dynamically on rows.

Signature:
function(params: GridRowParams) => string
params: With all properties from GridRowParams.
returns (string): The CSS class to apply to the row.", "getRowId": "Return the id of a given GridRowModel.", - "getTreeDataPath": "Determines the path of a row in the tree data

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row", + "getTreeDataPath": "Determines the path of a row in the tree data. For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"] Note than all paths must contain at least one element.

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row.", "groupingColDef": "The grouping column used by the tree data", "headerHeight": "Set the height in pixel of the column headers in the grid.", "hideFooter": "If true, the footer component is hidden.", diff --git a/docs/translations/api-docs/data-grid/data-grid-pro-zh.json b/docs/translations/api-docs/data-grid/data-grid-pro-zh.json index 43ed2a29895be..8f32d917b5528 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro-zh.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro-zh.json @@ -39,7 +39,7 @@ "getCellClassName": "Function that applies CSS classes dynamically on cells.

Signature:
function(params: GridCellParams) => string
params: With all properties from GridCellParams.
returns (string): The CSS class to apply to the cell.", "getRowClassName": "Function that applies CSS classes dynamically on rows.

Signature:
function(params: GridRowParams) => string
params: With all properties from GridRowParams.
returns (string): The CSS class to apply to the row.", "getRowId": "Return the id of a given GridRowModel.", - "getTreeDataPath": "Determines the path of a row in the tree data

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row", + "getTreeDataPath": "Determines the path of a row in the tree data. For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"] Note than all paths must contain at least one element.

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row.", "groupingColDef": "The grouping column used by the tree data", "headerHeight": "Set the height in pixel of the column headers in the grid.", "hideFooter": "If true, the footer component is hidden.", diff --git a/docs/translations/api-docs/data-grid/data-grid-pro.json b/docs/translations/api-docs/data-grid/data-grid-pro.json index 43ed2a29895be..8f32d917b5528 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro.json @@ -39,7 +39,7 @@ "getCellClassName": "Function that applies CSS classes dynamically on cells.

Signature:
function(params: GridCellParams) => string
params: With all properties from GridCellParams.
returns (string): The CSS class to apply to the cell.", "getRowClassName": "Function that applies CSS classes dynamically on rows.

Signature:
function(params: GridRowParams) => string
params: With all properties from GridRowParams.
returns (string): The CSS class to apply to the row.", "getRowId": "Return the id of a given GridRowModel.", - "getTreeDataPath": "Determines the path of a row in the tree data

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row", + "getTreeDataPath": "Determines the path of a row in the tree data. For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"] Note than all paths must contain at least one element.

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row.", "groupingColDef": "The grouping column used by the tree data", "headerHeight": "Set the height in pixel of the column headers in the grid.", "hideFooter": "If true, the footer component is hidden.", diff --git a/docs/translations/api-docs/data-grid/data-grid-pt.json b/docs/translations/api-docs/data-grid/data-grid-pt.json index 8453fa991d383..fa582d30756ee 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pt.json +++ b/docs/translations/api-docs/data-grid/data-grid-pt.json @@ -13,10 +13,7 @@ "columnTypes": "Extend native column types with your new column types.", "components": "Overrideable components.", "componentsProps": "Overrideable components props dynamically passed to the component at rendering.", - "defaultGroupingExpansionDepth": "If above 0, the row children will be expanded up to this depth If equal to -1, all the row children will be expanded", "density": "Set the density of the grid.", - "disableChildrenFiltering": "If true, the filtering will only be applied to the top level rows", - "disableChildrenSorting": "If true, the sorting will only be applied to the top level rows", "disableColumnFilter": "If true, column filters are disabled.", "disableColumnMenu": "If true, the column menu is disabled.", "disableColumnSelector": "If true, hiding/showing columns is disabled.", @@ -32,8 +29,6 @@ "getCellClassName": "Function that applies CSS classes dynamically on cells.

Signature:
function(params: GridCellParams) => string
params: With all properties from GridCellParams.
returns (string): The CSS class to apply to the cell.", "getRowClassName": "Function that applies CSS classes dynamically on rows.

Signature:
function(params: GridRowParams) => string
params: With all properties from GridRowParams.
returns (string): The CSS class to apply to the row.", "getRowId": "Return the id of a given GridRowModel.", - "getTreeDataPath": "Determines the path of a row in the tree data

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row", - "groupingColDef": "The grouping column used by the tree data", "headerHeight": "Set the height in pixel of the column headers in the grid.", "hideFooter": "If true, the footer component is hidden.", "hideFooterPagination": "If true, the pagination component in the footer is hidden.", diff --git a/docs/translations/api-docs/data-grid/data-grid-zh.json b/docs/translations/api-docs/data-grid/data-grid-zh.json index 8453fa991d383..fa582d30756ee 100644 --- a/docs/translations/api-docs/data-grid/data-grid-zh.json +++ b/docs/translations/api-docs/data-grid/data-grid-zh.json @@ -13,10 +13,7 @@ "columnTypes": "Extend native column types with your new column types.", "components": "Overrideable components.", "componentsProps": "Overrideable components props dynamically passed to the component at rendering.", - "defaultGroupingExpansionDepth": "If above 0, the row children will be expanded up to this depth If equal to -1, all the row children will be expanded", "density": "Set the density of the grid.", - "disableChildrenFiltering": "If true, the filtering will only be applied to the top level rows", - "disableChildrenSorting": "If true, the sorting will only be applied to the top level rows", "disableColumnFilter": "If true, column filters are disabled.", "disableColumnMenu": "If true, the column menu is disabled.", "disableColumnSelector": "If true, hiding/showing columns is disabled.", @@ -32,8 +29,6 @@ "getCellClassName": "Function that applies CSS classes dynamically on cells.

Signature:
function(params: GridCellParams) => string
params: With all properties from GridCellParams.
returns (string): The CSS class to apply to the cell.", "getRowClassName": "Function that applies CSS classes dynamically on rows.

Signature:
function(params: GridRowParams) => string
params: With all properties from GridRowParams.
returns (string): The CSS class to apply to the row.", "getRowId": "Return the id of a given GridRowModel.", - "getTreeDataPath": "Determines the path of a row in the tree data

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row", - "groupingColDef": "The grouping column used by the tree data", "headerHeight": "Set the height in pixel of the column headers in the grid.", "hideFooter": "If true, the footer component is hidden.", "hideFooterPagination": "If true, the pagination component in the footer is hidden.", diff --git a/docs/translations/api-docs/data-grid/data-grid.json b/docs/translations/api-docs/data-grid/data-grid.json index 8453fa991d383..fa582d30756ee 100644 --- a/docs/translations/api-docs/data-grid/data-grid.json +++ b/docs/translations/api-docs/data-grid/data-grid.json @@ -13,10 +13,7 @@ "columnTypes": "Extend native column types with your new column types.", "components": "Overrideable components.", "componentsProps": "Overrideable components props dynamically passed to the component at rendering.", - "defaultGroupingExpansionDepth": "If above 0, the row children will be expanded up to this depth If equal to -1, all the row children will be expanded", "density": "Set the density of the grid.", - "disableChildrenFiltering": "If true, the filtering will only be applied to the top level rows", - "disableChildrenSorting": "If true, the sorting will only be applied to the top level rows", "disableColumnFilter": "If true, column filters are disabled.", "disableColumnMenu": "If true, the column menu is disabled.", "disableColumnSelector": "If true, hiding/showing columns is disabled.", @@ -32,8 +29,6 @@ "getCellClassName": "Function that applies CSS classes dynamically on cells.

Signature:
function(params: GridCellParams) => string
params: With all properties from GridCellParams.
returns (string): The CSS class to apply to the cell.", "getRowClassName": "Function that applies CSS classes dynamically on rows.

Signature:
function(params: GridRowParams) => string
params: With all properties from GridRowParams.
returns (string): The CSS class to apply to the row.", "getRowId": "Return the id of a given GridRowModel.", - "getTreeDataPath": "Determines the path of a row in the tree data

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row", - "groupingColDef": "The grouping column used by the tree data", "headerHeight": "Set the height in pixel of the column headers in the grid.", "hideFooter": "If true, the footer component is hidden.", "hideFooterPagination": "If true, the pagination component in the footer is hidden.", From c678a4c0917fa59e7f1d07d7b3c4ac684b12d51a Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 25 Oct 2021 15:25:41 +0200 Subject: [PATCH 326/390] Fix --- docs/pages/playground.tsx | 17 +++++++++--- .../useGridRowGroupsPreProcessing.ts | 11 ++++---- .../hooks/features/filter/useGridFilter.ts | 5 ++-- .../grid/hooks/features/rows/useGridRows.ts | 27 +++++++++---------- .../hooks/features/sorting/useGridSorting.ts | 5 ++-- 5 files changed, 39 insertions(+), 26 deletions(-) diff --git a/docs/pages/playground.tsx b/docs/pages/playground.tsx index 8d573e70e7b82..d0a474eeafb38 100644 --- a/docs/pages/playground.tsx +++ b/docs/pages/playground.tsx @@ -1,5 +1,16 @@ import * as React from 'react'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import { DataGridPro } from '@mui/x-data-grid-pro'; -export default function Playground() { - return
This file is listed in `.gitignore`
; -} +export default () => { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100000, + }); + + return ( +
+ +
+ ); +}; diff --git a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts index 01853b9a1e833..6c42ae9fd96b9 100644 --- a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts +++ b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts @@ -9,17 +9,18 @@ import { import { GridEvents } from '../../../constants/eventsConstants'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; -const getFlatRowTree: GridRowGroupingPreProcessing = (params) => { +const getFlatRowTree: GridRowGroupingPreProcessing = ({ ids, idRowsLookup }) => { const tree: GridRowTreeConfig = {}; - params.ids.forEach((rowId) => { + for (let i = 0; i < ids.length; i += 1) { + const rowId = ids[i]; tree[rowId] = { id: rowId, depth: 0, parent: null, groupingValue: '' }; - }); + } return { tree, treeDepth: 1, - idRowsLookup: params.idRowsLookup, - ids: params.ids, + idRowsLookup, + ids, }; }; diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 63b07dc935cf9..73e08980bec8c 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -172,9 +172,10 @@ export const useGridFilter = ( if (!filteringMethod) { const visibleDescendantsCountLookup: Record = {}; if (shouldApplyTreeFiltering) { - rowIds.forEach((rowId) => { + for (let i = 0; i < rowIds.length; i += 1) { + const rowId = rowIds[i]; visibleDescendantsCountLookup[rowId] = rowTree[rowId].descendantCount ?? 0; - }); + } } return { diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index a637d209a530a..1f42f498e369c 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -37,10 +37,10 @@ interface GridRowsInternalCacheState { * The rows as they were the last time all the rows have been updated at once * It is used to avoid processing several time the same set of rows */ - rowsBeforeUpdate: GridRowsProp; + rowsBeforePartialUpdates: GridRowsProp; } -export interface GridRowsInternalCache { +interface GridRowsInternalCache { state: GridRowsInternalCacheState; timeout: NodeJS.Timeout | null; lastUpdateMs: number; @@ -62,21 +62,20 @@ interface ConvertGridRowsPropToStateParams { rows?: GridRowsProp; } -export function convertGridRowsPropToState({ +const convertGridRowsPropToState = ({ prevState, - rows: inputRows, + rows, props: inputProps, -}: ConvertGridRowsPropToStateParams): GridRowsInternalCacheState { +}: ConvertGridRowsPropToStateParams): GridRowsInternalCacheState => { const props = inputProps ?? prevState.props; let value: GridRowGroupParams; - if (inputRows) { + if (rows) { value = { idRowsLookup: {}, ids: [], }; - - inputRows.forEach((rowData) => { + rows.forEach((rowData) => { const id = getGridRowId(rowData, props.getRowId); value.idRowsLookup[id] = rowData; value.ids.push(id); @@ -88,9 +87,9 @@ export function convertGridRowsPropToState({ return { value, props, - rowsBeforeUpdate: inputRows ?? prevState.rowsBeforeUpdate, + rowsBeforePartialUpdates: rows ?? prevState.rowsBeforePartialUpdates, }; -} +}; const getRowsStateFromCache = ( rowsCache: GridRowsInternalCache, @@ -125,7 +124,7 @@ const INITIAL_GRID_ROWS_INTERNAL_CACHE: GridRowsInternalCache = { rowCount: undefined, getRowId: undefined, }, - rowsBeforeUpdate: [], + rowsBeforePartialUpdates: [], }, timeout: null, lastUpdateMs: 0, @@ -133,7 +132,7 @@ const INITIAL_GRID_ROWS_INTERNAL_CACHE: GridRowsInternalCache = { /** * @requires useGridRowGroupsPreProcessing (method) - * @requires useGridSorting (method) - can be after, async only (TODO: Remove after moving the 2 methods to useGridSorting) + * @requires useGridSorting (method) - can be after, async only (TODO: Remove after moving the `getRowIndex` and `getRowIdFromIndex` to `useGridSorting`) */ export const useGridRows = ( apiRef: GridApiRef, @@ -349,7 +348,7 @@ export const useGridRows = ( } // The new rows have already been applied (most likely in the `GridEvents.rowGroupsPreProcessingChange` listener) - if (rowsCache.current.state.rowsBeforeUpdate === props.rows) { + if (rowsCache.current.state.rowsBeforePartialUpdates === props.rows) { return; } @@ -368,7 +367,7 @@ export const useGridRows = ( logger.info(`Row grouping pre-processing have changed, regenerating the row tree`); let rows: GridRowsProp | undefined; - if (rowsCache.current.state.rowsBeforeUpdate === props.rows) { + if (rowsCache.current.state.rowsBeforePartialUpdates === props.rows) { // The `props.rows` has not changed since the last row grouping // We can keep the potential updates stored in `inputRowsAfterUpdates` on the new grouping rows = undefined; diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index f4094b7d0178e..4bb5f4b8a2328 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -189,7 +189,8 @@ export const useGridSorting = ( // Group the rows by parent const groupedByParentRows = new Map([[null, []]]); - rowIds.forEach((rowId) => { + for (let i = 0; i < rowIds.length; i += 1) { + const rowId = rowIds[i]; const node = rowTree[rowId]; const isExpanded = node.parent == null || rowTree[node.parent].expanded; @@ -201,7 +202,7 @@ export const useGridSorting = ( } group.push(node); } - }); + } // Apply the sorting to each list of children const sortedGroupedByParentRows = new Map(); From cee1ccaea39377cf40d471ba3aafab745ebc02dc Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 25 Oct 2021 15:28:26 +0200 Subject: [PATCH 327/390] Fix --- docs/pages/playground.tsx | 17 +++-------------- .../grid/hooks/features/filter/useGridFilter.ts | 11 +++++++---- .../grid/hooks/features/rows/useGridRows.ts | 9 +++++---- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/docs/pages/playground.tsx b/docs/pages/playground.tsx index d0a474eeafb38..8d573e70e7b82 100644 --- a/docs/pages/playground.tsx +++ b/docs/pages/playground.tsx @@ -1,16 +1,5 @@ import * as React from 'react'; -import { useDemoData } from '@mui/x-data-grid-generator'; -import { DataGridPro } from '@mui/x-data-grid-pro'; -export default () => { - const { data } = useDemoData({ - dataSet: 'Commodity', - rowLength: 100000, - }); - - return ( -
- -
- ); -}; +export default function Playground() { + return
This file is listed in `.gitignore`
; +} diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 73e08980bec8c..4b26d15da9d83 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -243,15 +243,18 @@ export const useGridFilter = ( return shouldBeVisible; }; - Object.values(rowTree).forEach((node) => { + const nodes = Object.values(rowTree); + for (let i = 0; i < nodes.length; i += 1) { + const node = nodes[i]; if (node.depth === 0) { filterTreeNode(node, true); } - }); + } } else { - rowIds.forEach((rowId) => { + for (let i = 0; i < rowIds.length; i += 1) { + const rowId = rowIds[i]; visibleRowsLookup[rowId] = filteringMethod(rowId); - }); + } } return { diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 1f42f498e369c..b0111d5a4ad5b 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -75,11 +75,12 @@ const convertGridRowsPropToState = ({ idRowsLookup: {}, ids: [], }; - rows.forEach((rowData) => { - const id = getGridRowId(rowData, props.getRowId); - value.idRowsLookup[id] = rowData; + for (let i = 0; i < rows.length; i += 1) { + const row = rows[i]; + const id = getGridRowId(row, props.getRowId); + value.idRowsLookup[id] = row; value.ids.push(id); - }); + } } else { value = prevState.value; } From a1c3a8701ccb5448a5da16f182e5023aaf49215f Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 25 Oct 2021 17:00:59 +0200 Subject: [PATCH 328/390] Rename methods --- docs/pages/api-docs/data-grid/grid-api.md | 164 +++++++++--------- .../api-docs/data-grid/grid-filter-api.json | 24 +-- .../panel/filterPanel/GridFilterPanel.tsx | 8 +- .../hooks/features/filter/useGridFilter.ts | 36 ++-- .../grid/models/api/gridFilterApi.ts | 8 +- 5 files changed, 120 insertions(+), 120 deletions(-) diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index 77e3ed8674b11..6a47db3241f7c 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -10,85 +10,85 @@ import { GridApi } from '@mui/x-data-grid-pro'; ## Properties -| Name | Type | Description | -| :----------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| applyFilterLinkOperator | (operator: GridLinkOperator) => void | Changes the GridLinkOperator used to connect the filters. | -| applyFilters | () => void | Applies all filters on all rows. | -| applySorting | () => void | Applies the current sort model to the rows. | -| commitCellChange | (params: GridCommitCellChangeParams, event?: any) => boolean | Updates the field at the given id with the value stored in the edit row model. | -| commitRowChange | (id: GridRowId, event?: any) => boolean | Updates the row at the given id with the values stored in the edit row model. | -| deleteFilter | (item: GridFilterItem) => void | Deletes a GridFilterItem. | -| exportDataAsCsv | (options?: GridCsvExportOptions) => void | Downloads and exports a CSV of the grid's data. | -| exportDataAsPrint | (options?: GridPrintExportOptions) => void | Print the grid's data. | -| forceUpdate | Dispatch<any> | Forces the grid to rerender. It's often used after a state update. | -| getAllColumns | () => GridStateColDef[] | Returns an array of [GridColDef](/api/data-grid/grid-col-def/) containing all the column definitions. | -| getAllRowIds | () => GridRowId[] | Gets the list of row ids. | -| getCellElement | (id: GridRowId, field: string) => null \| HTMLDivElement | Gets the underlying DOM element for a cell at the given `id` and `field`. | -| getCellMode | (id: GridRowId, field: string) => GridCellMode | Gets the mode of a cell. | -| getCellParams | (id: GridRowId, field: string) => GridCellParams<any, any, any> | Gets the [GridCellParams](/api/data-grid/grid-cell-params/) object that is passed as argument in events. | -| getCellValue | (id: GridRowId, field: string) => GridCellValue | Gets the value of a cell at the given `id` and `field`. | -| getColumn | (field: string) => GridStateColDef | Returns the [GridColDef](/api/data-grid/grid-col-def/) for the given `field`. | -| getColumnHeaderElement | (field: string) => null \| HTMLDivElement | Gets the underlying DOM element for the column header with the given `field`. | -| getColumnHeaderParams | (field: string) => GridColumnHeaderParams | Gets the GridColumnHeaderParams object that is passed as argument in events. | -| getColumnIndex | (field: string, useVisibleColumns?: boolean) => number | Returns the index position of a column. By default, only the visible columns are considered.
Pass `false` to `useVisibleColumns` to consider all columns. | -| getColumnPosition | (field: string) => number | Returns the left-position of a column relative to the inner border of the grid. | -| getColumnsMeta | () => GridColumnsMeta | Returns the GridColumnsMeta for each column. | -| getDataAsCsv | (options?: GridCsvExportOptions) => string | Returns the grid data as a CSV string.
This method is used internally by `exportDataAsCsv`. | -| getEditRowsModel | () => GridEditRowsModel | Gets the edit rows model of the grid. | -| getLocaleText | <T extends keyof GridLocaleText>(key: T) => GridLocaleText[T] | Returns the translation for the `key`. | -| getRow | (id: GridRowId) => null \| { [key: string]: any } | Gets the row data with a given id. | -| getRowElement | (id: GridRowId) => null \| HTMLDivElement | Gets the underlying DOM element for a row at the given `id`. | -| getRowIdFromRowIndex | (index: number) => GridRowId | Gets the `GridRowId` of a row at a specific index. | -| getRowIndex | (id: GridRowId) => number | Gets the row index of a row with a given id. | -| getRowMode | (id: GridRowId) => GridRowMode | Gets the mode of a row. | -| getRowModels | () => Map<GridRowId, { [key: string]: any }> | Gets the full set of rows as Map<GridRowId, GridRowModel>. | -| getRowParams | (id: GridRowId) => GridRowParams<{ [key: string]: any }> | Gets the [GridRowParams](/api/data-grid/grid-row-params/) object that is passed as argument in events. | -| getRowsCount | () => number | Gets the total number of rows in the grid. | -| getScrollPosition | () => GridScrollParams | Returns the current scroll position. | -| getSelectedRows | () => Map<GridRowId, { [key: string]: any }> | Returns an array of the selected rows. | -| getSortedRowIds | () => GridRowId[] | Returns all row ids sorted according to the active sort model. | -| getSortedRows | () => { [key: string]: any }[] | Returns all rows sorted according to the active sort model. | -| getSortModel | () => GridSortModel | Returns the sort model currently applied to the grid. | -| getVisibleColumns | () => GridStateColDef[] | Returns the currently visible columns. | -| getVisibleRowModels | () => Map<GridRowId, { [key: string]: any }> | Returns a sorted `Map` containing only the visible rows. | -| hideColumnMenu | () => void | Hides the column menu that is open. | -| hideFilterPanel | () => void | Hides the filter panel. | -| hidePreferences | () => void | Hides the preferences panel. | -| isCellEditable | (params: GridCellParams<any, any, any>) => boolean | Controls if a cell is editable. | -| isRowSelected | (id: GridRowId) => boolean | Determines if a row is selected or not. | -| publishEvent | (name: string, params?: any, event?: MuiEvent<MouseEvent \| UIEvent \| Event \| SyntheticEvent<Element, Event> \| TouchEvent \| ErrorEvent \| ProgressEvent<EventTarget> \| ClipboardEvent \| AnimationEvent \| InputEvent \| FocusEvent \| CompositionEvent \| DragEvent \| FormDataEvent \| PointerEvent \| KeyboardEvent \| SecurityPolicyViolationEvent \| TransitionEvent \| WheelEvent>) => void | Emits an event. | -| resize | () => void | Triggers a resize of the component and recalculation of width and height. | -| scroll | (params: Partial<GridScrollParams>) => void | Triggers the viewport to scroll to the given positions (in pixels). | -| scrollToIndexes | (params: Partial<GridCellIndexCoordinates>) => boolean | Triggers the viewport to scroll to the cell at indexes given by `params`.
Returns `true` if the grid had to scroll to reach the target. | -| selectRow | (id: GridRowId, isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of a row. | -| selectRowRange | (range: { endId: GridRowId; startId: GridRowId }, isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of all the selectable rows in a range. | -| selectRows | (ids: GridRowId[], isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of multiple rows. | -| setCellFocus | (id: GridRowId, field: string) => void | Sets the focus to the cell at the given `id` and `field`. | -| setCellMode | (id: GridRowId, field: string, mode: GridCellMode) => void | Sets the mode of a cell. | -| setColumnHeaderFocus | (field: string, event?: SyntheticEvent<Element, Event>) => void | Sets the focus to the column header at the given `field`. | -| setColumnIndex | (field: string, targetIndexPosition: number) => void | Moves a column from its original position to the position given by `targetIndexPosition`. | -| setColumnVisibility | (field: string, isVisible: boolean) => void | Changes the visibility of the column referred by `field`. | -| setColumnWidth | (field: string, width: number) => void | Updates the width of a column. | -| setDensity | (density: GridDensity, headerHeight?: number, rowHeight?: number) => void | Sets the density of the grid. | -| setEditCellValue | (params: GridEditCellValueParams, event?: SyntheticEvent<Element, Event>) => void | Sets the value of the edit cell.
Commonly used inside the edit cell component. | -| setEditRowsModel | (model: GridEditRowsModel) => void | Set the edit rows model of the grid. | -| setFilterModel | (model: GridFilterModel) => void | Sets the filter model to the one given by `model`. | -| setPage | (page: number) => void | Sets the displayed page to the value given by `page`. | -| setPageSize | (pageSize: number) => void | Sets the number of displayed rows to the value given by `pageSize`. | -| setRowMode | (id: GridRowId, mode: GridRowMode) => void | Sets the mode of a row. | -| setRows | (rows: { [key: string]: any }[]) => void | Sets a new set of rows. | -| setSelectionModel | (rowIds: GridRowId[]) => void | Updates the selected rows to be those passed to the `rowIds` argument.
Any row already selected will be unselected. | -| setSortModel | (model: GridSortModel) => void | Updates the sort model and triggers the sorting of rows. | -| setState | (state: GridState \| ((previousState: GridState) => GridState)) => void | Sets the whole state of the grid. | -| showColumnMenu | (field: string) => void | Display the column menu under the `field` column. | -| showError | (props: any) => void | Displays the error overlay component. | -| showFilterPanel | (targetColumnField?: string) => void | Shows the filter panel. If `targetColumnField` is given, a filter for this field is also added. | -| showPreferences | (newValue: GridPreferencePanelsValue) => void | Displays the preferences panel. The `newValue` argument controls the content of the panel. | -| sortColumn | (column: GridColDef, direction?: GridSortDirection, allowMultipleSorting?: boolean) => void | Sorts a column. | -| state | GridState | Property that contains the whole state of the grid. | -| subscribeEvent | <Params, Event extends MuiEvent<BaseEvent>>(event: string, handler: GridListener<Params, Event>, options?: GridSubscribeEventOptions) => () => void | Registers a handler for an event. | -| toggleColumnMenu | (field: string) => void | Toggles the column menu under the `field` column. | -| updateColumn | (col: GridColDef) => void | Updates the definition of a column. | -| updateColumns | (cols: GridColDef[]) => void | Updates the definition of multiple columns at the same time. | -| updateRows | (updates: GridRowModelUpdate<>[]) => void | Allows to updates, insert and delete rows in a single call. | -| upsertFilter | (item: GridFilterItem) => void | Updates or inserts a GridFilterItem. | +| Name | Type | Description | +| :---------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| applySorting | () => void | Applies the current sort model to the rows. | +| commitCellChange | (params: GridCommitCellChangeParams, event?: any) => boolean | Updates the field at the given id with the value stored in the edit row model. | +| commitRowChange | (id: GridRowId, event?: any) => boolean | Updates the row at the given id with the values stored in the edit row model. | +| deleteFilterItem | (item: GridFilterItem) => void | Deletes a GridFilterItem. | +| exportDataAsCsv | (options?: GridCsvExportOptions) => void | Downloads and exports a CSV of the grid's data. | +| exportDataAsPrint | (options?: GridPrintExportOptions) => void | Print the grid's data. | +| forceUpdate | Dispatch<any> | Forces the grid to rerender. It's often used after a state update. | +| getAllColumns | () => GridStateColDef[] | Returns an array of [GridColDef](/api/data-grid/grid-col-def/) containing all the column definitions. | +| getAllRowIds | () => GridRowId[] | Gets the list of row ids. | +| getCellElement | (id: GridRowId, field: string) => null \| HTMLDivElement | Gets the underlying DOM element for a cell at the given `id` and `field`. | +| getCellMode | (id: GridRowId, field: string) => GridCellMode | Gets the mode of a cell. | +| getCellParams | (id: GridRowId, field: string) => GridCellParams<any, any, any> | Gets the [GridCellParams](/api/data-grid/grid-cell-params/) object that is passed as argument in events. | +| getCellValue | (id: GridRowId, field: string) => GridCellValue | Gets the value of a cell at the given `id` and `field`. | +| getColumn | (field: string) => GridStateColDef | Returns the [GridColDef](/api/data-grid/grid-col-def/) for the given `field`. | +| getColumnHeaderElement | (field: string) => null \| HTMLDivElement | Gets the underlying DOM element for the column header with the given `field`. | +| getColumnHeaderParams | (field: string) => GridColumnHeaderParams | Gets the GridColumnHeaderParams object that is passed as argument in events. | +| getColumnIndex | (field: string, useVisibleColumns?: boolean) => number | Returns the index position of a column. By default, only the visible columns are considered.
Pass `false` to `useVisibleColumns` to consider all columns. | +| getColumnPosition | (field: string) => number | Returns the left-position of a column relative to the inner border of the grid. | +| getColumnsMeta | () => GridColumnsMeta | Returns the GridColumnsMeta for each column. | +| getDataAsCsv | (options?: GridCsvExportOptions) => string | Returns the grid data as a CSV string.
This method is used internally by `exportDataAsCsv`. | +| getEditRowsModel | () => GridEditRowsModel | Gets the edit rows model of the grid. | +| getLocaleText | <T extends keyof GridLocaleText>(key: T) => GridLocaleText[T] | Returns the translation for the `key`. | +| getRow | (id: GridRowId) => null \| { [key: string]: any } | Gets the row data with a given id. | +| getRowElement | (id: GridRowId) => null \| HTMLDivElement | Gets the underlying DOM element for a row at the given `id`. | +| getRowIdFromRowIndex | (index: number) => GridRowId | Gets the `GridRowId` of a row at a specific index. | +| getRowIndex | (id: GridRowId) => number | Gets the row index of a row with a given id. | +| getRowMode | (id: GridRowId) => GridRowMode | Gets the mode of a row. | +| getRowModels | () => Map<GridRowId, { [key: string]: any }> | Gets the full set of rows as Map<GridRowId, GridRowModel>. | +| getRowParams | (id: GridRowId) => GridRowParams<{ [key: string]: any }> | Gets the [GridRowParams](/api/data-grid/grid-row-params/) object that is passed as argument in events. | +| getRowsCount | () => number | Gets the total number of rows in the grid. | +| getScrollPosition | () => GridScrollParams | Returns the current scroll position. | +| getSelectedRows | () => Map<GridRowId, { [key: string]: any }> | Returns an array of the selected rows. | +| getSortedRowIds | () => GridRowId[] | Returns all row ids sorted according to the active sort model. | +| getSortedRows | () => { [key: string]: any }[] | Returns all rows sorted according to the active sort model. | +| getSortModel | () => GridSortModel | Returns the sort model currently applied to the grid. | +| getVisibleColumns | () => GridStateColDef[] | Returns the currently visible columns. | +| getVisibleRowModels | () => Map<GridRowId, { [key: string]: any }> | Returns a sorted `Map` containing only the visible rows. | +| hideColumnMenu | () => void | Hides the column menu that is open. | +| hideFilterPanel | () => void | Hides the filter panel. | +| hidePreferences | () => void | Hides the preferences panel. | +| isCellEditable | (params: GridCellParams<any, any, any>) => boolean | Controls if a cell is editable. | +| isRowSelected | (id: GridRowId) => boolean | Determines if a row is selected or not. | +| publishEvent | (name: string, params?: any, event?: MuiEvent<MouseEvent \| UIEvent \| Event \| SyntheticEvent<Element, Event> \| TouchEvent \| ErrorEvent \| ProgressEvent<EventTarget> \| ClipboardEvent \| AnimationEvent \| InputEvent \| FocusEvent \| CompositionEvent \| DragEvent \| FormDataEvent \| PointerEvent \| KeyboardEvent \| SecurityPolicyViolationEvent \| TransitionEvent \| WheelEvent>) => void | Emits an event. | +| resize | () => void | Triggers a resize of the component and recalculation of width and height. | +| scroll | (params: Partial<GridScrollParams>) => void | Triggers the viewport to scroll to the given positions (in pixels). | +| scrollToIndexes | (params: Partial<GridCellIndexCoordinates>) => boolean | Triggers the viewport to scroll to the cell at indexes given by `params`.
Returns `true` if the grid had to scroll to reach the target. | +| selectRow | (id: GridRowId, isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of a row. | +| selectRowRange | (range: { endId: GridRowId; startId: GridRowId }, isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of all the selectable rows in a range. | +| selectRows | (ids: GridRowId[], isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of multiple rows. | +| setCellFocus | (id: GridRowId, field: string) => void | Sets the focus to the cell at the given `id` and `field`. | +| setCellMode | (id: GridRowId, field: string, mode: GridCellMode) => void | Sets the mode of a cell. | +| setColumnHeaderFocus | (field: string, event?: SyntheticEvent<Element, Event>) => void | Sets the focus to the column header at the given `field`. | +| setColumnIndex | (field: string, targetIndexPosition: number) => void | Moves a column from its original position to the position given by `targetIndexPosition`. | +| setColumnVisibility | (field: string, isVisible: boolean) => void | Changes the visibility of the column referred by `field`. | +| setColumnWidth | (field: string, width: number) => void | Updates the width of a column. | +| setDensity | (density: GridDensity, headerHeight?: number, rowHeight?: number) => void | Sets the density of the grid. | +| setEditCellValue | (params: GridEditCellValueParams, event?: SyntheticEvent<Element, Event>) => void | Sets the value of the edit cell.
Commonly used inside the edit cell component. | +| setEditRowsModel | (model: GridEditRowsModel) => void | Set the edit rows model of the grid. | +| setFilterLinkOperator | (operator: GridLinkOperator) => void | Changes the GridLinkOperator used to connect the filters. | +| setFilterModel | (model: GridFilterModel) => void | Sets the filter model to the one given by `model`. | +| setPage | (page: number) => void | Sets the displayed page to the value given by `page`. | +| setPageSize | (pageSize: number) => void | Sets the number of displayed rows to the value given by `pageSize`. | +| setRowMode | (id: GridRowId, mode: GridRowMode) => void | Sets the mode of a row. | +| setRows | (rows: { [key: string]: any }[]) => void | Sets a new set of rows. | +| setSelectionModel | (rowIds: GridRowId[]) => void | Updates the selected rows to be those passed to the `rowIds` argument.
Any row already selected will be unselected. | +| setSortModel | (model: GridSortModel) => void | Updates the sort model and triggers the sorting of rows. | +| setState | (state: GridState \| ((previousState: GridState) => GridState)) => void | Sets the whole state of the grid. | +| showColumnMenu | (field: string) => void | Display the column menu under the `field` column. | +| showError | (props: any) => void | Displays the error overlay component. | +| showFilterPanel | (targetColumnField?: string) => void | Shows the filter panel. If `targetColumnField` is given, a filter for this field is also added. | +| showPreferences | (newValue: GridPreferencePanelsValue) => void | Displays the preferences panel. The `newValue` argument controls the content of the panel. | +| sortColumn | (column: GridColDef, direction?: GridSortDirection, allowMultipleSorting?: boolean) => void | Sorts a column. | +| state | GridState | Property that contains the whole state of the grid. | +| subscribeEvent | <Params, Event extends MuiEvent<BaseEvent>>(event: string, handler: GridListener<Params, Event>, options?: GridSubscribeEventOptions) => () => void | Registers a handler for an event. | +| toggleColumnMenu | (field: string) => void | Toggles the column menu under the `field` column. | +| unsafe_applyFilters | () => void | Applies all filters on all rows. | +| updateColumn | (col: GridColDef) => void | Updates the definition of a column. | +| updateColumns | (cols: GridColDef[]) => void | Updates the definition of multiple columns at the same time. | +| updateRows | (updates: GridRowModelUpdate<>[]) => void | Allows to updates, insert and delete rows in a single call. | +| upsertFilterItem | (item: GridFilterItem) => void | Updates or inserts a GridFilterItem. | diff --git a/docs/pages/api-docs/data-grid/grid-filter-api.json b/docs/pages/api-docs/data-grid/grid-filter-api.json index 4da5f74605c6c..358a73fbc7347 100644 --- a/docs/pages/api-docs/data-grid/grid-filter-api.json +++ b/docs/pages/api-docs/data-grid/grid-filter-api.json @@ -3,17 +3,7 @@ "description": "The filter API interface that is available in the grid apiRef.", "properties": [ { - "name": "applyFilterLinkOperator", - "description": "Changes the GridLinkOperator used to connect the filters.", - "type": "(operator: GridLinkOperator) => void" - }, - { - "name": "applyFilters", - "description": "Applies all filters on all rows.", - "type": "() => void" - }, - { - "name": "deleteFilter", + "name": "deleteFilterItem", "description": "Deletes a GridFilterItem.", "type": "(item: GridFilterItem) => void" }, @@ -23,6 +13,11 @@ "type": "() => Map" }, { "name": "hideFilterPanel", "description": "Hides the filter panel.", "type": "() => void" }, + { + "name": "setFilterLinkOperator", + "description": "Changes the GridLinkOperator used to connect the filters.", + "type": "(operator: GridLinkOperator) => void" + }, { "name": "setFilterModel", "description": "Sets the filter model to the one given by model.", @@ -34,7 +29,12 @@ "type": "(targetColumnField?: string) => void" }, { - "name": "upsertFilter", + "name": "unsafe_applyFilters", + "description": "Applies all filters on all rows.", + "type": "() => void" + }, + { + "name": "upsertFilterItem", "description": "Updates or inserts a GridFilterItem.", "type": "(item: GridFilterItem) => void" } diff --git a/packages/grid/_modules_/grid/components/panel/filterPanel/GridFilterPanel.tsx b/packages/grid/_modules_/grid/components/panel/filterPanel/GridFilterPanel.tsx index 003e4e958a34b..329d73608c5cc 100644 --- a/packages/grid/_modules_/grid/components/panel/filterPanel/GridFilterPanel.tsx +++ b/packages/grid/_modules_/grid/components/panel/filterPanel/GridFilterPanel.tsx @@ -20,25 +20,25 @@ export function GridFilterPanel() { const applyFilter = React.useCallback( (item: GridFilterItem) => { - apiRef.current.upsertFilter(item); + apiRef.current.upsertFilterItem(item); }, [apiRef], ); const applyFilterLinkOperator = React.useCallback( (operator: GridLinkOperator) => { - apiRef.current.applyFilterLinkOperator(operator); + apiRef.current.setFilterLinkOperator(operator); }, [apiRef], ); const addNewFilter = React.useCallback(() => { - apiRef.current.upsertFilter({}); + apiRef.current.upsertFilterItem({}); }, [apiRef]); const deleteFilter = React.useCallback( (item: GridFilterItem) => { - apiRef.current.deleteFilter(item); + apiRef.current.deleteFilterItem(item); }, [apiRef], ); diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 809147c05a5a2..4c2a5c0e65168 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -148,7 +148,7 @@ export const useGridFilter = ( [apiRef], ); - const applyFilters = React.useCallback(() => { + const applyFilters = React.useCallback(() => { setGridState((state) => { const filterModel = gridFilterModelSelector(state); @@ -197,7 +197,7 @@ export const useGridFilter = ( forceUpdate(); }, [apiRef, setGridState, forceUpdate, props.filterMode, buildAggregatedFilterApplier]); - const upsertFilter = React.useCallback( + const upsertFilterItem = React.useCallback( (item) => { logger.debug('Upserting filter'); @@ -237,12 +237,12 @@ export const useGridFilter = ( filter: { ...state.filter, filterModel: { ...state.filter.filterModel, items } }, }; }); - apiRef.current.applyFilters(); + apiRef.current.unsafe_applyFilters(); }, [apiRef, logger, setGridState, props.disableMultipleColumnsFiltering], ); - const deleteFilter = React.useCallback( + const deleteFilterItem = React.useCallback( (item) => { logger.debug(`Deleting filter on column ${item.columnField} with value ${item.value}`); setGridState((state) => { @@ -256,9 +256,9 @@ export const useGridFilter = ( }; }); if (gridFilterModelSelector(apiRef.current.state).items.length === 0) { - apiRef.current.upsertFilter({}); + apiRef.current.upsertFilterItem({}); } - apiRef.current.applyFilters(); + apiRef.current.unsafe_applyFilters(); }, [apiRef, logger, setGridState], ); @@ -272,7 +272,7 @@ export const useGridFilter = ( const lastFilter = filterModel.items.length > 0 ? filterModel.items[filterModel.items.length - 1] : null; if (!lastFilter || lastFilter.columnField !== targetColumnField) { - apiRef.current.upsertFilter({ columnField: targetColumnField }); + apiRef.current.upsertFilterItem({ columnField: targetColumnField }); } } apiRef.current.showPreferences(GridPreferencePanelsValue.filters); @@ -285,14 +285,14 @@ export const useGridFilter = ( apiRef.current.hidePreferences(); }, [apiRef, logger]); - const applyFilterLinkOperator = React.useCallback( + const setFilterLinkOperator = React.useCallback( (linkOperator) => { logger.debug('Applying filter link operator'); setGridState((state) => ({ ...state, filter: { ...state.filter, filterModel: { ...state.filter.filterModel, linkOperator } }, })); - apiRef.current.applyFilters(); + apiRef.current.unsafe_applyFilters(); }, [apiRef, logger, setGridState], ); @@ -311,7 +311,7 @@ export const useGridFilter = ( filterModel: model, }, })); - apiRef.current.applyFilters(); + apiRef.current.unsafe_applyFilters(); } }, [apiRef, logger, setGridState], @@ -325,10 +325,10 @@ export const useGridFilter = ( useGridApiMethod( apiRef, { - applyFilterLinkOperator, - applyFilters, - deleteFilter, - upsertFilter, + setFilterLinkOperator, + unsafe_applyFilters: applyFilters, + deleteFilterItem, + upsertFilterItem, setFilterModel, showFilterPanel, hideFilterPanel, @@ -345,10 +345,10 @@ export const useGridFilter = ( filterModel.items.forEach((filter) => { if (!columnsIds.find((field) => field === filter.columnField)) { - apiRef.current.deleteFilter(filter); + apiRef.current.deleteFilterItem(filter); } }); - apiRef.current.applyFilters(); + apiRef.current.unsafe_applyFilters(); }, [apiRef, logger]); React.useEffect(() => { @@ -357,8 +357,8 @@ export const useGridFilter = ( } }, [apiRef, logger, props.filterModel]); - useFirstRender(() => apiRef.current.applyFilters()); + useFirstRender(() => apiRef.current.unsafe_applyFilters()); - useGridApiEventHandler(apiRef, GridEvents.rowsSet, apiRef.current.applyFilters); + useGridApiEventHandler(apiRef, GridEvents.rowsSet, apiRef.current.unsafe_applyFilters); useGridApiEventHandler(apiRef, GridEvents.columnsChange, onColUpdated); }; diff --git a/packages/grid/_modules_/grid/models/api/gridFilterApi.ts b/packages/grid/_modules_/grid/models/api/gridFilterApi.ts index 8de7d25c0f50d..c1fbf0cc18878 100644 --- a/packages/grid/_modules_/grid/models/api/gridFilterApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridFilterApi.ts @@ -19,21 +19,21 @@ export interface GridFilterApi { * Updates or inserts a [[GridFilterItem]]. * @param {GridFilterItem} item The filter to update. */ - upsertFilter: (item: GridFilterItem) => void; + upsertFilterItem: (item: GridFilterItem) => void; /** * Applies all filters on all rows. */ - applyFilters: () => void; + unsafe_applyFilters: () => void; /** * Deletes a [[GridFilterItem]]. * @param {GridFilterItem} item The filter to delete. */ - deleteFilter: (item: GridFilterItem) => void; + deleteFilterItem: (item: GridFilterItem) => void; /** * Changes the [[GridLinkOperator]] used to connect the filters. * @param {GridLinkOperator} operator The new link operator. It can be: `"and"` or `"or`". */ - applyFilterLinkOperator: (operator: GridLinkOperator) => void; + setFilterLinkOperator: (operator: GridLinkOperator) => void; /** * Sets the filter model to the one given by `model`. * @param {GridFilterModel} model The new filter model. From 38eb0d4fa71958ecc79585c2fd61056513618e16 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 25 Oct 2021 17:02:09 +0200 Subject: [PATCH 329/390] Fi x --- packages/grid/_modules_/grid/models/api/gridFilterApi.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/grid/_modules_/grid/models/api/gridFilterApi.ts b/packages/grid/_modules_/grid/models/api/gridFilterApi.ts index c1fbf0cc18878..3f004980bf87a 100644 --- a/packages/grid/_modules_/grid/models/api/gridFilterApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridFilterApi.ts @@ -18,6 +18,7 @@ export interface GridFilterApi { /** * Updates or inserts a [[GridFilterItem]]. * @param {GridFilterItem} item The filter to update. + * @ignore - do not document. */ upsertFilterItem: (item: GridFilterItem) => void; /** From b879c72c6b26eedaa43e6d647f42c70ae92871ce Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 25 Oct 2021 17:02:34 +0200 Subject: [PATCH 330/390] Fix --- docs/pages/api-docs/data-grid/grid-api.md | 1 - docs/pages/api-docs/data-grid/grid-filter-api.json | 5 ----- 2 files changed, 6 deletions(-) diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index 6a47db3241f7c..5da476e3f1956 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -91,4 +91,3 @@ import { GridApi } from '@mui/x-data-grid-pro'; | updateColumn | (col: GridColDef) => void | Updates the definition of a column. | | updateColumns | (cols: GridColDef[]) => void | Updates the definition of multiple columns at the same time. | | updateRows | (updates: GridRowModelUpdate<>[]) => void | Allows to updates, insert and delete rows in a single call. | -| upsertFilterItem | (item: GridFilterItem) => void | Updates or inserts a GridFilterItem. | diff --git a/docs/pages/api-docs/data-grid/grid-filter-api.json b/docs/pages/api-docs/data-grid/grid-filter-api.json index 358a73fbc7347..f3873dc6ec752 100644 --- a/docs/pages/api-docs/data-grid/grid-filter-api.json +++ b/docs/pages/api-docs/data-grid/grid-filter-api.json @@ -32,11 +32,6 @@ "name": "unsafe_applyFilters", "description": "Applies all filters on all rows.", "type": "() => void" - }, - { - "name": "upsertFilterItem", - "description": "Updates or inserts a GridFilterItem.", - "type": "(item: GridFilterItem) => void" } ] } From b8efc85366d22dd492e138c9c93f7ccb04f5f0b7 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 25 Oct 2021 18:15:40 +0200 Subject: [PATCH 331/390] Empty From 784525a5879801177b30047798f0bafcc46a5488 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 10:01:10 +0200 Subject: [PATCH 332/390] Fix --- .../hooks/features/filter/useGridFilter.ts | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 4b26d15da9d83..1a1d4d5270c67 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -149,7 +149,7 @@ export const useGridFilter = ( [apiRef], ); - const applyFilters = React.useCallback(() => { + const applyFilters = React.useCallback(() => { setGridState((state) => { const filterModel = gridFilterModelSelector(state); const rowIds = gridRowIdsSelector(state); @@ -277,7 +277,7 @@ export const useGridFilter = ( props.disableChildrenFiltering, ]); - const upsertFilter = React.useCallback( + const upsertFilterItem = React.useCallback( (item) => { logger.debug('Upserting filter'); @@ -317,12 +317,12 @@ export const useGridFilter = ( filter: { ...state.filter, filterModel: { ...state.filter.filterModel, items } }, }; }); - apiRef.current.applyFilters(); + apiRef.current.unsafe_applyFilters(); }, [apiRef, logger, setGridState, props.disableMultipleColumnsFiltering], ); - const deleteFilter = React.useCallback( + const deleteFilterItem = React.useCallback( (item) => { logger.debug(`Deleting filter on column ${item.columnField} with value ${item.value}`); setGridState((state) => { @@ -336,9 +336,9 @@ export const useGridFilter = ( }; }); if (gridFilterModelSelector(apiRef.current.state).items.length === 0) { - apiRef.current.upsertFilter({}); + apiRef.current.upsertFilterItem({}); } - apiRef.current.applyFilters(); + apiRef.current.unsafe_applyFilters(); }, [apiRef, logger, setGridState], ); @@ -352,7 +352,7 @@ export const useGridFilter = ( const lastFilter = filterModel.items.length > 0 ? filterModel.items[filterModel.items.length - 1] : null; if (!lastFilter || lastFilter.columnField !== targetColumnField) { - apiRef.current.upsertFilter({ columnField: targetColumnField }); + apiRef.current.upsertFilterItem({ columnField: targetColumnField }); } } apiRef.current.showPreferences(GridPreferencePanelsValue.filters); @@ -365,14 +365,14 @@ export const useGridFilter = ( apiRef.current.hidePreferences(); }, [apiRef, logger]); - const applyFilterLinkOperator = React.useCallback( + const setFilterLinkOperator = React.useCallback( (linkOperator) => { logger.debug('Applying filter link operator'); setGridState((state) => ({ ...state, filter: { ...state.filter, filterModel: { ...state.filter.filterModel, linkOperator } }, })); - apiRef.current.applyFilters(); + apiRef.current.unsafe_applyFilters(); }, [apiRef, logger, setGridState], ); @@ -391,7 +391,7 @@ export const useGridFilter = ( filterModel: model, }, })); - apiRef.current.applyFilters(); + apiRef.current.unsafe_applyFilters(); } }, [apiRef, logger, setGridState], @@ -405,10 +405,10 @@ export const useGridFilter = ( useGridApiMethod( apiRef, { - applyFilterLinkOperator, - applyFilters, - deleteFilter, - upsertFilter, + setFilterLinkOperator, + unsafe_applyFilters: applyFilters, + deleteFilterItem, + upsertFilterItem, setFilterModel, showFilterPanel, hideFilterPanel, @@ -425,10 +425,10 @@ export const useGridFilter = ( filterModel.items.forEach((filter) => { if (!columnsIds.find((field) => field === filter.columnField)) { - apiRef.current.deleteFilter(filter); + apiRef.current.deleteFilterItem(filter); } }); - apiRef.current.applyFilters(); + apiRef.current.unsafe_applyFilters(); }, [apiRef, logger]); React.useEffect(() => { @@ -444,11 +444,11 @@ export const useGridFilter = ( isFirstRender.current = false; return; } - apiRef.current.applyFilters(); + apiRef.current.unsafe_applyFilters(); }, [apiRef, props.disableChildrenFiltering]); - useFirstRender(() => apiRef.current.applyFilters()); + useFirstRender(() => apiRef.current.unsafe_applyFilters()); - useGridApiEventHandler(apiRef, GridEvents.rowsSet, apiRef.current.applyFilters); + useGridApiEventHandler(apiRef, GridEvents.rowsSet, apiRef.current.unsafe_applyFilters); useGridApiEventHandler(apiRef, GridEvents.columnsChange, onColUpdated); }; From 13ae7d9e31cb72a9ab9cb7783061902e140df191 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 10:01:49 +0200 Subject: [PATCH 333/390] Fix --- .../grid/_modules_/grid/hooks/features/filter/useGridFilter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 1a1d4d5270c67..e493b398d7675 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -15,7 +15,7 @@ import { useGridState } from '../../utils/useGridState'; import { GridPreferencePanelsValue } from '../preferencesPanel/gridPreferencePanelsValue'; import { getDefaultGridFilterModel } from './gridFilterState'; import { GridFilterModel } from '../../../models/gridFilterModel'; -import { gridFilterModelSelector, gridVisibleSortedRowEntriesSelector } from './gridFilterSelector'; +import { gridVisibleSortedRowEntriesSelector, gridFilterModelSelector } from './gridFilterSelector'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; import { gridRowIdsSelector, gridRowTreeDepthSelector, gridRowTreeSelector } from '../rows'; From 913fabb5cd1cf7cdd246adeded3580153e8fd66b Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 10:40:29 +0200 Subject: [PATCH 334/390] Fix server filtering / sorting --- .../features/filter/gridFilterSelector.ts | 10 ++-- .../hooks/features/filter/useGridFilter.ts | 56 +++++++------------ .../hooks/features/sorting/useGridSorting.ts | 16 ++---- .../src/tests/treeData.DataGridPro.test.tsx | 7 +++ 4 files changed, 36 insertions(+), 53 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index c1d2745d0a093..4e83bae15007b 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -43,23 +43,23 @@ export const gridVisibleSortedTopLevelRowEntriesSelector = createSelector( gridVisibleSortedRowEntriesSelector, gridRowTreeSelector, gridRowTreeDepthSelector, - (sortedVisibleRows, rowTree, rowTreeDepth) => { + (visibleSortedRows, rowTree, rowTreeDepth) => { if (rowTreeDepth < 2) { - return sortedVisibleRows; + return visibleSortedRows; } - return sortedVisibleRows.filter((row) => rowTree[row.id]?.depth === 0); + return visibleSortedRows.filter((row) => rowTree[row.id]?.depth === 0); }, ); export const gridVisibleRowCountSelector = createSelector( gridVisibleSortedRowEntriesSelector, - (sortedVisibleRows) => sortedVisibleRows.length, + (visibleSortedRows) => visibleSortedRows.length, ); export const gridVisibleTopLevelRowCountSelector = createSelector( gridVisibleSortedTopLevelRowEntriesSelector, - (sortedVisibleTopLevelRows) => sortedVisibleTopLevelRows.length, + (visibleSortedTopLevelRows) => visibleSortedTopLevelRows.length, ); export const gridFilterActiveItemsSelector = createSelector( diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index e493b398d7675..434cdd28c621d 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -149,57 +149,41 @@ export const useGridFilter = ( [apiRef], ); + /** + * Generate the `visibleRowsLookup` and `visibleDescendantsCountLookup` for the current `filterModel` + * If the tree is not flat, we have to create the lookups even with "server" filtering or 0 filter item to remove to collapsed rows. + */ const applyFilters = React.useCallback(() => { setGridState((state) => { const filterModel = gridFilterModelSelector(state); const rowIds = gridRowIdsSelector(state); const rowTree = gridRowTreeSelector(state); const shouldApplyTreeFiltering = gridRowTreeDepthSelector(state) > 1; - - if (props.filterMode === GridFeatureModeConstant.server) { - return { - ...state, - filter: { - ...state.filter, - visibleRowsLookup: {}, - visibleDescendantsCountLookup: {}, - }, - }; - } - - // No filter to apply - const filteringMethod = buildAggregatedFilterApplier(filterModel); - if (!filteringMethod) { - const visibleDescendantsCountLookup: Record = {}; - if (shouldApplyTreeFiltering) { - for (let i = 0; i < rowIds.length; i += 1) { - const rowId = rowIds[i]; - visibleDescendantsCountLookup[rowId] = rowTree[rowId].descendantCount ?? 0; - } - } - - return { - ...state, - filter: { - ...state.filter, - visibleRowsLookup: {}, - visibleDescendantsCountLookup, - }, - }; - } + const filteringMethod = + props.filterMode === GridFeatureModeConstant.client + ? buildAggregatedFilterApplier(filterModel) + : null; const visibleRowsLookup: Record = {}; const visibleDescendantsCountLookup: Record = {}; if (shouldApplyTreeFiltering) { // A node is visible if // - One of its children is passing the filter - // - He is passing the filter + // - It is passing the filter const filterTreeNode = ( node: GridRowTreeNodeConfig, isParentMatchingFilters: boolean, ): boolean => { const shouldSkipFilters = props.disableChildrenFiltering && node.depth > 0; - const isMatchingFilters = shouldSkipFilters ? null : filteringMethod(node.id); + + let isMatchingFilters: boolean | null; + if (shouldSkipFilters) { + isMatchingFilters = null; + } else if (!filteringMethod) { + isMatchingFilters = true; + } else { + isMatchingFilters = filteringMethod(node.id); + } let visibleDescendantCount = 0; node.children?.forEach((childId) => { @@ -250,10 +234,10 @@ export const useGridFilter = ( filterTreeNode(node, true); } } - } else { + } else if (props.filterMode === GridFeatureModeConstant.client) { for (let i = 0; i < rowIds.length; i += 1) { const rowId = rowIds[i]; - visibleRowsLookup[rowId] = filteringMethod(rowId); + visibleRowsLookup[rowId] = filteringMethod?.(rowId) ?? true; } } diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 4bb5f4b8a2328..fa5d03b5ce5d6 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -172,20 +172,13 @@ export const useGridSorting = ( ); const applySorting = React.useCallback(() => { - if (props.sortingMode === GridFeatureModeConstant.server) { - logger.debug('Skipping sorting rows as sortingMode = server'); - setGridState((state) => ({ - ...state, - sorting: { ...state.sorting, sortedRows: apiRef.current.getAllRowIds() }, - })); - return; - } - const rowTree = gridRowTreeSelector(apiRef.current.state); const rowIds = gridRowIdsSelector(apiRef.current.state); const sortModel = gridSortModelSelector(apiRef.current.state); - const comparatorList = buildComparatorList(sortModel); - const aggregatedComparator = comparatorListAggregate(comparatorList); + + const shouldApplySorting = props.sortingMode === GridFeatureModeConstant.client; + const comparatorList = shouldApplySorting ? buildComparatorList(sortModel) : []; + const aggregatedComparator = comparatorListAggregate(buildComparatorList(sortModel)); // Group the rows by parent const groupedByParentRows = new Map([[null, []]]); @@ -263,7 +256,6 @@ export const useGridSorting = ( forceUpdate(); }, [ apiRef, - logger, getSortCellParams, setGridState, forceUpdate, diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index 81301e9c1bc72..ce96596022295 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -421,5 +421,12 @@ describe(' - Tree Data', () => { fireEvent.click(getCell(2, 0).querySelector('button')); expect(getColumnValues(1)).to.deep.equal(['C', 'B', 'A', 'A.A', 'A.B']); }); + + it('should update the order server side', () => { + const { setProps } = render(); + expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); + setProps({ rows: [...rowsWithoutGap].reverse() }); + expect(getColumnValues(1)).to.deep.equal(['C', 'B', 'A']); + }); }); }); From bd039aca0dae450052f10af25045b71dff040c18 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 11:23:57 +0200 Subject: [PATCH 335/390] Fix --- .../CustomGroupingColumnTreeData.js | 6 +-- .../CustomGroupingColumnTreeData.tsx | 4 +- .../cell/GridTreeDataGroupingCell.tsx | 10 ++-- .../features/filter/gridFilterSelector.ts | 4 +- .../hooks/features/filter/gridFilterState.ts | 7 +-- .../hooks/features/filter/useGridFilter.ts | 47 +++++++++-------- .../hooks/features/sorting/useGridSorting.ts | 30 ++++++----- .../treeData/gridTreeDataGroupColDef.tsx | 6 +-- .../src/tests/treeData.DataGridPro.test.tsx | 51 +++++++------------ 9 files changed, 78 insertions(+), 87 deletions(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js index f9d1dec4a32d7..cc5cd7858dfec 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js @@ -33,14 +33,14 @@ const CustomGridTreeDataGroupingCell = (props) => { return (
- {value.visibleDescendantCount > 0 ? ( + {value.filteredDescendantCount > 0 ? ( ) : ( @@ -66,7 +66,7 @@ CustomGridTreeDataGroupingCell.propTypes = { depth: PropTypes.number.isRequired, expanded: PropTypes.bool.isRequired, label: PropTypes.string.isRequired, - visibleDescendantCount: PropTypes.number.isRequired, + filteredDescendantCount: PropTypes.number.isRequired, }).isRequired, }; diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx index b8ada7d7d9452..600834a1315df 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx @@ -43,14 +43,14 @@ const CustomGridTreeDataGroupingCell = ( return (
- {value.visibleDescendantCount > 0 ? ( + {value.filteredDescendantCount > 0 ? ( ) : ( diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index ea63fcbc1d47a..24fcbec3f1636 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -24,7 +24,7 @@ const useStyles = makeStyles({ export interface GridTreeDataGroupingCellValue { label: string; - visibleDescendantCount: number; + filteredDescendantCount: number; depth: number; expanded: boolean; } @@ -58,7 +58,7 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams
- {value.visibleDescendantCount > 0 && ( + {value.filteredDescendantCount > 0 && ( {value.label} - {value.visibleDescendantCount > 0 ? ` (${value.visibleDescendantCount})` : ''} + {value.filteredDescendantCount > 0 ? ` (${value.filteredDescendantCount})` : ''} ); @@ -110,7 +110,7 @@ GridTreeDataGroupingCell.propTypes = { depth: PropTypes.number.isRequired, expanded: PropTypes.bool.isRequired, label: PropTypes.string.isRequired, - visibleDescendantCount: PropTypes.number.isRequired, + filteredDescendantCount: PropTypes.number.isRequired, }).isRequired, /** * Get the cell value of a row and field. @@ -161,7 +161,7 @@ GridTreeDataGroupingCell.propTypes = { depth: PropTypes.number.isRequired, expanded: PropTypes.bool.isRequired, label: PropTypes.string.isRequired, - visibleDescendantCount: PropTypes.number.isRequired, + filteredDescendantCount: PropTypes.number.isRequired, }).isRequired, } as any; diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index 4e83bae15007b..e08ee03575d3a 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -17,9 +17,9 @@ export const gridVisibleRowsLookupSelector = createSelector( (filterState) => filterState.visibleRowsLookup, ); -export const gridVisibleDescendantCountLookupSelector = createSelector( +export const gridFilteredDescendantCountLookupSelector = createSelector( gridFilterStateSelector, - (filterState) => filterState.visibleDescendantsCountLookup, + (filterState) => filterState.filteredDescendantCountLookup, ); export const gridVisibleSortedRowEntriesSelector = createSelector( diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts index 233a2bee3694a..2256038fd064e 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts @@ -12,17 +12,18 @@ export interface GridFilterState { /** * Visibility status for each row + * A row is visible if it is passing the filters AND if its parent is expanded. * If a row is not registered in this lookup, it is supposed to be visible */ visibleRowsLookup: Record; /** - * Amount of visible descendants for each row + * Amount of descendants that are passing the filters. * For the Tree Data, it includes all the intermediate depth levels (= amount of children + amount of grand children + ...) * For the Row Grouping by Column, it does not include the intermediate depth levels (= amount of descendant of maximum depth) - * If a row is not registered in this lookup, it is supposed to have no descendant. + * If a row is not registered in this lookup, it is supposed to have no descendant passing the filters. */ - visibleDescendantsCountLookup: Record; + filteredDescendantCountLookup: Record; } export interface GridFilterInitialState { diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 434cdd28c621d..ef6d1cc0b9a43 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -66,7 +66,7 @@ export const useGridFilter = ( props.initialState?.filter?.filterModel ?? getDefaultGridFilterModel(), visibleRowsLookup: {}, - visibleDescendantsCountLookup: {}, + filteredDescendantCountLookup: {}, }, }; }); @@ -165,7 +165,7 @@ export const useGridFilter = ( : null; const visibleRowsLookup: Record = {}; - const visibleDescendantsCountLookup: Record = {}; + const filteredDescendantCountLookup: Record = {}; if (shouldApplyTreeFiltering) { // A node is visible if // - One of its children is passing the filter @@ -173,7 +173,8 @@ export const useGridFilter = ( const filterTreeNode = ( node: GridRowTreeNodeConfig, isParentMatchingFilters: boolean, - ): boolean => { + isParentExpanded: boolean, + ): number => { const shouldSkipFilters = props.disableChildrenFiltering && node.depth > 0; let isMatchingFilters: boolean | null; @@ -185,59 +186,57 @@ export const useGridFilter = ( isMatchingFilters = filteringMethod(node.id); } - let visibleDescendantCount = 0; + let filteredDescendantCount = 0; node.children?.forEach((childId) => { const childNode = rowTree[childId]; - const isNodeVisible = filterTreeNode( + const childSubTreeSize = filterTreeNode( childNode, isMatchingFilters ?? isParentMatchingFilters, + !!node.expanded, ); - let childrenVisibleNodeCount = visibleDescendantsCountLookup[childId] ?? 0; - // TODO: For column grouping, we do not want to count the intermediate depth nodes in the visible descendant count - if (isNodeVisible) { - childrenVisibleNodeCount += 1; - } - - visibleDescendantCount += childrenVisibleNodeCount; + filteredDescendantCount += childSubTreeSize; }); - let shouldBeVisible: boolean; + let shouldPassFilters: boolean; switch (isMatchingFilters) { case true: { - shouldBeVisible = true; + shouldPassFilters = true; break; } case false: { - shouldBeVisible = visibleDescendantCount > 0; + shouldPassFilters = filteredDescendantCount > 0; break; } default: { - shouldBeVisible = isParentMatchingFilters; + shouldPassFilters = isParentMatchingFilters; break; } } - visibleRowsLookup[node.id] = shouldBeVisible; + visibleRowsLookup[node.id] = shouldPassFilters && isParentExpanded; - if (shouldBeVisible && visibleDescendantCount > 0) { - visibleDescendantsCountLookup[node.id] = visibleDescendantCount; + if (!shouldPassFilters) { + return 0; } - return shouldBeVisible; + filteredDescendantCountLookup[node.id] = filteredDescendantCount; + + // TODO: For column grouping, we do not want to count the intermediate depth nodes in the visible descendant count + return filteredDescendantCount + 1; }; const nodes = Object.values(rowTree); for (let i = 0; i < nodes.length; i += 1) { const node = nodes[i]; if (node.depth === 0) { - filterTreeNode(node, true); + filterTreeNode(node, true, true); } } - } else if (props.filterMode === GridFeatureModeConstant.client) { + } else if (props.filterMode === GridFeatureModeConstant.client && filteringMethod) { for (let i = 0; i < rowIds.length; i += 1) { const rowId = rowIds[i]; - visibleRowsLookup[rowId] = filteringMethod?.(rowId) ?? true; + visibleRowsLookup[rowId] = filteringMethod(rowId); } } @@ -246,7 +245,7 @@ export const useGridFilter = ( filter: { ...state.filter, visibleRowsLookup, - visibleDescendantsCountLookup, + filteredDescendantCountLookup, }, }; }); diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index fa5d03b5ce5d6..fff5efb9e9699 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -172,29 +172,32 @@ export const useGridSorting = ( ); const applySorting = React.useCallback(() => { + if (props.sortingMode === GridFeatureModeConstant.server) { + logger.debug('Skipping sorting rows as sortingMode = server'); + setGridState((state) => ({ + ...state, + sorting: { ...state.sorting, sortedRows: apiRef.current.getAllRowIds() }, + })); + return; + } + const rowTree = gridRowTreeSelector(apiRef.current.state); const rowIds = gridRowIdsSelector(apiRef.current.state); const sortModel = gridSortModelSelector(apiRef.current.state); - - const shouldApplySorting = props.sortingMode === GridFeatureModeConstant.client; - const comparatorList = shouldApplySorting ? buildComparatorList(sortModel) : []; - const aggregatedComparator = comparatorListAggregate(buildComparatorList(sortModel)); + const comparatorList = buildComparatorList(sortModel); + const aggregatedComparator = comparatorListAggregate(comparatorList); // Group the rows by parent const groupedByParentRows = new Map([[null, []]]); for (let i = 0; i < rowIds.length; i += 1) { const rowId = rowIds[i]; const node = rowTree[rowId]; - const isExpanded = node.parent == null || rowTree[node.parent].expanded; - - if (isExpanded) { - let group = groupedByParentRows.get(node.parent); - if (!group) { - group = []; - groupedByParentRows.set(node.parent, group); - } - group.push(node); + let group = groupedByParentRows.get(node.parent); + if (!group) { + group = []; + groupedByParentRows.set(node.parent, group); } + group.push(node); } // Apply the sorting to each list of children @@ -256,6 +259,7 @@ export const useGridSorting = ( forceUpdate(); }, [ apiRef, + logger, getSortCellParams, setGridState, forceUpdate, diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx index 49064c8491bbe..9c0f990ca13a5 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx +++ b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx @@ -5,11 +5,11 @@ import { GridTreeDataGroupingCellValue, } from '../../../components/cell/GridTreeDataGroupingCell'; import { GRID_STRING_COL_DEF } from '../../../models/colDef/gridStringColDef'; -import { gridVisibleDescendantCountLookupSelector } from '../filter/gridFilterSelector'; +import { gridFilteredDescendantCountLookupSelector } from '../filter/gridFilterSelector'; import { GridRenderCellParams } from '../../../models'; /** - * TODO: Add sorting and filtering on the value and the visibleDescendantCount + * TODO: Add sorting and filtering on the value and the filteredDescendantCount */ export const GRID_TREE_DATA_GROUP_COL_DEF: GridColDef = { ...GRID_STRING_COL_DEF, @@ -26,7 +26,7 @@ export const GRID_TREE_DATA_GROUP_COL_DEF: GridColDef = { label: rowNode.groupingValue, depth: rowNode.depth, expanded: rowNode.expanded ?? false, - visibleDescendantCount: gridVisibleDescendantCountLookupSelector(api.state)[rowNode.id] ?? 0, + filteredDescendantCount: gridFilteredDescendantCountLookupSelector(api.state)[rowNode.id] ?? 0, }), renderCell: (params: GridRenderCellParams) => ( diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index ce96596022295..ac6b9f1db89b6 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -315,39 +315,12 @@ describe(' - Tree Data', () => { }); describe('filter', () => { - const filterBaselineProps = { - autoHeight: isJSDOM, - columns: [ - { - field: 'name', - width: 200, - }, - ], - treeData: true, - getTreeDataPath: (row) => row.name.split('.'), - getRowId: (row) => row.name, - }; - - const TestFilter = (props: Omit) => { - apiRef = useGridApiRef(); - - return ( -
- -
- ); - }; - it('should not show a node if none of its children match the filters and it does not match the filters', () => { render( - , ); @@ -356,9 +329,10 @@ describe(' - Tree Data', () => { it('should show a node if some of its children match the filters even if it does not match the filters', () => { render( - , ); @@ -367,9 +341,10 @@ describe(' - Tree Data', () => { it('should show a node if none of its children match the filters but it does match the filters', () => { render( - , ); @@ -378,7 +353,7 @@ describe(' - Tree Data', () => { it('should not filter the children if props.disableChildrenFiltering = true', () => { render( - - Tree Data', () => { 'MUI: The `filterMode="server"` prop is not available when the `treeData` is enabled.', ); }); + + it('should set the descendantCount on matching nodes even if the children are collapsed', () => { + render( + , + ); + + // A has A.A but not A.B + // B has B.A (match filter), B.B (has matching children), B.B.A (match filters), B.B.A.A (match filters) + expect(getColumnValues(0)).to.deep.equal(['A (1)', 'B (4)']); + }); }); describe('sorting', () => { From ffba5a14b9b93d2d0d0f8376d8724fb3284c2d03 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 11:30:39 +0200 Subject: [PATCH 336/390] Add warning doc --- .../components/data-grid/group-pivot/group-pivot.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md index 5a8ef58967462..d5524af673cf7 100644 --- a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md +++ b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md @@ -99,6 +99,17 @@ You can limit the filtering to the top level rows with the `disableChildrenSorti {{"demo": "pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js", "bg": "inline", "defaultCodeOpen": false}} +**Note**: If you are using `sortingMode="server"`, you need to always set the children of a row after this row. +For instance: + +```ts +// The row A.A is just after its parent +const validRows = [{ path: ['A'] }, { path: ['A', 'A'] }, { path: ['B'] }]; + +// The row A.A is not just after its parent +const invalidRows = [{ path: ['A'] }, { path: ['B'] }, { path: ['A', 'A'] }]; +``` + ### Full Example {{"demo": "pages/components/data-grid/group-pivot/TreeDataFullExample.js", "bg": "inline", "defaultCodeOpen": false}} From 9aa117e2eb9ea08050e673724735107dbe7f8217 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 11:31:41 +0200 Subject: [PATCH 337/390] Proptypes --- .../grid/components/cell/GridTreeDataGroupingCell.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index 24fcbec3f1636..f963f5b5ed70b 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -109,8 +109,8 @@ GridTreeDataGroupingCell.propTypes = { formattedValue: PropTypes.shape({ depth: PropTypes.number.isRequired, expanded: PropTypes.bool.isRequired, - label: PropTypes.string.isRequired, filteredDescendantCount: PropTypes.number.isRequired, + label: PropTypes.string.isRequired, }).isRequired, /** * Get the cell value of a row and field. @@ -160,8 +160,8 @@ GridTreeDataGroupingCell.propTypes = { value: PropTypes.shape({ depth: PropTypes.number.isRequired, expanded: PropTypes.bool.isRequired, - label: PropTypes.string.isRequired, filteredDescendantCount: PropTypes.number.isRequired, + label: PropTypes.string.isRequired, }).isRequired, } as any; From b3cca0f177f9a944a84cb013f20c7358d15b4619 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 11:37:28 +0200 Subject: [PATCH 338/390] Proptypes --- .../data-grid/group-pivot/CustomGroupingColumnTreeData.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js index cc5cd7858dfec..4e3db7634d0a4 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js @@ -65,8 +65,8 @@ CustomGridTreeDataGroupingCell.propTypes = { value: PropTypes.shape({ depth: PropTypes.number.isRequired, expanded: PropTypes.bool.isRequired, - label: PropTypes.string.isRequired, filteredDescendantCount: PropTypes.number.isRequired, + label: PropTypes.string.isRequired, }).isRequired, }; From c37529957c1c13fae0000bea0fc56423d3f93972 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 12:03:05 +0200 Subject: [PATCH 339/390] Fix --- .../grid/hooks/features/sorting/useGridSorting.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index fff5efb9e9699..c49e9cf6163bf 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -27,7 +27,7 @@ import { gridSortedRowEntriesSelector, gridSortModelSelector, } from './gridSortingSelector'; -import { gridRowIdsSelector, gridRowTreeSelector } from '../rows'; +import { gridRowTreeSelector } from '../rows'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; @@ -172,17 +172,18 @@ export const useGridSorting = ( ); const applySorting = React.useCallback(() => { + const rowIds = apiRef.current.getAllRowIds(); + if (props.sortingMode === GridFeatureModeConstant.server) { logger.debug('Skipping sorting rows as sortingMode = server'); setGridState((state) => ({ ...state, - sorting: { ...state.sorting, sortedRows: apiRef.current.getAllRowIds() }, + sorting: { ...state.sorting, sortedRows: rowIds }, })); return; } const rowTree = gridRowTreeSelector(apiRef.current.state); - const rowIds = gridRowIdsSelector(apiRef.current.state); const sortModel = gridSortModelSelector(apiRef.current.state); const comparatorList = buildComparatorList(sortModel); const aggregatedComparator = comparatorListAggregate(comparatorList); From 6e6becbcffe10c8f71e9bd3250ce6a59e3430007 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 12:04:08 +0200 Subject: [PATCH 340/390] Fix --- .../_modules_/grid/hooks/features/sorting/useGridSorting.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index c49e9cf6163bf..7c8012597ffc6 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -23,9 +23,9 @@ import { useGridLogger } from '../../utils/useGridLogger'; import { allGridColumnsSelector } from '../columns/gridColumnsSelector'; import { useGridState } from '../../utils/useGridState'; import { + gridSortModelSelector, gridSortedRowIdsSelector, gridSortedRowEntriesSelector, - gridSortModelSelector, } from './gridSortingSelector'; import { gridRowTreeSelector } from '../rows'; import { useGridStateInit } from '../../utils/useGridStateInit'; From ae5c082fa466f27b8fcf16249cf16433a9a456b1 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 12:19:06 +0200 Subject: [PATCH 341/390] Do not apply tree sorting on flat tree --- .../hooks/features/sorting/useGridSorting.ts | 165 ++++++++++-------- 1 file changed, 90 insertions(+), 75 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 7c8012597ffc6..b178c3f1e8358 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -27,7 +27,7 @@ import { gridSortedRowIdsSelector, gridSortedRowEntriesSelector, } from './gridSortingSelector'; -import { gridRowTreeSelector } from '../rows'; +import { gridRowIdsSelector, gridRowTreeDepthSelector, gridRowTreeSelector } from '../rows'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; @@ -172,94 +172,109 @@ export const useGridSorting = ( ); const applySorting = React.useCallback(() => { - const rowIds = apiRef.current.getAllRowIds(); - if (props.sortingMode === GridFeatureModeConstant.server) { logger.debug('Skipping sorting rows as sortingMode = server'); setGridState((state) => ({ ...state, - sorting: { ...state.sorting, sortedRows: rowIds }, + sorting: { ...state.sorting, sortedRows: gridRowIdsSelector(state) }, })); return; } - const rowTree = gridRowTreeSelector(apiRef.current.state); - const sortModel = gridSortModelSelector(apiRef.current.state); - const comparatorList = buildComparatorList(sortModel); - const aggregatedComparator = comparatorListAggregate(comparatorList); - - // Group the rows by parent - const groupedByParentRows = new Map([[null, []]]); - for (let i = 0; i < rowIds.length; i += 1) { - const rowId = rowIds[i]; - const node = rowTree[rowId]; - let group = groupedByParentRows.get(node.parent); - if (!group) { - group = []; - groupedByParentRows.set(node.parent, group); - } - group.push(node); - } - - // Apply the sorting to each list of children - const sortedGroupedByParentRows = new Map(); - groupedByParentRows.forEach((rowList, parent) => { - if (rowList.length === 0) { - return sortedGroupedByParentRows.set(parent, []); - } + setGridState((state) => { + const rowIds = gridRowIdsSelector(state); + const rowTree = gridRowTreeSelector(state); + const shouldApplyTreeSorting = gridRowTreeDepthSelector(state) > 1; + const sortModel = gridSortModelSelector(state); + const comparatorList = buildComparatorList(sortModel); + const aggregatedComparator = comparatorListAggregate(comparatorList); + + const sortRowList = (rowList: GridRowTreeNodeConfig[]) => + rowList + .map((value) => ({ + value, + params: comparatorList.map((colComparator) => + getSortCellParams(value.id, colComparator.field), + ), + })) + .sort((a, b) => aggregatedComparator(a.params, b.params)) + .map((row) => row.value.id); + + let sortedRows: GridRowId[] = []; + if (shouldApplyTreeSorting) { + // Group the rows by parent + const groupedByParentRows = new Map([ + [null, []], + ]); + for (let i = 0; i < rowIds.length; i += 1) { + const rowId = rowIds[i]; + const node = rowTree[rowId]; + let group = groupedByParentRows.get(node.parent); + if (!group) { + group = []; + groupedByParentRows.set(node.parent, group); + } + group.push(node); + } - const depth = rowList[0].depth; - if ((depth > 0 && props.disableChildrenSorting) || comparatorList.length === 0) { - return sortedGroupedByParentRows.set( - parent, - rowList.map((row) => row.id), - ); + // Apply the sorting to each list of children + const sortedGroupedByParentRows = new Map(); + groupedByParentRows.forEach((rowList, parent) => { + if (rowList.length === 0) { + sortedGroupedByParentRows.set(parent, []); + } else { + const depth = rowList[0].depth; + if (depth > 0 && props.disableChildrenSorting) { + sortedGroupedByParentRows.set( + parent, + rowList.map((row) => row.id), + ); + } else if (comparatorList.length === 0) { + sortedGroupedByParentRows.set( + parent, + rowList.map((row) => row.id), + ); + } else { + sortedGroupedByParentRows.set(parent, sortRowList(rowList)); + } + } + }); + + // Flatten the sorted lists to have children just after their parent + const insertRowListIntoSortedRows = (startIndex: number, rowList: GridRowId[]) => { + sortedRows = [ + ...sortedRows.slice(0, startIndex), + ...rowList, + ...sortedRows.slice(startIndex), + ]; + + let treeSize = 0; + rowList.forEach((rowId) => { + treeSize += 1; + const children = sortedGroupedByParentRows.get(rowId); + if (children?.length) { + const subTreeSize = insertRowListIntoSortedRows(startIndex + treeSize, children); + treeSize += subTreeSize; + } + }); + + return treeSize; + }; + + insertRowListIntoSortedRows(0, sortedGroupedByParentRows.get(null)!); + } else if (comparatorList.length === 0) { + sortedRows = rowIds; + } else { + sortedRows = sortRowList(Object.values(rowTree)); } - const sortedRowList = rowList - .map((value) => ({ - value, - params: comparatorList.map((colComparator) => - getSortCellParams(value.id, colComparator.field), - ), - })) - .sort((a, b) => aggregatedComparator(a.params, b.params)) - .map((row) => row.value.id); - - return sortedGroupedByParentRows.set(parent, sortedRowList); + return { + ...state, + sorting: { ...state.sorting, sortedRows }, + }; }); - - // Flatten the sorted lists to have children just after their parent - let sortedRows: GridRowId[] = []; - const insertRowListIntoSortedRows = (startIndex: number, rowList: GridRowId[]) => { - sortedRows = [ - ...sortedRows.slice(0, startIndex), - ...rowList, - ...sortedRows.slice(startIndex), - ]; - - let treeSize = 0; - rowList.forEach((rowId) => { - treeSize += 1; - const children = sortedGroupedByParentRows.get(rowId); - if (children?.length) { - const subTreeSize = insertRowListIntoSortedRows(startIndex + treeSize, children); - treeSize += subTreeSize; - } - }); - - return treeSize; - }; - - insertRowListIntoSortedRows(0, sortedGroupedByParentRows.get(null)!); - - setGridState((state) => ({ - ...state, - sorting: { ...state.sorting, sortedRows }, - })); forceUpdate(); }, [ - apiRef, logger, getSortCellParams, setGridState, From fced4d604de71730c5a1c37a8792e3b5f58ccd62 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 13:32:46 +0200 Subject: [PATCH 342/390] Fix --- .../_modules_/grid/constants/eventsConstants.ts | 5 +++++ .../grid/hooks/features/filter/useGridFilter.ts | 3 +++ .../grid/hooks/features/rows/useGridRows.ts | 15 ++++++++------- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/grid/_modules_/grid/constants/eventsConstants.ts b/packages/grid/_modules_/grid/constants/eventsConstants.ts index 61a8e2824648e..978bae54fbc2c 100644 --- a/packages/grid/_modules_/grid/constants/eventsConstants.ts +++ b/packages/grid/_modules_/grid/constants/eventsConstants.ts @@ -254,6 +254,11 @@ export enum GridEvents { * @ignore - do not document. */ rowsSet = 'rowsSet', + /** + * Fired when the expansion of a row is changed. Called with a [[GridRowTreeNodeConfig]] object. + * @ignore - do not document + */ + rowExpansionChange = 'rowExpansionChange', /** * Fired when the visible rows are updated * @ignore - do not document. diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index ef6d1cc0b9a43..60323793d9195 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -400,6 +400,8 @@ export const useGridFilter = ( 'FilterApi', ); + // TODO: Do not rerun applyFilters if no filterItem has been removed + // TODO: Use the column lookup to avoid linear complexity if `columnsIds.find` const onColUpdated = React.useCallback(() => { logger.debug('onColUpdated - GridColumns changed, applying filters'); const filterModel = gridFilterModelSelector(apiRef.current.state); @@ -433,5 +435,6 @@ export const useGridFilter = ( useFirstRender(() => apiRef.current.unsafe_applyFilters()); useGridApiEventHandler(apiRef, GridEvents.rowsSet, apiRef.current.unsafe_applyFilters); + useGridApiEventHandler(apiRef, GridEvents.rowExpansionChange, apiRef.current.unsafe_applyFilters); useGridApiEventHandler(apiRef, GridEvents.columnsChange, onColUpdated); }; diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index b0111d5a4ad5b..5af2650e572a9 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -9,6 +9,7 @@ import { GridRowId, GridRowsProp, GridRowIdGetter, + GridRowTreeNodeConfig, } from '../../../models/gridRows'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; @@ -305,22 +306,22 @@ export const useGridRows = ( const setRowExpansion = React.useCallback( (id, isExpanded) => { + const currentNode = apiRef.current.unstable_getRowNode(id); + if (!currentNode) { + throw new Error(`MUI: No row with id #${id} found`); + } + const newNode: GridRowTreeNodeConfig = { ...currentNode, expanded: isExpanded }; setGridState((state) => { - const node = apiRef.current.unstable_getRowNode(id); - if (!node) { - throw new Error(`MUI: No row with id #${id} found`); - } - return { ...state, rows: { ...state.rows, - tree: { ...state.rows.tree, [id]: { ...node, expanded: isExpanded } }, + tree: { ...state.rows.tree, [id]: newNode }, }, }; }); forceUpdate(); - apiRef.current.publishEvent(GridEvents.rowsSet); + apiRef.current.publishEvent(GridEvents.rowExpansionChange, newNode); }, [apiRef, setGridState, forceUpdate], ); From 8eb0e5a228e6a83751672ca010e84aee781c8354 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 13:37:26 +0200 Subject: [PATCH 343/390] Fix --- .../grid/_modules_/grid/utils/rowTreeUtils.ts | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/grid/_modules_/grid/utils/rowTreeUtils.ts b/packages/grid/_modules_/grid/utils/rowTreeUtils.ts index b2394778e2457..c0470e568d2ca 100644 --- a/packages/grid/_modules_/grid/utils/rowTreeUtils.ts +++ b/packages/grid/_modules_/grid/utils/rowTreeUtils.ts @@ -19,18 +19,20 @@ interface TempRowTreeNode extends Omit { /** * Transform a list of rows into a tree structure where each row references its parent and children. - * Add the auto generated row to the `ids` and `idRowsLookup`. + * Add the auto generated rows to `ids` and `idRowsLookup`. */ export const buildRowTree = (params: GenerateRowTreeParams): GridRowGroupingResult => { // During the build, we store the children as a Record to avoid linear complexity when checking if a children is already defined. const tempTree: Record = {}; let treeDepth = 1; + const ids = [...params.ids]; const idRowsLookup = { ...params.idRowsLookup }; const nodeNameToIdTree: GridNodeNameToIdTree = {}; - params.rows.forEach((row) => { + for (let i = 0; i < params.rows.length; i += 1) { + const row = params.rows[i]; let nodeNameToIdSubTree = nodeNameToIdTree; let parentNode: TempRowTreeNode | null = null; @@ -39,15 +41,15 @@ export const buildRowTree = (params: GenerateRowTreeParams): GridRowGroupingResu let nodeId: GridRowId; const expanded = - params.defaultGroupingExpansionDepth === -1 || params.defaultGroupingExpansionDepth > depth; + params.defaultGroupingExpansionDepth === -1 || params.defaultGroupingExpansionDepth > depth; let nodeNameConfig = nodeNameToIdSubTree[nodeName]; if (!nodeNameConfig) { nodeId = - depth === row.path.length - 1 - ? row.id - : `auto-generated-row-${row.path.slice(0, depth + 1).join('-')}`; + depth === row.path.length - 1 + ? row.id + : `auto-generated-row-${row.path.slice(0, depth + 1).join('-')}`; nodeNameConfig = { id: nodeId, children: {} }; nodeNameToIdSubTree[nodeName] = nodeNameConfig; @@ -96,16 +98,17 @@ export const buildRowTree = (params: GenerateRowTreeParams): GridRowGroupingResu } treeDepth = Math.max(treeDepth, row.path.length); - }); + } const tree: GridRowTreeConfig = {}; - ids.forEach((rowId) => { + for (let i = 0; i < ids.length; i += 1) { + const rowId = ids[i]; const tempNode = tempTree[rowId]; tree[rowId] = { ...tempNode, children: tempNode.children ? Object.values(tempNode.children) : undefined, }; - }); + } return { tree, From 2cc1aa1f8e91f80a6fcd2b9c1da3ec99b3756c96 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 13:37:32 +0200 Subject: [PATCH 344/390] Fix --- packages/grid/_modules_/grid/utils/rowTreeUtils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/grid/_modules_/grid/utils/rowTreeUtils.ts b/packages/grid/_modules_/grid/utils/rowTreeUtils.ts index c0470e568d2ca..b7457251281ba 100644 --- a/packages/grid/_modules_/grid/utils/rowTreeUtils.ts +++ b/packages/grid/_modules_/grid/utils/rowTreeUtils.ts @@ -41,15 +41,15 @@ export const buildRowTree = (params: GenerateRowTreeParams): GridRowGroupingResu let nodeId: GridRowId; const expanded = - params.defaultGroupingExpansionDepth === -1 || params.defaultGroupingExpansionDepth > depth; + params.defaultGroupingExpansionDepth === -1 || params.defaultGroupingExpansionDepth > depth; let nodeNameConfig = nodeNameToIdSubTree[nodeName]; if (!nodeNameConfig) { nodeId = - depth === row.path.length - 1 - ? row.id - : `auto-generated-row-${row.path.slice(0, depth + 1).join('-')}`; + depth === row.path.length - 1 + ? row.id + : `auto-generated-row-${row.path.slice(0, depth + 1).join('-')}`; nodeNameConfig = { id: nodeId, children: {} }; nodeNameToIdSubTree[nodeName] = nodeNameConfig; From be2b27e352cb6812e60e1369fd5bdfd96ab171c6 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 13:44:04 +0200 Subject: [PATCH 345/390] Fix --- docs/pages/api-docs/data-grid/grid-cell-params.md | 2 +- docs/pages/api-docs/data-grid/grid-col-def.md | 2 +- packages/grid/_modules_/grid/GridComponentProps.ts | 4 ++-- .../components/cell/GridTreeDataGroupingCell.tsx | 2 +- .../columnSelection/GridCellCheckboxRenderer.tsx | 2 +- .../_modules_/grid/constants/eventsConstants.ts | 6 +++--- .../gridColumnsPreProcessingApi.ts | 4 ++-- .../gridRowGroupsPreProcessingApi.ts | 14 +++++++------- .../grid/hooks/features/filter/gridFilterState.ts | 10 +++++----- .../grid/hooks/features/rows/gridRowsState.ts | 8 ++++---- .../_modules_/grid/models/colDef/gridColDef.ts | 3 ++- .../grid/_modules_/grid/models/gridOptions.tsx | 10 +++++----- packages/grid/_modules_/grid/models/gridRows.ts | 14 +++++++------- .../_modules_/grid/models/params/gridCellParams.ts | 2 +- packages/grid/x-grid/src/DataGridPro.tsx | 14 +++++++------- 15 files changed, 49 insertions(+), 48 deletions(-) diff --git a/docs/pages/api-docs/data-grid/grid-cell-params.md b/docs/pages/api-docs/data-grid/grid-cell-params.md index b34aa78b5077a..3a87caf12f9b7 100644 --- a/docs/pages/api-docs/data-grid/grid-cell-params.md +++ b/docs/pages/api-docs/data-grid/grid-cell-params.md @@ -23,6 +23,6 @@ import { GridCellParams } from '@mui/x-data-grid'; | id | GridRowId | The grid row id. | | isEditable? | boolean | If true, the cell is editable. | | row | R | The row model of the row that the current cell belongs to. | -| rowNode | GridRowTreeNodeConfig | The node of the row that the current cell belongs to | +| rowNode | GridRowTreeNodeConfig | The node of the row that the current cell belongs to. | | tabIndex | 0 \| -1 | the tabIndex value. | | value | V | The cell value, but if the column has valueGetter, use getValue. | diff --git a/docs/pages/api-docs/data-grid/grid-col-def.md b/docs/pages/api-docs/data-grid/grid-col-def.md index 702ad4a8fbdd4..3baac3034afe5 100644 --- a/docs/pages/api-docs/data-grid/grid-col-def.md +++ b/docs/pages/api-docs/data-grid/grid-col-def.md @@ -35,7 +35,7 @@ import { GridColDef } from '@mui/x-data-grid'; | renderEditCell? | (params: GridRenderEditCellParams) => ReactNode | | Allows to override the component rendered in edit cell mode for this column. | | renderHeader? | (params: GridColumnHeaderParams) => ReactNode | | Allows to render a component in the column header cell. | | resizable? | boolean | true
| If `true`, the column is resizable. | -| shouldRenderAutoGeneratedRows? | boolean | | If `true`, the `renderCell` will be called for the auto generated rows | +| shouldRenderAutoGeneratedRows? | boolean | false
| If `true`, the `renderCell` will be called for the auto generated rows. | | sortable? | boolean | true
| If `true`, the column is sortable. | | sortComparator? | GridComparatorFn | | A comparator function used to sort rows. | | type? | string | 'string'
| Type allows to merge this object with a default definition [GridColDef](/api/data-grid/grid-col-def/). | diff --git a/packages/grid/_modules_/grid/GridComponentProps.ts b/packages/grid/_modules_/grid/GridComponentProps.ts index b6944277a2a12..b2570bbf98c64 100644 --- a/packages/grid/_modules_/grid/GridComponentProps.ts +++ b/packages/grid/_modules_/grid/GridComponentProps.ts @@ -105,7 +105,7 @@ interface GridComponentOtherProps { isRowSelectable?: (params: GridRowParams) => boolean; /** * Determines the path of a row in the tree data. - * For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"] + * For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"]. * Note than all paths must contain at least one element. * @param {GridRowModel} row The row from which we want the path. * @returns {string[]} the path to the row. @@ -501,7 +501,7 @@ interface GridComponentOtherProps { */ componentsProps?: GridSlotsComponentsProps; /** - * The grouping column used by the tree data + * The grouping column used by the tree data. */ groupingColDef?: GridColDefOverride | GridColDefOverrideCallback; } diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index f963f5b5ed70b..b5816ce18d187 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -136,7 +136,7 @@ GridTreeDataGroupingCell.propTypes = { */ row: PropTypes.any.isRequired, /** - * The node of the row that the current cell belongs to + * The node of the row that the current cell belongs to. */ rowNode: PropTypes.shape({ children: PropTypes.arrayOf( diff --git a/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx b/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx index b9ef3ecf85554..b2f2283704b14 100644 --- a/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx +++ b/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx @@ -135,7 +135,7 @@ GridCellCheckboxForwardRef.propTypes = { */ row: PropTypes.any.isRequired, /** - * The node of the row that the current cell belongs to + * The node of the row that the current cell belongs to. */ rowNode: PropTypes.shape({ children: PropTypes.arrayOf( diff --git a/packages/grid/_modules_/grid/constants/eventsConstants.ts b/packages/grid/_modules_/grid/constants/eventsConstants.ts index 978bae54fbc2c..56cedbc17dacc 100644 --- a/packages/grid/_modules_/grid/constants/eventsConstants.ts +++ b/packages/grid/_modules_/grid/constants/eventsConstants.ts @@ -256,7 +256,7 @@ export enum GridEvents { rowsSet = 'rowsSet', /** * Fired when the expansion of a row is changed. Called with a [[GridRowTreeNodeConfig]] object. - * @ignore - do not document + * @ignore - do not document. */ rowExpansionChange = 'rowExpansionChange', /** @@ -271,12 +271,12 @@ export enum GridEvents { columnsChange = 'columnsChange', /** * Fired when a column pre-processing is changed - * @ignore - do not document + * @ignore - do not document. */ columnsPreProcessingChange = 'columnsPreProcessingChange', /** * Fired when the row grouping function is changed - * @ignore - do not document + * @ignore - do not document. */ rowGroupsPreProcessingChange = 'rowGroupsPreProcessingChange', /** diff --git a/packages/grid/_modules_/grid/hooks/core/columnsPreProcessing/gridColumnsPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/core/columnsPreProcessing/gridColumnsPreProcessingApi.ts index e947681f84b1e..35fa9c0d4e924 100644 --- a/packages/grid/_modules_/grid/hooks/core/columnsPreProcessing/gridColumnsPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/core/columnsPreProcessing/gridColumnsPreProcessingApi.ts @@ -7,7 +7,7 @@ export interface GridColumnsPreProcessingApi { * Register a column pre-processing and emit an event to re-apply all the columns pre-processing * @param {string} processingName Name of the pre-processing. Used to clean the previous version of the pre-processing. * @param {GridColumnsPreProcessing | null } columnsPreProcessing Pre-processing to register. - * @ignore - do not document + * @ignore - do not document. */ unstable_registerColumnPreProcessing: ( processingName: string, @@ -17,7 +17,7 @@ export interface GridColumnsPreProcessingApi { * Apply all the columns pre-processing * @param {GridColumns} columns. Columns to pre-process * @returns {GridColumns} The pre-processed columns - * @ignore - do not document + * @ignore - do not document. */ unstable_applyAllColumnPreProcessing: (columns: GridColumns) => GridColumns; } diff --git a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts index b8bbe7c2d87f7..56a9a0cc56c3f 100644 --- a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts @@ -18,10 +18,10 @@ export type GridRowGroupingPreProcessing = ( export interface GridRowGroupsPreProcessingApi { /** - * Register a column pre-processing and emit an event to re-apply the row grouping pre-processing + * Register a column pre-processing and emit an event to re-apply the row grouping pre-processing. * @param {string} processingName Name of the pre-processing. Used to clean the previous version of the pre-processing. * @param {GridRowGroupingPreProcessing} columnsPreProcessing Pre-processing to register. - * @ignore - do not document + * @ignore - do not document. */ unstable_registerRowGroupsBuilder: ( processingName: string, @@ -29,11 +29,11 @@ export interface GridRowGroupsPreProcessingApi { ) => void; /** - * Apply the first row grouping pre-processing that does not return null - * @param {GridRowsLookup} rowsLookup. Lookup of the rows to group - * @param {GridRowId[]} List of the rows IDs - * @returns {GridRowGroupingResult} The grouped rows - * @ignore - do not document + * Apply the first row grouping pre-processing that does not return null. + * @param {GridRowsLookup} rowsLookup. Lookup of the rows to group. + * @param {GridRowId[]} List of the rows IDs. + * @returns {GridRowGroupingResult} The grouped rows. + * @ignore - do not document. */ unstable_groupRows: (params: GridRowGroupParams) => GridRowGroupingResult; } diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts index 2256038fd064e..641f7865b38c5 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts @@ -11,17 +11,17 @@ export interface GridFilterState { filterModel: GridFilterModel; /** - * Visibility status for each row + * Visibility status for each row. * A row is visible if it is passing the filters AND if its parent is expanded. - * If a row is not registered in this lookup, it is supposed to be visible + * If a row is not registered in this lookup, it is visible. */ visibleRowsLookup: Record; /** * Amount of descendants that are passing the filters. - * For the Tree Data, it includes all the intermediate depth levels (= amount of children + amount of grand children + ...) - * For the Row Grouping by Column, it does not include the intermediate depth levels (= amount of descendant of maximum depth) - * If a row is not registered in this lookup, it is supposed to have no descendant passing the filters. + * For the Tree Data, it includes all the intermediate depth levels (= amount of children + amount of grand children + ...). + * For the Row Grouping by Column, it does not include the intermediate depth levels (= amount of descendant of maximum depth). + * If a row is not registered in this lookup, it is supposed to have no descendant passing the filters.. */ filteredDescendantCountLookup: Record; } diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts index 708d036e8540b..eb444c2a611f3 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts @@ -2,14 +2,14 @@ import { GridRowGroupingResult } from '../../core/rowGroupsPerProcessing'; export interface GridRowsState extends GridRowGroupingResult { /** - * Amount of rows before applying the filtering - * It also count the expanded and collapsed children rows + * Amount of rows before applying the filtering. + * It also count the expanded and collapsed children rows. */ totalRowCount: number; /** - * Amount of rows before applying the filtering - * It does not count the expanded children rows + * Amount of rows before applying the filtering. + * It does not count the expanded children rows. */ totalTopLevelRowCount: number; } diff --git a/packages/grid/_modules_/grid/models/colDef/gridColDef.ts b/packages/grid/_modules_/grid/models/colDef/gridColDef.ts index 19fb4a7e668a2..2c5f9e2bc947a 100644 --- a/packages/grid/_modules_/grid/models/colDef/gridColDef.ts +++ b/packages/grid/_modules_/grid/models/colDef/gridColDef.ts @@ -166,7 +166,8 @@ export interface GridColDef { */ filterOperators?: GridFilterOperator[]; /** - * If `true`, the `renderCell` will be called for the auto generated rows + * If `true`, the `renderCell` will be called for the auto generated rows. + * @default false */ shouldRenderAutoGeneratedRows?: boolean; /** diff --git a/packages/grid/_modules_/grid/models/gridOptions.tsx b/packages/grid/_modules_/grid/models/gridOptions.tsx index a1ad11c4c3453..a07f01add8610 100644 --- a/packages/grid/_modules_/grid/models/gridOptions.tsx +++ b/packages/grid/_modules_/grid/models/gridOptions.tsx @@ -119,7 +119,7 @@ export interface GridSimpleOptions { */ disableMultipleColumnsFiltering: boolean; /** - * If `true`, the filtering will only be applied to the top level rows + * If `true`, the filtering will only be applied to the top level rows. * @default false */ disableChildrenFiltering: boolean; @@ -134,7 +134,7 @@ export interface GridSimpleOptions { */ disableMultipleColumnsSorting: boolean; /** - * If `true`, the sorting will only be applied to the top level rows + * If `true`, the sorting will only be applied to the top level rows. * @default false */ disableChildrenSorting: boolean; @@ -218,13 +218,13 @@ export interface GridSimpleOptions { */ rowsPerPageOptions: number[]; /** - * If `true`, the rows will be gathered in a tree structure, following the `getTreeDataPath` prop + * If `true`, the rows will be gathered in a tree structure according to the `getTreeDataPath` prop. * @default false */ treeData: boolean; /** - * If above 0, the row children will be expanded up to this depth - * If equal to -1, all the row children will be expanded + * If above 0, the row children will be expanded up to this depth. + * If equal to -1, all the row children will be expanded. * @default 0 */ defaultGroupingExpansionDepth: number; diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index 15e3d7cc0db9e..4c4a5f9165e5b 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -23,37 +23,37 @@ export interface GridRowTreeNodeConfig { id: GridRowId; /** - * The id of the row children + * The id of the row children. */ children?: GridRowId[]; /** - * Amount of descendants (children, children's children, ...) before the filtering + * Amount of descendants (children, children's children, ...) before the filtering. */ descendantCount?: number; /** - * The row id of the parent (null if this row is a top level row) + * The row id of the parent (null if this row is a top level row). */ parent: GridRowId | null; /** - * Current expansion status of the row + * Current expansion status of the row. */ expanded?: boolean; /** - * 0-based depth of the row in the tree + * 0-based depth of the row in the tree. */ depth: number; /** - * The value used to group the children of this row + * The value used to group the children of this row. */ groupingValue: string; /** - * If `true`, this node has been automatically added to fill a gap in the tree structure + * If `true`, this node has been automatically added to fill a gap in the tree structure. */ isAutoGenerated?: boolean; } diff --git a/packages/grid/_modules_/grid/models/params/gridCellParams.ts b/packages/grid/_modules_/grid/models/params/gridCellParams.ts index eee1f8b7e2fd0..796b6180b8c40 100644 --- a/packages/grid/_modules_/grid/models/params/gridCellParams.ts +++ b/packages/grid/_modules_/grid/models/params/gridCellParams.ts @@ -28,7 +28,7 @@ export interface GridCellParams { */ row: GridRowModel; /** - * The node of the row that the current cell belongs to + * The node of the row that the current cell belongs to. */ rowNode: GridRowTreeNodeConfig; /** diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index 91945acf234dd..02259498bdaac 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -135,8 +135,8 @@ DataGridProRaw.propTypes = { */ componentsProps: PropTypes.object, /** - * If above 0, the row children will be expanded up to this depth - * If equal to -1, all the row children will be expanded + * If above 0, the row children will be expanded up to this depth. + * If equal to -1, all the row children will be expanded. * @default 0 */ defaultGroupingExpansionDepth: PropTypes.number, @@ -146,12 +146,12 @@ DataGridProRaw.propTypes = { */ density: PropTypes.oneOf(['comfortable', 'compact', 'standard']), /** - * If `true`, the filtering will only be applied to the top level rows + * If `true`, the filtering will only be applied to the top level rows. * @default false */ disableChildrenFiltering: PropTypes.bool, /** - * If `true`, the sorting will only be applied to the top level rows + * If `true`, the sorting will only be applied to the top level rows. * @default false */ disableChildrenSorting: PropTypes.bool, @@ -273,14 +273,14 @@ DataGridProRaw.propTypes = { getRowId: PropTypes.func, /** * Determines the path of a row in the tree data. - * For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"] + * For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"]. * Note than all paths must contain at least one element. * @param {GridRowModel} row The row from which we want the path. * @returns {string[]} the path to the row. */ getTreeDataPath: PropTypes.func, /** - * The grouping column used by the tree data + * The grouping column used by the tree data. */ groupingColDef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), /** @@ -700,7 +700,7 @@ DataGridProRaw.propTypes = { */ throttleRowsMs: PropTypes.number, /** - * If `true`, the rows will be gathered in a tree structure, following the `getTreeDataPath` prop + * If `true`, the rows will be gathered in a tree structure according to the `getTreeDataPath` prop. * @default false */ treeData: PropTypes.bool, From 718fadcd990b5036ebb6afa286d427e2f479a689 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 13:49:38 +0200 Subject: [PATCH 346/390] Fix --- .../src/tests/treeData.DataGridPro.test.tsx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index ac6b9f1db89b6..16e7f91a390f4 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -412,8 +412,22 @@ describe(' - Tree Data', () => { it('should update the order server side', () => { const { setProps } = render(); expect(getColumnValues(1)).to.deep.equal(['A', 'B', 'C']); - setProps({ rows: [...rowsWithoutGap].reverse() }); + setProps({ + rows: [ + { name: 'C' }, + { name: 'B' }, + { name: 'B.B' }, + { name: 'B.B.A' }, + { name: 'B.B.A.A' }, + { name: 'B.A' }, + { name: 'A' }, + { name: 'A.B' }, + { name: 'A.A' }, + ], + }); expect(getColumnValues(1)).to.deep.equal(['C', 'B', 'A']); + fireEvent.click(getCell(2, 0).querySelector('button')); + expect(getColumnValues(1)).to.deep.equal(['C', 'B', 'A', 'A.B', 'A.A']); }); }); }); From 34357c9de8d108cca728d397db86640d080e38d5 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 13:58:32 +0200 Subject: [PATCH 347/390] Fix --- .../api-docs/data-grid/data-grid-pro-pt.json | 12 ++++++------ .../api-docs/data-grid/data-grid-pro-zh.json | 12 ++++++------ .../api-docs/data-grid/data-grid-pro.json | 12 ++++++------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/translations/api-docs/data-grid/data-grid-pro-pt.json b/docs/translations/api-docs/data-grid/data-grid-pro-pt.json index 8f32d917b5528..b61f75743676a 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro-pt.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro-pt.json @@ -15,10 +15,10 @@ "columnTypes": "Extend native column types with your new column types.", "components": "Overrideable components.", "componentsProps": "Overrideable components props dynamically passed to the component at rendering.", - "defaultGroupingExpansionDepth": "If above 0, the row children will be expanded up to this depth If equal to -1, all the row children will be expanded", + "defaultGroupingExpansionDepth": "If above 0, the row children will be expanded up to this depth. If equal to -1, all the row children will be expanded.", "density": "Set the density of the grid.", - "disableChildrenFiltering": "If true, the filtering will only be applied to the top level rows", - "disableChildrenSorting": "If true, the sorting will only be applied to the top level rows", + "disableChildrenFiltering": "If true, the filtering will only be applied to the top level rows.", + "disableChildrenSorting": "If true, the sorting will only be applied to the top level rows.", "disableColumnFilter": "If true, column filters are disabled.", "disableColumnMenu": "If true, the column menu is disabled.", "disableColumnReorder": "If true, reordering columns is disabled.", @@ -39,8 +39,8 @@ "getCellClassName": "Function that applies CSS classes dynamically on cells.

Signature:
function(params: GridCellParams) => string
params: With all properties from GridCellParams.
returns (string): The CSS class to apply to the cell.", "getRowClassName": "Function that applies CSS classes dynamically on rows.

Signature:
function(params: GridRowParams) => string
params: With all properties from GridRowParams.
returns (string): The CSS class to apply to the row.", "getRowId": "Return the id of a given GridRowModel.", - "getTreeDataPath": "Determines the path of a row in the tree data. For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"] Note than all paths must contain at least one element.

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row.", - "groupingColDef": "The grouping column used by the tree data", + "getTreeDataPath": "Determines the path of a row in the tree data. For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"]. Note than all paths must contain at least one element.

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row.", + "groupingColDef": "The grouping column used by the tree data.", "headerHeight": "Set the height in pixel of the column headers in the grid.", "hideFooter": "If true, the footer component is hidden.", "hideFooterPagination": "If true, the pagination component in the footer is hidden.", @@ -107,7 +107,7 @@ "sortingOrder": "The order of the sorting sequence.", "sortModel": "Set the sort model of the grid.", "throttleRowsMs": "If positive, the Grid will throttle updates coming from apiRef.current.updateRows and apiRef.current.setRows. It can be useful if you have a high update rate but do not want to do heavy work like filtering / sorting or rendering on each individual update.", - "treeData": "If true, the rows will be gathered in a tree structure, following the getTreeDataPath prop" + "treeData": "If true, the rows will be gathered in a tree structure according to the getTreeDataPath prop." }, "classDescriptions": { "autoHeight": { diff --git a/docs/translations/api-docs/data-grid/data-grid-pro-zh.json b/docs/translations/api-docs/data-grid/data-grid-pro-zh.json index 8f32d917b5528..b61f75743676a 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro-zh.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro-zh.json @@ -15,10 +15,10 @@ "columnTypes": "Extend native column types with your new column types.", "components": "Overrideable components.", "componentsProps": "Overrideable components props dynamically passed to the component at rendering.", - "defaultGroupingExpansionDepth": "If above 0, the row children will be expanded up to this depth If equal to -1, all the row children will be expanded", + "defaultGroupingExpansionDepth": "If above 0, the row children will be expanded up to this depth. If equal to -1, all the row children will be expanded.", "density": "Set the density of the grid.", - "disableChildrenFiltering": "If true, the filtering will only be applied to the top level rows", - "disableChildrenSorting": "If true, the sorting will only be applied to the top level rows", + "disableChildrenFiltering": "If true, the filtering will only be applied to the top level rows.", + "disableChildrenSorting": "If true, the sorting will only be applied to the top level rows.", "disableColumnFilter": "If true, column filters are disabled.", "disableColumnMenu": "If true, the column menu is disabled.", "disableColumnReorder": "If true, reordering columns is disabled.", @@ -39,8 +39,8 @@ "getCellClassName": "Function that applies CSS classes dynamically on cells.

Signature:
function(params: GridCellParams) => string
params: With all properties from GridCellParams.
returns (string): The CSS class to apply to the cell.", "getRowClassName": "Function that applies CSS classes dynamically on rows.

Signature:
function(params: GridRowParams) => string
params: With all properties from GridRowParams.
returns (string): The CSS class to apply to the row.", "getRowId": "Return the id of a given GridRowModel.", - "getTreeDataPath": "Determines the path of a row in the tree data. For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"] Note than all paths must contain at least one element.

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row.", - "groupingColDef": "The grouping column used by the tree data", + "getTreeDataPath": "Determines the path of a row in the tree data. For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"]. Note than all paths must contain at least one element.

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row.", + "groupingColDef": "The grouping column used by the tree data.", "headerHeight": "Set the height in pixel of the column headers in the grid.", "hideFooter": "If true, the footer component is hidden.", "hideFooterPagination": "If true, the pagination component in the footer is hidden.", @@ -107,7 +107,7 @@ "sortingOrder": "The order of the sorting sequence.", "sortModel": "Set the sort model of the grid.", "throttleRowsMs": "If positive, the Grid will throttle updates coming from apiRef.current.updateRows and apiRef.current.setRows. It can be useful if you have a high update rate but do not want to do heavy work like filtering / sorting or rendering on each individual update.", - "treeData": "If true, the rows will be gathered in a tree structure, following the getTreeDataPath prop" + "treeData": "If true, the rows will be gathered in a tree structure according to the getTreeDataPath prop." }, "classDescriptions": { "autoHeight": { diff --git a/docs/translations/api-docs/data-grid/data-grid-pro.json b/docs/translations/api-docs/data-grid/data-grid-pro.json index 8f32d917b5528..b61f75743676a 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro.json @@ -15,10 +15,10 @@ "columnTypes": "Extend native column types with your new column types.", "components": "Overrideable components.", "componentsProps": "Overrideable components props dynamically passed to the component at rendering.", - "defaultGroupingExpansionDepth": "If above 0, the row children will be expanded up to this depth If equal to -1, all the row children will be expanded", + "defaultGroupingExpansionDepth": "If above 0, the row children will be expanded up to this depth. If equal to -1, all the row children will be expanded.", "density": "Set the density of the grid.", - "disableChildrenFiltering": "If true, the filtering will only be applied to the top level rows", - "disableChildrenSorting": "If true, the sorting will only be applied to the top level rows", + "disableChildrenFiltering": "If true, the filtering will only be applied to the top level rows.", + "disableChildrenSorting": "If true, the sorting will only be applied to the top level rows.", "disableColumnFilter": "If true, column filters are disabled.", "disableColumnMenu": "If true, the column menu is disabled.", "disableColumnReorder": "If true, reordering columns is disabled.", @@ -39,8 +39,8 @@ "getCellClassName": "Function that applies CSS classes dynamically on cells.

Signature:
function(params: GridCellParams) => string
params: With all properties from GridCellParams.
returns (string): The CSS class to apply to the cell.", "getRowClassName": "Function that applies CSS classes dynamically on rows.

Signature:
function(params: GridRowParams) => string
params: With all properties from GridRowParams.
returns (string): The CSS class to apply to the row.", "getRowId": "Return the id of a given GridRowModel.", - "getTreeDataPath": "Determines the path of a row in the tree data. For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"] Note than all paths must contain at least one element.

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row.", - "groupingColDef": "The grouping column used by the tree data", + "getTreeDataPath": "Determines the path of a row in the tree data. For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"]. Note than all paths must contain at least one element.

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row.", + "groupingColDef": "The grouping column used by the tree data.", "headerHeight": "Set the height in pixel of the column headers in the grid.", "hideFooter": "If true, the footer component is hidden.", "hideFooterPagination": "If true, the pagination component in the footer is hidden.", @@ -107,7 +107,7 @@ "sortingOrder": "The order of the sorting sequence.", "sortModel": "Set the sort model of the grid.", "throttleRowsMs": "If positive, the Grid will throttle updates coming from apiRef.current.updateRows and apiRef.current.setRows. It can be useful if you have a high update rate but do not want to do heavy work like filtering / sorting or rendering on each individual update.", - "treeData": "If true, the rows will be gathered in a tree structure, following the getTreeDataPath prop" + "treeData": "If true, the rows will be gathered in a tree structure according to the getTreeDataPath prop." }, "classDescriptions": { "autoHeight": { From 33466a5abbc1381f874f5bc159aa5b1123f9d328 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 15:01:20 +0200 Subject: [PATCH 348/390] Gen doc --- docs/pages/api-docs/data-grid/grid-api.md | 1 - docs/pages/api-docs/data-grid/grid-filter-api.json | 5 ----- 2 files changed, 6 deletions(-) diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index 5da476e3f1956..ac67a09879e95 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -87,7 +87,6 @@ import { GridApi } from '@mui/x-data-grid-pro'; | state | GridState | Property that contains the whole state of the grid. | | subscribeEvent | <Params, Event extends MuiEvent<BaseEvent>>(event: string, handler: GridListener<Params, Event>, options?: GridSubscribeEventOptions) => () => void | Registers a handler for an event. | | toggleColumnMenu | (field: string) => void | Toggles the column menu under the `field` column. | -| unsafe_applyFilters | () => void | Applies all filters on all rows. | | updateColumn | (col: GridColDef) => void | Updates the definition of a column. | | updateColumns | (cols: GridColDef[]) => void | Updates the definition of multiple columns at the same time. | | updateRows | (updates: GridRowModelUpdate<>[]) => void | Allows to updates, insert and delete rows in a single call. | diff --git a/docs/pages/api-docs/data-grid/grid-filter-api.json b/docs/pages/api-docs/data-grid/grid-filter-api.json index 61fe557b90fb1..91b85069ad5e6 100644 --- a/docs/pages/api-docs/data-grid/grid-filter-api.json +++ b/docs/pages/api-docs/data-grid/grid-filter-api.json @@ -27,11 +27,6 @@ "name": "showFilterPanel", "description": "Shows the filter panel. If targetColumnField is given, a filter for this field is also added.", "type": "(targetColumnField?: string) => void" - }, - { - "name": "upsertFilterItem", - "description": "Updates or inserts a GridFilterItem.", - "type": "(item: GridFilterItem) => void" } ] } From 9ab0d26bdd5495b3d5d40e9cf708858e23a3b55b Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 15:02:42 +0200 Subject: [PATCH 349/390] Gen doc --- docs/pages/api-docs/data-grid/grid-api.md | 1 + docs/pages/api-docs/data-grid/grid-filter-api.json | 5 +++++ packages/grid/_modules_/grid/models/api/gridFilterApi.ts | 1 - 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index ac67a09879e95..9be74395c4ad8 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -90,3 +90,4 @@ import { GridApi } from '@mui/x-data-grid-pro'; | updateColumn | (col: GridColDef) => void | Updates the definition of a column. | | updateColumns | (cols: GridColDef[]) => void | Updates the definition of multiple columns at the same time. | | updateRows | (updates: GridRowModelUpdate<>[]) => void | Allows to updates, insert and delete rows in a single call. | +| upsertFilterItem | (item: GridFilterItem) => void | Updates or inserts a GridFilterItem. | diff --git a/docs/pages/api-docs/data-grid/grid-filter-api.json b/docs/pages/api-docs/data-grid/grid-filter-api.json index 91b85069ad5e6..61fe557b90fb1 100644 --- a/docs/pages/api-docs/data-grid/grid-filter-api.json +++ b/docs/pages/api-docs/data-grid/grid-filter-api.json @@ -27,6 +27,11 @@ "name": "showFilterPanel", "description": "Shows the filter panel. If targetColumnField is given, a filter for this field is also added.", "type": "(targetColumnField?: string) => void" + }, + { + "name": "upsertFilterItem", + "description": "Updates or inserts a GridFilterItem.", + "type": "(item: GridFilterItem) => void" } ] } diff --git a/packages/grid/_modules_/grid/models/api/gridFilterApi.ts b/packages/grid/_modules_/grid/models/api/gridFilterApi.ts index d9b75ae3bb7dc..c50df0ad49192 100644 --- a/packages/grid/_modules_/grid/models/api/gridFilterApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridFilterApi.ts @@ -18,7 +18,6 @@ export interface GridFilterApi { /** * Updates or inserts a [[GridFilterItem]]. * @param {GridFilterItem} item The filter to update. - * @ignore - do not document. */ upsertFilterItem: (item: GridFilterItem) => void; /** From b16856ec067a0934c7793e7d304d5751ceecc535 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 15:31:10 +0200 Subject: [PATCH 350/390] [core] Implement tree-based row management --- .../api-docs/data-grid/data-grid-pro.json | 2 + .../api-docs/data-grid/grid-cell-params.md | 1 + docs/pages/api-docs/data-grid/grid-col-def.md | 65 +++-- .../api-docs/data-grid/data-grid-pro-pt.json | 6 +- .../api-docs/data-grid/data-grid-pro-zh.json | 6 +- .../api-docs/data-grid/data-grid-pro.json | 6 +- .../api-docs/data-grid/data-grid-pt.json | 4 +- .../api-docs/data-grid/data-grid-zh.json | 4 +- .../api-docs/data-grid/data-grid.json | 4 +- .../grid/_modules_/grid/GridComponentProps.ts | 4 +- .../_modules_/grid/components/GridFooter.tsx | 10 +- .../_modules_/grid/components/GridRow.tsx | 10 +- .../grid/components/GridVirtualScroller.tsx | 35 +-- .../grid/components/cell/GridBooleanCell.tsx | 1 + .../components/cell/GridEditBooleanCell.tsx | 1 + .../grid/components/cell/GridEditDateCell.tsx | 1 + .../components/cell/GridEditInputCell.tsx | 1 + .../cell/GridEditSingleSelectCell.tsx | 1 + .../GridCellCheckboxRenderer.tsx | 15 + .../grid/constants/eventsConstants.ts | 9 +- .../gridColumnsPreProcessingApi.ts | 4 +- .../gridRowGroupsPreProcessingApi.ts | 27 +- .../useGridRowGroupsPreProcessing.ts | 20 +- .../features/filter/gridFilterSelector.ts | 38 ++- .../hooks/features/filter/gridFilterState.ts | 15 +- .../hooks/features/filter/useGridFilter.ts | 142 ++++++--- .../pagination/gridPaginationSelector.ts | 91 +++++- .../hooks/features/pagination/useGridPage.ts | 8 +- .../hooks/features/rows/gridRowsSelector.ts | 18 +- .../grid/hooks/features/rows/gridRowsState.ts | 16 +- .../hooks/features/rows/useGridParamsApi.ts | 8 +- .../grid/hooks/features/rows/useGridRows.ts | 275 +++++++++++++----- .../features/sorting/gridSortingSelector.ts | 8 +- .../hooks/features/sorting/useGridSorting.ts | 125 ++++++-- .../grid/hooks/utils/useCurrentPageRows.ts | 45 +++ .../_modules_/grid/models/api/gridRowApi.ts | 16 +- .../grid/models/colDef/gridColDef.ts | 16 + .../_modules_/grid/models/gridOptions.tsx | 12 + .../grid/_modules_/grid/models/gridRows.ts | 40 ++- .../grid/models/params/gridCellParams.ts | 6 +- packages/grid/data-grid/src/DataGrid.tsx | 2 + packages/grid/data-grid/src/DataGridProps.ts | 2 + .../grid/data-grid/src/useDataGridProps.ts | 2 + packages/grid/x-grid/src/DataGridPro.tsx | 12 + 44 files changed, 865 insertions(+), 269 deletions(-) create mode 100644 packages/grid/_modules_/grid/hooks/utils/useCurrentPageRows.ts diff --git a/docs/pages/api-docs/data-grid/data-grid-pro.json b/docs/pages/api-docs/data-grid/data-grid-pro.json index ec3cd814fe163..7c539a3c02552 100644 --- a/docs/pages/api-docs/data-grid/data-grid-pro.json +++ b/docs/pages/api-docs/data-grid/data-grid-pro.json @@ -31,6 +31,8 @@ }, "default": "\"standard\"" }, + "disableChildrenFiltering": { "type": { "name": "bool" } }, + "disableChildrenSorting": { "type": { "name": "bool" } }, "disableColumnFilter": { "type": { "name": "bool" } }, "disableColumnMenu": { "type": { "name": "bool" } }, "disableColumnReorder": { "type": { "name": "bool" } }, diff --git a/docs/pages/api-docs/data-grid/grid-cell-params.md b/docs/pages/api-docs/data-grid/grid-cell-params.md index 0d85ecd1b67e4..3a87caf12f9b7 100644 --- a/docs/pages/api-docs/data-grid/grid-cell-params.md +++ b/docs/pages/api-docs/data-grid/grid-cell-params.md @@ -23,5 +23,6 @@ import { GridCellParams } from '@mui/x-data-grid'; | id | GridRowId | The grid row id. | | isEditable? | boolean | If true, the cell is editable. | | row | R | The row model of the row that the current cell belongs to. | +| rowNode | GridRowTreeNodeConfig | The node of the row that the current cell belongs to. | | tabIndex | 0 \| -1 | the tabIndex value. | | value | V | The cell value, but if the column has valueGetter, use getValue. | diff --git a/docs/pages/api-docs/data-grid/grid-col-def.md b/docs/pages/api-docs/data-grid/grid-col-def.md index f63373e9bd316..3baac3034afe5 100644 --- a/docs/pages/api-docs/data-grid/grid-col-def.md +++ b/docs/pages/api-docs/data-grid/grid-col-def.md @@ -12,35 +12,36 @@ import { GridColDef } from '@mui/x-data-grid'; ## Properties -| Name | Type | Default | Description | -| :-------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------- | -| align? | GridAlignment | | Allows to align the column values in cells. | -| cellClassName? | GridCellClassNamePropType | | Class name that will be added in cells for that column. | -| description? | string | | The description of the column rendered as tooltip if the column header name is not fully displayed. | -| disableColumnMenu? | boolean | false
| If `true`, the column menu is disabled for this column. | -| disableExport? | boolean | false
| If `true`, this column will not be included in exports. | -| disableReorder? | boolean | false
| If `true`, this column cannot be reordered. | -| editable? | boolean | false
| If `true`, the cells of the column are editable. | -| field | string | | The column identifier. It's used to map with GridRowModel values. | -| filterable? | boolean | true
| If `true`, the column is filterable. | -| filterOperators? | GridFilterOperator[] | | Allows setting the filter operators for this column. | -| flex? | number | | If set, it indicates that a column has fluid width. Range [0, ∞). | -| headerAlign? | GridAlignment | | Header cell element alignment. | -| headerClassName? | GridColumnHeaderClassNamePropType | | Class name that will be added in the column header cell. | -| headerName? | string | | The title of the column rendered in the column header cell. | -| hide? | boolean | false
| If `true`, hide the column. | -| hideSortIcons? | boolean | false
| Toggle the visibility of the sort icons. | -| minWidth? | number | 50
| Sets the minimum width of a column. | -| renderCell? | (params: GridRenderCellParams<any, any, any>) => ReactNode | | Allows to override the component rendered as cell for this column. | -| renderEditCell? | (params: GridRenderEditCellParams) => ReactNode | | Allows to override the component rendered in edit cell mode for this column. | -| renderHeader? | (params: GridColumnHeaderParams) => ReactNode | | Allows to render a component in the column header cell. | -| resizable? | boolean | true
| If `true`, the column is resizable. | -| sortable? | boolean | true
| If `true`, the column is sortable. | -| sortComparator? | GridComparatorFn | | A comparator function used to sort rows. | -| type? | string | 'string'
| Type allows to merge this object with a default definition [GridColDef](/api/data-grid/grid-col-def/). | -| valueFormatter? | (params: GridValueFormatterParams) => GridCellValue | | Function that allows to apply a formatter before rendering its value. | -| valueGetter? | (params: GridValueGetterParams) => GridCellValue | | Function that allows to get a specific data instead of field to render in the cell. | -| valueOptions? | (string \| number \| { label: string; value: any })[] | | To be used in combination with `type: 'singleSelect'`. This is an array of the possible cell values and labels. | -| valueParser? | (value: GridCellValue, params?: GridCellParams<any, any, any>) => GridCellValue | | Function that takes the user-entered value and converts it to a value used internally. | -| valueSetter? | (params: GridValueSetterParams) => { [key: string]: any } | | Function that allows to customize how the entered value is stored in the row.
It only works with cell/row editing. | -| width? | number | 100
| Set the width of the column. | +| Name | Type | Default | Description | +| :-------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------- | +| align? | GridAlignment | | Allows to align the column values in cells. | +| cellClassName? | GridCellClassNamePropType | | Class name that will be added in cells for that column. | +| description? | string | | The description of the column rendered as tooltip if the column header name is not fully displayed. | +| disableColumnMenu? | boolean | false
| If `true`, the column menu is disabled for this column. | +| disableExport? | boolean | false
| If `true`, this column will not be included in exports. | +| disableReorder? | boolean | false
| If `true`, this column cannot be reordered. | +| editable? | boolean | false
| If `true`, the cells of the column are editable. | +| field | string | | The column identifier. It's used to map with GridRowModel values. | +| filterable? | boolean | true
| If `true`, the column is filterable. | +| filterOperators? | GridFilterOperator[] | | Allows setting the filter operators for this column. | +| flex? | number | | If set, it indicates that a column has fluid width. Range [0, ∞). | +| headerAlign? | GridAlignment | | Header cell element alignment. | +| headerClassName? | GridColumnHeaderClassNamePropType | | Class name that will be added in the column header cell. | +| headerName? | string | | The title of the column rendered in the column header cell. | +| hide? | boolean | false
| If `true`, hide the column. | +| hideSortIcons? | boolean | false
| Toggle the visibility of the sort icons. | +| minWidth? | number | 50
| Sets the minimum width of a column. | +| renderCell? | (params: GridRenderCellParams<any, any, any>) => ReactNode | | Allows to override the component rendered as cell for this column. | +| renderEditCell? | (params: GridRenderEditCellParams) => ReactNode | | Allows to override the component rendered in edit cell mode for this column. | +| renderHeader? | (params: GridColumnHeaderParams) => ReactNode | | Allows to render a component in the column header cell. | +| resizable? | boolean | true
| If `true`, the column is resizable. | +| shouldRenderAutoGeneratedRows? | boolean | false
| If `true`, the `renderCell` will be called for the auto generated rows. | +| sortable? | boolean | true
| If `true`, the column is sortable. | +| sortComparator? | GridComparatorFn | | A comparator function used to sort rows. | +| type? | string | 'string'
| Type allows to merge this object with a default definition [GridColDef](/api/data-grid/grid-col-def/). | +| valueFormatter? | (params: GridValueFormatterParams) => GridCellValue | | Function that allows to apply a formatter before rendering its value. | +| valueGetter? | (params: GridValueGetterParams) => GridCellValue | | Function that allows to get a specific data instead of field to render in the cell. | +| valueOptions? | (string \| number \| { label: string; value: any })[] | | To be used in combination with `type: 'singleSelect'`. This is an array of the possible cell values and labels. | +| valueParser? | (value: GridCellValue, params?: GridCellParams<any, any, any>) => GridCellValue | | Function that takes the user-entered value and converts it to a value used internally. | +| valueSetter? | (params: GridValueSetterParams) => { [key: string]: any } | | Function that allows to customize how the entered value is stored in the row.
It only works with cell/row editing. | +| width? | number | 100
| Set the width of the column. | diff --git a/docs/translations/api-docs/data-grid/data-grid-pro-pt.json b/docs/translations/api-docs/data-grid/data-grid-pro-pt.json index 164aeb8950544..aa667de4084a8 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro-pt.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro-pt.json @@ -16,6 +16,8 @@ "components": "Overrideable components.", "componentsProps": "Overrideable components props dynamically passed to the component at rendering.", "density": "Set the density of the grid.", + "disableChildrenFiltering": "If true, the filtering will only be applied to the top level rows.", + "disableChildrenSorting": "If true, the sorting will only be applied to the top level rows.", "disableColumnFilter": "If true, column filters are disabled.", "disableColumnMenu": "If true, the column menu is disabled.", "disableColumnReorder": "If true, reordering columns is disabled.", @@ -84,11 +86,11 @@ "onSortModelChange": "Callback fired when the sort model changes before a column is sorted.

Signature:
function(model: GridSortModel, details: GridCallbackDetails) => void
model: With all properties from GridSortModel.
details: Additional details for this callback.", "onStateChange": "Callback fired when the state of the grid is updated.

Signature:
function(state: GridState, event: MuiEvent, details: GridCallbackDetails) => void
state: The new state.
event: The event object.
details: Additional details for this callback.", "page": "The zero-based index of the current page.", - "pageSize": "Set the number of rows in one page.", + "pageSize": "Set the number of rows in one page. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows wanted on each page.", "pagination": "If true, pagination is enabled.", "paginationMode": "Pagination can be processed on the server or client-side. Set it to 'client' if you would like to handle the pagination on the client-side. Set it to 'server' if you would like to handle the pagination on the server-side.", "rowBuffer": "Number of extra rows to be rendered before/after the visible slice.", - "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop.", + "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows.", "rowHeight": "Set the height in pixel of a row in the grid.", "rows": "Set of rows of type GridRowsProp.", "rowsPerPageOptions": "Select the pageSize dynamically using the component UI.", diff --git a/docs/translations/api-docs/data-grid/data-grid-pro-zh.json b/docs/translations/api-docs/data-grid/data-grid-pro-zh.json index 164aeb8950544..aa667de4084a8 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro-zh.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro-zh.json @@ -16,6 +16,8 @@ "components": "Overrideable components.", "componentsProps": "Overrideable components props dynamically passed to the component at rendering.", "density": "Set the density of the grid.", + "disableChildrenFiltering": "If true, the filtering will only be applied to the top level rows.", + "disableChildrenSorting": "If true, the sorting will only be applied to the top level rows.", "disableColumnFilter": "If true, column filters are disabled.", "disableColumnMenu": "If true, the column menu is disabled.", "disableColumnReorder": "If true, reordering columns is disabled.", @@ -84,11 +86,11 @@ "onSortModelChange": "Callback fired when the sort model changes before a column is sorted.

Signature:
function(model: GridSortModel, details: GridCallbackDetails) => void
model: With all properties from GridSortModel.
details: Additional details for this callback.", "onStateChange": "Callback fired when the state of the grid is updated.

Signature:
function(state: GridState, event: MuiEvent, details: GridCallbackDetails) => void
state: The new state.
event: The event object.
details: Additional details for this callback.", "page": "The zero-based index of the current page.", - "pageSize": "Set the number of rows in one page.", + "pageSize": "Set the number of rows in one page. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows wanted on each page.", "pagination": "If true, pagination is enabled.", "paginationMode": "Pagination can be processed on the server or client-side. Set it to 'client' if you would like to handle the pagination on the client-side. Set it to 'server' if you would like to handle the pagination on the server-side.", "rowBuffer": "Number of extra rows to be rendered before/after the visible slice.", - "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop.", + "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows.", "rowHeight": "Set the height in pixel of a row in the grid.", "rows": "Set of rows of type GridRowsProp.", "rowsPerPageOptions": "Select the pageSize dynamically using the component UI.", diff --git a/docs/translations/api-docs/data-grid/data-grid-pro.json b/docs/translations/api-docs/data-grid/data-grid-pro.json index 164aeb8950544..aa667de4084a8 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro.json @@ -16,6 +16,8 @@ "components": "Overrideable components.", "componentsProps": "Overrideable components props dynamically passed to the component at rendering.", "density": "Set the density of the grid.", + "disableChildrenFiltering": "If true, the filtering will only be applied to the top level rows.", + "disableChildrenSorting": "If true, the sorting will only be applied to the top level rows.", "disableColumnFilter": "If true, column filters are disabled.", "disableColumnMenu": "If true, the column menu is disabled.", "disableColumnReorder": "If true, reordering columns is disabled.", @@ -84,11 +86,11 @@ "onSortModelChange": "Callback fired when the sort model changes before a column is sorted.

Signature:
function(model: GridSortModel, details: GridCallbackDetails) => void
model: With all properties from GridSortModel.
details: Additional details for this callback.", "onStateChange": "Callback fired when the state of the grid is updated.

Signature:
function(state: GridState, event: MuiEvent, details: GridCallbackDetails) => void
state: The new state.
event: The event object.
details: Additional details for this callback.", "page": "The zero-based index of the current page.", - "pageSize": "Set the number of rows in one page.", + "pageSize": "Set the number of rows in one page. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows wanted on each page.", "pagination": "If true, pagination is enabled.", "paginationMode": "Pagination can be processed on the server or client-side. Set it to 'client' if you would like to handle the pagination on the client-side. Set it to 'server' if you would like to handle the pagination on the server-side.", "rowBuffer": "Number of extra rows to be rendered before/after the visible slice.", - "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop.", + "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows.", "rowHeight": "Set the height in pixel of a row in the grid.", "rows": "Set of rows of type GridRowsProp.", "rowsPerPageOptions": "Select the pageSize dynamically using the component UI.", diff --git a/docs/translations/api-docs/data-grid/data-grid-pt.json b/docs/translations/api-docs/data-grid/data-grid-pt.json index 132e73b1d116e..37a6a1ab7574d 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pt.json +++ b/docs/translations/api-docs/data-grid/data-grid-pt.json @@ -75,10 +75,10 @@ "onSortModelChange": "Callback fired when the sort model changes before a column is sorted.

Signature:
function(model: GridSortModel, details: GridCallbackDetails) => void
model: With all properties from GridSortModel.
details: Additional details for this callback.", "onStateChange": "Callback fired when the state of the grid is updated.

Signature:
function(state: GridState, event: MuiEvent, details: GridCallbackDetails) => void
state: The new state.
event: The event object.
details: Additional details for this callback.", "page": "The zero-based index of the current page.", - "pageSize": "Set the number of rows in one page.", + "pageSize": "Set the number of rows in one page. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows wanted on each page.", "paginationMode": "Pagination can be processed on the server or client-side. Set it to 'client' if you would like to handle the pagination on the client-side. Set it to 'server' if you would like to handle the pagination on the server-side.", "rowBuffer": "Number of extra rows to be rendered before/after the visible slice.", - "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop.", + "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows.", "rowHeight": "Set the height in pixel of a row in the grid.", "rows": "Set of rows of type GridRowsProp.", "rowsPerPageOptions": "Select the pageSize dynamically using the component UI.", diff --git a/docs/translations/api-docs/data-grid/data-grid-zh.json b/docs/translations/api-docs/data-grid/data-grid-zh.json index 132e73b1d116e..37a6a1ab7574d 100644 --- a/docs/translations/api-docs/data-grid/data-grid-zh.json +++ b/docs/translations/api-docs/data-grid/data-grid-zh.json @@ -75,10 +75,10 @@ "onSortModelChange": "Callback fired when the sort model changes before a column is sorted.

Signature:
function(model: GridSortModel, details: GridCallbackDetails) => void
model: With all properties from GridSortModel.
details: Additional details for this callback.", "onStateChange": "Callback fired when the state of the grid is updated.

Signature:
function(state: GridState, event: MuiEvent, details: GridCallbackDetails) => void
state: The new state.
event: The event object.
details: Additional details for this callback.", "page": "The zero-based index of the current page.", - "pageSize": "Set the number of rows in one page.", + "pageSize": "Set the number of rows in one page. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows wanted on each page.", "paginationMode": "Pagination can be processed on the server or client-side. Set it to 'client' if you would like to handle the pagination on the client-side. Set it to 'server' if you would like to handle the pagination on the server-side.", "rowBuffer": "Number of extra rows to be rendered before/after the visible slice.", - "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop.", + "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows.", "rowHeight": "Set the height in pixel of a row in the grid.", "rows": "Set of rows of type GridRowsProp.", "rowsPerPageOptions": "Select the pageSize dynamically using the component UI.", diff --git a/docs/translations/api-docs/data-grid/data-grid.json b/docs/translations/api-docs/data-grid/data-grid.json index 132e73b1d116e..37a6a1ab7574d 100644 --- a/docs/translations/api-docs/data-grid/data-grid.json +++ b/docs/translations/api-docs/data-grid/data-grid.json @@ -75,10 +75,10 @@ "onSortModelChange": "Callback fired when the sort model changes before a column is sorted.

Signature:
function(model: GridSortModel, details: GridCallbackDetails) => void
model: With all properties from GridSortModel.
details: Additional details for this callback.", "onStateChange": "Callback fired when the state of the grid is updated.

Signature:
function(state: GridState, event: MuiEvent, details: GridCallbackDetails) => void
state: The new state.
event: The event object.
details: Additional details for this callback.", "page": "The zero-based index of the current page.", - "pageSize": "Set the number of rows in one page.", + "pageSize": "Set the number of rows in one page. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows wanted on each page.", "paginationMode": "Pagination can be processed on the server or client-side. Set it to 'client' if you would like to handle the pagination on the client-side. Set it to 'server' if you would like to handle the pagination on the server-side.", "rowBuffer": "Number of extra rows to be rendered before/after the visible slice.", - "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop.", + "rowCount": "Set the total number of rows, if it is different than the length of the value rows prop. If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows.", "rowHeight": "Set the height in pixel of a row in the grid.", "rows": "Set of rows of type GridRowsProp.", "rowsPerPageOptions": "Select the pageSize dynamically using the component UI.", diff --git a/packages/grid/_modules_/grid/GridComponentProps.ts b/packages/grid/_modules_/grid/GridComponentProps.ts index 4455b20448e83..2a33b94baae31 100644 --- a/packages/grid/_modules_/grid/GridComponentProps.ts +++ b/packages/grid/_modules_/grid/GridComponentProps.ts @@ -8,7 +8,7 @@ import { GridMergedOptions, } from './models/gridOptions'; import { MuiEvent } from './models/muiEvent'; -import { GridRowId, GridRowIdGetter, GridRowsProp } from './models/gridRows'; +import { GridRowId, GridRowIdGetter, GridRowModel, GridRowsProp } from './models/gridRows'; import { ElementSize } from './models/elementSize'; import { GridColumnTypesRecord } from './models/colDef/gridColumnTypesRecord'; import { GridSortModel } from './models/gridSortModel'; @@ -68,6 +68,7 @@ interface GridComponentOtherProps { columnTypes?: GridColumnTypesRecord; /** * Set the total number of rows, if it is different than the length of the value `rows` prop. + * If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows. */ rowCount?: number; /** @@ -383,6 +384,7 @@ interface GridComponentOtherProps { onPageChange?: (page: number, details: GridCallbackDetails) => void; /** * Set the number of rows in one page. + * If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows wanted on each page. * @default 100 */ pageSize?: number; diff --git a/packages/grid/_modules_/grid/components/GridFooter.tsx b/packages/grid/_modules_/grid/components/GridFooter.tsx index 0f45eff837106..8241a564af934 100644 --- a/packages/grid/_modules_/grid/components/GridFooter.tsx +++ b/packages/grid/_modules_/grid/components/GridFooter.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import { useGridSelector } from '../hooks/utils/useGridSelector'; -import { gridRowCountSelector } from '../hooks/features/rows/gridRowsSelector'; +import { gridTopLevelRowCountSelector } from '../hooks/features/rows/gridRowsSelector'; import { selectedGridRowsCountSelector } from '../hooks/features/selection/gridSelectionSelector'; -import { gridVisibleRowCountSelector } from '../hooks/features/filter/gridFilterSelector'; +import { gridVisibleTopLevelRowCountSelector } from '../hooks/features/filter/gridFilterSelector'; import { useGridApiContext } from '../hooks/utils/useGridApiContext'; import { GridRowCount } from './GridRowCount'; import { GridSelectedRowCount } from './GridSelectedRowCount'; @@ -13,9 +13,9 @@ export const GridFooter = React.forwardRef 0 ? ( @@ -26,7 +26,7 @@ export const GridFooter = React.forwardRef + ) : null; const paginationElement = rootProps.pagination && diff --git a/packages/grid/_modules_/grid/components/GridRow.tsx b/packages/grid/_modules_/grid/components/GridRow.tsx index e1b3226eeeefb..4ce0f38a45f05 100644 --- a/packages/grid/_modules_/grid/components/GridRow.tsx +++ b/packages/grid/_modules_/grid/components/GridRow.tsx @@ -164,7 +164,9 @@ function GridRow(props: React.HTMLAttributes & GridRowProps) { const editCellState = editRowsState[rowId] ? editRowsState[rowId][column.field] : null; let content: React.ReactNode = null; - if (editCellState == null && column.renderCell) { + const skipRender = !column.shouldRenderAutoGeneratedRows && cellParams.rowNode?.isAutoGenerated; + + if (editCellState == null && column.renderCell && !skipRender) { content = column.renderCell({ ...cellParams, api: apiRef.current }); // TODO move to GridCell classNames.push( @@ -172,7 +174,7 @@ function GridRow(props: React.HTMLAttributes & GridRowProps) { ); } - if (editCellState != null && column.renderEditCell) { + if (editCellState != null && column.renderEditCell && !skipRender) { const params = { ...cellParams, ...editCellState, api: apiRef.current }; content = column.renderEditCell(params); // TODO move to GridCell @@ -198,13 +200,13 @@ function GridRow(props: React.HTMLAttributes & GridRowProps) { cells.push( (null); const rootRef = React.useRef(null); const handleRef = useForkRef(ref, rootRef); @@ -123,19 +121,11 @@ const GridVirtualScroller = React.forwardRef { - if (rootProps.pagination && rootProps.paginationMode === 'client') { - const start = paginationState.pageSize * paginationState.page; - return visibleSortedRowEntries.slice(start, start + paginationState.pageSize); - } - return visibleSortedRowEntries; - }, [paginationState, rootProps.pagination, rootProps.paginationMode, visibleSortedRowEntries]); - const computeRenderContext = React.useCallback(() => { if (disableVirtualization) { return { firstRowIndex: 0, - lastRowIndex: rowsInCurrentPage.length, + lastRowIndex: currentPage.rows.length, firstColumnIndex: 0, lastColumnIndex: visibleColumns.length, }; @@ -144,7 +134,7 @@ const GridVirtualScroller = React.forwardRef { - if (!renderContext || containerWidth == null) { + if (!currentPage.range || !renderContext || containerWidth == null) { return null; } @@ -266,7 +256,7 @@ const GridVirtualScroller = React.forwardRef, @@ -316,10 +305,10 @@ const GridVirtualScroller = React.forwardRef GridColumns; } diff --git a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts index cd9cc074cebca..56a9a0cc56c3f 100644 --- a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/gridRowGroupsPreProcessingApi.ts @@ -1,24 +1,27 @@ import { GridRowTreeConfig, GridRowId, GridRowsLookup } from '../../../models/gridRows'; -export type RowGroupParams = { +export interface GridRowGroupParams { ids: GridRowId[]; idRowsLookup: GridRowsLookup; -}; +} export interface GridRowGroupingResult { tree: GridRowTreeConfig; - paths: Record; + treeDepth: number; + ids: GridRowId[]; idRowsLookup: GridRowsLookup; } -export type GridRowGroupingPreProcessing = (params: RowGroupParams) => GridRowGroupingResult | null; +export type GridRowGroupingPreProcessing = ( + params: GridRowGroupParams, +) => GridRowGroupingResult | null; export interface GridRowGroupsPreProcessingApi { /** - * Register a column pre-processing and emit an event to re-apply the row grouping pre-processing + * Register a column pre-processing and emit an event to re-apply the row grouping pre-processing. * @param {string} processingName Name of the pre-processing. Used to clean the previous version of the pre-processing. * @param {GridRowGroupingPreProcessing} columnsPreProcessing Pre-processing to register. - * @ignore - do not document + * @ignore - do not document. */ unstable_registerRowGroupsBuilder: ( processingName: string, @@ -26,11 +29,11 @@ export interface GridRowGroupsPreProcessingApi { ) => void; /** - * Apply the first row grouping pre-processing that does not return null - * @param {GridRowsLookup} rowsLookup. Lookup of the rows to group - * @param {GridRowId[]} List of the rows IDs - * @returns {GridRowGroupingResult} The grouped rows - * @ignore - do not document + * Apply the first row grouping pre-processing that does not return null. + * @param {GridRowsLookup} rowsLookup. Lookup of the rows to group. + * @param {GridRowId[]} List of the rows IDs. + * @returns {GridRowGroupingResult} The grouped rows. + * @ignore - do not document. */ - unstable_groupRows: (params: RowGroupParams) => GridRowGroupingResult; + unstable_groupRows: (params: GridRowGroupParams) => GridRowGroupingResult; } diff --git a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts index 7f9bf2fae5461..6c42ae9fd96b9 100644 --- a/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts +++ b/packages/grid/_modules_/grid/hooks/core/rowGroupsPerProcessing/useGridRowGroupsPreProcessing.ts @@ -1,5 +1,6 @@ import * as React from 'react'; import { GridApiRef } from '../../../models/api/gridApiRef'; +import { GridRowTreeConfig } from '../../../models/gridRows'; import { GridRowGroupsPreProcessingApi, GridRowGroupingPreProcessing, @@ -8,11 +9,20 @@ import { import { GridEvents } from '../../../constants/eventsConstants'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; -const getFlatRowTree: GridRowGroupingPreProcessing = (params) => ({ - tree: new Map(params.ids.map((id) => [id.toString(), { id, depth: 0 }])), - paths: Object.fromEntries(params.ids.map((id) => [id, [id.toString()]])), - idRowsLookup: params.idRowsLookup, -}); +const getFlatRowTree: GridRowGroupingPreProcessing = ({ ids, idRowsLookup }) => { + const tree: GridRowTreeConfig = {}; + for (let i = 0; i < ids.length; i += 1) { + const rowId = ids[i]; + tree[rowId] = { id: rowId, depth: 0, parent: null, groupingValue: '' }; + } + + return { + tree, + treeDepth: 1, + idRowsLookup, + ids, + }; +}; export const useGridRowGroupsPreProcessing = (apiRef: GridApiRef) => { const rowGroupsPreProcessingRef = React.useRef( diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts index f28f8db4ef420..e08ee03575d3a 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterSelector.ts @@ -1,9 +1,9 @@ import { createSelector } from 'reselect'; import { GridFilterItem } from '../../../models/gridFilterItem'; import { GridState } from '../../../models/gridState'; -import { gridRowCountSelector } from '../rows/gridRowsSelector'; import { gridSortedRowEntriesSelector } from '../sorting/gridSortingSelector'; import { gridColumnLookupSelector } from '../columns/gridColumnsSelector'; +import { gridRowTreeDepthSelector, gridRowTreeSelector } from '../rows'; export const gridFilterStateSelector = (state: GridState) => state.filter; @@ -17,6 +17,11 @@ export const gridVisibleRowsLookupSelector = createSelector( (filterState) => filterState.visibleRowsLookup, ); +export const gridFilteredDescendantCountLookupSelector = createSelector( + gridFilterStateSelector, + (filterState) => filterState.filteredDescendantCountLookup, +); + export const gridVisibleSortedRowEntriesSelector = createSelector( gridVisibleRowsLookupSelector, gridSortedRowEntriesSelector, @@ -29,17 +34,34 @@ export const gridVisibleSortedRowIdsSelector = createSelector( (visibleSortedRowEntries) => visibleSortedRowEntries.map((row) => row.id), ); -export const gridVisibleRowCountSelector = createSelector( - gridFilterStateSelector, - gridRowCountSelector, - (filterState, totalRowsCount) => { - if (filterState.visibleRows == null) { - return totalRowsCount; +/** + * @deprecated Use `gridVisibleSortedRowIdsSelector` instead + */ +export const gridVisibleRowsSelector = gridVisibleSortedRowIdsSelector; + +export const gridVisibleSortedTopLevelRowEntriesSelector = createSelector( + gridVisibleSortedRowEntriesSelector, + gridRowTreeSelector, + gridRowTreeDepthSelector, + (visibleSortedRows, rowTree, rowTreeDepth) => { + if (rowTreeDepth < 2) { + return visibleSortedRows; } - return filterState.visibleRows.length; + + return visibleSortedRows.filter((row) => rowTree[row.id]?.depth === 0); }, ); +export const gridVisibleRowCountSelector = createSelector( + gridVisibleSortedRowEntriesSelector, + (visibleSortedRows) => visibleSortedRows.length, +); + +export const gridVisibleTopLevelRowCountSelector = createSelector( + gridVisibleSortedTopLevelRowEntriesSelector, + (visibleSortedTopLevelRows) => visibleSortedTopLevelRows.length, +); + export const gridFilterActiveItemsSelector = createSelector( gridFilterModelSelector, gridColumnLookupSelector, diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts index b07e7dbbfe0e0..641f7865b38c5 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterState.ts @@ -9,8 +9,21 @@ export const getDefaultGridFilterModel: () => GridFilterModel = () => ({ export interface GridFilterState { filterModel: GridFilterModel; + + /** + * Visibility status for each row. + * A row is visible if it is passing the filters AND if its parent is expanded. + * If a row is not registered in this lookup, it is visible. + */ visibleRowsLookup: Record; - visibleRows: GridRowId[] | null; + + /** + * Amount of descendants that are passing the filters. + * For the Tree Data, it includes all the intermediate depth levels (= amount of children + amount of grand children + ...). + * For the Row Grouping by Column, it does not include the intermediate depth levels (= amount of descendant of maximum depth). + * If a row is not registered in this lookup, it is supposed to have no descendant passing the filters.. + */ + filteredDescendantCountLookup: Record; } export interface GridFilterInitialState { diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 2c40059d598bd..60323793d9195 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -5,7 +5,7 @@ import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridFilterApi } from '../../../models/api/gridFilterApi'; import { GridFeatureModeConstant } from '../../../models/gridFeatureMode'; import { GridFilterItem, GridLinkOperator } from '../../../models/gridFilterItem'; -import { GridRowId, GridRowModel } from '../../../models/gridRows'; +import { GridRowId, GridRowModel, GridRowTreeNodeConfig } from '../../../models/gridRows'; import { isDeepEqual } from '../../../utils/utils'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; @@ -13,12 +13,12 @@ import { useGridLogger } from '../../utils/useGridLogger'; import { filterableGridColumnsIdsSelector } from '../columns/gridColumnsSelector'; import { useGridState } from '../../utils/useGridState'; import { GridPreferencePanelsValue } from '../preferencesPanel/gridPreferencePanelsValue'; -import { gridSortedRowIdsSelector } from '../sorting/gridSortingSelector'; import { getDefaultGridFilterModel } from './gridFilterState'; import { GridFilterModel } from '../../../models/gridFilterModel'; import { gridVisibleSortedRowEntriesSelector, gridFilterModelSelector } from './gridFilterSelector'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; +import { gridRowIdsSelector, gridRowTreeDepthSelector, gridRowTreeSelector } from '../rows'; type GridFilterItemApplier = (rowId: GridRowId) => boolean; @@ -48,6 +48,7 @@ export const useGridFilter = ( | 'onFilterModelChange' | 'filterMode' | 'disableMultipleColumnsFiltering' + | 'disableChildrenFiltering' >, ): void => { const logger = useGridLogger(apiRef, 'useGridFilter'); @@ -65,7 +66,7 @@ export const useGridFilter = ( props.initialState?.filter?.filterModel ?? getDefaultGridFilterModel(), visibleRowsLookup: {}, - visibleRows: null, + filteredDescendantCountLookup: {}, }, }; }); @@ -148,54 +149,116 @@ export const useGridFilter = ( [apiRef], ); + /** + * Generate the `visibleRowsLookup` and `visibleDescendantsCountLookup` for the current `filterModel` + * If the tree is not flat, we have to create the lookups even with "server" filtering or 0 filter item to remove to collapsed rows. + */ const applyFilters = React.useCallback(() => { setGridState((state) => { const filterModel = gridFilterModelSelector(state); + const rowIds = gridRowIdsSelector(state); + const rowTree = gridRowTreeSelector(state); + const shouldApplyTreeFiltering = gridRowTreeDepthSelector(state) > 1; + const filteringMethod = + props.filterMode === GridFeatureModeConstant.client + ? buildAggregatedFilterApplier(filterModel) + : null; - if (props.filterMode === GridFeatureModeConstant.server) { - return { - ...state, - filter: { - ...state.filter, - visibleRowsLookup: {}, - visibleRows: null, - }, + const visibleRowsLookup: Record = {}; + const filteredDescendantCountLookup: Record = {}; + if (shouldApplyTreeFiltering) { + // A node is visible if + // - One of its children is passing the filter + // - It is passing the filter + const filterTreeNode = ( + node: GridRowTreeNodeConfig, + isParentMatchingFilters: boolean, + isParentExpanded: boolean, + ): number => { + const shouldSkipFilters = props.disableChildrenFiltering && node.depth > 0; + + let isMatchingFilters: boolean | null; + if (shouldSkipFilters) { + isMatchingFilters = null; + } else if (!filteringMethod) { + isMatchingFilters = true; + } else { + isMatchingFilters = filteringMethod(node.id); + } + + let filteredDescendantCount = 0; + node.children?.forEach((childId) => { + const childNode = rowTree[childId]; + const childSubTreeSize = filterTreeNode( + childNode, + isMatchingFilters ?? isParentMatchingFilters, + !!node.expanded, + ); + + filteredDescendantCount += childSubTreeSize; + }); + + let shouldPassFilters: boolean; + switch (isMatchingFilters) { + case true: { + shouldPassFilters = true; + break; + } + case false: { + shouldPassFilters = filteredDescendantCount > 0; + break; + } + default: { + shouldPassFilters = isParentMatchingFilters; + break; + } + } + + visibleRowsLookup[node.id] = shouldPassFilters && isParentExpanded; + + if (!shouldPassFilters) { + return 0; + } + + filteredDescendantCountLookup[node.id] = filteredDescendantCount; + + // TODO: For column grouping, we do not want to count the intermediate depth nodes in the visible descendant count + return filteredDescendantCount + 1; }; - } - // No filter to apply - const filteringMethod = buildAggregatedFilterApplier(filterModel); - if (!filteringMethod) { - return { - ...state, - filter: { - ...state.filter, - visibleRowsLookup: {}, - visibleRows: null, - }, - }; + const nodes = Object.values(rowTree); + for (let i = 0; i < nodes.length; i += 1) { + const node = nodes[i]; + if (node.depth === 0) { + filterTreeNode(node, true, true); + } + } + } else if (props.filterMode === GridFeatureModeConstant.client && filteringMethod) { + for (let i = 0; i < rowIds.length; i += 1) { + const rowId = rowIds[i]; + visibleRowsLookup[rowId] = filteringMethod(rowId); + } } - const rowIds = gridSortedRowIdsSelector(apiRef.current.state); - const visibleRowsLookup: Record = {}; - rowIds.forEach((rowId) => { - visibleRowsLookup[rowId] = filteringMethod(rowId); - }); - return { ...state, filter: { ...state.filter, visibleRowsLookup, - visibleRows: Object.entries(visibleRowsLookup) - .filter(([, isVisible]) => isVisible) - .map(([id]) => id), + filteredDescendantCountLookup, }, }; }); apiRef.current.publishEvent(GridEvents.visibleRowsSet); forceUpdate(); - }, [apiRef, setGridState, forceUpdate, props.filterMode, buildAggregatedFilterApplier]); + }, [ + apiRef, + setGridState, + forceUpdate, + props.filterMode, + buildAggregatedFilterApplier, + props.disableChildrenFiltering, + ]); const upsertFilterItem = React.useCallback( (item) => { @@ -337,6 +400,8 @@ export const useGridFilter = ( 'FilterApi', ); + // TODO: Do not rerun applyFilters if no filterItem has been removed + // TODO: Use the column lookup to avoid linear complexity if `columnsIds.find` const onColUpdated = React.useCallback(() => { logger.debug('onColUpdated - GridColumns changed, applying filters'); const filterModel = gridFilterModelSelector(apiRef.current.state); @@ -357,8 +422,19 @@ export const useGridFilter = ( } }, [apiRef, logger, props.filterModel]); + // The filter options have changed + const isFirstRender = React.useRef(true); + React.useEffect(() => { + if (isFirstRender.current) { + isFirstRender.current = false; + return; + } + apiRef.current.unsafe_applyFilters(); + }, [apiRef, props.disableChildrenFiltering]); + useFirstRender(() => apiRef.current.unsafe_applyFilters()); useGridApiEventHandler(apiRef, GridEvents.rowsSet, apiRef.current.unsafe_applyFilters); + useGridApiEventHandler(apiRef, GridEvents.rowExpansionChange, apiRef.current.unsafe_applyFilters); useGridApiEventHandler(apiRef, GridEvents.columnsChange, onColUpdated); }; diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts index 1ed45757237db..ddaafc846bc16 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/gridPaginationSelector.ts @@ -1,7 +1,12 @@ import { createSelector } from 'reselect'; import { GridState } from '../../../models/gridState'; -import { gridVisibleSortedRowIdsSelector } from '../filter/gridFilterSelector'; +import { + gridVisibleSortedRowEntriesSelector, + gridVisibleSortedRowIdsSelector, + gridVisibleSortedTopLevelRowEntriesSelector, +} from '../filter/gridFilterSelector'; import { GridPaginationState } from './gridPaginationState'; +import { gridRowTreeDepthSelector, gridRowTreeSelector } from '../rows/gridRowsSelector'; export const gridPaginationSelector = (state: GridState): GridPaginationState => state.pagination; @@ -15,13 +20,87 @@ export const gridPageSizeSelector = createSelector( (pagination) => pagination.pageSize, ); -export const gridPaginatedVisibleSortedGridRowIdsSelector = createSelector( +export const gridPaginationRowRangeSelector = createSelector( gridPaginationSelector, + gridRowTreeSelector, + gridRowTreeDepthSelector, + gridVisibleSortedRowEntriesSelector, + gridVisibleSortedTopLevelRowEntriesSelector, + (pagination, rowTree, rowTreeDepth, visibleSortedRowEntries, visibleSortedTopLevelRowEntries) => { + const visibleTopLevelRowCount = visibleSortedTopLevelRowEntries.length; + const topLevelFirstRowIndex = Math.min( + pagination.pageSize * pagination.page, + visibleTopLevelRowCount - 1, + ); + const topLevelLastRowIndex = Math.min( + topLevelFirstRowIndex + pagination.pageSize - 1, + visibleTopLevelRowCount - 1, + ); + + // The range contains no element + if (topLevelFirstRowIndex === -1 || topLevelLastRowIndex === -1) { + return null; + } + + // The tree is flat, their is no need to look for children + if (rowTreeDepth < 2) { + return { firstRowIndex: topLevelFirstRowIndex, lastRowIndex: topLevelLastRowIndex }; + } + + const topLevelFirstRow = visibleSortedTopLevelRowEntries[topLevelFirstRowIndex]; + const topLevelRowsInCurrentPageCount = topLevelLastRowIndex - topLevelFirstRowIndex + 1; + const firstRowIndex = visibleSortedRowEntries.findIndex( + (row) => row.id === topLevelFirstRow.id, + ); + let lastRowIndex = firstRowIndex; + let topLevelRowAdded = 0; + + while ( + lastRowIndex < visibleSortedRowEntries.length && + topLevelRowAdded <= topLevelRowsInCurrentPageCount + ) { + const row = visibleSortedRowEntries[lastRowIndex]; + const depth = rowTree[row.id].depth; + + if (topLevelRowAdded < topLevelRowsInCurrentPageCount || depth > 0) { + lastRowIndex += 1; + } + + if (depth === 0) { + topLevelRowAdded += 1; + } + } + + return { firstRowIndex, lastRowIndex: lastRowIndex - 1 }; + }, +); + +export const gridPaginatedVisibleSortedGridRowEntriesSelector = createSelector( + gridVisibleSortedRowEntriesSelector, + gridPaginationRowRangeSelector, + (visibleSortedRowEntries, paginationRange) => { + if (!paginationRange) { + return []; + } + + return visibleSortedRowEntries.slice( + paginationRange.firstRowIndex, + paginationRange.lastRowIndex + 1, + ); + }, +); + +export const gridPaginatedVisibleSortedGridRowIdsSelector = createSelector( gridVisibleSortedRowIdsSelector, - (pagination, visibleSortedRows) => { - const firstSelectedRowIndex = pagination.page * pagination.pageSize; - const lastSelectedRowIndex = firstSelectedRowIndex + pagination.pageSize; + gridPaginationRowRangeSelector, + (visibleSortedRowIds, paginationRange) => { + if (!paginationRange) { + return []; + } - return visibleSortedRows.slice(firstSelectedRowIndex, lastSelectedRowIndex); + return visibleSortedRowIds.slice( + paginationRange.firstRowIndex, + paginationRange.lastRowIndex + 1, + ); }, ); diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts index b03a1fcc11180..9933f2f6c43ff 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts @@ -11,7 +11,7 @@ import { GridEvents } from '../../../constants/eventsConstants'; import { GridComponentProps } from '../../../GridComponentProps'; import { GridPageApi } from '../../../models/api/gridPageApi'; import { GridPaginationState } from './gridPaginationState'; -import { gridVisibleRowCountSelector } from '../filter'; +import { gridVisibleTopLevelRowCountSelector } from '../filter'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { gridPageSelector } from './gridPaginationSelector'; @@ -56,7 +56,7 @@ export const useGridPage = ( })); const [, setGridState, forceUpdate] = useGridState(apiRef); - const visibleRowCount = useGridSelector(apiRef, gridVisibleRowCountSelector); + const visibleTopLevelRowCount = useGridSelector(apiRef, gridVisibleTopLevelRowCountSelector); apiRef.current.updateControlState({ stateId: 'page', @@ -84,7 +84,7 @@ export const useGridPage = ( React.useEffect(() => { setGridState((state) => { - const rowCount = props.rowCount !== undefined ? props.rowCount : visibleRowCount; + const rowCount = props.rowCount !== undefined ? props.rowCount : visibleTopLevelRowCount; const pageCount = getPageCount(rowCount, state.pagination.pageSize); const page = props.page == null ? state.pagination.page : props.page; @@ -99,7 +99,7 @@ export const useGridPage = ( }; }); forceUpdate(); - }, [setGridState, forceUpdate, visibleRowCount, props.rowCount, props.page, apiRef]); + }, [setGridState, forceUpdate, visibleTopLevelRowCount, props.rowCount, props.page, apiRef]); const handlePageSizeChange = React.useCallback( (pageSize: number) => { diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts index 9e08ac056ce1f..40ad066a9ff02 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsSelector.ts @@ -1,20 +1,28 @@ import { createSelector } from 'reselect'; import { GridState } from '../../../models/gridState'; -import { GridRowsState } from './gridRowsState'; export const gridRowsStateSelector = (state: GridState) => state.rows; export const gridRowCountSelector = createSelector( gridRowsStateSelector, - (rows: GridRowsState) => rows.totalRowCount, + (rows) => rows.totalRowCount, +); + +export const gridTopLevelRowCountSelector = createSelector( + gridRowsStateSelector, + (rows) => rows.totalTopLevelRowCount, ); export const gridRowsLookupSelector = createSelector( gridRowsStateSelector, - (rows: GridRowsState) => rows.idRowsLookup, + (rows) => rows.idRowsLookup, ); -export const gridRowIdsSelector = createSelector( +export const gridRowTreeSelector = createSelector(gridRowsStateSelector, (rows) => rows.tree); + +export const gridRowTreeDepthSelector = createSelector( gridRowsStateSelector, - (rows: GridRowsState) => rows.allRows, + (rows) => rows.treeDepth, ); + +export const gridRowIdsSelector = createSelector(gridRowsStateSelector, (rows) => rows.ids); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts index abea96ec43c36..eb444c2a611f3 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/gridRowsState.ts @@ -1,7 +1,15 @@ -import { GridRowId, GridRowModel } from '../../../models/gridRows'; +import { GridRowGroupingResult } from '../../core/rowGroupsPerProcessing'; -export interface GridRowsState { - idRowsLookup: Record; - allRows: GridRowId[]; +export interface GridRowsState extends GridRowGroupingResult { + /** + * Amount of rows before applying the filtering. + * It also count the expanded and collapsed children rows. + */ totalRowCount: number; + + /** + * Amount of rows before applying the filtering. + * It does not count the expanded children rows. + */ + totalTopLevelRowCount: number; } diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridParamsApi.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridParamsApi.ts index bcd4861278773..0fe78415b5206 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridParamsApi.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridParamsApi.ts @@ -63,8 +63,9 @@ export function useGridParamsApi(apiRef: GridApiRef) { const getBaseCellParams = React.useCallback( (id: GridRowId, field: string) => { const row = apiRef.current.getRow(id); + const rowNode = apiRef.current.unstable_getRowNode(id); - if (!row) { + if (!row || !rowNode) { throw new Error(`No row with id #${id} found`); } @@ -75,6 +76,7 @@ export function useGridParamsApi(apiRef: GridApiRef) { id, field, row, + rowNode, value: row[field], colDef: apiRef.current.getColumn(field), cellMode: apiRef.current.getCellMode(id, field), @@ -94,8 +96,9 @@ export function useGridParamsApi(apiRef: GridApiRef) { const colDef = apiRef.current.getColumn(field); const value = apiRef.current.getCellValue(id, field); const row = apiRef.current.getRow(id); + const rowNode = apiRef.current.unstable_getRowNode(id); - if (!row) { + if (!row || !rowNode) { throw new Error(`No row with id #${id} found`); } @@ -106,6 +109,7 @@ export function useGridParamsApi(apiRef: GridApiRef) { id, field, row, + rowNode, colDef, cellMode: apiRef.current.getCellMode(id, field), getValue: apiRef.current.getCellValue, diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 72720ebe78e90..5af2650e572a9 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -9,6 +9,7 @@ import { GridRowId, GridRowsProp, GridRowIdGetter, + GridRowTreeNodeConfig, } from '../../../models/gridRows'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; import { useGridLogger } from '../../utils/useGridLogger'; @@ -18,13 +19,32 @@ import { GridRowsState } from './gridRowsState'; import { gridRowCountSelector, gridRowsLookupSelector, + gridRowTreeSelector, gridRowIdsSelector, } from './gridRowsSelector'; +import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; +import { GridRowGroupParams } from '../../core/rowGroupsPerProcessing'; + +interface GridRowsInternalCacheState { + value: GridRowGroupParams; + + /** + * The value of the properties used by the grouping when the internal cache was created + * We are storing it instead of accessing it directly when storing the cache to avoid synchronization issues + */ + props: Pick; + + /** + * The rows as they were the last time all the rows have been updated at once + * It is used to avoid processing several time the same set of rows + */ + rowsBeforePartialUpdates: GridRowsProp; +} -export interface GridRowsInternalCache { - state: GridRowsState; +interface GridRowsInternalCache { + state: GridRowsInternalCacheState; timeout: NodeJS.Timeout | null; - lastUpdateMs: number | null; + lastUpdateMs: number; } function getGridRowId( @@ -37,29 +57,84 @@ function getGridRowId( return id; } -export function convertGridRowsPropToState( - rows: GridRowsProp, - propRowCount?: number, - rowIdGetter?: GridRowIdGetter, -): GridRowsState { - const state: GridRowsState = { - idRowsLookup: {}, - allRows: [], - totalRowCount: propRowCount && propRowCount > rows.length ? propRowCount : rows.length, +interface ConvertGridRowsPropToStateParams { + prevState: GridRowsInternalCacheState; + props?: Pick; + rows?: GridRowsProp; +} + +const convertGridRowsPropToState = ({ + prevState, + rows, + props: inputProps, +}: ConvertGridRowsPropToStateParams): GridRowsInternalCacheState => { + const props = inputProps ?? prevState.props; + + let value: GridRowGroupParams; + if (rows) { + value = { + idRowsLookup: {}, + ids: [], + }; + for (let i = 0; i < rows.length; i += 1) { + const row = rows[i]; + const id = getGridRowId(row, props.getRowId); + value.idRowsLookup[id] = row; + value.ids.push(id); + } + } else { + value = prevState.value; + } + + return { + value, + props, + rowsBeforePartialUpdates: rows ?? prevState.rowsBeforePartialUpdates, }; +}; - rows.forEach((rowData) => { - const id = getGridRowId(rowData, rowIdGetter); - state.allRows.push(id); - state.idRowsLookup[id] = rowData; - }); +const getRowsStateFromCache = ( + rowsCache: GridRowsInternalCache, + apiRef: GridApiRef, +): GridRowsState => { + const { + props: { rowCount: propRowCount = 0 }, + value, + } = rowsCache.state; + + const groupingResponse = apiRef.current.unstable_groupRows(value); + + const dataTopLevelRowCount = Object.values(groupingResponse.tree).filter( + (node) => node.parent == null, + ).length; + const totalRowCount = + propRowCount > groupingResponse.ids.length ? propRowCount : groupingResponse.ids.length; + const totalTopLevelRowCount = + propRowCount > dataTopLevelRowCount ? propRowCount : dataTopLevelRowCount; + + return { ...groupingResponse, totalRowCount, totalTopLevelRowCount }; +}; - return state; -} +// The cache is always redefined synchronously in `useGridStateInit` so this object don't need to be regenerated across DataGrid instances. +const INITIAL_GRID_ROWS_INTERNAL_CACHE: GridRowsInternalCache = { + state: { + value: { + idRowsLookup: {}, + ids: [], + }, + props: { + rowCount: undefined, + getRowId: undefined, + }, + rowsBeforePartialUpdates: [], + }, + timeout: null, + lastUpdateMs: 0, +}; /** - * @requires useGridSorting (method) - * TODO: Impossible priority - useGridSorting also needs to be after useGridRows (which causes all the existence check for apiRef.current.apiRef.current.getSortedRowIds) + * @requires useGridRowGroupsPreProcessing (method) + * @requires useGridSorting (method) - can be after, async only (TODO: Remove after moving the `getRowIndex` and `getRowIdFromIndex` to `useGridSorting`) */ export const useGridRows = ( apiRef: GridApiRef, @@ -71,46 +146,33 @@ export const useGridRows = ( } const logger = useGridLogger(apiRef, 'useGridRows'); - - const rowsCache = React.useRef({ - state: { - idRowsLookup: {}, - allRows: [], - totalRowCount: 0, - }, - timeout: null, - lastUpdateMs: Date.now(), - }); + const rowsCache = React.useRef(INITIAL_GRID_ROWS_INTERNAL_CACHE); useGridStateInit(apiRef, (state) => { - rowsCache.current.state = convertGridRowsPropToState( - props.rows, - props.rowCount, - props.getRowId, - ); - - return { ...state, rows: rowsCache.current.state }; + rowsCache.current.state = convertGridRowsPropToState({ + rows: props.rows, + props: { + rowCount: props.rowCount, + getRowId: props.getRowId, + }, + prevState: rowsCache.current.state, + }); + rowsCache.current.lastUpdateMs = Date.now(); + + return { ...state, rows: getRowsStateFromCache(rowsCache.current, apiRef) }; }); const [, setGridState, forceUpdate] = useGridState(apiRef); + // TODO: Move in useGridSorting const getRowIndex = React.useCallback( - (id) => { - if (apiRef.current.getSortedRowIds) { - return apiRef.current.getSortedRowIds().indexOf(id); - } - return apiRef.current.state.rows.allRows.indexOf(id); - }, + (id) => apiRef.current.getSortedRowIds().indexOf(id), [apiRef], ); + // TODO: Move in useGridSorting const getRowIdFromRowIndex = React.useCallback( - (index) => { - if (apiRef.current.getSortedRowIds) { - return apiRef.current.getSortedRowIds()[index]; - } - return apiRef.current.state.rows.allRows[index]; - }, + (index) => apiRef.current.getSortedRowIds()[index], [apiRef], ); @@ -120,11 +182,14 @@ export const useGridRows = ( ); const throttledRowsChange = React.useCallback( - (newState: GridRowsState, throttle: boolean) => { + (newState: GridRowsInternalCacheState, throttle: boolean) => { const run = () => { rowsCache.current.timeout = null; rowsCache.current.lastUpdateMs = Date.now(); - setGridState((state) => ({ ...state, rows: rowsCache.current.state })); + setGridState((state) => ({ + ...state, + rows: getRowsStateFromCache(rowsCache.current, apiRef), + })); apiRef.current.publishEvent(GridEvents.rowsSet); forceUpdate(); }; @@ -142,10 +207,7 @@ export const useGridRows = ( } const throttleRemainingTimeMs = - rowsCache.current.lastUpdateMs === null - ? 0 - : props.throttleRowsMs - (Date.now() - rowsCache.current.lastUpdateMs); - + props.throttleRowsMs - (Date.now() - rowsCache.current.lastUpdateMs); if (throttleRemainingTimeMs > 0) { rowsCache.current.timeout = setTimeout(run, throttleRemainingTimeMs); return; @@ -159,9 +221,12 @@ export const useGridRows = ( const setRows = React.useCallback( (rows) => { logger.debug(`Updating all rows, new length ${rows.length}`); - throttledRowsChange(convertGridRowsPropToState(rows, props.rowCount, props.getRowId), true); + throttledRowsChange( + convertGridRowsPropToState({ rows, prevState: rowsCache.current.state }), + true, + ); }, - [logger, throttledRowsChange, props.rowCount, props.getRowId], + [logger, throttledRowsChange], ); const updateRows = React.useCallback( @@ -185,43 +250,41 @@ export const useGridRows = ( const deletedRowIds: GridRowId[] = []; - const idRowsLookup = { ...rowsCache.current.state.idRowsLookup }; - let allRows = [...rowsCache.current.state.allRows]; + const newStateValue: GridRowGroupParams = { + idRowsLookup: { ...rowsCache.current.state.value.idRowsLookup }, + ids: [...rowsCache.current.state.value.ids], + }; uniqUpdates.forEach((partialRow, id) => { // eslint-disable-next-line no-underscore-dangle if (partialRow._action === 'delete') { - delete idRowsLookup[id]; + delete newStateValue.idRowsLookup[id]; deletedRowIds.push(id); return; } const oldRow = apiRef.current.getRow(id); if (!oldRow) { - idRowsLookup[id] = partialRow; - allRows.push(id); + newStateValue.idRowsLookup[id] = partialRow; + newStateValue.ids.push(id); return; } - idRowsLookup[id] = { ...apiRef.current.getRow(id), ...partialRow }; + newStateValue.idRowsLookup[id] = { ...apiRef.current.getRow(id), ...partialRow }; }); if (deletedRowIds.length > 0) { - allRows = allRows.filter((id) => !deletedRowIds.includes(id)); + newStateValue.ids = newStateValue.ids.filter((id) => !deletedRowIds.includes(id)); } - const totalRowCount = - props.rowCount && props.rowCount > allRows.length ? props.rowCount : allRows.length; - - const state: GridRowsState = { - idRowsLookup, - allRows, - totalRowCount, + const state: GridRowsInternalCacheState = { + ...rowsCache.current.state, + value: newStateValue, }; throttledRowsChange(state, true); }, - [apiRef, props.getRowId, props.rowCount, throttledRowsChange], + [apiRef, props.getRowId, throttledRowsChange], ); const getRowModels = React.useCallback(() => { @@ -241,6 +304,33 @@ export const useGridRows = ( [apiRef], ); + const setRowExpansion = React.useCallback( + (id, isExpanded) => { + const currentNode = apiRef.current.unstable_getRowNode(id); + if (!currentNode) { + throw new Error(`MUI: No row with id #${id} found`); + } + const newNode: GridRowTreeNodeConfig = { ...currentNode, expanded: isExpanded }; + setGridState((state) => { + return { + ...state, + rows: { + ...state.rows, + tree: { ...state.rows.tree, [id]: newNode }, + }, + }; + }); + forceUpdate(); + apiRef.current.publishEvent(GridEvents.rowExpansionChange, newNode); + }, + [apiRef, setGridState, forceUpdate], + ); + + const getRowNode = React.useCallback( + (id) => gridRowTreeSelector(apiRef.current.state)[id] ?? null, + [apiRef], + ); + React.useEffect(() => { return () => { if (rowsCache.current.timeout !== null) { @@ -259,13 +349,48 @@ export const useGridRows = ( return; } + // The new rows have already been applied (most likely in the `GridEvents.rowGroupsPreProcessingChange` listener) + if (rowsCache.current.state.rowsBeforePartialUpdates === props.rows) { + return; + } + logger.debug(`Updating all rows, new length ${props.rows.length}`); throttledRowsChange( - convertGridRowsPropToState(props.rows, props.rowCount, props.getRowId), + convertGridRowsPropToState({ + rows: props.rows, + props: { rowCount: props.rowCount, getRowId: props.getRowId }, + prevState: rowsCache.current.state, + }), false, ); }, [props.rows, props.rowCount, props.getRowId, logger, throttledRowsChange]); + const handleGroupRows = React.useCallback(() => { + logger.info(`Row grouping pre-processing have changed, regenerating the row tree`); + + let rows: GridRowsProp | undefined; + if (rowsCache.current.state.rowsBeforePartialUpdates === props.rows) { + // The `props.rows` has not changed since the last row grouping + // We can keep the potential updates stored in `inputRowsAfterUpdates` on the new grouping + rows = undefined; + } else { + // The `props.rows` has changed since the last row grouping + // We must use the new `props.rows` on the new grouping + // This occurs because this event is triggered before the `useEffect` on the rows when both the grouping pre-processing and the rows changes on the same render + rows = props.rows; + } + throttledRowsChange( + convertGridRowsPropToState({ + rows, + props: { rowCount: props.rowCount, getRowId: props.getRowId }, + prevState: rowsCache.current.state, + }), + false, + ); + }, [logger, throttledRowsChange, props.rowCount, props.getRowId, props.rows]); + + useGridApiEventHandler(apiRef, GridEvents.rowGroupsPreProcessingChange, handleGroupRows); + const rowApi: GridRowApi = { getRowIndex, getRowIdFromRowIndex, @@ -275,6 +400,8 @@ export const useGridRows = ( getAllRowIds, setRows, updateRows, + unstable_setRowExpansion: setRowExpansion, + unstable_getRowNode: getRowNode, }; useGridApiMethod(apiRef, rowApi, 'GridRowApi'); diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts index 2db211ee9101c..b66cb4785e452 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingSelector.ts @@ -1,17 +1,13 @@ import { createSelector } from 'reselect'; -import { GridRowId } from '../../../models/gridRows'; import { GridSortDirection, GridSortModel } from '../../../models/gridSortModel'; import { GridState } from '../../../models/gridState'; -import { gridRowsLookupSelector, gridRowIdsSelector } from '../rows/gridRowsSelector'; -import { GridSortingState } from './gridSortingState'; +import { gridRowsLookupSelector } from '../rows/gridRowsSelector'; const gridSortingStateSelector = (state: GridState) => state.sorting; export const gridSortedRowIdsSelector = createSelector( gridSortingStateSelector, - gridRowIdsSelector, - (sortingState: GridSortingState, allRows: GridRowId[]) => - sortingState.sortedRows.length ? sortingState.sortedRows : allRows, + (sortingState) => sortingState.sortedRows, ); export const gridSortedRowEntriesSelector = createSelector( diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 7917305a8722f..b178c3f1e8358 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -7,7 +7,7 @@ import { GridCellValue } from '../../../models/gridCell'; import { GridColDef } from '../../../models/colDef/gridColDef'; import { GridFeatureModeConstant } from '../../../models/gridFeatureMode'; import { GridColumnHeaderParams } from '../../../models/params/gridColumnHeaderParams'; -import { GridRowId } from '../../../models/gridRows'; +import { GridRowId, GridRowTreeNodeConfig } from '../../../models/gridRows'; import { GridFieldComparatorList, GridSortItem, @@ -27,6 +27,7 @@ import { gridSortedRowIdsSelector, gridSortedRowEntriesSelector, } from './gridSortingSelector'; +import { gridRowIdsSelector, gridRowTreeDepthSelector, gridRowTreeSelector } from '../rows'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; @@ -45,6 +46,7 @@ export const useGridSorting = ( | 'sortingOrder' | 'sortingMode' | 'disableMultipleColumnsSorting' + | 'disableChildrenSorting' >, ) => { const logger = useGridLogger(apiRef, 'useGridSorting'); @@ -170,39 +172,109 @@ export const useGridSorting = ( ); const applySorting = React.useCallback(() => { - let sortedRows = apiRef.current.getAllRowIds(); - if (props.sortingMode === GridFeatureModeConstant.server) { logger.debug('Skipping sorting rows as sortingMode = server'); setGridState((state) => ({ ...state, - sorting: { ...state.sorting, sortedRows }, + sorting: { ...state.sorting, sortedRows: gridRowIdsSelector(state) }, })); return; } - const sortModel = gridSortModelSelector(apiRef.current.state); - - if (sortModel.length > 0) { + setGridState((state) => { + const rowIds = gridRowIdsSelector(state); + const rowTree = gridRowTreeSelector(state); + const shouldApplyTreeSorting = gridRowTreeDepthSelector(state) > 1; + const sortModel = gridSortModelSelector(state); const comparatorList = buildComparatorList(sortModel); - logger.debug('Sorting rows with ', sortModel); - sortedRows = sortedRows - .map((id) => { - return comparatorList.map((colComparator) => { - return getSortCellParams(id, colComparator.field); + const aggregatedComparator = comparatorListAggregate(comparatorList); + + const sortRowList = (rowList: GridRowTreeNodeConfig[]) => + rowList + .map((value) => ({ + value, + params: comparatorList.map((colComparator) => + getSortCellParams(value.id, colComparator.field), + ), + })) + .sort((a, b) => aggregatedComparator(a.params, b.params)) + .map((row) => row.value.id); + + let sortedRows: GridRowId[] = []; + if (shouldApplyTreeSorting) { + // Group the rows by parent + const groupedByParentRows = new Map([ + [null, []], + ]); + for (let i = 0; i < rowIds.length; i += 1) { + const rowId = rowIds[i]; + const node = rowTree[rowId]; + let group = groupedByParentRows.get(node.parent); + if (!group) { + group = []; + groupedByParentRows.set(node.parent, group); + } + group.push(node); + } + + // Apply the sorting to each list of children + const sortedGroupedByParentRows = new Map(); + groupedByParentRows.forEach((rowList, parent) => { + if (rowList.length === 0) { + sortedGroupedByParentRows.set(parent, []); + } else { + const depth = rowList[0].depth; + if (depth > 0 && props.disableChildrenSorting) { + sortedGroupedByParentRows.set( + parent, + rowList.map((row) => row.id), + ); + } else if (comparatorList.length === 0) { + sortedGroupedByParentRows.set( + parent, + rowList.map((row) => row.id), + ); + } else { + sortedGroupedByParentRows.set(parent, sortRowList(rowList)); + } + } + }); + + // Flatten the sorted lists to have children just after their parent + const insertRowListIntoSortedRows = (startIndex: number, rowList: GridRowId[]) => { + sortedRows = [ + ...sortedRows.slice(0, startIndex), + ...rowList, + ...sortedRows.slice(startIndex), + ]; + + let treeSize = 0; + rowList.forEach((rowId) => { + treeSize += 1; + const children = sortedGroupedByParentRows.get(rowId); + if (children?.length) { + const subTreeSize = insertRowListIntoSortedRows(startIndex + treeSize, children); + treeSize += subTreeSize; + } }); - }) - .sort(comparatorListAggregate(comparatorList)) - .map((field) => field[0].id); - } - setGridState((state) => ({ - ...state, - sorting: { ...state.sorting, sortedRows }, - })); + return treeSize; + }; + + insertRowListIntoSortedRows(0, sortedGroupedByParentRows.get(null)!); + } else if (comparatorList.length === 0) { + sortedRows = rowIds; + } else { + sortedRows = sortRowList(Object.values(rowTree)); + } + + return { + ...state, + sorting: { ...state.sorting, sortedRows }, + }; + }); forceUpdate(); }, [ - apiRef, logger, getSortCellParams, setGridState, @@ -210,6 +282,7 @@ export const useGridSorting = ( buildComparatorList, comparatorListAggregate, props.sortingMode, + props.disableChildrenSorting, ]); const setSortModel = React.useCallback( @@ -273,6 +346,16 @@ export const useGridSorting = ( } }, [apiRef, props.sortModel]); + // The sorting options have changed + const isFirstRender = React.useRef(true); + React.useEffect(() => { + if (isFirstRender.current) { + isFirstRender.current = false; + return; + } + apiRef.current.applySorting(); + }, [apiRef, props.disableChildrenSorting]); + useFirstRender(() => apiRef.current.applySorting()); const handleColumnHeaderClick = React.useCallback( diff --git a/packages/grid/_modules_/grid/hooks/utils/useCurrentPageRows.ts b/packages/grid/_modules_/grid/hooks/utils/useCurrentPageRows.ts new file mode 100644 index 0000000000000..7949ff1122378 --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/utils/useCurrentPageRows.ts @@ -0,0 +1,45 @@ +import * as React from 'react'; +import { GridComponentProps } from '../../GridComponentProps'; +import { + gridPaginationRowRangeSelector, + gridPaginatedVisibleSortedGridRowEntriesSelector, +} from '../features/pagination/gridPaginationSelector'; +import { gridVisibleSortedRowEntriesSelector } from '../features/filter/gridFilterSelector'; +import type { GridApiRef, GridRowEntry } from '../../models'; +import { useGridState } from './useGridState'; + +/** + * Compute the list of the rows in the current page + * - If the pagination is disabled or in server mode, it equals all the visible rows + * - If the row tree has several layers, it contains up to `state.pageSize` top level rows and all their descendants + * - If the row tree is flat, it only contains up to `state.pageSize` rows + */ +export const useCurrentPageRows = ( + apiRef: GridApiRef, + props: Pick, +) => { + const [state] = useGridState(apiRef); + + let range: { firstRowIndex: number; lastRowIndex: number } | null; + let rows: GridRowEntry[]; + + if (props.pagination && props.paginationMode === 'client') { + range = gridPaginationRowRangeSelector(state); + rows = gridPaginatedVisibleSortedGridRowEntriesSelector(state); + } else { + rows = gridVisibleSortedRowEntriesSelector(state); + if (rows.length === 0) { + range = null; + } else { + range = { firstRowIndex: 0, lastRowIndex: rows.length - 1 }; + } + } + + return React.useMemo( + () => ({ + rows, + range, + }), + [rows, range], + ); +}; diff --git a/packages/grid/_modules_/grid/models/api/gridRowApi.ts b/packages/grid/_modules_/grid/models/api/gridRowApi.ts index 9c16e4126e32b..35704259d4200 100644 --- a/packages/grid/_modules_/grid/models/api/gridRowApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridRowApi.ts @@ -1,4 +1,4 @@ -import { GridRowModel, GridRowId, GridRowModelUpdate } from '../gridRows'; +import { GridRowModel, GridRowId, GridRowModelUpdate, GridRowTreeNodeConfig } from '../gridRows'; /** * The Row API interface that is available in the grid `apiRef`. @@ -47,4 +47,18 @@ export interface GridRowApi { * @returns {GridRowModel} The row data. */ getRow: (id: GridRowId) => GridRowModel | null; + /** + * Gets the row node from the internal tree structure. + * @param {GridRowId} id The id of the row. + * @returns {GridRowTreeNodeConfig} The row data. + * @ignore - do not document. + */ + unstable_getRowNode: (id: GridRowId) => GridRowTreeNodeConfig | null; + /** + * Expand or collapse a row children. + * @param {GridRowId} id the ID of the row to expand or collapse. + * @param {boolean} isExpanded A boolean indicating if the row must be expanded or collapsed. + * @ignore - do not document. + */ + unstable_setRowExpansion: (id: GridRowId, isExpanded: boolean) => void; } diff --git a/packages/grid/_modules_/grid/models/colDef/gridColDef.ts b/packages/grid/_modules_/grid/models/colDef/gridColDef.ts index 8452783592038..2c5f9e2bc947a 100644 --- a/packages/grid/_modules_/grid/models/colDef/gridColDef.ts +++ b/packages/grid/_modules_/grid/models/colDef/gridColDef.ts @@ -165,6 +165,11 @@ export interface GridColDef { * Allows setting the filter operators for this column. */ filterOperators?: GridFilterOperator[]; + /** + * If `true`, the `renderCell` will be called for the auto generated rows. + * @default false + */ + shouldRenderAutoGeneratedRows?: boolean; /** * If `true`, this column cannot be reordered. * @default false @@ -213,3 +218,14 @@ export interface GridColumnsState { all: string[]; lookup: GridColumnLookup; } + +export type GridColDefOverride = Omit, 'field'>; + +export type GridColDefOverrideCallback = (params: GridColDefOverrideParams) => GridColDefOverride; + +export interface GridColDefOverrideParams { + /** + * The column we are generating before the override. + */ + colDef: GridColDef; +} diff --git a/packages/grid/_modules_/grid/models/gridOptions.tsx b/packages/grid/_modules_/grid/models/gridOptions.tsx index cda35d3f4d973..9e2b9ac986d10 100644 --- a/packages/grid/_modules_/grid/models/gridOptions.tsx +++ b/packages/grid/_modules_/grid/models/gridOptions.tsx @@ -118,6 +118,11 @@ export interface GridSimpleOptions { * @default false */ disableMultipleColumnsFiltering: boolean; + /** + * If `true`, the filtering will only be applied to the top level rows. + * @default false + */ + disableChildrenFiltering: boolean; /** * If `true`, multiple selection using the CTRL or CMD key is disabled. * @default false @@ -128,6 +133,11 @@ export interface GridSimpleOptions { * @default false */ disableMultipleColumnsSorting: boolean; + /** + * If `true`, the sorting will only be applied to the top level rows. + * @default false + */ + disableChildrenSorting: boolean; /** * If `true`, the selection on click on a row or cell is disabled. * @default false @@ -262,8 +272,10 @@ export const GRID_DEFAULT_SIMPLE_OPTIONS: GridSimpleOptions = { disableColumnSelector: false, disableDensitySelector: false, disableMultipleColumnsFiltering: false, + disableChildrenFiltering: false, disableMultipleSelection: false, disableMultipleColumnsSorting: false, + disableChildrenSorting: false, disableSelectionOnClick: false, disableVirtualization: false, editMode: GridEditModes.Cell, diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index 53b93cb160922..4c4a5f9165e5b 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -17,18 +17,48 @@ export interface GridRowModelUpdate extends GridRowModel { } export interface GridRowTreeNodeConfig { + /** + * The grid row id. + */ id: GridRowId; - children?: GridRowTreeConfig; - descendantsCount?: number; + + /** + * The id of the row children. + */ + children?: GridRowId[]; + + /** + * Amount of descendants (children, children's children, ...) before the filtering. + */ + descendantCount?: number; + + /** + * The row id of the parent (null if this row is a top level row). + */ + parent: GridRowId | null; + + /** + * Current expansion status of the row. + */ expanded?: boolean; /** - * If `true`, this node has been automatically added to fill a gap in the tree structure + * 0-based depth of the row in the tree. + */ + depth: number; + + /** + * The value used to group the children of this row. + */ + groupingValue: string; + + /** + * If `true`, this node has been automatically added to fill a gap in the tree structure. */ - fillerNode?: boolean; + isAutoGenerated?: boolean; } -export type GridRowTreeConfig = Map; +export type GridRowTreeConfig = Record; export type GridRowsLookup = Record; diff --git a/packages/grid/_modules_/grid/models/params/gridCellParams.ts b/packages/grid/_modules_/grid/models/params/gridCellParams.ts index 2da0b1de02671..796b6180b8c40 100644 --- a/packages/grid/_modules_/grid/models/params/gridCellParams.ts +++ b/packages/grid/_modules_/grid/models/params/gridCellParams.ts @@ -1,5 +1,5 @@ import { GridCellMode, GridCellValue } from '../gridCell'; -import { GridRowId, GridRowModel } from '../gridRows'; +import { GridRowId, GridRowModel, GridRowTreeNodeConfig } from '../gridRows'; import type { GridStateColDef } from '../colDef'; import { GridEditCellProps } from '../gridEditRowModel'; @@ -27,6 +27,10 @@ export interface GridCellParams { * The row model of the row that the current cell belongs to. */ row: GridRowModel; + /** + * The node of the row that the current cell belongs to. + */ + rowNode: GridRowTreeNodeConfig; /** * The column of the row that the current cell belongs to. */ diff --git a/packages/grid/data-grid/src/DataGrid.tsx b/packages/grid/data-grid/src/DataGrid.tsx index 29fec7936625d..188ae38a9f20d 100644 --- a/packages/grid/data-grid/src/DataGrid.tsx +++ b/packages/grid/data-grid/src/DataGrid.tsx @@ -496,6 +496,7 @@ DataGridRaw.propTypes = { page: PropTypes.number, /** * Set the number of rows in one page. + * If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows wanted on each page. * @default 100 */ pageSize: chainPropTypes(PropTypes.number, (props: any) => { @@ -542,6 +543,7 @@ DataGridRaw.propTypes = { rowBuffer: PropTypes.number, /** * Set the total number of rows, if it is different than the length of the value `rows` prop. + * If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows. */ rowCount: PropTypes.number, /** diff --git a/packages/grid/data-grid/src/DataGridProps.ts b/packages/grid/data-grid/src/DataGridProps.ts index cd35bad7b69e9..4d5683f89e79a 100644 --- a/packages/grid/data-grid/src/DataGridProps.ts +++ b/packages/grid/data-grid/src/DataGridProps.ts @@ -14,6 +14,8 @@ export type DataGridProps = Omit< | 'disableMultipleColumnsFiltering' | 'disableMultipleColumnsSorting' | 'disableMultipleSelection' + | 'disableChildrenFiltering' + | 'disableChildrenSorting' | 'throttleRowsMs' | 'hideFooterRowCount' | 'options' diff --git a/packages/grid/data-grid/src/useDataGridProps.ts b/packages/grid/data-grid/src/useDataGridProps.ts index 8c06ef857acd1..6c245415e5554 100644 --- a/packages/grid/data-grid/src/useDataGridProps.ts +++ b/packages/grid/data-grid/src/useDataGridProps.ts @@ -16,6 +16,8 @@ const FORCED_PROPS: { [key in ForcedPropsKey]-?: GridInputComponentProps[key] } disableMultipleColumnsFiltering: true, disableMultipleColumnsSorting: true, disableMultipleSelection: true, + disableChildrenFiltering: undefined, + disableChildrenSorting: undefined, throttleRowsMs: undefined, hideFooterRowCount: false, pagination: true, diff --git a/packages/grid/x-grid/src/DataGridPro.tsx b/packages/grid/x-grid/src/DataGridPro.tsx index 54ceedb323fee..2e5775673a282 100644 --- a/packages/grid/x-grid/src/DataGridPro.tsx +++ b/packages/grid/x-grid/src/DataGridPro.tsx @@ -139,6 +139,16 @@ DataGridProRaw.propTypes = { * @default "standard" */ density: PropTypes.oneOf(['comfortable', 'compact', 'standard']), + /** + * If `true`, the filtering will only be applied to the top level rows. + * @default false + */ + disableChildrenFiltering: PropTypes.bool, + /** + * If `true`, the sorting will only be applied to the top level rows. + * @default false + */ + disableChildrenSorting: PropTypes.bool, /** * If `true`, column filters are disabled. * @default false @@ -562,6 +572,7 @@ DataGridProRaw.propTypes = { page: PropTypes.number, /** * Set the number of rows in one page. + * If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows wanted on each page. * @default 100 */ pageSize: PropTypes.number, @@ -584,6 +595,7 @@ DataGridProRaw.propTypes = { rowBuffer: PropTypes.number, /** * Set the total number of rows, if it is different than the length of the value `rows` prop. + * If some of the rows have children (for instance in the tree data), this number represents the amount of top level rows. */ rowCount: PropTypes.number, /** From b8bdd3abfd0de5a2f6a365a785faacbbf3545ebd Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 15:36:56 +0200 Subject: [PATCH 351/390] Lint --- packages/grid/_modules_/grid/GridComponentProps.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/GridComponentProps.ts b/packages/grid/_modules_/grid/GridComponentProps.ts index 2a33b94baae31..70dc5e58d6a7a 100644 --- a/packages/grid/_modules_/grid/GridComponentProps.ts +++ b/packages/grid/_modules_/grid/GridComponentProps.ts @@ -8,7 +8,7 @@ import { GridMergedOptions, } from './models/gridOptions'; import { MuiEvent } from './models/muiEvent'; -import { GridRowId, GridRowIdGetter, GridRowModel, GridRowsProp } from './models/gridRows'; +import { GridRowId, GridRowIdGetter, GridRowsProp } from './models/gridRows'; import { ElementSize } from './models/elementSize'; import { GridColumnTypesRecord } from './models/colDef/gridColumnTypesRecord'; import { GridSortModel } from './models/gridSortModel'; From 1bb350cce79d430106d0e1738bccdc3d131c67f6 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 15:52:46 +0200 Subject: [PATCH 352/390] Work --- .../infiniteLoader/useGridInfiniteLoader.ts | 23 ++---------- .../keyboard/useGridKeyboardNavigation.ts | 37 ++++++------------- 2 files changed, 15 insertions(+), 45 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts b/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts index 56e8f040f54f6..53ddbb4f9e174 100644 --- a/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts +++ b/packages/grid/_modules_/grid/hooks/features/infiniteLoader/useGridInfiniteLoader.ts @@ -11,9 +11,8 @@ import { GridRowScrollEndParams } from '../../../models/params/gridRowScrollEndP import { visibleGridColumnsSelector } from '../columns/gridColumnsSelector'; import { GridComponentProps } from '../../../GridComponentProps'; import { GridScrollParams } from '../../../models/params/gridScrollParams'; -import { gridVisibleSortedRowEntriesSelector } from '../filter/gridFilterSelector'; -import { gridPaginationSelector } from '../pagination/gridPaginationSelector'; import { gridDensityRowHeightSelector } from '../density/densitySelector'; +import { useCurrentPageRows } from '../../utils/useCurrentPageRows'; /** * Only available in DataGridPro @@ -30,25 +29,9 @@ export const useGridInfiniteLoader = ( ): void => { const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); const visibleColumns = useGridSelector(apiRef, visibleGridColumnsSelector); - const visibleSortedRowEntries = useGridSelector(apiRef, gridVisibleSortedRowEntriesSelector); - const paginationState = useGridSelector(apiRef, gridPaginationSelector); + const currentPage = useCurrentPageRows(apiRef, props); const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector); - - const rowsInCurrentPage = React.useMemo(() => { - if (props.pagination && props.paginationMode === 'client') { - const start = paginationState.pageSize * paginationState.page; - return visibleSortedRowEntries.slice(start, start + paginationState.pageSize); - } - return visibleSortedRowEntries; - }, [ - paginationState.page, - paginationState.pageSize, - props.pagination, - props.paginationMode, - visibleSortedRowEntries, - ]); - - const contentHeight = Math.max(rowsInCurrentPage.length * rowHeight, 1); + const contentHeight = Math.max(currentPage.rows.length * rowHeight, 1); const isInScrollBottomArea = React.useRef(false); diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts index ece57dc3f4d5e..09e189a1a3dbd 100644 --- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts +++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboardNavigation.ts @@ -18,14 +18,11 @@ import { import { gridContainerSizesSelector } from '../container/gridContainerSizesSelector'; import { visibleGridColumnsLengthSelector } from '../columns/gridColumnsSelector'; import { useGridSelector } from '../../utils/useGridSelector'; -import { gridPaginationSelector } from '../pagination/gridPaginationSelector'; import { useGridLogger } from '../../utils/useGridLogger'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { GridComponentProps } from '../../../GridComponentProps'; -import { - gridVisibleRowCountSelector, - gridVisibleSortedRowEntriesSelector, -} from '../filter/gridFilterSelector'; +import { gridVisibleSortedRowEntriesSelector } from '../filter/gridFilterSelector'; +import { useCurrentPageRows } from '../../utils/useCurrentPageRows'; const getNextCellIndexes = (key: string, indexes: GridCellIndexCoordinates) => { if (!isArrowKeys(key)) { @@ -74,14 +71,13 @@ const getNextColumnHeaderIndexes = (key: string, indexes: GridColumnHeaderIndexC */ export const useGridKeyboardNavigation = ( apiRef: GridApiRef, - props: Pick, + props: Pick, ): void => { const logger = useGridLogger(apiRef, 'useGridKeyboardNavigation'); - const paginationState = useGridSelector(apiRef, gridPaginationSelector); - const totalVisibleRowCount = useGridSelector(apiRef, gridVisibleRowCountSelector); const colCount = useGridSelector(apiRef, visibleGridColumnsLengthSelector); const containerSizes = useGridSelector(apiRef, gridContainerSizesSelector); const visibleSortedRows = useGridSelector(apiRef, gridVisibleSortedRowEntriesSelector); + const currentPage = useCurrentPageRows(apiRef, props); const mapKey = (event: React.KeyboardEvent) => { if (isEnterKey(event.key)) { @@ -96,16 +92,17 @@ export const useGridKeyboardNavigation = ( const navigateCells = React.useCallback( (params: GridCellParams, event: React.KeyboardEvent) => { event.preventDefault(); + + if (!currentPage.range) { + return; + } + const colIndex = apiRef.current.getColumnIndex(params.field); const rowIndex = visibleSortedRows.findIndex((row) => row.id === params.id); const key = mapKey(event); const isCtrlPressed = event.ctrlKey || event.metaKey || event.shiftKey; - let rowCount = totalVisibleRowCount; - - if (props.pagination && totalVisibleRowCount > paginationState.pageSize) { - rowCount = paginationState.pageSize * (paginationState.page + 1); - } + const rowCount = currentPage.range.lastRowIndex - currentPage.range.firstRowIndex + 1; let nextCellIndexes: GridCellIndexCoordinates; if (isArrowKeys(key)) { @@ -123,7 +120,7 @@ export const useGridKeyboardNavigation = ( // In that case we go to first row, first col, or last row last col! let newRowIndex = 0; if (colIdx === 0) { - newRowIndex = props.pagination ? rowCount - paginationState.pageSize : 0; + newRowIndex = currentPage.range.firstRowIndex; } else { newRowIndex = rowCount - 1; } @@ -161,17 +158,7 @@ export const useGridKeyboardNavigation = ( const node = visibleSortedRows[nextCellIndexes.rowIndex]; apiRef.current.setCellFocus(node.id, field); }, - [ - apiRef, - visibleSortedRows, - totalVisibleRowCount, - props.pagination, - paginationState.pageSize, - paginationState.page, - colCount, - logger, - containerSizes, - ], + [apiRef, visibleSortedRows, colCount, logger, containerSizes, currentPage], ); const navigateColumnHeaders = React.useCallback( From 40669aa4f70385695ad177215a647cf41073e9f0 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 15:53:24 +0200 Subject: [PATCH 353/390] Revert useGridContainerProps --- .../hooks/features/container/useGridContainerProps.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/container/useGridContainerProps.ts b/packages/grid/_modules_/grid/hooks/features/container/useGridContainerProps.ts index 5dd07793acd27..ece93a01a713b 100644 --- a/packages/grid/_modules_/grid/hooks/features/container/useGridContainerProps.ts +++ b/packages/grid/_modules_/grid/hooks/features/container/useGridContainerProps.ts @@ -50,7 +50,6 @@ export const useGridContainerProps = ( | 'pagination' | 'autoPageSize' | 'pageSize' - | 'paginationMode' | 'autoHeight' | 'hideFooter' | 'scrollbarSize' @@ -96,11 +95,7 @@ export const useGridContainerProps = ( const getVirtualRowCount = React.useCallback(() => { logger.debug('Calculating virtual row count.'); - if ( - props.pagination && - props.paginationMode === 'client' && - (!props.autoPageSize || props.pageSize) - ) { + if (props.pagination && (!props.autoPageSize || props.pageSize)) { const rowsLeft = visibleRowsCount - paginationState.page * paginationState.pageSize; return rowsLeft > paginationState.pageSize ? paginationState.pageSize : rowsLeft; } @@ -110,7 +105,6 @@ export const useGridContainerProps = ( props.autoPageSize, props.pagination, props.pageSize, - props.paginationMode, paginationState.page, paginationState.pageSize, visibleRowsCount, From 036445368e2e61656821f30e5ca8d80cf5e79393 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 15:56:37 +0200 Subject: [PATCH 354/390] Empty From 32700687b2281b959f02bb919033b680ed22bf1a Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 26 Oct 2021 16:31:20 +0200 Subject: [PATCH 355/390] Fix --- packages/grid/_modules_/grid/components/GridVirtualScroller.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx b/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx index 35c8d7a7adc4d..94c482e8df05f 100644 --- a/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx +++ b/packages/grid/_modules_/grid/components/GridVirtualScroller.tsx @@ -271,7 +271,7 @@ const GridVirtualScroller = React.forwardRef Date: Wed, 27 Oct 2021 10:24:32 +0200 Subject: [PATCH 356/390] Fix --- .../components/columnSelection/GridCellCheckboxRenderer.tsx | 1 - packages/grid/_modules_/grid/models/gridRows.ts | 5 ----- 2 files changed, 6 deletions(-) diff --git a/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx b/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx index b2f2283704b14..0547cc51bfc1a 100644 --- a/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx +++ b/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx @@ -142,7 +142,6 @@ GridCellCheckboxForwardRef.propTypes = { PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, ), depth: PropTypes.number.isRequired, - descendantCount: PropTypes.number, expanded: PropTypes.bool, groupingValue: PropTypes.string.isRequired, id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index 4c4a5f9165e5b..288a27daea776 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -27,11 +27,6 @@ export interface GridRowTreeNodeConfig { */ children?: GridRowId[]; - /** - * Amount of descendants (children, children's children, ...) before the filtering. - */ - descendantCount?: number; - /** * The row id of the parent (null if this row is a top level row). */ From 7f499d5a96158b0507e5155179db1203fdcbb77e Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 27 Oct 2021 10:27:01 +0200 Subject: [PATCH 357/390] Work --- .../grid/components/cell/GridTreeDataGroupingCell.tsx | 1 - .../grid/hooks/features/treeData/useGridTreeData.ts | 6 +++++- packages/grid/_modules_/grid/utils/rowTreeUtils.ts | 2 -- .../grid/x-grid/src/tests/treeData.DataGridPro.test.tsx | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index b5816ce18d187..cc4a3f86e7189 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -143,7 +143,6 @@ GridTreeDataGroupingCell.propTypes = { PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, ), depth: PropTypes.number.isRequired, - descendantCount: PropTypes.number, expanded: PropTypes.bool, groupingValue: PropTypes.string.isRequired, id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index b01bd44ceb167..727d63aa70b96 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -11,6 +11,7 @@ import { useFirstRender } from '../../utils/useFirstRender'; import { buildRowTree } from '../../../utils/rowTreeUtils'; import { GridRowGroupingPreProcessing } from '../../core/rowGroupsPerProcessing'; import { isFunction } from '../../../utils/utils'; +import { gridFilteredDescendantCountLookupSelector } from '../filter'; /** * Only available in DataGridPro @@ -121,7 +122,10 @@ export const useGridTreeData = ( event.preventDefault(); const node = apiRef.current.unstable_getRowNode(params.id); - if (!node?.descendantCount) { + const filteredDescendantCount = + gridFilteredDescendantCountLookupSelector(apiRef.current.state)[params.id] ?? 0; + + if (!node || filteredDescendantCount === 0) { return; } diff --git a/packages/grid/_modules_/grid/utils/rowTreeUtils.ts b/packages/grid/_modules_/grid/utils/rowTreeUtils.ts index b7457251281ba..8c515a80e5576 100644 --- a/packages/grid/_modules_/grid/utils/rowTreeUtils.ts +++ b/packages/grid/_modules_/grid/utils/rowTreeUtils.ts @@ -74,8 +74,6 @@ export const buildRowTree = (params: GenerateRowTreeParams): GridRowGroupingResu idRowsLookup[nodeId] = {}; ids.push(nodeId); } - - node.descendantCount = (node.descendantCount ?? 0) + 1; } else { tempTree[row.id] = { id: row.id, diff --git a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx index 16e7f91a390f4..263e4621deb47 100644 --- a/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-grid/src/tests/treeData.DataGridPro.test.tsx @@ -374,7 +374,7 @@ describe(' - Tree Data', () => { ); }); - it('should set the descendantCount on matching nodes even if the children are collapsed', () => { + it('should set the filtered descendant count on matching nodes even if the children are collapsed', () => { render( Date: Wed, 27 Oct 2021 10:29:48 +0200 Subject: [PATCH 358/390] Update exports --- scripts/exportsSnapshot.json | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/scripts/exportsSnapshot.json b/scripts/exportsSnapshot.json index a5212e9e1f9dd..40398818e413d 100644 --- a/scripts/exportsSnapshot.json +++ b/scripts/exportsSnapshot.json @@ -138,7 +138,6 @@ { "name": "GridValueSetterParams", "kind": "Interface" }, { "name": "Logger", "kind": "Interface" }, { "name": "DataGridProps", "kind": "Type alias" }, - { "name": "FilterColumnLookup", "kind": "Type alias" }, { "name": "GridActionsCellItemProps", "kind": "Type alias" }, { "name": "GridAlignment", "kind": "Type alias" }, { "name": "GridApiRef", "kind": "Type alias" }, @@ -165,6 +164,7 @@ { "name": "GridExportFormat", "kind": "Type alias" }, { "name": "GridFeatureMode", "kind": "Type alias" }, { "name": "GridFieldComparatorList", "kind": "Type alias" }, + { "name": "GridFilterActiveItemsLookup", "kind": "Type alias" }, { "name": "GridFooterContainerProps", "kind": "Type alias" }, { "name": "GridInputSelectionModel", "kind": "Type alias" }, { "name": "GridNativeColTypes", "kind": "Type alias" }, @@ -174,6 +174,7 @@ { "name": "GridRootContainerRef", "kind": "Type alias" }, { "name": "GridRootProps", "kind": "Type alias" }, { "name": "GridRowData", "kind": "Type alias" }, + { "name": "GridRowEntry", "kind": "Type alias" }, { "name": "GridRowId", "kind": "Type alias" }, { "name": "GridRowIdGetter", "kind": "Type alias" }, { "name": "GridRowMode", "kind": "Type alias" }, @@ -265,7 +266,6 @@ { "name": "MAX_PAGE_SIZE", "kind": "Variable" }, { "name": "SUBMIT_FILTER_STROKE_TIME", "kind": "Variable" }, { "name": "SortGridMenuItems", "kind": "Variable" }, - { "name": "activeGridFilterItemsSelector", "kind": "Variable" }, { "name": "allGridColumnsSelector", "kind": "Variable" }, { "name": "arSD", "kind": "Variable" }, { "name": "bgBG", "kind": "Variable" }, @@ -275,8 +275,6 @@ { "name": "enUS", "kind": "Variable" }, { "name": "esES", "kind": "Variable" }, { "name": "faIR", "kind": "Variable" }, - { "name": "filterGridColumnLookupSelector", "kind": "Variable" }, - { "name": "filterGridItemsCounterSelector", "kind": "Variable" }, { "name": "filterableGridColumnsIdsSelector", "kind": "Variable" }, { "name": "filterableGridColumnsSelector", "kind": "Variable" }, { "name": "frFR", "kind": "Variable" }, @@ -287,6 +285,8 @@ { "name": "gridDensityHeaderHeightSelector", "kind": "Variable" }, { "name": "gridDensityRowHeightSelector", "kind": "Variable" }, { "name": "gridDensityValueSelector", "kind": "Variable" }, + { "name": "gridFilterActiveItemsLookupSelector", "kind": "Variable" }, + { "name": "gridFilterActiveItemsSelector", "kind": "Variable" }, { "name": "gridFilterModelSelector", "kind": "Variable" }, { "name": "gridFocusCellSelector", "kind": "Variable" }, { "name": "gridFocusColumnHeaderSelector", "kind": "Variable" }, @@ -296,13 +296,19 @@ { "name": "gridPanelClasses", "kind": "Variable" }, { "name": "gridResizingColumnFieldSelector", "kind": "Variable" }, { "name": "gridRowCountSelector", "kind": "Variable" }, + { "name": "gridRowIdsSelector", "kind": "Variable" }, { "name": "gridRowsLookupSelector", "kind": "Variable" }, { "name": "gridScrollSelector", "kind": "Variable" }, { "name": "gridSortColumnLookupSelector", "kind": "Variable" }, { "name": "gridSortModelSelector", "kind": "Variable" }, + { "name": "gridSortedRowEntriesSelector", "kind": "Variable" }, + { "name": "gridSortedRowIdsSelector", "kind": "Variable" }, { "name": "gridTabIndexCellSelector", "kind": "Variable" }, { "name": "gridTabIndexColumnHeaderSelector", "kind": "Variable" }, + { "name": "gridVisibleRowCountSelector", "kind": "Variable" }, { "name": "gridVisibleRowsLookupSelector", "kind": "Variable" }, + { "name": "gridVisibleSortedRowEntriesSelector", "kind": "Variable" }, + { "name": "gridVisibleSortedRowIdsSelector", "kind": "Variable" }, { "name": "itIT", "kind": "Variable" }, { "name": "jaJP", "kind": "Variable" }, { "name": "koKR", "kind": "Variable" }, @@ -314,19 +320,11 @@ { "name": "selectedGridRowsSelector", "kind": "Variable" }, { "name": "selectedIdsLookupSelector", "kind": "Variable" }, { "name": "skSK", "kind": "Variable" }, - { "name": "sortedGridRowIdsSelector", "kind": "Variable" }, - { "name": "sortedGridRowsSelector", "kind": "Variable" }, { "name": "trTR", "kind": "Variable" }, { "name": "ukUA", "kind": "Variable" }, - { "name": "unorderedGridRowIdsSelector", "kind": "Variable" }, - { "name": "unorderedGridRowModelsSelector", "kind": "Variable" }, { "name": "viVN", "kind": "Variable" }, { "name": "visibleGridColumnsLengthSelector", "kind": "Variable" }, { "name": "visibleGridColumnsSelector", "kind": "Variable" }, - { "name": "visibleGridRowCountSelector", "kind": "Variable" }, - { "name": "visibleSortedGridRowIdsSelector", "kind": "Variable" }, - { "name": "visibleSortedGridRowsAsArraySelector", "kind": "Variable" }, - { "name": "visibleSortedGridRowsSelector", "kind": "Variable" }, { "name": "zhCN", "kind": "Variable" }, { "name": "GridBody", "kind": "Function" }, { "name": "GridCell", "kind": "Function" }, From ef5d0c9bd5cfef267f6a9d2a0ee99afbba73da07 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 27 Oct 2021 13:37:48 +0200 Subject: [PATCH 359/390] Empty From 76b0a7652205d55acee82cf187d3f26a14db109b Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 27 Oct 2021 16:13:05 +0200 Subject: [PATCH 360/390] Regen doc --- scripts/exportsSnapshot.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/scripts/exportsSnapshot.json b/scripts/exportsSnapshot.json index 40398818e413d..edc56c8351807 100644 --- a/scripts/exportsSnapshot.json +++ b/scripts/exportsSnapshot.json @@ -15,6 +15,7 @@ { "name": "GridFilterMenuItem", "kind": "Namespace" }, { "name": "GridMenu", "kind": "Namespace" }, { "name": "GridRow", "kind": "Namespace" }, + { "name": "GridTreeDataGroupingCell", "kind": "Namespace" }, { "name": "HideGridColMenuItem", "kind": "Namespace" }, { "name": "SortGridMenuItems", "kind": "Namespace" }, { "name": "GridCellModes", "kind": "Enumeration" }, @@ -39,6 +40,7 @@ { "name": "GridClasses", "kind": "Interface" }, { "name": "GridClipboardApi", "kind": "Interface" }, { "name": "GridColDef", "kind": "Interface" }, + { "name": "GridColDefOverrideParams", "kind": "Interface" }, { "name": "GridColumnApi", "kind": "Interface" }, { "name": "GridColumnHeaderIndexCoordinates", "kind": "Interface" }, { "name": "GridColumnHeaderMenuProps", "kind": "Interface" }, @@ -133,6 +135,7 @@ { "name": "GridTabIndexState", "kind": "Interface" }, { "name": "GridToolbarExportProps", "kind": "Interface" }, { "name": "GridToolbarFilterButtonProps", "kind": "Interface" }, + { "name": "GridTreeDataGroupingCellValue", "kind": "Interface" }, { "name": "GridTypeFilterInputValueProps", "kind": "Interface" }, { "name": "GridValueFormatterParams", "kind": "Interface" }, { "name": "GridValueSetterParams", "kind": "Interface" }, @@ -147,6 +150,8 @@ { "name": "GridCellMode", "kind": "Type alias" }, { "name": "GridCellValue", "kind": "Type alias" }, { "name": "GridClassKey", "kind": "Type alias" }, + { "name": "GridColDefOverride", "kind": "Type alias" }, + { "name": "GridColDefOverrideCallback", "kind": "Type alias" }, { "name": "GridColType", "kind": "Type alias" }, { "name": "GridColTypeDef", "kind": "Type alias" }, { "name": "GridColumnHeaderClassFn", "kind": "Type alias" }, @@ -227,6 +232,8 @@ { "name": "GridColumnsHeader", "kind": "Variable" }, { "name": "GridColumnsMenuItem", "kind": "Variable" }, { "name": "GridDragIcon", "kind": "Variable" }, + { "name": "GridExpandLessIcon", "kind": "Variable" }, + { "name": "GridExpandMoreIcon", "kind": "Variable" }, { "name": "GridFeatureModeConstant", "kind": "Variable" }, { "name": "GridFilterAltIcon", "kind": "Variable" }, { "name": "GridFilterListIcon", "kind": "Variable" }, @@ -259,6 +266,7 @@ { "name": "GridToolbarDensitySelector", "kind": "Variable" }, { "name": "GridToolbarExport", "kind": "Variable" }, { "name": "GridToolbarFilterButton", "kind": "Variable" }, + { "name": "GridTreeDataGroupingCell", "kind": "Variable" }, { "name": "GridTripleDotsVerticalIcon", "kind": "Variable" }, { "name": "GridViewHeadlineIcon", "kind": "Variable" }, { "name": "GridViewStreamIcon", "kind": "Variable" }, @@ -288,15 +296,20 @@ { "name": "gridFilterActiveItemsLookupSelector", "kind": "Variable" }, { "name": "gridFilterActiveItemsSelector", "kind": "Variable" }, { "name": "gridFilterModelSelector", "kind": "Variable" }, + { "name": "gridFilteredDescendantCountLookupSelector", "kind": "Variable" }, { "name": "gridFocusCellSelector", "kind": "Variable" }, { "name": "gridFocusColumnHeaderSelector", "kind": "Variable" }, { "name": "gridPageSelector", "kind": "Variable" }, { "name": "gridPageSizeSelector", "kind": "Variable" }, + { "name": "gridPaginatedVisibleSortedGridRowEntriesSelector", "kind": "Variable" }, { "name": "gridPaginatedVisibleSortedGridRowIdsSelector", "kind": "Variable" }, + { "name": "gridPaginationRowRangeSelector", "kind": "Variable" }, { "name": "gridPanelClasses", "kind": "Variable" }, { "name": "gridResizingColumnFieldSelector", "kind": "Variable" }, { "name": "gridRowCountSelector", "kind": "Variable" }, { "name": "gridRowIdsSelector", "kind": "Variable" }, + { "name": "gridRowTreeDepthSelector", "kind": "Variable" }, + { "name": "gridRowTreeSelector", "kind": "Variable" }, { "name": "gridRowsLookupSelector", "kind": "Variable" }, { "name": "gridScrollSelector", "kind": "Variable" }, { "name": "gridSortColumnLookupSelector", "kind": "Variable" }, @@ -305,10 +318,14 @@ { "name": "gridSortedRowIdsSelector", "kind": "Variable" }, { "name": "gridTabIndexCellSelector", "kind": "Variable" }, { "name": "gridTabIndexColumnHeaderSelector", "kind": "Variable" }, + { "name": "gridTopLevelRowCountSelector", "kind": "Variable" }, { "name": "gridVisibleRowCountSelector", "kind": "Variable" }, { "name": "gridVisibleRowsLookupSelector", "kind": "Variable" }, + { "name": "gridVisibleRowsSelector", "kind": "Variable" }, { "name": "gridVisibleSortedRowEntriesSelector", "kind": "Variable" }, { "name": "gridVisibleSortedRowIdsSelector", "kind": "Variable" }, + { "name": "gridVisibleSortedTopLevelRowEntriesSelector", "kind": "Variable" }, + { "name": "gridVisibleTopLevelRowCountSelector", "kind": "Variable" }, { "name": "itIT", "kind": "Variable" }, { "name": "jaJP", "kind": "Variable" }, { "name": "koKR", "kind": "Variable" }, From ceac48dedb080d6a9d17b932ca2724cfdffdf969 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 28 Oct 2021 15:59:02 +0200 Subject: [PATCH 361/390] Fix --- docs/pages/api-docs/data-grid/grid-col-def.md | 2 +- scripts/exportsSnapshot.json | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/pages/api-docs/data-grid/grid-col-def.md b/docs/pages/api-docs/data-grid/grid-col-def.md index bed6a3bbcaaf9..25211c48bb432 100644 --- a/docs/pages/api-docs/data-grid/grid-col-def.md +++ b/docs/pages/api-docs/data-grid/grid-col-def.md @@ -35,7 +35,7 @@ import { GridColDef } from '@mui/x-data-grid'; | renderEditCell? | (params: GridRenderEditCellParams) => ReactNode | | Allows to override the component rendered in edit cell mode for this column. | | renderHeader? | (params: GridColumnHeaderParams) => ReactNode | | Allows to render a component in the column header cell. | | resizable? | boolean | true
| If `true`, the column is resizable. | -| shouldRenderAutoGeneratedRows? | boolean | false
| If `true`, the `renderCell` will be called for the auto generated rows. | +| shouldRenderAutoGeneratedRows? | boolean | false
| If `true`, the `renderCell` will be called for the auto generated rows. | | sortable? | boolean | true
| If `true`, the column is sortable. | | sortComparator? | GridComparatorFn | | A comparator function used to sort rows. | | type? | string | 'string'
| Type allows to merge this object with a default definition [GridColDef](/api/data-grid/grid-col-def/). | diff --git a/scripts/exportsSnapshot.json b/scripts/exportsSnapshot.json index 40398818e413d..e1661afaa10ce 100644 --- a/scripts/exportsSnapshot.json +++ b/scripts/exportsSnapshot.json @@ -39,6 +39,7 @@ { "name": "GridClasses", "kind": "Interface" }, { "name": "GridClipboardApi", "kind": "Interface" }, { "name": "GridColDef", "kind": "Interface" }, + { "name": "GridColDefOverrideParams", "kind": "Interface" }, { "name": "GridColumnApi", "kind": "Interface" }, { "name": "GridColumnHeaderIndexCoordinates", "kind": "Interface" }, { "name": "GridColumnHeaderMenuProps", "kind": "Interface" }, @@ -135,6 +136,7 @@ { "name": "GridToolbarFilterButtonProps", "kind": "Interface" }, { "name": "GridTypeFilterInputValueProps", "kind": "Interface" }, { "name": "GridValueFormatterParams", "kind": "Interface" }, + { "name": "GridValueOptionsParams", "kind": "Interface" }, { "name": "GridValueSetterParams", "kind": "Interface" }, { "name": "Logger", "kind": "Interface" }, { "name": "DataGridProps", "kind": "Type alias" }, @@ -147,6 +149,8 @@ { "name": "GridCellMode", "kind": "Type alias" }, { "name": "GridCellValue", "kind": "Type alias" }, { "name": "GridClassKey", "kind": "Type alias" }, + { "name": "GridColDefOverride", "kind": "Type alias" }, + { "name": "GridColDefOverrideCallback", "kind": "Type alias" }, { "name": "GridColType", "kind": "Type alias" }, { "name": "GridColTypeDef", "kind": "Type alias" }, { "name": "GridColumnHeaderClassFn", "kind": "Type alias" }, @@ -288,15 +292,20 @@ { "name": "gridFilterActiveItemsLookupSelector", "kind": "Variable" }, { "name": "gridFilterActiveItemsSelector", "kind": "Variable" }, { "name": "gridFilterModelSelector", "kind": "Variable" }, + { "name": "gridFilteredDescendantCountLookupSelector", "kind": "Variable" }, { "name": "gridFocusCellSelector", "kind": "Variable" }, { "name": "gridFocusColumnHeaderSelector", "kind": "Variable" }, { "name": "gridPageSelector", "kind": "Variable" }, { "name": "gridPageSizeSelector", "kind": "Variable" }, + { "name": "gridPaginatedVisibleSortedGridRowEntriesSelector", "kind": "Variable" }, { "name": "gridPaginatedVisibleSortedGridRowIdsSelector", "kind": "Variable" }, + { "name": "gridPaginationRowRangeSelector", "kind": "Variable" }, { "name": "gridPanelClasses", "kind": "Variable" }, { "name": "gridResizingColumnFieldSelector", "kind": "Variable" }, { "name": "gridRowCountSelector", "kind": "Variable" }, { "name": "gridRowIdsSelector", "kind": "Variable" }, + { "name": "gridRowTreeDepthSelector", "kind": "Variable" }, + { "name": "gridRowTreeSelector", "kind": "Variable" }, { "name": "gridRowsLookupSelector", "kind": "Variable" }, { "name": "gridScrollSelector", "kind": "Variable" }, { "name": "gridSortColumnLookupSelector", "kind": "Variable" }, @@ -305,10 +314,14 @@ { "name": "gridSortedRowIdsSelector", "kind": "Variable" }, { "name": "gridTabIndexCellSelector", "kind": "Variable" }, { "name": "gridTabIndexColumnHeaderSelector", "kind": "Variable" }, + { "name": "gridTopLevelRowCountSelector", "kind": "Variable" }, { "name": "gridVisibleRowCountSelector", "kind": "Variable" }, { "name": "gridVisibleRowsLookupSelector", "kind": "Variable" }, + { "name": "gridVisibleRowsSelector", "kind": "Variable" }, { "name": "gridVisibleSortedRowEntriesSelector", "kind": "Variable" }, { "name": "gridVisibleSortedRowIdsSelector", "kind": "Variable" }, + { "name": "gridVisibleSortedTopLevelRowEntriesSelector", "kind": "Variable" }, + { "name": "gridVisibleTopLevelRowCountSelector", "kind": "Variable" }, { "name": "itIT", "kind": "Variable" }, { "name": "jaJP", "kind": "Variable" }, { "name": "koKR", "kind": "Variable" }, From f77a1e6f3fbe615f52480f9d3d27a9bfb114f55c Mon Sep 17 00:00:00 2001 From: delangle Date: Sat, 30 Oct 2021 12:18:56 +0200 Subject: [PATCH 362/390] Fix --- scripts/exportsSnapshot.json | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/exportsSnapshot.json b/scripts/exportsSnapshot.json index 40398818e413d..dc70f3599a549 100644 --- a/scripts/exportsSnapshot.json +++ b/scripts/exportsSnapshot.json @@ -135,6 +135,7 @@ { "name": "GridToolbarFilterButtonProps", "kind": "Interface" }, { "name": "GridTypeFilterInputValueProps", "kind": "Interface" }, { "name": "GridValueFormatterParams", "kind": "Interface" }, + { "name": "GridValueOptionsParams", "kind": "Interface" }, { "name": "GridValueSetterParams", "kind": "Interface" }, { "name": "Logger", "kind": "Interface" }, { "name": "DataGridProps", "kind": "Type alias" }, From a1784738868f2e60e832075a7fd65f22ca713cf4 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 2 Nov 2021 09:16:16 +0100 Subject: [PATCH 363/390] Fix --- docs/pages/api-docs/data-grid/grid-col-def.md | 1 - packages/grid/_modules_/grid/components/GridRow.tsx | 10 ++++------ .../grid/_modules_/grid/models/colDef/gridColDef.ts | 5 ----- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/docs/pages/api-docs/data-grid/grid-col-def.md b/docs/pages/api-docs/data-grid/grid-col-def.md index 25211c48bb432..4cf7f0deaa4b7 100644 --- a/docs/pages/api-docs/data-grid/grid-col-def.md +++ b/docs/pages/api-docs/data-grid/grid-col-def.md @@ -35,7 +35,6 @@ import { GridColDef } from '@mui/x-data-grid'; | renderEditCell? | (params: GridRenderEditCellParams) => ReactNode | | Allows to override the component rendered in edit cell mode for this column. | | renderHeader? | (params: GridColumnHeaderParams) => ReactNode | | Allows to render a component in the column header cell. | | resizable? | boolean | true
| If `true`, the column is resizable. | -| shouldRenderAutoGeneratedRows? | boolean | false
| If `true`, the `renderCell` will be called for the auto generated rows. | | sortable? | boolean | true
| If `true`, the column is sortable. | | sortComparator? | GridComparatorFn | | A comparator function used to sort rows. | | type? | string | 'string'
| Type allows to merge this object with a default definition [GridColDef](/api/data-grid/grid-col-def/). | diff --git a/packages/grid/_modules_/grid/components/GridRow.tsx b/packages/grid/_modules_/grid/components/GridRow.tsx index 4ce0f38a45f05..e1b3226eeeefb 100644 --- a/packages/grid/_modules_/grid/components/GridRow.tsx +++ b/packages/grid/_modules_/grid/components/GridRow.tsx @@ -164,9 +164,7 @@ function GridRow(props: React.HTMLAttributes & GridRowProps) { const editCellState = editRowsState[rowId] ? editRowsState[rowId][column.field] : null; let content: React.ReactNode = null; - const skipRender = !column.shouldRenderAutoGeneratedRows && cellParams.rowNode?.isAutoGenerated; - - if (editCellState == null && column.renderCell && !skipRender) { + if (editCellState == null && column.renderCell) { content = column.renderCell({ ...cellParams, api: apiRef.current }); // TODO move to GridCell classNames.push( @@ -174,7 +172,7 @@ function GridRow(props: React.HTMLAttributes & GridRowProps) { ); } - if (editCellState != null && column.renderEditCell && !skipRender) { + if (editCellState != null && column.renderEditCell) { const params = { ...cellParams, ...editCellState, api: apiRef.current }; content = column.renderEditCell(params); // TODO move to GridCell @@ -200,13 +198,13 @@ function GridRow(props: React.HTMLAttributes & GridRowProps) { cells.push( Date: Tue, 2 Nov 2021 09:17:47 +0100 Subject: [PATCH 364/390] Fix --- .../pages/components/data-grid/group-pivot/TreeDataWithGap.js | 1 - .../pages/components/data-grid/group-pivot/TreeDataWithGap.tsx | 1 - .../grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx | 1 - 3 files changed, 3 deletions(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.js b/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.js index 7ec62dd707007..1c130b17fd91b 100644 --- a/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.js +++ b/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.js @@ -89,7 +89,6 @@ const columns = [ headerName: 'Gap', type: 'boolean', valueGetter: (params) => params.id.toString().startsWith('auto-generated-row'), - shouldRenderAutoGeneratedRows: true, }, ]; diff --git a/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.tsx b/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.tsx index 13a2f9dcc6ec7..3831988a0d506 100644 --- a/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.tsx @@ -89,7 +89,6 @@ const columns: GridColumns = [ headerName: 'Gap', type: 'boolean', valueGetter: (params) => params.id.toString().startsWith('auto-generated-row'), - shouldRenderAutoGeneratedRows: true, }, ]; diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx index 9c0f990ca13a5..3df09b075aab6 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx +++ b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx @@ -19,7 +19,6 @@ export const GRID_TREE_DATA_GROUP_COL_DEF: GridColDef = { filterable: false, disableColumnMenu: true, disableReorder: true, - shouldRenderAutoGeneratedRows: true, align: 'left', width: 200, valueGetter: ({ rowNode, api }): GridTreeDataGroupingCellValue => ({ From f36f7e1a2c19cb63926411e2d6caa64e5628aba2 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 2 Nov 2021 16:42:34 +0100 Subject: [PATCH 365/390] Build api --- docs/pages/api-docs/data-grid/grid-col-def.md | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/docs/pages/api-docs/data-grid/grid-col-def.md b/docs/pages/api-docs/data-grid/grid-col-def.md index 4cf7f0deaa4b7..4533c1ba1f410 100644 --- a/docs/pages/api-docs/data-grid/grid-col-def.md +++ b/docs/pages/api-docs/data-grid/grid-col-def.md @@ -12,35 +12,35 @@ import { GridColDef } from '@mui/x-data-grid'; ## Properties -| Name | Type | Default | Description | -| :-------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- | -| align? | GridAlignment | | Allows to align the column values in cells. | -| cellClassName? | GridCellClassNamePropType | | Class name that will be added in cells for that column. | -| description? | string | | The description of the column rendered as tooltip if the column header name is not fully displayed. | -| disableColumnMenu? | boolean | false
| If `true`, the column menu is disabled for this column. | -| disableExport? | boolean | false
| If `true`, this column will not be included in exports. | -| disableReorder? | boolean | false
| If `true`, this column cannot be reordered. | -| editable? | boolean | false
| If `true`, the cells of the column are editable. | -| field | string | | The column identifier. It's used to map with GridRowModel values. | -| filterable? | boolean | true
| If `true`, the column is filterable. | -| filterOperators? | GridFilterOperator[] | | Allows setting the filter operators for this column. | -| flex? | number | | If set, it indicates that a column has fluid width. Range [0, ∞). | -| headerAlign? | GridAlignment | | Header cell element alignment. | -| headerClassName? | GridColumnHeaderClassNamePropType | | Class name that will be added in the column header cell. | -| headerName? | string | | The title of the column rendered in the column header cell. | -| hide? | boolean | false
| If `true`, hide the column. | -| hideSortIcons? | boolean | false
| Toggle the visibility of the sort icons. | -| minWidth? | number | 50
| Sets the minimum width of a column. | -| renderCell? | (params: GridRenderCellParams<any, any, any>) => ReactNode | | Allows to override the component rendered as cell for this column. | -| renderEditCell? | (params: GridRenderEditCellParams) => ReactNode | | Allows to override the component rendered in edit cell mode for this column. | -| renderHeader? | (params: GridColumnHeaderParams) => ReactNode | | Allows to render a component in the column header cell. | -| resizable? | boolean | true
| If `true`, the column is resizable. | -| sortable? | boolean | true
| If `true`, the column is sortable. | -| sortComparator? | GridComparatorFn | | A comparator function used to sort rows. | -| type? | string | 'string'
| Type allows to merge this object with a default definition [GridColDef](/api/data-grid/grid-col-def/). | -| valueFormatter? | (params: GridValueFormatterParams) => GridCellValue | | Function that allows to apply a formatter before rendering its value. | -| valueGetter? | (params: GridValueGetterParams) => GridCellValue | | Function that allows to get a specific data instead of field to render in the cell. | -| valueOptions? | ValueOptions[] \| ((params: GridValueOptionsParams) => ValueOptions[]) | | To be used in combination with `type: 'singleSelect'`. This is an array (or a function returning an array) of the possible cell values and labels. | -| valueParser? | (value: GridCellValue, params?: GridCellParams<any, any, any>) => GridCellValue | | Function that takes the user-entered value and converts it to a value used internally. | -| valueSetter? | (params: GridValueSetterParams) => { [key: string]: any } | | Function that allows to customize how the entered value is stored in the row.
It only works with cell/row editing. | -| width? | number | 100
| Set the width of the column. | +| Name | Type | Default | Description | +| :-------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- | +| align? | GridAlignment | | Allows to align the column values in cells. | +| cellClassName? | GridCellClassNamePropType | | Class name that will be added in cells for that column. | +| description? | string | | The description of the column rendered as tooltip if the column header name is not fully displayed. | +| disableColumnMenu? | boolean | false
| If `true`, the column menu is disabled for this column. | +| disableExport? | boolean | false
| If `true`, this column will not be included in exports. | +| disableReorder? | boolean | false
| If `true`, this column cannot be reordered. | +| editable? | boolean | false
| If `true`, the cells of the column are editable. | +| field | string | | The column identifier. It's used to map with GridRowModel values. | +| filterable? | boolean | true
| If `true`, the column is filterable. | +| filterOperators? | GridFilterOperator[] | | Allows setting the filter operators for this column. | +| flex? | number | | If set, it indicates that a column has fluid width. Range [0, ∞). | +| headerAlign? | GridAlignment | | Header cell element alignment. | +| headerClassName? | GridColumnHeaderClassNamePropType | | Class name that will be added in the column header cell. | +| headerName? | string | | The title of the column rendered in the column header cell. | +| hide? | boolean | false
| If `true`, hide the column. | +| hideSortIcons? | boolean | false
| Toggle the visibility of the sort icons. | +| minWidth? | number | 50
| Sets the minimum width of a column. | +| renderCell? | (params: GridRenderCellParams<any, any, any>) => ReactNode | | Allows to override the component rendered as cell for this column. | +| renderEditCell? | (params: GridRenderEditCellParams) => ReactNode | | Allows to override the component rendered in edit cell mode for this column. | +| renderHeader? | (params: GridColumnHeaderParams) => ReactNode | | Allows to render a component in the column header cell. | +| resizable? | boolean | true
| If `true`, the column is resizable. | +| sortable? | boolean | true
| If `true`, the column is sortable. | +| sortComparator? | GridComparatorFn | | A comparator function used to sort rows. | +| type? | string | 'string'
| Type allows to merge this object with a default definition [GridColDef](/api/data-grid/grid-col-def/). | +| valueFormatter? | (params: GridValueFormatterParams) => GridCellValue | | Function that allows to apply a formatter before rendering its value. | +| valueGetter? | (params: GridValueGetterParams) => GridCellValue | | Function that allows to get a specific data instead of field to render in the cell. | +| valueOptions? | ValueOptions[] \| ((params: GridValueOptionsParams) => ValueOptions[]) | | To be used in combination with `type: 'singleSelect'`. This is an array (or a function returning an array) of the possible cell values and labels. | +| valueParser? | (value: GridCellValue, params?: GridCellParams<any, any, any>) => GridCellValue | | Function that takes the user-entered value and converts it to a value used internally. | +| valueSetter? | (params: GridValueSetterParams) => { [key: string]: any } | | Function that allows to customize how the entered value is stored in the row.
It only works with cell/row editing. | +| width? | number | 100
| Set the width of the column. | From c3e3d01eadacb2249a2d3b91e95e3e2a31a5c5f4 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 3 Nov 2021 09:15:56 +0100 Subject: [PATCH 366/390] Empty From 704ea9c199f0e2d00e5dcec4fa687083cab42888 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 5 Nov 2021 17:12:28 +0100 Subject: [PATCH 367/390] Fix --- .../data-grid/group-pivot/BasicTreeData.tsx.preview | 6 ++++++ .../CustomGroupingColumnTreeData.tsx.preview | 7 +++++++ .../DefaultGroupingExpansionDepthTreeData.tsx.preview | 7 +++++++ .../group-pivot/SetRowExpansionTreeData.tsx.preview | 10 ++++++++++ .../group-pivot/TreeDataFullExample.tsx.preview | 1 + .../data-grid/group-pivot/TreeDataWithGap.tsx.preview | 6 ++++++ 6 files changed, 37 insertions(+) create mode 100644 docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx.preview create mode 100644 docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx.preview create mode 100644 docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.tsx.preview create mode 100644 docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx.preview create mode 100644 docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.tsx.preview create mode 100644 docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.tsx.preview diff --git a/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx.preview b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx.preview new file mode 100644 index 0000000000000..7efeac785781e --- /dev/null +++ b/docs/src/pages/components/data-grid/group-pivot/BasicTreeData.tsx.preview @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx.preview b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx.preview new file mode 100644 index 0000000000000..35b9b522e32e6 --- /dev/null +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx.preview @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.tsx.preview b/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.tsx.preview new file mode 100644 index 0000000000000..e907316a9015d --- /dev/null +++ b/docs/src/pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.tsx.preview @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx.preview b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx.preview new file mode 100644 index 0000000000000..da8c1ae5b397e --- /dev/null +++ b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx.preview @@ -0,0 +1,10 @@ + +
+ +
\ No newline at end of file diff --git a/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.tsx.preview b/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.tsx.preview new file mode 100644 index 0000000000000..93f8c958ce3c1 --- /dev/null +++ b/docs/src/pages/components/data-grid/group-pivot/TreeDataFullExample.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.tsx.preview b/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.tsx.preview new file mode 100644 index 0000000000000..7efeac785781e --- /dev/null +++ b/docs/src/pages/components/data-grid/group-pivot/TreeDataWithGap.tsx.preview @@ -0,0 +1,6 @@ + \ No newline at end of file From e1c3832c999917e00abda306452ef9026296b1a5 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 8 Nov 2021 09:42:24 +0100 Subject: [PATCH 368/390] Fix --- .../features/treeData/useGridTreeData.ts | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 727d63aa70b96..38052c9277e45 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -1,21 +1,27 @@ import * as React from 'react'; import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridComponentProps } from '../../../GridComponentProps'; -import { GridColumnsPreProcessing } from '../../core/columnsPreProcessing'; import { GRID_TREE_DATA_GROUP_COL_DEF } from './gridTreeDataGroupColDef'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { GridEvents } from '../../../constants'; -import { GridCellParams, GridColDef, GridColDefOverrideParams, MuiEvent } from '../../../models'; +import { + GridCellParams, + GridColDef, + GridColDefOverrideParams, + GridColumns, + MuiEvent, +} from '../../../models'; import { isSpaceKey } from '../../../utils/keyboardUtils'; import { useFirstRender } from '../../utils/useFirstRender'; import { buildRowTree } from '../../../utils/rowTreeUtils'; import { GridRowGroupingPreProcessing } from '../../core/rowGroupsPerProcessing'; import { isFunction } from '../../../utils/utils'; import { gridFilteredDescendantCountLookupSelector } from '../filter'; +import { GridPreProcessingGroup } from '../../core/preProcessing'; /** * Only available in DataGridPro - * @requires useGridColumnsPreProcessing (method) + * @requires useGridPreProcessing (method) * @requires useGridRowGroupsPreProcessing (method) */ export const useGridTreeData = ( @@ -51,7 +57,7 @@ export const useGridTreeData = ( }, [apiRef, props.groupingColDef]); const updateColumnsPreProcessing = React.useCallback(() => { - const addGroupingColumn: GridColumnsPreProcessing = (columns) => { + const addGroupingColumn = (columns: GridColumns) => { if (!props.treeData) { return columns; } @@ -61,7 +67,11 @@ export const useGridTreeData = ( return [...columns.slice(0, index), groupingColDef, ...columns.slice(index)]; }; - apiRef.current.unstable_registerColumnPreProcessing('treeData', addGroupingColumn); + apiRef.current.unstable_registerPreProcessor( + GridPreProcessingGroup.hydrateColumns, + 'treeData', + addGroupingColumn, + ); }, [apiRef, props.treeData, groupingColDef]); const updateRowGrouping = React.useCallback(() => { From 846a5fa0b73cd5e1813e44f2fd3e9fc4fbaeaf1b Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 8 Nov 2021 13:25:45 +0100 Subject: [PATCH 369/390] Use new styling engine --- .../cell/GridTreeDataGroupingCell.tsx | 58 +++++++++++++------ packages/grid/_modules_/grid/gridClasses.ts | 9 +++ 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index cc4a3f86e7189..d5a4e8377155a 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { makeStyles } from '@mui/styles'; +import { unstable_composeClasses as composeClasses } from '@mui/core'; +import { styled } from '@mui/material/styles'; import IconButton from '@mui/material/IconButton'; import Box from '@mui/material/Box'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; @@ -8,18 +9,40 @@ import { useGridApiContext } from '../../hooks/utils/useGridApiContext'; import { GridRenderCellParams } from '../../models/params/gridCellParams'; import { isNavigationKey, isSpaceKey } from '../../utils/keyboardUtils'; import { GridEvents } from '../../constants'; +import { getDataGridUtilityClass } from '../../gridClasses'; +import { GridComponentProps } from '../../GridComponentProps'; -const useStyles = makeStyles({ - root: { - display: 'flex', - alignItems: 'center', - width: '100%', - }, - toggle: { - flex: '0 0 28px', - alignSelf: 'stretch', - marginRight: 16, - }, +type OwnerState = { classes: GridComponentProps['classes'] }; + +const useUtilityClasses = (ownerState: OwnerState) => { + const { classes } = ownerState; + + const slots = { + root: ['treeDataGroupingCell'], + toggle: ['treeDataGroupingCellToggle'], + }; + + return composeClasses(slots, getDataGridUtilityClass, classes); +}; + +const GridTreeDataGroupingCellRoot = styled(Box, { + name: 'MuiDataGrid', + slot: 'GridTreeDataGroupingCell', + overridesResolver: (props, styles) => styles.treeDataGroupingCell, +})({ + display: 'flex', + alignItems: 'center', + width: '100%', +}); + +const GridTreeDataGroupingCellToggle = styled('div', { + name: 'MuiDataGrid', + slot: 'GridTreeDataGroupingCellToggle', + overridesResolver: (props, styles) => styles.treeDataGroupingCellToggle, +})({ + flex: '0 0 28px', + alignSelf: 'stretch', + marginRight: 16, }); export interface GridTreeDataGroupingCellValue { @@ -34,7 +57,8 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams -
+ + {value.filteredDescendantCount > 0 && ( )} -
+ {value.label} {value.filteredDescendantCount > 0 ? ` (${value.filteredDescendantCount})` : ''} - + ); }; diff --git a/packages/grid/_modules_/grid/gridClasses.ts b/packages/grid/_modules_/grid/gridClasses.ts index 7e9bc4b2759b9..6d898409992b5 100644 --- a/packages/grid/_modules_/grid/gridClasses.ts +++ b/packages/grid/_modules_/grid/gridClasses.ts @@ -278,6 +278,14 @@ export interface GridClasses { * Styles applied to both the cell and the column header if `showColumnRightBorder={true}`. */ withBorder: string; + /** + * Styled applied to the root of the grouping column of the tree data. + */ + treeDataGroupingCell: string; + /** + * Styled applied to the toggle of the grouping column of the tree data. + */ + treeDataGroupingCellToggle: string; } export type GridClassKey = keyof GridClasses; @@ -357,4 +365,5 @@ export const gridClasses = generateUtilityClasses('MuiDataGrid', [ 'virtualScrollerContent', 'virtualScrollerRenderZone', 'withBorder', + 'treeDataGroupingCell', ]); From e35268de0c9bfcc1d81ba60e8dd95abbecc94e1c Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 8 Nov 2021 13:26:52 +0100 Subject: [PATCH 370/390] Fix --- packages/grid/_modules_/grid/gridClasses.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/grid/_modules_/grid/gridClasses.ts b/packages/grid/_modules_/grid/gridClasses.ts index 6d898409992b5..e77d811f41229 100644 --- a/packages/grid/_modules_/grid/gridClasses.ts +++ b/packages/grid/_modules_/grid/gridClasses.ts @@ -366,4 +366,5 @@ export const gridClasses = generateUtilityClasses('MuiDataGrid', [ 'virtualScrollerRenderZone', 'withBorder', 'treeDataGroupingCell', + 'treeDataGroupingCellToggle', ]); From bc091627bb158eb98e3ada2f78b04be13543e484 Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 8 Nov 2021 13:39:27 +0100 Subject: [PATCH 371/390] Fix --- docs/pages/api-docs/data-grid/data-grid-pro.json | 4 +++- docs/pages/api-docs/data-grid/data-grid.json | 4 +++- docs/translations/api-docs/data-grid/data-grid-pro-pt.json | 6 ++++++ docs/translations/api-docs/data-grid/data-grid-pro-zh.json | 6 ++++++ docs/translations/api-docs/data-grid/data-grid-pro.json | 6 ++++++ docs/translations/api-docs/data-grid/data-grid-pt.json | 6 ++++++ docs/translations/api-docs/data-grid/data-grid-zh.json | 6 ++++++ docs/translations/api-docs/data-grid/data-grid.json | 6 ++++++ 8 files changed, 42 insertions(+), 2 deletions(-) diff --git a/docs/pages/api-docs/data-grid/data-grid-pro.json b/docs/pages/api-docs/data-grid/data-grid-pro.json index 4ce68ffef009e..293bf7885e0a9 100644 --- a/docs/pages/api-docs/data-grid/data-grid-pro.json +++ b/docs/pages/api-docs/data-grid/data-grid-pro.json @@ -292,7 +292,9 @@ "sortIcon", "toolbarContainer", "toolbarFilterList", - "withBorder" + "withBorder", + "treeDataGroupingCell", + "treeDataGroupingCellToggle" ], "globalClasses": {}, "name": "MuiDataGrid" diff --git a/docs/pages/api-docs/data-grid/data-grid.json b/docs/pages/api-docs/data-grid/data-grid.json index 0f7348fe2dc9c..0db5eeed76ca4 100644 --- a/docs/pages/api-docs/data-grid/data-grid.json +++ b/docs/pages/api-docs/data-grid/data-grid.json @@ -260,7 +260,9 @@ "sortIcon", "toolbarContainer", "toolbarFilterList", - "withBorder" + "withBorder", + "treeDataGroupingCell", + "treeDataGroupingCellToggle" ], "globalClasses": {}, "name": "MuiDataGrid" diff --git a/docs/translations/api-docs/data-grid/data-grid-pro-pt.json b/docs/translations/api-docs/data-grid/data-grid-pro-pt.json index d7399521689d9..055bce70d9a9c 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro-pt.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro-pt.json @@ -387,6 +387,12 @@ "description": "Styles applied to {{nodeName}} if {{conditions}}.", "nodeName": "both the cell and the column header", "conditions": "showColumnRightBorder={true}" + }, + "treeDataGroupingCell": { + "description": "Styled applied to the root of the grouping column of the tree data." + }, + "treeDataGroupingCellToggle": { + "description": "Styled applied to the toggle of the grouping column of the tree data." } }, "slotDescriptions": { diff --git a/docs/translations/api-docs/data-grid/data-grid-pro-zh.json b/docs/translations/api-docs/data-grid/data-grid-pro-zh.json index d7399521689d9..055bce70d9a9c 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro-zh.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro-zh.json @@ -387,6 +387,12 @@ "description": "Styles applied to {{nodeName}} if {{conditions}}.", "nodeName": "both the cell and the column header", "conditions": "showColumnRightBorder={true}" + }, + "treeDataGroupingCell": { + "description": "Styled applied to the root of the grouping column of the tree data." + }, + "treeDataGroupingCellToggle": { + "description": "Styled applied to the toggle of the grouping column of the tree data." } }, "slotDescriptions": { diff --git a/docs/translations/api-docs/data-grid/data-grid-pro.json b/docs/translations/api-docs/data-grid/data-grid-pro.json index d7399521689d9..055bce70d9a9c 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro.json @@ -387,6 +387,12 @@ "description": "Styles applied to {{nodeName}} if {{conditions}}.", "nodeName": "both the cell and the column header", "conditions": "showColumnRightBorder={true}" + }, + "treeDataGroupingCell": { + "description": "Styled applied to the root of the grouping column of the tree data." + }, + "treeDataGroupingCellToggle": { + "description": "Styled applied to the toggle of the grouping column of the tree data." } }, "slotDescriptions": { diff --git a/docs/translations/api-docs/data-grid/data-grid-pt.json b/docs/translations/api-docs/data-grid/data-grid-pt.json index aa22a563773d4..c2e7251edc0e0 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pt.json +++ b/docs/translations/api-docs/data-grid/data-grid-pt.json @@ -369,6 +369,12 @@ "description": "Styles applied to {{nodeName}} if {{conditions}}.", "nodeName": "both the cell and the column header", "conditions": "showColumnRightBorder={true}" + }, + "treeDataGroupingCell": { + "description": "Styled applied to the root of the grouping column of the tree data." + }, + "treeDataGroupingCellToggle": { + "description": "Styled applied to the toggle of the grouping column of the tree data." } }, "slotDescriptions": { diff --git a/docs/translations/api-docs/data-grid/data-grid-zh.json b/docs/translations/api-docs/data-grid/data-grid-zh.json index aa22a563773d4..c2e7251edc0e0 100644 --- a/docs/translations/api-docs/data-grid/data-grid-zh.json +++ b/docs/translations/api-docs/data-grid/data-grid-zh.json @@ -369,6 +369,12 @@ "description": "Styles applied to {{nodeName}} if {{conditions}}.", "nodeName": "both the cell and the column header", "conditions": "showColumnRightBorder={true}" + }, + "treeDataGroupingCell": { + "description": "Styled applied to the root of the grouping column of the tree data." + }, + "treeDataGroupingCellToggle": { + "description": "Styled applied to the toggle of the grouping column of the tree data." } }, "slotDescriptions": { diff --git a/docs/translations/api-docs/data-grid/data-grid.json b/docs/translations/api-docs/data-grid/data-grid.json index aa22a563773d4..c2e7251edc0e0 100644 --- a/docs/translations/api-docs/data-grid/data-grid.json +++ b/docs/translations/api-docs/data-grid/data-grid.json @@ -369,6 +369,12 @@ "description": "Styles applied to {{nodeName}} if {{conditions}}.", "nodeName": "both the cell and the column header", "conditions": "showColumnRightBorder={true}" + }, + "treeDataGroupingCell": { + "description": "Styled applied to the root of the grouping column of the tree data." + }, + "treeDataGroupingCellToggle": { + "description": "Styled applied to the toggle of the grouping column of the tree data." } }, "slotDescriptions": { From d5791b47a178d61aa426f7374150cde01b679002 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 12 Nov 2021 09:32:46 +0100 Subject: [PATCH 372/390] Code review --- .../group-pivot/SetRowExpansionTreeData.js | 1 + .../group-pivot/SetRowExpansionTreeData.tsx | 4 +- .../api-docs/data-grid/data-grid-pro-pt.json | 8 ++-- .../api-docs/data-grid/data-grid-pro-zh.json | 8 ++-- .../api-docs/data-grid/data-grid-pro.json | 8 ++-- .../api-docs/data-grid/data-grid-pt.json | 6 ++- .../api-docs/data-grid/data-grid-zh.json | 6 ++- .../api-docs/data-grid/data-grid.json | 6 ++- .../grid/_modules_/grid/GridComponentProps.ts | 4 +- .../cell/GridTreeDataGroupingCell.tsx | 29 ++------------- .../components/containers/GridRootStyles.ts | 12 ++++++ packages/grid/_modules_/grid/gridClasses.ts | 4 +- .../grid/hooks/core/preProcessing/index.ts | 1 + .../useGridRegisterPreProcessor.ts | 37 +++++++++++++++++++ .../hooks/features/columns/useGridColumns.ts | 1 - .../features/treeData/useGridTreeData.ts | 31 +++++----------- .../grid/x-data-grid-pro/src/DataGridPro.tsx | 4 +- .../src/tests/treeData.DataGridPro.test.tsx | 11 +----- 18 files changed, 101 insertions(+), 80 deletions(-) create mode 100644 packages/grid/_modules_/grid/hooks/core/preProcessing/useGridRegisterPreProcessor.ts diff --git a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js index 36de96eaa2108..363b3fa69ee72 100644 --- a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js @@ -113,6 +113,7 @@ export default function SetRowExpansionTreeData() { const toggleFirstRow = () => { const rowId = apiRef.current.getRowIdFromRowIndex(0); + console.log(rowId); apiRef.current.unstable_setRowExpansion( rowId, !apiRef.current.unstable_getRowNode(rowId)?.expanded, diff --git a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx index 9138176a6b22f..ea0c56a419d13 100644 --- a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx @@ -117,7 +117,7 @@ export default function SetRowExpansionTreeData() { const apiRef = useGridApiRef(); const toggleFirstRow = () => { - const rowId = apiRef.current.getRowIdFromRowIndex(0); + const rowId = apiRef.current.getRowIdFromRowIndex(1); apiRef.current.unstable_setRowExpansion( rowId, !apiRef.current.unstable_getRowNode(rowId)?.expanded, @@ -126,7 +126,7 @@ export default function SetRowExpansionTreeData() { return ( - +

Signature:
function(params: GridCellParams) => string
params: With all properties from GridCellParams.
returns (string): The CSS class to apply to the cell.", "getRowClassName": "Function that applies CSS classes dynamically on rows.

Signature:
function(params: GridRowParams) => string
params: With all properties from GridRowParams.
returns (string): The CSS class to apply to the row.", "getRowId": "Return the id of a given GridRowModel.", - "getTreeDataPath": "Determines the path of a row in the tree data. For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"]. Note than all paths must contain at least one element.

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row.", + "getTreeDataPath": "Determines the path of a row in the tree data. For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"]. Note that all paths must contain at least one element.

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): The path to the row.", "groupingColDef": "The grouping column used by the tree data.", "headerHeight": "Set the height in pixel of the column headers in the grid.", "hideFooter": "If true, the footer component is hidden.", @@ -389,10 +389,12 @@ "conditions": "showColumnRightBorder={true}" }, "treeDataGroupingCell": { - "description": "Styled applied to the root of the grouping column of the tree data." + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the root of the grouping column of the tree data" }, "treeDataGroupingCellToggle": { - "description": "Styled applied to the toggle of the grouping column of the tree data." + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toggle of the grouping column of the tree data" } }, "slotDescriptions": { diff --git a/docs/translations/api-docs/data-grid/data-grid-pro-zh.json b/docs/translations/api-docs/data-grid/data-grid-pro-zh.json index 724e55a0771a4..99c841bace238 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro-zh.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro-zh.json @@ -39,7 +39,7 @@ "getCellClassName": "Function that applies CSS classes dynamically on cells.

Signature:
function(params: GridCellParams) => string
params: With all properties from GridCellParams.
returns (string): The CSS class to apply to the cell.", "getRowClassName": "Function that applies CSS classes dynamically on rows.

Signature:
function(params: GridRowParams) => string
params: With all properties from GridRowParams.
returns (string): The CSS class to apply to the row.", "getRowId": "Return the id of a given GridRowModel.", - "getTreeDataPath": "Determines the path of a row in the tree data. For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"]. Note than all paths must contain at least one element.

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row.", + "getTreeDataPath": "Determines the path of a row in the tree data. For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"]. Note that all paths must contain at least one element.

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): The path to the row.", "groupingColDef": "The grouping column used by the tree data.", "headerHeight": "Set the height in pixel of the column headers in the grid.", "hideFooter": "If true, the footer component is hidden.", @@ -389,10 +389,12 @@ "conditions": "showColumnRightBorder={true}" }, "treeDataGroupingCell": { - "description": "Styled applied to the root of the grouping column of the tree data." + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the root of the grouping column of the tree data" }, "treeDataGroupingCellToggle": { - "description": "Styled applied to the toggle of the grouping column of the tree data." + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toggle of the grouping column of the tree data" } }, "slotDescriptions": { diff --git a/docs/translations/api-docs/data-grid/data-grid-pro.json b/docs/translations/api-docs/data-grid/data-grid-pro.json index 724e55a0771a4..99c841bace238 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro.json @@ -39,7 +39,7 @@ "getCellClassName": "Function that applies CSS classes dynamically on cells.

Signature:
function(params: GridCellParams) => string
params: With all properties from GridCellParams.
returns (string): The CSS class to apply to the cell.", "getRowClassName": "Function that applies CSS classes dynamically on rows.

Signature:
function(params: GridRowParams) => string
params: With all properties from GridRowParams.
returns (string): The CSS class to apply to the row.", "getRowId": "Return the id of a given GridRowModel.", - "getTreeDataPath": "Determines the path of a row in the tree data. For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"]. Note than all paths must contain at least one element.

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): the path to the row.", + "getTreeDataPath": "Determines the path of a row in the tree data. For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"]. Note that all paths must contain at least one element.

Signature:
function(row: GridRowModel) => Array<string>
row: The row from which we want the path.
returns (Array): The path to the row.", "groupingColDef": "The grouping column used by the tree data.", "headerHeight": "Set the height in pixel of the column headers in the grid.", "hideFooter": "If true, the footer component is hidden.", @@ -389,10 +389,12 @@ "conditions": "showColumnRightBorder={true}" }, "treeDataGroupingCell": { - "description": "Styled applied to the root of the grouping column of the tree data." + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the root of the grouping column of the tree data" }, "treeDataGroupingCellToggle": { - "description": "Styled applied to the toggle of the grouping column of the tree data." + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toggle of the grouping column of the tree data" } }, "slotDescriptions": { diff --git a/docs/translations/api-docs/data-grid/data-grid-pt.json b/docs/translations/api-docs/data-grid/data-grid-pt.json index 6c7150cb154d7..975b5690c2f2d 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pt.json +++ b/docs/translations/api-docs/data-grid/data-grid-pt.json @@ -371,10 +371,12 @@ "conditions": "showColumnRightBorder={true}" }, "treeDataGroupingCell": { - "description": "Styled applied to the root of the grouping column of the tree data." + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the root of the grouping column of the tree data" }, "treeDataGroupingCellToggle": { - "description": "Styled applied to the toggle of the grouping column of the tree data." + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toggle of the grouping column of the tree data" } }, "slotDescriptions": { diff --git a/docs/translations/api-docs/data-grid/data-grid-zh.json b/docs/translations/api-docs/data-grid/data-grid-zh.json index 6c7150cb154d7..975b5690c2f2d 100644 --- a/docs/translations/api-docs/data-grid/data-grid-zh.json +++ b/docs/translations/api-docs/data-grid/data-grid-zh.json @@ -371,10 +371,12 @@ "conditions": "showColumnRightBorder={true}" }, "treeDataGroupingCell": { - "description": "Styled applied to the root of the grouping column of the tree data." + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the root of the grouping column of the tree data" }, "treeDataGroupingCellToggle": { - "description": "Styled applied to the toggle of the grouping column of the tree data." + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toggle of the grouping column of the tree data" } }, "slotDescriptions": { diff --git a/docs/translations/api-docs/data-grid/data-grid.json b/docs/translations/api-docs/data-grid/data-grid.json index 6c7150cb154d7..975b5690c2f2d 100644 --- a/docs/translations/api-docs/data-grid/data-grid.json +++ b/docs/translations/api-docs/data-grid/data-grid.json @@ -371,10 +371,12 @@ "conditions": "showColumnRightBorder={true}" }, "treeDataGroupingCell": { - "description": "Styled applied to the root of the grouping column of the tree data." + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the root of the grouping column of the tree data" }, "treeDataGroupingCellToggle": { - "description": "Styled applied to the toggle of the grouping column of the tree data." + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toggle of the grouping column of the tree data" } }, "slotDescriptions": { diff --git a/packages/grid/_modules_/grid/GridComponentProps.ts b/packages/grid/_modules_/grid/GridComponentProps.ts index cbb88036a46d9..aa59e5628167a 100644 --- a/packages/grid/_modules_/grid/GridComponentProps.ts +++ b/packages/grid/_modules_/grid/GridComponentProps.ts @@ -106,9 +106,9 @@ interface GridComponentOtherProps { /** * Determines the path of a row in the tree data. * For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"]. - * Note than all paths must contain at least one element. + * Note that all paths must contain at least one element. * @param {GridRowModel} row The row from which we want the path. - * @returns {string[]} the path to the row. + * @returns {string[]} The path to the row. */ getTreeDataPath?: (row: GridRowModel) => string[]; /** diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index d5a4e8377155a..9d7b0aa3cb60d 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { unstable_composeClasses as composeClasses } from '@mui/core'; -import { styled } from '@mui/material/styles'; import IconButton from '@mui/material/IconButton'; import Box from '@mui/material/Box'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; @@ -25,26 +24,6 @@ const useUtilityClasses = (ownerState: OwnerState) => { return composeClasses(slots, getDataGridUtilityClass, classes); }; -const GridTreeDataGroupingCellRoot = styled(Box, { - name: 'MuiDataGrid', - slot: 'GridTreeDataGroupingCell', - overridesResolver: (props, styles) => styles.treeDataGroupingCell, -})({ - display: 'flex', - alignItems: 'center', - width: '100%', -}); - -const GridTreeDataGroupingCellToggle = styled('div', { - name: 'MuiDataGrid', - slot: 'GridTreeDataGroupingCellToggle', - overridesResolver: (props, styles) => styles.treeDataGroupingCellToggle, -})({ - flex: '0 0 28px', - alignSelf: 'stretch', - marginRight: 16, -}); - export interface GridTreeDataGroupingCellValue { label: string; filteredDescendantCount: number; @@ -80,8 +59,8 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams - + +
{value.filteredDescendantCount > 0 && ( )} - +
{value.label} {value.filteredDescendantCount > 0 ? ` (${value.filteredDescendantCount})` : ''} - +
); }; diff --git a/packages/grid/_modules_/grid/components/containers/GridRootStyles.ts b/packages/grid/_modules_/grid/components/containers/GridRootStyles.ts index aa0c8b339aec8..fcfa85ad72ed2 100644 --- a/packages/grid/_modules_/grid/components/containers/GridRootStyles.ts +++ b/packages/grid/_modules_/grid/components/containers/GridRootStyles.ts @@ -42,6 +42,8 @@ export const GridRootStyles = styled('div', { { [`& .${gridClasses.row}`]: styles.row }, { [`& .${gridClasses.sortIcon}`]: styles.sortIcon }, { [`& .${gridClasses.withBorder}`]: styles.withBorder }, + { [`& .${gridClasses.treeDataGroupingCell}`]: styles.treeDataGroupingCell }, + { [`& .${gridClasses.treeDataGroupingCellToggle}`]: styles.treeDataGroupingCellToggle }, styles.root, ], })(({ theme }) => { @@ -288,6 +290,16 @@ export const GridRootStyles = styled('div', { borderRadius: theme.shape.borderRadius, opacity: theme.palette.action.disabledOpacity, }, + [`& .${gridClasses.treeDataGroupingCell}`]: { + display: 'flex', + alignItems: 'center', + width: '100%', + }, + [`& .${gridClasses.treeDataGroupingCellToggle}`]: { + flex: '0 0 28px', + alignSelf: 'stretch', + marginRight: 16, + }, }; if (theme.palette.mode === 'dark') { diff --git a/packages/grid/_modules_/grid/gridClasses.ts b/packages/grid/_modules_/grid/gridClasses.ts index e77d811f41229..e6bfe5dadc2ac 100644 --- a/packages/grid/_modules_/grid/gridClasses.ts +++ b/packages/grid/_modules_/grid/gridClasses.ts @@ -279,11 +279,11 @@ export interface GridClasses { */ withBorder: string; /** - * Styled applied to the root of the grouping column of the tree data. + * Styles applied to the root of the grouping column of the tree data. */ treeDataGroupingCell: string; /** - * Styled applied to the toggle of the grouping column of the tree data. + * Styles applied to the toggle of the grouping column of the tree data. */ treeDataGroupingCellToggle: string; } diff --git a/packages/grid/_modules_/grid/hooks/core/preProcessing/index.ts b/packages/grid/_modules_/grid/hooks/core/preProcessing/index.ts index fb33e1410dfbe..ebd0c8059a104 100644 --- a/packages/grid/_modules_/grid/hooks/core/preProcessing/index.ts +++ b/packages/grid/_modules_/grid/hooks/core/preProcessing/index.ts @@ -1,2 +1,3 @@ export * from './gridPreProcessingApi'; export * from './useGridPreProcessing'; +export * from './useGridRegisterPreProcessor'; diff --git a/packages/grid/_modules_/grid/hooks/core/preProcessing/useGridRegisterPreProcessor.ts b/packages/grid/_modules_/grid/hooks/core/preProcessing/useGridRegisterPreProcessor.ts new file mode 100644 index 0000000000000..bbaadb34e694c --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/core/preProcessing/useGridRegisterPreProcessor.ts @@ -0,0 +1,37 @@ +import * as React from 'react'; +import { useFirstRender } from '../../utils/useFirstRender'; +import { GridApiRef } from '../../../models/api/gridApiRef'; +import { GridPreProcessingGroup, PreProcessorCallback } from './gridPreProcessingApi'; + +export const useGridRegisterPreProcessor = ( + apiRef: GridApiRef, + group: GridPreProcessingGroup, + callback: PreProcessorCallback, +) => { + const cleanup = React.useRef<(() => void) | null>(); + const id = React.useRef(`mui-${Math.round(Math.random() * 1e9)}`); + + const registerPreProcessor = React.useCallback(() => { + cleanup.current = apiRef.current.unstable_registerPreProcessor(group, id.current, callback); + }, [apiRef, callback, group]); + + useFirstRender(() => { + registerPreProcessor(); + }); + + const isFirstRender = React.useRef(true); + React.useEffect(() => { + if (isFirstRender.current) { + isFirstRender.current = false; + } else { + registerPreProcessor(); + } + + return () => { + if (cleanup.current) { + cleanup.current(); + cleanup.current = null; + } + }; + }, [registerPreProcessor]); +}; diff --git a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts index efca67d0d2f30..db2bfa0c1e4a1 100644 --- a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts +++ b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts @@ -113,7 +113,6 @@ const upsertColumnsState = (columnUpdates: GridColDef[], prevColumnsState?: Grid }; /** - * @requires useGridColumnsPreProcessing (method) * @requires useGridParamsApi (method) * @requires useGridContainerProps (state) * TODO: Impossible priority - useGridParamsApi also needs to be after useGridColumns diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 38052c9277e45..0c34f5ea8313a 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -15,9 +15,8 @@ import { isSpaceKey } from '../../../utils/keyboardUtils'; import { useFirstRender } from '../../utils/useFirstRender'; import { buildRowTree } from '../../../utils/rowTreeUtils'; import { GridRowGroupingPreProcessing } from '../../core/rowGroupsPerProcessing'; -import { isFunction } from '../../../utils/utils'; import { gridFilteredDescendantCountLookupSelector } from '../filter'; -import { GridPreProcessingGroup } from '../../core/preProcessing'; +import { GridPreProcessingGroup, useGridRegisterPreProcessor } from '../../core/preProcessing'; /** * Only available in DataGridPro @@ -40,7 +39,7 @@ export const useGridTreeData = ( }; let colDefOverride: Partial; - if (isFunction(propGroupingColDef)) { + if (typeof propGroupingColDef === 'function') { const params: GridColDefOverrideParams = { colDef: baseColDef, }; @@ -56,8 +55,8 @@ export const useGridTreeData = ( }; }, [apiRef, props.groupingColDef]); - const updateColumnsPreProcessing = React.useCallback(() => { - const addGroupingColumn = (columns: GridColumns) => { + const addGroupingColumn = React.useCallback( + (columns: GridColumns) => { if (!props.treeData) { return columns; } @@ -65,14 +64,9 @@ export const useGridTreeData = ( const index = columns[0].type === 'checkboxSelection' ? 1 : 0; return [...columns.slice(0, index), groupingColDef, ...columns.slice(index)]; - }; - - apiRef.current.unstable_registerPreProcessor( - GridPreProcessingGroup.hydrateColumns, - 'treeData', - addGroupingColumn, - ); - }, [apiRef, props.treeData, groupingColDef]); + }, + [props.treeData, groupingColDef], + ); const updateRowGrouping = React.useCallback(() => { if (!props.treeData) { @@ -102,19 +96,10 @@ export const useGridTreeData = ( }, [apiRef, props.getTreeDataPath, props.treeData, props.defaultGroupingExpansionDepth]); useFirstRender(() => { - updateColumnsPreProcessing(); updateRowGrouping(); }); const isFirstRender = React.useRef(true); - React.useEffect(() => { - if (isFirstRender.current) { - return; - } - - updateColumnsPreProcessing(); - }, [updateColumnsPreProcessing]); - React.useEffect(() => { if (isFirstRender.current) { isFirstRender.current = false; @@ -124,6 +109,8 @@ export const useGridTreeData = ( updateRowGrouping(); }, [updateRowGrouping]); + useGridRegisterPreProcessor(apiRef, GridPreProcessingGroup.hydrateColumns, addGroupingColumn); + const handleCellKeyDown = React.useCallback( (params: GridCellParams, event: MuiEvent) => { const cellParams = apiRef.current.getCellParams(params.id, params.field); diff --git a/packages/grid/x-data-grid-pro/src/DataGridPro.tsx b/packages/grid/x-data-grid-pro/src/DataGridPro.tsx index 8aff7f95ffd0c..d85b63077a391 100644 --- a/packages/grid/x-data-grid-pro/src/DataGridPro.tsx +++ b/packages/grid/x-data-grid-pro/src/DataGridPro.tsx @@ -279,9 +279,9 @@ DataGridProRaw.propTypes = { /** * Determines the path of a row in the tree data. * For instance, a row with the path ["A", "B"] is the child of the row with the path ["A"]. - * Note than all paths must contain at least one element. + * Note that all paths must contain at least one element. * @param {GridRowModel} row The row from which we want the path. - * @returns {string[]} the path to the row. + * @returns {string[]} The path to the row. */ getTreeDataPath: PropTypes.func, /** diff --git a/packages/grid/x-data-grid-pro/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/treeData.DataGridPro.test.tsx index 263e4621deb47..940a89e1639d4 100644 --- a/packages/grid/x-data-grid-pro/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/treeData.DataGridPro.test.tsx @@ -1,10 +1,4 @@ -import { - createClientRenderStrictMode, - // @ts-expect-error need to migrate helpers to TypeScript - fireEvent, - // @ts-expect-error need to migrate helpers to TypeScript - screen, -} from 'test/utils'; +import { createRenderer, fireEvent, screen } from '@material-ui/monorepo/test/utils'; import { getCell, getColumnHeadersTextContent, getColumnValues } from 'test/utils/helperFn'; import * as React from 'react'; import { expect } from 'chai'; @@ -54,8 +48,7 @@ const baselineProps: DataGridProProps = { }; describe(' - Tree Data', () => { - // TODO v5: replace with createClientRender - const render = createClientRenderStrictMode(); + const { render } = createRenderer(); let apiRef: GridApiRef; From 2c440520fc61391398cdf86b2792968d4f9a6650 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 12 Nov 2021 09:41:29 +0100 Subject: [PATCH 373/390] Fix --- .../data-grid/group-pivot/SetRowExpansionTreeData.js | 5 ++--- .../group-pivot/SetRowExpansionTreeData.tsx.preview | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js index 363b3fa69ee72..498692c9f6bad 100644 --- a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js @@ -112,8 +112,7 @@ export default function SetRowExpansionTreeData() { const apiRef = useGridApiRef(); const toggleFirstRow = () => { - const rowId = apiRef.current.getRowIdFromRowIndex(0); - console.log(rowId); + const rowId = apiRef.current.getRowIdFromRowIndex(1); apiRef.current.unstable_setRowExpansion( rowId, !apiRef.current.unstable_getRowNode(rowId)?.expanded, @@ -122,7 +121,7 @@ export default function SetRowExpansionTreeData() { return ( - +
Toggle 1st row expansion +
Date: Fri, 12 Nov 2021 10:01:09 +0100 Subject: [PATCH 374/390] Do not show node when grand parent is collapsed but not parent --- .../_modules_/grid/hooks/features/filter/useGridFilter.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index db198afe75a8d..0c8a906122290 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -172,7 +172,7 @@ export const useGridFilter = ( const filterTreeNode = ( node: GridRowTreeNodeConfig, isParentMatchingFilters: boolean, - isParentExpanded: boolean, + areAncestorsExpanded: boolean, ): number => { const shouldSkipFilters = props.disableChildrenFiltering && node.depth > 0; @@ -191,7 +191,7 @@ export const useGridFilter = ( const childSubTreeSize = filterTreeNode( childNode, isMatchingFilters ?? isParentMatchingFilters, - !!node.expanded, + areAncestorsExpanded && !!node.expanded, ); filteredDescendantCount += childSubTreeSize; @@ -213,7 +213,7 @@ export const useGridFilter = ( } } - visibleRowsLookup[node.id] = shouldPassFilters && isParentExpanded; + visibleRowsLookup[node.id] = shouldPassFilters && areAncestorsExpanded; if (!shouldPassFilters) { return 0; From b53bd57fb1e9daab0af30039c03097ef97b58a5b Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 15 Nov 2021 09:59:45 +0100 Subject: [PATCH 375/390] Code review --- .../grid/_modules_/grid/utils/rowTreeUtils.ts | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/utils/rowTreeUtils.ts b/packages/grid/_modules_/grid/utils/rowTreeUtils.ts index 8c515a80e5576..8918bf0a4ad12 100644 --- a/packages/grid/_modules_/grid/utils/rowTreeUtils.ts +++ b/packages/grid/_modules_/grid/utils/rowTreeUtils.ts @@ -19,7 +19,34 @@ interface TempRowTreeNode extends Omit { /** * Transform a list of rows into a tree structure where each row references its parent and children. - * Add the auto generated rows to `ids` and `idRowsLookup`. + * If a row have a parent which does not exist in the input rows, creates an auto generated row + * + ``` + params = { + ids: [0, 1, 2], + idRowsLookup: { 0: {...}, 1: {...}, 2: {...} }, + rows: [ + { id: 0, path: ['A'] }, + { id: 1, path: ['B', 'A'] }, + { id: 2, path: ['B', 'A', 'A'] } + ], + defaultGroupingExpansionDepth: 0, + } + + Returns: + + { + ids: [0, 1, 2, 'auto-generated-row-B'], + idRowsLookup: { 0: {...}, 1: {...}, 2: {...}, 'auto-generated-row-B': {} }, + tree: { + '0': { id: 0, parent: null, expanded: false, depth: 0, groupingValue: 'A' }, + 'auto-generated-row-B': { id: 'auto-generated-row-B', parent: null, expanded: false, depth: 0, groupingValue: 'B' }, + '1': { id: 1, parent: 'auto-generated-row-B', expanded: false, depth: 1, groupingValue: 'A' }, + '2': { id: 2, parent: 1, expanded: false, depth: 2, groupingValue: 'A' }, + }, + treeDepth: 3, + } + ``` */ export const buildRowTree = (params: GenerateRowTreeParams): GridRowGroupingResult => { // During the build, we store the children as a Record to avoid linear complexity when checking if a children is already defined. From 95d57583cf61f299087a66a0404b8983d261b977 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 16 Nov 2021 12:22:01 +0100 Subject: [PATCH 376/390] Add tests for buildRowTree --- .../_modules_/grid/utils/rowTreeUtils.test.ts | 184 ++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 packages/grid/_modules_/grid/utils/rowTreeUtils.test.ts diff --git a/packages/grid/_modules_/grid/utils/rowTreeUtils.test.ts b/packages/grid/_modules_/grid/utils/rowTreeUtils.test.ts new file mode 100644 index 0000000000000..ee8b451e90796 --- /dev/null +++ b/packages/grid/_modules_/grid/utils/rowTreeUtils.test.ts @@ -0,0 +1,184 @@ +import { expect } from 'chai'; +import { buildRowTree } from './rowTreeUtils'; + +describe('buildRowTree', () => { + it('should not expand the rows when defaultGroupingExpansionDepth === 0', () => { + const response = buildRowTree({ + idRowsLookup: { + 0: {}, + 1: {}, + 2: {}, + }, + ids: [0, 1, 2], + rows: [ + { id: 0, path: ['A'] }, + { id: 1, path: ['A', 'A'] }, + { id: 2, path: ['A', 'A', 'A'] }, + ], + defaultGroupingExpansionDepth: 0, + }); + + expect( + Object.values(response.tree).map((node) => ({ id: node.id, expanded: node.expanded })), + ).to.deep.equal([ + { id: 0, expanded: false }, + { id: 1, expanded: false }, + { id: 2, expanded: false }, + ]); + }); + + it('should expand the rows up to defaultGroupingExpansionDepth', () => { + const response = buildRowTree({ + idRowsLookup: { + 0: {}, + 1: {}, + 2: {}, + }, + ids: [0, 1, 2], + rows: [ + { id: 0, path: ['A'] }, + { id: 1, path: ['A', 'A'] }, + { id: 2, path: ['A', 'A', 'A'] }, + ], + defaultGroupingExpansionDepth: 2, + }); + + expect( + Object.values(response.tree).map((node) => ({ id: node.id, expanded: node.expanded })), + ).to.deep.equal([ + { id: 0, expanded: true }, + { id: 1, expanded: true }, + { id: 2, expanded: false }, + ]); + }); + + it('should expanded all the rows when defaultGroupingExpansionDepth === -1', () => { + const response = buildRowTree({ + idRowsLookup: { + 0: {}, + 1: {}, + 2: {}, + }, + ids: [0, 1, 2], + rows: [ + { id: 0, path: ['A'] }, + { id: 1, path: ['A', 'A'] }, + { id: 2, path: ['A', 'A', 'A'] }, + ], + defaultGroupingExpansionDepth: -1, + }); + + expect( + Object.values(response.tree).map((node) => ({ id: node.id, expanded: node.expanded })), + ).to.deep.equal([ + { id: 0, expanded: true }, + { id: 1, expanded: true }, + { id: 2, expanded: true }, + ]); + }); + + it('should link parent and children in the tree', () => { + const response = buildRowTree({ + idRowsLookup: { + 0: {}, + 1: {}, + 2: {}, + 3: {}, + }, + ids: [0, 1, 2, 3], + rows: [ + { id: 0, path: ['A'] }, + { id: 1, path: ['A', 'A'] }, + { id: 2, path: ['A', 'A', 'A'] }, + { id: 3, path: ['A', 'A', 'B'] }, + ], + defaultGroupingExpansionDepth: 0, + }); + + expect( + Object.values(response.tree).map((node) => ({ + id: node.id, + parent: node.parent, + children: node.children, + })), + ).to.deep.equal([ + { id: 0, parent: null, children: [1] }, + { id: 1, parent: 0, children: [2, 3] }, + { id: 2, parent: 1, children: undefined }, + { id: 3, parent: 1, children: undefined }, + ]); + }); + + it('should calculate the depth of the tree', () => { + const response = buildRowTree({ + idRowsLookup: { + 0: {}, + 1: {}, + 2: {}, + 3: {}, + }, + ids: [0, 1, 2, 3], + rows: [ + { id: 0, path: ['A'] }, + { id: 1, path: ['A', 'A'] }, + { id: 2, path: ['A', 'A', 'A'] }, + { id: 3, path: ['A', 'A', 'B'] }, + ], + defaultGroupingExpansionDepth: 0, + }); + + expect(response.treeDepth).to.equal(3); + }); + + it('should add auto generated row when missing parent', () => { + const response = buildRowTree({ + idRowsLookup: { + 0: {}, + 1: {}, + }, + ids: [0, 1], + rows: [ + { id: 0, path: ['A'] }, + { id: 1, path: ['A', 'A', 'A'] }, + ], + defaultGroupingExpansionDepth: 0, + }); + + expect(response).to.deep.equal({ + idRowsLookup: { + 0: {}, + 1: {}, + 'auto-generated-row-A-A': {}, + }, + ids: [0, 1, 'auto-generated-row-A-A'], + treeDepth: 3, + tree: { + 0: { + children: ['auto-generated-row-A-A'], + depth: 0, + expanded: false, + groupingValue: 'A', + id: 0, + parent: null, + }, + 'auto-generated-row-A-A': { + children: [1], + depth: 1, + expanded: false, + groupingValue: 'A', + id: 'auto-generated-row-A-A', + isAutoGenerated: true, + parent: 0, + }, + 1: { + children: undefined, + depth: 2, + expanded: false, + groupingValue: 'A', + id: 1, + parent: 'auto-generated-row-A-A', + }, + }, + }); + }); +}); From ba5efcce2749df0dd7c8d5031ad0898797886daf Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Thu, 18 Nov 2021 09:06:16 +0100 Subject: [PATCH 377/390] Update docs/src/pages/components/data-grid/group-pivot/group-pivot.md Co-authored-by: Matheus Wichman --- docs/src/pages/components/data-grid/group-pivot/group-pivot.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md index d5524af673cf7..19bbc176b2584 100644 --- a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md +++ b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md @@ -95,7 +95,7 @@ You can limit the filtering to the top level rows with the `disableChildrenFilte ### Sorting By default, the sorting is applied to every depth of the tree. -You can limit the filtering to the top level rows with the `disableChildrenSorting` prop. +You can limit the sorting to the top level rows with the `disableChildrenSorting` prop. {{"demo": "pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js", "bg": "inline", "defaultCodeOpen": false}} From 4a02b0b5485effca507392b021a5d277f066a590 Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Thu, 18 Nov 2021 09:06:32 +0100 Subject: [PATCH 378/390] Update docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx Co-authored-by: Matheus Wichman --- .../data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx index 65878431aef13..60c5dedf505ad 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx @@ -116,7 +116,7 @@ const columns: GridColumns = [ const getTreeDataPath = (row) => row.hierarchy; -export default function DisableChildrenSortingTreeData() { +export default function DisableChildrenFilteringTreeData() { const [disableChildrenFiltering, setDisableChildrenFiltering] = React.useState(true); const [filterModel, setFilterModel] = React.useState({ From 9932acc251064b7ade597d3da27c57f35a64abe7 Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Thu, 18 Nov 2021 09:06:42 +0100 Subject: [PATCH 379/390] Update packages/grid/_modules_/grid/components/containers/GridRootStyles.ts Co-authored-by: Matheus Wichman --- .../grid/_modules_/grid/components/containers/GridRootStyles.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/components/containers/GridRootStyles.ts b/packages/grid/_modules_/grid/components/containers/GridRootStyles.ts index 356113849aa50..d83ebe88b663a 100644 --- a/packages/grid/_modules_/grid/components/containers/GridRootStyles.ts +++ b/packages/grid/_modules_/grid/components/containers/GridRootStyles.ts @@ -298,7 +298,7 @@ export const GridRootStyles = styled('div', { [`& .${gridClasses.treeDataGroupingCellToggle}`]: { flex: '0 0 28px', alignSelf: 'stretch', - marginRight: 16, + marginRight: theme.spacing(2), }, }; From 50994f0213e62ec693b5d4ad0b6ceaf1fcd79d11 Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Thu, 18 Nov 2021 09:06:51 +0100 Subject: [PATCH 380/390] Update docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx Co-authored-by: Matheus Wichman --- .../group-pivot/DisableChildrenFilteringTreeData.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx index 60c5dedf505ad..f5908e3233c96 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx @@ -140,7 +140,11 @@ export default function DisableChildrenFilteringTreeData() { onChange={(event) => setDisableChildrenFiltering(event.target.checked)} /> } - label="Enable `disableChildrenFiltering`" + label={ + + Enable disableChildrenFiltering + + } />
From 06cbd87daf90efde22faef32302b5bcc412c80bf Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 18 Nov 2021 09:13:51 +0100 Subject: [PATCH 381/390] Code review --- .../CustomGroupingColumnTreeData.js | 2 +- .../CustomGroupingColumnTreeData.tsx | 2 +- .../DisableChildrenFilteringTreeData.js | 10 ++++++-- .../DisableChildrenFilteringTreeData.tsx | 2 ++ .../group-pivot/SetRowExpansionTreeData.js | 25 +++++++++++++------ .../group-pivot/SetRowExpansionTreeData.tsx | 20 +++++++++------ .../SetRowExpansionTreeData.tsx.preview | 3 ++- .../data-grid/group-pivot/group-pivot.md | 4 +-- .../cell/GridTreeDataGroupingCell.tsx | 2 +- .../hooks/features/filter/useGridFilter.ts | 2 +- .../hooks/features/rows/useGridParamsApi.ts | 4 +-- .../grid/hooks/features/rows/useGridRows.ts | 12 ++++----- .../treeData/gridTreeDataGroupColDef.tsx | 2 +- .../features/treeData/useGridTreeData.ts | 4 +-- .../_modules_/grid/models/api/gridRowApi.ts | 6 ++--- .../grid/_modules_/grid/models/gridRows.ts | 2 +- .../_modules_/grid/utils/rowTreeUtils.test.ts | 15 ++++++++--- 17 files changed, 73 insertions(+), 44 deletions(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js index 4e3db7634d0a4..02e11ac62beb2 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.js @@ -25,7 +25,7 @@ const CustomGridTreeDataGroupingCell = (props) => { }; const handleClick = (event) => { - apiRef.current.unstable_setRowExpansion(id, !value.expanded); + apiRef.current.setRowChildrenExpansion(id, !value.expanded); apiRef.current.setCellFocus(id, field); event.stopPropagation(); }; diff --git a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx index 600834a1315df..3efd3e1c1efbe 100644 --- a/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/CustomGroupingColumnTreeData.tsx @@ -35,7 +35,7 @@ const CustomGridTreeDataGroupingCell = ( }; const handleClick = (event) => { - apiRef.current.unstable_setRowExpansion(id, !value.expanded); + apiRef.current.setRowChildrenExpansion(id, !value.expanded); apiRef.current.setCellFocus(id, field); event.stopPropagation(); }; diff --git a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js index c9d365081c2e5..a608d8b716c14 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.js @@ -4,6 +4,7 @@ import Stack from '@mui/material/Stack'; import Checkbox from '@mui/material/Checkbox'; import FormGroup from '@mui/material/FormGroup'; import FormControlLabel from '@mui/material/FormControlLabel'; +import Typography from '@mui/material/Typography'; const rows = [ { @@ -110,13 +111,14 @@ const columns = [ const getTreeDataPath = (row) => row.hierarchy; -export default function DisableChildrenSortingTreeData() { +export default function DisableChildrenFilteringTreeData() { const [disableChildrenFiltering, setDisableChildrenFiltering] = React.useState(true); const [filterModel, setFilterModel] = React.useState({ linkOperator: GridLinkOperator.Or, items: [ { + id: 0, columnField: 'recruitmentDate', operatorValue: 'before', value: '2018-01-01', @@ -134,7 +136,11 @@ export default function DisableChildrenSortingTreeData() { onChange={(event) => setDisableChildrenFiltering(event.target.checked)} /> } - label="Enable `disableChildrenFiltering`" + label={ + + Enable disableChildrenFiltering + + } />
diff --git a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx index f5908e3233c96..256e8a37a8f8d 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenFilteringTreeData.tsx @@ -10,6 +10,7 @@ import Stack from '@mui/material/Stack'; import Checkbox from '@mui/material/Checkbox'; import FormGroup from '@mui/material/FormGroup'; import FormControlLabel from '@mui/material/FormControlLabel'; +import Typography from '@mui/material/Typography'; const rows: GridRowsProp = [ { @@ -123,6 +124,7 @@ export default function DisableChildrenFilteringTreeData() { linkOperator: GridLinkOperator.Or, items: [ { + id: 0, columnField: 'recruitmentDate', operatorValue: 'before', value: '2018-01-01', diff --git a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js index 498692c9f6bad..25a936898a2ec 100644 --- a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js @@ -1,5 +1,9 @@ import * as React from 'react'; -import { DataGridPro, useGridApiRef } from '@mui/x-data-grid-pro'; +import { + DataGridPro, + useGridApiRef, + gridVisibleSortedRowIdsSelector, +} from '@mui/x-data-grid-pro'; import Stack from '@mui/material/Stack'; import Button from '@mui/material/Button'; @@ -111,17 +115,21 @@ const getTreeDataPath = (row) => row.hierarchy; export default function SetRowExpansionTreeData() { const apiRef = useGridApiRef(); - const toggleFirstRow = () => { - const rowId = apiRef.current.getRowIdFromRowIndex(1); - apiRef.current.unstable_setRowExpansion( - rowId, - !apiRef.current.unstable_getRowNode(rowId)?.expanded, - ); + const toggleSecondRow = () => { + const rowIds = gridVisibleSortedRowIdsSelector(apiRef.current.state); + + if (rows.length > 1) { + const rowId = rowIds[1]; + apiRef.current.setRowChildrenExpansion( + rowId, + !apiRef.current.getRowNode(rowId)?.childrenExpanded, + ); + } }; return ( - +
diff --git a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx index ea0c56a419d13..2512e502342c5 100644 --- a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx @@ -4,6 +4,7 @@ import { GridColumns, GridRowsProp, useGridApiRef, + gridVisibleSortedRowIdsSelector, } from '@mui/x-data-grid-pro'; import Stack from '@mui/material/Stack'; import Button from '@mui/material/Button'; @@ -116,17 +117,21 @@ const getTreeDataPath = (row) => row.hierarchy; export default function SetRowExpansionTreeData() { const apiRef = useGridApiRef(); - const toggleFirstRow = () => { - const rowId = apiRef.current.getRowIdFromRowIndex(1); - apiRef.current.unstable_setRowExpansion( - rowId, - !apiRef.current.unstable_getRowNode(rowId)?.expanded, - ); + const toggleSecondRow = () => { + const rowIds = gridVisibleSortedRowIdsSelector(apiRef.current.state); + + if (rows.length > 1) { + const rowId = rowIds[1]; + apiRef.current.setRowChildrenExpansion( + rowId, + !apiRef.current.getRowNode(rowId)?.childrenExpanded, + ); + } }; return ( - +
diff --git a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx.preview b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx.preview index 1b53f54631a60..68347f54d4626 100644 --- a/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx.preview +++ b/docs/src/pages/components/data-grid/group-pivot/SetRowExpansionTreeData.tsx.preview @@ -1,4 +1,4 @@ - +
\ No newline at end of file diff --git a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md index 19bbc176b2584..81aa39ac289b9 100644 --- a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md +++ b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md @@ -10,8 +10,6 @@ title: Data Grid - Group & Pivot Tree Data allows to display data with parent / child relationships. -### General behavior - To enable the Tree Data, you simply have to use the `treeData` prop as well as provide a `getTreeDataPath` prop. The `getTreeDataPath` function returns an array of strings which represents the path to a given row. @@ -70,7 +68,7 @@ If you want to expand the whole tree, set `defaultGroupingExpansionDepth = -1` {{"demo": "pages/components/data-grid/group-pivot/DefaultGroupingExpansionDepthTreeData.js", "bg": "inline", "defaultCodeOpen": false}} -Use the `unstable_setRowExpansion` method on `apiRef` to programmatically set the expansion of a row. +Use the `setRowChildrenExpansion` method on `apiRef` to programmatically set the expansion of a row. {{"demo": "pages/components/data-grid/group-pivot/SetRowExpansionTreeData.js", "bg": "inline", "defaultCodeOpen": false}} diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index 9d7b0aa3cb60d..3fea1a487d372 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -53,7 +53,7 @@ const GridTreeDataGroupingCell = (props: GridRenderCellParams { - apiRef.current.unstable_setRowExpansion(id, !value.expanded); + apiRef.current.setRowChildrenExpansion(id, !value.expanded); apiRef.current.setCellFocus(id, field); event.stopPropagation(); }; diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 0c8a906122290..07a3b9bf203a3 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -191,7 +191,7 @@ export const useGridFilter = ( const childSubTreeSize = filterTreeNode( childNode, isMatchingFilters ?? isParentMatchingFilters, - areAncestorsExpanded && !!node.expanded, + areAncestorsExpanded && !!node.childrenExpanded, ); filteredDescendantCount += childSubTreeSize; diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridParamsApi.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridParamsApi.ts index 0fe78415b5206..9513aac504911 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridParamsApi.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridParamsApi.ts @@ -63,7 +63,7 @@ export function useGridParamsApi(apiRef: GridApiRef) { const getBaseCellParams = React.useCallback( (id: GridRowId, field: string) => { const row = apiRef.current.getRow(id); - const rowNode = apiRef.current.unstable_getRowNode(id); + const rowNode = apiRef.current.getRowNode(id); if (!row || !rowNode) { throw new Error(`No row with id #${id} found`); @@ -96,7 +96,7 @@ export function useGridParamsApi(apiRef: GridApiRef) { const colDef = apiRef.current.getColumn(field); const value = apiRef.current.getCellValue(id, field); const row = apiRef.current.getRow(id); - const rowNode = apiRef.current.unstable_getRowNode(id); + const rowNode = apiRef.current.getRowNode(id); if (!row || !rowNode) { throw new Error(`No row with id #${id} found`); diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts index 529772a86a967..6e9b10e667634 100644 --- a/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts +++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridRows.ts @@ -301,13 +301,13 @@ export const useGridRows = ( [apiRef], ); - const setRowExpansion = React.useCallback( + const setRowChildrenExpansion = React.useCallback( (id, isExpanded) => { - const currentNode = apiRef.current.unstable_getRowNode(id); + const currentNode = apiRef.current.getRowNode(id); if (!currentNode) { throw new Error(`MUI: No row with id #${id} found`); } - const newNode: GridRowTreeNodeConfig = { ...currentNode, expanded: isExpanded }; + const newNode: GridRowTreeNodeConfig = { ...currentNode, childrenExpanded: isExpanded }; setGridState((state) => { return { ...state, @@ -323,7 +323,7 @@ export const useGridRows = ( [apiRef, setGridState, forceUpdate], ); - const getRowNode = React.useCallback( + const getRowNode = React.useCallback( (id) => gridRowTreeSelector(apiRef.current.state)[id] ?? null, [apiRef], ); @@ -395,8 +395,8 @@ export const useGridRows = ( getAllRowIds, setRows, updateRows, - unstable_setRowExpansion: setRowExpansion, - unstable_getRowNode: getRowNode, + setRowChildrenExpansion, + getRowNode, }; useGridApiMethod(apiRef, rowApi, 'GridRowApi'); diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx index 3df09b075aab6..99e28df02f083 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx +++ b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx @@ -24,7 +24,7 @@ export const GRID_TREE_DATA_GROUP_COL_DEF: GridColDef = { valueGetter: ({ rowNode, api }): GridTreeDataGroupingCellValue => ({ label: rowNode.groupingValue, depth: rowNode.depth, - expanded: rowNode.expanded ?? false, + expanded: rowNode.childrenExpanded ?? false, filteredDescendantCount: gridFilteredDescendantCountLookupSelector(api.state)[rowNode.id] ?? 0, }), renderCell: (params: GridRenderCellParams) => ( diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 0c34f5ea8313a..97c49b0f405fc 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -118,7 +118,7 @@ export const useGridTreeData = ( event.stopPropagation(); event.preventDefault(); - const node = apiRef.current.unstable_getRowNode(params.id); + const node = apiRef.current.getRowNode(params.id); const filteredDescendantCount = gridFilteredDescendantCountLookupSelector(apiRef.current.state)[params.id] ?? 0; @@ -126,7 +126,7 @@ export const useGridTreeData = ( return; } - apiRef.current.unstable_setRowExpansion(params.id, !node.expanded); + apiRef.current.setRowChildrenExpansion(params.id, !node.childrenExpanded); } }, [apiRef], diff --git a/packages/grid/_modules_/grid/models/api/gridRowApi.ts b/packages/grid/_modules_/grid/models/api/gridRowApi.ts index e2f2cd9ba1a51..7e1d70fae38aa 100644 --- a/packages/grid/_modules_/grid/models/api/gridRowApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridRowApi.ts @@ -39,14 +39,12 @@ export interface GridRowApi { * Gets the row node from the internal tree structure. * @param {GridRowId} id The id of the row. * @returns {GridRowTreeNodeConfig} The row data. - * @ignore - do not document. */ - unstable_getRowNode: (id: GridRowId) => GridRowTreeNodeConfig | null; + getRowNode: (id: GridRowId) => GridRowTreeNodeConfig | null; /** * Expand or collapse a row children. * @param {GridRowId} id the ID of the row to expand or collapse. * @param {boolean} isExpanded A boolean indicating if the row must be expanded or collapsed. - * @ignore - do not document. */ - unstable_setRowExpansion: (id: GridRowId, isExpanded: boolean) => void; + setRowChildrenExpansion: (id: GridRowId, isExpanded: boolean) => void; } diff --git a/packages/grid/_modules_/grid/models/gridRows.ts b/packages/grid/_modules_/grid/models/gridRows.ts index 288a27daea776..b73caa71b0815 100644 --- a/packages/grid/_modules_/grid/models/gridRows.ts +++ b/packages/grid/_modules_/grid/models/gridRows.ts @@ -35,7 +35,7 @@ export interface GridRowTreeNodeConfig { /** * Current expansion status of the row. */ - expanded?: boolean; + childrenExpanded?: boolean; /** * 0-based depth of the row in the tree. diff --git a/packages/grid/_modules_/grid/utils/rowTreeUtils.test.ts b/packages/grid/_modules_/grid/utils/rowTreeUtils.test.ts index ee8b451e90796..52263ceb70d5c 100644 --- a/packages/grid/_modules_/grid/utils/rowTreeUtils.test.ts +++ b/packages/grid/_modules_/grid/utils/rowTreeUtils.test.ts @@ -19,7 +19,10 @@ describe('buildRowTree', () => { }); expect( - Object.values(response.tree).map((node) => ({ id: node.id, expanded: node.expanded })), + Object.values(response.tree).map((node) => ({ + id: node.id, + expanded: node.childrenExpanded, + })), ).to.deep.equal([ { id: 0, expanded: false }, { id: 1, expanded: false }, @@ -44,7 +47,10 @@ describe('buildRowTree', () => { }); expect( - Object.values(response.tree).map((node) => ({ id: node.id, expanded: node.expanded })), + Object.values(response.tree).map((node) => ({ + id: node.id, + expanded: node.childrenExpanded, + })), ).to.deep.equal([ { id: 0, expanded: true }, { id: 1, expanded: true }, @@ -69,7 +75,10 @@ describe('buildRowTree', () => { }); expect( - Object.values(response.tree).map((node) => ({ id: node.id, expanded: node.expanded })), + Object.values(response.tree).map((node) => ({ + id: node.id, + expanded: node.childrenExpanded, + })), ).to.deep.equal([ { id: 0, expanded: true }, { id: 1, expanded: true }, From 296906f750d08a1065d79507da83eea717a2f3a5 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 18 Nov 2021 09:30:36 +0100 Subject: [PATCH 382/390] Fix --- docs/pages/api-docs/data-grid/grid-api.md | 166 +++++++++--------- .../cell/GridTreeDataGroupingCell.tsx | 2 +- .../GridCellCheckboxRenderer.tsx | 2 +- .../_modules_/grid/utils/rowTreeUtils.test.ts | 30 ++-- .../grid/_modules_/grid/utils/rowTreeUtils.ts | 14 +- 5 files changed, 108 insertions(+), 106 deletions(-) diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index 20aea98608dd8..e6ca4035e3788 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -10,85 +10,87 @@ import { GridApi } from '@mui/x-data-grid-pro'; ## Properties -| Name | Type | Description | -| :---------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| applySorting | () => void | Applies the current sort model to the rows. | -| commitCellChange | (params: GridCommitCellChangeParams, event?: any) => boolean \| Promise<boolean> | Updates the field at the given id with the value stored in the edit row model. | -| commitRowChange | (id: GridRowId, event?: any) => boolean \| Promise<boolean> | Updates the row at the given id with the values stored in the edit row model. | -| deleteFilterItem | (item: GridFilterItem) => void | Deletes a GridFilterItem. | -| exportDataAsCsv | (options?: GridCsvExportOptions) => void | Downloads and exports a CSV of the grid's data. | -| exportDataAsPrint | (options?: GridPrintExportOptions) => void | Print the grid's data. | -| forceUpdate | Dispatch<any> | Forces the grid to rerender. It's often used after a state update. | -| getAllColumns | () => GridStateColDef[] | Returns an array of [GridColDef](/api/data-grid/grid-col-def/) containing all the column definitions. | -| getAllRowIds | () => GridRowId[] | Gets the list of row ids. | -| getCellElement | (id: GridRowId, field: string) => null \| HTMLDivElement | Gets the underlying DOM element for a cell at the given `id` and `field`. | -| getCellMode | (id: GridRowId, field: string) => GridCellMode | Gets the mode of a cell. | -| getCellParams | (id: GridRowId, field: string) => GridCellParams<any, any, any> | Gets the [GridCellParams](/api/data-grid/grid-cell-params/) object that is passed as argument in events. | -| getCellValue | (id: GridRowId, field: string) => GridCellValue | Gets the value of a cell at the given `id` and `field`. | -| getColumn | (field: string) => GridStateColDef | Returns the [GridColDef](/api/data-grid/grid-col-def/) for the given `field`. | -| getColumnHeaderElement | (field: string) => null \| HTMLDivElement | Gets the underlying DOM element for the column header with the given `field`. | -| getColumnHeaderParams | (field: string) => GridColumnHeaderParams | Gets the GridColumnHeaderParams object that is passed as argument in events. | -| getColumnIndex | (field: string, useVisibleColumns?: boolean) => number | Returns the index position of a column. By default, only the visible columns are considered.
Pass `false` to `useVisibleColumns` to consider all columns. | -| getColumnPosition | (field: string) => number | Returns the left-position of a column relative to the inner border of the grid. | -| getColumnsMeta | () => GridColumnsMeta | Returns the GridColumnsMeta for each column. | -| getDataAsCsv | (options?: GridCsvExportOptions) => string | Returns the grid data as a CSV string.
This method is used internally by `exportDataAsCsv`. | -| getEditRowsModel | () => GridEditRowsModel | Gets the edit rows model of the grid. | -| getLocaleText | <T extends keyof GridLocaleText>(key: T) => GridLocaleText[T] | Returns the translation for the `key`. | -| getRootDimensions | () => null \| GridDimensions | Returns the dimensions of the grid | -| getRow | (id: GridRowId) => null \| { [key: string]: any } | Gets the row data with a given id. | -| getRowElement | (id: GridRowId) => null \| HTMLDivElement | Gets the underlying DOM element for a row at the given `id`. | -| getRowIdFromRowIndex | (index: number) => GridRowId | Gets the `GridRowId` of a row at a specific index.
The index is based on the sorted but unfiltered row list. | -| getRowIndex | (id: GridRowId) => number | Gets the row index of a row with a given id.
The index is based on the sorted but unfiltered row list. | -| getRowMode | (id: GridRowId) => GridRowMode | Gets the mode of a row. | -| getRowModels | () => Map<GridRowId, { [key: string]: any }> | Gets the full set of rows as Map<GridRowId, GridRowModel>. | -| getRowParams | (id: GridRowId) => GridRowParams<{ [key: string]: any }> | Gets the [GridRowParams](/api/data-grid/grid-row-params/) object that is passed as argument in events. | -| getRowsCount | () => number | Gets the total number of rows in the grid. | -| getScrollPosition | () => GridScrollParams | Returns the current scroll position. | -| getSelectedRows | () => Map<GridRowId, { [key: string]: any }> | Returns an array of the selected rows. | -| getSortedRowIds | () => GridRowId[] | Returns all row ids sorted according to the active sort model. | -| getSortedRows | () => { [key: string]: any }[] | Returns all rows sorted according to the active sort model. | -| getSortModel | () => GridSortModel | Returns the sort model currently applied to the grid. | -| getVisibleColumns | () => GridStateColDef[] | Returns the currently visible columns. | -| getVisibleRowModels | () => Map<GridRowId, { [key: string]: any }> | Returns a sorted `Map` containing only the visible rows. | -| hideColumnMenu | () => void | Hides the column menu that is open. | -| hideFilterPanel | () => void | Hides the filter panel. | -| hidePreferences | () => void | Hides the preferences panel. | -| isCellEditable | (params: GridCellParams<any, any, any>) => boolean | Controls if a cell is editable. | -| isRowSelected | (id: GridRowId) => boolean | Determines if a row is selected or not. | -| publishEvent | (name: string, params?: any, event?: MuiEvent<MouseEvent \| UIEvent \| Event \| SyntheticEvent<Element, Event> \| TouchEvent \| ErrorEvent \| ProgressEvent<EventTarget> \| ClipboardEvent \| AnimationEvent \| InputEvent \| FocusEvent \| CompositionEvent \| DragEvent \| FormDataEvent \| PointerEvent \| KeyboardEvent \| SecurityPolicyViolationEvent \| TransitionEvent \| WheelEvent>) => void | Emits an event. | -| resize | () => void | Triggers a resize of the component and recalculation of width and height. | -| scroll | (params: Partial<GridScrollParams>) => void | Triggers the viewport to scroll to the given positions (in pixels). | -| scrollToIndexes | (params: Partial<GridCellIndexCoordinates>) => boolean | Triggers the viewport to scroll to the cell at indexes given by `params`.
Returns `true` if the grid had to scroll to reach the target. | -| selectRow | (id: GridRowId, isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of a row. | -| selectRowRange | (range: { endId: GridRowId; startId: GridRowId }, isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of all the selectable rows in a range. | -| selectRows | (ids: GridRowId[], isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of multiple rows. | -| setCellFocus | (id: GridRowId, field: string) => void | Sets the focus to the cell at the given `id` and `field`. | -| setCellMode | (id: GridRowId, field: string, mode: GridCellMode) => void | Sets the mode of a cell. | -| setColumnHeaderFocus | (field: string, event?: SyntheticEvent<Element, Event>) => void | Sets the focus to the column header at the given `field`. | -| setColumnIndex | (field: string, targetIndexPosition: number) => void | Moves a column from its original position to the position given by `targetIndexPosition`. | -| setColumnVisibility | (field: string, isVisible: boolean) => void | Changes the visibility of the column referred by `field`. | -| setColumnWidth | (field: string, width: number) => void | Updates the width of a column. | -| setDensity | (density: GridDensity, headerHeight?: number, rowHeight?: number) => void | Sets the density of the grid. | -| setEditCellValue | (params: GridEditCellValueParams, event?: SyntheticEvent<Element, Event>) => void | Sets the value of the edit cell.
Commonly used inside the edit cell component. | -| setEditRowsModel | (model: GridEditRowsModel) => void | Set the edit rows model of the grid. | -| setFilterLinkOperator | (operator: GridLinkOperator) => void | Changes the GridLinkOperator used to connect the filters. | -| setFilterModel | (model: GridFilterModel) => void | Sets the filter model to the one given by `model`. | -| setPage | (page: number) => void | Sets the displayed page to the value given by `page`. | -| setPageSize | (pageSize: number) => void | Sets the number of displayed rows to the value given by `pageSize`. | -| setRowMode | (id: GridRowId, mode: GridRowMode) => void | Sets the mode of a row. | -| setRows | (rows: { [key: string]: any }[]) => void | Sets a new set of rows. | -| setSelectionModel | (rowIds: GridRowId[]) => void | Updates the selected rows to be those passed to the `rowIds` argument.
Any row already selected will be unselected. | -| setSortModel | (model: GridSortModel) => void | Updates the sort model and triggers the sorting of rows. | -| setState | (state: GridState \| ((previousState: GridState) => GridState)) => void | Sets the whole state of the grid. | -| showColumnMenu | (field: string) => void | Display the column menu under the `field` column. | -| showError | (props: any) => void | Displays the error overlay component. | -| showFilterPanel | (targetColumnField?: string) => void | Shows the filter panel. If `targetColumnField` is given, a filter for this field is also added. | -| showPreferences | (newValue: GridPreferencePanelsValue) => void | Displays the preferences panel. The `newValue` argument controls the content of the panel. | -| sortColumn | (column: GridColDef, direction?: GridSortDirection, allowMultipleSorting?: boolean) => void | Sorts a column. | -| state | GridState | Property that contains the whole state of the grid. | -| subscribeEvent | <Params, Event extends MuiEvent<BaseEvent>>(event: string, handler: GridEventListener<Params, Event>, options?: EventListenerOptions) => () => void | Registers a handler for an event. | -| toggleColumnMenu | (field: string) => void | Toggles the column menu under the `field` column. | -| updateColumn | (col: GridColDef) => void | Updates the definition of a column. | -| updateColumns | (cols: GridColDef[]) => void | Updates the definition of multiple columns at the same time. | -| updateRows | (updates: GridRowModelUpdate<>[]) => void | Allows to updates, insert and delete rows in a single call. | -| upsertFilterItem | (item: GridFilterItem) => void | Updates or inserts a GridFilterItem. | +| Name | Type | Description | +| :----------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| applySorting | () => void | Applies the current sort model to the rows. | +| commitCellChange | (params: GridCommitCellChangeParams, event?: any) => boolean \| Promise<boolean> | Updates the field at the given id with the value stored in the edit row model. | +| commitRowChange | (id: GridRowId, event?: any) => boolean \| Promise<boolean> | Updates the row at the given id with the values stored in the edit row model. | +| deleteFilterItem | (item: GridFilterItem) => void | Deletes a GridFilterItem. | +| exportDataAsCsv | (options?: GridCsvExportOptions) => void | Downloads and exports a CSV of the grid's data. | +| exportDataAsPrint | (options?: GridPrintExportOptions) => void | Print the grid's data. | +| forceUpdate | Dispatch<any> | Forces the grid to rerender. It's often used after a state update. | +| getAllColumns | () => GridStateColDef[] | Returns an array of [GridColDef](/api/data-grid/grid-col-def/) containing all the column definitions. | +| getAllRowIds | () => GridRowId[] | Gets the list of row ids. | +| getCellElement | (id: GridRowId, field: string) => null \| HTMLDivElement | Gets the underlying DOM element for a cell at the given `id` and `field`. | +| getCellMode | (id: GridRowId, field: string) => GridCellMode | Gets the mode of a cell. | +| getCellParams | (id: GridRowId, field: string) => GridCellParams<any, any, any> | Gets the [GridCellParams](/api/data-grid/grid-cell-params/) object that is passed as argument in events. | +| getCellValue | (id: GridRowId, field: string) => GridCellValue | Gets the value of a cell at the given `id` and `field`. | +| getColumn | (field: string) => GridStateColDef | Returns the [GridColDef](/api/data-grid/grid-col-def/) for the given `field`. | +| getColumnHeaderElement | (field: string) => null \| HTMLDivElement | Gets the underlying DOM element for the column header with the given `field`. | +| getColumnHeaderParams | (field: string) => GridColumnHeaderParams | Gets the GridColumnHeaderParams object that is passed as argument in events. | +| getColumnIndex | (field: string, useVisibleColumns?: boolean) => number | Returns the index position of a column. By default, only the visible columns are considered.
Pass `false` to `useVisibleColumns` to consider all columns. | +| getColumnPosition | (field: string) => number | Returns the left-position of a column relative to the inner border of the grid. | +| getColumnsMeta | () => GridColumnsMeta | Returns the GridColumnsMeta for each column. | +| getDataAsCsv | (options?: GridCsvExportOptions) => string | Returns the grid data as a CSV string.
This method is used internally by `exportDataAsCsv`. | +| getEditRowsModel | () => GridEditRowsModel | Gets the edit rows model of the grid. | +| getLocaleText | <T extends keyof GridLocaleText>(key: T) => GridLocaleText[T] | Returns the translation for the `key`. | +| getRootDimensions | () => null \| GridDimensions | Returns the dimensions of the grid | +| getRow | (id: GridRowId) => null \| { [key: string]: any } | Gets the row data with a given id. | +| getRowElement | (id: GridRowId) => null \| HTMLDivElement | Gets the underlying DOM element for a row at the given `id`. | +| getRowIdFromRowIndex | (index: number) => GridRowId | Gets the `GridRowId` of a row at a specific index.
The index is based on the sorted but unfiltered row list. | +| getRowIndex | (id: GridRowId) => number | Gets the row index of a row with a given id.
The index is based on the sorted but unfiltered row list. | +| getRowMode | (id: GridRowId) => GridRowMode | Gets the mode of a row. | +| getRowModels | () => Map<GridRowId, { [key: string]: any }> | Gets the full set of rows as Map<GridRowId, GridRowModel>. | +| getRowNode | (id: GridRowId) => null \| GridRowTreeNodeConfig | Gets the row node from the internal tree structure. | +| getRowParams | (id: GridRowId) => GridRowParams<{ [key: string]: any }> | Gets the [GridRowParams](/api/data-grid/grid-row-params/) object that is passed as argument in events. | +| getRowsCount | () => number | Gets the total number of rows in the grid. | +| getScrollPosition | () => GridScrollParams | Returns the current scroll position. | +| getSelectedRows | () => Map<GridRowId, { [key: string]: any }> | Returns an array of the selected rows. | +| getSortedRowIds | () => GridRowId[] | Returns all row ids sorted according to the active sort model. | +| getSortedRows | () => { [key: string]: any }[] | Returns all rows sorted according to the active sort model. | +| getSortModel | () => GridSortModel | Returns the sort model currently applied to the grid. | +| getVisibleColumns | () => GridStateColDef[] | Returns the currently visible columns. | +| getVisibleRowModels | () => Map<GridRowId, { [key: string]: any }> | Returns a sorted `Map` containing only the visible rows. | +| hideColumnMenu | () => void | Hides the column menu that is open. | +| hideFilterPanel | () => void | Hides the filter panel. | +| hidePreferences | () => void | Hides the preferences panel. | +| isCellEditable | (params: GridCellParams<any, any, any>) => boolean | Controls if a cell is editable. | +| isRowSelected | (id: GridRowId) => boolean | Determines if a row is selected or not. | +| publishEvent | (name: string, params?: any, event?: MuiEvent<MouseEvent \| UIEvent \| Event \| SyntheticEvent<Element, Event> \| TouchEvent \| ErrorEvent \| ProgressEvent<EventTarget> \| ClipboardEvent \| AnimationEvent \| InputEvent \| FocusEvent \| CompositionEvent \| DragEvent \| FormDataEvent \| PointerEvent \| KeyboardEvent \| SecurityPolicyViolationEvent \| TransitionEvent \| WheelEvent>) => void | Emits an event. | +| resize | () => void | Triggers a resize of the component and recalculation of width and height. | +| scroll | (params: Partial<GridScrollParams>) => void | Triggers the viewport to scroll to the given positions (in pixels). | +| scrollToIndexes | (params: Partial<GridCellIndexCoordinates>) => boolean | Triggers the viewport to scroll to the cell at indexes given by `params`.
Returns `true` if the grid had to scroll to reach the target. | +| selectRow | (id: GridRowId, isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of a row. | +| selectRowRange | (range: { endId: GridRowId; startId: GridRowId }, isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of all the selectable rows in a range. | +| selectRows | (ids: GridRowId[], isSelected?: boolean, resetSelection?: boolean) => void | Change the selection state of multiple rows. | +| setCellFocus | (id: GridRowId, field: string) => void | Sets the focus to the cell at the given `id` and `field`. | +| setCellMode | (id: GridRowId, field: string, mode: GridCellMode) => void | Sets the mode of a cell. | +| setColumnHeaderFocus | (field: string, event?: SyntheticEvent<Element, Event>) => void | Sets the focus to the column header at the given `field`. | +| setColumnIndex | (field: string, targetIndexPosition: number) => void | Moves a column from its original position to the position given by `targetIndexPosition`. | +| setColumnVisibility | (field: string, isVisible: boolean) => void | Changes the visibility of the column referred by `field`. | +| setColumnWidth | (field: string, width: number) => void | Updates the width of a column. | +| setDensity | (density: GridDensity, headerHeight?: number, rowHeight?: number) => void | Sets the density of the grid. | +| setEditCellValue | (params: GridEditCellValueParams, event?: SyntheticEvent<Element, Event>) => void | Sets the value of the edit cell.
Commonly used inside the edit cell component. | +| setEditRowsModel | (model: GridEditRowsModel) => void | Set the edit rows model of the grid. | +| setFilterLinkOperator | (operator: GridLinkOperator) => void | Changes the GridLinkOperator used to connect the filters. | +| setFilterModel | (model: GridFilterModel) => void | Sets the filter model to the one given by `model`. | +| setPage | (page: number) => void | Sets the displayed page to the value given by `page`. | +| setPageSize | (pageSize: number) => void | Sets the number of displayed rows to the value given by `pageSize`. | +| setRowChildrenExpansion | (id: GridRowId, isExpanded: boolean) => void | Expand or collapse a row children. | +| setRowMode | (id: GridRowId, mode: GridRowMode) => void | Sets the mode of a row. | +| setRows | (rows: { [key: string]: any }[]) => void | Sets a new set of rows. | +| setSelectionModel | (rowIds: GridRowId[]) => void | Updates the selected rows to be those passed to the `rowIds` argument.
Any row already selected will be unselected. | +| setSortModel | (model: GridSortModel) => void | Updates the sort model and triggers the sorting of rows. | +| setState | (state: GridState \| ((previousState: GridState) => GridState)) => void | Sets the whole state of the grid. | +| showColumnMenu | (field: string) => void | Display the column menu under the `field` column. | +| showError | (props: any) => void | Displays the error overlay component. | +| showFilterPanel | (targetColumnField?: string) => void | Shows the filter panel. If `targetColumnField` is given, a filter for this field is also added. | +| showPreferences | (newValue: GridPreferencePanelsValue) => void | Displays the preferences panel. The `newValue` argument controls the content of the panel. | +| sortColumn | (column: GridColDef, direction?: GridSortDirection, allowMultipleSorting?: boolean) => void | Sorts a column. | +| state | GridState | Property that contains the whole state of the grid. | +| subscribeEvent | <Params, Event extends MuiEvent<BaseEvent>>(event: string, handler: GridEventListener<Params, Event>, options?: EventListenerOptions) => () => void | Registers a handler for an event. | +| toggleColumnMenu | (field: string) => void | Toggles the column menu under the `field` column. | +| updateColumn | (col: GridColDef) => void | Updates the definition of a column. | +| updateColumns | (cols: GridColDef[]) => void | Updates the definition of multiple columns at the same time. | +| updateRows | (updates: GridRowModelUpdate<>[]) => void | Allows to updates, insert and delete rows in a single call. | +| upsertFilterItem | (item: GridFilterItem) => void | Updates or inserts a GridFilterItem. | diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index 3fea1a487d372..573b107073971 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -145,8 +145,8 @@ GridTreeDataGroupingCell.propTypes = { children: PropTypes.arrayOf( PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, ), + childrenExpanded: PropTypes.bool, depth: PropTypes.number.isRequired, - expanded: PropTypes.bool, groupingValue: PropTypes.string.isRequired, id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, isAutoGenerated: PropTypes.bool, diff --git a/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx b/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx index 5dee6402cfda4..d1bbe80bfcbdf 100644 --- a/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx +++ b/packages/grid/_modules_/grid/components/columnSelection/GridCellCheckboxRenderer.tsx @@ -141,8 +141,8 @@ GridCellCheckboxForwardRef.propTypes = { children: PropTypes.arrayOf( PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, ), + childrenExpanded: PropTypes.bool, depth: PropTypes.number.isRequired, - expanded: PropTypes.bool, groupingValue: PropTypes.string.isRequired, id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, isAutoGenerated: PropTypes.bool, diff --git a/packages/grid/_modules_/grid/utils/rowTreeUtils.test.ts b/packages/grid/_modules_/grid/utils/rowTreeUtils.test.ts index 52263ceb70d5c..fe646e2ecb195 100644 --- a/packages/grid/_modules_/grid/utils/rowTreeUtils.test.ts +++ b/packages/grid/_modules_/grid/utils/rowTreeUtils.test.ts @@ -21,12 +21,12 @@ describe('buildRowTree', () => { expect( Object.values(response.tree).map((node) => ({ id: node.id, - expanded: node.childrenExpanded, + childrenExpanded: node.childrenExpanded, })), ).to.deep.equal([ - { id: 0, expanded: false }, - { id: 1, expanded: false }, - { id: 2, expanded: false }, + { id: 0, childrenExpanded: false }, + { id: 1, childrenExpanded: false }, + { id: 2, childrenExpanded: false }, ]); }); @@ -49,12 +49,12 @@ describe('buildRowTree', () => { expect( Object.values(response.tree).map((node) => ({ id: node.id, - expanded: node.childrenExpanded, + childrenExpanded: node.childrenExpanded, })), ).to.deep.equal([ - { id: 0, expanded: true }, - { id: 1, expanded: true }, - { id: 2, expanded: false }, + { id: 0, childrenExpanded: true }, + { id: 1, childrenExpanded: true }, + { id: 2, childrenExpanded: false }, ]); }); @@ -77,12 +77,12 @@ describe('buildRowTree', () => { expect( Object.values(response.tree).map((node) => ({ id: node.id, - expanded: node.childrenExpanded, + childrenExpanded: node.childrenExpanded, })), ).to.deep.equal([ - { id: 0, expanded: true }, - { id: 1, expanded: true }, - { id: 2, expanded: true }, + { id: 0, childrenExpanded: true }, + { id: 1, childrenExpanded: true }, + { id: 2, childrenExpanded: true }, ]); }); @@ -165,7 +165,7 @@ describe('buildRowTree', () => { 0: { children: ['auto-generated-row-A-A'], depth: 0, - expanded: false, + childrenExpanded: false, groupingValue: 'A', id: 0, parent: null, @@ -173,7 +173,7 @@ describe('buildRowTree', () => { 'auto-generated-row-A-A': { children: [1], depth: 1, - expanded: false, + childrenExpanded: false, groupingValue: 'A', id: 'auto-generated-row-A-A', isAutoGenerated: true, @@ -182,7 +182,7 @@ describe('buildRowTree', () => { 1: { children: undefined, depth: 2, - expanded: false, + childrenExpanded: false, groupingValue: 'A', id: 1, parent: 'auto-generated-row-A-A', diff --git a/packages/grid/_modules_/grid/utils/rowTreeUtils.ts b/packages/grid/_modules_/grid/utils/rowTreeUtils.ts index 8918bf0a4ad12..fbf6ab4f31c4c 100644 --- a/packages/grid/_modules_/grid/utils/rowTreeUtils.ts +++ b/packages/grid/_modules_/grid/utils/rowTreeUtils.ts @@ -39,10 +39,10 @@ interface TempRowTreeNode extends Omit { ids: [0, 1, 2, 'auto-generated-row-B'], idRowsLookup: { 0: {...}, 1: {...}, 2: {...}, 'auto-generated-row-B': {} }, tree: { - '0': { id: 0, parent: null, expanded: false, depth: 0, groupingValue: 'A' }, - 'auto-generated-row-B': { id: 'auto-generated-row-B', parent: null, expanded: false, depth: 0, groupingValue: 'B' }, - '1': { id: 1, parent: 'auto-generated-row-B', expanded: false, depth: 1, groupingValue: 'A' }, - '2': { id: 2, parent: 1, expanded: false, depth: 2, groupingValue: 'A' }, + '0': { id: 0, parent: null, childrenExpanded: false, depth: 0, groupingValue: 'A' }, + 'auto-generated-row-B': { id: 'auto-generated-row-B', parent: null, childrenExpanded: false, depth: 0, groupingValue: 'B' }, + '1': { id: 1, parent: 'auto-generated-row-B', childrenExpanded: false, depth: 1, groupingValue: 'A' }, + '2': { id: 2, parent: 1, childrenExpanded: false, depth: 2, groupingValue: 'A' }, }, treeDepth: 3, } @@ -67,7 +67,7 @@ export const buildRowTree = (params: GenerateRowTreeParams): GridRowGroupingResu const nodeName = row.path[depth]; let nodeId: GridRowId; - const expanded = + const childrenExpanded = params.defaultGroupingExpansionDepth === -1 || params.defaultGroupingExpansionDepth > depth; let nodeNameConfig = nodeNameToIdSubTree[nodeName]; @@ -91,7 +91,7 @@ export const buildRowTree = (params: GenerateRowTreeParams): GridRowGroupingResu node = { id: nodeId, isAutoGenerated: true, - expanded, + childrenExpanded, parent: parentNode?.id ?? null, groupingValue: row.path[depth], depth, @@ -104,7 +104,7 @@ export const buildRowTree = (params: GenerateRowTreeParams): GridRowGroupingResu } else { tempTree[row.id] = { id: row.id, - expanded, + childrenExpanded, parent: parentNode?.id ?? null, groupingValue: row.path[depth], depth, From 9d6e15cb439b4d14a649f657a22c627eb2729079 Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Thu, 18 Nov 2021 15:31:30 +0100 Subject: [PATCH 383/390] Update packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx Co-authored-by: Matheus Wichman --- .../_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx index 573b107073971..5cfae80e500d8 100644 --- a/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx +++ b/packages/grid/_modules_/grid/components/cell/GridTreeDataGroupingCell.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { unstable_composeClasses as composeClasses } from '@mui/core'; +import { unstable_composeClasses as composeClasses } from '@mui/material'; import IconButton from '@mui/material/IconButton'; import Box from '@mui/material/Box'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; From 69be5407e5f1fe0a285976985e3d346210bf7bb1 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 19 Nov 2021 14:42:19 +0100 Subject: [PATCH 384/390] Fix groupColDef override --- packages/grid/_modules_/grid/GridComponentProps.ts | 4 +++- .../hooks/features/treeData/gridTreeDataGroupColDef.tsx | 8 ++++++-- .../grid/_modules_/grid/hooks/features/treeData/index.ts | 1 - .../grid/hooks/features/treeData/useGridTreeData.ts | 6 +++++- packages/grid/_modules_/grid/models/colDef/gridColDef.ts | 9 +++++++-- .../grid/x-data-grid-pro/src/useDataGridProComponent.tsx | 2 +- 6 files changed, 22 insertions(+), 8 deletions(-) delete mode 100644 packages/grid/_modules_/grid/hooks/features/treeData/index.ts diff --git a/packages/grid/_modules_/grid/GridComponentProps.ts b/packages/grid/_modules_/grid/GridComponentProps.ts index 99a83e2f4cdea..e6ac14c909f60 100644 --- a/packages/grid/_modules_/grid/GridComponentProps.ts +++ b/packages/grid/_modules_/grid/GridComponentProps.ts @@ -504,5 +504,7 @@ interface GridComponentOtherProps { /** * The grouping column used by the tree data. */ - groupingColDef?: GridColDefOverride | GridColDefOverrideCallback; + groupingColDef?: + | GridColDefOverride<'field' | 'editable'> + | GridColDefOverrideCallback<'field' | 'editable'>; } diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx index 99e28df02f083..f2aefa7295ea6 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx +++ b/packages/grid/_modules_/grid/hooks/features/treeData/gridTreeDataGroupColDef.tsx @@ -11,9 +11,8 @@ import { GridRenderCellParams } from '../../../models'; /** * TODO: Add sorting and filtering on the value and the filteredDescendantCount */ -export const GRID_TREE_DATA_GROUP_COL_DEF: GridColDef = { +export const GRID_TREE_DATA_GROUP_COL_DEF: Omit = { ...GRID_STRING_COL_DEF, - field: '__tree_data_group__', type: 'treeDataGroup', sortable: false, filterable: false, @@ -31,3 +30,8 @@ export const GRID_TREE_DATA_GROUP_COL_DEF: GridColDef = { ), }; + +export const GRID_TREE_DATA_GROUP_COL_DEF_FORCED_FIELDS: Pick = { + field: '__tree_data_group__', + editable: false, +}; diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/index.ts b/packages/grid/_modules_/grid/hooks/features/treeData/index.ts deleted file mode 100644 index fd93cf42729e0..0000000000000 --- a/packages/grid/_modules_/grid/hooks/features/treeData/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './useGridTreeData'; diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 97c49b0f405fc..55c255f142046 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -1,7 +1,10 @@ import * as React from 'react'; import { GridApiRef } from '../../../models/api/gridApiRef'; import { GridComponentProps } from '../../../GridComponentProps'; -import { GRID_TREE_DATA_GROUP_COL_DEF } from './gridTreeDataGroupColDef'; +import { + GRID_TREE_DATA_GROUP_COL_DEF, + GRID_TREE_DATA_GROUP_COL_DEF_FORCED_FIELDS, +} from './gridTreeDataGroupColDef'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { GridEvents } from '../../../constants'; import { @@ -36,6 +39,7 @@ export const useGridTreeData = ( const baseColDef: GridColDef = { ...GRID_TREE_DATA_GROUP_COL_DEF, headerName: apiRef.current.getLocaleText('treeDataGroupingHeaderName'), + ...GRID_TREE_DATA_GROUP_COL_DEF_FORCED_FIELDS, }; let colDefOverride: Partial; diff --git a/packages/grid/_modules_/grid/models/colDef/gridColDef.ts b/packages/grid/_modules_/grid/models/colDef/gridColDef.ts index 0e9a715d0397b..508982658ee1e 100644 --- a/packages/grid/_modules_/grid/models/colDef/gridColDef.ts +++ b/packages/grid/_modules_/grid/models/colDef/gridColDef.ts @@ -227,9 +227,14 @@ export interface GridColumnsState { lookup: GridColumnLookup; } -export type GridColDefOverride = Omit, 'field'>; +export type GridColDefOverride = Omit< + Partial, + ForcedFields +>; -export type GridColDefOverrideCallback = (params: GridColDefOverrideParams) => GridColDefOverride; +export type GridColDefOverrideCallback = ( + params: GridColDefOverrideParams, +) => GridColDefOverride; export interface GridColDefOverrideParams { /** diff --git a/packages/grid/x-data-grid-pro/src/useDataGridProComponent.tsx b/packages/grid/x-data-grid-pro/src/useDataGridProComponent.tsx index 0e1b34d4b5cff..c7c5b0cbc811b 100644 --- a/packages/grid/x-data-grid-pro/src/useDataGridProComponent.tsx +++ b/packages/grid/x-data-grid-pro/src/useDataGridProComponent.tsx @@ -26,7 +26,7 @@ import { useGridSorting } from '../../_modules_/grid/hooks/features/sorting/useG import { useGridScroll } from '../../_modules_/grid/hooks/features/scroll/useGridScroll'; import { useGridEvents } from '../../_modules_/grid/hooks/features/events/useGridEvents'; import { useGridDimensions } from '../../_modules_/grid/hooks/features/dimensions/useGridDimensions'; -import { useGridTreeData } from '../../_modules_/grid/hooks/features/treeData'; +import { useGridTreeData } from '../../_modules_/grid/hooks/features/treeData/useGridTreeData'; export const useDataGridProComponent = (apiRef: GridApiRef, props: GridComponentProps) => { useGridInitialization(apiRef, props); From 70072207e5df3dd4a9898dd812aa4b4fc817438d Mon Sep 17 00:00:00 2001 From: delangle Date: Mon, 22 Nov 2021 08:58:36 +0100 Subject: [PATCH 385/390] Code review --- .../_modules_/grid/hooks/features/treeData/useGridTreeData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts index 55c255f142046..45c86b0f52454 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.ts @@ -65,7 +65,7 @@ export const useGridTreeData = ( return columns; } - const index = columns[0].type === 'checkboxSelection' ? 1 : 0; + const index = columns[0]?.field === '__check__' ? 1 : 0; return [...columns.slice(0, index), groupingColDef, ...columns.slice(index)]; }, From c23565fccc38170877fa5afb8baad2309dfa5c4f Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 23 Nov 2021 08:40:00 +0100 Subject: [PATCH 386/390] Fix --- .../pages/components/data-grid/group-pivot/group-pivot.md | 8 ++++---- .../src/services/tree-data-generator.ts | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md index 81aa39ac289b9..8be513f2e0dba 100644 --- a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md +++ b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md @@ -97,14 +97,14 @@ You can limit the sorting to the top level rows with the `disableChildrenSorting {{"demo": "pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js", "bg": "inline", "defaultCodeOpen": false}} -**Note**: If you are using `sortingMode="server"`, you need to always set the children of a row after this row. -For instance: +> If you are using `sortingMode="server"`, you need to always set the children of a row after this row. +> For instance: ```ts -// The row A.A is just after its parent +// The row A.A is immediately after its parent const validRows = [{ path: ['A'] }, { path: ['A', 'A'] }, { path: ['B'] }]; -// The row A.A is not just after its parent +// The row A.A is not immediately after its parent const invalidRows = [{ path: ['A'] }, { path: ['B'] }, { path: ['A', 'A'] }]; ``` diff --git a/packages/grid/x-data-grid-generator/src/services/tree-data-generator.ts b/packages/grid/x-data-grid-generator/src/services/tree-data-generator.ts index e63081bb6f742..ae09f606fd2df 100644 --- a/packages/grid/x-data-grid-generator/src/services/tree-data-generator.ts +++ b/packages/grid/x-data-grid-generator/src/services/tree-data-generator.ts @@ -42,6 +42,11 @@ export const addTreeDataOptionsToDemoData = ( return data; } + if (data.rows.length > 1000) { + // eslint-disable-next-line no-console + throw new Error('MUI: useDemoData tree data mode only works up to 1000 rows.'); + } + const rowsByTreeDepth: Record< number, { rows: { [index: number]: RowWithParentIndex }; rowIndexes: number[] } From 74f105d7c957e4bd92784bed978b7f8eb74da1eb Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 23 Nov 2021 08:55:32 +0100 Subject: [PATCH 387/390] Fix --- .../x-data-grid-generator/src/services/tree-data-generator.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/grid/x-data-grid-generator/src/services/tree-data-generator.ts b/packages/grid/x-data-grid-generator/src/services/tree-data-generator.ts index ae09f606fd2df..eb19ab2defdc3 100644 --- a/packages/grid/x-data-grid-generator/src/services/tree-data-generator.ts +++ b/packages/grid/x-data-grid-generator/src/services/tree-data-generator.ts @@ -43,7 +43,6 @@ export const addTreeDataOptionsToDemoData = ( } if (data.rows.length > 1000) { - // eslint-disable-next-line no-console throw new Error('MUI: useDemoData tree data mode only works up to 1000 rows.'); } From f8cb6e13b8f9d47a4fd721121307837c99c253a3 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 23 Nov 2021 15:51:39 +0100 Subject: [PATCH 388/390] Code review --- .../DisableChildrenSortingTreeData.js | 7 ++++++- .../DisableChildrenSortingTreeData.tsx | 7 ++++++- .../data-grid/group-pivot/group-pivot.md | 16 ++++++++-------- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js index 6c61742e0b2f2..19e5fadc359d8 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js +++ b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js @@ -4,6 +4,7 @@ import Stack from '@mui/material/Stack'; import Checkbox from '@mui/material/Checkbox'; import FormGroup from '@mui/material/FormGroup'; import FormControlLabel from '@mui/material/FormControlLabel'; +import Typography from '@mui/material/Typography'; const rows = [ { @@ -126,7 +127,11 @@ export default function DisableChildrenSortingTreeData() { onChange={(event) => setDisableChildrenSorting(event.target.checked)} /> } - label="Enable `disableChildrenSorting`" + label={ + + Enable disableChildrenSorting + + } />
diff --git a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.tsx b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.tsx index 191698fe25c1b..012a19e36a291 100644 --- a/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.tsx +++ b/docs/src/pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.tsx @@ -9,6 +9,7 @@ import Stack from '@mui/material/Stack'; import Checkbox from '@mui/material/Checkbox'; import FormGroup from '@mui/material/FormGroup'; import FormControlLabel from '@mui/material/FormControlLabel'; +import Typography from '@mui/material/Typography'; const rows: GridRowsProp = [ { @@ -131,7 +132,11 @@ export default function DisableChildrenSortingTreeData() { onChange={(event) => setDisableChildrenSorting(event.target.checked)} /> } - label="Enable `disableChildrenSorting`" + label={ + + Enable disableChildrenSorting + + } />
diff --git a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md index 8be513f2e0dba..34e5cbd8d3842 100644 --- a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md +++ b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md @@ -99,14 +99,14 @@ You can limit the sorting to the top level rows with the `disableChildrenSorting > If you are using `sortingMode="server"`, you need to always set the children of a row after this row. > For instance: - -```ts -// The row A.A is immediately after its parent -const validRows = [{ path: ['A'] }, { path: ['A', 'A'] }, { path: ['B'] }]; - -// The row A.A is not immediately after its parent -const invalidRows = [{ path: ['A'] }, { path: ['B'] }, { path: ['A', 'A'] }]; -``` +> +> ```ts +> // ✅ The row A.A is immediately after its parent +> const validRows = [{ path: ['A'] }, { path: ['A', 'A'] }, { path: ['B'] }]; +> +> // ❌ The row A.A is not immediately after its parent +> const invalidRows = [{ path: ['A'] }, { path: ['B'] }, { path: ['A', 'A'] }]; +> ``` ### Full Example From 5e448d60563cede9e6f666c48865418a04c13f12 Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Wed, 24 Nov 2021 14:01:40 +0100 Subject: [PATCH 389/390] Update docs/src/pages/components/data-grid/group-pivot/group-pivot.md Co-authored-by: Matheus Wichman --- docs/src/pages/components/data-grid/group-pivot/group-pivot.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md index 34e5cbd8d3842..a5b351617af4f 100644 --- a/docs/src/pages/components/data-grid/group-pivot/group-pivot.md +++ b/docs/src/pages/components/data-grid/group-pivot/group-pivot.md @@ -97,7 +97,7 @@ You can limit the sorting to the top level rows with the `disableChildrenSorting {{"demo": "pages/components/data-grid/group-pivot/DisableChildrenSortingTreeData.js", "bg": "inline", "defaultCodeOpen": false}} -> If you are using `sortingMode="server"`, you need to always set the children of a row after this row. +> If you are using `sortingMode="server"`, you need to always put the children of a row after its parent. > For instance: > > ```ts From b304525468e4cc359339da9d6cfacde05865695f Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 24 Nov 2021 14:02:59 +0100 Subject: [PATCH 390/390] Fix --- packages/grid/_modules_/grid/locales/arSD.ts | 5 +++++ packages/grid/_modules_/grid/locales/bgBG.ts | 5 +++++ packages/grid/_modules_/grid/locales/deDE.ts | 5 +++++ packages/grid/_modules_/grid/locales/elGR.ts | 5 +++++ packages/grid/_modules_/grid/locales/esES.ts | 5 +++++ packages/grid/_modules_/grid/locales/faIR.ts | 5 +++++ packages/grid/_modules_/grid/locales/heIL.ts | 5 +++++ packages/grid/_modules_/grid/locales/itIT.ts | 5 +++++ packages/grid/_modules_/grid/locales/jaJP.ts | 5 +++++ packages/grid/_modules_/grid/locales/koKR.ts | 5 +++++ packages/grid/_modules_/grid/locales/nlNL.ts | 5 +++++ packages/grid/_modules_/grid/locales/plPL.ts | 5 +++++ packages/grid/_modules_/grid/locales/ptBR.ts | 5 +++++ packages/grid/_modules_/grid/locales/ruRU.ts | 5 +++++ packages/grid/_modules_/grid/locales/skSK.ts | 5 +++++ packages/grid/_modules_/grid/locales/trTR.ts | 5 +++++ packages/grid/_modules_/grid/locales/ukUA.ts | 5 +++++ packages/grid/_modules_/grid/locales/viVN.ts | 5 +++++ packages/grid/_modules_/grid/locales/zhCN.ts | 5 +++++ 19 files changed, 95 insertions(+) diff --git a/packages/grid/_modules_/grid/locales/arSD.ts b/packages/grid/_modules_/grid/locales/arSD.ts index 55d7df742d371..09c43e5689e81 100644 --- a/packages/grid/_modules_/grid/locales/arSD.ts +++ b/packages/grid/_modules_/grid/locales/arSD.ts @@ -104,6 +104,11 @@ const arSDGrid: Partial = { // Actions cell more text // actionsCellMore: 'more', + + // Tree Data + // treeDataGroupingHeaderName: 'Group', + // treeDataExpand: 'see children', + // treeDataCollapse: 'hide children', }; export const arSD: Localization = getGridLocalization(arSDGrid, arSDCore); diff --git a/packages/grid/_modules_/grid/locales/bgBG.ts b/packages/grid/_modules_/grid/locales/bgBG.ts index 55bceae5ae50d..2b39413529e79 100644 --- a/packages/grid/_modules_/grid/locales/bgBG.ts +++ b/packages/grid/_modules_/grid/locales/bgBG.ts @@ -103,6 +103,11 @@ const bgBGGrid: Partial = { // Actions cell more text // actionsCellMore: 'more', + + // Tree Data + // treeDataGroupingHeaderName: 'Group', + // treeDataExpand: 'see children', + // treeDataCollapse: 'hide children', }; export const bgBG: Localization = getGridLocalization(bgBGGrid, bgBGCore); diff --git a/packages/grid/_modules_/grid/locales/deDE.ts b/packages/grid/_modules_/grid/locales/deDE.ts index 8b45af35789cb..158a1ac5134c7 100644 --- a/packages/grid/_modules_/grid/locales/deDE.ts +++ b/packages/grid/_modules_/grid/locales/deDE.ts @@ -106,6 +106,11 @@ const deDEGrid: Partial = { // Actions cell more text actionsCellMore: 'Mehr', + + // Tree Data + // treeDataGroupingHeaderName: 'Group', + // treeDataExpand: 'see children', + // treeDataCollapse: 'hide children', }; export const deDE: Localization = getGridLocalization(deDEGrid, deDECore); diff --git a/packages/grid/_modules_/grid/locales/elGR.ts b/packages/grid/_modules_/grid/locales/elGR.ts index af385f89d9fcc..ed507d19be259 100644 --- a/packages/grid/_modules_/grid/locales/elGR.ts +++ b/packages/grid/_modules_/grid/locales/elGR.ts @@ -105,6 +105,11 @@ const elGRGrid: Partial = { // Actions cell more text actionsCellMore: 'περισσότερα', + + // Tree Data + // treeDataGroupingHeaderName: 'Group', + // treeDataExpand: 'see children', + // treeDataCollapse: 'hide children', }; export const elGR: Localization = getGridLocalization(elGRGrid); diff --git a/packages/grid/_modules_/grid/locales/esES.ts b/packages/grid/_modules_/grid/locales/esES.ts index 69d811608c112..7b33b4d46cca4 100644 --- a/packages/grid/_modules_/grid/locales/esES.ts +++ b/packages/grid/_modules_/grid/locales/esES.ts @@ -106,6 +106,11 @@ const esESGrid: Partial = { // Actions cell more text actionsCellMore: 'más', + + // Tree Data + // treeDataGroupingHeaderName: 'Group', + // treeDataExpand: 'see children', + // treeDataCollapse: 'hide children', }; export const esES: Localization = getGridLocalization(esESGrid, esESCore); diff --git a/packages/grid/_modules_/grid/locales/faIR.ts b/packages/grid/_modules_/grid/locales/faIR.ts index 1005881224771..e421cdd63c3f0 100644 --- a/packages/grid/_modules_/grid/locales/faIR.ts +++ b/packages/grid/_modules_/grid/locales/faIR.ts @@ -106,6 +106,11 @@ const faIRGrid: Partial = { // Actions cell more text actionsCellMore: 'بیشتر', + + // Tree Data + // treeDataGroupingHeaderName: 'Group', + // treeDataExpand: 'see children', + // treeDataCollapse: 'hide children', }; export const faIR: Localization = getGridLocalization(faIRGrid, faIRCore); diff --git a/packages/grid/_modules_/grid/locales/heIL.ts b/packages/grid/_modules_/grid/locales/heIL.ts index 2825a8a9aa01e..76afc80b4534e 100644 --- a/packages/grid/_modules_/grid/locales/heIL.ts +++ b/packages/grid/_modules_/grid/locales/heIL.ts @@ -103,6 +103,11 @@ const heILGrid: Partial = { // Actions cell more text actionsCellMore: 'עוד', + + // Tree Data + // treeDataGroupingHeaderName: 'Group', + // treeDataExpand: 'see children', + // treeDataCollapse: 'hide children', }; export const heIL: Localization = getGridLocalization(heILGrid, heILCore); diff --git a/packages/grid/_modules_/grid/locales/itIT.ts b/packages/grid/_modules_/grid/locales/itIT.ts index dea8533d14bdc..6cc035d374136 100644 --- a/packages/grid/_modules_/grid/locales/itIT.ts +++ b/packages/grid/_modules_/grid/locales/itIT.ts @@ -106,6 +106,11 @@ const itITGrid: Partial = { // Actions cell more text actionsCellMore: 'più', + + // Tree Data + // treeDataGroupingHeaderName: 'Group', + // treeDataExpand: 'see children', + // treeDataCollapse: 'hide children', }; export const itIT: Localization = getGridLocalization(itITGrid, itITCore); diff --git a/packages/grid/_modules_/grid/locales/jaJP.ts b/packages/grid/_modules_/grid/locales/jaJP.ts index 46c2a582bc3fa..c72050571cfbb 100644 --- a/packages/grid/_modules_/grid/locales/jaJP.ts +++ b/packages/grid/_modules_/grid/locales/jaJP.ts @@ -101,6 +101,11 @@ const jaJPGrid: Partial = { // Actions cell more text // actionsCellMore: 'more', + + // Tree Data + // treeDataGroupingHeaderName: 'Group', + // treeDataExpand: 'see children', + // treeDataCollapse: 'hide children', }; export const jaJP: Localization = getGridLocalization(jaJPGrid, jaJPCore); diff --git a/packages/grid/_modules_/grid/locales/koKR.ts b/packages/grid/_modules_/grid/locales/koKR.ts index 524dba7e7887b..47782be0c236b 100644 --- a/packages/grid/_modules_/grid/locales/koKR.ts +++ b/packages/grid/_modules_/grid/locales/koKR.ts @@ -101,6 +101,11 @@ const koKRGrid: Partial = { // Actions cell more text // actionsCellMore: 'more', + + // Tree Data + // treeDataGroupingHeaderName: 'Group', + // treeDataExpand: 'see children', + // treeDataCollapse: 'hide children', }; export const koKR: Localization = getGridLocalization(koKRGrid, koKRCore); diff --git a/packages/grid/_modules_/grid/locales/nlNL.ts b/packages/grid/_modules_/grid/locales/nlNL.ts index ef35c49a6719b..cd47878c4fd6a 100644 --- a/packages/grid/_modules_/grid/locales/nlNL.ts +++ b/packages/grid/_modules_/grid/locales/nlNL.ts @@ -105,6 +105,11 @@ const nlNLGrid: Partial = { // Actions cell more text // actionsCellMore: 'more', + + // Tree Data + // treeDataGroupingHeaderName: 'Group', + // treeDataExpand: 'see children', + // treeDataCollapse: 'hide children', }; export const nlNL: Localization = getGridLocalization(nlNLGrid, nlNLCore); diff --git a/packages/grid/_modules_/grid/locales/plPL.ts b/packages/grid/_modules_/grid/locales/plPL.ts index 74af2105332ce..03a4479b41384 100644 --- a/packages/grid/_modules_/grid/locales/plPL.ts +++ b/packages/grid/_modules_/grid/locales/plPL.ts @@ -101,6 +101,11 @@ const plPLGrid: Partial = { // Actions cell more text actionsCellMore: 'więcej', + + // Tree Data + // treeDataGroupingHeaderName: 'Group', + // treeDataExpand: 'see children', + // treeDataCollapse: 'hide children', }; export const plPL: Localization = getGridLocalization(plPLGrid, plPLCore); diff --git a/packages/grid/_modules_/grid/locales/ptBR.ts b/packages/grid/_modules_/grid/locales/ptBR.ts index e07bbf5d90f09..05207ba96ec54 100644 --- a/packages/grid/_modules_/grid/locales/ptBR.ts +++ b/packages/grid/_modules_/grid/locales/ptBR.ts @@ -106,6 +106,11 @@ const ptBRGrid: Partial = { // Actions cell more text actionsCellMore: 'mais', + + // Tree Data + // treeDataGroupingHeaderName: 'Group', + // treeDataExpand: 'see children', + // treeDataCollapse: 'hide children', }; export const ptBR: Localization = getGridLocalization(ptBRGrid, ptBRCore); diff --git a/packages/grid/_modules_/grid/locales/ruRU.ts b/packages/grid/_modules_/grid/locales/ruRU.ts index feae531068822..d15e1df63719b 100644 --- a/packages/grid/_modules_/grid/locales/ruRU.ts +++ b/packages/grid/_modules_/grid/locales/ruRU.ts @@ -133,6 +133,11 @@ const ruRUGrid: Partial = { // Actions cell more text // actionsCellMore: 'more', + + // Tree Data + // treeDataGroupingHeaderName: 'Group', + // treeDataExpand: 'see children', + // treeDataCollapse: 'hide children', }; export const ruRU: Localization = getGridLocalization(ruRUGrid, ruRUCore); diff --git a/packages/grid/_modules_/grid/locales/skSK.ts b/packages/grid/_modules_/grid/locales/skSK.ts index 2df9f63784110..43162c9638e45 100644 --- a/packages/grid/_modules_/grid/locales/skSK.ts +++ b/packages/grid/_modules_/grid/locales/skSK.ts @@ -130,6 +130,11 @@ const skSKGrid: Partial = { // Actions cell more text // actionsCellMore: 'more', + + // Tree Data + // treeDataGroupingHeaderName: 'Group', + // treeDataExpand: 'see children', + // treeDataCollapse: 'hide children', }; export const skSK: Localization = getGridLocalization(skSKGrid, skSKCore); diff --git a/packages/grid/_modules_/grid/locales/trTR.ts b/packages/grid/_modules_/grid/locales/trTR.ts index e22f347a27702..cd0c67e5b3878 100644 --- a/packages/grid/_modules_/grid/locales/trTR.ts +++ b/packages/grid/_modules_/grid/locales/trTR.ts @@ -101,6 +101,11 @@ const trTRGrid: Partial = { // Actions cell more text // actionsCellMore: 'more', + + // Tree Data + // treeDataGroupingHeaderName: 'Group', + // treeDataExpand: 'see children', + // treeDataCollapse: 'hide children', }; export const trTR: Localization = getGridLocalization(trTRGrid, trTRCore); diff --git a/packages/grid/_modules_/grid/locales/ukUA.ts b/packages/grid/_modules_/grid/locales/ukUA.ts index be75568304c4b..5f6b29d27c832 100644 --- a/packages/grid/_modules_/grid/locales/ukUA.ts +++ b/packages/grid/_modules_/grid/locales/ukUA.ts @@ -105,6 +105,11 @@ const ukUAGrid: Partial = { // Actions cell more text // actionsCellMore: 'more', + + // Tree Data + // treeDataGroupingHeaderName: 'Group', + // treeDataExpand: 'see children', + // treeDataCollapse: 'hide children', }; export const ukUA: Localization = getGridLocalization(ukUAGrid, ukUACore); diff --git a/packages/grid/_modules_/grid/locales/viVN.ts b/packages/grid/_modules_/grid/locales/viVN.ts index ca0ddc357388b..44170a824b61e 100644 --- a/packages/grid/_modules_/grid/locales/viVN.ts +++ b/packages/grid/_modules_/grid/locales/viVN.ts @@ -104,6 +104,11 @@ const viVNGrid: Partial = { // Actions cell more text actionsCellMore: 'Thêm', + + // Tree Data + // treeDataGroupingHeaderName: 'Group', + // treeDataExpand: 'see children', + // treeDataCollapse: 'hide children', }; export const viVN: Localization = getGridLocalization(viVNGrid, viVNCore); diff --git a/packages/grid/_modules_/grid/locales/zhCN.ts b/packages/grid/_modules_/grid/locales/zhCN.ts index d3f1e69ab702d..20fa2d1f681ea 100644 --- a/packages/grid/_modules_/grid/locales/zhCN.ts +++ b/packages/grid/_modules_/grid/locales/zhCN.ts @@ -102,6 +102,11 @@ const zhCNGrid: Partial = { // Actions cell more text // actionsCellMore: 'more', + + // Tree Data + // treeDataGroupingHeaderName: 'Group', + // treeDataExpand: 'see children', + // treeDataCollapse: 'hide children', }; export const zhCN: Localization = getGridLocalization(zhCNGrid, zhCNCore);