From 9ff83b34ebc5ec60cb6f048e772408a676540910 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 11 Jan 2022 14:27:45 +0100 Subject: [PATCH 01/26] [DataGrid] Add methods to important and export the state --- docs/pages/api-docs/data-grid/grid-api.md | 180 +++++++++--------- .../api-docs/data-grid/grid-filter-item.js | 2 +- .../api-docs/data-grid/grid-filter-model.js | 2 +- .../data-grid/grid-filter-operator.js | 2 +- .../preProcessing/gridPreProcessingApi.ts | 55 +++++- .../preProcessing/useGridPreProcessing.ts | 4 +- .../columnPinning/useGridColumnPinning.tsx | 66 +++++-- .../columnReorder/columnReorderInterfaces.ts | 7 + .../columnReorder/columnReorderState.ts | 3 - .../hooks/features/columnReorder/index.ts | 2 +- ...lumnsState.ts => gridColumnsInterfaces.ts} | 0 .../features/columns/gridColumnsUtils.ts | 2 +- .../grid/hooks/features/columns/index.ts | 2 +- .../hooks/features/columns/useGridColumns.ts | 2 +- .../hooks/features/filter/useGridFilter.ts | 49 ++++- .../filter/useGridRegisterFilteringMethod.ts | 14 +- .../hooks/features/pagination/useGridPage.ts | 48 +++++ .../features/pagination/useGridPageSize.ts | 52 +++++ .../useGridPreferencesPanel.ts | 50 +++++ .../hooks/features/scroll/useGridScroll.ts | 2 +- .../features/selection/useGridSelection.ts | 13 +- .../sorting/useGridRegisterSortingMethod.ts | 14 +- .../hooks/features/sorting/useGridSorting.ts | 49 ++++- .../GridStatePersistenceApi.ts | 24 +++ .../hooks/features/statePersistence/index.ts | 1 + .../useGridStatePersistence.ts | 47 +++++ .../features/treeData/useGridTreeData.tsx | 13 +- .../grid/hooks/utils/useGridStateInit.ts | 8 +- .../grid/_modules_/grid/models/api/gridApi.ts | 4 +- .../grid/_modules_/grid/models/gridState.ts | 12 +- .../src/useDataGridProComponent.tsx | 2 + .../x-data-grid/src/useDataGridComponent.tsx | 2 + scripts/x-data-grid-pro.exports.json | 4 +- scripts/x-data-grid.exports.json | 4 +- 34 files changed, 597 insertions(+), 144 deletions(-) create mode 100644 packages/grid/_modules_/grid/hooks/features/columnReorder/columnReorderInterfaces.ts delete mode 100644 packages/grid/_modules_/grid/hooks/features/columnReorder/columnReorderState.ts rename packages/grid/_modules_/grid/hooks/features/columns/{gridColumnsState.ts => gridColumnsInterfaces.ts} (100%) create mode 100644 packages/grid/_modules_/grid/hooks/features/statePersistence/GridStatePersistenceApi.ts create mode 100644 packages/grid/_modules_/grid/hooks/features/statePersistence/index.ts create mode 100644 packages/grid/_modules_/grid/hooks/features/statePersistence/useGridStatePersistence.ts diff --git a/docs/pages/api-docs/data-grid/grid-api.md b/docs/pages/api-docs/data-grid/grid-api.md index 92aec1a29d26..f52c1623f70d 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -10,92 +10,94 @@ 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?: MuiBaseEvent) => boolean \| Promise<boolean> | Updates the field at the given id with the value stored in the edit row model. | -| commitRowChange | (id: GridRowId, event?: MuiBaseEvent) => 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](/api/data-grid/grid-filter-item/). | -| exportDataAsCsv | (options?: GridCsvExportOptions) => void | Downloads and exports a CSV of the grid's data. | -| exportDataAsPrint | (options?: GridPrintExportOptions) => void | Print the grid's data. | -| forceUpdate | () => void | 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) => HTMLDivElement \| null | 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) => HTMLDivElement \| null | 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 GridTranslationKeys>(key: T) => GridLocaleText[T] | Returns the translation for the `key`. | -| getPinnedColumns | () => GridPinnedColumns | Returns which columns are pinned. | -| getRootDimensions | () => GridDimensions \| null | Returns the dimensions of the grid | -| getRow | (id: GridRowId) => GridRowModel \| null | Gets the row data with a given id. | -| getRowElement | (id: GridRowId) => HTMLDivElement \| null | 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, GridRowModel> | Gets the full set of rows as Map<GridRowId, GridRowModel>. | -| getRowNode | (id: GridRowId) => GridRowTreeNodeConfig \| null | 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. | -| getRowsCount | () => number | Gets the total number of rows in the grid. | -| getScrollPosition | () => GridScrollParams | Returns the current scroll position. | -| getSelectedRows | () => Map<GridRowId, GridRowModel> | Returns an array of the selected rows. | -| getSortedRowIds | () => GridRowId[] | Returns all row ids sorted according to the active sort model. | -| getSortedRows | () => GridRowModel[] | 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, GridRowModel> | 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. | -| isColumnPinned | (field: string) => GridPinnedPosition \| false | Returns which side a column is pinned to. | -| isRowSelected | (id: GridRowId) => boolean | Determines if a row is selected or not. | -| pinColumn | (field: string, side: GridPinnedPosition) => void | Pins a column to the left or right side of the grid. | -| publishEvent | GridEventPublisher | 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: { startId: GridRowId; endId: 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?: MuiBaseEvent) => 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?: MuiBaseEvent) => 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`. | -| setPinnedColumns | (pinnedColumns: GridPinnedColumns) => void | Changes the pinned columns. | -| 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: GridRowModel[]) => 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)) => boolean | 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 | <E extends GridEventsStr>(event: E, handler: GridEventListener<E>, options?: EventListenerOptions) => () => void | Registers a handler for an event. | -| toggleColumnMenu | (field: string) => void | Toggles the column menu under the `field` column. | -| unpinColumn | (field: string) => void | Unpins a 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](/api/data-grid/grid-filter-item/). | +| Name | Type | Description | +| :----------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| applySorting | () => void | Applies the current sort model to the rows. | +| commitCellChange | (params: GridCommitCellChangeParams, event?: MuiBaseEvent) => boolean \| Promise<boolean> | Updates the field at the given id with the value stored in the edit row model. | +| commitRowChange | (id: GridRowId, event?: MuiBaseEvent) => 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](/api/data-grid/grid-filter-item/). | +| exportDataAsCsv | (options?: GridCsvExportOptions) => void | Downloads and exports a CSV of the grid's data. | +| exportDataAsPrint | (options?: GridPrintExportOptions) => void | Print the grid's data. | +| exportState | () => GridInitialState | Generates a serializable object containing the exportable parts of the DataGrid state.
These values can then be passed to the `initialState` prop or injected using the `restoreState` method. | +| forceUpdate | () => void | 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) => HTMLDivElement \| null | 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) => HTMLDivElement \| null | 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 GridTranslationKeys>(key: T) => GridLocaleText[T] | Returns the translation for the `key`. | +| getPinnedColumns | () => GridPinnedColumns | Returns which columns are pinned. | +| getRootDimensions | () => GridDimensions \| null | Returns the dimensions of the grid | +| getRow | (id: GridRowId) => GridRowModel \| null | Gets the row data with a given id. | +| getRowElement | (id: GridRowId) => HTMLDivElement \| null | 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, GridRowModel> | Gets the full set of rows as Map<GridRowId, GridRowModel>. | +| getRowNode | (id: GridRowId) => GridRowTreeNodeConfig \| null | 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. | +| getRowsCount | () => number | Gets the total number of rows in the grid. | +| getScrollPosition | () => GridScrollParams | Returns the current scroll position. | +| getSelectedRows | () => Map<GridRowId, GridRowModel> | Returns an array of the selected rows. | +| getSortedRowIds | () => GridRowId[] | Returns all row ids sorted according to the active sort model. | +| getSortedRows | () => GridRowModel[] | 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, GridRowModel> | 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. | +| isColumnPinned | (field: string) => GridPinnedPosition \| false | Returns which side a column is pinned to. | +| isRowSelected | (id: GridRowId) => boolean | Determines if a row is selected or not. | +| pinColumn | (field: string, side: GridPinnedPosition) => void | Pins a column to the left or right side of the grid. | +| publishEvent | GridEventPublisher | Emits an event. | +| resize | () => void | Triggers a resize of the component and recalculation of width and height. | +| restoreState | (stateToRestore: GridInitialState) => void | Inject the given values into the state of the DataGrid. | +| 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: { startId: GridRowId; endId: 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?: MuiBaseEvent) => 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?: MuiBaseEvent) => 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`. | +| setPinnedColumns | (pinnedColumns: GridPinnedColumns) => void | Changes the pinned columns. | +| 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: GridRowModel[]) => 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)) => boolean | 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 | <E extends GridEventsStr>(event: E, handler: GridEventListener<E>, options?: EventListenerOptions) => () => void | Registers a handler for an event. | +| toggleColumnMenu | (field: string) => void | Toggles the column menu under the `field` column. | +| unpinColumn | (field: string) => void | Unpins a 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](/api/data-grid/grid-filter-item/). | diff --git a/docs/pages/api-docs/data-grid/grid-filter-item.js b/docs/pages/api-docs/data-grid/grid-filter-item.js index 96177a43fd67..5155144ea50e 100644 --- a/docs/pages/api-docs/data-grid/grid-filter-item.js +++ b/docs/pages/api-docs/data-grid/grid-filter-item.js @@ -1,5 +1,5 @@ import * as React from 'react'; -import MarkdownDocs from '@material-ui/monorepo/docs/src/modules/components/MarkdownDocs'; +import MarkdownDocs from '@mui/monorepo/docs/src/modules/components/MarkdownDocs'; import { demos, docs, demoComponents } from './grid-filter-item.md?@mui/markdown'; export default function Page() { diff --git a/docs/pages/api-docs/data-grid/grid-filter-model.js b/docs/pages/api-docs/data-grid/grid-filter-model.js index 35447b4d301a..61239d3ad426 100644 --- a/docs/pages/api-docs/data-grid/grid-filter-model.js +++ b/docs/pages/api-docs/data-grid/grid-filter-model.js @@ -1,5 +1,5 @@ import * as React from 'react'; -import MarkdownDocs from '@material-ui/monorepo/docs/src/modules/components/MarkdownDocs'; +import MarkdownDocs from '@mui/monorepo/docs/src/modules/components/MarkdownDocs'; import { demos, docs, demoComponents } from './grid-filter-model.md?@mui/markdown'; export default function Page() { diff --git a/docs/pages/api-docs/data-grid/grid-filter-operator.js b/docs/pages/api-docs/data-grid/grid-filter-operator.js index 83ab3cae12fa..2cbd12ae534e 100644 --- a/docs/pages/api-docs/data-grid/grid-filter-operator.js +++ b/docs/pages/api-docs/data-grid/grid-filter-operator.js @@ -1,5 +1,5 @@ import * as React from 'react'; -import MarkdownDocs from '@material-ui/monorepo/docs/src/modules/components/MarkdownDocs'; +import MarkdownDocs from '@mui/monorepo/docs/src/modules/components/MarkdownDocs'; import { demos, docs, demoComponents } from './grid-filter-operator.md?@mui/markdown'; export default function Page() { diff --git a/packages/grid/_modules_/grid/hooks/core/preProcessing/gridPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/core/preProcessing/gridPreProcessingApi.ts index c87869d11e6d..9f5c007ae623 100644 --- a/packages/grid/_modules_/grid/hooks/core/preProcessing/gridPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/core/preProcessing/gridPreProcessingApi.ts @@ -1,3 +1,18 @@ +import { + GridCellIndexCoordinates, + GridColDef, + GridPartialInitialState, + GridScrollParams, +} from '../../../models'; +import { + GridRestoreStatePreProcessingContext, + GridRestoreStatePreProcessingValue, +} from '../../features/statePersistence'; +import { GridFilteringMethodCollection } from '../../features/filter/gridFilterState'; +import { GridSortingMethodCollection } from '../../features/sorting/gridSortingState'; +import { GridCanBeReorderedPreProcessingContext } from '../../features/columnReorder/columnReorderInterfaces'; +import { GridColumnsRawState } from '../../features/columns/gridColumnsInterfaces'; + export type PreProcessorCallback = (value: any, params?: any) => any; export enum GridPreProcessingGroup { @@ -7,8 +22,46 @@ export enum GridPreProcessingGroup { canBeReordered = 'canBeReordered', filteringMethod = 'filteringMethod', sortingMethod = 'sortingMethod', + exportState = 'exportState', + restoreState = 'restoreState', +} + +interface GridPreProcessingGroupLookup { + [GridPreProcessingGroup.hydrateColumns]: { value: GridColumnsRawState }; + [GridPreProcessingGroup.scrollToIndexes]: { + value: Partial; + context: Partial; + }; + [GridPreProcessingGroup.columnMenu]: { value: JSX.Element[]; context: GridColDef }; + [GridPreProcessingGroup.canBeReordered]: { + value: boolean; + context: GridCanBeReorderedPreProcessingContext; + }; + [GridPreProcessingGroup.filteringMethod]: { value: GridFilteringMethodCollection }; + [GridPreProcessingGroup.sortingMethod]: { value: GridSortingMethodCollection }; + [GridPreProcessingGroup.exportState]: { value: GridPartialInitialState }; + [GridPreProcessingGroup.restoreState]: { + value: GridRestoreStatePreProcessingValue; + context: GridRestoreStatePreProcessingContext; + }; } +export type GridPreProcessor

= ( + value: GridPreProcessingGroupLookup[P]['value'], + context: GridPreProcessingGroupLookup[P] extends { context: any } + ? GridPreProcessingGroupLookup[P]['context'] + : undefined, +) => GridPreProcessingGroupLookup[P]['value']; + +type GridPreProcessorsApplierArg< + P extends GridPreProcessingGroup, + T extends { value: any }, +> = T extends { context: any } ? [P, T['value'], T['context']] : [P, T['value']]; + +type GridPreProcessorsApplier =

( + ...params: GridPreProcessorsApplierArg +) => GridPreProcessingGroupLookup[P]['value']; + export interface GridPreProcessingApi { /** * Register a pre-processor and emit an event to notify the agents to re-apply the pre-processors. @@ -31,5 +84,5 @@ export interface GridPreProcessingApi { * @returns {any} The value after passing through all pre-processors. * @ignore - do not document. */ - unstable_applyPreProcessors: (group: GridPreProcessingGroup, value: any, params?: any) => any; + unstable_applyPreProcessors: GridPreProcessorsApplier; } diff --git a/packages/grid/_modules_/grid/hooks/core/preProcessing/useGridPreProcessing.ts b/packages/grid/_modules_/grid/hooks/core/preProcessing/useGridPreProcessing.ts index 4fa049034d09..bea5fc9e63a7 100644 --- a/packages/grid/_modules_/grid/hooks/core/preProcessing/useGridPreProcessing.ts +++ b/packages/grid/_modules_/grid/hooks/core/preProcessing/useGridPreProcessing.ts @@ -34,14 +34,14 @@ export const useGridPreProcessing = (apiRef: GridApiRef) => { ); const applyPreProcessors = React.useCallback( - (group, value, params) => { + (group, value, ...params) => { if (!preProcessorsRef.current[group]) { return value; } const preProcessors = Object.values(preProcessorsRef.current[group]!); return preProcessors.reduce((acc, preProcessor) => { - return preProcessor(acc, params); + return preProcessor(acc, ...params); }, value); }, [], diff --git a/packages/grid/_modules_/grid/hooks/features/columnPinning/useGridColumnPinning.tsx b/packages/grid/_modules_/grid/hooks/features/columnPinning/useGridColumnPinning.tsx index 9fc40a120bb7..5def0bb37d08 100644 --- a/packages/grid/_modules_/grid/hooks/features/columnPinning/useGridColumnPinning.tsx +++ b/packages/grid/_modules_/grid/hooks/features/columnPinning/useGridColumnPinning.tsx @@ -7,8 +7,7 @@ import { gridColumnsMetaSelector, gridVisibleColumnFieldsSelector, } from '../columns/gridColumnsSelector'; -import { GridCellIndexCoordinates } from '../../../models/gridCell'; -import { GridPreProcessingGroup } from '../../core/preProcessing'; +import { GridPreProcessingGroup, GridPreProcessor } from '../../core/preProcessing'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { GridEvents } from '../../../models/events'; import { gridClasses } from '../../../gridClasses'; @@ -22,8 +21,6 @@ import { useGridSelector } from '../../utils/useGridSelector'; import { filterColumns } from '../../../../../x-data-grid-pro/src/DataGridProVirtualScroller'; import { GridRowParams } from '../../../models/params/gridRowParams'; import { MuiEvent } from '../../../models/muiEvent'; -import { GridColumnsRawState } from '../columns/gridColumnsState'; -import { GridColDef } from '../../../models/colDef/gridColDef'; const Divider = () => event.stopPropagation()} />; @@ -96,8 +93,13 @@ export const useGridColumnPinning = ( useGridApiEventHandler(apiRef, GridEvents.rowMouseEnter, handleMouseEnter); useGridApiEventHandler(apiRef, GridEvents.rowMouseLeave, handleMouseLeave); - const calculateScrollLeft = React.useCallback( - (initialValue, params: Partial) => { + /** + * PRE-PROCESSING + */ + const calculateScrollLeft = React.useCallback< + GridPreProcessor + >( + (initialValue, params) => { if (props.disableColumnPinning) { return initialValue; } @@ -138,8 +140,10 @@ export const useGridColumnPinning = ( [apiRef, pinnedColumns, props.disableColumnPinning], ); - const addColumnMenuButtons = React.useCallback( - (initialValue: JSX.Element[], column: GridColDef) => { + const addColumnMenuButtons = React.useCallback< + GridPreProcessor + >( + (initialValue, column) => { if (props.disableColumnPinning) { return initialValue; } @@ -153,8 +157,10 @@ export const useGridColumnPinning = ( [props.disableColumnPinning], ); - const reorderPinnedColumns = React.useCallback( - (columnsState: GridColumnsRawState) => { + const reorderPinnedColumns = React.useCallback< + GridPreProcessor + >( + (columnsState) => { if (columnsState.all.length === 0 || props.disableColumnPinning) { return columnsState; } @@ -180,8 +186,10 @@ export const useGridColumnPinning = ( [pinnedColumns, props.disableColumnPinning], ); - const checkIfCanBeReordered = React.useCallback( - (initialValue, { targetIndex }: { targetIndex: number }) => { + const checkIfCanBeReordered = React.useCallback< + GridPreProcessor + >( + (initialValue, { targetIndex }) => { const visibleColumnFields = gridVisibleColumnFieldsSelector(apiRef.current.state); const [leftPinnedColumns, rightPinnedColumns] = filterColumns( pinnedColumns, @@ -207,10 +215,44 @@ export const useGridColumnPinning = ( [apiRef, pinnedColumns], ); + const stateExportPreProcessing = React.useCallback< + GridPreProcessor + >( + (prevState) => { + return { + ...prevState, + pinnedColumns: gridPinnedColumnsSelector(apiRef.current.state), + }; + }, + [apiRef], + ); + + const stateRestorePreProcessing = React.useCallback< + GridPreProcessor + >((params, context) => { + if (context.stateToRestore.pinnedColumns == null) { + return params; + } + + return { + ...params, + state: { + ...params.state, + pinnedColumns: context.stateToRestore.pinnedColumns, + }, + }; + }, []); + useGridRegisterPreProcessor(apiRef, GridPreProcessingGroup.scrollToIndexes, calculateScrollLeft); useGridRegisterPreProcessor(apiRef, GridPreProcessingGroup.columnMenu, addColumnMenuButtons); useGridRegisterPreProcessor(apiRef, GridPreProcessingGroup.hydrateColumns, reorderPinnedColumns); useGridRegisterPreProcessor(apiRef, GridPreProcessingGroup.canBeReordered, checkIfCanBeReordered); + useGridRegisterPreProcessor(apiRef, GridPreProcessingGroup.exportState, stateExportPreProcessing); + useGridRegisterPreProcessor( + apiRef, + GridPreProcessingGroup.restoreState, + stateRestorePreProcessing, + ); apiRef.current.unstable_updateControlState({ stateId: 'pinnedColumns', diff --git a/packages/grid/_modules_/grid/hooks/features/columnReorder/columnReorderInterfaces.ts b/packages/grid/_modules_/grid/hooks/features/columnReorder/columnReorderInterfaces.ts new file mode 100644 index 000000000000..4fbd3f4d953c --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/features/columnReorder/columnReorderInterfaces.ts @@ -0,0 +1,7 @@ +export interface GridColumnReorderState { + dragCol: string; +} + +export interface GridCanBeReorderedPreProcessingContext { + targetIndex: number; +} diff --git a/packages/grid/_modules_/grid/hooks/features/columnReorder/columnReorderState.ts b/packages/grid/_modules_/grid/hooks/features/columnReorder/columnReorderState.ts deleted file mode 100644 index b08d7f315276..000000000000 --- a/packages/grid/_modules_/grid/hooks/features/columnReorder/columnReorderState.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface GridColumnReorderState { - dragCol: string; -} diff --git a/packages/grid/_modules_/grid/hooks/features/columnReorder/index.ts b/packages/grid/_modules_/grid/hooks/features/columnReorder/index.ts index cad03e375c20..3b036758a677 100644 --- a/packages/grid/_modules_/grid/hooks/features/columnReorder/index.ts +++ b/packages/grid/_modules_/grid/hooks/features/columnReorder/index.ts @@ -1,2 +1,2 @@ export * from './columnReorderSelector'; -export * from './columnReorderState'; +export type { GridColumnReorderState } from './columnReorderInterfaces'; diff --git a/packages/grid/_modules_/grid/hooks/features/columns/gridColumnsState.ts b/packages/grid/_modules_/grid/hooks/features/columns/gridColumnsInterfaces.ts similarity index 100% rename from packages/grid/_modules_/grid/hooks/features/columns/gridColumnsState.ts rename to packages/grid/_modules_/grid/hooks/features/columns/gridColumnsInterfaces.ts diff --git a/packages/grid/_modules_/grid/hooks/features/columns/gridColumnsUtils.ts b/packages/grid/_modules_/grid/hooks/features/columns/gridColumnsUtils.ts index 9b2b6eff38bb..7f55598f6a00 100644 --- a/packages/grid/_modules_/grid/hooks/features/columns/gridColumnsUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/columns/gridColumnsUtils.ts @@ -1,4 +1,4 @@ -import { GridColumnLookup, GridColumnsState, GridColumnsRawState } from './gridColumnsState'; +import { GridColumnLookup, GridColumnsState, GridColumnsRawState } from './gridColumnsInterfaces'; import { DEFAULT_GRID_COL_TYPE_KEY, getGridDefaultColumnTypes, diff --git a/packages/grid/_modules_/grid/hooks/features/columns/index.ts b/packages/grid/_modules_/grid/hooks/features/columns/index.ts index 616484a999e3..dd879cf7b78e 100644 --- a/packages/grid/_modules_/grid/hooks/features/columns/index.ts +++ b/packages/grid/_modules_/grid/hooks/features/columns/index.ts @@ -1,3 +1,3 @@ export * from './gridColumnsSelector'; -export type { GridColumnLookup, GridColumnsState } from './gridColumnsState'; +export type { GridColumnLookup, GridColumnsState } from './gridColumnsInterfaces'; export { getGridColDef } from './gridColumnsUtils'; diff --git a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts index 6fa34037138e..61e5bc089629 100644 --- a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts +++ b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts @@ -21,7 +21,7 @@ import { DataGridProcessedProps } from '../../../models/props/DataGridProps'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { GridColumnVisibilityChangeParams } from '../../../models'; import { GridPreProcessingGroup } from '../../core/preProcessing'; -import { GridColumnsState } from './gridColumnsState'; +import { GridColumnsState } from './gridColumnsInterfaces'; import { hydrateColumnsWidth, computeColumnTypes, createColumnsState } from './gridColumnsUtils'; /** diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 4af2ad267eb6..b309c5782e36 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -21,7 +21,11 @@ import { gridFilterModelSelector, gridVisibleSortedRowEntriesSelector } from './ import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; import { gridRowIdsSelector, gridRowGroupingNameSelector } from '../rows'; -import { GridPreProcessingGroup } from '../../core/preProcessing'; +import { + GridPreProcessingGroup, + GridPreProcessor, + useGridRegisterPreProcessor, +} from '../../core/preProcessing'; import { useGridRegisterFilteringMethod } from './useGridRegisterFilteringMethod'; import { buildAggregatedFilterApplier, cleanFilterItem } from './gridFilterUtils'; @@ -243,6 +247,43 @@ export const useGridFilter = ( /** * PRE-PROCESSING */ + const stateExportPreProcessing = React.useCallback< + GridPreProcessor + >( + (prevState) => { + return { + ...prevState, + filter: { + filterModel: gridFilterModelSelector(apiRef.current.state), + }, + }; + }, + [apiRef], + ); + + const stateRestorePreProcessing = React.useCallback< + GridPreProcessor + >( + (params, context) => { + if (context.stateToRestore.filter?.filterModel == null) { + return params; + } + + return { + ...params, + callbacks: [...params.callbacks, apiRef.current.unstable_applyFilters], + state: { + ...params.state, + filter: { + ...params.state.filter, + filterModel: context.stateToRestore.filter.filterModel, + }, + }, + }; + }, + [apiRef], + ); + const flatFilteringMethod = React.useCallback( (params) => { if (props.filterMode === GridFeatureModeConstant.client && params.isRowMatchingFilters) { @@ -266,6 +307,12 @@ export const useGridFilter = ( [apiRef, props.filterMode], ); + useGridRegisterPreProcessor(apiRef, GridPreProcessingGroup.exportState, stateExportPreProcessing); + useGridRegisterPreProcessor( + apiRef, + GridPreProcessingGroup.restoreState, + stateRestorePreProcessing, + ); useGridRegisterFilteringMethod(apiRef, 'none', flatFilteringMethod); /** diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridRegisterFilteringMethod.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridRegisterFilteringMethod.ts index 3452e20aad5d..cb182cc4b2f5 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridRegisterFilteringMethod.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridRegisterFilteringMethod.ts @@ -1,6 +1,10 @@ import * as React from 'react'; -import { GridFilteringMethod, GridFilteringMethodCollection } from './gridFilterState'; -import { GridPreProcessingGroup, useGridRegisterPreProcessor } from '../../core/preProcessing'; +import { GridFilteringMethod } from './gridFilterState'; +import { + GridPreProcessingGroup, + GridPreProcessor, + useGridRegisterPreProcessor, +} from '../../core/preProcessing'; import { GridApiRef } from '../../../models'; export const useGridRegisterFilteringMethod = ( @@ -8,8 +12,10 @@ export const useGridRegisterFilteringMethod = ( groupingName: string, filteringMethod: GridFilteringMethod, ) => { - const updateRegistration = React.useCallback( - (filteringMethodCollection: GridFilteringMethodCollection) => { + const updateRegistration = React.useCallback< + GridPreProcessor + >( + (filteringMethodCollection) => { filteringMethodCollection[groupingName] = filteringMethod; return filteringMethodCollection; }, diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts index aeef97fbe6da..f93becdb0739 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts @@ -12,6 +12,11 @@ import { GridPageApi, GridPaginationState } from './gridPaginationInterfaces'; import { gridVisibleTopLevelRowCountSelector } from '../filter'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { gridPageSelector } from './gridPaginationSelector'; +import { + GridPreProcessingGroup, + GridPreProcessor, + useGridRegisterPreProcessor, +} from '../../core/preProcessing'; const getPageCount = (rowCount: number, pageSize: number): number => { if (pageSize > 0 && rowCount > 0) { @@ -87,6 +92,49 @@ export const useGridPage = ( useGridApiMethod(apiRef, pageApi, 'GridPageApi'); + /** + * PRE-PROCESSING + */ + const stateExportPreProcessing = React.useCallback< + GridPreProcessor + >( + (prevState) => { + return { + ...prevState, + pagination: { + ...prevState.pagination, + page: gridPageSelector(apiRef.current.state), + }, + }; + }, + [apiRef], + ); + + const stateRestorePreProcessing = React.useCallback< + GridPreProcessor + >((params, context) => { + // We apply the constraint even if the page did not change in case the pageSize changed. + const paginationState = applyValidPage({ + ...params.state.pagination, + page: context.stateToRestore.pagination?.page ?? gridPageSelector(params.state), + }); + + return { + ...params, + state: { + ...params.state, + pagination: paginationState, + }, + }; + }, []); + + useGridRegisterPreProcessor(apiRef, GridPreProcessingGroup.exportState, stateExportPreProcessing); + useGridRegisterPreProcessor( + apiRef, + GridPreProcessingGroup.restoreState, + stateRestorePreProcessing, + ); + /** * EVENTS */ diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts index 482eaee4b357..d7af62f0ce59 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts @@ -12,6 +12,11 @@ import { import { useGridStateInit } from '../../utils/useGridStateInit'; import { gridPageSizeSelector } from './gridPaginationSelector'; import { gridDensityRowHeightSelector } from '../density'; +import { + GridPreProcessingGroup, + GridPreProcessor, + useGridRegisterPreProcessor, +} from '../../core/preProcessing'; /** * @requires useGridDimensions (event) - can be after @@ -84,6 +89,53 @@ export const useGridPageSize = ( useGridApiMethod(apiRef, pageSizeApi, 'GridPageSizeApi'); + /** + * PRE-PROCESSING + */ + const stateExportPreProcessing = React.useCallback< + GridPreProcessor + >( + (prevState) => { + return { + ...prevState, + pagination: { + ...prevState.pagination, + pageSize: gridPageSizeSelector(apiRef.current.state), + }, + }; + }, + [apiRef], + ); + + /** + * TODO: Add error if `prop.autoHeight = true` + */ + const stateRestorePreProcessing = React.useCallback< + GridPreProcessor + >((params, context) => { + if (context.stateToRestore.pagination?.pageSize == null) { + return params; + } + + return { + ...params, + state: { + ...params.state, + pagination: { + ...params.state.pagination, + pageSize: context.stateToRestore.pagination.pageSize, + }, + }, + }; + }, []); + + useGridRegisterPreProcessor(apiRef, GridPreProcessingGroup.exportState, stateExportPreProcessing); + useGridRegisterPreProcessor( + apiRef, + GridPreProcessingGroup.restoreState, + stateRestorePreProcessing, + ); + /** * EVENTS */ diff --git a/packages/grid/_modules_/grid/hooks/features/preferencesPanel/useGridPreferencesPanel.ts b/packages/grid/_modules_/grid/hooks/features/preferencesPanel/useGridPreferencesPanel.ts index 779aa211b236..7521d032976c 100644 --- a/packages/grid/_modules_/grid/hooks/features/preferencesPanel/useGridPreferencesPanel.ts +++ b/packages/grid/_modules_/grid/hooks/features/preferencesPanel/useGridPreferencesPanel.ts @@ -5,6 +5,12 @@ import { useGridLogger } from '../../utils/useGridLogger'; import { GridPreferencePanelsValue } from './gridPreferencePanelsValue'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { DataGridProcessedProps } from '../../../models/props/DataGridProps'; +import { + GridPreProcessingGroup, + GridPreProcessor, + useGridRegisterPreProcessor, +} from '../../core/preProcessing'; +import { gridPreferencePanelStateSelector } from './gridPreferencePanelSelector'; export const useGridPreferencesPanel = ( apiRef: GridApiRef, @@ -19,6 +25,9 @@ export const useGridPreferencesPanel = ( const hideTimeout = React.useRef(); const immediateTimeout = React.useRef(); + /** + * API METHODS + */ const hidePreferences = React.useCallback(() => { logger.debug('Hiding Preferences Panel'); apiRef.current.setState((state) => ({ ...state, preferencePanel: { open: false } })); @@ -59,6 +68,47 @@ export const useGridPreferencesPanel = ( 'ColumnMenuApi', ); + /** + * PRE-PROCESSING + */ + const stateExportPreProcessing = React.useCallback< + GridPreProcessor + >( + (prevState) => { + return { + ...prevState, + preferencePanel: gridPreferencePanelStateSelector(apiRef.current.state), + }; + }, + [apiRef], + ); + + const stateRestorePreProcessing = React.useCallback< + GridPreProcessor + >((params, context) => { + if (context.stateToRestore.preferencePanel == null) { + return params; + } + + return { + ...params, + state: { + ...params.state, + preferencePanel: context.stateToRestore.preferencePanel, + }, + }; + }, []); + + useGridRegisterPreProcessor(apiRef, GridPreProcessingGroup.exportState, stateExportPreProcessing); + useGridRegisterPreProcessor( + apiRef, + GridPreProcessingGroup.restoreState, + stateRestorePreProcessing, + ); + + /** + * EFFECTS + */ React.useEffect(() => { return () => { clearTimeout(hideTimeout.current); diff --git a/packages/grid/_modules_/grid/hooks/features/scroll/useGridScroll.ts b/packages/grid/_modules_/grid/hooks/features/scroll/useGridScroll.ts index bc3724f9c665..1fafc001db9c 100644 --- a/packages/grid/_modules_/grid/hooks/features/scroll/useGridScroll.ts +++ b/packages/grid/_modules_/grid/hooks/features/scroll/useGridScroll.ts @@ -61,7 +61,7 @@ export const useGridScroll = ( logger.debug(`Scrolling to cell at row ${params.rowIndex}, col: ${params.colIndex} `); - let scrollCoordinates: any = {}; + let scrollCoordinates: Partial = {}; if (params.colIndex != null) { scrollCoordinates.left = scrollIntoView({ diff --git a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts index 4cf5b038cfd1..531933edbb53 100644 --- a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts +++ b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts @@ -20,9 +20,12 @@ import { gridVisibleSortedRowIdsSelector } from '../filter/gridFilterSelector'; import { GRID_CHECKBOX_SELECTION_COL_DEF, GridColDef } from '../../../models'; import { getDataGridUtilityClass, gridClasses } from '../../../gridClasses'; import { useGridStateInit } from '../../utils/useGridStateInit'; -import { GridPreProcessingGroup, useGridRegisterPreProcessor } from '../../core/preProcessing'; +import { + GridPreProcessingGroup, + GridPreProcessor, + useGridRegisterPreProcessor, +} from '../../core/preProcessing'; import { GridCellModes } from '../../../models/gridEditRowModel'; -import { GridColumnsRawState } from '../columns/gridColumnsState'; import { isKeyboardEvent } from '../../../utils/keyboardUtils'; type OwnerState = { classes: DataGridProcessedProps['classes'] }; @@ -118,8 +121,10 @@ export const useGridSelection = ( /** * PRE-PROCESSING */ - const updateSelectionColumn = React.useCallback( - (columnsState: GridColumnsRawState) => { + const updateSelectionColumn = React.useCallback< + GridPreProcessor + >( + (columnsState) => { const selectionColumn: GridColDef = { ...GRID_CHECKBOX_SELECTION_COL_DEF, cellClassName: classes.cellCheckbox, diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridRegisterSortingMethod.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridRegisterSortingMethod.ts index b9fc61b1c919..b6f9a84da420 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridRegisterSortingMethod.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridRegisterSortingMethod.ts @@ -1,6 +1,10 @@ import * as React from 'react'; -import { GridSortingMethod, GridSortingMethodCollection } from './gridSortingState'; -import { GridPreProcessingGroup, useGridRegisterPreProcessor } from '../../core/preProcessing'; +import { GridSortingMethod } from './gridSortingState'; +import { + GridPreProcessingGroup, + GridPreProcessor, + useGridRegisterPreProcessor, +} from '../../core/preProcessing'; import { GridApiRef } from '../../../models'; export const useGridRegisterSortingMethod = ( @@ -8,8 +12,10 @@ export const useGridRegisterSortingMethod = ( groupingName: string, filteringMethod: GridSortingMethod, ) => { - const updateRegistration = React.useCallback( - (sortingMethodCollection: GridSortingMethodCollection) => { + const updateRegistration = React.useCallback< + GridPreProcessor + >( + (sortingMethodCollection) => { sortingMethodCollection[groupingName] = filteringMethod; return sortingMethodCollection; }, diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 0e6dab93bc05..af2804d79f98 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 { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; import { GridSortingMethod, GridSortingMethodCollection } from './gridSortingState'; import { buildAggregatedSortingApplier } from './gridSortingUtils'; -import { GridPreProcessingGroup } from '../../core/preProcessing'; +import { + GridPreProcessingGroup, + GridPreProcessor, + useGridRegisterPreProcessor, +} from '../../core/preProcessing'; import { useGridRegisterSortingMethod } from './useGridRegisterSortingMethod'; /** @@ -212,6 +216,43 @@ export const useGridSorting = ( /** * PRE-PROCESSING */ + const stateExportPreProcessing = React.useCallback< + GridPreProcessor + >( + (prevState) => { + return { + ...prevState, + sorting: { + sortModel: gridSortModelSelector(apiRef.current.state), + }, + }; + }, + [apiRef], + ); + + const stateRestorePreProcessing = React.useCallback< + GridPreProcessor + >( + (params, context) => { + if (context.stateToRestore.sorting?.sortModel == null) { + return params; + } + + return { + ...params, + callbacks: [...params.callbacks, apiRef.current.applySorting], + state: { + ...params.state, + sorting: { + ...params.state.sorting, + sortModel: context.stateToRestore.sorting.sortModel, + }, + }, + }; + }, + [apiRef], + ); + const flatSortingMethod = React.useCallback( (params) => { if (!params.sortRowList) { @@ -224,6 +265,12 @@ export const useGridSorting = ( [apiRef], ); + useGridRegisterPreProcessor(apiRef, GridPreProcessingGroup.exportState, stateExportPreProcessing); + useGridRegisterPreProcessor( + apiRef, + GridPreProcessingGroup.restoreState, + stateRestorePreProcessing, + ); useGridRegisterSortingMethod(apiRef, 'none', flatSortingMethod); /** diff --git a/packages/grid/_modules_/grid/hooks/features/statePersistence/GridStatePersistenceApi.ts b/packages/grid/_modules_/grid/hooks/features/statePersistence/GridStatePersistenceApi.ts new file mode 100644 index 000000000000..a3daade1dcdf --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/features/statePersistence/GridStatePersistenceApi.ts @@ -0,0 +1,24 @@ +import { GridInitialState, GridState } from '../../../models'; + +export interface GridStatePersistenceApi { + /** + * Generates a serializable object containing the exportable parts of the DataGrid state. + * These values can then be passed to the `initialState` prop or injected using the `restoreState` method. + * @returns {GridInitialState} The exported state. + */ + exportState: () => GridInitialState; + /** + * Inject the given values into the state of the DataGrid. + * @param {GridInitialState} stateToRestore The exported state to restore. + */ + restoreState: (stateToRestore: GridInitialState) => void; +} + +export interface GridRestoreStatePreProcessingValue { + state: GridState; + callbacks: (() => void)[]; +} + +export interface GridRestoreStatePreProcessingContext { + stateToRestore: GridInitialState; +} diff --git a/packages/grid/_modules_/grid/hooks/features/statePersistence/index.ts b/packages/grid/_modules_/grid/hooks/features/statePersistence/index.ts new file mode 100644 index 000000000000..799d23a98feb --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/features/statePersistence/index.ts @@ -0,0 +1 @@ +export * from './GridStatePersistenceApi'; diff --git a/packages/grid/_modules_/grid/hooks/features/statePersistence/useGridStatePersistence.ts b/packages/grid/_modules_/grid/hooks/features/statePersistence/useGridStatePersistence.ts new file mode 100644 index 000000000000..924df791dafd --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/features/statePersistence/useGridStatePersistence.ts @@ -0,0 +1,47 @@ +import * as React from 'react'; +import { GridApiRef, GridInitialState } from '../../../models'; +import { GridStatePersistenceApi } from './GridStatePersistenceApi'; +import { useGridApiMethod } from '../../utils'; +import { GridPreProcessingGroup } from '../../core/preProcessing'; + +export const useGridStatePersistence = (apiRef: GridApiRef) => { + const exportState = React.useCallback(() => { + const stateToExport = apiRef.current.unstable_applyPreProcessors( + GridPreProcessingGroup.exportState, + {}, + ); + + return stateToExport as GridInitialState; + }, [apiRef]); + + const restoreState = React.useCallback( + (stateToRestore) => { + const response = apiRef.current.unstable_applyPreProcessors( + GridPreProcessingGroup.restoreState, + { + state: apiRef.current.state, + callbacks: [], + }, + { + stateToRestore, + }, + ); + + apiRef.current.setState(response.state); + + response.callbacks.forEach((callback) => { + callback(); + }); + + apiRef.current.forceUpdate(); + }, + [apiRef], + ); + + const statePersistenceApi: GridStatePersistenceApi = { + exportState, + restoreState, + }; + + useGridApiMethod(apiRef, statePersistenceApi, 'GridStatePersistenceApi'); +}; diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.tsx b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.tsx index 97538c4a17ad..fa770b801444 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.tsx +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.tsx @@ -17,8 +17,11 @@ import { useFirstRender } from '../../utils/useFirstRender'; import { buildRowTree, BuildRowTreeGroupingCriteria } from '../../../utils/tree/buildRowTree'; import { GridRowGroupingPreProcessing } from '../../core/rowGroupsPerProcessing'; import { gridFilteredDescendantCountLookupSelector } from '../filter'; -import { GridPreProcessingGroup, useGridRegisterPreProcessor } from '../../core/preProcessing'; -import { GridColumnsRawState } from '../columns/gridColumnsState'; +import { + GridPreProcessingGroup, + GridPreProcessor, + useGridRegisterPreProcessor, +} from '../../core/preProcessing'; import { GridFilteringMethod } from '../filter/gridFilterState'; import { gridRowIdsSelector, gridRowTreeSelector } from '../rows'; import { useGridRegisterFilteringMethod } from '../filter/useGridRegisterFilteringMethod'; @@ -137,8 +140,10 @@ export const useGridTreeData = ( }; }, [apiRef, props.groupingColDef]); - const updateGroupingColumn = React.useCallback( - (columnsState: GridColumnsRawState) => { + const updateGroupingColumn = React.useCallback< + GridPreProcessor + >( + (columnsState) => { const groupingColDefField = GRID_TREE_DATA_GROUPING_COL_DEF_FORCED_PROPERTIES.field; const shouldHaveGroupingColumn = props.treeData; diff --git a/packages/grid/_modules_/grid/hooks/utils/useGridStateInit.ts b/packages/grid/_modules_/grid/hooks/utils/useGridStateInit.ts index 7a8362ae3c4e..1116b8fe64b8 100644 --- a/packages/grid/_modules_/grid/hooks/utils/useGridStateInit.ts +++ b/packages/grid/_modules_/grid/hooks/utils/useGridStateInit.ts @@ -1,14 +1,10 @@ import * as React from 'react'; import { GridApiRef } from '../../models/api/gridApiRef'; -import { GridState } from '../../models/gridState'; - -type DeepPartial = { - [P in keyof T]?: DeepPartial; -}; +import { GridState, GridPartialState } from '../../models/gridState'; export const useGridStateInit = ( apiRef: GridApiRef, - callback: (state: DeepPartial) => DeepPartial, + callback: (state: GridPartialState) => GridPartialState, ) => { const isInitialized = React.useRef(false); diff --git a/packages/grid/_modules_/grid/models/api/gridApi.ts b/packages/grid/_modules_/grid/models/api/gridApi.ts index 02013968489a..f5126ef3f2c3 100644 --- a/packages/grid/_modules_/grid/models/api/gridApi.ts +++ b/packages/grid/_modules_/grid/models/api/gridApi.ts @@ -23,6 +23,7 @@ import type { GridPreProcessingApi } from '../../hooks/core/preProcessing'; import type { GridRowGroupsPreProcessingApi } from '../../hooks/core/rowGroupsPerProcessing'; import type { GridDimensionsApi } from '../../hooks/features/dimensions'; import type { GridPaginationApi } from '../../hooks/features/pagination'; +import type { GridStatePersistenceApi } from '../../hooks/features/statePersistence'; /** * The full grid API. @@ -52,4 +53,5 @@ export interface GridApi GridLocaleTextApi, GridClipboardApi, GridScrollApi, - GridColumnPinningApi {} + GridColumnPinningApi, + GridStatePersistenceApi {} diff --git a/packages/grid/_modules_/grid/models/gridState.ts b/packages/grid/_modules_/grid/models/gridState.ts index b5c6ee205c15..fde26021b26b 100644 --- a/packages/grid/_modules_/grid/models/gridState.ts +++ b/packages/grid/_modules_/grid/models/gridState.ts @@ -1,7 +1,7 @@ -import type { GridColumnsState } from '../hooks/features/columns/gridColumnsState'; +import type { GridColumnsState } from '../hooks/features/columns/gridColumnsInterfaces'; import type { GridEditRowsModel } from './gridEditRowModel'; import type { GridColumnMenuState } from '../hooks/features/columnMenu/columnMenuState'; -import type { GridColumnReorderState } from '../hooks/features/columnReorder/columnReorderState'; +import type { GridColumnReorderState } from '../hooks/features/columnReorder'; import type { GridColumnResizeState } from '../hooks/features/columnResize/columnResizeState'; import type { GridDensityState } from '../hooks/features/density/densityState'; import type { GridFocusState, GridTabIndexState } from '../hooks/features/focus/gridFocusState'; @@ -48,3 +48,11 @@ export interface GridInitialState { preferencePanel?: GridPreferencePanelInitialState; pinnedColumns?: GridColumnPinningState; } + +type DeepPartial = { + [P in keyof T]?: DeepPartial; +}; + +export type GridPartialState = DeepPartial; + +export type GridPartialInitialState = DeepPartial; diff --git a/packages/grid/x-data-grid-pro/src/useDataGridProComponent.tsx b/packages/grid/x-data-grid-pro/src/useDataGridProComponent.tsx index ee1229138f6e..e5a93efe5947 100644 --- a/packages/grid/x-data-grid-pro/src/useDataGridProComponent.tsx +++ b/packages/grid/x-data-grid-pro/src/useDataGridProComponent.tsx @@ -29,6 +29,7 @@ import { useGridEvents } from '../../_modules_/grid/hooks/features/events/useGri import { useGridDimensions } from '../../_modules_/grid/hooks/features/dimensions/useGridDimensions'; import { useGridTreeData } from '../../_modules_/grid/hooks/features/treeData/useGridTreeData'; import { useGridColumnPinning } from '../../_modules_/grid/hooks/features/columnPinning/useGridColumnPinning'; +import { useGridStatePersistence } from '../../_modules_/grid/hooks/features/statePersistence/useGridStatePersistence'; export const useDataGridProComponent = (apiRef: GridApiRef, props: DataGridProProcessedProps) => { useGridInitialization(apiRef, props); @@ -58,4 +59,5 @@ export const useDataGridProComponent = (apiRef: GridApiRef, props: DataGridProPr useGridDimensions(apiRef, props); useGridColumnPinning(apiRef, props); useGridEvents(apiRef, props); + useGridStatePersistence(apiRef); }; diff --git a/packages/grid/x-data-grid/src/useDataGridComponent.tsx b/packages/grid/x-data-grid/src/useDataGridComponent.tsx index 22462e9fce7a..67f66b8170b1 100644 --- a/packages/grid/x-data-grid/src/useDataGridComponent.tsx +++ b/packages/grid/x-data-grid/src/useDataGridComponent.tsx @@ -24,6 +24,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 { useGridStatePersistence } from '../../_modules_/grid/hooks/features/statePersistence/useGridStatePersistence'; export const useDataGridComponent = (apiRef: GridApiRef, props: DataGridProcessedProps) => { useGridInitialization(apiRef, props); @@ -48,4 +49,5 @@ export const useDataGridComponent = (apiRef: GridApiRef, props: DataGridProcesse useGridClipboard(apiRef); useGridDimensions(apiRef, props); useGridEvents(apiRef, props); + useGridStatePersistence(apiRef); }; diff --git a/scripts/x-data-grid-pro.exports.json b/scripts/x-data-grid-pro.exports.json index 10337ba3bded..2e9484f391c4 100644 --- a/scripts/x-data-grid-pro.exports.json +++ b/scripts/x-data-grid-pro.exports.json @@ -112,7 +112,7 @@ { "name": "GridColumnPinningMenuItems", "kind": "ExportSpecifier" }, { "name": "gridColumnReorderDragColSelector", "kind": "Variable" }, { "name": "gridColumnReorderSelector", "kind": "Variable" }, - { "name": "GridColumnReorderState", "kind": "Interface" }, + { "name": "GridColumnReorderState", "kind": "ExportSpecifier" }, { "name": "GridColumnResizeParams", "kind": "Interface" }, { "name": "gridColumnResizeSelector", "kind": "Variable" }, { "name": "GridColumnResizeState", "kind": "Interface" }, @@ -249,6 +249,8 @@ { "name": "GridPanelProps", "kind": "Interface" }, { "name": "GridPanelWrapper", "kind": "Function" }, { "name": "GridParamsApi", "kind": "Interface" }, + { "name": "GridPartialInitialState", "kind": "TypeAlias" }, + { "name": "GridPartialState", "kind": "TypeAlias" }, { "name": "GridPinnedColumns", "kind": "Interface" }, { "name": "GridPinnedPosition", "kind": "Enum" }, { "name": "GridPreferencePanelInitialState", "kind": "TypeAlias" }, diff --git a/scripts/x-data-grid.exports.json b/scripts/x-data-grid.exports.json index 27eebfc40919..a5d0e662d0e7 100644 --- a/scripts/x-data-grid.exports.json +++ b/scripts/x-data-grid.exports.json @@ -112,7 +112,7 @@ { "name": "GridColumnPinningMenuItems", "kind": "ExportSpecifier" }, { "name": "gridColumnReorderDragColSelector", "kind": "Variable" }, { "name": "gridColumnReorderSelector", "kind": "Variable" }, - { "name": "GridColumnReorderState", "kind": "Interface" }, + { "name": "GridColumnReorderState", "kind": "ExportSpecifier" }, { "name": "GridColumnResizeParams", "kind": "Interface" }, { "name": "gridColumnResizeSelector", "kind": "Variable" }, { "name": "GridColumnResizeState", "kind": "Interface" }, @@ -249,6 +249,8 @@ { "name": "GridPanelProps", "kind": "Interface" }, { "name": "GridPanelWrapper", "kind": "Function" }, { "name": "GridParamsApi", "kind": "Interface" }, + { "name": "GridPartialInitialState", "kind": "TypeAlias" }, + { "name": "GridPartialState", "kind": "TypeAlias" }, { "name": "GridPinnedColumns", "kind": "Interface" }, { "name": "GridPinnedPosition", "kind": "Enum" }, { "name": "GridPreferencePanelInitialState", "kind": "TypeAlias" }, From c984c271c009838ccf06476f7406a5b0e3679b5e Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 11 Jan 2022 15:33:57 +0100 Subject: [PATCH 02/26] Add examples --- .../data-grid/state/RestoreStateApiRef.js | 152 ++++++++++++++++ .../data-grid/state/RestoreStateApiRef.tsx | 165 ++++++++++++++++++ .../state/RestoreStateInitialState.js | 87 +++++++++ .../state/RestoreStateInitialState.tsx | 93 ++++++++++ .../RestoreStateInitialState.tsx.preview | 1 + .../pages/components/data-grid/state/state.md | 13 +- 6 files changed, 508 insertions(+), 3 deletions(-) create mode 100644 docs/src/pages/components/data-grid/state/RestoreStateApiRef.js create mode 100644 docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx create mode 100644 docs/src/pages/components/data-grid/state/RestoreStateInitialState.js create mode 100644 docs/src/pages/components/data-grid/state/RestoreStateInitialState.tsx create mode 100644 docs/src/pages/components/data-grid/state/RestoreStateInitialState.tsx.preview diff --git a/docs/src/pages/components/data-grid/state/RestoreStateApiRef.js b/docs/src/pages/components/data-grid/state/RestoreStateApiRef.js new file mode 100644 index 000000000000..c3d839bfee7c --- /dev/null +++ b/docs/src/pages/components/data-grid/state/RestoreStateApiRef.js @@ -0,0 +1,152 @@ +import * as React from 'react'; +import { DataGridPro, useGridApiRef } from '@mui/x-data-grid-pro'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import Stack from '@mui/material/Stack'; +import IconButton from '@mui/material/IconButton'; +import TextField from '@mui/material/TextField'; +import Box from '@mui/material/Box'; +import Chip from '@mui/material/Chip'; +import AddIcon from '@mui/icons-material/Add'; +var DemoActionTypes; +(function (DemoActionTypes) { + DemoActionTypes[(DemoActionTypes['CreateView'] = 0)] = 'CreateView'; + DemoActionTypes[(DemoActionTypes['DeleteView'] = 1)] = 'DeleteView'; + DemoActionTypes[(DemoActionTypes['SetActiveView'] = 2)] = 'SetActiveView'; + DemoActionTypes[(DemoActionTypes['SetNewViewLabel'] = 3)] = 'SetNewViewLabel'; +})(DemoActionTypes || (DemoActionTypes = {})); + +const demoReducer = (state, action) => { + switch (action.type) { + case DemoActionTypes.CreateView: { + const id = Math.random().toString(); + + return { + ...state, + activeViewId: id, + newViewLabel: '', + views: { + ...state.views, + [id]: { label: state.newViewLabel, value: action.value }, + }, + }; + } + + case DemoActionTypes.DeleteView: { + const views = Object.fromEntries( + Object.entries(state.views).filter(([id]) => id !== action.id), + ); + + let activeViewId; + if (state.activeViewId !== action.id) { + activeViewId = state.activeViewId; + } else { + const viewIds = Object.keys(state.views); + + if (viewIds.length === 0) { + activeViewId = null; + } else { + activeViewId = viewIds[0]; + } + } + + return { + ...state, + views, + activeViewId, + }; + } + + case DemoActionTypes.SetActiveView: { + return { + ...state, + activeViewId: action.id, + }; + } + + case DemoActionTypes.SetNewViewLabel: { + return { + ...state, + newViewLabel: action.label, + }; + } + + default: { + return state; + } + } +}; + +const DEMO_INITIAL_STATE = { + views: {}, + newViewLabel: '', + activeViewId: null, +}; + +export default function RestoreStateApiRef() { + const apiRef = useGridApiRef(); + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 500, + }); + + const [state, dispatch] = React.useReducer(demoReducer, DEMO_INITIAL_STATE); + + const createNewView = () => { + dispatch({ + type: DemoActionTypes.CreateView, + value: apiRef.current.exportState(), + }); + }; + + const handleNewViewLabelChange = (e) => { + dispatch({ type: DemoActionTypes.SetNewViewLabel, label: e.target.value }); + }; + + const handleDeleteView = (viewId) => { + dispatch({ type: DemoActionTypes.DeleteView, id: viewId }); + }; + + const handleSetActiveView = (viewId) => { + apiRef.current.restoreState(state.views[viewId].value); + dispatch({ type: DemoActionTypes.SetActiveView, id: viewId }); + }; + + const isNewViewLabelValid = React.useMemo(() => { + if (state.newViewLabel.length === 0) { + return false; + } + + return Object.values(state.views).every( + (view) => view.label !== state.newViewLabel, + ); + }, [state.views, state.newViewLabel]); + + return ( + + + + + + + + + {Object.entries(state.views).map(([viewId, view]) => ( + handleDeleteView(viewId)} + variant={viewId === state.activeViewId ? 'filled' : 'outlined'} + onClick={() => handleSetActiveView(viewId)} + /> + ))} + + + + + + ); +} diff --git a/docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx b/docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx new file mode 100644 index 000000000000..d1c4c6a1b861 --- /dev/null +++ b/docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx @@ -0,0 +1,165 @@ +import * as React from 'react'; +import { DataGridPro, GridInitialState, useGridApiRef } from '@mui/x-data-grid-pro'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import Stack from '@mui/material/Stack'; +import IconButton from '@mui/material/IconButton'; +import TextField from '@mui/material/TextField'; +import Box from '@mui/material/Box'; +import Chip from '@mui/material/Chip'; +import AddIcon from '@mui/icons-material/Add'; + +interface DemoState { + views: { [id: string]: { label: string; value: GridInitialState } }; + newViewLabel: string; + activeViewId: string | null; +} + +enum DemoActionTypes { + CreateView, + DeleteView, + SetActiveView, + SetNewViewLabel, +} + +type DemoActions = + | { type: DemoActionTypes.CreateView; value: GridInitialState } + | { type: DemoActionTypes.DeleteView; id: string } + | { type: DemoActionTypes.SetNewViewLabel; label: string } + | { type: DemoActionTypes.SetActiveView; id: string | null }; + +const demoReducer: React.Reducer = (state, action) => { + switch (action.type) { + case DemoActionTypes.CreateView: { + const id = Math.random().toString(); + + return { + ...state, + activeViewId: id, + newViewLabel: '', + views: { + ...state.views, + [id]: { label: state.newViewLabel, value: action.value }, + }, + }; + } + + case DemoActionTypes.DeleteView: { + const views = Object.fromEntries( + Object.entries(state.views).filter(([id]) => id !== action.id), + ); + + let activeViewId: string | null; + if (state.activeViewId !== action.id) { + activeViewId = state.activeViewId; + } else { + const viewIds = Object.keys(state.views); + + if (viewIds.length === 0) { + activeViewId = null; + } else { + activeViewId = viewIds[0]; + } + } + + return { + ...state, + views, + activeViewId, + }; + } + + case DemoActionTypes.SetActiveView: { + return { + ...state, + activeViewId: action.id, + }; + } + + case DemoActionTypes.SetNewViewLabel: { + return { + ...state, + newViewLabel: action.label, + }; + } + + default: { + return state; + } + } +}; + +const DEMO_INITIAL_STATE: DemoState = { + views: {}, + newViewLabel: '', + activeViewId: null, +}; + +export default function RestoreStateApiRef() { + const apiRef = useGridApiRef(); + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 500, + }); + const [state, dispatch] = React.useReducer(demoReducer, DEMO_INITIAL_STATE); + + const createNewView = () => { + dispatch({ + type: DemoActionTypes.CreateView, + value: apiRef.current.exportState(), + }); + }; + + const handleNewViewLabelChange = ( + e: React.ChangeEvent, + ) => { + dispatch({ type: DemoActionTypes.SetNewViewLabel, label: e.target.value }); + }; + + const handleDeleteView = (viewId: string) => { + dispatch({ type: DemoActionTypes.DeleteView, id: viewId }); + }; + + const handleSetActiveView = (viewId: string) => { + apiRef.current.restoreState(state.views[viewId].value); + dispatch({ type: DemoActionTypes.SetActiveView, id: viewId }); + }; + + const isNewViewLabelValid = React.useMemo(() => { + if (state.newViewLabel.length === 0) { + return false; + } + + return Object.values(state.views).every( + (view) => view.label !== state.newViewLabel, + ); + }, [state.views, state.newViewLabel]); + + return ( + + + + + + + + + {Object.entries(state.views).map(([viewId, view]) => ( + handleDeleteView(viewId)} + variant={viewId === state.activeViewId ? 'filled' : 'outlined'} + onClick={() => handleSetActiveView(viewId)} + /> + ))} + + + + + + ); +} diff --git a/docs/src/pages/components/data-grid/state/RestoreStateInitialState.js b/docs/src/pages/components/data-grid/state/RestoreStateInitialState.js new file mode 100644 index 000000000000..0af30a461cda --- /dev/null +++ b/docs/src/pages/components/data-grid/state/RestoreStateInitialState.js @@ -0,0 +1,87 @@ +import Button from '@mui/material/Button'; +import PropTypes from 'prop-types'; +import Box from '@mui/material/Box'; +import { + DataGridPro, + GridToolbarContainer, + GridToolbarDensitySelector, + GridToolbarFilterButton, + useGridApiContext, + useGridRootProps, +} from '@mui/x-data-grid-pro'; +import * as React from 'react'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const GridCustomToolbar = ({ unMount }) => { + const rootProps = useGridRootProps(); + const apiRef = useGridApiContext(); + + return ( + + + + } + onClick={() => unMount(apiRef.current.exportState())} + {...rootProps.componentsProps?.baseButton} + > + Save state and unmount + + + ); +}; + +GridCustomToolbar.propTypes = { + unMount: PropTypes.func.isRequired, +}; + +const WrappedDataGridPro = (props) => { + const { unMount, ...other } = props; + + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 500, + }); + + if (loading) { + return null; + } + + return ( + + ); +}; + +WrappedDataGridPro.propTypes = { + unMount: PropTypes.func.isRequired, +}; + +export default function RestoreStateInitialState() { + const [savedState, setSavedState] = React.useState(undefined); + + const [isMounted, setIsMounted] = React.useState(true); + + const unMountGrid = React.useCallback((stateToState) => { + setIsMounted(false); + setSavedState(stateToState); + }, []); + + const restoreGrid = () => setIsMounted(true); + + if (isMounted) { + return ( + + + + ); + } + + return ; +} diff --git a/docs/src/pages/components/data-grid/state/RestoreStateInitialState.tsx b/docs/src/pages/components/data-grid/state/RestoreStateInitialState.tsx new file mode 100644 index 000000000000..1e269552684f --- /dev/null +++ b/docs/src/pages/components/data-grid/state/RestoreStateInitialState.tsx @@ -0,0 +1,93 @@ +import Button from '@mui/material/Button'; +import Box from '@mui/material/Box'; +import { + DataGridPro, + DataGridProProps, + GridInitialState, + GridToolbarContainer, + GridToolbarDensitySelector, + GridToolbarFilterButton, + useGridApiContext, + useGridRootProps, +} from '@mui/x-data-grid-pro'; +import * as React from 'react'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const GridCustomToolbar = ({ + unMount, +}: { + unMount: (stateToSave: GridInitialState) => void; +}) => { + const rootProps = useGridRootProps(); + const apiRef = useGridApiContext(); + + return ( + + + + } + onClick={() => unMount(apiRef.current.exportState())} + {...rootProps.componentsProps?.baseButton} + > + Save state and unmount + + + ); +}; + +interface WrappedDataGridProProps + extends Omit< + DataGridProProps, + 'columns' | 'rows' | 'loading' | 'apiRef' | 'components' | 'componentsProps' + > { + unMount: (stateToSave: GridInitialState) => void; +} + +const WrappedDataGridPro = (props: WrappedDataGridProProps) => { + const { unMount, ...other } = props; + + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 500, + }); + + if (loading) { + return null; + } + + return ( + + ); +}; + +export default function RestoreStateInitialState() { + const [savedState, setSavedState] = React.useState( + undefined, + ); + const [isMounted, setIsMounted] = React.useState(true); + + const unMountGrid = React.useCallback((stateToState: GridInitialState) => { + setIsMounted(false); + setSavedState(stateToState); + }, []); + + const restoreGrid = () => setIsMounted(true); + + if (isMounted) { + return ( + + + + ); + } + + return ; +} diff --git a/docs/src/pages/components/data-grid/state/RestoreStateInitialState.tsx.preview b/docs/src/pages/components/data-grid/state/RestoreStateInitialState.tsx.preview new file mode 100644 index 000000000000..03bd4701ede7 --- /dev/null +++ b/docs/src/pages/components/data-grid/state/RestoreStateInitialState.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/src/pages/components/data-grid/state/state.md b/docs/src/pages/components/data-grid/state/state.md index 98548ac1d0d1..21e221918fab 100644 --- a/docs/src/pages/components/data-grid/state/state.md +++ b/docs/src/pages/components/data-grid/state/state.md @@ -9,6 +9,7 @@ title: Data Grid - State ## Initialize the state Some state keys can be initialized with the `initialState` prop. +This prop has the same format as the returned value of `apiRef.current.exportState()`. > ⚠️ The `initialState` can only be used to set the initial value of the state, the grid will not react if you change the `initialState` value later on. > @@ -55,9 +56,15 @@ Some selectors are yet to be documented. ## Save and restore the state -> ⚠️ This feature isn't implemented yet. It's coming. -> -> 👍 Upvote [issue #820](https://github.com/mui-org/material-ui-x/issues/820) if you want to see it land faster. +You can export the current state of the grid by calling `apiRef.current.exportState()` and restore it by either passing it to the `initialState` prop or to the `apiRef.current.restoreState()` method. + +### Save and restore the state with `initialState` + +{{"demo": "pages/components/data-grid/state/RestoreStateInitialState.js", "bg": "inline"}} + +### Save and restore the state with `apiRef` [](https://mui.com/store/items/material-ui-pro/) + +{{"demo": "pages/components/data-grid/state/RestoreStateApiRef.js", "bg": "inline"}} ## API From ef0ac055586650a5623b196c0b70c2c9e8c674cc Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 11 Jan 2022 15:45:10 +0100 Subject: [PATCH 03/26] Work --- docs/src/pages/components/data-grid/filtering/filtering.md | 2 +- docs/src/pages/components/data-grid/state/state.md | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/src/pages/components/data-grid/filtering/filtering.md b/docs/src/pages/components/data-grid/filtering/filtering.md index 8f2425f5ef6d..bc39c2f6924c 100644 --- a/docs/src/pages/components/data-grid/filtering/filtering.md +++ b/docs/src/pages/components/data-grid/filtering/filtering.md @@ -147,7 +147,7 @@ You can get them by importing the following functions: | `number` | `getGridNumericOperators()` | | `boolean` | `getGridBooleanOperators()` | | `date` | `getGridDateOperators()` | -| `dateTime ` | `getGridDateOperators(true)` | +| `dateTime` | `getGridDateOperators(true)` | | `singleSelect` | `getGridSingleSelectOperators()` | You can find more information about the supported column types in the [columns section](/components/data-grid/columns/#column-types). diff --git a/docs/src/pages/components/data-grid/state/state.md b/docs/src/pages/components/data-grid/state/state.md index 21e221918fab..492f9f8e54a0 100644 --- a/docs/src/pages/components/data-grid/state/state.md +++ b/docs/src/pages/components/data-grid/state/state.md @@ -58,11 +58,13 @@ Some selectors are yet to be documented. You can export the current state of the grid by calling `apiRef.current.exportState()` and restore it by either passing it to the `initialState` prop or to the `apiRef.current.restoreState()` method. -### Save and restore the state with `initialState` +### Restore the state with `initialState` + +> ⚠️ If you restore the page using `initialState` before the data are fetched, the grid will automatically move to the 1st page. {{"demo": "pages/components/data-grid/state/RestoreStateInitialState.js", "bg": "inline"}} -### Save and restore the state with `apiRef` [](https://mui.com/store/items/material-ui-pro/) +### Restore the state with `apiRef` [](https://mui.com/store/items/material-ui-pro/) {{"demo": "pages/components/data-grid/state/RestoreStateApiRef.js", "bg": "inline"}} From 03d10d69c22d00b8236bdb805cc2a30be18592de Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 11 Jan 2022 15:49:27 +0100 Subject: [PATCH 04/26] Work --- .../features/statePersistence/GridStatePersistenceApi.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/grid/_modules_/grid/hooks/features/statePersistence/GridStatePersistenceApi.ts b/packages/grid/_modules_/grid/hooks/features/statePersistence/GridStatePersistenceApi.ts index a3daade1dcdf..081260f8c835 100644 --- a/packages/grid/_modules_/grid/hooks/features/statePersistence/GridStatePersistenceApi.ts +++ b/packages/grid/_modules_/grid/hooks/features/statePersistence/GridStatePersistenceApi.ts @@ -16,6 +16,11 @@ export interface GridStatePersistenceApi { export interface GridRestoreStatePreProcessingValue { state: GridState; + + /** + * Functions to run after the state has been updated but before re-rendering. + * This is usually used to apply derived states like `applyFilters` or `applySorting` + */ callbacks: (() => void)[]; } From efe89198d88714ba7db4b60520cdf6e2b0f914b4 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 11 Jan 2022 15:57:49 +0100 Subject: [PATCH 05/26] [core] Add typing to the pre-processors methods --- .../preProcessing/gridPreProcessingApi.ts | 43 ++++++++++++++++++- .../preProcessing/useGridPreProcessing.ts | 4 +- .../columnPinning/useGridColumnPinning.tsx | 32 ++++++++------ .../columnReorder/columnReorderInterfaces.ts | 7 +++ .../hooks/features/columnReorder/index.ts | 2 +- .../features/columns/gridColumnsInterfaces.ts | 15 +++++++ .../features/columns/gridColumnsUtils.ts | 2 +- .../grid/hooks/features/columns/index.ts | 2 +- .../hooks/features/columns/useGridColumns.ts | 2 +- .../hooks/features/filter/useGridFilter.ts | 6 ++- .../filter/useGridRegisterFilteringMethod.ts | 14 ++++-- .../hooks/features/scroll/useGridScroll.ts | 2 +- .../features/selection/useGridSelection.ts | 13 ++++-- .../sorting/useGridRegisterSortingMethod.ts | 14 ++++-- .../hooks/features/sorting/useGridSorting.ts | 6 ++- .../features/treeData/useGridTreeData.tsx | 13 ++++-- .../grid/_modules_/grid/models/gridState.ts | 4 +- 17 files changed, 141 insertions(+), 40 deletions(-) create mode 100644 packages/grid/_modules_/grid/hooks/features/columnReorder/columnReorderInterfaces.ts create mode 100644 packages/grid/_modules_/grid/hooks/features/columns/gridColumnsInterfaces.ts diff --git a/packages/grid/_modules_/grid/hooks/core/preProcessing/gridPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/core/preProcessing/gridPreProcessingApi.ts index c87869d11e6d..659dd526ed83 100644 --- a/packages/grid/_modules_/grid/hooks/core/preProcessing/gridPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/core/preProcessing/gridPreProcessingApi.ts @@ -1,3 +1,13 @@ +import { + GridCellIndexCoordinates, + GridColDef, + GridScrollParams, +} from '../../../models'; +import { GridFilteringMethodCollection } from '../../features/filter/gridFilterState'; +import { GridSortingMethodCollection } from '../../features/sorting/gridSortingState'; +import { GridCanBeReorderedPreProcessingContext } from '../../features/columnReorder/columnReorderInterfaces'; +import { GridColumnsRawState } from '../../features/columns/gridColumnsInterfaces'; + export type PreProcessorCallback = (value: any, params?: any) => any; export enum GridPreProcessingGroup { @@ -9,6 +19,37 @@ export enum GridPreProcessingGroup { sortingMethod = 'sortingMethod', } +interface GridPreProcessingGroupLookup { + [GridPreProcessingGroup.hydrateColumns]: { value: GridColumnsRawState }; + [GridPreProcessingGroup.scrollToIndexes]: { + value: Partial; + context: Partial; + }; + [GridPreProcessingGroup.columnMenu]: { value: JSX.Element[]; context: GridColDef }; + [GridPreProcessingGroup.canBeReordered]: { + value: boolean; + context: GridCanBeReorderedPreProcessingContext; + }; + [GridPreProcessingGroup.filteringMethod]: { value: GridFilteringMethodCollection }; + [GridPreProcessingGroup.sortingMethod]: { value: GridSortingMethodCollection }; +} + +export type GridPreProcessor

= ( + value: GridPreProcessingGroupLookup[P]['value'], + context: GridPreProcessingGroupLookup[P] extends { context: any } + ? GridPreProcessingGroupLookup[P]['context'] + : undefined, +) => GridPreProcessingGroupLookup[P]['value']; + +type GridPreProcessorsApplierArg< + P extends GridPreProcessingGroup, + T extends { value: any }, +> = T extends { context: any } ? [P, T['value'], T['context']] : [P, T['value']]; + +type GridPreProcessorsApplier =

( + ...params: GridPreProcessorsApplierArg +) => GridPreProcessingGroupLookup[P]['value']; + export interface GridPreProcessingApi { /** * Register a pre-processor and emit an event to notify the agents to re-apply the pre-processors. @@ -31,5 +72,5 @@ export interface GridPreProcessingApi { * @returns {any} The value after passing through all pre-processors. * @ignore - do not document. */ - unstable_applyPreProcessors: (group: GridPreProcessingGroup, value: any, params?: any) => any; + unstable_applyPreProcessors: GridPreProcessorsApplier; } diff --git a/packages/grid/_modules_/grid/hooks/core/preProcessing/useGridPreProcessing.ts b/packages/grid/_modules_/grid/hooks/core/preProcessing/useGridPreProcessing.ts index 4fa049034d09..bea5fc9e63a7 100644 --- a/packages/grid/_modules_/grid/hooks/core/preProcessing/useGridPreProcessing.ts +++ b/packages/grid/_modules_/grid/hooks/core/preProcessing/useGridPreProcessing.ts @@ -34,14 +34,14 @@ export const useGridPreProcessing = (apiRef: GridApiRef) => { ); const applyPreProcessors = React.useCallback( - (group, value, params) => { + (group, value, ...params) => { if (!preProcessorsRef.current[group]) { return value; } const preProcessors = Object.values(preProcessorsRef.current[group]!); return preProcessors.reduce((acc, preProcessor) => { - return preProcessor(acc, params); + return preProcessor(acc, ...params); }, value); }, [], diff --git a/packages/grid/_modules_/grid/hooks/features/columnPinning/useGridColumnPinning.tsx b/packages/grid/_modules_/grid/hooks/features/columnPinning/useGridColumnPinning.tsx index 9fc40a120bb7..62fbfb7aab78 100644 --- a/packages/grid/_modules_/grid/hooks/features/columnPinning/useGridColumnPinning.tsx +++ b/packages/grid/_modules_/grid/hooks/features/columnPinning/useGridColumnPinning.tsx @@ -7,8 +7,7 @@ import { gridColumnsMetaSelector, gridVisibleColumnFieldsSelector, } from '../columns/gridColumnsSelector'; -import { GridCellIndexCoordinates } from '../../../models/gridCell'; -import { GridPreProcessingGroup } from '../../core/preProcessing'; +import { GridPreProcessingGroup, GridPreProcessor } from '../../core/preProcessing'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { GridEvents } from '../../../models/events'; import { gridClasses } from '../../../gridClasses'; @@ -22,8 +21,6 @@ import { useGridSelector } from '../../utils/useGridSelector'; import { filterColumns } from '../../../../../x-data-grid-pro/src/DataGridProVirtualScroller'; import { GridRowParams } from '../../../models/params/gridRowParams'; import { MuiEvent } from '../../../models/muiEvent'; -import { GridColumnsRawState } from '../columns/gridColumnsState'; -import { GridColDef } from '../../../models/colDef/gridColDef'; const Divider = () => event.stopPropagation()} />; @@ -96,8 +93,13 @@ export const useGridColumnPinning = ( useGridApiEventHandler(apiRef, GridEvents.rowMouseEnter, handleMouseEnter); useGridApiEventHandler(apiRef, GridEvents.rowMouseLeave, handleMouseLeave); - const calculateScrollLeft = React.useCallback( - (initialValue, params: Partial) => { + /** + * PRE-PROCESSING + */ + const calculateScrollLeft = React.useCallback< + GridPreProcessor + >( + (initialValue, params) => { if (props.disableColumnPinning) { return initialValue; } @@ -138,8 +140,10 @@ export const useGridColumnPinning = ( [apiRef, pinnedColumns, props.disableColumnPinning], ); - const addColumnMenuButtons = React.useCallback( - (initialValue: JSX.Element[], column: GridColDef) => { + const addColumnMenuButtons = React.useCallback< + GridPreProcessor + >( + (initialValue, column) => { if (props.disableColumnPinning) { return initialValue; } @@ -153,8 +157,10 @@ export const useGridColumnPinning = ( [props.disableColumnPinning], ); - const reorderPinnedColumns = React.useCallback( - (columnsState: GridColumnsRawState) => { + const reorderPinnedColumns = React.useCallback< + GridPreProcessor + >( + (columnsState) => { if (columnsState.all.length === 0 || props.disableColumnPinning) { return columnsState; } @@ -180,8 +186,10 @@ export const useGridColumnPinning = ( [pinnedColumns, props.disableColumnPinning], ); - const checkIfCanBeReordered = React.useCallback( - (initialValue, { targetIndex }: { targetIndex: number }) => { + const checkIfCanBeReordered = React.useCallback< + GridPreProcessor + >( + (initialValue, { targetIndex }) => { const visibleColumnFields = gridVisibleColumnFieldsSelector(apiRef.current.state); const [leftPinnedColumns, rightPinnedColumns] = filterColumns( pinnedColumns, diff --git a/packages/grid/_modules_/grid/hooks/features/columnReorder/columnReorderInterfaces.ts b/packages/grid/_modules_/grid/hooks/features/columnReorder/columnReorderInterfaces.ts new file mode 100644 index 000000000000..4fbd3f4d953c --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/features/columnReorder/columnReorderInterfaces.ts @@ -0,0 +1,7 @@ +export interface GridColumnReorderState { + dragCol: string; +} + +export interface GridCanBeReorderedPreProcessingContext { + targetIndex: number; +} diff --git a/packages/grid/_modules_/grid/hooks/features/columnReorder/index.ts b/packages/grid/_modules_/grid/hooks/features/columnReorder/index.ts index cad03e375c20..3b036758a677 100644 --- a/packages/grid/_modules_/grid/hooks/features/columnReorder/index.ts +++ b/packages/grid/_modules_/grid/hooks/features/columnReorder/index.ts @@ -1,2 +1,2 @@ export * from './columnReorderSelector'; -export * from './columnReorderState'; +export type { GridColumnReorderState } from './columnReorderInterfaces'; diff --git a/packages/grid/_modules_/grid/hooks/features/columns/gridColumnsInterfaces.ts b/packages/grid/_modules_/grid/hooks/features/columns/gridColumnsInterfaces.ts new file mode 100644 index 000000000000..fdc237d257f7 --- /dev/null +++ b/packages/grid/_modules_/grid/hooks/features/columns/gridColumnsInterfaces.ts @@ -0,0 +1,15 @@ +import type { GridStateColDef } from '../../../models'; +import { GridColDef } from '../../../models'; + +export type GridColumnLookup = { [field: string]: GridStateColDef }; + +export type GridColumnRawLookup = { [field: string]: GridColDef | GridStateColDef }; + +export interface GridColumnsState { + all: string[]; + lookup: GridColumnLookup; +} + +export type GridColumnsRawState = Omit & { + lookup: GridColumnRawLookup; +}; diff --git a/packages/grid/_modules_/grid/hooks/features/columns/gridColumnsUtils.ts b/packages/grid/_modules_/grid/hooks/features/columns/gridColumnsUtils.ts index 9b2b6eff38bb..7f55598f6a00 100644 --- a/packages/grid/_modules_/grid/hooks/features/columns/gridColumnsUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/columns/gridColumnsUtils.ts @@ -1,4 +1,4 @@ -import { GridColumnLookup, GridColumnsState, GridColumnsRawState } from './gridColumnsState'; +import { GridColumnLookup, GridColumnsState, GridColumnsRawState } from './gridColumnsInterfaces'; import { DEFAULT_GRID_COL_TYPE_KEY, getGridDefaultColumnTypes, diff --git a/packages/grid/_modules_/grid/hooks/features/columns/index.ts b/packages/grid/_modules_/grid/hooks/features/columns/index.ts index 616484a999e3..dd879cf7b78e 100644 --- a/packages/grid/_modules_/grid/hooks/features/columns/index.ts +++ b/packages/grid/_modules_/grid/hooks/features/columns/index.ts @@ -1,3 +1,3 @@ export * from './gridColumnsSelector'; -export type { GridColumnLookup, GridColumnsState } from './gridColumnsState'; +export type { GridColumnLookup, GridColumnsState } from './gridColumnsInterfaces'; export { getGridColDef } from './gridColumnsUtils'; diff --git a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts index 6fa34037138e..61e5bc089629 100644 --- a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts +++ b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts @@ -21,7 +21,7 @@ import { DataGridProcessedProps } from '../../../models/props/DataGridProps'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { GridColumnVisibilityChangeParams } from '../../../models'; import { GridPreProcessingGroup } from '../../core/preProcessing'; -import { GridColumnsState } from './gridColumnsState'; +import { GridColumnsState } from './gridColumnsInterfaces'; import { hydrateColumnsWidth, computeColumnTypes, createColumnsState } from './gridColumnsUtils'; /** diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 4af2ad267eb6..e57f7fba81ff 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -21,7 +21,11 @@ import { gridFilterModelSelector, gridVisibleSortedRowEntriesSelector } from './ import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; import { gridRowIdsSelector, gridRowGroupingNameSelector } from '../rows'; -import { GridPreProcessingGroup } from '../../core/preProcessing'; +import { + GridPreProcessingGroup, + GridPreProcessor, + useGridRegisterPreProcessor, +} from '../../core/preProcessing'; import { useGridRegisterFilteringMethod } from './useGridRegisterFilteringMethod'; import { buildAggregatedFilterApplier, cleanFilterItem } from './gridFilterUtils'; diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridRegisterFilteringMethod.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridRegisterFilteringMethod.ts index 3452e20aad5d..cb182cc4b2f5 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridRegisterFilteringMethod.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridRegisterFilteringMethod.ts @@ -1,6 +1,10 @@ import * as React from 'react'; -import { GridFilteringMethod, GridFilteringMethodCollection } from './gridFilterState'; -import { GridPreProcessingGroup, useGridRegisterPreProcessor } from '../../core/preProcessing'; +import { GridFilteringMethod } from './gridFilterState'; +import { + GridPreProcessingGroup, + GridPreProcessor, + useGridRegisterPreProcessor, +} from '../../core/preProcessing'; import { GridApiRef } from '../../../models'; export const useGridRegisterFilteringMethod = ( @@ -8,8 +12,10 @@ export const useGridRegisterFilteringMethod = ( groupingName: string, filteringMethod: GridFilteringMethod, ) => { - const updateRegistration = React.useCallback( - (filteringMethodCollection: GridFilteringMethodCollection) => { + const updateRegistration = React.useCallback< + GridPreProcessor + >( + (filteringMethodCollection) => { filteringMethodCollection[groupingName] = filteringMethod; return filteringMethodCollection; }, diff --git a/packages/grid/_modules_/grid/hooks/features/scroll/useGridScroll.ts b/packages/grid/_modules_/grid/hooks/features/scroll/useGridScroll.ts index bc3724f9c665..1fafc001db9c 100644 --- a/packages/grid/_modules_/grid/hooks/features/scroll/useGridScroll.ts +++ b/packages/grid/_modules_/grid/hooks/features/scroll/useGridScroll.ts @@ -61,7 +61,7 @@ export const useGridScroll = ( logger.debug(`Scrolling to cell at row ${params.rowIndex}, col: ${params.colIndex} `); - let scrollCoordinates: any = {}; + let scrollCoordinates: Partial = {}; if (params.colIndex != null) { scrollCoordinates.left = scrollIntoView({ diff --git a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts index 4cf5b038cfd1..531933edbb53 100644 --- a/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts +++ b/packages/grid/_modules_/grid/hooks/features/selection/useGridSelection.ts @@ -20,9 +20,12 @@ import { gridVisibleSortedRowIdsSelector } from '../filter/gridFilterSelector'; import { GRID_CHECKBOX_SELECTION_COL_DEF, GridColDef } from '../../../models'; import { getDataGridUtilityClass, gridClasses } from '../../../gridClasses'; import { useGridStateInit } from '../../utils/useGridStateInit'; -import { GridPreProcessingGroup, useGridRegisterPreProcessor } from '../../core/preProcessing'; +import { + GridPreProcessingGroup, + GridPreProcessor, + useGridRegisterPreProcessor, +} from '../../core/preProcessing'; import { GridCellModes } from '../../../models/gridEditRowModel'; -import { GridColumnsRawState } from '../columns/gridColumnsState'; import { isKeyboardEvent } from '../../../utils/keyboardUtils'; type OwnerState = { classes: DataGridProcessedProps['classes'] }; @@ -118,8 +121,10 @@ export const useGridSelection = ( /** * PRE-PROCESSING */ - const updateSelectionColumn = React.useCallback( - (columnsState: GridColumnsRawState) => { + const updateSelectionColumn = React.useCallback< + GridPreProcessor + >( + (columnsState) => { const selectionColumn: GridColDef = { ...GRID_CHECKBOX_SELECTION_COL_DEF, cellClassName: classes.cellCheckbox, diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridRegisterSortingMethod.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridRegisterSortingMethod.ts index b9fc61b1c919..b6f9a84da420 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridRegisterSortingMethod.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridRegisterSortingMethod.ts @@ -1,6 +1,10 @@ import * as React from 'react'; -import { GridSortingMethod, GridSortingMethodCollection } from './gridSortingState'; -import { GridPreProcessingGroup, useGridRegisterPreProcessor } from '../../core/preProcessing'; +import { GridSortingMethod } from './gridSortingState'; +import { + GridPreProcessingGroup, + GridPreProcessor, + useGridRegisterPreProcessor, +} from '../../core/preProcessing'; import { GridApiRef } from '../../../models'; export const useGridRegisterSortingMethod = ( @@ -8,8 +12,10 @@ export const useGridRegisterSortingMethod = ( groupingName: string, filteringMethod: GridSortingMethod, ) => { - const updateRegistration = React.useCallback( - (sortingMethodCollection: GridSortingMethodCollection) => { + const updateRegistration = React.useCallback< + GridPreProcessor + >( + (sortingMethodCollection) => { sortingMethodCollection[groupingName] = filteringMethod; return sortingMethodCollection; }, diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 0e6dab93bc05..23e6bca333b6 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 { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; import { GridSortingMethod, GridSortingMethodCollection } from './gridSortingState'; import { buildAggregatedSortingApplier } from './gridSortingUtils'; -import { GridPreProcessingGroup } from '../../core/preProcessing'; +import { + GridPreProcessingGroup, + GridPreProcessor, + useGridRegisterPreProcessor, +} from '../../core/preProcessing'; import { useGridRegisterSortingMethod } from './useGridRegisterSortingMethod'; /** diff --git a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.tsx b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.tsx index 97538c4a17ad..fa770b801444 100644 --- a/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.tsx +++ b/packages/grid/_modules_/grid/hooks/features/treeData/useGridTreeData.tsx @@ -17,8 +17,11 @@ import { useFirstRender } from '../../utils/useFirstRender'; import { buildRowTree, BuildRowTreeGroupingCriteria } from '../../../utils/tree/buildRowTree'; import { GridRowGroupingPreProcessing } from '../../core/rowGroupsPerProcessing'; import { gridFilteredDescendantCountLookupSelector } from '../filter'; -import { GridPreProcessingGroup, useGridRegisterPreProcessor } from '../../core/preProcessing'; -import { GridColumnsRawState } from '../columns/gridColumnsState'; +import { + GridPreProcessingGroup, + GridPreProcessor, + useGridRegisterPreProcessor, +} from '../../core/preProcessing'; import { GridFilteringMethod } from '../filter/gridFilterState'; import { gridRowIdsSelector, gridRowTreeSelector } from '../rows'; import { useGridRegisterFilteringMethod } from '../filter/useGridRegisterFilteringMethod'; @@ -137,8 +140,10 @@ export const useGridTreeData = ( }; }, [apiRef, props.groupingColDef]); - const updateGroupingColumn = React.useCallback( - (columnsState: GridColumnsRawState) => { + const updateGroupingColumn = React.useCallback< + GridPreProcessor + >( + (columnsState) => { const groupingColDefField = GRID_TREE_DATA_GROUPING_COL_DEF_FORCED_PROPERTIES.field; const shouldHaveGroupingColumn = props.treeData; diff --git a/packages/grid/_modules_/grid/models/gridState.ts b/packages/grid/_modules_/grid/models/gridState.ts index b5c6ee205c15..82e070c9a4e7 100644 --- a/packages/grid/_modules_/grid/models/gridState.ts +++ b/packages/grid/_modules_/grid/models/gridState.ts @@ -1,7 +1,7 @@ -import type { GridColumnsState } from '../hooks/features/columns/gridColumnsState'; +import type { GridColumnsState } from '../hooks/features/columns/gridColumnsInterfaces'; import type { GridEditRowsModel } from './gridEditRowModel'; import type { GridColumnMenuState } from '../hooks/features/columnMenu/columnMenuState'; -import type { GridColumnReorderState } from '../hooks/features/columnReorder/columnReorderState'; +import type { GridColumnReorderState } from '../hooks/features/columnReorder'; import type { GridColumnResizeState } from '../hooks/features/columnResize/columnResizeState'; import type { GridDensityState } from '../hooks/features/density/densityState'; import type { GridFocusState, GridTabIndexState } from '../hooks/features/focus/gridFocusState'; From 15fb5d67dfd028b7993da0200bf843b66902bbc2 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 11 Jan 2022 16:22:30 +0100 Subject: [PATCH 06/26] Prettier --- .../grid/hooks/core/preProcessing/gridPreProcessingApi.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/core/preProcessing/gridPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/core/preProcessing/gridPreProcessingApi.ts index 659dd526ed83..87e9ce018966 100644 --- a/packages/grid/_modules_/grid/hooks/core/preProcessing/gridPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/core/preProcessing/gridPreProcessingApi.ts @@ -1,8 +1,4 @@ -import { - GridCellIndexCoordinates, - GridColDef, - GridScrollParams, -} from '../../../models'; +import { GridCellIndexCoordinates, GridColDef, GridScrollParams } from '../../../models'; import { GridFilteringMethodCollection } from '../../features/filter/gridFilterState'; import { GridSortingMethodCollection } from '../../features/sorting/gridSortingState'; import { GridCanBeReorderedPreProcessingContext } from '../../features/columnReorder/columnReorderInterfaces'; From 450dcce3bce80adc29082d16c1a6055167df31a0 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 11 Jan 2022 16:32:30 +0100 Subject: [PATCH 07/26] Fix --- .../_modules_/grid/hooks/features/filter/useGridFilter.ts | 6 +----- .../_modules_/grid/hooks/features/sorting/useGridSorting.ts | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index e57f7fba81ff..4af2ad267eb6 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -21,11 +21,7 @@ import { gridFilterModelSelector, gridVisibleSortedRowEntriesSelector } from './ import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; import { gridRowIdsSelector, gridRowGroupingNameSelector } from '../rows'; -import { - GridPreProcessingGroup, - GridPreProcessor, - useGridRegisterPreProcessor, -} from '../../core/preProcessing'; +import { GridPreProcessingGroup } from '../../core/preProcessing'; import { useGridRegisterFilteringMethod } from './useGridRegisterFilteringMethod'; import { buildAggregatedFilterApplier, cleanFilterItem } from './gridFilterUtils'; diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 23e6bca333b6..0e6dab93bc05 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -22,11 +22,7 @@ import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; import { GridSortingMethod, GridSortingMethodCollection } from './gridSortingState'; import { buildAggregatedSortingApplier } from './gridSortingUtils'; -import { - GridPreProcessingGroup, - GridPreProcessor, - useGridRegisterPreProcessor, -} from '../../core/preProcessing'; +import { GridPreProcessingGroup } from '../../core/preProcessing'; import { useGridRegisterSortingMethod } from './useGridRegisterSortingMethod'; /** From 170749a307550b558cc0f127279bf1f5794f14e5 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 11 Jan 2022 16:52:27 +0100 Subject: [PATCH 08/26] Work --- scripts/x-data-grid-pro.exports.json | 2 +- scripts/x-data-grid.exports.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/x-data-grid-pro.exports.json b/scripts/x-data-grid-pro.exports.json index 5b8c4381599e..3bada8eda7b9 100644 --- a/scripts/x-data-grid-pro.exports.json +++ b/scripts/x-data-grid-pro.exports.json @@ -113,7 +113,7 @@ { "name": "GridColumnPinningState", "kind": "TypeAlias" }, { "name": "gridColumnReorderDragColSelector", "kind": "Variable" }, { "name": "gridColumnReorderSelector", "kind": "Variable" }, - { "name": "GridColumnReorderState", "kind": "Interface" }, + { "name": "GridColumnReorderState", "kind": "ExportSpecifier" }, { "name": "GridColumnResizeParams", "kind": "Interface" }, { "name": "gridColumnResizeSelector", "kind": "Variable" }, { "name": "GridColumnResizeState", "kind": "Interface" }, diff --git a/scripts/x-data-grid.exports.json b/scripts/x-data-grid.exports.json index f1d5a13b39e1..60b8d094f85c 100644 --- a/scripts/x-data-grid.exports.json +++ b/scripts/x-data-grid.exports.json @@ -113,7 +113,7 @@ { "name": "GridColumnPinningState", "kind": "TypeAlias" }, { "name": "gridColumnReorderDragColSelector", "kind": "Variable" }, { "name": "gridColumnReorderSelector", "kind": "Variable" }, - { "name": "GridColumnReorderState", "kind": "Interface" }, + { "name": "GridColumnReorderState", "kind": "ExportSpecifier" }, { "name": "GridColumnResizeParams", "kind": "Interface" }, { "name": "gridColumnResizeSelector", "kind": "Variable" }, { "name": "GridColumnResizeState", "kind": "Interface" }, From e3530f7fbed43a9b825c18dc367933f5156fa351 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 12 Jan 2022 13:23:50 +0100 Subject: [PATCH 09/26] Work --- .../data-grid/state/RestoreStateApiRef.js | 23 +++++--------- .../data-grid/state/RestoreStateApiRef.tsx | 31 +++++++------------ 2 files changed, 20 insertions(+), 34 deletions(-) diff --git a/docs/src/pages/components/data-grid/state/RestoreStateApiRef.js b/docs/src/pages/components/data-grid/state/RestoreStateApiRef.js index c3d839bfee7c..015af268c6f8 100644 --- a/docs/src/pages/components/data-grid/state/RestoreStateApiRef.js +++ b/docs/src/pages/components/data-grid/state/RestoreStateApiRef.js @@ -7,17 +7,10 @@ import TextField from '@mui/material/TextField'; import Box from '@mui/material/Box'; import Chip from '@mui/material/Chip'; import AddIcon from '@mui/icons-material/Add'; -var DemoActionTypes; -(function (DemoActionTypes) { - DemoActionTypes[(DemoActionTypes['CreateView'] = 0)] = 'CreateView'; - DemoActionTypes[(DemoActionTypes['DeleteView'] = 1)] = 'DeleteView'; - DemoActionTypes[(DemoActionTypes['SetActiveView'] = 2)] = 'SetActiveView'; - DemoActionTypes[(DemoActionTypes['SetNewViewLabel'] = 3)] = 'SetNewViewLabel'; -})(DemoActionTypes || (DemoActionTypes = {})); const demoReducer = (state, action) => { switch (action.type) { - case DemoActionTypes.CreateView: { + case 'createView': { const id = Math.random().toString(); return { @@ -31,7 +24,7 @@ const demoReducer = (state, action) => { }; } - case DemoActionTypes.DeleteView: { + case 'deleteView': { const views = Object.fromEntries( Object.entries(state.views).filter(([id]) => id !== action.id), ); @@ -56,14 +49,14 @@ const demoReducer = (state, action) => { }; } - case DemoActionTypes.SetActiveView: { + case 'setActiveView': { return { ...state, activeViewId: action.id, }; } - case DemoActionTypes.SetNewViewLabel: { + case 'setNewViewLabel': { return { ...state, newViewLabel: action.label, @@ -93,22 +86,22 @@ export default function RestoreStateApiRef() { const createNewView = () => { dispatch({ - type: DemoActionTypes.CreateView, + type: 'createView', value: apiRef.current.exportState(), }); }; const handleNewViewLabelChange = (e) => { - dispatch({ type: DemoActionTypes.SetNewViewLabel, label: e.target.value }); + dispatch({ type: 'setNewViewLabel', label: e.target.value }); }; const handleDeleteView = (viewId) => { - dispatch({ type: DemoActionTypes.DeleteView, id: viewId }); + dispatch({ type: 'deleteView', id: viewId }); }; const handleSetActiveView = (viewId) => { apiRef.current.restoreState(state.views[viewId].value); - dispatch({ type: DemoActionTypes.SetActiveView, id: viewId }); + dispatch({ type: 'setActiveView', id: viewId }); }; const isNewViewLabelValid = React.useMemo(() => { diff --git a/docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx b/docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx index d1c4c6a1b861..e5693ffcb342 100644 --- a/docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx +++ b/docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx @@ -14,22 +14,15 @@ interface DemoState { activeViewId: string | null; } -enum DemoActionTypes { - CreateView, - DeleteView, - SetActiveView, - SetNewViewLabel, -} - type DemoActions = - | { type: DemoActionTypes.CreateView; value: GridInitialState } - | { type: DemoActionTypes.DeleteView; id: string } - | { type: DemoActionTypes.SetNewViewLabel; label: string } - | { type: DemoActionTypes.SetActiveView; id: string | null }; + | { type: 'createView'; value: GridInitialState } + | { type: 'deleteView'; id: string } + | { type: 'setNewViewLabel'; label: string } + | { type: 'setActiveView'; id: string | null }; const demoReducer: React.Reducer = (state, action) => { switch (action.type) { - case DemoActionTypes.CreateView: { + case 'createView': { const id = Math.random().toString(); return { @@ -43,7 +36,7 @@ const demoReducer: React.Reducer = (state, action) => { }; } - case DemoActionTypes.DeleteView: { + case 'deleteView': { const views = Object.fromEntries( Object.entries(state.views).filter(([id]) => id !== action.id), ); @@ -68,14 +61,14 @@ const demoReducer: React.Reducer = (state, action) => { }; } - case DemoActionTypes.SetActiveView: { + case 'setActiveView': { return { ...state, activeViewId: action.id, }; } - case DemoActionTypes.SetNewViewLabel: { + case 'setNewViewLabel': { return { ...state, newViewLabel: action.label, @@ -104,7 +97,7 @@ export default function RestoreStateApiRef() { const createNewView = () => { dispatch({ - type: DemoActionTypes.CreateView, + type: 'createView', value: apiRef.current.exportState(), }); }; @@ -112,16 +105,16 @@ export default function RestoreStateApiRef() { const handleNewViewLabelChange = ( e: React.ChangeEvent, ) => { - dispatch({ type: DemoActionTypes.SetNewViewLabel, label: e.target.value }); + dispatch({ type: 'setNewViewLabel', label: e.target.value }); }; const handleDeleteView = (viewId: string) => { - dispatch({ type: DemoActionTypes.DeleteView, id: viewId }); + dispatch({ type: 'deleteView', id: viewId }); }; const handleSetActiveView = (viewId: string) => { apiRef.current.restoreState(state.views[viewId].value); - dispatch({ type: DemoActionTypes.SetActiveView, id: viewId }); + dispatch({ type: 'setActiveView', id: viewId }); }; const isNewViewLabelValid = React.useMemo(() => { From 54e1798a368db6d5c323a1b8f90e1d952305b027 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 13 Jan 2022 13:33:54 +0100 Subject: [PATCH 10/26] Work --- .../grid/components/base/GridBody.tsx | 2 + .../hooks/core/useGridStateInitialization.ts | 14 +- .../hooks/features/pagination/useGridPage.ts | 5 +- .../tests/columnPinning.DataGridPro.test.tsx | 8 +- .../statePersistence.DataGridPro.test.tsx | 145 ++++++++++++++++++ 5 files changed, 162 insertions(+), 12 deletions(-) create mode 100644 packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx diff --git a/packages/grid/_modules_/grid/components/base/GridBody.tsx b/packages/grid/_modules_/grid/components/base/GridBody.tsx index 7df8df5d1128..df8d48e54a91 100644 --- a/packages/grid/_modules_/grid/components/base/GridBody.tsx +++ b/packages/grid/_modules_/grid/components/base/GridBody.tsx @@ -47,6 +47,8 @@ function GridBody(props: GridBodyProps) { setIsVirtualizationDisabled(false); }, []); + console.log('RENDER') + // The `useGridApiMethod` 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 diff --git a/packages/grid/_modules_/grid/hooks/core/useGridStateInitialization.ts b/packages/grid/_modules_/grid/hooks/core/useGridStateInitialization.ts index 3529f9b4284e..b4ed34326585 100644 --- a/packages/grid/_modules_/grid/hooks/core/useGridStateInitialization.ts +++ b/packages/grid/_modules_/grid/hooks/core/useGridStateInitialization.ts @@ -65,13 +65,13 @@ export const useGridStateInitialization = (apiRef: GridApiRef, props: DataGridPr // Each hook modify its own state, and it should not leak // Events are here to forward to other hooks and apply changes. // You are trying to update several states in a no isolated way. - throw new Error( - `You're not allowed to update several sub-state in one transaction. You already updated ${ - updatedControlStateIds[0] - }, therefore, you're not allowed to update ${updatedControlStateIds.join( - ', ', - )} in the same transaction.`, - ); + // throw new Error( + // `You're not allowed to update several sub-state in one transaction. You already updated ${ + // updatedControlStateIds[0] + // }, therefore, you're not allowed to update ${updatedControlStateIds.join( + // ', ', + // )} in the same transaction.`, + // ); } if (!ignoreSetState) { diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts index f93becdb0739..736b9acbbe1a 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts @@ -114,9 +114,12 @@ export const useGridPage = ( GridPreProcessor >((params, context) => { // We apply the constraint even if the page did not change in case the pageSize changed. + const page = context.stateToRestore.pagination?.page ?? gridPageSelector(params.state) + const pageCount = getPageCount(params.state.pagination.rowCount, params.state.pagination.page); const paginationState = applyValidPage({ ...params.state.pagination, - page: context.stateToRestore.pagination?.page ?? gridPageSelector(params.state), + pageCount, + page, }); return { diff --git a/packages/grid/x-data-grid-pro/src/tests/columnPinning.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/columnPinning.DataGridPro.test.tsx index a41b529e18d6..9bb5bcc99acd 100644 --- a/packages/grid/x-data-grid-pro/src/tests/columnPinning.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/columnPinning.DataGridPro.test.tsx @@ -222,7 +222,7 @@ describe(' - Column pinning', () => { }); describe('props: onPinnedColumnsChange', () => { - it('shoull call when a column is pinned', () => { + it('should call when a column is pinned', () => { const handlePinnedColumnsChange = spy(); render(); apiRef.current.pinColumn('currencyPair', GridPinnedPosition.left); @@ -237,7 +237,7 @@ describe(' - Column pinning', () => { }); }); - it('shoull not change the pinned columns when it is called', () => { + it('should not change the pinned columns when it is called', () => { const handlePinnedColumnsChange = spy(); render( - Column pinning', () => { }); describe('props: pinnedColumns', () => { - it('shoull pin the columns specified', () => { + it('should pin the columns specified', () => { render(); const leftColumns = document.querySelector( `.${gridClasses['pinnedColumns--left']}`, @@ -283,7 +283,7 @@ describe(' - Column pinning', () => { ).not.to.equal(null); }); - it('shoull filter our duplicated columns', () => { + it('should filter our duplicated columns', () => { render(); const leftColumns = document.querySelector( `.${gridClasses['pinnedColumns--left']}`, diff --git a/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx new file mode 100644 index 000000000000..558e7b91ac43 --- /dev/null +++ b/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx @@ -0,0 +1,145 @@ +import * as React from 'react'; +import { + DataGridPro, + DataGridProProps, + GridApiRef, + GridPreferencePanelsValue, + useGridApiRef, +} from '@mui/x-data-grid-pro'; +import { useData } from 'packages/storybook/src/hooks/useData'; +import { createRenderer, screen } from '@mui/monorepo/test/utils'; +import { expect } from 'chai'; +import { getColumnValues} from "../../../../../test/utils/helperFn"; + +const isJSDOM = /jsdom/.test(window.navigator.userAgent); + +describe.only(' - State Persistence', () => { + const { render } = createRenderer(); + + let apiRef: GridApiRef; + + const TestCase = (props: Omit) => { + const basicData = useData(100, 2); + + apiRef = useGridApiRef(); + + return ( +

+ +
+ ); + }; + + describe('apiRef: exportState', () => { + it('should return the whole exportable state', () => { + render(); + expect(apiRef.current.exportState()).to.deep.equal({ + filter: { + filterModel: { + items: [], + linkOperator: 'and', + }, + }, + pagination: { + page: 0, + pageSize: 100, + }, + pinnedColumns: { + left: undefined, + right: undefined, + }, + preferencePanel: { + open: false, + }, + sorting: { + sortModel: [], + }, + }); + }); + + it('should export the current version of the exportable state', () => { + render(); + apiRef.current.setPageSize(5) + apiRef.current.setPage(1) + apiRef.current.setPinnedColumns({ left: ['id']}) + apiRef.current.showPreferences(GridPreferencePanelsValue.filters) + apiRef.current.setSortModel([{ field: 'id', sort: 'desc' }]) + apiRef.current.setFilterModel({ items: [{ columnField: 'id', operatorValue: '<', value: '97' }]}) + expect(apiRef.current.exportState()).to.deep.equal({ + filter: { + filterModel: { + items: [{ columnField: 'id', operatorValue: '<', value: '97' }], + }, + }, + pagination: { + page: 1, + pageSize: 5, + }, + pinnedColumns: { + left: ['id'], + }, + preferencePanel: { + open: true, + openedPanelValue: GridPreferencePanelsValue.filters, + }, + sorting: { + sortModel: [{ field: 'id', sort: 'desc' }], + }, + }); + }) + }) + + describe('apiRef: restoreState', () => { + it.only('should restore the whole exportable state', () => { + render(); + + console.log('_________________') + + apiRef.current.restoreState({ + filter: { + filterModel: { + items: [{ columnField: 'id', operatorValue: '<', value: '97' }], + }, + }, + pagination: { + page: 1, + pageSize: 5, + }, + pinnedColumns: { + left: ['id'], + }, + preferencePanel: { + open: true, + openedPanelValue: GridPreferencePanelsValue.filters, + }, + sorting: { + sortModel: [{ field: 'id', sort: 'desc' }], + }, + }) + + console.log('______') + + // Test sorting + filtering + pagination + expect(getColumnValues(0)).to.deep.equal(['91', '90', '89', '88', '87']) + // Preference panel + expect(screen.getByRole('button', { name: /Add Filter/i })).to.not.equal(null) + // Pinning + expect( + document.querySelector('.MuiDataGrid-pinnedColumnHeaders--left')?.textContent, + ).to.deep.equal('id'); + }) + + it('should restore partial exportable state', () => { + render(); + + apiRef.current.restoreState({ + pagination: { + page: 1, + pageSize: 5, + }, + }) + + expect(getColumnValues(0)).to.deep.equal(['5', '6', '7', '8', '9']) + }) + }) +}); From 0742e739e37c8f71d803529c014408d07aebec31 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 13 Jan 2022 15:30:52 +0100 Subject: [PATCH 11/26] Work --- .../grid/components/base/GridBody.tsx | 2 - .../hooks/core/useGridStateInitialization.ts | 14 +- .../columnPinning/useGridColumnPinning.tsx | 35 ++- .../hooks/features/filter/gridFilterUtils.ts | 19 ++ .../hooks/features/filter/useGridFilter.ts | 33 +- .../hooks/features/pagination/useGridPage.ts | 50 ++- .../features/pagination/useGridPageSize.ts | 44 ++- .../useGridPreferencesPanel.ts | 28 +- .../features/sorting/gridSortingUtils.ts | 11 + .../hooks/features/sorting/useGridSorting.ts | 18 +- .../GridStatePersistenceApi.ts | 4 +- .../useGridStatePersistence.ts | 3 - .../statePersistence.DataGridPro.test.tsx | 293 +++++++++++------- 13 files changed, 311 insertions(+), 243 deletions(-) diff --git a/packages/grid/_modules_/grid/components/base/GridBody.tsx b/packages/grid/_modules_/grid/components/base/GridBody.tsx index df8d48e54a91..7df8df5d1128 100644 --- a/packages/grid/_modules_/grid/components/base/GridBody.tsx +++ b/packages/grid/_modules_/grid/components/base/GridBody.tsx @@ -47,8 +47,6 @@ function GridBody(props: GridBodyProps) { setIsVirtualizationDisabled(false); }, []); - console.log('RENDER') - // The `useGridApiMethod` 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 diff --git a/packages/grid/_modules_/grid/hooks/core/useGridStateInitialization.ts b/packages/grid/_modules_/grid/hooks/core/useGridStateInitialization.ts index b4ed34326585..3529f9b4284e 100644 --- a/packages/grid/_modules_/grid/hooks/core/useGridStateInitialization.ts +++ b/packages/grid/_modules_/grid/hooks/core/useGridStateInitialization.ts @@ -65,13 +65,13 @@ export const useGridStateInitialization = (apiRef: GridApiRef, props: DataGridPr // Each hook modify its own state, and it should not leak // Events are here to forward to other hooks and apply changes. // You are trying to update several states in a no isolated way. - // throw new Error( - // `You're not allowed to update several sub-state in one transaction. You already updated ${ - // updatedControlStateIds[0] - // }, therefore, you're not allowed to update ${updatedControlStateIds.join( - // ', ', - // )} in the same transaction.`, - // ); + throw new Error( + `You're not allowed to update several sub-state in one transaction. You already updated ${ + updatedControlStateIds[0] + }, therefore, you're not allowed to update ${updatedControlStateIds.join( + ', ', + )} in the same transaction.`, + ); } if (!ignoreSetState) { diff --git a/packages/grid/_modules_/grid/hooks/features/columnPinning/useGridColumnPinning.tsx b/packages/grid/_modules_/grid/hooks/features/columnPinning/useGridColumnPinning.tsx index 5def0bb37d08..767a09eda696 100644 --- a/packages/grid/_modules_/grid/hooks/features/columnPinning/useGridColumnPinning.tsx +++ b/packages/grid/_modules_/grid/hooks/features/columnPinning/useGridColumnPinning.tsx @@ -14,16 +14,25 @@ import { gridClasses } from '../../../gridClasses'; import { useGridRegisterPreProcessor } from '../../core/preProcessing/useGridRegisterPreProcessor'; import { GridColumnPinningMenuItems } from '../../../components/menu/columnMenu/GridColumnPinningMenuItems'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; -import { GridColumnPinningApi, GridPinnedPosition } from '../../../models/api/gridColumnPinningApi'; +import { + GridColumnPinningApi, + GridPinnedColumns, + GridPinnedPosition, +} from '../../../models/api/gridColumnPinningApi'; import { gridPinnedColumnsSelector } from './columnPinningSelector'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { useGridSelector } from '../../utils/useGridSelector'; import { filterColumns } from '../../../../../x-data-grid-pro/src/DataGridProVirtualScroller'; import { GridRowParams } from '../../../models/params/gridRowParams'; import { MuiEvent } from '../../../models/muiEvent'; +import { GridState } from '../../../models'; const Divider = () => event.stopPropagation()} />; +const setStatePinnedColumns = + (pinnedColumns: GridPinnedColumns) => + (state: GridState): GridState => ({ ...state, pinnedColumns }); + export const useGridColumnPinning = ( apiRef: GridApiRef, props: Pick< @@ -229,19 +238,17 @@ export const useGridColumnPinning = ( const stateRestorePreProcessing = React.useCallback< GridPreProcessor - >((params, context) => { - if (context.stateToRestore.pinnedColumns == null) { - return params; - } + >( + (params, context) => { + const newPinnedColumns = context.stateToRestore.pinnedColumns; + if (newPinnedColumns != null) { + apiRef.current.setState(setStatePinnedColumns(newPinnedColumns)); + } - return { - ...params, - state: { - ...params.state, - pinnedColumns: context.stateToRestore.pinnedColumns, - }, - }; - }, []); + return params; + }, + [apiRef], + ); useGridRegisterPreProcessor(apiRef, GridPreProcessingGroup.scrollToIndexes, calculateScrollLeft); useGridRegisterPreProcessor(apiRef, GridPreProcessingGroup.columnMenu, addColumnMenuButtons); @@ -320,7 +327,7 @@ export const useGridColumnPinning = ( const setPinnedColumns = React.useCallback( (newPinnedColumns) => { checkIfEnabled('setPinnedColumns'); - apiRef.current.setState((state) => ({ ...state, pinnedColumns: newPinnedColumns })); + apiRef.current.setState(setStatePinnedColumns(newPinnedColumns)); apiRef.current.forceUpdate(); }, [apiRef, checkIfEnabled], diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterUtils.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterUtils.ts index b935229b1332..e71f09a84491 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterUtils.ts @@ -4,10 +4,29 @@ import { GridFilterModel, GridLinkOperator, GridRowId, + GridState, } from '../../../models'; type GridFilterItemApplier = (rowId: GridRowId) => boolean; +export const setStateFilterModel = ( + filterModel: GridFilterModel, + disableMultipleColumnsFiltering: boolean, +) => { + const cleanFilterModel = { ...filterModel }; + if (cleanFilterModel.items.length > 1 && disableMultipleColumnsFiltering) { + cleanFilterModel.items = [cleanFilterModel.items[0]]; + } + + return (state: GridState): GridState => ({ + ...state, + filter: { + ...state.filter, + filterModel, + }, + }); +}; + /** * Adds default values to the optional fields of a filter items. * @param {GridFilterItem} item The raw 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 b309c5782e36..fa0afe432758 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -27,7 +27,11 @@ import { useGridRegisterPreProcessor, } from '../../core/preProcessing'; import { useGridRegisterFilteringMethod } from './useGridRegisterFilteringMethod'; -import { buildAggregatedFilterApplier, cleanFilterItem } from './gridFilterUtils'; +import { + buildAggregatedFilterApplier, + cleanFilterItem, + setStateFilterModel, +} from './gridFilterUtils'; const checkFilterModelValidity = (model: GridFilterModel) => { if (model.items.length > 1) { @@ -208,18 +212,8 @@ export const useGridFilter = ( if (currentModel !== model) { checkFilterModelValidity(model); - if (model.items.length > 1 && props.disableMultipleColumnsFiltering) { - model.items = [model.items[0]]; - } - logger.debug('Setting filter model'); - apiRef.current.setState((state) => ({ - ...state, - filter: { - ...state.filter, - filterModel: model, - }, - })); + apiRef.current.setState(setStateFilterModel(model, props.disableMultipleColumnsFiltering)); apiRef.current.unstable_applyFilters(); } }, @@ -265,23 +259,20 @@ export const useGridFilter = ( GridPreProcessor >( (params, context) => { - if (context.stateToRestore.filter?.filterModel == null) { + const filterModel = context.stateToRestore.filter?.filterModel; + if (filterModel == null) { return params; } + apiRef.current.setState( + setStateFilterModel(filterModel, props.disableMultipleColumnsFiltering), + ); return { ...params, callbacks: [...params.callbacks, apiRef.current.unstable_applyFilters], - state: { - ...params.state, - filter: { - ...params.state.filter, - filterModel: context.stateToRestore.filter.filterModel, - }, - }, }; }, - [apiRef], + [apiRef, props.disableMultipleColumnsFiltering], ); const flatFilteringMethod = React.useCallback( diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts index 736b9acbbe1a..ed5cf35a625a 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts @@ -1,5 +1,5 @@ import * as React from 'react'; -import { GridApiRef } from '../../../models'; +import { GridApiRef, GridState } from '../../../models'; import { useGridLogger, useGridSelector, @@ -37,6 +37,16 @@ const applyValidPage = (paginationState: GridPaginationState): GridPaginationSta }; }; +const setStatePage = + (page: number) => + (state: GridState): GridState => ({ + ...state, + pagination: applyValidPage({ + ...state.pagination, + page, + }), + }); + /** * @requires useGridPageSize (state, event) * @requires useGridFilter (state) @@ -73,14 +83,7 @@ export const useGridPage = ( const setPage = React.useCallback( (page) => { logger.debug(`Setting page to ${page}`); - - apiRef.current.setState((state) => ({ - ...state, - pagination: applyValidPage({ - ...state.pagination, - page, - }), - })); + apiRef.current.setState(setStatePage(page)); apiRef.current.forceUpdate(); }, [apiRef, logger], @@ -112,24 +115,17 @@ export const useGridPage = ( const stateRestorePreProcessing = React.useCallback< GridPreProcessor - >((params, context) => { - // We apply the constraint even if the page did not change in case the pageSize changed. - const page = context.stateToRestore.pagination?.page ?? gridPageSelector(params.state) - const pageCount = getPageCount(params.state.pagination.rowCount, params.state.pagination.page); - const paginationState = applyValidPage({ - ...params.state.pagination, - pageCount, - page, - }); - - return { - ...params, - state: { - ...params.state, - pagination: paginationState, - }, - }; - }, []); + >( + (params, context) => { + // We apply the constraint even if the page did not change in case the pageSize changed. + const page = + context.stateToRestore.pagination?.page ?? gridPageSelector(apiRef.current.state); + console.log(page, apiRef.current.state.pagination); + apiRef.current.setState(setStatePage(page)); + return params; + }, + [apiRef], + ); useGridRegisterPreProcessor(apiRef, GridPreProcessingGroup.exportState, stateExportPreProcessing); useGridRegisterPreProcessor( diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts index d7af62f0ce59..cbd0fea46698 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts @@ -1,5 +1,5 @@ import * as React from 'react'; -import { GridApiRef } from '../../../models'; +import { GridApiRef, GridState } from '../../../models'; import { DataGridProcessedProps } from '../../../models/props/DataGridProps'; import { GridPageSizeApi } from './gridPaginationInterfaces'; import { GridEvents } from '../../../models/events'; @@ -18,6 +18,16 @@ import { useGridRegisterPreProcessor, } from '../../core/preProcessing'; +const setStatePageSize = + (pageSize: number) => + (state: GridState): GridState => ({ + ...state, + pagination: { + ...state.pagination, + pageSize, + }, + }); + /** * @requires useGridDimensions (event) - can be after * @requires useGridFilter (state) @@ -71,13 +81,7 @@ export const useGridPageSize = ( logger.debug(`Setting page size to ${pageSize}`); - apiRef.current.setState((state) => ({ - ...state, - pagination: { - ...state.pagination, - pageSize, - }, - })); + apiRef.current.setState(setStatePageSize(pageSize)); apiRef.current.forceUpdate(); }, [apiRef, logger], @@ -112,22 +116,16 @@ export const useGridPageSize = ( */ const stateRestorePreProcessing = React.useCallback< GridPreProcessor - >((params, context) => { - if (context.stateToRestore.pagination?.pageSize == null) { + >( + (params, context) => { + const pageSize = context.stateToRestore.pagination?.pageSize; + if (pageSize != null) { + apiRef.current.setState(setStatePageSize(pageSize)); + } return params; - } - - return { - ...params, - state: { - ...params.state, - pagination: { - ...params.state.pagination, - pageSize: context.stateToRestore.pagination.pageSize, - }, - }, - }; - }, []); + }, + [apiRef], + ); useGridRegisterPreProcessor(apiRef, GridPreProcessingGroup.exportState, stateExportPreProcessing); useGridRegisterPreProcessor( diff --git a/packages/grid/_modules_/grid/hooks/features/preferencesPanel/useGridPreferencesPanel.ts b/packages/grid/_modules_/grid/hooks/features/preferencesPanel/useGridPreferencesPanel.ts index 7521d032976c..269e55150f2b 100644 --- a/packages/grid/_modules_/grid/hooks/features/preferencesPanel/useGridPreferencesPanel.ts +++ b/packages/grid/_modules_/grid/hooks/features/preferencesPanel/useGridPreferencesPanel.ts @@ -12,6 +12,9 @@ import { } from '../../core/preProcessing'; import { gridPreferencePanelStateSelector } from './gridPreferencePanelSelector'; +/** + * TODO: Add a single `setPreferencePanel` method to avoid multiple `setState` + */ export const useGridPreferencesPanel = ( apiRef: GridApiRef, props: Pick, @@ -85,19 +88,20 @@ export const useGridPreferencesPanel = ( const stateRestorePreProcessing = React.useCallback< GridPreProcessor - >((params, context) => { - if (context.stateToRestore.preferencePanel == null) { - return params; - } + >( + (params, context) => { + const preferencePanel = context.stateToRestore.preferencePanel; + if (preferencePanel != null) { + apiRef.current.setState((state) => ({ + ...state, + preferencePanel, + })); + } - return { - ...params, - state: { - ...params.state, - preferencePanel: context.stateToRestore.preferencePanel, - }, - }; - }, []); + return params; + }, + [apiRef], + ); useGridRegisterPreProcessor(apiRef, GridPreProcessingGroup.exportState, stateExportPreProcessing); useGridRegisterPreProcessor( diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingUtils.ts b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingUtils.ts index 8d7ae8ad129b..c5129b3fd98b 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingUtils.ts @@ -7,6 +7,7 @@ import { GridSortCellParams, GridSortItem, GridSortModel, + GridState, } from '../../../models'; import { isDesc } from '../../../utils/sortingUtils'; @@ -20,6 +21,16 @@ interface GridParsedSortItem { getSortCellParams: (id: GridRowId) => GridSortCellParams; } +export const setStateSortModel = + (sortModel: GridSortModel) => + (state: GridState): GridState => ({ + ...state, + sorting: { + ...state.sorting, + sortModel, + }, + }); + /** * Transform an item of the sorting model into a method comparing two rows. * @param {GridSortItem} sortItem The sort item we want to apply. diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index af2804d79f98..282892980129 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -21,7 +21,7 @@ import { gridRowIdsSelector, gridRowGroupingNameSelector, gridRowTreeSelector } import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; import { GridSortingMethod, GridSortingMethodCollection } from './gridSortingState'; -import { buildAggregatedSortingApplier } from './gridSortingUtils'; +import { buildAggregatedSortingApplier, setStateSortModel } from './gridSortingUtils'; import { GridPreProcessingGroup, GridPreProcessor, @@ -148,10 +148,7 @@ export const useGridSorting = ( const currentModel = gridSortModelSelector(apiRef.current.state); if (currentModel !== model) { logger.debug(`Setting sort model`); - apiRef.current.setState((state) => ({ - ...state, - sorting: { ...state.sorting, sortModel: model }, - })); + apiRef.current.setState(setStateSortModel(model)); apiRef.current.forceUpdate(); apiRef.current.applySorting(); } @@ -234,20 +231,15 @@ export const useGridSorting = ( GridPreProcessor >( (params, context) => { - if (context.stateToRestore.sorting?.sortModel == null) { + const sortModel = context.stateToRestore.sorting?.sortModel; + if (sortModel == null) { return params; } + apiRef.current.setState(setStateSortModel(sortModel)); return { ...params, callbacks: [...params.callbacks, apiRef.current.applySorting], - state: { - ...params.state, - sorting: { - ...params.state.sorting, - sortModel: context.stateToRestore.sorting.sortModel, - }, - }, }; }, [apiRef], diff --git a/packages/grid/_modules_/grid/hooks/features/statePersistence/GridStatePersistenceApi.ts b/packages/grid/_modules_/grid/hooks/features/statePersistence/GridStatePersistenceApi.ts index 081260f8c835..0edcfdda98ca 100644 --- a/packages/grid/_modules_/grid/hooks/features/statePersistence/GridStatePersistenceApi.ts +++ b/packages/grid/_modules_/grid/hooks/features/statePersistence/GridStatePersistenceApi.ts @@ -1,4 +1,4 @@ -import { GridInitialState, GridState } from '../../../models'; +import { GridInitialState } from '../../../models'; export interface GridStatePersistenceApi { /** @@ -15,8 +15,6 @@ export interface GridStatePersistenceApi { } export interface GridRestoreStatePreProcessingValue { - state: GridState; - /** * Functions to run after the state has been updated but before re-rendering. * This is usually used to apply derived states like `applyFilters` or `applySorting` diff --git a/packages/grid/_modules_/grid/hooks/features/statePersistence/useGridStatePersistence.ts b/packages/grid/_modules_/grid/hooks/features/statePersistence/useGridStatePersistence.ts index 924df791dafd..236443d43a91 100644 --- a/packages/grid/_modules_/grid/hooks/features/statePersistence/useGridStatePersistence.ts +++ b/packages/grid/_modules_/grid/hooks/features/statePersistence/useGridStatePersistence.ts @@ -19,7 +19,6 @@ export const useGridStatePersistence = (apiRef: GridApiRef) => { const response = apiRef.current.unstable_applyPreProcessors( GridPreProcessingGroup.restoreState, { - state: apiRef.current.state, callbacks: [], }, { @@ -27,8 +26,6 @@ export const useGridStatePersistence = (apiRef: GridApiRef) => { }, ); - apiRef.current.setState(response.state); - response.callbacks.forEach((callback) => { callback(); }); diff --git a/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx index 558e7b91ac43..bcfca18fd294 100644 --- a/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx @@ -1,19 +1,19 @@ import * as React from 'react'; import { - DataGridPro, - DataGridProProps, - GridApiRef, - GridPreferencePanelsValue, - useGridApiRef, + DataGridPro, + DataGridProProps, + GridApiRef, + GridPreferencePanelsValue, + useGridApiRef, } from '@mui/x-data-grid-pro'; import { useData } from 'packages/storybook/src/hooks/useData'; -import { createRenderer, screen } from '@mui/monorepo/test/utils'; +import { createRenderer, screen, waitFor } from '@mui/monorepo/test/utils'; import { expect } from 'chai'; -import { getColumnValues} from "../../../../../test/utils/helperFn"; +import { getColumnValues } from '../../../../../test/utils/helperFn'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); -describe.only(' - State Persistence', () => { +describe(' - State Persistence', () => { const { render } = createRenderer(); let apiRef: GridApiRef; @@ -25,121 +25,178 @@ describe.only(' - State Persistence', () => { return (
- +
); }; describe('apiRef: exportState', () => { - it('should return the whole exportable state', () => { - render(); - expect(apiRef.current.exportState()).to.deep.equal({ - filter: { - filterModel: { - items: [], - linkOperator: 'and', - }, - }, - pagination: { - page: 0, - pageSize: 100, - }, - pinnedColumns: { - left: undefined, - right: undefined, - }, - preferencePanel: { - open: false, - }, - sorting: { - sortModel: [], - }, - }); + it('should return the whole exportable state', () => { + render(); + expect(apiRef.current.exportState()).to.deep.equal({ + filter: { + filterModel: { + items: [], + linkOperator: 'and', + }, + }, + pagination: { + page: 0, + pageSize: 100, + }, + pinnedColumns: { + left: undefined, + right: undefined, + }, + preferencePanel: { + open: false, + }, + sorting: { + sortModel: [], + }, + }); + }); + + it('should export the current version of the exportable state', () => { + render(); + apiRef.current.setPageSize(5); + apiRef.current.setPage(1); + apiRef.current.setPinnedColumns({ left: ['id'] }); + apiRef.current.showPreferences(GridPreferencePanelsValue.filters); + apiRef.current.setSortModel([{ field: 'id', sort: 'desc' }]); + apiRef.current.setFilterModel({ + items: [{ columnField: 'id', operatorValue: '<', value: '97' }], + }); + expect(apiRef.current.exportState()).to.deep.equal({ + filter: { + filterModel: { + items: [{ columnField: 'id', operatorValue: '<', value: '97' }], + }, + }, + pagination: { + page: 1, + pageSize: 5, + }, + pinnedColumns: { + left: ['id'], + }, + preferencePanel: { + open: true, + openedPanelValue: GridPreferencePanelsValue.filters, + }, + sorting: { + sortModel: [{ field: 'id', sort: 'desc' }], + }, + }); + }); + }); + + describe('apiRef: restoreState', () => { + it('should restore the whole exportable state', () => { + render(); + + apiRef.current.restoreState({ + filter: { + filterModel: { + items: [{ columnField: 'id', operatorValue: '<', value: '97' }], + }, + }, + pagination: { + page: 1, + pageSize: 5, + }, + pinnedColumns: { + left: ['id'], + }, + preferencePanel: { + open: true, + openedPanelValue: GridPreferencePanelsValue.filters, + }, + sorting: { + sortModel: [{ field: 'id', sort: 'desc' }], + }, + }); + + // Test sorting + filtering + pagination + expect(getColumnValues(0)).to.deep.equal(['91', '90', '89', '88', '87']); + // Preference panel + expect(screen.getByRole('button', { name: /Add Filter/i })).to.not.equal(null); + // Pinning + expect( + document.querySelector('.MuiDataGrid-pinnedColumnHeaders--left')?.textContent, + ).to.deep.equal('id'); + }); + + it('should restore partial exportable state', () => { + render(); + + apiRef.current.restoreState({ + pagination: { + page: 1, + pageSize: 5, + }, + }); + + expect(getColumnValues(0)).to.deep.equal(['5', '6', '7', '8', '9']); + }); + + it('should restore controlled sub-state', () => { + const ControlledTest = () => { + const [page, setPage] = React.useState(0); + + return ( + { + setPage(newPage); + }} + /> + ); + }; + + render(); + apiRef.current.restoreState({ + pagination: { + page: 1, + pageSize: 5, + }, }); - it('should export the current version of the exportable state', () => { - render(); - apiRef.current.setPageSize(5) - apiRef.current.setPage(1) - apiRef.current.setPinnedColumns({ left: ['id']}) - apiRef.current.showPreferences(GridPreferencePanelsValue.filters) - apiRef.current.setSortModel([{ field: 'id', sort: 'desc' }]) - apiRef.current.setFilterModel({ items: [{ columnField: 'id', operatorValue: '<', value: '97' }]}) - expect(apiRef.current.exportState()).to.deep.equal({ - filter: { - filterModel: { - items: [{ columnField: 'id', operatorValue: '<', value: '97' }], - }, - }, - pagination: { - page: 1, - pageSize: 5, - }, - pinnedColumns: { - left: ['id'], - }, - preferencePanel: { - open: true, - openedPanelValue: GridPreferencePanelsValue.filters, - }, - sorting: { - sortModel: [{ field: 'id', sort: 'desc' }], - }, - }); - }) - }) - - describe('apiRef: restoreState', () => { - it.only('should restore the whole exportable state', () => { - render(); - - console.log('_________________') - - apiRef.current.restoreState({ - filter: { - filterModel: { - items: [{ columnField: 'id', operatorValue: '<', value: '97' }], - }, - }, - pagination: { - page: 1, - pageSize: 5, - }, - pinnedColumns: { - left: ['id'], - }, - preferencePanel: { - open: true, - openedPanelValue: GridPreferencePanelsValue.filters, - }, - sorting: { - sortModel: [{ field: 'id', sort: 'desc' }], - }, - }) - - console.log('______') - - // Test sorting + filtering + pagination - expect(getColumnValues(0)).to.deep.equal(['91', '90', '89', '88', '87']) - // Preference panel - expect(screen.getByRole('button', { name: /Add Filter/i })).to.not.equal(null) - // Pinning - expect( - document.querySelector('.MuiDataGrid-pinnedColumnHeaders--left')?.textContent, - ).to.deep.equal('id'); - }) - - it('should restore partial exportable state', () => { - render(); - - apiRef.current.restoreState({ - pagination: { - page: 1, - pageSize: 5, - }, - }) - - expect(getColumnValues(0)).to.deep.equal(['5', '6', '7', '8', '9']) - }) - }) + waitFor(() => { + expect(getColumnValues(0)).to.deep.equal(['5', '6', '7', '8', '9']); + }); + }); + + // Not sure how to make that one work, if the `pageSize` update is async, we try to update the `page` with an outdated `pageSize` which can cause problem. + // it('should restore when controlled pageSize but not page', () => { + // const ControlledTest = () => { + // const [pageSize, setPageSize] = React.useState(100) + // + // return ( + // { + // setPageSize(newPageSize) + // }} /> + // ) + // } + // + // render() + // apiRef.current.restoreState({ + // pagination: { + // page: 1, + // pageSize: 5, + // }, + // }) + // + // waitFor(() => { + // expect(getColumnValues(0)).to.deep.equal(['5', '6', '7', '8', '9']) + // }) + // }) + }); }); From 09df64877bb42b72c3d712d72bcec938fe302c50 Mon Sep 17 00:00:00 2001 From: delangle Date: Fri, 14 Jan 2022 11:11:27 +0100 Subject: [PATCH 12/26] Work --- .../pages/components/data-grid/state/state.md | 21 ++++++++++++++++--- .../hooks/features/pagination/useGridPage.ts | 1 - .../statePersistence.DataGridPro.test.tsx | 18 +++++++--------- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/docs/src/pages/components/data-grid/state/state.md b/docs/src/pages/components/data-grid/state/state.md index 492f9f8e54a0..c25d2a3245a0 100644 --- a/docs/src/pages/components/data-grid/state/state.md +++ b/docs/src/pages/components/data-grid/state/state.md @@ -56,17 +56,32 @@ Some selectors are yet to be documented. ## Save and restore the state -You can export the current state of the grid by calling `apiRef.current.exportState()` and restore it by either passing it to the `initialState` prop or to the `apiRef.current.restoreState()` method. +The current state of the grid can be exported using `apiRef.current.exportState()`. +It can then be restored by either passing it to the `initialState` prop or to the `apiRef.current.restoreState()` method. ### Restore the state with `initialState` > ⚠️ If you restore the page using `initialState` before the data are fetched, the grid will automatically move to the 1st page. -{{"demo": "pages/components/data-grid/state/RestoreStateInitialState.js", "bg": "inline"}} +{{"demo": "pages/components/data-grid/state/RestoreStateInitialState.js", "bg": "inline", "defaultCodeOpen": false}} ### Restore the state with `apiRef` [](https://mui.com/store/items/material-ui-pro/) -{{"demo": "pages/components/data-grid/state/RestoreStateApiRef.js", "bg": "inline"}} +{{"demo": "pages/components/data-grid/state/RestoreStateApiRef.js", "bg": "inline", "defaultCodeOpen": false}} + +### Restore part of the state + +It is possible to only pass some keys of the state to the `apiRef.current.restoreState()` method. +For instance, to only restore the pinned columns: + +```ts +apiRef.current.restoreState({ + pinnedColumns: ['brand'], +}); +``` + +**Note**: Most of the state keys are not fully independent. +Restoring the pagination without restoring the filters or the sorting will work, but the rows displayed will not be the same as before. ## API diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts index ed5cf35a625a..5880d54a6ebb 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts @@ -120,7 +120,6 @@ export const useGridPage = ( // We apply the constraint even if the page did not change in case the pageSize changed. const page = context.stateToRestore.pagination?.page ?? gridPageSelector(apiRef.current.state); - console.log(page, apiRef.current.state.pagination); apiRef.current.setState(setStatePage(page)); return params; }, diff --git a/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx index bcfca18fd294..a5d57845143d 100644 --- a/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx @@ -7,14 +7,14 @@ import { useGridApiRef, } from '@mui/x-data-grid-pro'; import { useData } from 'packages/storybook/src/hooks/useData'; -import { createRenderer, screen, waitFor } from '@mui/monorepo/test/utils'; +import { createRenderer, screen } from '@mui/monorepo/test/utils'; import { expect } from 'chai'; import { getColumnValues } from '../../../../../test/utils/helperFn'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); describe(' - State Persistence', () => { - const { render } = createRenderer(); + const { render, clock } = createRenderer({ clock: 'fake' }); let apiRef: GridApiRef; @@ -168,14 +168,12 @@ describe(' - State Persistence', () => { pageSize: 5, }, }); - - waitFor(() => { - expect(getColumnValues(0)).to.deep.equal(['5', '6', '7', '8', '9']); - }); + clock.runToLast(); + expect(getColumnValues(0)).to.deep.equal(['5', '6', '7', '8', '9']); }); // Not sure how to make that one work, if the `pageSize` update is async, we try to update the `page` with an outdated `pageSize` which can cause problem. - // it('should restore when controlled pageSize but not page', () => { + // it.only('should restore when controlled pageSize but not page', () => { // const ControlledTest = () => { // const [pageSize, setPageSize] = React.useState(100) // @@ -193,10 +191,8 @@ describe(' - State Persistence', () => { // pageSize: 5, // }, // }) - // - // waitFor(() => { - // expect(getColumnValues(0)).to.deep.equal(['5', '6', '7', '8', '9']) - // }) + // clock.runToLast(); + // expect(getColumnValues(0)).to.deep.equal(['5', '6', '7', '8', '9']); // }) }); }); From 8ac284c0a6e86082ddc07aeee1f40e0026a5adc0 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 18 Jan 2022 16:10:55 +0100 Subject: [PATCH 13/26] Work --- docs/pages/api-docs/data-grid/grid-api.md | 12 ++++++------ 1 file changed, 6 insertions(+), 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 09e8b2eebefe..a571a4942742 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -12,14 +12,14 @@ import { GridApi } from '@mui/x-data-grid-pro'; | Name | Type | Description | | :--------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| addRowGroupingCriteria | (groupingCriteriaField: string, groupingIndex?: number) => void | Adds the field to the row grouping model. | +| addRowGroupingCriteria | (groupingCriteriaField: string, groupingIndex?: number) => void | Adds the field to the row grouping model. | | applySorting | () => void | Applies the current sort model to the rows. | | commitCellChange | (params: GridCommitCellChangeParams, event?: MuiBaseEvent) => boolean \| Promise<boolean> | Updates the field at the given id with the value stored in the edit row model. | | commitRowChange | (id: GridRowId, event?: MuiBaseEvent) => 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](/api/data-grid/grid-filter-item/). | | exportDataAsCsv | (options?: GridCsvExportOptions) => void | Downloads and exports a CSV of the grid's data. | | exportDataAsPrint | (options?: GridPrintExportOptions) => void | Print the grid's data. | -| exportState | () => GridInitialState | Generates a serializable object containing the exportable parts of the DataGrid state.
These values can then be passed to the `initialState` prop or injected using the `restoreState` method. | +| exportState | () => GridInitialState | Generates a serializable object containing the exportable parts of the DataGrid state.
These values can then be passed to the `initialState` prop or injected using the `restoreState` method. | | forceUpdate | () => void | 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. | @@ -62,9 +62,9 @@ import { GridApi } from '@mui/x-data-grid-pro'; | isRowSelected | (id: GridRowId) => boolean | Determines if a row is selected or not. | | pinColumn | (field: string, side: GridPinnedPosition) => void | Pins a column to the left or right side of the grid. | | publishEvent | GridEventPublisher | Emits an event. | -| removeRowGroupingCriteria | (groupingCriteriaField: string) => void | sRemove the field from the row grouping model. | +| removeRowGroupingCriteria | (groupingCriteriaField: string) => void | sRemove the field from the row grouping model. | | resize | () => void | Triggers a resize of the component and recalculation of width and height. | -| restoreState | (stateToRestore: GridInitialState) => void | Inject the given values into the state of the DataGrid. | +| restoreState | (stateToRestore: GridInitialState) => void | Inject the given values into the state of the DataGrid. | | 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. | @@ -85,8 +85,8 @@ import { GridApi } from '@mui/x-data-grid-pro'; | setPageSize | (pageSize: number) => void | Sets the number of displayed rows to the value given by `pageSize`. | | setPinnedColumns | (pinnedColumns: GridPinnedColumns) => void | Changes the pinned columns. | | setRowChildrenExpansion | (id: GridRowId, isExpanded: boolean) => void | Expand or collapse a row children. | -| setRowGroupingCriteriaIndex | (groupingCriteriaField: string, groupingIndex: number) => void | Sets the grouping index of a grouping criteria. | -| setRowGroupingModel | (model: GridRowGroupingModel) => void | Sets the columns to use as grouping criteria. | +| setRowGroupingCriteriaIndex | (groupingCriteriaField: string, groupingIndex: number) => void | Sets the grouping index of a grouping criteria. | +| setRowGroupingModel | (model: GridRowGroupingModel) => void | Sets the columns to use as grouping criteria. | | setRowMode | (id: GridRowId, mode: GridRowMode) => void | Sets the mode of a row. | | setRows | (rows: GridRowModel[]) => 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. | From 3dbf93f8a9e56936ecfc6a794eabe86514983895 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 18 Jan 2022 16:14:31 +0100 Subject: [PATCH 14/26] Work --- .../grid/hooks/features/rowGrouping/createGroupingColDef.tsx | 2 +- .../grid/hooks/features/rowGrouping/useGridRowGrouping.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/rowGrouping/createGroupingColDef.tsx b/packages/grid/_modules_/grid/hooks/features/rowGrouping/createGroupingColDef.tsx index ce4e976c595e..2cee7d03a469 100644 --- a/packages/grid/_modules_/grid/hooks/features/rowGrouping/createGroupingColDef.tsx +++ b/packages/grid/_modules_/grid/hooks/features/rowGrouping/createGroupingColDef.tsx @@ -9,7 +9,7 @@ import { GridValueGetterFullParams, GridComparatorFn, } from '../../../models'; -import { GridColumnRawLookup } from '../columns/gridColumnsState'; +import { GridColumnRawLookup } from '../columns/gridColumnsInterfaces'; import { GridGroupingCriteriaCell } from '../../../components/cell/GridGroupingCriteriaCell'; import { GridGroupingColumnLeafCell } from '../../../components/cell/GridGroupingColumnLeafCell'; import { diff --git a/packages/grid/_modules_/grid/hooks/features/rowGrouping/useGridRowGrouping.tsx b/packages/grid/_modules_/grid/hooks/features/rowGrouping/useGridRowGrouping.tsx index d33343953475..2731bd961052 100644 --- a/packages/grid/_modules_/grid/hooks/features/rowGrouping/useGridRowGrouping.tsx +++ b/packages/grid/_modules_/grid/hooks/features/rowGrouping/useGridRowGrouping.tsx @@ -32,7 +32,7 @@ import { } from './createGroupingColDef'; import { isDeepEqual } from '../../../utils/utils'; import { GridPreProcessingGroup, useGridRegisterPreProcessor } from '../../core/preProcessing'; -import { GridColumnRawLookup, GridColumnsRawState } from '../columns/gridColumnsState'; +import { GridColumnRawLookup, GridColumnsRawState } from '../columns/gridColumnsInterfaces'; import { useGridRegisterFilteringMethod } from '../filter/useGridRegisterFilteringMethod'; import { GridFilteringMethod } from '../filter/gridFilterState'; import { gridRowIdsSelector, gridRowTreeSelector } from '../rows'; From 7a7ebd11ed820b09b5a319a07b0e72d032cca2bb Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 18 Jan 2022 18:02:35 +0100 Subject: [PATCH 15/26] Work --- docs/src/pages/components/data-grid/state/state.md | 4 ++-- .../grid/hooks/core/preProcessing/gridPreProcessingApi.ts | 4 ++-- .../grid/_modules_/grid/hooks/utils/useGridStateInit.ts | 8 ++++++-- packages/grid/_modules_/grid/models/gridState.ts | 8 -------- scripts/x-data-grid-pro.exports.json | 2 -- scripts/x-data-grid.exports.json | 2 -- 6 files changed, 10 insertions(+), 18 deletions(-) diff --git a/docs/src/pages/components/data-grid/state/state.md b/docs/src/pages/components/data-grid/state/state.md index c25d2a3245a0..c37d1b0c3ad2 100644 --- a/docs/src/pages/components/data-grid/state/state.md +++ b/docs/src/pages/components/data-grid/state/state.md @@ -69,7 +69,7 @@ It can then be restored by either passing it to the `initialState` prop or to th {{"demo": "pages/components/data-grid/state/RestoreStateApiRef.js", "bg": "inline", "defaultCodeOpen": false}} -### Restore part of the state +#### Restore part of the state It is possible to only pass some keys of the state to the `apiRef.current.restoreState()` method. For instance, to only restore the pinned columns: @@ -81,7 +81,7 @@ apiRef.current.restoreState({ ``` **Note**: Most of the state keys are not fully independent. -Restoring the pagination without restoring the filters or the sorting will work, but the rows displayed will not be the same as before. +Restoring the pagination without restoring the filters or the sorting will work, but the rows displayed after the re-import will not be the same as before the export. ## API diff --git a/packages/grid/_modules_/grid/hooks/core/preProcessing/gridPreProcessingApi.ts b/packages/grid/_modules_/grid/hooks/core/preProcessing/gridPreProcessingApi.ts index 9f5c007ae623..41506dad1867 100644 --- a/packages/grid/_modules_/grid/hooks/core/preProcessing/gridPreProcessingApi.ts +++ b/packages/grid/_modules_/grid/hooks/core/preProcessing/gridPreProcessingApi.ts @@ -1,7 +1,7 @@ import { GridCellIndexCoordinates, GridColDef, - GridPartialInitialState, + GridInitialState, GridScrollParams, } from '../../../models'; import { @@ -39,7 +39,7 @@ interface GridPreProcessingGroupLookup { }; [GridPreProcessingGroup.filteringMethod]: { value: GridFilteringMethodCollection }; [GridPreProcessingGroup.sortingMethod]: { value: GridSortingMethodCollection }; - [GridPreProcessingGroup.exportState]: { value: GridPartialInitialState }; + [GridPreProcessingGroup.exportState]: { value: GridInitialState }; [GridPreProcessingGroup.restoreState]: { value: GridRestoreStatePreProcessingValue; context: GridRestoreStatePreProcessingContext; diff --git a/packages/grid/_modules_/grid/hooks/utils/useGridStateInit.ts b/packages/grid/_modules_/grid/hooks/utils/useGridStateInit.ts index 1116b8fe64b8..7a8362ae3c4e 100644 --- a/packages/grid/_modules_/grid/hooks/utils/useGridStateInit.ts +++ b/packages/grid/_modules_/grid/hooks/utils/useGridStateInit.ts @@ -1,10 +1,14 @@ import * as React from 'react'; import { GridApiRef } from '../../models/api/gridApiRef'; -import { GridState, GridPartialState } from '../../models/gridState'; +import { GridState } from '../../models/gridState'; + +type DeepPartial = { + [P in keyof T]?: DeepPartial; +}; export const useGridStateInit = ( apiRef: GridApiRef, - callback: (state: GridPartialState) => GridPartialState, + callback: (state: DeepPartial) => DeepPartial, ) => { const isInitialized = React.useRef(false); diff --git a/packages/grid/_modules_/grid/models/gridState.ts b/packages/grid/_modules_/grid/models/gridState.ts index a3011fcf30fc..b26b9afac6c2 100644 --- a/packages/grid/_modules_/grid/models/gridState.ts +++ b/packages/grid/_modules_/grid/models/gridState.ts @@ -57,11 +57,3 @@ export interface GridInitialState { rowGrouping?: GridRowGroupingInitialState; pinnedColumns?: GridColumnPinningState; } - -type DeepPartial = { - [P in keyof T]?: DeepPartial; -}; - -export type GridPartialState = DeepPartial; - -export type GridPartialInitialState = DeepPartial; diff --git a/scripts/x-data-grid-pro.exports.json b/scripts/x-data-grid-pro.exports.json index b168b8ef2086..a3fcce5c2021 100644 --- a/scripts/x-data-grid-pro.exports.json +++ b/scripts/x-data-grid-pro.exports.json @@ -257,8 +257,6 @@ { "name": "GridPanelProps", "kind": "Interface" }, { "name": "GridPanelWrapper", "kind": "Function" }, { "name": "GridParamsApi", "kind": "Interface" }, - { "name": "GridPartialInitialState", "kind": "TypeAlias" }, - { "name": "GridPartialState", "kind": "TypeAlias" }, { "name": "GridPinnedColumns", "kind": "Interface" }, { "name": "gridPinnedColumnsSelector", "kind": "Variable" }, { "name": "GridPinnedPosition", "kind": "Enum" }, diff --git a/scripts/x-data-grid.exports.json b/scripts/x-data-grid.exports.json index 262bb3f0b4c5..eb9c706eda16 100644 --- a/scripts/x-data-grid.exports.json +++ b/scripts/x-data-grid.exports.json @@ -257,8 +257,6 @@ { "name": "GridPanelProps", "kind": "Interface" }, { "name": "GridPanelWrapper", "kind": "Function" }, { "name": "GridParamsApi", "kind": "Interface" }, - { "name": "GridPartialInitialState", "kind": "TypeAlias" }, - { "name": "GridPartialState", "kind": "TypeAlias" }, { "name": "GridPinnedColumns", "kind": "Interface" }, { "name": "gridPinnedColumnsSelector", "kind": "Variable" }, { "name": "GridPinnedPosition", "kind": "Enum" }, From 18722fd75d09fad4d27f600a453435cdd11efbc9 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 19 Jan 2022 17:58:42 +0100 Subject: [PATCH 16/26] Work --- 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 b5d2bd333aab..1be0ce154234 100644 --- a/docs/pages/api-docs/data-grid/grid-api.md +++ b/docs/pages/api-docs/data-grid/grid-api.md @@ -75,7 +75,7 @@ import { GridApi } from '@mui/x-data-grid-pro'; | setColumnHeaderFocus | (field: string, event?: MuiBaseEvent) => 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`. | -| setColumnVisibilityModel | (model: GridColumnVisibilityModel) => void | Sets the column visibility model to the one given by `model`. | +| setColumnVisibilityModel | (model: GridColumnVisibilityModel) => void | Sets the column visibility model to the one given by `model`. | | 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?: MuiBaseEvent) => void | Sets the value of the edit cell.
Commonly used inside the edit cell component. | From d179776319daf00ceea57ac637d78baa19855178 Mon Sep 17 00:00:00 2001 From: delangle Date: Thu, 20 Jan 2022 15:34:16 +0100 Subject: [PATCH 17/26] Add columnVisibilityModel and rowGroupingModel --- .../features/columns/gridColumnsUtils.ts | 8 ++ .../hooks/features/columns/useGridColumns.ts | 68 +++++++++++++- .../rowGrouping/gridRowGroupingUtils.ts | 9 ++ .../rowGrouping/useGridRowGrouping.tsx | 57 ++++++++++- .../x-data-grid-generator/src/useMovieData.ts | 2 +- .../statePersistence.DataGridPro.test.tsx | 94 +++++++++++++------ 6 files changed, 202 insertions(+), 36 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/columns/gridColumnsUtils.ts b/packages/grid/_modules_/grid/hooks/features/columns/gridColumnsUtils.ts index f393408771ed..1c14b37a49fe 100644 --- a/packages/grid/_modules_/grid/hooks/features/columns/gridColumnsUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/columns/gridColumnsUtils.ts @@ -11,6 +11,7 @@ import { GridColDef, GridColType, GridColumnTypesRecord, + GridState, GridStateColDef, } from '../../../models'; import { GridPreProcessingGroup } from '../../core/preProcessing'; @@ -196,3 +197,10 @@ export const createColumnsState = ({ apiRef.current.getRootDimensions?.()?.viewportInnerSize.width ?? 0, ); }; + +export const setColumnsState = + (columnsState: GridColumnsState) => + (state: GridState): GridState => ({ + ...state, + columns: columnsState, + }); diff --git a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts index 4c8721c38b47..a8138a1defaa 100644 --- a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts +++ b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts @@ -21,9 +21,18 @@ import { import { DataGridProcessedProps } from '../../../models/props/DataGridProps'; import { useGridStateInit } from '../../utils/useGridStateInit'; import { GridColumnVisibilityChangeParams } from '../../../models'; -import { GridPreProcessingGroup } from '../../core/preProcessing'; +import { + GridPreProcessingGroup, + GridPreProcessor, + useGridRegisterPreProcessor, +} from '../../core/preProcessing'; import { GridColumnsState, GridColumnVisibilityModel } from './gridColumnsInterfaces'; -import { hydrateColumnsWidth, computeColumnTypes, createColumnsState } from './gridColumnsUtils'; +import { + hydrateColumnsWidth, + computeColumnTypes, + createColumnsState, + setColumnsState, +} from './gridColumnsUtils'; /** * @requires useGridParamsApi (method) @@ -88,7 +97,7 @@ export function useGridColumns( (columnsState: GridColumnsState) => { logger.debug('Updating columns state.'); - apiRef.current.setState((state) => ({ ...state, columns: columnsState })); + apiRef.current.setState(setColumnsState(columnsState)); apiRef.current.forceUpdate(); apiRef.current.publishEvent(GridEvents.columnsChange, columnsState.all); }, @@ -269,6 +278,59 @@ export function useGridColumns( useGridApiMethod(apiRef, columnApi, 'GridColumnApi'); + /** + * PRE-PROCESSING + */ + const stateExportPreProcessing = React.useCallback< + GridPreProcessor + >( + (prevState) => { + if (!shouldUseVisibleColumnModel) { + return prevState; + } + + return { + ...prevState, + columns: { + columnVisibilityModel: gridColumnVisibilityModelSelector(apiRef.current.state), + }, + }; + }, + [apiRef, shouldUseVisibleColumnModel], + ); + + const stateRestorePreProcessing = React.useCallback< + GridPreProcessor + >( + (params, context) => { + if (!shouldUseVisibleColumnModel) { + return params; + } + + const columnVisibilityModel = context.stateToRestore.columns?.columnVisibilityModel; + if (columnVisibilityModel != null) { + const columnsState = createColumnsState({ + apiRef, + columnsTypes, + columnsToUpsert: [], + shouldRegenColumnVisibilityModelFromColumns: false, + currentColumnVisibilityModel: columnVisibilityModel, + reset: false, + }); + apiRef.current.setState(setColumnsState(columnsState)); + } + return params; + }, + [apiRef, shouldUseVisibleColumnModel, columnsTypes], + ); + + useGridRegisterPreProcessor(apiRef, GridPreProcessingGroup.exportState, stateExportPreProcessing); + useGridRegisterPreProcessor( + apiRef, + GridPreProcessingGroup.restoreState, + stateRestorePreProcessing, + ); + /** * EVENTS */ diff --git a/packages/grid/_modules_/grid/hooks/features/rowGrouping/gridRowGroupingUtils.ts b/packages/grid/_modules_/grid/hooks/features/rowGrouping/gridRowGroupingUtils.ts index be89667aaf8b..d1ffe2946f69 100644 --- a/packages/grid/_modules_/grid/hooks/features/rowGrouping/gridRowGroupingUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/rowGrouping/gridRowGroupingUtils.ts @@ -3,10 +3,12 @@ import { GridRowId, GridRowTreeConfig, GridRowTreeNodeConfig, + GridState, } from '../../../models'; import { GridFilterState } from '../filter'; import { DataGridProProcessedProps } from '../../../models/props/DataGridProProps'; import { GridAggregatedFilterItemApplier } from '../filter/gridFilterState'; +import { GridRowGroupingModel } from './gridRowGroupingInterfaces'; export const GRID_ROW_GROUPING_SINGLE_GROUPING_FIELD = '__row_group_by_columns_group__'; @@ -143,3 +145,10 @@ export const getColDefOverrides = ( return groupingColDefProp; }; + +export const setStateRowGroupingModel = + (rowGroupingModel: GridRowGroupingModel) => + (state: GridState): GridState => ({ + ...state, + rowGrouping: { ...state.rowGrouping, model: rowGroupingModel }, + }); diff --git a/packages/grid/_modules_/grid/hooks/features/rowGrouping/useGridRowGrouping.tsx b/packages/grid/_modules_/grid/hooks/features/rowGrouping/useGridRowGrouping.tsx index 2731bd961052..354a973c8da9 100644 --- a/packages/grid/_modules_/grid/hooks/features/rowGrouping/useGridRowGrouping.tsx +++ b/packages/grid/_modules_/grid/hooks/features/rowGrouping/useGridRowGrouping.tsx @@ -25,13 +25,18 @@ import { getColDefOverrides, GROUPING_COLUMNS_FEATURE_NAME, isGroupingColumn, + setStateRowGroupingModel, } from './gridRowGroupingUtils'; import { createGroupingColDefForOneGroupingCriteria, createGroupingColDefForAllGroupingCriteria, } from './createGroupingColDef'; import { isDeepEqual } from '../../../utils/utils'; -import { GridPreProcessingGroup, useGridRegisterPreProcessor } from '../../core/preProcessing'; +import { + GridPreProcessingGroup, + GridPreProcessor, + useGridRegisterPreProcessor, +} from '../../core/preProcessing'; import { GridColumnRawLookup, GridColumnsRawState } from '../columns/gridColumnsInterfaces'; import { useGridRegisterFilteringMethod } from '../filter/useGridRegisterFilteringMethod'; import { GridFilteringMethod } from '../filter/gridFilterState'; @@ -363,10 +368,7 @@ export const useGridRowGrouping = ( (model) => { const currentModel = gridRowGroupingModelSelector(apiRef.current.state); if (currentModel !== model) { - apiRef.current.setState((state) => ({ - ...state, - rowGrouping: { ...state.rowGrouping, model }, - })); + apiRef.current.setState(setStateRowGroupingModel(model)); updateRowGrouping(); apiRef.current.forceUpdate(); } @@ -437,6 +439,51 @@ export const useGridRowGrouping = ( 'GridRowGroupingApi', ); + /** + * PRE-PROCESSING + */ + const stateExportPreProcessing = React.useCallback< + GridPreProcessor + >( + (prevState) => { + if (props.disableRowGrouping) { + return prevState; + } + + return { + ...prevState, + rowGrouping: { + model: gridRowGroupingModelSelector(apiRef.current.state), + }, + }; + }, + [apiRef, props.disableRowGrouping], + ); + + const stateRestorePreProcessing = React.useCallback< + GridPreProcessor + >( + (params, context) => { + if (props.disableRowGrouping) { + return params; + } + + const rowGroupingModel = context.stateToRestore.rowGrouping?.model; + if (rowGroupingModel != null) { + apiRef.current.setState(setStateRowGroupingModel(rowGroupingModel)); + } + return params; + }, + [apiRef, props.disableRowGrouping], + ); + + useGridRegisterPreProcessor(apiRef, GridPreProcessingGroup.exportState, stateExportPreProcessing); + useGridRegisterPreProcessor( + apiRef, + GridPreProcessingGroup.restoreState, + stateRestorePreProcessing, + ); + /** * EVENTS */ diff --git a/packages/grid/x-data-grid-generator/src/useMovieData.ts b/packages/grid/x-data-grid-generator/src/useMovieData.ts index 9dbcf0ae44b9..068bcc8bfa34 100644 --- a/packages/grid/x-data-grid-generator/src/useMovieData.ts +++ b/packages/grid/x-data-grid-generator/src/useMovieData.ts @@ -285,7 +285,7 @@ const ROWS: GridRowModel[] = [ { id: 20, title: 'Minions', - gross: 11159398397, + gross: 1159398397, director: 'Pierre Coffin & Kyle Balda', company: 'Universal Pictures', year: 2015, diff --git a/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx index a5d57845143d..b7788e14dc30 100644 --- a/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx @@ -6,10 +6,10 @@ import { GridPreferencePanelsValue, useGridApiRef, } from '@mui/x-data-grid-pro'; -import { useData } from 'packages/storybook/src/hooks/useData'; import { createRenderer, screen } from '@mui/monorepo/test/utils'; +import { useMovieData } from '@mui/x-data-grid-generator'; import { expect } from 'chai'; -import { getColumnValues } from '../../../../../test/utils/helperFn'; +import { getColumnHeadersTextContent, getColumnValues } from '../../../../../test/utils/helperFn'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); @@ -19,19 +19,27 @@ describe(' - State Persistence', () => { let apiRef: GridApiRef; const TestCase = (props: Omit) => { - const basicData = useData(100, 2); + const data = useMovieData(); apiRef = useGridApiRef(); return (
); @@ -41,6 +49,9 @@ describe(' - State Persistence', () => { it('should return the whole exportable state', () => { render(); expect(apiRef.current.exportState()).to.deep.equal({ + columns: { + columnVisibilityModel: {}, + }, filter: { filterModel: { items: [], @@ -58,6 +69,9 @@ describe(' - State Persistence', () => { preferencePanel: { open: false, }, + rowGrouping: { + model: [], + }, sorting: { sortModel: [], }, @@ -66,33 +80,42 @@ describe(' - State Persistence', () => { it('should export the current version of the exportable state', () => { render(); - apiRef.current.setPageSize(5); + apiRef.current.setPageSize(2); apiRef.current.setPage(1); - apiRef.current.setPinnedColumns({ left: ['id'] }); + apiRef.current.setPinnedColumns({ left: ['company'] }); apiRef.current.showPreferences(GridPreferencePanelsValue.filters); - apiRef.current.setSortModel([{ field: 'id', sort: 'desc' }]); + apiRef.current.setSortModel([{ field: 'director', sort: 'asc' }]); apiRef.current.setFilterModel({ - items: [{ columnField: 'id', operatorValue: '<', value: '97' }], + items: [{ columnField: 'gross', operatorValue: '>', value: '1500000000' }], }); + apiRef.current.setRowGroupingModel(['director']); + apiRef.current.setColumnVisibilityModel({ year: false }); + expect(apiRef.current.exportState()).to.deep.equal({ + columns: { + columnVisibilityModel: { year: false }, + }, filter: { filterModel: { - items: [{ columnField: 'id', operatorValue: '<', value: '97' }], + items: [{ columnField: 'gross', operatorValue: '>', value: '1500000000' }], }, }, pagination: { page: 1, - pageSize: 5, + pageSize: 2, }, pinnedColumns: { - left: ['id'], + left: ['company'], }, preferencePanel: { open: true, openedPanelValue: GridPreferencePanelsValue.filters, }, sorting: { - sortModel: [{ field: 'id', sort: 'desc' }], + sortModel: [{ field: 'director', sort: 'asc' }], + }, + rowGrouping: { + model: ['director'], }, }); }); @@ -103,35 +126,52 @@ describe(' - State Persistence', () => { render(); apiRef.current.restoreState({ + columns: { + columnVisibilityModel: { year: false }, + }, filter: { filterModel: { - items: [{ columnField: 'id', operatorValue: '<', value: '97' }], + items: [{ columnField: 'gross', operatorValue: '>', value: '1500000000' }], }, }, pagination: { page: 1, - pageSize: 5, + pageSize: 2, }, pinnedColumns: { - left: ['id'], + left: ['company'], }, preferencePanel: { open: true, openedPanelValue: GridPreferencePanelsValue.filters, }, sorting: { - sortModel: [{ field: 'id', sort: 'desc' }], + sortModel: [{ field: 'director', sort: 'asc' }], + }, + rowGrouping: { + model: ['director'], }, }); - // Test sorting + filtering + pagination - expect(getColumnValues(0)).to.deep.equal(['91', '90', '89', '88', '87']); + // Pagination + expect(getColumnValues(0)).to.deep.equal(['', 'Disney Studios', '', 'Universal Pictures']); + + // Sorting and row grouping + expect(getColumnValues(1)).to.deep.equal(['J. J. Abrams (1)', '', 'Colin Trevorrow (1)', '']); + + // Filtering + expect(getColumnValues(3)).to.deep.equal(['', '2,068,223,624$', '', '1,671,713,208$']); + // Preference panel expect(screen.getByRole('button', { name: /Add Filter/i })).to.not.equal(null); + + // Columns visibility + expect(getColumnHeadersTextContent()).to.not.include('Year'); + // Pinning expect( document.querySelector('.MuiDataGrid-pinnedColumnHeaders--left')?.textContent, - ).to.deep.equal('id'); + ).to.deep.equal('Company'); }); it('should restore partial exportable state', () => { @@ -140,11 +180,11 @@ describe(' - State Persistence', () => { apiRef.current.restoreState({ pagination: { page: 1, - pageSize: 5, + pageSize: 2, }, }); - expect(getColumnValues(0)).to.deep.equal(['5', '6', '7', '8', '9']); + expect(getColumnValues(0)).to.deep.equal(['Titanic', 'Star Wars: The Force Awakens']); }); it('should restore controlled sub-state', () => { @@ -165,15 +205,15 @@ describe(' - State Persistence', () => { apiRef.current.restoreState({ pagination: { page: 1, - pageSize: 5, + pageSize: 2, }, }); clock.runToLast(); - expect(getColumnValues(0)).to.deep.equal(['5', '6', '7', '8', '9']); + expect(getColumnValues(0)).to.deep.equal(['Titanic', 'Star Wars: The Force Awakens']); }); // Not sure how to make that one work, if the `pageSize` update is async, we try to update the `page` with an outdated `pageSize` which can cause problem. - // it.only('should restore when controlled pageSize but not page', () => { + // it('should restore when controlled pageSize but not page', () => { // const ControlledTest = () => { // const [pageSize, setPageSize] = React.useState(100) // @@ -188,11 +228,11 @@ describe(' - State Persistence', () => { // apiRef.current.restoreState({ // pagination: { // page: 1, - // pageSize: 5, + // pageSize: 2, // }, // }) // clock.runToLast(); - // expect(getColumnValues(0)).to.deep.equal(['5', '6', '7', '8', '9']); + // expect(getColumnValues(0)).to.deep.equal(['Titanic', 'Star Wars: The Force Awakens']); // }) }); }); From 5808f09f8a63bdc7042918c735ca7b39145cc2b1 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 25 Jan 2022 10:37:36 +0100 Subject: [PATCH 18/26] Code review: Matheus --- .../data-grid/state/RestoreStateInitialState.js | 14 ++++++++++---- .../data-grid/state/RestoreStateInitialState.tsx | 12 +++++++++--- docs/src/pages/components/data-grid/state/state.md | 10 +++++++--- .../columnPinning/useGridColumnPinning.tsx | 6 +++--- .../grid/hooks/features/filter/gridFilterUtils.ts | 2 +- .../grid/hooks/features/filter/useGridFilter.ts | 8 +++++--- .../grid/hooks/features/pagination/useGridPage.ts | 6 +++--- .../hooks/features/pagination/useGridPageSize.ts | 6 +++--- .../features/rowGrouping/gridRowGroupingUtils.ts | 2 +- .../features/rowGrouping/useGridRowGrouping.tsx | 6 +++--- .../hooks/features/sorting/gridSortingUtils.ts | 2 +- .../grid/hooks/features/sorting/useGridSorting.ts | 6 +++--- 12 files changed, 49 insertions(+), 31 deletions(-) diff --git a/docs/src/pages/components/data-grid/state/RestoreStateInitialState.js b/docs/src/pages/components/data-grid/state/RestoreStateInitialState.js index 0af30a461cda..d27faf028d86 100644 --- a/docs/src/pages/components/data-grid/state/RestoreStateInitialState.js +++ b/docs/src/pages/components/data-grid/state/RestoreStateInitialState.js @@ -1,5 +1,6 @@ -import Button from '@mui/material/Button'; +import * as React from 'react'; import PropTypes from 'prop-types'; +import Button from '@mui/material/Button'; import Box from '@mui/material/Box'; import { DataGridPro, @@ -9,8 +10,8 @@ import { useGridApiContext, useGridRootProps, } from '@mui/x-data-grid-pro'; -import * as React from 'react'; import { useDemoData } from '@mui/x-data-grid-generator'; +import Alert from '@mui/material/Alert'; const GridCustomToolbar = ({ unMount }) => { const rootProps = useGridRootProps(); @@ -20,7 +21,7 @@ const GridCustomToolbar = ({ unMount }) => { - } @@ -28,7 +29,7 @@ const GridCustomToolbar = ({ unMount }) => { {...rootProps.componentsProps?.baseButton} > Save state and unmount - + ); }; @@ -79,6 +80,11 @@ export default function RestoreStateInitialState() { return ( + {!!savedState && ( + + Initial state: {JSON.stringify(savedState)} + + )} ); } diff --git a/docs/src/pages/components/data-grid/state/RestoreStateInitialState.tsx b/docs/src/pages/components/data-grid/state/RestoreStateInitialState.tsx index 1e269552684f..6cb7d8ef30a9 100644 --- a/docs/src/pages/components/data-grid/state/RestoreStateInitialState.tsx +++ b/docs/src/pages/components/data-grid/state/RestoreStateInitialState.tsx @@ -1,3 +1,4 @@ +import * as React from 'react'; import Button from '@mui/material/Button'; import Box from '@mui/material/Box'; import { @@ -10,8 +11,8 @@ import { useGridApiContext, useGridRootProps, } from '@mui/x-data-grid-pro'; -import * as React from 'react'; import { useDemoData } from '@mui/x-data-grid-generator'; +import Alert from '@mui/material/Alert'; const GridCustomToolbar = ({ unMount, @@ -25,7 +26,7 @@ const GridCustomToolbar = ({ - } @@ -33,7 +34,7 @@ const GridCustomToolbar = ({ {...rootProps.componentsProps?.baseButton} > Save state and unmount - + ); }; @@ -85,6 +86,11 @@ export default function RestoreStateInitialState() { return ( + {!!savedState && ( + + Initial state: {JSON.stringify(savedState)} + + )} ); } diff --git a/docs/src/pages/components/data-grid/state/state.md b/docs/src/pages/components/data-grid/state/state.md index c37d1b0c3ad2..43dc329be703 100644 --- a/docs/src/pages/components/data-grid/state/state.md +++ b/docs/src/pages/components/data-grid/state/state.md @@ -59,9 +59,12 @@ Some selectors are yet to be documented. The current state of the grid can be exported using `apiRef.current.exportState()`. It can then be restored by either passing it to the `initialState` prop or to the `apiRef.current.restoreState()` method. +If one of the sub-state is being controlled, the grid will call the callback linked to that model (`onFilterModelChange` if `filterModel` is being controlled for instance). +But if the callback is not defined or if calling it does not update the prop value, then the restored value will not be applied. + ### Restore the state with `initialState` -> ⚠️ If you restore the page using `initialState` before the data are fetched, the grid will automatically move to the 1st page. +> ⚠️ If you restore the page using `initialState` before the data is fetched, the grid will automatically move to the 1st page. {{"demo": "pages/components/data-grid/state/RestoreStateInitialState.js", "bg": "inline", "defaultCodeOpen": false}} @@ -80,8 +83,9 @@ apiRef.current.restoreState({ }); ``` -**Note**: Most of the state keys are not fully independent. -Restoring the pagination without restoring the filters or the sorting will work, but the rows displayed after the re-import will not be the same as before the export. +> ⚠️ Most of the state keys are not fully independent. +> +> Restoring the pagination without restoring the filters or the sorting will work, but the rows displayed after the re-import will not be the same as before the export. ## API diff --git a/packages/grid/_modules_/grid/hooks/features/columnPinning/useGridColumnPinning.tsx b/packages/grid/_modules_/grid/hooks/features/columnPinning/useGridColumnPinning.tsx index 767a09eda696..bd2259dbb4de 100644 --- a/packages/grid/_modules_/grid/hooks/features/columnPinning/useGridColumnPinning.tsx +++ b/packages/grid/_modules_/grid/hooks/features/columnPinning/useGridColumnPinning.tsx @@ -29,7 +29,7 @@ import { GridState } from '../../../models'; const Divider = () => event.stopPropagation()} />; -const setStatePinnedColumns = +const mergeStateWithPinnedColumns = (pinnedColumns: GridPinnedColumns) => (state: GridState): GridState => ({ ...state, pinnedColumns }); @@ -242,7 +242,7 @@ export const useGridColumnPinning = ( (params, context) => { const newPinnedColumns = context.stateToRestore.pinnedColumns; if (newPinnedColumns != null) { - apiRef.current.setState(setStatePinnedColumns(newPinnedColumns)); + apiRef.current.setState(mergeStateWithPinnedColumns(newPinnedColumns)); } return params; @@ -327,7 +327,7 @@ export const useGridColumnPinning = ( const setPinnedColumns = React.useCallback( (newPinnedColumns) => { checkIfEnabled('setPinnedColumns'); - apiRef.current.setState(setStatePinnedColumns(newPinnedColumns)); + apiRef.current.setState(mergeStateWithPinnedColumns(newPinnedColumns)); apiRef.current.forceUpdate(); }, [apiRef, checkIfEnabled], diff --git a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterUtils.ts b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterUtils.ts index 9ee521a2c8f1..a29c9b4e385d 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/gridFilterUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/gridFilterUtils.ts @@ -13,7 +13,7 @@ type GridFilterItemApplier = { item: GridFilterItem; }; -export const setStateFilterModel = ( +export const mergeStateWithFilterModel = ( filterModel: GridFilterModel, disableMultipleColumnsFiltering: boolean, ) => { diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index fa0afe432758..a919fdd51184 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 @@ import { useGridRegisterFilteringMethod } from './useGridRegisterFilteringMethod import { buildAggregatedFilterApplier, cleanFilterItem, - setStateFilterModel, + mergeStateWithFilterModel, } from './gridFilterUtils'; const checkFilterModelValidity = (model: GridFilterModel) => { @@ -213,7 +213,9 @@ export const useGridFilter = ( checkFilterModelValidity(model); logger.debug('Setting filter model'); - apiRef.current.setState(setStateFilterModel(model, props.disableMultipleColumnsFiltering)); + apiRef.current.setState( + mergeStateWithFilterModel(model, props.disableMultipleColumnsFiltering), + ); apiRef.current.unstable_applyFilters(); } }, @@ -264,7 +266,7 @@ export const useGridFilter = ( return params; } apiRef.current.setState( - setStateFilterModel(filterModel, props.disableMultipleColumnsFiltering), + mergeStateWithFilterModel(filterModel, props.disableMultipleColumnsFiltering), ); return { diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts index 5880d54a6ebb..33740d5c3ec8 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts @@ -37,7 +37,7 @@ const applyValidPage = (paginationState: GridPaginationState): GridPaginationSta }; }; -const setStatePage = +const mergeStateWithPage = (page: number) => (state: GridState): GridState => ({ ...state, @@ -83,7 +83,7 @@ export const useGridPage = ( const setPage = React.useCallback( (page) => { logger.debug(`Setting page to ${page}`); - apiRef.current.setState(setStatePage(page)); + apiRef.current.setState(mergeStateWithPage(page)); apiRef.current.forceUpdate(); }, [apiRef, logger], @@ -120,7 +120,7 @@ export const useGridPage = ( // We apply the constraint even if the page did not change in case the pageSize changed. const page = context.stateToRestore.pagination?.page ?? gridPageSelector(apiRef.current.state); - apiRef.current.setState(setStatePage(page)); + apiRef.current.setState(mergeStateWithPage(page)); return params; }, [apiRef], diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts index cbd0fea46698..1e64953541a3 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts @@ -18,7 +18,7 @@ import { useGridRegisterPreProcessor, } from '../../core/preProcessing'; -const setStatePageSize = +const mergeStateWithPageSize = (pageSize: number) => (state: GridState): GridState => ({ ...state, @@ -81,7 +81,7 @@ export const useGridPageSize = ( logger.debug(`Setting page size to ${pageSize}`); - apiRef.current.setState(setStatePageSize(pageSize)); + apiRef.current.setState(mergeStateWithPageSize(pageSize)); apiRef.current.forceUpdate(); }, [apiRef, logger], @@ -120,7 +120,7 @@ export const useGridPageSize = ( (params, context) => { const pageSize = context.stateToRestore.pagination?.pageSize; if (pageSize != null) { - apiRef.current.setState(setStatePageSize(pageSize)); + apiRef.current.setState(mergeStateWithPageSize(pageSize)); } return params; }, diff --git a/packages/grid/_modules_/grid/hooks/features/rowGrouping/gridRowGroupingUtils.ts b/packages/grid/_modules_/grid/hooks/features/rowGrouping/gridRowGroupingUtils.ts index d1ffe2946f69..c77487c25fed 100644 --- a/packages/grid/_modules_/grid/hooks/features/rowGrouping/gridRowGroupingUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/rowGrouping/gridRowGroupingUtils.ts @@ -146,7 +146,7 @@ export const getColDefOverrides = ( return groupingColDefProp; }; -export const setStateRowGroupingModel = +export const mergeStateWithRowGroupingModel = (rowGroupingModel: GridRowGroupingModel) => (state: GridState): GridState => ({ ...state, diff --git a/packages/grid/_modules_/grid/hooks/features/rowGrouping/useGridRowGrouping.tsx b/packages/grid/_modules_/grid/hooks/features/rowGrouping/useGridRowGrouping.tsx index 354a973c8da9..08d9ac2daeb3 100644 --- a/packages/grid/_modules_/grid/hooks/features/rowGrouping/useGridRowGrouping.tsx +++ b/packages/grid/_modules_/grid/hooks/features/rowGrouping/useGridRowGrouping.tsx @@ -25,7 +25,7 @@ import { getColDefOverrides, GROUPING_COLUMNS_FEATURE_NAME, isGroupingColumn, - setStateRowGroupingModel, + mergeStateWithRowGroupingModel, } from './gridRowGroupingUtils'; import { createGroupingColDefForOneGroupingCriteria, @@ -368,7 +368,7 @@ export const useGridRowGrouping = ( (model) => { const currentModel = gridRowGroupingModelSelector(apiRef.current.state); if (currentModel !== model) { - apiRef.current.setState(setStateRowGroupingModel(model)); + apiRef.current.setState(mergeStateWithRowGroupingModel(model)); updateRowGrouping(); apiRef.current.forceUpdate(); } @@ -470,7 +470,7 @@ export const useGridRowGrouping = ( const rowGroupingModel = context.stateToRestore.rowGrouping?.model; if (rowGroupingModel != null) { - apiRef.current.setState(setStateRowGroupingModel(rowGroupingModel)); + apiRef.current.setState(mergeStateWithRowGroupingModel(rowGroupingModel)); } return params; }, diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingUtils.ts b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingUtils.ts index 9d500cedf16e..4df49277c7e6 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingUtils.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/gridSortingUtils.ts @@ -21,7 +21,7 @@ interface GridParsedSortItem { getSortCellParams: (id: GridRowId) => GridSortCellParams; } -export const setStateSortModel = +export const mergeStateWithSortModel = (sortModel: GridSortModel) => (state: GridState): GridState => ({ ...state, diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index 282892980129..e54b3e25ee58 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -21,7 +21,7 @@ import { gridRowIdsSelector, gridRowGroupingNameSelector, gridRowTreeSelector } import { useGridStateInit } from '../../utils/useGridStateInit'; import { useFirstRender } from '../../utils/useFirstRender'; import { GridSortingMethod, GridSortingMethodCollection } from './gridSortingState'; -import { buildAggregatedSortingApplier, setStateSortModel } from './gridSortingUtils'; +import { buildAggregatedSortingApplier, mergeStateWithSortModel } from './gridSortingUtils'; import { GridPreProcessingGroup, GridPreProcessor, @@ -148,7 +148,7 @@ export const useGridSorting = ( const currentModel = gridSortModelSelector(apiRef.current.state); if (currentModel !== model) { logger.debug(`Setting sort model`); - apiRef.current.setState(setStateSortModel(model)); + apiRef.current.setState(mergeStateWithSortModel(model)); apiRef.current.forceUpdate(); apiRef.current.applySorting(); } @@ -235,7 +235,7 @@ export const useGridSorting = ( if (sortModel == null) { return params; } - apiRef.current.setState(setStateSortModel(sortModel)); + apiRef.current.setState(mergeStateWithSortModel(sortModel)); return { ...params, From 8e3a7c32f7dc703f64c26bdbfdef81e23d5dbeca Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Wed, 26 Jan 2022 12:15:19 +0100 Subject: [PATCH 19/26] Update docs/src/pages/components/data-grid/state/state.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: José Rodolfo Freitas --- docs/src/pages/components/data-grid/state/state.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/pages/components/data-grid/state/state.md b/docs/src/pages/components/data-grid/state/state.md index 43dc329be703..bc19c1e30f8f 100644 --- a/docs/src/pages/components/data-grid/state/state.md +++ b/docs/src/pages/components/data-grid/state/state.md @@ -59,7 +59,7 @@ Some selectors are yet to be documented. The current state of the grid can be exported using `apiRef.current.exportState()`. It can then be restored by either passing it to the `initialState` prop or to the `apiRef.current.restoreState()` method. -If one of the sub-state is being controlled, the grid will call the callback linked to that model (`onFilterModelChange` if `filterModel` is being controlled for instance). +Watch out for controlled models and their callbacks (`onFilterModelChange` if you use `filterModel` for instance), the grid will call those callbacks when restoring the state. But if the callback is not defined or if calling it does not update the prop value, then the restored value will not be applied. ### Restore the state with `initialState` From 477167bd0c986ab139c4c12b08fb28b5e17d1ee1 Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Wed, 26 Jan 2022 12:15:25 +0100 Subject: [PATCH 20/26] Update docs/src/pages/components/data-grid/state/state.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: José Rodolfo Freitas --- docs/src/pages/components/data-grid/state/state.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/pages/components/data-grid/state/state.md b/docs/src/pages/components/data-grid/state/state.md index bc19c1e30f8f..86b3199d13ba 100644 --- a/docs/src/pages/components/data-grid/state/state.md +++ b/docs/src/pages/components/data-grid/state/state.md @@ -74,7 +74,7 @@ But if the callback is not defined or if calling it does not update the prop val #### Restore part of the state -It is possible to only pass some keys of the state to the `apiRef.current.restoreState()` method. +It is possible to restore specific properties of the state using the `apiRef.current.restoreState()` method. For instance, to only restore the pinned columns: ```ts From bd6eb7725c7dbc351d0fd56ac0546fbe7909cfd9 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 26 Jan 2022 13:15:32 +0100 Subject: [PATCH 21/26] Code review: Matheus --- .../data-grid/state/RestoreStateApiRef.js | 268 +++++++++++++++--- .../data-grid/state/RestoreStateApiRef.tsx | 255 ++++++++++++++--- .../state/RestoreStateApiRef.tsx.preview | 7 + .../pages/components/data-grid/state/state.md | 2 +- 4 files changed, 453 insertions(+), 79 deletions(-) create mode 100644 docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx.preview diff --git a/docs/src/pages/components/data-grid/state/RestoreStateApiRef.js b/docs/src/pages/components/data-grid/state/RestoreStateApiRef.js index 015af268c6f8..4c04dbed70fb 100644 --- a/docs/src/pages/components/data-grid/state/RestoreStateApiRef.js +++ b/docs/src/pages/components/data-grid/state/RestoreStateApiRef.js @@ -1,12 +1,23 @@ import * as React from 'react'; -import { DataGridPro, useGridApiRef } from '@mui/x-data-grid-pro'; +import PropTypes from 'prop-types'; +import { DataGridPro, useGridApiContext, useGridApiRef } from '@mui/x-data-grid-pro'; import { useDemoData } from '@mui/x-data-grid-generator'; -import Stack from '@mui/material/Stack'; +import ClickAwayListener from '@mui/material/ClickAwayListener'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemText from '@mui/material/ListItemText'; +import Button from '@mui/material/Button'; import IconButton from '@mui/material/IconButton'; import TextField from '@mui/material/TextField'; import Box from '@mui/material/Box'; -import Chip from '@mui/material/Chip'; +import Paper from '@mui/material/Paper'; import AddIcon from '@mui/icons-material/Add'; +import DeleteIcon from '@mui/icons-material/Delete'; +import DoneIcon from '@mui/icons-material/Done'; +import Divider from '@mui/material/Divider'; +import Fade from '@mui/material/Fade'; +import Popper from '@mui/material/Popper'; const demoReducer = (state, action) => { switch (action.type) { @@ -53,6 +64,7 @@ const demoReducer = (state, action) => { return { ...state, activeViewId: action.id, + isMenuOpened: false, }; } @@ -63,6 +75,21 @@ const demoReducer = (state, action) => { }; } + case 'togglePopper': { + return { + ...state, + isMenuOpened: !state.isMenuOpened, + menuAnchorEl: action.element, + }; + } + + case 'closePopper': { + return { + ...state, + isMenuOpened: false, + }; + } + default: { return state; } @@ -72,16 +99,130 @@ const demoReducer = (state, action) => { const DEMO_INITIAL_STATE = { views: {}, newViewLabel: '', - activeViewId: null, + isMenuOpened: false, + menuAnchorEl: null, }; -export default function RestoreStateApiRef() { - const apiRef = useGridApiRef(); - const { data, loading } = useDemoData({ - dataSet: 'Commodity', - rowLength: 500, - }); +const ViewListItem = (props) => { + const { view, viewId, selected, onDelete, onSelect } = props; + return ( + onSelect(viewId)} + secondaryAction={ + { + event.stopPropagation(); + onDelete(viewId); + }} + > + + + } + > + + + + + ); +}; + +ViewListItem.propTypes = { + onDelete: PropTypes.func.isRequired, + onSelect: PropTypes.func.isRequired, + selected: PropTypes.bool.isRequired, + view: PropTypes.shape({ + label: PropTypes.string.isRequired, + value: PropTypes.shape({ + columns: PropTypes.shape({ + columnVisibilityModel: PropTypes.object, + }), + filter: PropTypes.shape({ + filterModel: PropTypes.object, + }), + pagination: PropTypes.shape({ + page: PropTypes.number, + pageSize: PropTypes.number, + }), + pinnedColumns: PropTypes.shape({ + left: PropTypes.arrayOf(PropTypes.string), + right: PropTypes.arrayOf(PropTypes.string), + }), + preferencePanel: PropTypes.shape({ + open: PropTypes.bool.isRequired, + openedPanelValue: PropTypes.oneOf(['columns', 'filters']), + }), + rowGrouping: PropTypes.shape({ + model: PropTypes.arrayOf(PropTypes.string), + }), + sorting: PropTypes.shape({ + sortModel: PropTypes.arrayOf(PropTypes.object), + }), + }).isRequired, + }).isRequired, + viewId: PropTypes.string.isRequired, +}; + +const NewViewListItem = (props) => { + const { label, onLabelChange, onSubmit, isValid } = props; + const [isAddingView, setIsAddingView] = React.useState(false); + + if (isAddingView) { + return ( + { + onSubmit(); + setIsAddingView(false); + }} + disabled={!isValid} + > + + + } + > + + + ); + } + + return ( + + + + ); +}; + +NewViewListItem.propTypes = { + isValid: PropTypes.bool.isRequired, + label: PropTypes.string.isRequired, + onLabelChange: PropTypes.func.isRequired, + onSubmit: PropTypes.func.isRequired, +}; + +const CustomToolbar = () => { + const apiRef = useGridApiContext(); const [state, dispatch] = React.useReducer(demoReducer, DEMO_INITIAL_STATE); const createNewView = () => { @@ -95,15 +236,24 @@ export default function RestoreStateApiRef() { dispatch({ type: 'setNewViewLabel', label: e.target.value }); }; - const handleDeleteView = (viewId) => { + const handleDeleteView = React.useCallback((viewId) => { dispatch({ type: 'deleteView', id: viewId }); - }; + }, []); const handleSetActiveView = (viewId) => { apiRef.current.restoreState(state.views[viewId].value); dispatch({ type: 'setActiveView', id: viewId }); }; + const handlePopperAnchorClick = (event) => { + dispatch({ type: 'togglePopper', element: event.currentTarget }); + event.stopPropagation(); + }; + + const handleClosePopper = () => { + dispatch({ type: 'closePopper' }); + }; + const isNewViewLabelValid = React.useMemo(() => { if (state.newViewLabel.length === 0) { return false; @@ -114,32 +264,74 @@ export default function RestoreStateApiRef() { ); }, [state.views, state.newViewLabel]); + const canBeMenuOpened = state.isMenuOpened && Boolean(state.menuAnchorEl); + const popperId = canBeMenuOpened ? 'transition-popper' : undefined; + return ( - - - - - - - - - {Object.entries(state.views).map(([viewId, view]) => ( - handleDeleteView(viewId)} - variant={viewId === state.activeViewId ? 'filled' : 'outlined'} - onClick={() => handleSetActiveView(viewId)} - /> - ))} - - - - - + + + + + {({ TransitionProps }) => ( + + + + {Object.entries(state.views).map(([viewId, view]) => ( + + ))} + + + + + + + )} + + + + ); +}; + +export default function RestoreStateApiRef() { + const apiRef = useGridApiRef(); + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 500, + }); + + return ( + + + ); } diff --git a/docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx b/docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx index e5693ffcb342..6e234bd323cd 100644 --- a/docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx +++ b/docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx @@ -1,24 +1,48 @@ import * as React from 'react'; -import { DataGridPro, GridInitialState, useGridApiRef } from '@mui/x-data-grid-pro'; +import { + DataGridPro, + GridInitialState, + useGridApiContext, + useGridApiRef, +} from '@mui/x-data-grid-pro'; import { useDemoData } from '@mui/x-data-grid-generator'; -import Stack from '@mui/material/Stack'; +import ClickAwayListener from '@mui/material/ClickAwayListener'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemText from '@mui/material/ListItemText'; +import Button from '@mui/material/Button'; import IconButton from '@mui/material/IconButton'; import TextField from '@mui/material/TextField'; import Box from '@mui/material/Box'; -import Chip from '@mui/material/Chip'; +import Paper from '@mui/material/Paper'; import AddIcon from '@mui/icons-material/Add'; +import DeleteIcon from '@mui/icons-material/Delete'; +import DoneIcon from '@mui/icons-material/Done'; +import Divider from '@mui/material/Divider'; +import Fade from '@mui/material/Fade'; +import Popper from '@mui/material/Popper'; + +interface StateView { + label: string; + value: GridInitialState; +} interface DemoState { - views: { [id: string]: { label: string; value: GridInitialState } }; + views: { [id: string]: StateView }; newViewLabel: string; activeViewId: string | null; + isMenuOpened: boolean; + menuAnchorEl: HTMLElement | null; } type DemoActions = | { type: 'createView'; value: GridInitialState } | { type: 'deleteView'; id: string } | { type: 'setNewViewLabel'; label: string } - | { type: 'setActiveView'; id: string | null }; + | { type: 'setActiveView'; id: string | null } + | { type: 'togglePopper'; element: HTMLElement } + | { type: 'closePopper' }; const demoReducer: React.Reducer = (state, action) => { switch (action.type) { @@ -65,6 +89,7 @@ const demoReducer: React.Reducer = (state, action) => { return { ...state, activeViewId: action.id, + isMenuOpened: false, }; } @@ -75,6 +100,21 @@ const demoReducer: React.Reducer = (state, action) => { }; } + case 'togglePopper': { + return { + ...state, + isMenuOpened: !state.isMenuOpened, + menuAnchorEl: action.element, + }; + } + + case 'closePopper': { + return { + ...state, + isMenuOpened: false, + }; + } + default: { return state; } @@ -84,15 +124,100 @@ const demoReducer: React.Reducer = (state, action) => { const DEMO_INITIAL_STATE: DemoState = { views: {}, newViewLabel: '', - activeViewId: null, + isMenuOpened: false, + menuAnchorEl: null, }; -export default function RestoreStateApiRef() { - const apiRef = useGridApiRef(); - const { data, loading } = useDemoData({ - dataSet: 'Commodity', - rowLength: 500, - }); +const ViewListItem = (props: { + view: StateView; + viewId: string; + selected: boolean; + onDelete: (viewId: string) => void; + onSelect: (viewId: string) => void; +}) => { + const { view, viewId, selected, onDelete, onSelect } = props; + + return ( + onSelect(viewId)} + secondaryAction={ + { + event.stopPropagation(); + onDelete(viewId); + }} + > + + + } + > + + + + + ); +}; + +const NewViewListItem = (props: { + label: string; + onLabelChange: ( + e: React.ChangeEvent, + ) => void; + onSubmit: () => void; + isValid: boolean; +}) => { + const { label, onLabelChange, onSubmit, isValid } = props; + const [isAddingView, setIsAddingView] = React.useState(false); + + if (isAddingView) { + return ( + { + onSubmit(); + setIsAddingView(false); + }} + disabled={!isValid} + > + + + } + > + + + ); + } + + return ( + + + + ); +}; + +const CustomToolbar = () => { + const apiRef = useGridApiContext(); const [state, dispatch] = React.useReducer(demoReducer, DEMO_INITIAL_STATE); const createNewView = () => { @@ -108,15 +233,24 @@ export default function RestoreStateApiRef() { dispatch({ type: 'setNewViewLabel', label: e.target.value }); }; - const handleDeleteView = (viewId: string) => { + const handleDeleteView = React.useCallback((viewId: string) => { dispatch({ type: 'deleteView', id: viewId }); - }; + }, []); const handleSetActiveView = (viewId: string) => { apiRef.current.restoreState(state.views[viewId].value); dispatch({ type: 'setActiveView', id: viewId }); }; + const handlePopperAnchorClick = (event: React.MouseEvent) => { + dispatch({ type: 'togglePopper', element: event.currentTarget as HTMLElement }); + event.stopPropagation(); + }; + + const handleClosePopper = () => { + dispatch({ type: 'closePopper' }); + }; + const isNewViewLabelValid = React.useMemo(() => { if (state.newViewLabel.length === 0) { return false; @@ -127,32 +261,73 @@ export default function RestoreStateApiRef() { ); }, [state.views, state.newViewLabel]); + const canBeMenuOpened = state.isMenuOpened && Boolean(state.menuAnchorEl); + const popperId = canBeMenuOpened ? 'transition-popper' : undefined; + return ( - - - - - - - - - {Object.entries(state.views).map(([viewId, view]) => ( - handleDeleteView(viewId)} - variant={viewId === state.activeViewId ? 'filled' : 'outlined'} - onClick={() => handleSetActiveView(viewId)} - /> - ))} - - - - - + + + + + {({ TransitionProps }) => ( + + + + {Object.entries(state.views).map(([viewId, view]) => ( + + ))} + + + + + + )} + + + + ); +}; + +export default function RestoreStateApiRef() { + const apiRef = useGridApiRef(); + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 500, + }); + + return ( + + + ); } diff --git a/docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx.preview b/docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx.preview new file mode 100644 index 000000000000..aa54d80bcd0f --- /dev/null +++ b/docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx.preview @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/docs/src/pages/components/data-grid/state/state.md b/docs/src/pages/components/data-grid/state/state.md index 86b3199d13ba..67ef2e79a79b 100644 --- a/docs/src/pages/components/data-grid/state/state.md +++ b/docs/src/pages/components/data-grid/state/state.md @@ -59,7 +59,7 @@ Some selectors are yet to be documented. The current state of the grid can be exported using `apiRef.current.exportState()`. It can then be restored by either passing it to the `initialState` prop or to the `apiRef.current.restoreState()` method. -Watch out for controlled models and their callbacks (`onFilterModelChange` if you use `filterModel` for instance), the grid will call those callbacks when restoring the state. +Watch out for controlled models and their callbacks (`onFilterModelChange` if you use `filterModel` for instance), the grid will call those callbacks when restoring the state. But if the callback is not defined or if calling it does not update the prop value, then the restored value will not be applied. ### Restore the state with `initialState` From 89f5899a1001ead6f2a4d3e947cd51dd8b5326e3 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 26 Jan 2022 13:23:00 +0100 Subject: [PATCH 22/26] Fix --- docs/src/pages/components/data-grid/state/RestoreStateApiRef.js | 1 + docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/src/pages/components/data-grid/state/RestoreStateApiRef.js b/docs/src/pages/components/data-grid/state/RestoreStateApiRef.js index 4c04dbed70fb..0841c8558c51 100644 --- a/docs/src/pages/components/data-grid/state/RestoreStateApiRef.js +++ b/docs/src/pages/components/data-grid/state/RestoreStateApiRef.js @@ -101,6 +101,7 @@ const DEMO_INITIAL_STATE = { newViewLabel: '', isMenuOpened: false, menuAnchorEl: null, + activeViewId: null, }; const ViewListItem = (props) => { diff --git a/docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx b/docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx index 6e234bd323cd..2aadb298c734 100644 --- a/docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx +++ b/docs/src/pages/components/data-grid/state/RestoreStateApiRef.tsx @@ -126,6 +126,7 @@ const DEMO_INITIAL_STATE: DemoState = { newViewLabel: '', isMenuOpened: false, menuAnchorEl: null, + activeViewId: null, }; const ViewListItem = (props: { From f58ad6de7b08db919ea73e4afcf1b006f4497012 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 1 Feb 2022 10:20:08 +0100 Subject: [PATCH 23/26] Code review: Matheus --- .../columnPinning/useGridColumnPinning.tsx | 34 +++-- .../hooks/features/columns/useGridColumns.ts | 10 +- .../hooks/features/filter/useGridFilter.ts | 10 +- .../hooks/features/pagination/useGridPage.ts | 7 +- .../features/pagination/useGridPageSize.ts | 13 +- .../useGridPreferencesPanel.ts | 7 +- .../rowGrouping/useGridRowGrouping.tsx | 7 +- .../hooks/features/sorting/useGridSorting.ts | 7 +- .../statePersistence.DataGridPro.test.tsx | 122 ++++++------------ 9 files changed, 119 insertions(+), 98 deletions(-) diff --git a/packages/grid/_modules_/grid/hooks/features/columnPinning/useGridColumnPinning.tsx b/packages/grid/_modules_/grid/hooks/features/columnPinning/useGridColumnPinning.tsx index bd2259dbb4de..52277f9a3227 100644 --- a/packages/grid/_modules_/grid/hooks/features/columnPinning/useGridColumnPinning.tsx +++ b/packages/grid/_modules_/grid/hooks/features/columnPinning/useGridColumnPinning.tsx @@ -40,13 +40,23 @@ export const useGridColumnPinning = ( 'initialState' | 'disableColumnPinning' | 'pinnedColumns' | 'onPinnedColumnsChange' >, ): void => { - useGridStateInit(apiRef, (state) => ({ - ...state, - pinnedColumns: { - left: !props.disableColumnPinning ? props.initialState?.pinnedColumns?.left : undefined, - right: !props.disableColumnPinning ? props.initialState?.pinnedColumns?.right : undefined, - }, - })); + useGridStateInit(apiRef, (state) => { + let model: GridPinnedColumns; + if (props.disableColumnPinning) { + model = {}; + } else if (props.pinnedColumns) { + model = props.pinnedColumns; + } else if (props.initialState?.pinnedColumns) { + model = props.initialState?.pinnedColumns; + } else { + model = {}; + } + + return { + ...state, + pinnedColumns: model, + }; + }); const pinnedColumns = useGridSelector(apiRef, gridPinnedColumnsSelector); // Each visible row (not to be confused with a filter result) is composed of a central .MuiDataGrid-row element @@ -228,9 +238,17 @@ export const useGridColumnPinning = ( GridPreProcessor >( (prevState) => { + const pinnedColumnsToExport = gridPinnedColumnsSelector(apiRef.current.state); + if ( + (!pinnedColumnsToExport.left || pinnedColumnsToExport.left.length === 0) && + (!pinnedColumnsToExport.right || pinnedColumnsToExport.right.length === 0) + ) { + return prevState; + } + return { ...prevState, - pinnedColumns: gridPinnedColumnsSelector(apiRef.current.state), + pinnedColumns: pinnedColumnsToExport, }; }, [apiRef], diff --git a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts index a8138a1defaa..36d21c75f417 100644 --- a/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts +++ b/packages/grid/_modules_/grid/hooks/features/columns/useGridColumns.ts @@ -289,10 +289,18 @@ export function useGridColumns( return prevState; } + const columnVisibilityModelToExport = gridColumnVisibilityModelSelector(apiRef.current.state); + const hasHiddenColumns = Object.values(columnVisibilityModelToExport).some( + (value) => value === false, + ); + if (!hasHiddenColumns) { + return prevState; + } + return { ...prevState, columns: { - columnVisibilityModel: gridColumnVisibilityModelSelector(apiRef.current.state), + columnVisibilityModel: columnVisibilityModelToExport, }, }; }, diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts index 3b1a7c3fc84d..d89076fd2c24 100644 --- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts +++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts @@ -247,10 +247,18 @@ export const useGridFilter = ( GridPreProcessor >( (prevState) => { + const filterModelToExport = gridFilterModelSelector(apiRef.current.state); + if ( + filterModelToExport.items.length === 0 && + filterModelToExport.linkOperator === getDefaultGridFilterModel().linkOperator + ) { + return prevState; + } + return { ...prevState, filter: { - filterModel: gridFilterModelSelector(apiRef.current.state), + filterModel: filterModelToExport, }, }; }, diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts index 33740d5c3ec8..8877d1231168 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPage.ts @@ -102,11 +102,16 @@ export const useGridPage = ( GridPreProcessor >( (prevState) => { + const pageToExport = gridPageSelector(apiRef.current.state); + if (pageToExport === 0) { + return prevState; + } + return { ...prevState, pagination: { ...prevState.pagination, - page: gridPageSelector(apiRef.current.state), + page: pageToExport, }, }; }, diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts index 1e64953541a3..250c9c36df53 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts @@ -42,16 +42,16 @@ export const useGridPageSize = ( const logger = useGridLogger(apiRef, 'useGridPageSize'); const rowHeight = useGridSelector(apiRef, gridDensityRowHeightSelector); + const defaultPageSize = props.autoPageSize ? 0 : 100; + useGridStateInit(apiRef, (state) => { let pageSize: number; if (props.pageSize != null) { pageSize = props.pageSize; } else if (props.initialState?.pagination?.pageSize != null) { pageSize = props.initialState.pagination.pageSize; - } else if (props.autoPageSize) { - pageSize = 0; } else { - pageSize = 100; + pageSize = defaultPageSize; } return { @@ -100,11 +100,16 @@ export const useGridPageSize = ( GridPreProcessor >( (prevState) => { + const pageSizeToExport = gridPageSizeSelector(apiRef.current.state); + if (pageSizeToExport === defaultPageSize) { + return prevState; + } + return { ...prevState, pagination: { ...prevState.pagination, - pageSize: gridPageSizeSelector(apiRef.current.state), + pageSize: pageSizeToExport, }, }; }, diff --git a/packages/grid/_modules_/grid/hooks/features/preferencesPanel/useGridPreferencesPanel.ts b/packages/grid/_modules_/grid/hooks/features/preferencesPanel/useGridPreferencesPanel.ts index 269e55150f2b..7fe1f22bab25 100644 --- a/packages/grid/_modules_/grid/hooks/features/preferencesPanel/useGridPreferencesPanel.ts +++ b/packages/grid/_modules_/grid/hooks/features/preferencesPanel/useGridPreferencesPanel.ts @@ -78,9 +78,14 @@ export const useGridPreferencesPanel = ( GridPreProcessor >( (prevState) => { + const preferencePanelToExport = gridPreferencePanelStateSelector(apiRef.current.state); + if (!preferencePanelToExport.open && !preferencePanelToExport.openedPanelValue) { + return prevState; + } + return { ...prevState, - preferencePanel: gridPreferencePanelStateSelector(apiRef.current.state), + preferencePanel: preferencePanelToExport, }; }, [apiRef], diff --git a/packages/grid/_modules_/grid/hooks/features/rowGrouping/useGridRowGrouping.tsx b/packages/grid/_modules_/grid/hooks/features/rowGrouping/useGridRowGrouping.tsx index 08d9ac2daeb3..25a015dea678 100644 --- a/packages/grid/_modules_/grid/hooks/features/rowGrouping/useGridRowGrouping.tsx +++ b/packages/grid/_modules_/grid/hooks/features/rowGrouping/useGridRowGrouping.tsx @@ -450,10 +450,15 @@ export const useGridRowGrouping = ( return prevState; } + const rowGroupingModelToExport = gridRowGroupingModelSelector(apiRef.current.state); + if (rowGroupingModelToExport.length === 0) { + return prevState; + } + return { ...prevState, rowGrouping: { - model: gridRowGroupingModelSelector(apiRef.current.state), + model: rowGroupingModelToExport, }, }; }, diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts index a7e631b8ce6c..edf65dea2a13 100644 --- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts +++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts @@ -220,10 +220,15 @@ export const useGridSorting = ( GridPreProcessor >( (prevState) => { + const sortModelToExport = gridSortModelSelector(apiRef.current.state); + if (sortModelToExport.length === 0) { + return prevState; + } + return { ...prevState, sorting: { - sortModel: gridSortModelSelector(apiRef.current.state), + sortModel: sortModelToExport, }, }; }, diff --git a/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx index b7788e14dc30..3690e5afe211 100644 --- a/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx @@ -3,6 +3,7 @@ import { DataGridPro, DataGridProProps, GridApiRef, + GridInitialState, GridPreferencePanelsValue, useGridApiRef, } from '@mui/x-data-grid-pro'; @@ -35,8 +36,12 @@ describe(' - State Persistence', () => { rowsPerPageOptions={[100, 2]} experimentalFeatures={{ rowGrouping: true }} // To enable the `rowGroupingModel in export / restore initialState={{ + ...props.initialState, columns: { - columnVisibilityModel: {}, // To enable the `columnVisibilityModel` in export / restore + ...props.initialState?.columns, + columnVisibilityModel: { + ...props.initialState?.columns?.columnVisibilityModel, + }, // To enable the `columnVisibilityModel` in export / restore }, }} defaultGroupingExpansionDepth={-1} @@ -46,36 +51,42 @@ describe(' - State Persistence', () => { }; describe('apiRef: exportState', () => { - it('should return the whole exportable state', () => { + const FULL_INITIAL_STATE: GridInitialState = { + columns: { + columnVisibilityModel: { year: false }, + }, + filter: { + filterModel: { + items: [{ columnField: 'gross', operatorValue: '>', value: '1500000000' }], + }, + }, + pagination: { + page: 1, + pageSize: 2, + }, + pinnedColumns: { + left: ['company'], + }, + preferencePanel: { + open: true, + openedPanelValue: GridPreferencePanelsValue.filters, + }, + sorting: { + sortModel: [{ field: 'director', sort: 'asc' }], + }, + rowGrouping: { + model: ['director'], + }, + }; + + it('should not return the default values of the models', () => { render(); - expect(apiRef.current.exportState()).to.deep.equal({ - columns: { - columnVisibilityModel: {}, - }, - filter: { - filterModel: { - items: [], - linkOperator: 'and', - }, - }, - pagination: { - page: 0, - pageSize: 100, - }, - pinnedColumns: { - left: undefined, - right: undefined, - }, - preferencePanel: { - open: false, - }, - rowGrouping: { - model: [], - }, - sorting: { - sortModel: [], - }, - }); + expect(apiRef.current.exportState()).to.deep.equal({}); + }); + + it('should export the initial values of the models', () => { + render(); + expect(apiRef.current.exportState()).to.deep.equal(FULL_INITIAL_STATE); }); it('should export the current version of the exportable state', () => { @@ -91,33 +102,7 @@ describe(' - State Persistence', () => { apiRef.current.setRowGroupingModel(['director']); apiRef.current.setColumnVisibilityModel({ year: false }); - expect(apiRef.current.exportState()).to.deep.equal({ - columns: { - columnVisibilityModel: { year: false }, - }, - filter: { - filterModel: { - items: [{ columnField: 'gross', operatorValue: '>', value: '1500000000' }], - }, - }, - pagination: { - page: 1, - pageSize: 2, - }, - pinnedColumns: { - left: ['company'], - }, - preferencePanel: { - open: true, - openedPanelValue: GridPreferencePanelsValue.filters, - }, - sorting: { - sortModel: [{ field: 'director', sort: 'asc' }], - }, - rowGrouping: { - model: ['director'], - }, - }); + expect(apiRef.current.exportState()).to.deep.equal(FULL_INITIAL_STATE); }); }); @@ -211,28 +196,5 @@ describe(' - State Persistence', () => { clock.runToLast(); expect(getColumnValues(0)).to.deep.equal(['Titanic', 'Star Wars: The Force Awakens']); }); - - // Not sure how to make that one work, if the `pageSize` update is async, we try to update the `page` with an outdated `pageSize` which can cause problem. - // it('should restore when controlled pageSize but not page', () => { - // const ControlledTest = () => { - // const [pageSize, setPageSize] = React.useState(100) - // - // return ( - // { - // setPageSize(newPageSize) - // }} /> - // ) - // } - // - // render() - // apiRef.current.restoreState({ - // pagination: { - // page: 1, - // pageSize: 2, - // }, - // }) - // clock.runToLast(); - // expect(getColumnValues(0)).to.deep.equal(['Titanic', 'Star Wars: The Force Awakens']); - // }) }); }); From 56e304fb173df2fed59e8a6b804c2fa329b0ed4e Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 1 Feb 2022 10:30:13 +0100 Subject: [PATCH 24/26] Fix --- .../_modules_/grid/hooks/features/pagination/useGridPageSize.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts index 250c9c36df53..d48a665ea4f4 100644 --- a/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts +++ b/packages/grid/_modules_/grid/hooks/features/pagination/useGridPageSize.ts @@ -113,7 +113,7 @@ export const useGridPageSize = ( }, }; }, - [apiRef], + [apiRef, defaultPageSize], ); /** From 8053e8f9a574cc302aa8d823ba3456b06550aef8 Mon Sep 17 00:00:00 2001 From: delangle Date: Tue, 1 Feb 2022 10:39:54 +0100 Subject: [PATCH 25/26] Code review: Matheus --- .../data-grid/state/RestoreStateInitialState.js | 8 +++++--- .../data-grid/state/RestoreStateInitialState.tsx | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/src/pages/components/data-grid/state/RestoreStateInitialState.js b/docs/src/pages/components/data-grid/state/RestoreStateInitialState.js index d27faf028d86..7be28245fad9 100644 --- a/docs/src/pages/components/data-grid/state/RestoreStateInitialState.js +++ b/docs/src/pages/components/data-grid/state/RestoreStateInitialState.js @@ -78,14 +78,16 @@ export default function RestoreStateInitialState() { if (isMounted) { return ( - - + + + + {!!savedState && ( Initial state: {JSON.stringify(savedState)} )} - + ); } diff --git a/docs/src/pages/components/data-grid/state/RestoreStateInitialState.tsx b/docs/src/pages/components/data-grid/state/RestoreStateInitialState.tsx index 6cb7d8ef30a9..d400bb118627 100644 --- a/docs/src/pages/components/data-grid/state/RestoreStateInitialState.tsx +++ b/docs/src/pages/components/data-grid/state/RestoreStateInitialState.tsx @@ -84,14 +84,16 @@ export default function RestoreStateInitialState() { if (isMounted) { return ( - - + + + + {!!savedState && ( Initial state: {JSON.stringify(savedState)} )} - + ); } From 02e75342f09a10dde6ac48d527bc1bfebadbbfd9 Mon Sep 17 00:00:00 2001 From: delangle Date: Wed, 2 Feb 2022 09:47:48 +0100 Subject: [PATCH 26/26] Hide doc --- .../pages/components/data-grid/state/state.md | 62 ++++++++++--------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/docs/src/pages/components/data-grid/state/state.md b/docs/src/pages/components/data-grid/state/state.md index 20bef0bcb38d..b7a8f444a582 100644 --- a/docs/src/pages/components/data-grid/state/state.md +++ b/docs/src/pages/components/data-grid/state/state.md @@ -58,36 +58,40 @@ Some selectors are yet to be documented. ## Save and restore the state -The current state of the grid can be exported using `apiRef.current.exportState()`. -It can then be restored by either passing it to the `initialState` prop or to the `apiRef.current.restoreState()` method. - -Watch out for controlled models and their callbacks (`onFilterModelChange` if you use `filterModel` for instance), the grid will call those callbacks when restoring the state. -But if the callback is not defined or if calling it does not update the prop value, then the restored value will not be applied. - -### Restore the state with `initialState` - -> ⚠️ If you restore the page using `initialState` before the data is fetched, the grid will automatically move to the 1st page. - -{{"demo": "pages/components/data-grid/state/RestoreStateInitialState.js", "bg": "inline", "defaultCodeOpen": false}} - -### Restore the state with `apiRef` [](https://mui.com/store/items/material-ui-pro/) - -{{"demo": "pages/components/data-grid/state/RestoreStateApiRef.js", "bg": "inline", "defaultCodeOpen": false}} - -#### Restore part of the state - -It is possible to restore specific properties of the state using the `apiRef.current.restoreState()` method. -For instance, to only restore the pinned columns: - -```ts -apiRef.current.restoreState({ - pinnedColumns: ['brand'], -}); -``` - -> ⚠️ Most of the state keys are not fully independent. +> ⚠️ This feature isn't implemented yet. It's coming. > -> Restoring the pagination without restoring the filters or the sorting will work, but the rows displayed after the re-import will not be the same as before the export. +> 👍 Upvote [issue #820](https://github.com/mui-org/material-ui-x/issues/820) if you want to see it land faster. + +[//]: # 'The current state of the grid can be exported using `apiRef.current.exportState()`.' +[//]: # 'It can then be restored by either passing it to the `initialState` prop or to the `apiRef.current.restoreState()` method.' +[//]: # +[//]: # 'Watch out for controlled models and their callbacks (`onFilterModelChange` if you use `filterModel` for instance), the grid will call those callbacks when restoring the state.' +[//]: # 'But if the callback is not defined or if calling it does not update the prop value, then the restored value will not be applied.' +[//]: # +[//]: # '### Restore the state with `initialState`' +[//]: # +[//]: # '> ⚠️ If you restore the page using `initialState` before the data is fetched, the grid will automatically move to the 1st page.' +[//]: # +[//]: # '{{"demo": "pages/components/data-grid/state/RestoreStateInitialState.js", "bg": "inline", "defaultCodeOpen": false}}' +[//]: # +[//]: # '### Restore the state with `apiRef` [](https://mui.com/store/items/material-ui-pro/)' +[//]: # +[//]: # '{{"demo": "pages/components/data-grid/state/RestoreStateApiRef.js", "bg": "inline", "defaultCodeOpen": false}}' +[//]: # +[//]: # '#### Restore part of the state' +[//]: # +[//]: # 'It is possible to restore specific properties of the state using the `apiRef.current.restoreState()` method.' +[//]: # 'For instance, to only restore the pinned columns:' +[//]: # +[//]: # '```ts' +[//]: # 'apiRef.current.restoreState({' +[//]: # " pinnedColumns: ['brand']," +[//]: # '});' +[//]: # '```' +[//]: # +[//]: # '> ⚠️ Most of the state keys are not fully independent.' +[//]: # '>' +[//]: # '> Restoring the pagination without restoring the filters or the sorting will work, but the rows displayed after the re-import will not be the same as before the export.' ## API