Skip to content

Commit

Permalink
[DataGrid] Add column order and dimensions to the portable state (#3816)
Browse files Browse the repository at this point in the history
  • Loading branch information
flaviendelangle authored Mar 14, 2022
1 parent f6ccf75 commit 3ea869f
Show file tree
Hide file tree
Showing 8 changed files with 362 additions and 114 deletions.
2 changes: 2 additions & 0 deletions docs/data/data-grid/state/RestoreStateApiRef.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ ViewListItem.propTypes = {
value: PropTypes.shape({
columns: PropTypes.shape({
columnVisibilityModel: PropTypes.object,
dimensions: PropTypes.object,
orderedFields: PropTypes.arrayOf(PropTypes.string),
}),
detailPanel: PropTypes.shape({
expandedRowIds: PropTypes.arrayOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,104 @@ import {
DataGridPro,
DataGridProProps,
GridApi,
GridColDef,
GridInitialState,
GridPreferencePanelsValue,
GridRowsProp,
useGridApiRef,
} from '@mui/x-data-grid-pro';
// @ts-ignore Remove once the test utils are typed
import { createRenderer, screen } from '@mui/monorepo/test/utils';
import { useMovieData } from '@mui/x-data-grid-generator';
import { expect } from 'chai';
import { getColumnHeadersTextContent, getColumnValues } from '../../../../../test/utils/helperFn';
import {
getColumnHeaderCell,
getColumnHeadersTextContent,
getColumnValues,
} from '../../../../../test/utils/helperFn';

const isJSDOM = /jsdom/.test(window.navigator.userAgent);

const rows: GridRowsProp = [
{ id: 0, category1: 'Cat A' },
{ id: 1, category1: 'Cat A' },
{ id: 2, category1: 'Cat A' },
{ id: 3, category1: 'Cat B' },
{ id: 4, category1: 'Cat B' },
{ id: 5, category1: 'Cat B' },
];

const columns: GridColDef[] = [
{
field: 'id',
type: 'number',
},
{
field: 'category1',
},
{
field: 'idBis',
type: 'number',
valueGetter: (params) => params.row.id,
},
];

const FULL_INITIAL_STATE: GridInitialState = {
columns: {
columnVisibilityModel: { category1: false },
orderedFields: ['id', '__row_group_by_columns_group__', 'idBis', 'category1'],
dimensions: {
idBis: {
width: 75,
maxWidth: Infinity,
minWidth: 50,
flex: undefined,
},
},
},
filter: {
filterModel: {
items: [{ columnField: 'id', operatorValue: '<', value: '5' }],
},
},
pagination: {
page: 1,
pageSize: 1,
},
pinnedColumns: {
left: ['id'],
},
preferencePanel: {
open: true,
openedPanelValue: GridPreferencePanelsValue.filters,
},
sorting: {
sortModel: [{ field: 'id', sort: 'desc' }],
},
rowGrouping: {
model: ['category1'],
},
};

describe('<DataGridPro /> - State Persistence', () => {
const { render, clock } = createRenderer({ clock: 'fake' });

let apiRef: React.MutableRefObject<GridApi>;

const TestCase = (props: Omit<DataGridProProps, 'rows' | 'columns' | 'apiRef'>) => {
const data = useMovieData();

apiRef = useGridApiRef();

return (
<div style={{ width: 300, height: 300 }}>
<DataGridPro
{...data}
rows={rows}
columns={columns}
pagination
autoHeight={isJSDOM}
{...props}
apiRef={apiRef}
disableVirtualization
rowsPerPageOptions={[100, 2]}
rowsPerPageOptions={[100, 1]}
experimentalFeatures={{ rowGrouping: true }} // To enable the `rowGroupingModel in export / restore
{...props}
initialState={{
...props.initialState,
columns: {
Expand All @@ -46,43 +111,22 @@ describe('<DataGridPro /> - State Persistence', () => {
},
}}
defaultGroupingExpansionDepth={-1}
groupingColDef={{ headerName: 'Group' }}
/>
</div>
);
};

describe('apiRef: exportState', () => {
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'],
},
};

// We always export the `orderedFields`,
// If it's something problematic we could introduce an `hasBeenReordered` property and only export if at least one column has been reordered.
it('should not return the default values of the models', () => {
render(<TestCase />);
expect(apiRef.current.exportState()).to.deep.equal({});
expect(apiRef.current.exportState()).to.deep.equal({
columns: {
orderedFields: ['id', 'category1', 'idBis'],
},
});
});

it('should export the initial values of the models', () => {
Expand All @@ -92,16 +136,18 @@ describe('<DataGridPro /> - State Persistence', () => {

it('should export the current version of the exportable state', () => {
render(<TestCase />);
apiRef.current.setPageSize(2);
apiRef.current.setPageSize(1);
apiRef.current.setPage(1);
apiRef.current.setPinnedColumns({ left: ['company'] });
apiRef.current.setPinnedColumns({ left: ['id'] });
apiRef.current.showPreferences(GridPreferencePanelsValue.filters);
apiRef.current.setSortModel([{ field: 'director', sort: 'asc' }]);
apiRef.current.setSortModel([{ field: 'id', sort: 'desc' }]);
apiRef.current.setFilterModel({
items: [{ columnField: 'gross', operatorValue: '>', value: '1500000000' }],
items: [{ columnField: 'id', operatorValue: '<', value: '5' }],
});
apiRef.current.setRowGroupingModel(['director']);
apiRef.current.setColumnVisibilityModel({ year: false });
apiRef.current.setRowGroupingModel(['category1']);
apiRef.current.setColumnIndex('idBis', 2);
apiRef.current.setColumnWidth('idBis', 75);
apiRef.current.setColumnVisibilityModel({ category1: false });

expect(apiRef.current.exportState()).to.deep.equal(FULL_INITIAL_STATE);
});
Expand All @@ -111,53 +157,22 @@ describe('<DataGridPro /> - State Persistence', () => {
it('should restore the whole exportable state', () => {
render(<TestCase />);

apiRef.current.restoreState({
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'],
},
});

// Pagination
expect(getColumnValues(0)).to.deep.equal(['', 'Disney Studios', '', 'Universal Pictures']);
apiRef.current.restoreState(FULL_INITIAL_STATE);

// Sorting and row grouping
expect(getColumnValues(1)).to.deep.equal(['J. J. Abrams (1)', '', 'Colin Trevorrow (1)', '']);
// Pinning sorting and filtering
expect(getColumnValues(0)).to.deep.equal(['', '4', '3']);

// Filtering
expect(getColumnValues(3)).to.deep.equal(['', '2,068,223,624$', '', '1,671,713,208$']);
// Pagination and row grouping
expect(getColumnValues(1)).to.deep.equal(['Cat B (2)', '', '']);

// Preference panel
expect(screen.getByRole('button', { name: /Add Filter/i })).to.not.equal(null);

// Columns visibility
expect(getColumnHeadersTextContent()).to.not.include('Year');
expect(getColumnHeadersTextContent()).to.deep.equal(['id', 'Group', 'idBis']);

// Pinning
expect(
document.querySelector('.MuiDataGrid-pinnedColumnHeaders--left')?.textContent,
).to.deep.equal('Company');
// Columns dimensions
expect(getColumnHeaderCell(2)).toHaveInlineStyle({ width: '75px' });
});

it('should restore partial exportable state', () => {
Expand All @@ -166,11 +181,11 @@ describe('<DataGridPro /> - State Persistence', () => {
apiRef.current.restoreState({
pagination: {
page: 1,
pageSize: 2,
pageSize: 1,
},
});

expect(getColumnValues(0)).to.deep.equal(['Titanic', 'Star Wars: The Force Awakens']);
expect(getColumnValues(0)).to.deep.equal(['1']);
});

it('should restore controlled sub-state', () => {
Expand All @@ -191,11 +206,49 @@ describe('<DataGridPro /> - State Persistence', () => {
apiRef.current.restoreState({
pagination: {
page: 1,
pageSize: 2,
pageSize: 1,
},
});
clock.runToLast();
expect(getColumnValues(0)).to.deep.equal(['Titanic', 'Star Wars: The Force Awakens']);
expect(getColumnValues(0)).to.deep.equal(['1']);
});

it('should not restore the column visibility model when using the legacy column visibility', () => {
const TestCaseLegacyColumnVisibility = () => {
apiRef = useGridApiRef();

return (
<div style={{ width: 300, height: 300 }}>
<DataGridPro
rows={rows}
columns={[
{
field: 'id',
hide: true,
},
{
field: 'category1',
},
]}
autoHeight={isJSDOM}
apiRef={apiRef}
disableVirtualization
/>
</div>
);
};

render(<TestCaseLegacyColumnVisibility />);

apiRef.current.restoreState({
columns: {
columnVisibilityModel: {
category1: false,
},
},
});

expect(getColumnHeadersTextContent()).to.deep.equal(['category1']);
});
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { GridRowId } from '../../../models';
import { GridColDef, GridStateColDef } from '../../../models/colDef/gridColDef';
import type { GridColumnDimensionProperties } from './gridColumnsUtils';

export type GridColumnLookup = {
[field: string]: GridStateColDef;
Expand All @@ -11,15 +12,19 @@ export type GridColumnRawLookup = {

export interface GridColumnsState {
/**
* TODO v6: Rename `orderedFields`.
* TODO v6: Rename `all` to `orderedFields`
*/
all: string[];
lookup: GridColumnLookup;
columnVisibilityModel: GridColumnVisibilityModel;
}

export type GridColumnDimensions = Pick<GridStateColDef, GridColumnDimensionProperties>;

export interface GridColumnsInitialState {
columnVisibilityModel?: GridColumnVisibilityModel;
orderedFields?: string[];
dimensions?: Record<string, GridColumnDimensions>;
}

export type GridColumnsRawState = Omit<GridColumnsState, 'lookup'> & {
Expand Down
Loading

0 comments on commit 3ea869f

Please sign in to comment.