Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DataGrid] Add column order and dimensions to the portable state #3816

Merged
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
fdcd5a1
[DataGrid] Add column order and dimensions to the portable state
flaviendelangle Feb 2, 2022
066aed0
Work
flaviendelangle Feb 2, 2022
4ed8a46
Work
flaviendelangle Feb 3, 2022
0405f5a
Merge branch 'master' into state-columns-import-export
flaviendelangle Feb 9, 2022
d1f08ec
Merge branch 'master' into state-columns-import-export
flaviendelangle Feb 10, 2022
df5be23
Work
flaviendelangle Feb 10, 2022
cdb70e0
Fix
flaviendelangle Feb 10, 2022
41e4ce3
Work
flaviendelangle Feb 10, 2022
8011d94
Fix
flaviendelangle Feb 10, 2022
436fe47
Merge
flaviendelangle Feb 14, 2022
2258fa3
Code review: Matheus
flaviendelangle Feb 15, 2022
70cf322
Merge branch 'master' into state-columns-import-export
flaviendelangle Feb 18, 2022
cee4119
Merge branch 'master' into state-columns-import-export
flaviendelangle Feb 24, 2022
108090b
Merge branch 'master' into state-columns-import-export
flaviendelangle Feb 24, 2022
975f565
Merge
flaviendelangle Feb 25, 2022
e5ae7b7
Merge
flaviendelangle Feb 25, 2022
ff39cb6
Merge branch 'master' into state-columns-import-export
flaviendelangle Feb 28, 2022
75d4c27
Fix
flaviendelangle Feb 28, 2022
a9be458
Merge
flaviendelangle Mar 1, 2022
fd300b9
Code review: Matheus
flaviendelangle Mar 1, 2022
0410a1e
Merge
flaviendelangle Mar 2, 2022
e4ad627
Merge branch 'master' into state-columns-import-export
flaviendelangle Mar 3, 2022
e1608d8
Merge
flaviendelangle Mar 4, 2022
f059ed3
Work
flaviendelangle Mar 4, 2022
44eccc2
Merge branch 'master' into state-columns-import-export
flaviendelangle Mar 7, 2022
3899d87
Merge branch 'master' into state-columns-import-export
flaviendelangle Mar 7, 2022
5fe44ac
Work
flaviendelangle Mar 7, 2022
0c75a1b
Merge branch 'master' into state-columns-import-export
flaviendelangle Mar 8, 2022
c07a044
Work
flaviendelangle Mar 8, 2022
09d1d53
Merge branch 'master' into state-columns-import-export
flaviendelangle Mar 14, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
flaviendelangle marked this conversation as resolved.
Show resolved Hide resolved
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