Skip to content

Commit

Permalink
[DataGrid] Column Visibility: Update "Reset" button behavior (#16626)
Browse files Browse the repository at this point in the history
  • Loading branch information
MBilalShafi authored Feb 17, 2025
1 parent bb972f5 commit be0c6ab
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,16 @@ Below are described the steps you need to make to migrate from v7 to v8.

- `createUseGridApiEventHandler()` is not exported anymore.

### Behavioral changes

- The "Reset" button in the column visibility panel now resets to the initial column visibility model instead of the model when the panel was opened. The reset behavior follows these rules:

1. If an initial `columnVisibilityModel` is provided, it resets to that model.
2. If a controlled `columnVisibilityModel` is provided, it resets to the first model value.
3. When the columns are updated (via the `columns` prop or `updateColumns()` API method), the reset reference point updates to the current `columnVisibilityModel`.

To revert to the previous behavior, provide a custom component to the `slots.columnsManagement`.

### Localization

- If `estimatedRowCount` is used, the text provided to the [Table Pagination](/material-ui/api/table-pagination/) component from the Material UI library is updated and requires additional translations. Check the example at the end of [Index-based pagination section](/x/react-data-grid/pagination/#index-based-pagination).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { TextFieldProps } from '../../models/gridBaseSlots';
import {
gridColumnDefinitionsSelector,
gridColumnVisibilityModelSelector,
gridInitialColumnVisibilityModelSelector,
} from '../../hooks/features/columns/gridColumnsSelector';
import { useGridSelector } from '../../hooks/utils/useGridSelector';
import { useGridApiContext } from '../../hooks/utils/useGridApiContext';
Expand All @@ -16,7 +17,6 @@ import type { DataGridProcessedProps } from '../../models/props/DataGridProps';
import type { GridColDef } from '../../models/colDef/gridColDef';
import type { GridSlotProps } from '../../models/gridSlotsComponentsProps';
import { getDataGridUtilityClass } from '../../constants/gridClasses';
import { useLazyRef } from '../../hooks/utils/useLazyRef';
import { checkColumnVisibilityModelsSame, defaultSearchPredicate } from './utils';
import { NotRendered } from '../../utils/assert';

Expand Down Expand Up @@ -84,9 +84,10 @@ function GridColumnsManagement(props: GridColumnsManagementProps) {
const apiRef = useGridApiContext();
const searchInputRef = React.useRef<HTMLInputElement>(null);
const columns = useGridSelector(apiRef, gridColumnDefinitionsSelector);
const initialColumnVisibilityModel = useLazyRef(() =>
gridColumnVisibilityModelSelector(apiRef),
).current;
const initialColumnVisibilityModel = useGridSelector(
apiRef,
gridInitialColumnVisibilityModelSelector,
);
const columnVisibilityModel = useGridSelector(apiRef, gridColumnVisibilityModelSelector);
const rootProps = useGridRootProps();
const [searchValue, setSearchValue] = React.useState('');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface GridColumnsState {
orderedFields: string[];
lookup: GridColumnLookup;
columnVisibilityModel: GridColumnVisibilityModel;
initialColumnVisibilityModel: GridColumnVisibilityModel;
}

export interface GridPinnedColumnFields {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,17 @@ export const gridColumnVisibilityModelSelector = createSelector(
(columnsState) => columnsState.columnVisibilityModel,
);

/**
* Get the "initial" column visibility model, containing the visibility status of each column.
* It is updated when the `columns` prop is updated or when `updateColumns` API method is called.
* If a column is not registered in the model, it is visible.
* @category Visible Columns
*/
export const gridInitialColumnVisibilityModelSelector = createSelector(
gridColumnsStateSelector,
(columnsState) => columnsState.initialColumnVisibilityModel,
);

/**
* Get the visible columns as a lookup (an object containing the field for keys and the definition for values).
* @category Visible Columns
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,13 @@ export const createColumnsState = ({
initialState,
columnVisibilityModel = gridColumnVisibilityModelSelector(apiRef),
keepOnlyColumnsToUpsert = false,
updateInitialVisibilityModel = false,
}: {
columnsToUpsert: readonly GridColDef[];
initialState: GridColumnsInitialState | undefined;
columnVisibilityModel?: GridColumnVisibilityModel;
keepOnlyColumnsToUpsert: boolean;
updateInitialVisibilityModel?: boolean;
apiRef: RefObject<GridApiCommunity>;
}) => {
const isInsideStateInitializer = !apiRef.current.state.columns;
Expand All @@ -325,13 +327,17 @@ export const createColumnsState = ({
orderedFields: [],
lookup: {},
columnVisibilityModel,
initialColumnVisibilityModel: columnVisibilityModel,
};
} else {
const currentState = gridColumnsStateSelector(apiRef);
columnsState = {
orderedFields: keepOnlyColumnsToUpsert ? [] : [...currentState.orderedFields],
lookup: { ...currentState.lookup }, // Will be cleaned later if keepOnlyColumnsToUpsert=true
columnVisibilityModel,
initialColumnVisibilityModel: updateInitialVisibilityModel
? columnVisibilityModel
: currentState.initialColumnVisibilityModel,
};
}

Expand Down
16 changes: 15 additions & 1 deletion packages/x-data-grid/src/hooks/features/columns/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,16 @@
export * from './gridColumnsSelector';
export {
gridColumnsStateSelector,
gridColumnFieldsSelector,
gridColumnLookupSelector,
gridColumnVisibilityModelSelector,
gridColumnDefinitionsSelector,
gridVisibleColumnDefinitionsSelector,
gridVisibleColumnFieldsSelector,
gridPinnedColumnsSelector,
gridVisiblePinnedColumnDefinitionsSelector,
gridColumnPositionsSelector,
gridFilterableColumnDefinitionsSelector,
gridFilterableColumnLookupSelector,
gridHasColSpanSelector,
} from './gridColumnsSelector';
export * from './gridColumnsInterfaces';
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ export function useGridColumns(
columnsToUpsert: columns,
initialState: undefined,
keepOnlyColumnsToUpsert: false,
updateInitialVisibilityModel: true,
});
setGridColumnsState(columnsState);
},
Expand Down Expand Up @@ -451,6 +452,7 @@ export function useGridColumns(
// If the user provides a model, we don't want to set it in the state here because it has it's dedicated `useEffect` which calls `setColumnVisibilityModel`
columnsToUpsert: props.columns,
keepOnlyColumnsToUpsert: true,
updateInitialVisibilityModel: true,
});
previousColumnsProp.current = props.columns;
setGridColumnsState(columnsState);
Expand Down
113 changes: 113 additions & 0 deletions packages/x-data-grid/src/tests/columnsVisibility.DataGrid.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,119 @@ describe('<DataGrid /> - Columns visibility', () => {
expect(resetButton).to.have.attribute('disabled');
});

it('should use the initial column visibility model as a reference when `Reset` button is clicked in columns management panel', async () => {
const { user } = render(
<TestDataGrid
slots={{
toolbar: GridToolbar,
}}
initialState={{
columns: {
columnVisibilityModel: { idBis: false },
},
}}
/>,
);

expect(getColumnHeadersTextContent()).to.deep.equal(['id']);
await user.click(screen.getByRole('button', { name: 'Select columns' }));
const resetButton = screen.getByRole('button', { name: 'Reset' });
expect(resetButton).to.have.attribute('disabled');

// Show `idBis` column
await user.click(screen.getByRole('checkbox', { name: 'idBis' }));
expect(getColumnHeadersTextContent()).to.deep.equal(['id', 'idBis']);
expect(resetButton).not.to.have.attribute('disabled');

// Close columns management
await user.click(screen.getByRole('button', { name: 'Select columns' }));

// Reopen columns management
await user.click(screen.getByRole('button', { name: 'Select columns' }));

const resetButton1 = screen.getByRole('button', { name: 'Reset' });
expect(resetButton1).not.to.have.attribute('disabled');

// Reset columns
await user.click(resetButton1);
expect(getColumnHeadersTextContent()).to.deep.equal(['id']);
expect(resetButton1).to.have.attribute('disabled');
});

it('should use the first controlled column visibility model as a reference when `Reset` button is clicked in columns management panel', async () => {
function ControlledTest() {
const [model, setModel] = React.useState<GridColumnVisibilityModel>({ idBis: false });
return (
<TestDataGrid
slots={{
toolbar: GridToolbar,
}}
columnVisibilityModel={model}
onColumnVisibilityModelChange={setModel}
/>
);
}
const { user } = render(<ControlledTest />);

expect(getColumnHeadersTextContent()).to.deep.equal(['id']);
await user.click(screen.getByRole('button', { name: 'Select columns' }));
const resetButton = screen.getByRole('button', { name: 'Reset' });
expect(resetButton).to.have.attribute('disabled');

// Show `idBis` column
await user.click(screen.getByRole('checkbox', { name: 'idBis' }));
expect(getColumnHeadersTextContent()).to.deep.equal(['id', 'idBis']);
expect(resetButton).not.to.have.attribute('disabled');

// Close columns management
await user.click(screen.getByRole('button', { name: 'Select columns' }));

// Reopen columns management
await user.click(screen.getByRole('button', { name: 'Select columns' }));

const resetButton1 = screen.getByRole('button', { name: 'Reset' });
expect(resetButton1).not.to.have.attribute('disabled');

// Reset columns
await user.click(resetButton1);
expect(getColumnHeadersTextContent()).to.deep.equal(['id']);
expect(resetButton1).to.have.attribute('disabled');
});

it('should update the initial column visibility model when the columns are updated', async () => {
const { user, setProps } = render(
<TestDataGrid
slots={{
toolbar: GridToolbar,
}}
initialState={{
columns: {
columnVisibilityModel: { idBis: false },
},
}}
/>,
);

expect(getColumnHeadersTextContent()).to.deep.equal(['id']);
await user.click(screen.getByRole('button', { name: 'Select columns' }));
const resetButton = screen.getByRole('button', { name: 'Reset' });
expect(resetButton).to.have.attribute('disabled');

// Show `idBis` column
await user.click(screen.getByRole('checkbox', { name: 'idBis' }));
expect(getColumnHeadersTextContent()).to.deep.equal(['id', 'idBis']);
expect(resetButton).not.to.have.attribute('disabled');

// Reset columns
setProps({
columns: [{ field: 'id' }, { field: 'idBis' }],
});

expect(getColumnHeadersTextContent()).to.deep.equal(['id', 'idBis']);
// Reference updated to the current `columnVisibilityModel`
expect(resetButton).to.have.attribute('disabled');
});

describe('prop: `getTogglableColumns`', () => {
it('should control columns shown in columns panel using `getTogglableColumns` prop', () => {
const getTogglableColumns = (cols: GridColDef[]) =>
Expand Down

0 comments on commit be0c6ab

Please sign in to comment.